feat: add dashboard handlers for roles, sessions, skill definitions, tags, tickets, users, and WeChat integration

- Implement role management endpoints including listing, creating, updating, and deleting roles.
- Add session management endpoints for viewing and revoking sessions.
- Create skill definition management endpoints for CRUD operations and status updates.
- Introduce tag management endpoints for handling tags with sorting and status updates.
- Develop ticket management endpoints for viewing, creating, updating, and managing ticket progress.
- Implement user management endpoints for CRUD operations, role assignments, and password resets.
- Add WeChat callback handlers for verifying and processing messages.
This commit is contained in:
mlogclub
2026-05-23 22:30:30 +08:00
parent f9ddf39c83
commit c6c1c00141
35 changed files with 3 additions and 3 deletions
+102
View File
@@ -0,0 +1,102 @@
package api
import (
"cs-agent/internal/pkg/config"
"cs-agent/internal/pkg/dto/request"
"cs-agent/internal/pkg/httpx"
"cs-agent/internal/pkg/httpx/params"
"cs-agent/internal/services"
"net/http"
"net/url"
"strings"
"github.com/gin-gonic/gin"
)
func Login(ctx *gin.Context) {
cfg := config.Current()
req := request.LoginRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
ret, err := services.AuthService.Login(req, cfg.Auth, ctx.ClientIP(), ctx.GetHeader("User-Agent"))
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, ret)
}
func WxWorkLogin(ctx *gin.Context) {
loginURL, err := services.WxWorkLoginService.BuildWxWorkLoginURL(ctx.Query("next"))
if err != nil {
ctx.Redirect(http.StatusFound, "/login?wxworkError="+url.QueryEscape(wxWorkErrorMessage(err.Error())))
return
}
ctx.Redirect(http.StatusFound, loginURL)
}
func WxWorkQRLogin(ctx *gin.Context) {
loginURL, err := services.WxWorkLoginService.BuildWxWorkQRCodeLoginURL(ctx.Query("next"))
if err != nil {
ctx.Redirect(http.StatusFound, "/login?wxworkError="+url.QueryEscape(wxWorkErrorMessage(err.Error())))
return
}
ctx.Redirect(http.StatusFound, loginURL)
}
func WxWorkCallback(ctx *gin.Context) {
cfg := config.Current()
ticket, next, err := services.WxWorkLoginService.LoginByWxWork(
ctx.Query("code"),
ctx.Query("state"),
cfg.Auth,
ctx.ClientIP(),
ctx.GetHeader("User-Agent"),
)
if err != nil {
ctx.Redirect(http.StatusFound, "/login?wxworkError="+url.QueryEscape(wxWorkErrorMessage(err.Error())))
return
}
ctx.Redirect(http.StatusFound, "/dashboard/login/wxwork/callback?ticket="+url.QueryEscape(ticket)+"&next="+url.QueryEscape(next))
}
func WxWorkExchange(ctx *gin.Context) {
req := request.WxWorkExchangeRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
ret, err := services.WxWorkLoginService.ExchangeWxWorkLoginTicket(req.Ticket)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, ret)
}
func Logout(ctx *gin.Context) {
if err := services.AuthService.Logout(ctx.GetHeader("Authorization")); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func Profile(ctx *gin.Context) {
ret, err := services.AuthService.CurrentProfile(ctx)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, ret)
}
func wxWorkErrorMessage(message string) string {
if idx := strings.Index(message, ": "); idx >= 0 {
message = message[idx+2:]
}
return message
}
+75
View File
@@ -0,0 +1,75 @@
package api
import (
"cs-agent/internal/pkg/dto/response"
"cs-agent/internal/pkg/enums"
"cs-agent/internal/pkg/errorsx"
"cs-agent/internal/pkg/httpx"
"cs-agent/internal/services"
"github.com/gin-gonic/gin"
"github.com/mlogclub/simple/web"
)
func ChannelAnyConfig(ctx *gin.Context) {
channel := services.ChannelService.GetEnabledChannel(ctx)
if channel == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("接入渠道未初始化"))
return
}
cfg, err := resolveWidgetConfig(channel.ChannelType, channel.ConfigJSON)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
ret := response.WidgetConfigResponse{
ChannelID: channel.ChannelID,
ChannelType: channel.ChannelType,
Title: cfg.Title,
Subtitle: cfg.Subtitle,
ThemeColor: cfg.ThemeColor,
Position: cfg.Position,
Width: cfg.Width,
}
httpx.WriteJSON(ctx, ret)
}
type webLikeWidgetConfig struct {
Title string
Subtitle string
ThemeColor string
Position string
Width string
}
func resolveWidgetConfig(channelType, rawConfig string) (*webLikeWidgetConfig, error) {
switch channelType {
case enums.ChannelTypeWeb:
cfg, err := services.ChannelService.ParseWebChannelConfig(rawConfig)
if err != nil {
return nil, err
}
return &webLikeWidgetConfig{
Title: cfg.Title,
Subtitle: cfg.Subtitle,
ThemeColor: cfg.ThemeColor,
Position: cfg.Position,
Width: cfg.Width,
}, nil
case enums.ChannelTypeWechatMP:
cfg, err := services.ChannelService.ParseWechatMPChannelConfig(rawConfig)
if err != nil {
return nil, err
}
return &webLikeWidgetConfig{
Title: cfg.Title,
Subtitle: cfg.Subtitle,
ThemeColor: cfg.ThemeColor,
Position: "right",
Width: "100%",
}, nil
default:
return nil, errorsx.InvalidParam("该渠道不支持开放客服配置")
}
}
@@ -0,0 +1,89 @@
package api
import (
"cs-agent/internal/builders"
"cs-agent/internal/pkg/dto/request"
"cs-agent/internal/pkg/dto/response"
"cs-agent/internal/pkg/httpx"
"cs-agent/internal/services"
"cs-agent/internal/pkg/httpx/params"
"github.com/gin-gonic/gin"
"github.com/mlogclub/simple/web"
)
func ConversationGetBy(ctx *gin.Context) {
id, ok := httpx.GetPathInt64(ctx, "id")
if !ok {
return
}
if services.ChannelService.GetEnabledChannel(ctx) == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("接入渠道未初始化"))
return
}
external := httpx.GetExternalUser(ctx)
if external == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("外部身份未初始化"))
return
}
item := services.ConversationService.Get(id)
if item == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("会话不存在"))
return
}
if !services.ConversationService.IsCustomerConversationOwner(item, *external) {
httpx.WriteJSON(ctx, web.JsonErrorMsg("无权访问该会话"))
return
}
detail := response.ConversationDetailResponse{
ConversationResponse: builders.BuildConversation(item),
Participants: builders.BuildParticipantResponses(id),
}
httpx.WriteJSON(ctx, detail)
}
func ConversationPostCreate_or_match(ctx *gin.Context) {
channel := services.ChannelService.GetEnabledChannel(ctx)
if channel == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("接入渠道未初始化"))
return
}
external := httpx.GetExternalUser(ctx)
if external == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("外部身份未初始化"))
return
}
item, err := services.ConversationService.Create(*external, channel.ID, channel.AIAgentID)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, builders.BuildConversation(item))
}
func ConversationPostClose(ctx *gin.Context) {
if services.ChannelService.GetEnabledChannel(ctx) == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("接入渠道未初始化"))
return
}
external := httpx.GetExternalUser(ctx)
if external == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("外部身份未初始化"))
return
}
req := request.CloseConversationRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.ConversationService.CloseCustomerConversation(req.ConversationID, *external); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
+29
View File
@@ -0,0 +1,29 @@
package api
import (
"cs-agent/internal/pkg/httpx"
"cs-agent/internal/pkg/openidentity"
"cs-agent/internal/services"
"github.com/gin-gonic/gin"
"github.com/mlogclub/simple/web"
)
func CustomerPostSession_exchange(ctx *gin.Context) {
channel := services.ChannelService.GetEnabledChannel(ctx)
if channel == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("接入渠道不存在或已停用"))
return
}
externalUser, err := openidentity.GetExternalUser(ctx, services.ChannelService.GetUserTokenSecret(channel))
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
resp, err := services.CustomerSessionService.Exchange(channel, *externalUser)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, resp)
}
+196
View File
@@ -0,0 +1,196 @@
package api
import (
"cs-agent/internal/builders"
"cs-agent/internal/pkg/dto/request"
"cs-agent/internal/pkg/enums"
"cs-agent/internal/pkg/httpx"
"cs-agent/internal/services"
"strconv"
"strings"
"cs-agent/internal/pkg/httpx/params"
"github.com/gin-gonic/gin"
"github.com/mlogclub/simple/web"
"github.com/spf13/cast"
)
func MessageAnyList(ctx *gin.Context) {
if services.ChannelService.GetEnabledChannel(ctx) == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("接入渠道未初始化"))
return
}
external := httpx.GetExternalUser(ctx)
if external == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("外部身份未初始化"))
return
}
conversationID, _ := params.GetInt64(ctx, "conversationId")
if conversationID <= 0 {
httpx.WriteJSON(ctx, web.JsonErrorMsg("conversationId不能为空"))
return
}
conversation := services.ConversationService.Get(conversationID)
if conversation == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("会话不存在"))
return
}
if !services.ConversationService.IsCustomerConversationOwner(conversation, *external) {
httpx.WriteJSON(ctx, web.JsonErrorMsg("无权访问该会话"))
return
}
var (
senderType, _ = params.Get(ctx, "senderType")
messageType, _ = params.Get(ctx, "messageType")
cursor, _ = params.GetInt64(ctx, "cursor")
limit, _ = params.GetInt(ctx, "limit")
)
list, nextCursor, hasMore := services.MessageService.FindByConversationIDCursor(
conversationID, cursor, limit, senderType, messageType,
)
results := builders.BuildMessages(list)
httpx.WriteJSON(ctx, httpx.CursorData(results, cast.ToString(nextCursor), hasMore))
}
func MessagePostSend(ctx *gin.Context) {
if services.ChannelService.GetEnabledChannel(ctx) == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("接入渠道未初始化"))
return
}
external := httpx.GetExternalUser(ctx)
if external == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("外部身份未初始化"))
return
}
req := request.SendConversationMessageRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item, err := services.MessageService.SendCustomerMessage(req.ConversationID, req.ClientMsgID, req.MessageType, req.Content, req.Payload, *external)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, builders.BuildMessage(item))
}
func MessagePostRead(ctx *gin.Context) {
if services.ChannelService.GetEnabledChannel(ctx) == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("接入渠道未初始化"))
return
}
external := httpx.GetExternalUser(ctx)
if external == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("外部身份未初始化"))
return
}
req := request.ReadConversationRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.ConversationService.MarkCustomerConversationReadToMessage(req.ConversationID, req.MessageID, external); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func MessagePostUpload_image(ctx *gin.Context) {
if services.ChannelService.GetEnabledChannel(ctx) == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("接入渠道未初始化"))
return
}
external := httpx.GetExternalUser(ctx)
if external == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("外部身份未初始化"))
return
}
rawConv := strings.TrimSpace(params.FormValue(ctx, "conversationId"))
if rawConv == "" {
httpx.WriteJSON(ctx, web.JsonErrorMsg("conversationId不能为空"))
return
}
conversationID, err := strconv.ParseInt(rawConv, 10, 64)
if err != nil || conversationID <= 0 {
httpx.WriteJSON(ctx, web.JsonErrorMsg("conversationId不能为空"))
return
}
conversation := services.ConversationService.Get(conversationID)
if conversation == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("会话不存在"))
return
}
if !services.ConversationService.IsCustomerConversationOwner(conversation, *external) {
httpx.WriteJSON(ctx, web.JsonErrorMsg("无权访问该会话"))
return
}
if _, err := services.MessageService.ValidateConversationSender(conversationID, enums.IMSenderTypeCustomer, nil, external); err != nil {
httpx.WriteJSON(ctx, err)
return
}
header, err := ctx.FormFile("file")
if err != nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("请选择上传图片"))
return
}
if !strings.HasPrefix(strings.ToLower(header.Header.Get("Content-Type")), "image/") {
httpx.WriteJSON(ctx, web.JsonErrorMsg("仅支持上传图片文件"))
return
}
item, err := services.AssetService.UploadFile(header, "images", nil)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, builders.BuildAsset(item))
}
func MessagePostUpload_attachment(ctx *gin.Context) {
if services.ChannelService.GetEnabledChannel(ctx) == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("接入渠道未初始化"))
return
}
external := httpx.GetExternalUser(ctx)
if external == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("外部身份未初始化"))
return
}
rawConv := strings.TrimSpace(params.FormValue(ctx, "conversationId"))
if rawConv == "" {
httpx.WriteJSON(ctx, web.JsonErrorMsg("conversationId不能为空"))
return
}
conversationID, err := strconv.ParseInt(rawConv, 10, 64)
if err != nil || conversationID <= 0 {
httpx.WriteJSON(ctx, web.JsonErrorMsg("conversationId不能为空"))
return
}
if _, err := services.MessageService.ValidateConversationSender(conversationID, enums.IMSenderTypeCustomer, nil, external); err != nil {
httpx.WriteJSON(ctx, err)
return
}
header, err := ctx.FormFile("file")
if err != nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("请选择上传附件"))
return
}
item, err := services.AssetService.UploadFile(header, "attachments", nil)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, builders.BuildAsset(item))
}
@@ -0,0 +1,116 @@
package dashboard
import (
"cs-agent/internal/builders"
"cs-agent/internal/pkg/constants"
"cs-agent/internal/pkg/dto/request"
"cs-agent/internal/pkg/httpx"
"cs-agent/internal/services"
"cs-agent/internal/pkg/httpx/params"
"github.com/gin-gonic/gin"
"github.com/mlogclub/simple/web"
)
func AgentAnyList(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionAgentView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
list, paging := services.AgentProfileService.FindPageByCnd(params.NewPagedSqlCnd(ctx,
params.QueryFilter{ParamName: "userId"},
params.QueryFilter{ParamName: "teamId"},
params.QueryFilter{ParamName: "serviceStatus"},
params.QueryFilter{ParamName: "agentCode", Op: params.Like},
params.QueryFilter{ParamName: "displayName", Op: params.Like},
).Desc("id"))
results := builders.BuildAgentProfileList(list)
httpx.WriteJSON(ctx, &web.PageResult{Results: results, Page: paging})
}
func AgentGetList_all(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionAgentView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
list := services.AgentProfileService.Find(params.NewPagedSqlCnd(ctx,
params.QueryFilter{ParamName: "userId"},
params.QueryFilter{ParamName: "teamId"},
params.QueryFilter{ParamName: "serviceStatus"},
params.QueryFilter{ParamName: "agentCode", Op: params.Like},
).Desc("id"))
httpx.WriteJSON(ctx, builders.BuildAgentProfileList(list))
}
func AgentGetBy(ctx *gin.Context) {
id, ok := httpx.GetPathInt64(ctx, "id")
if !ok {
return
}
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionAgentView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item := services.AgentProfileService.Get(id)
if item == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("客服档案不存在"))
return
}
httpx.WriteJSON(ctx, builders.BuildAgentProfileResponse(item))
}
func AgentPostCreate(ctx *gin.Context) {
user, err := services.AuthService.RequirePermission(ctx, constants.PermissionAgentCreate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.CreateAgentProfileRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item, err := services.AgentProfileService.CreateAgentProfile(req, user)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, builders.BuildAgentProfileResponse(item))
}
func AgentPostUpdate(ctx *gin.Context) {
user, err := services.AuthService.RequirePermission(ctx, constants.PermissionAgentUpdate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.UpdateAgentProfileRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.AgentProfileService.UpdateAgentProfile(req, user); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func AgentPostDelete(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionAgentDelete); err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.DeleteAgentProfileRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.AgentProfileService.DeleteAgentProfile(req.ID); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
@@ -0,0 +1,65 @@
package dashboard
import (
"cs-agent/internal/builders"
"cs-agent/internal/pkg/constants"
"cs-agent/internal/pkg/dto/response"
"cs-agent/internal/pkg/httpx"
"cs-agent/internal/services"
"cs-agent/internal/pkg/httpx/params"
"github.com/gin-gonic/gin"
"github.com/mlogclub/simple/web"
)
func AgentRunLogAnyList(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionConversationView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
cnd := params.NewPagedSqlCnd(ctx,
params.QueryFilter{ParamName: "conversationId"},
params.QueryFilter{ParamName: "messageId"},
params.QueryFilter{ParamName: "aiAgentId"},
params.QueryFilter{ParamName: "plannedAction"},
params.QueryFilter{ParamName: "plannedSkillCode", Op: params.Like},
params.QueryFilter{ParamName: "graphToolCode"},
params.QueryFilter{ParamName: "interruptType"},
params.QueryFilter{ParamName: "resumeSource"},
params.QueryFilter{ParamName: "finalStatus"},
params.QueryFilter{ParamName: "handoffReason", Op: params.Like},
params.QueryFilter{ParamName: "finalAction"},
params.QueryFilter{ParamName: "userMessage", Op: params.Like},
).Desc("id")
if hitlStatus, _ := params.Get(ctx, "hitlStatus"); hitlStatus != "" && hitlStatus != "all" {
cnd = services.AgentRunLogService.ApplyHITLStatusFilter(cnd, hitlStatus)
}
queryParams := params.NewQueryParams(ctx)
queryParams.Cnd = *cnd
list, paging := services.AgentRunLogService.FindPageByParams(queryParams)
results := make([]response.AgentRunLogResponse, 0, len(list))
for _, item := range list {
results = append(results, builders.BuildAgentRunLog(&item))
}
httpx.WriteJSON(ctx, &web.PageResult{Results: results, Page: paging})
}
func AgentRunLogGetBy(ctx *gin.Context) {
id, ok := httpx.GetPathInt64(ctx, "id")
if !ok {
return
}
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionConversationView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item := services.AgentRunLogService.Get(id)
if item == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("Agent 运行日志不存在"))
return
}
httpx.WriteJSON(ctx, builders.BuildAgentRunLog(item))
}
@@ -0,0 +1,139 @@
package dashboard
import (
"cs-agent/internal/models"
"cs-agent/internal/pkg/constants"
"cs-agent/internal/pkg/dto/request"
"cs-agent/internal/pkg/dto/response"
"cs-agent/internal/pkg/enums"
"cs-agent/internal/pkg/httpx"
"cs-agent/internal/services"
"cs-agent/internal/pkg/httpx/params"
"github.com/gin-gonic/gin"
"github.com/mlogclub/simple/sqls"
"github.com/mlogclub/simple/web"
)
func AgentTeamAnyList(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionAgentTeamView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
cnd := params.NewSqlCnd(ctx,
params.QueryFilter{ParamName: "status"},
params.QueryFilter{ParamName: "leaderUserId"},
params.QueryFilter{ParamName: "name", Op: params.Like},
).Desc("id")
if _, ok := params.Get(ctx, "status"); !ok {
cnd.Where("status <> ?", enums.StatusDeleted)
}
list := services.AgentTeamService.Find(cnd)
results := make([]response.AgentTeamResponse, 0, len(list))
for _, item := range list {
results = append(results, buildAgentTeamResponse(&item))
}
httpx.WriteJSON(ctx, results)
}
func AgentTeamGetList_all(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionAgentTeamView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
list := services.AgentTeamService.Find(sqls.NewCnd().Eq("status", enums.StatusOk))
results := make([]response.AgentTeamResponse, 0, len(list))
for _, item := range list {
results = append(results, buildAgentTeamResponse(&item))
}
httpx.WriteJSON(ctx, results)
}
func AgentTeamGetBy(ctx *gin.Context) {
id, ok := httpx.GetPathInt64(ctx, "id")
if !ok {
return
}
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionAgentTeamView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item := services.AgentTeamService.Get(id)
if item == nil || item.Status == enums.StatusDeleted {
httpx.WriteJSON(ctx, web.JsonErrorMsg("客服组不存在"))
return
}
httpx.WriteJSON(ctx, buildAgentTeamResponse(item))
}
func AgentTeamPostCreate(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionAgentTeamCreate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.CreateAgentTeamRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item, err := services.AgentTeamService.CreateAgentTeam(req, operator)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, buildAgentTeamResponse(item))
}
func AgentTeamPostUpdate(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionAgentTeamUpdate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.UpdateAgentTeamRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.AgentTeamService.UpdateAgentTeam(req, operator); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func AgentTeamPostDelete(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionAgentTeamDelete)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.DeleteAgentTeamRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.AgentTeamService.DeleteAgentTeam(req.ID, operator); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func buildAgentTeamResponse(item *models.AgentTeam) response.AgentTeamResponse {
ret := response.AgentTeamResponse{
ID: item.ID,
Name: item.Name,
LeaderUserID: item.LeaderUserID,
Status: item.Status,
Description: item.Description,
Remark: item.Remark,
}
if user := services.UserService.Get(item.LeaderUserID); user != nil {
ret.LeaderUsername = user.Username
ret.LeaderNickname = user.Nickname
}
return ret
}
@@ -0,0 +1,179 @@
package dashboard
import (
"cs-agent/internal/builders"
"cs-agent/internal/models"
"cs-agent/internal/pkg/constants"
"cs-agent/internal/pkg/dto/request"
"cs-agent/internal/pkg/dto/response"
"cs-agent/internal/pkg/httpx"
"cs-agent/internal/services"
"cs-agent/internal/pkg/httpx/params"
"github.com/gin-gonic/gin"
"github.com/mlogclub/simple/web"
)
func AgentTeamScheduleAnyList(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionAgentTeamScheduleView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
cnd := params.NewPagedSqlCnd(ctx,
params.QueryFilter{ParamName: "teamId"},
).Desc("start_at").Desc("id")
list, paging := services.AgentTeamScheduleService.FindPageByCnd(cnd)
results := make([]response.AgentTeamScheduleResponse, 0, len(list))
for _, item := range list {
results = append(results, buildAgentTeamScheduleResponse(&item))
}
httpx.WriteJSON(ctx, &web.PageResult{Results: results, Page: paging})
}
func AgentTeamScheduleAnyCalendar(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionAgentTeamScheduleView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
startAt, _ := params.Get(ctx, "startAt")
endAt, _ := params.Get(ctx, "endAt")
teamID, _ := params.GetInt64(ctx, "teamId")
list, err := services.AgentTeamScheduleService.FindCalendarSchedules(request.AgentTeamScheduleCalendarRequest{
StartAt: startAt,
EndAt: endAt,
TeamID: teamID,
})
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
results := make([]response.AgentTeamScheduleResponse, 0, len(list))
for _, item := range list {
results = append(results, buildAgentTeamScheduleResponse(&item))
}
httpx.WriteJSON(ctx, results)
}
func AgentTeamSchedulePostBatch_preview(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionAgentTeamScheduleBatchGenerate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.AgentTeamScheduleBatchRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
ret, err := services.AgentTeamScheduleService.BatchPreview(req, operator)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, builders.BuildAgentTeamScheduleBatchPreviewResponse(ret))
}
func AgentTeamSchedulePostBatch_generate(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionAgentTeamScheduleBatchGenerate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.AgentTeamScheduleBatchRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
ret, err := services.AgentTeamScheduleService.BatchGenerate(req, operator)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, builders.BuildAgentTeamScheduleBatchGenerateResponse(ret))
}
func AgentTeamScheduleGetBy(ctx *gin.Context) {
id, ok := httpx.GetPathInt64(ctx, "id")
if !ok {
return
}
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionAgentTeamScheduleView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item := services.AgentTeamScheduleService.Get(id)
if item == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("客服组排班不存在"))
return
}
httpx.WriteJSON(ctx, buildAgentTeamScheduleResponse(item))
}
func AgentTeamSchedulePostCreate(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionAgentTeamScheduleCreate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.CreateAgentTeamScheduleRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item, err := services.AgentTeamScheduleService.CreateAgentTeamSchedule(req, operator)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, buildAgentTeamScheduleResponse(item))
}
func AgentTeamSchedulePostUpdate(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionAgentTeamScheduleUpdate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.UpdateAgentTeamScheduleRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.AgentTeamScheduleService.UpdateAgentTeamSchedule(req, operator); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func AgentTeamSchedulePostDelete(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionAgentTeamScheduleDelete); err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.DeleteAgentTeamScheduleRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.AgentTeamScheduleService.DeleteAgentTeamSchedule(req.ID); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func buildAgentTeamScheduleResponse(item *models.AgentTeamSchedule) response.AgentTeamScheduleResponse {
ret := response.AgentTeamScheduleResponse{
ID: item.ID,
TeamID: item.TeamID,
StartAt: item.StartAt.Format("2006-01-02 15:04:05"),
EndAt: item.EndAt.Format("2006-01-02 15:04:05"),
Remark: item.Remark,
}
if team := services.AgentTeamService.Get(item.TeamID); team != nil {
ret.TeamName = team.Name
}
return ret
}
@@ -0,0 +1,292 @@
package dashboard
import (
"cs-agent/internal/pkg/httpx"
"encoding/json"
"strings"
"cs-agent/internal/models"
"cs-agent/internal/pkg/constants"
"cs-agent/internal/pkg/dto/request"
"cs-agent/internal/pkg/dto/response"
"cs-agent/internal/pkg/enums"
"cs-agent/internal/pkg/toolx"
"cs-agent/internal/pkg/utils"
"cs-agent/internal/services"
"cs-agent/internal/pkg/httpx/params"
"github.com/gin-gonic/gin"
"github.com/mlogclub/simple/sqls"
"github.com/mlogclub/simple/web"
)
func AIAgentAnyList(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionAIAgentView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
cnd := params.NewPagedSqlCnd(ctx,
params.QueryFilter{ParamName: "status"},
params.QueryFilter{ParamName: "name", Op: params.Like},
params.QueryFilter{ParamName: "code", Op: params.Like},
).Desc("sort_no").Desc("id")
list, paging := services.AIAgentService.FindPageByCnd(cnd)
results := make([]response.AIAgentResponse, 0, len(list))
for _, item := range list {
results = append(results, buildAIAgentResponse(&item))
}
httpx.WriteJSON(ctx, &web.PageResult{Results: results, Page: paging})
}
func AIAgentGetList_all(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionAIAgentView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
list := services.AIAgentService.Find(sqls.NewCnd().Where("status = ?", enums.StatusOk).Desc("sort_no").Desc("id"))
results := make([]response.AIAgentResponse, 0, len(list))
for _, item := range list {
results = append(results, buildAIAgentResponse(&item))
}
httpx.WriteJSON(ctx, results)
}
func AIAgentGetBy(ctx *gin.Context) {
id, ok := httpx.GetPathInt64(ctx, "id")
if !ok {
return
}
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionAIAgentView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item := services.AIAgentService.Get(id)
if item == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("AI Agent 不存在"))
return
}
httpx.WriteJSON(ctx, buildAIAgentResponse(item))
}
func AIAgentPostCreate(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionAIAgentCreate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.CreateAIAgentRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item, err := services.AIAgentService.CreateAIAgent(req, operator)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, buildAIAgentResponse(item))
}
func AIAgentPostUpdate(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionAIAgentUpdate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.UpdateAIAgentRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.AIAgentService.UpdateAIAgent(req, operator); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func AIAgentPostDelete(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionAIAgentDelete)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.DeleteAIAgentRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.AIAgentService.DeleteAIAgent(req.ID, operator); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func AIAgentPostUpdate_sort(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionAIAgentUpdate); err != nil {
httpx.WriteJSON(ctx, err)
return
}
var ids []int64
if err := params.ReadJSON(ctx, &ids); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.AIAgentService.UpdateSort(ids); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func AIAgentPostUpdate_status(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionAIAgentUpdate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.UpdateAIAgentStatusRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.AIAgentService.UpdateStatus(req.ID, req.Status, operator); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func buildAIAgentResponse(item *models.AIAgent) response.AIAgentResponse {
ret := response.AIAgentResponse{
ID: item.ID,
Name: item.Name,
Description: item.Description,
Status: item.Status,
StatusName: enums.GetStatusLabel(item.Status),
AIConfigID: item.AIConfigID,
ServiceMode: item.ServiceMode,
ServiceModeName: enums.GetIMConversationServiceModeLabel(item.ServiceMode),
SystemPrompt: item.SystemPrompt,
WelcomeMessage: item.WelcomeMessage,
ReplyTimeoutSeconds: item.ReplyTimeoutSeconds,
HandoffMode: item.HandoffMode,
HandoffModeName: enums.GetAIAgentHandoffModeLabel(item.HandoffMode),
FallbackMode: item.FallbackMode,
FallbackModeName: enums.GetAIAgentFallbackModeLabel(item.FallbackMode),
FallbackMessage: item.FallbackMessage,
KnowledgeIDs: utils.SplitInt64s(item.KnowledgeIDs),
SkillIDs: utils.SplitInt64s(item.SkillIDs),
KnowledgeBaseNames: make([]string, 0),
Skills: make([]response.AIAgentSkillResponse, 0),
Teams: make([]response.AIAgentTeamResponse, 0),
DirectTools: make([]response.AIAgentMCPToolResponse, 0),
GraphTools: make([]string, 0),
SortNo: item.SortNo,
CreatedAt: item.CreatedAt.Format("2006-01-02 15:04:05"),
UpdatedAt: item.UpdatedAt.Format("2006-01-02 15:04:05"),
CreateUserName: item.CreateUserName,
UpdateUserName: item.UpdateUserName,
}
if aiConfig := services.AIConfigService.Get(item.AIConfigID); aiConfig != nil {
ret.AIConfigName = aiConfig.Name
}
for _, id := range utils.SplitInt64s(item.TeamIDs) {
if team := services.AgentTeamService.Get(id); team != nil {
ret.Teams = append(ret.Teams, response.AIAgentTeamResponse{
ID: team.ID,
Name: team.Name,
})
}
}
for _, id := range ret.KnowledgeIDs {
if knowledgeBase := services.KnowledgeBaseService.Get(id); knowledgeBase != nil {
ret.KnowledgeBaseNames = append(ret.KnowledgeBaseNames, knowledgeBase.Name)
}
}
for _, id := range ret.SkillIDs {
if skill := services.SkillDefinitionService.Get(id); skill != nil {
ret.Skills = append(ret.Skills, response.AIAgentSkillResponse{
ID: skill.ID,
Code: skill.Code,
Name: skill.Name,
})
}
}
if raw := strings.TrimSpace(item.AllowedMCPTools); raw != "" {
var directTools []request.AIAgentMCPToolRequest
if err := json.Unmarshal([]byte(raw), &directTools); err == nil {
for _, tool := range directTools {
toolCode := strings.TrimSpace(tool.ToolCode)
if toolCode == "" {
toolCode = toolx.BuildMCPToolCode(tool.ServerCode, tool.ToolName)
}
toolCode = toolx.NormalizeToolCodeAlias(toolCode)
if toolx.IsAutoInjectedToolCode(toolCode) {
continue
}
if toolx.IsAgentDirectGraphToolCode(toolCode) {
ret.GraphTools = appendGraphToolCodeIfMissing(ret.GraphTools, toolCode)
continue
}
serverCode := strings.TrimSpace(tool.ServerCode)
toolName := strings.TrimSpace(tool.ToolName)
if registeredServerCode, registeredToolName, ok := toolx.GetRegisteredToolIdentity(toolCode); ok {
serverCode = registeredServerCode
toolName = registeredToolName
} else if parsedServerCode, parsedToolName := toolx.SplitMCPToolCode(toolCode); parsedServerCode != "" && parsedToolName != "" {
serverCode = parsedServerCode
toolName = parsedToolName
}
title := strings.TrimSpace(tool.Title)
if title == "" {
if registeredTitle := toolx.GetRegisteredToolTitle(toolCode); registeredTitle != "" {
title = registeredTitle
}
}
description := strings.TrimSpace(tool.Description)
if description == "" {
if registeredDescription := toolx.GetRegisteredToolDescription(toolCode); registeredDescription != "" {
description = registeredDescription
}
}
ret.DirectTools = append(ret.DirectTools, response.AIAgentMCPToolResponse{
ToolCode: toolCode,
ServerCode: serverCode,
ToolName: toolName,
Title: title,
Description: description,
Arguments: tool.Arguments,
})
}
}
}
if raw := strings.TrimSpace(item.AllowedGraphTools); raw != "" {
var graphTools []string
if err := json.Unmarshal([]byte(raw), &graphTools); err == nil {
for _, toolCode := range graphTools {
toolCode = toolx.NormalizeToolCodeAlias(strings.TrimSpace(toolCode))
if !toolx.IsAgentDirectGraphToolCode(toolCode) {
continue
}
ret.GraphTools = appendGraphToolCodeIfMissing(ret.GraphTools, toolCode)
}
}
}
return ret
}
func appendGraphToolCodeIfMissing(items []string, toolCode string) []string {
toolCode = strings.TrimSpace(toolCode)
if toolCode == "" {
return items
}
for _, item := range items {
if strings.TrimSpace(item) == toolCode {
return items
}
}
return append(items, toolCode)
}
@@ -0,0 +1,164 @@
package dashboard
import (
"cs-agent/internal/pkg/constants"
"cs-agent/internal/pkg/dto/request"
"cs-agent/internal/pkg/dto/response"
"cs-agent/internal/pkg/enums"
"cs-agent/internal/pkg/httpx"
"cs-agent/internal/services"
"cs-agent/internal/pkg/httpx/params"
"github.com/gin-gonic/gin"
"github.com/mlogclub/simple/web"
)
func AIConfigAnyList(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionAIConfigView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
list, paging := services.AIConfigService.FindPageByCnd(params.NewPagedSqlCnd(ctx,
params.QueryFilter{ParamName: "status"},
params.QueryFilter{ParamName: "provider"},
params.QueryFilter{ParamName: "modelType"},
params.QueryFilter{ParamName: "name", Op: params.Like},
params.QueryFilter{ParamName: "modelName", Op: params.Like},
).Desc("sort_no").Desc("id"))
results := make([]response.AIConfigResponse, 0, len(list))
for _, item := range list {
results = append(results, response.BuildAIConfigResponse(&item))
}
httpx.WriteJSON(ctx, &web.PageResult{Results: results, Page: paging})
}
func AIConfigAnyList_all(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionAIConfigView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
list := services.AIConfigService.Find(params.NewSqlCnd(ctx,
params.QueryFilter{ParamName: "modelType"},
).Eq("status", enums.StatusOk).Desc("sort_no").Desc("id"))
results := make([]response.AIConfigResponse, 0, len(list))
for _, item := range list {
results = append(results, response.BuildAIConfigResponse(&item))
}
httpx.WriteJSON(ctx, results)
}
func AIConfigGetBy(ctx *gin.Context) {
id, ok := httpx.GetPathInt64(ctx, "id")
if !ok {
return
}
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionAIConfigView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item := services.AIConfigService.Get(id)
if item == nil || item.Status == enums.StatusDeleted {
httpx.WriteJSON(ctx, web.JsonErrorMsg("AI配置不存在"))
return
}
httpx.WriteJSON(ctx, response.BuildAIConfigResponse(item))
}
func AIConfigPostCreate(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionAIConfigCreate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.CreateAIConfigRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item, err := services.AIConfigService.CreateAIConfig(req, operator)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, response.BuildAIConfigResponse(item))
}
func AIConfigPostUpdate(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionAIConfigUpdate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.UpdateAIConfigRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.AIConfigService.UpdateAIConfig(req, operator); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func AIConfigPostDelete(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionAIConfigDelete)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.DeleteAIConfigRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.AIConfigService.DeleteAIConfig(req.ID, operator); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func AIConfigPostUpdate_status(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionAIConfigUpdate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.UpdateAIConfigStatusRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.AIConfigService.UpdateStatus(req.ID, req.Status, operator); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func AIConfigPostUpdate_sort(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionAIConfigUpdate); err != nil {
httpx.WriteJSON(ctx, err)
return
}
var ids []int64
if err := params.ReadJSON(ctx, &ids); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.AIConfigService.UpdateSort(ids); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
@@ -0,0 +1,102 @@
package dashboard
import (
"cs-agent/internal/builders"
"cs-agent/internal/pkg/constants"
"cs-agent/internal/pkg/dto/request"
"cs-agent/internal/pkg/dto/response"
"cs-agent/internal/pkg/enums"
"cs-agent/internal/pkg/httpx"
"cs-agent/internal/services"
"strings"
"cs-agent/internal/pkg/httpx/params"
"github.com/gin-gonic/gin"
"github.com/mlogclub/simple/web"
)
func AssetAnyList(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionAssetView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
cnd := params.NewPagedSqlCnd(ctx,
params.QueryFilter{ParamName: "provider"},
params.QueryFilter{ParamName: "status"},
params.QueryFilter{ParamName: "createUserId"},
params.QueryFilter{ParamName: "filename", Op: params.Like},
).Desc("id")
if strings.TrimSpace(ctx.Query("status")) == "" {
cnd = cnd.Eq("status", enums.AssetStatusSuccess)
}
list, paging := services.AssetService.FindPageByCnd(cnd)
results := make([]response.AssetResponse, 0, len(list))
for _, item := range list {
results = append(results, builders.BuildAsset(&item))
}
httpx.WriteJSON(ctx, &web.PageResult{Results: results, Page: paging})
}
func AssetGetBy(ctx *gin.Context) {
id, ok := httpx.GetPathInt64(ctx, "id")
if !ok {
return
}
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionAssetView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item := services.AssetService.Get(id)
if item == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("文件不存在"))
return
}
httpx.WriteJSON(ctx, builders.BuildAsset(item))
}
func AssetPostCreate(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionAssetCreate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.CreateAssetRequest{}
if err := params.ReadForm(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
header, err := ctx.FormFile("file")
if err != nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("请选择上传文件"))
return
}
item, err := services.AssetService.UploadFile(header, req.Prefix, operator)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, builders.BuildAsset(item))
}
func AssetPostDelete(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionAssetDelete)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.DeleteAssetRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.AssetService.DeleteAsset(req.ID, operator); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
@@ -0,0 +1,167 @@
package dashboard
import (
"cs-agent/internal/models"
"cs-agent/internal/pkg/constants"
"cs-agent/internal/pkg/dto/request"
"cs-agent/internal/pkg/dto/response"
"cs-agent/internal/pkg/enums"
"cs-agent/internal/pkg/httpx"
"cs-agent/internal/services"
"cs-agent/internal/pkg/httpx/params"
"github.com/gin-gonic/gin"
"github.com/mlogclub/simple/web"
)
func ChannelAnyList(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionChannelView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
list, paging := services.ChannelService.FindPageByCnd(params.NewPagedSqlCnd(ctx,
params.QueryFilter{ParamName: "status"},
params.QueryFilter{ParamName: "name", Op: params.Like},
params.QueryFilter{ParamName: "channelType"},
params.QueryFilter{ParamName: "channelId", Op: params.Like},
).Where("status <> ?", enums.StatusDeleted).Desc("id"))
results := make([]response.ChannelResponse, 0, len(list))
for _, item := range list {
results = append(results, buildChannelResponse(&item))
}
httpx.WriteJSON(ctx, &web.PageResult{Results: results, Page: paging})
}
func ChannelGetBy(ctx *gin.Context) {
id, ok := httpx.GetPathInt64(ctx, "id")
if !ok {
return
}
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionChannelView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item := services.ChannelService.Get(id)
if item == nil || item.Status == enums.StatusDeleted {
httpx.WriteJSON(ctx, web.JsonErrorMsg("channel not found"))
return
}
httpx.WriteJSON(ctx, buildChannelResponse(item))
}
func ChannelAnyWxworkKfAccounts(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionChannelView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
list, err := services.ChannelService.ListWxWorkKFAccounts()
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, list)
}
func ChannelPostCreate(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionChannelCreate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.CreateChannelRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item, err := services.ChannelService.CreateChannel(req, operator)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, buildChannelResponse(item))
}
func ChannelPostUpdate(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionChannelUpdate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.UpdateChannelRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.ChannelService.UpdateChannel(req, operator); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func ChannelPostUpdate_status(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionChannelUpdate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.UpdateChannelStatusRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.ChannelService.UpdateStatus(req.ID, req.Status, operator); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func ChannelPostReset_user_token_secret(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionChannelUpdate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.ResetChannelUserTokenSecretRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
secret, err := services.ChannelService.ResetUserTokenSecret(req.ID, operator)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, map[string]string{"userTokenSecret": secret})
}
func ChannelPostDelete(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionChannelDelete)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.DeleteChannelRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.ChannelService.DeleteChannel(req.ID, operator); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func buildChannelResponse(item *models.Channel) response.ChannelResponse {
ret := response.BuildChannelResponse(item)
if item == nil {
return ret
}
if aiAgent := services.AIAgentService.Get(item.AIAgentID); aiAgent != nil {
ret.AIAgentName = aiAgent.Name
}
return ret
}
@@ -0,0 +1,130 @@
package dashboard
import (
"cs-agent/internal/builders"
"cs-agent/internal/pkg/constants"
"cs-agent/internal/pkg/dto/request"
"cs-agent/internal/pkg/enums"
"cs-agent/internal/pkg/httpx"
"cs-agent/internal/services"
"cs-agent/internal/pkg/httpx/params"
"github.com/gin-gonic/gin"
"github.com/mlogclub/simple/web"
)
func CompanyAnyList(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionCompanyView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
list, paging := services.CompanyService.FindPageByCnd(params.NewPagedSqlCnd(ctx,
params.QueryFilter{ParamName: "status"},
params.QueryFilter{ParamName: "name", Op: params.Like},
params.QueryFilter{ParamName: "code", Op: params.Like},
).Where("status <> ?", enums.StatusDeleted).Desc("id"))
results := builders.BuildCompanyList(list)
companyIDs := make([]int64, 0, len(results))
for _, item := range results {
companyIDs = append(companyIDs, item.ID)
}
countMap := services.CustomerService.CountByCompanyIDs(companyIDs)
for i := range results {
results[i].CustomerCount = countMap[results[i].ID]
}
httpx.WriteJSON(ctx, &web.PageResult{Results: results, Page: paging})
}
func CompanyGetBy(ctx *gin.Context) {
id, ok := httpx.GetPathInt64(ctx, "id")
if !ok {
return
}
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionCompanyView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item := services.CompanyService.Get(id)
if item == nil || item.Status == enums.StatusDeleted {
httpx.WriteJSON(ctx, nil)
return
}
ret := builders.BuildCompany(item)
httpx.WriteJSON(ctx, &ret)
}
func CompanyPostCreate(ctx *gin.Context) {
user, err := services.AuthService.RequirePermission(ctx, constants.PermissionCompanyCreate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.CreateCompanyRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item, err := services.CompanyService.CreateCompany(req, user)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
ret := builders.BuildCompany(item)
httpx.WriteJSON(ctx, &ret)
}
func CompanyPostUpdate(ctx *gin.Context) {
user, err := services.AuthService.RequirePermission(ctx, constants.PermissionCompanyUpdate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.UpdateCompanyRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.CompanyService.UpdateCompany(req, user); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func CompanyPostDelete(ctx *gin.Context) {
user, err := services.AuthService.RequirePermission(ctx, constants.PermissionCompanyDelete)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.DeleteCompanyRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.CompanyService.DeleteCompany(req.ID, *user); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func CompanyPostUpdate_status(ctx *gin.Context) {
user, err := services.AuthService.RequirePermission(ctx, constants.PermissionCompanyUpdate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.UpdateCompanyStatusRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.CompanyService.UpdateStatus(req.ID, req.Status, user); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
@@ -0,0 +1,414 @@
package dashboard
import (
"cs-agent/internal/builders"
"cs-agent/internal/pkg/constants"
"cs-agent/internal/pkg/dto/request"
"cs-agent/internal/pkg/dto/response"
"cs-agent/internal/pkg/enums"
"cs-agent/internal/pkg/httpx"
"cs-agent/internal/services"
"strconv"
"strings"
"cs-agent/internal/pkg/httpx/params"
"github.com/gin-gonic/gin"
"github.com/mlogclub/simple/common/strs"
"github.com/mlogclub/simple/web"
"github.com/spf13/cast"
)
func ConversationAnyList(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionConversationView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
cnd := params.NewPagedSqlCnd(ctx,
params.QueryFilter{ParamName: "status"},
params.QueryFilter{ParamName: "serviceMode"},
params.QueryFilter{ParamName: "currentAssigneeId"},
).Desc("last_message_at").Desc("id")
paging := params.GetPaging(ctx)
if keyword, _ := params.Get(ctx, "keyword"); strs.IsNotBlank(keyword) {
keywordLike := "%" + strings.TrimSpace(keyword) + "%"
cnd.Where("customer_name LIKE ? OR last_message_summary LIKE ?", keywordLike, keywordLike)
}
// 标签搜索
if tagID, _ := params.GetInt64(ctx, "tagId"); tagID > 0 {
tagIDs := services.TagService.GetSelfAndDescendantIDs(tagID)
if len(tagIDs) == 0 {
httpx.WriteJSON(ctx, &web.PageResult{
Results: []response.ConversationResponse{},
Page: paging,
})
return
}
cnd.Where("id IN (SELECT conversation_id FROM conversation_tag_rels WHERE tag_id IN (?))", tagIDs)
}
if agentTeamID, _ := params.GetInt64(ctx, "agentTeamId"); agentTeamID > 0 {
userIDs := services.AgentProfileService.GetUserIDsByTeamID(agentTeamID)
if len(userIDs) == 0 {
httpx.WriteJSON(ctx, &web.PageResult{
Results: []response.ConversationResponse{},
Page: paging,
})
return
}
cnd.In("current_assignee_id", userIDs)
}
list, paging := services.ConversationService.FindPageByCnd(cnd)
results := make([]response.ConversationResponse, 0, len(list))
for _, item := range list {
results = append(results, builders.BuildConversation(&item))
}
httpx.WriteJSON(ctx, &web.PageResult{Results: results, Page: paging})
}
func ConversationAnyConversations(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionConversationView)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
filterValue, _ := params.Get(ctx, "filter")
keyword, _ := params.Get(ctx, "keyword")
paging := params.GetPaging(ctx)
list, paging, err := services.ConversationService.ListConversations(
operator.UserID,
request.AgentConversationFilter(strings.TrimSpace(filterValue)),
keyword,
paging,
)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
results := make([]response.ConversationResponse, 0, len(list))
for _, item := range list {
results = append(results, builders.BuildConversation(&item))
}
httpx.WriteJSON(ctx, &web.PageResult{Results: results, Page: paging})
}
func ConversationGetBy(ctx *gin.Context) {
id, ok := httpx.GetPathInt64(ctx, "id")
if !ok {
return
}
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionConversationView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item := services.ConversationService.Get(id)
if item == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("会话不存在"))
return
}
detail := response.ConversationDetailResponse{
ConversationResponse: builders.BuildConversation(item),
Participants: builders.BuildParticipantResponses(id),
}
httpx.WriteJSON(ctx, detail)
}
func ConversationAnyMessage_list(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionConversationView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
var (
conversationID, _ = params.GetInt64(ctx, "conversationId")
senderType, _ = params.Get(ctx, "senderType")
messageType, _ = params.Get(ctx, "messageType")
cursor, _ = params.GetInt64(ctx, "cursor")
limit, _ = params.GetInt(ctx, "limit")
)
if conversation := services.ConversationService.Get(conversationID); conversation == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("会话不存在"))
return
}
list, nextCursor, hasMore := services.MessageService.FindByConversationIDCursor(
conversationID, cursor, limit, senderType, messageType,
)
results := builders.BuildMessages(list)
httpx.WriteJSON(ctx, httpx.CursorData(results, cast.ToString(nextCursor), hasMore))
}
func ConversationPostAssign(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionConversationAssign)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.AssignConversationRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.ConversationService.AssignConversation(req, operator); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func ConversationPostDispatch(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionConversationAssign)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.DispatchConversationRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.ConversationService.AutoAssignConversation(req.ConversationID, operator); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func ConversationPostTransfer(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionConversationTransfer)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.TransferConversationRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.ConversationService.TransferConversation(req.ConversationID, req.ToUserID, req.Reason, operator); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func ConversationPostClose(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionConversationClose)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.CloseConversationRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.ConversationService.CloseConversation(req.ConversationID, req.CloseReason, operator); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func ConversationPostLink_customer(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionConversationLinkCustomer)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.LinkConversationCustomerRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.ConversationService.LinkConversationCustomer(req.ConversationID, req.CustomerID, operator); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func ConversationPostSend_message(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionConversationSend)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.SendConversationMessageRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item, err := services.MessageService.SendAgentMessage(req.ConversationID, 0, req.ClientMsgID, req.MessageType, req.Content, req.Payload, operator)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, builders.BuildMessage(item))
}
func ConversationPostRecall_message(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionConversationSend)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.RecallConversationMessageRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item, err := services.MessageService.RecallAgentMessage(req.MessageID, operator)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, builders.BuildMessage(item))
}
func ConversationPostRead(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionConversationView)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.ReadConversationRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.ConversationService.MarkAgentConversationReadToMessage(req.ConversationID, req.MessageID, operator); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func ConversationPostUpload_image(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionConversationSend)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
rawConv := strings.TrimSpace(params.FormValue(ctx, "conversationId"))
if rawConv == "" {
httpx.WriteJSON(ctx, web.JsonErrorMsg("conversationId不能为空"))
return
}
conversationID, err := strconv.ParseInt(rawConv, 10, 64)
if err != nil || conversationID <= 0 {
httpx.WriteJSON(ctx, web.JsonErrorMsg("conversationId不能为空"))
return
}
if _, err := services.MessageService.ValidateConversationSender(conversationID, enums.IMSenderTypeAgent, operator, nil); err != nil {
httpx.WriteJSON(ctx, err)
return
}
header, err := ctx.FormFile("file")
if err != nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("请选择上传图片"))
return
}
if !strings.HasPrefix(strings.ToLower(header.Header.Get("Content-Type")), "image/") {
httpx.WriteJSON(ctx, web.JsonErrorMsg("仅支持上传图片文件"))
return
}
item, err := services.AssetService.UploadFile(header, "images", operator)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, builders.BuildAsset(item))
}
func ConversationPostUpload_attachment(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionConversationSend)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
rawConv := strings.TrimSpace(params.FormValue(ctx, "conversationId"))
if rawConv == "" {
httpx.WriteJSON(ctx, web.JsonErrorMsg("conversationId不能为空"))
return
}
conversationID, err := strconv.ParseInt(rawConv, 10, 64)
if err != nil || conversationID <= 0 {
httpx.WriteJSON(ctx, web.JsonErrorMsg("conversationId不能为空"))
return
}
if _, err := services.MessageService.ValidateConversationSender(conversationID, enums.IMSenderTypeAgent, operator, nil); err != nil {
httpx.WriteJSON(ctx, err)
return
}
header, err := ctx.FormFile("file")
if err != nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("请选择上传附件"))
return
}
item, err := services.AssetService.UploadFile(header, "attachments", operator)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, builders.BuildAsset(item))
}
func ConversationPostAdd_tag(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionConversationTag)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.AddConversationTagRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.ConversationTagService.AddTag(req, operator); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func ConversationPostRemove_tag(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionConversationTag); err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.RemoveConversationTagRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.ConversationTagService.RemoveTag(req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
@@ -0,0 +1,85 @@
package dashboard
import (
"cs-agent/internal/builders"
"cs-agent/internal/pkg/constants"
"cs-agent/internal/pkg/dto/request"
"cs-agent/internal/pkg/httpx"
"cs-agent/internal/services"
"cs-agent/internal/pkg/httpx/params"
"github.com/gin-gonic/gin"
"github.com/mlogclub/simple/web"
)
// AnyList GET/POST /customer-contact/list?customerId=
func CustomerContactAnyList(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionCustomerView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
customerID, _ := params.GetInt64(ctx, "customerId")
if customerID <= 0 {
httpx.WriteJSON(ctx, web.JsonErrorMsg("customerId 必填"))
return
}
list := services.CustomerContactService.FindActiveByCustomerID(customerID)
httpx.WriteJSON(ctx, builders.BuildCustomerContactList(list))
}
func CustomerContactPostCreate(ctx *gin.Context) {
user, err := services.AuthService.RequirePermission(ctx, constants.PermissionCustomerUpdate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.CreateCustomerContactRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item, err := services.CustomerContactService.CreateCustomerContact(req, user)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
ret := builders.BuildCustomerContactResponse(item)
httpx.WriteJSON(ctx, &ret)
}
func CustomerContactPostUpdate(ctx *gin.Context) {
user, err := services.AuthService.RequirePermission(ctx, constants.PermissionCustomerUpdate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.UpdateCustomerContactRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.CustomerContactService.UpdateCustomerContact(req, user); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func CustomerContactPostDelete(ctx *gin.Context) {
user, err := services.AuthService.RequirePermission(ctx, constants.PermissionCustomerUpdate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.DeleteCustomerContactRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.CustomerContactService.DeleteCustomerContact(req.ID, user); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
@@ -0,0 +1,150 @@
package dashboard
import (
"cs-agent/internal/builders"
"cs-agent/internal/pkg/constants"
"cs-agent/internal/pkg/dto"
"cs-agent/internal/pkg/dto/request"
"cs-agent/internal/pkg/enums"
"cs-agent/internal/pkg/httpx"
"cs-agent/internal/services"
"cs-agent/internal/pkg/httpx/params"
"github.com/gin-gonic/gin"
"github.com/mlogclub/simple/web"
)
func CustomerPostList(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionCustomerView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
var req request.CustomerListRequest
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
list, paging := services.CustomerService.ListCustomers(req)
httpx.WriteJSON(ctx, &web.PageResult{Results: builders.BuildCustomerList(list), Page: paging})
}
func CustomerGetBy(ctx *gin.Context) {
id, ok := httpx.GetPathInt64(ctx, "id")
if !ok {
return
}
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionCustomerView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item := services.CustomerService.Get(id)
if item == nil || item.Status == enums.StatusDeleted {
httpx.WriteJSON(ctx, nil)
return
}
ret := builders.BuildCustomer(item)
httpx.WriteJSON(ctx, &ret)
}
// PostSave_profile POST /save_profile — 客户主信息与联系方式在同一事务中保存。
func CustomerPostSave_profile(ctx *gin.Context) {
req := request.SaveCustomerProfileRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
createMode := req.ID == nil || *req.ID <= 0
var user *dto.AuthPrincipal
var err error
if createMode {
user, err = services.AuthService.RequirePermission(ctx, constants.PermissionCustomerCreate)
} else {
user, err = services.AuthService.RequirePermission(ctx, constants.PermissionCustomerUpdate)
}
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
item, err := services.CustomerService.SaveCustomerProfile(req, user)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
ret := builders.BuildCustomer(item)
httpx.WriteJSON(ctx, &ret)
}
func CustomerPostCreate(ctx *gin.Context) {
user, err := services.AuthService.RequirePermission(ctx, constants.PermissionCustomerCreate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.CreateCustomerRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item, err := services.CustomerService.CreateCustomer(req, user)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
ret := builders.BuildCustomer(item)
httpx.WriteJSON(ctx, &ret)
}
func CustomerPostUpdate(ctx *gin.Context) {
user, err := services.AuthService.RequirePermission(ctx, constants.PermissionCustomerUpdate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.UpdateCustomerRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.CustomerService.UpdateCustomer(req, user); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func CustomerPostDelete(ctx *gin.Context) {
user, err := services.AuthService.RequirePermission(ctx, constants.PermissionCustomerDelete)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.DeleteCustomerRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.CustomerService.DeleteCustomer(req.ID, *user); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func CustomerPostUpdate_status(ctx *gin.Context) {
user, err := services.AuthService.RequirePermission(ctx, constants.PermissionCustomerUpdate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.UpdateCustomerStatusRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.CustomerService.UpdateStatus(req.ID, req.Status, user); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
@@ -0,0 +1,15 @@
package dashboard
import (
"cs-agent/internal/pkg/httpx"
"cs-agent/internal/services"
"cs-agent/internal/pkg/httpx/params"
"github.com/gin-gonic/gin"
)
func DashboardGetOverview(ctx *gin.Context) {
rangeValue, _ := params.Get(ctx, "range")
httpx.WriteJSON(ctx, services.DashboardService.GetOverview(rangeValue))
}
@@ -0,0 +1,184 @@
package dashboard
import (
"context"
"cs-agent/internal/pkg/httpx"
"log/slog"
"cs-agent/internal/ai/rag"
"cs-agent/internal/builders"
"cs-agent/internal/pkg/constants"
"cs-agent/internal/pkg/dto/request"
"cs-agent/internal/pkg/dto/response"
"cs-agent/internal/repositories"
"cs-agent/internal/services"
"cs-agent/internal/pkg/httpx/params"
"github.com/gin-gonic/gin"
"github.com/mlogclub/simple/sqls"
"github.com/mlogclub/simple/web"
)
func KnowledgeBaseAnyList(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionKnowledgeBaseView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
cnd := params.NewPagedSqlCnd(ctx,
params.QueryFilter{ParamName: "status"},
params.QueryFilter{ParamName: "name", Op: params.Like},
).Asc("sort_no").Desc("id")
list, paging := services.KnowledgeBaseService.FindPageByCnd(cnd)
results := make([]response.KnowledgeBaseResponse, 0, len(list))
for _, item := range list {
docCount := repositories.KnowledgeDocumentRepository.CountByKnowledgeBaseID(sqls.DB(), item.ID)
faqCount := repositories.KnowledgeFAQRepository.CountByKnowledgeBaseID(sqls.DB(), item.ID)
resp := builders.BuildKnowledgeBase(&item)
resp.DocumentCount = docCount
resp.FAQCount = faqCount
results = append(results, resp)
}
httpx.WriteJSON(ctx, &web.PageResult{Results: results, Page: paging})
}
func KnowledgeBaseAnyList_all(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionKnowledgeBaseView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
list := services.KnowledgeBaseService.Find(params.NewSqlCnd(ctx,
params.QueryFilter{ParamName: "status"},
).Asc("sort_no").Desc("id"))
results := make([]response.KnowledgeBaseResponse, 0, len(list))
for _, item := range list {
resp := builders.BuildKnowledgeBase(&item)
results = append(results, resp)
}
httpx.WriteJSON(ctx, results)
}
func KnowledgeBaseGetBy(ctx *gin.Context) {
id, ok := httpx.GetPathInt64(ctx, "id")
if !ok {
return
}
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionKnowledgeBaseView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item := services.KnowledgeBaseService.Get(id)
if item == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("知识库不存在"))
return
}
resp := builders.BuildKnowledgeBase(item)
resp.DocumentCount = repositories.KnowledgeDocumentRepository.CountByKnowledgeBaseID(sqls.DB(), item.ID)
resp.FAQCount = repositories.KnowledgeFAQRepository.CountByKnowledgeBaseID(sqls.DB(), item.ID)
httpx.WriteJSON(ctx, resp)
}
func KnowledgeBasePostCreate(ctx *gin.Context) {
user, err := services.AuthService.RequirePermission(ctx, constants.PermissionKnowledgeBaseCreate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.CreateKnowledgeBaseRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item, err := services.KnowledgeBaseService.CreateKnowledgeBase(req, user)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, builders.BuildKnowledgeBase(item))
}
func KnowledgeBasePostUpdate(ctx *gin.Context) {
user, err := services.AuthService.RequirePermission(ctx, constants.PermissionKnowledgeBaseUpdate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.UpdateKnowledgeBaseRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.KnowledgeBaseService.UpdateKnowledgeBase(req, user); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func KnowledgeBasePostDelete(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionKnowledgeBaseDelete); err != nil {
httpx.WriteJSON(ctx, err)
return
}
var req struct {
ID int64 `json:"id"`
}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.KnowledgeBaseService.DeleteKnowledgeBase(req.ID); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func KnowledgeBasePostUpdate_sort(ctx *gin.Context) {
var ids []int64
if err := params.ReadJSON(ctx, &ids); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.KnowledgeBaseService.UpdateSort(ids); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func KnowledgeBasePostRebuild_index(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionKnowledgeBaseUpdate); err != nil {
httpx.WriteJSON(ctx, err)
return
}
var req struct {
ID int64 `json:"id"`
}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
knowledgeBase := services.KnowledgeBaseService.Get(req.ID)
if knowledgeBase == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("知识库不存在"))
return
}
go func() {
ctx := context.Background()
if err := rag.Index.RebuildKnowledgeBaseIndex(ctx, req.ID); err != nil {
slog.Error("Failed to rebuild knowledge base index", "knowledge_base_id", req.ID, "error", err)
}
}()
httpx.WriteJSON(ctx, nil)
}
@@ -0,0 +1,126 @@
package dashboard
import (
"cs-agent/internal/builders"
"cs-agent/internal/pkg/constants"
"cs-agent/internal/pkg/dto/request"
"cs-agent/internal/pkg/dto/response"
"cs-agent/internal/pkg/enums"
"cs-agent/internal/pkg/httpx"
"cs-agent/internal/services"
"cs-agent/internal/pkg/httpx/params"
"github.com/gin-gonic/gin"
"github.com/mlogclub/simple/web"
)
func KnowledgeDocumentAnyList(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionKnowledgeDocumentView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
cnd := params.NewPagedSqlCnd(ctx,
params.QueryFilter{ParamName: "knowledgeBaseId"},
params.QueryFilter{ParamName: "title", Op: params.Like},
).Desc("id")
if status, ok := params.GetInt64(ctx, "status"); ok {
cnd.Where("status = ?", status)
} else {
cnd.Where("status != ?", enums.StatusDeleted)
}
if indexStatus, ok := params.Get(ctx, "indexStatus"); ok {
if !enums.IsValidKnowledgeDocumentIndexStatus(indexStatus) {
httpx.WriteJSON(ctx, web.JsonErrorMsg("indexStatus参数不合法"))
return
}
cnd.Where("index_status = ?", indexStatus)
}
list, paging := services.KnowledgeDocumentService.FindPageListByCnd(cnd)
results := make([]response.KnowledgeDocumentListResponse, 0, len(list))
for _, item := range list {
results = append(results, builders.BuildKnowledgeDocumentList(&item))
}
httpx.WriteJSON(ctx, &web.PageResult{Results: results, Page: paging})
}
func KnowledgeDocumentGetBy(ctx *gin.Context) {
id, ok := httpx.GetPathInt64(ctx, "id")
if !ok {
return
}
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionKnowledgeDocumentView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item := services.KnowledgeDocumentService.Get(id)
if item == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("文档不存在"))
return
}
httpx.WriteJSON(ctx, builders.BuildKnowledgeDocument(item))
}
func KnowledgeDocumentPostCreate(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionKnowledgeDocumentCreate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.CreateKnowledgeDocumentRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item, err := services.KnowledgeDocumentService.CreateKnowledgeDocument(req, operator)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, builders.BuildKnowledgeDocument(item))
}
func KnowledgeDocumentPostUpdate(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionKnowledgeDocumentUpdate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.UpdateKnowledgeDocumentRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.KnowledgeDocumentService.UpdateKnowledgeDocument(req, operator); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func KnowledgeDocumentPostDelete(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionKnowledgeDocumentDelete); err != nil {
httpx.WriteJSON(ctx, err)
return
}
var req struct {
ID int64 `json:"id"`
}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.KnowledgeDocumentService.DeleteKnowledgeDocument(req.ID); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
@@ -0,0 +1,108 @@
package dashboard
import (
"cs-agent/internal/builders"
"cs-agent/internal/pkg/constants"
"cs-agent/internal/pkg/dto/request"
"cs-agent/internal/pkg/dto/response"
"cs-agent/internal/pkg/httpx"
"cs-agent/internal/services"
"cs-agent/internal/pkg/httpx/params"
"github.com/gin-gonic/gin"
"github.com/mlogclub/simple/web"
)
func KnowledgeFAQAnyList(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionKnowledgeFAQView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
cnd := params.NewPagedSqlCnd(ctx,
params.QueryFilter{ParamName: "knowledgeBaseId"},
params.QueryFilter{ParamName: "question", Op: params.Like},
params.QueryFilter{ParamName: "indexStatus"},
).Desc("id")
list, paging := services.KnowledgeFAQService.FindPageByCnd(cnd)
results := make([]response.KnowledgeFAQResponse, 0, len(list))
for _, item := range list {
results = append(results, builders.BuildKnowledgeFAQ(&item))
}
httpx.WriteJSON(ctx, &web.PageResult{Results: results, Page: paging})
}
func KnowledgeFAQGetBy(ctx *gin.Context) {
id, ok := httpx.GetPathInt64(ctx, "id")
if !ok {
return
}
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionKnowledgeFAQView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item := services.KnowledgeFAQService.Get(id)
if item == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("FAQ不存在"))
return
}
httpx.WriteJSON(ctx, builders.BuildKnowledgeFAQ(item))
}
func KnowledgeFAQPostCreate(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionKnowledgeFAQCreate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.CreateKnowledgeFAQRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item, err := services.KnowledgeFAQService.CreateKnowledgeFAQ(req, operator)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, builders.BuildKnowledgeFAQ(item))
}
func KnowledgeFAQPostUpdate(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionKnowledgeFAQUpdate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.UpdateKnowledgeFAQRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.KnowledgeFAQService.UpdateKnowledgeFAQ(req, operator); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func KnowledgeFAQPostDelete(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionKnowledgeFAQDelete); err != nil {
httpx.WriteJSON(ctx, err)
return
}
var req struct {
ID int64 `json:"id"`
}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.KnowledgeFAQService.DeleteKnowledgeFAQ(req.ID); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
@@ -0,0 +1,96 @@
package dashboard
import (
"context"
"cs-agent/internal/pkg/httpx"
"cs-agent/internal/ai/rag"
"cs-agent/internal/pkg/constants"
"cs-agent/internal/pkg/dto/request"
"cs-agent/internal/services"
"cs-agent/internal/pkg/httpx/params"
"github.com/gin-gonic/gin"
"github.com/mlogclub/simple/web"
)
func KnowledgeRetrievePostDebugSearch(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionKnowledgeDocumentView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.KnowledgeSearchRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
resp, err := rag.Answer.DebugSearch(context.Background(), req)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, resp)
}
func KnowledgeRetrievePostDebugAnswer(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionKnowledgeDocumentView)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.KnowledgeAnswerRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
resp, err := rag.Answer.DebugAnswer(context.Background(), req, operator)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, resp)
}
func KnowledgeRetrievePostBuild(ctx *gin.Context) {
req := struct {
DocumentID int64 `json:"documentId"`
FAQID int64 `json:"faqId"`
}{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if req.DocumentID > 0 {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionKnowledgeDocumentUpdate); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := rag.Answer.BuildDocumentIndex(context.Background(), req.DocumentID); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
return
}
if req.FAQID > 0 {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionKnowledgeFAQUpdate); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := rag.Index.IndexFAQByID(context.Background(), req.FAQID); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
return
}
httpx.WriteJSON(ctx, web.JsonErrorMsg("documentId或faqId不能为空"))
}
@@ -0,0 +1,82 @@
package dashboard
import (
"cs-agent/internal/builders"
"cs-agent/internal/pkg/constants"
"cs-agent/internal/pkg/dto/response"
"cs-agent/internal/pkg/httpx"
"cs-agent/internal/services"
"cs-agent/internal/pkg/httpx/params"
"github.com/gin-gonic/gin"
"github.com/mlogclub/simple/web"
)
func KnowledgeRetrieveLogAnyList(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionKnowledgeDocumentView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
cnd := params.NewPagedSqlCnd(ctx,
params.QueryFilter{ParamName: "knowledgeBaseId"},
params.QueryFilter{ParamName: "question", Op: params.Like},
params.QueryFilter{ParamName: "channel"},
params.QueryFilter{ParamName: "scene"},
params.QueryFilter{ParamName: "chunkProvider"},
).Desc("id")
if answerStatus, ok := params.GetInt64(ctx, "answerStatus"); ok && answerStatus > 0 {
cnd.Where("answer_status = ?", answerStatus)
}
if rerankEnabled, ok := params.GetInt64(ctx, "rerankEnabled"); ok {
cnd.Where("rerank_enabled = ?", rerankEnabled > 0)
}
queryParams := params.NewQueryParams(ctx)
queryParams.Cnd = *cnd
list, paging := services.KnowledgeRetrieveLogService.FindPageByParams(queryParams)
results := make([]response.KnowledgeRetrieveLogResponse, 0, len(list))
for _, item := range list {
resp := builders.BuildKnowledgeRetrieveLog(&item)
if knowledgeBase := services.KnowledgeBaseService.Get(item.KnowledgeBaseID); knowledgeBase != nil {
resp.KnowledgeBaseName = knowledgeBase.Name
}
results = append(results, resp)
}
httpx.WriteJSON(ctx, &web.PageResult{Results: results, Page: paging})
}
func KnowledgeRetrieveLogGetBy(ctx *gin.Context) {
id, ok := httpx.GetPathInt64(ctx, "id")
if !ok {
return
}
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionKnowledgeDocumentView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
logItem := services.KnowledgeRetrieveLogService.Get(id)
if logItem == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("检索日志不存在"))
return
}
logResp := builders.BuildKnowledgeRetrieveLog(logItem)
if knowledgeBase := services.KnowledgeBaseService.Get(logItem.KnowledgeBaseID); knowledgeBase != nil {
logResp.KnowledgeBaseName = knowledgeBase.Name
}
hits := services.KnowledgeRetrieveLogService.FindHitsByRetrieveLogID(id)
hitResults := make([]response.KnowledgeRetrieveHitResponse, 0, len(hits))
for _, item := range hits {
hitResults = append(hitResults, builders.BuildKnowledgeRetrieveHitResponse(&item))
}
httpx.WriteJSON(ctx, response.KnowledgeRetrieveLogDetailResponse{
Log: logResp,
Hits: hitResults,
})
}
+104
View File
@@ -0,0 +1,104 @@
package dashboard
import (
"context"
"cs-agent/internal/pkg/httpx"
"cs-agent/internal/pkg/constants"
"cs-agent/internal/pkg/dto/request"
"cs-agent/internal/pkg/dto/response"
"cs-agent/internal/services"
"cs-agent/internal/pkg/httpx/params"
"github.com/gin-gonic/gin"
)
func MCPAnyList_servers(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionMCPView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, response.BuildMCPServerInfoResponses(services.MCPDebugService.ListServers()))
}
func MCPAnyCatalog(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionMCPView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
items, err := services.ToolCatalogService.ListMCPTools(context.Background())
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
ret := make([]response.MCPToolCatalogResponse, 0, len(items))
for _, item := range items {
ret = append(ret, response.MCPToolCatalogResponse{
ToolCode: item.ToolCode,
ServerCode: item.ServerCode,
ToolName: item.ToolName,
SourceType: item.SourceType,
AutoInjected: item.AutoInjected,
Title: item.Title,
Description: item.Description,
InputSchema: item.InputSchema,
OutputSchema: item.OutputSchema,
})
}
httpx.WriteJSON(ctx, ret)
}
func MCPPostTest_connection(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionMCPView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.MCPServerDebugRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
result, err := services.MCPDebugService.TestConnection(context.Background(), req.ServerCode)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, response.BuildMCPConnectionResponse(result))
}
func MCPPostList_tools(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionMCPView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.MCPServerDebugRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
result, err := services.MCPDebugService.ListTools(context.Background(), req.ServerCode)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, response.BuildMCPToolInfoResponses(result))
}
func MCPPostCall_tool(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionMCPCall); err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.MCPCallToolRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
result, err := services.MCPDebugService.CallTool(context.Background(), req.ServerCode, req.ToolName, req.Arguments)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, response.BuildMCPCallToolResponse(result))
}
@@ -0,0 +1,87 @@
package dashboard
import (
"cs-agent/internal/pkg/httpx"
"strings"
"cs-agent/internal/builders"
"cs-agent/internal/pkg/constants"
"cs-agent/internal/pkg/dto/request"
"cs-agent/internal/pkg/dto/response"
"cs-agent/internal/pkg/enums"
"cs-agent/internal/services"
"cs-agent/internal/pkg/httpx/params"
"github.com/gin-gonic/gin"
"github.com/mlogclub/simple/web"
)
func NotificationAnyList(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionNotificationView)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
cnd := params.NewPagedSqlCnd(ctx,
params.QueryFilter{ParamName: "type", ColumnName: "notification_type"},
).Eq("recipient_user_id", operator.UserID).
Eq("status", enums.StatusOk).
Desc("id")
switch strings.TrimSpace(ctx.Query("readStatus")) {
case "unread":
cnd.Where("read_at IS NULL")
case "read":
cnd.Where("read_at IS NOT NULL")
}
list, paging := services.NotificationService.FindPageByCnd(cnd)
httpx.WriteJSON(ctx, &web.PageResult{
Results: builders.BuildNotificationList(list),
Page: paging,
})
}
func NotificationGetUnread_count(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionNotificationView)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, &response.NotificationUnreadCountResponse{
UnreadCount: services.NotificationService.CountUnread(operator.UserID),
})
}
func NotificationPostMark_read(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionNotificationUpdate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.MarkNotificationReadRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.NotificationService.MarkRead(req.ID, operator.UserID); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func NotificationPostMark_all_read(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionNotificationUpdate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.NotificationService.MarkAllRead(operator.UserID); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
@@ -0,0 +1,76 @@
package dashboard
import (
"cs-agent/internal/pkg/constants"
"cs-agent/internal/pkg/dto/response"
"cs-agent/internal/pkg/httpx"
"cs-agent/internal/services"
"cs-agent/internal/pkg/httpx/params"
"github.com/gin-gonic/gin"
"github.com/mlogclub/simple/common/strs"
"github.com/mlogclub/simple/web"
)
func PermissionAnyList(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionPermissionView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
cnd := params.NewPagedSqlCnd(ctx,
params.QueryFilter{ParamName: "groupName"},
params.QueryFilter{ParamName: "type"},
params.QueryFilter{ParamName: "status"},
).Desc("id")
if keyword, _ := params.Get(ctx, "keyword"); strs.IsNotBlank(keyword) {
cnd.Where("(name LIKE ? OR code LIKE ?)", "%"+keyword+"%", "%"+keyword+"%")
}
list, paging := services.PermissionService.FindPageByCnd(cnd)
results := make([]response.PermissionResponse, 0, len(list))
for _, item := range list {
results = append(results, response.PermissionResponse{
ID: item.ID,
Name: item.Name,
Code: item.Code,
Type: item.Type,
GroupName: item.GroupName,
Method: item.Method,
ApiPath: item.APIPath,
Status: item.Status,
SortNo: item.SortNo,
})
}
httpx.WriteJSON(ctx, &web.PageResult{Results: results, Page: paging})
}
func PermissionGetBy(ctx *gin.Context) {
id, ok := httpx.GetPathInt64(ctx, "id")
if !ok {
return
}
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionPermissionView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item := services.PermissionService.Get(id)
if item == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("权限不存在"))
return
}
httpx.WriteJSON(ctx, &response.PermissionResponse{
ID: item.ID,
Name: item.Name,
Code: item.Code,
Type: item.Type,
GroupName: item.GroupName,
Method: item.Method,
ApiPath: item.APIPath,
Status: item.Status,
SortNo: item.SortNo,
})
}
@@ -0,0 +1,127 @@
package dashboard
import (
"cs-agent/internal/pkg/constants"
"cs-agent/internal/pkg/dto/request"
"cs-agent/internal/pkg/dto/response"
"cs-agent/internal/pkg/enums"
"cs-agent/internal/pkg/httpx"
"cs-agent/internal/services"
"cs-agent/internal/pkg/httpx/params"
"github.com/gin-gonic/gin"
"github.com/mlogclub/simple/sqls"
"github.com/mlogclub/simple/web"
)
func QuickReplyAnyList(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionQuickReplyView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
cnd := params.NewPagedSqlCnd(ctx,
params.QueryFilter{ParamName: "status"},
params.QueryFilter{ParamName: "groupName"},
params.QueryFilter{ParamName: "title", Op: params.Like},
).Asc("sort_no").Desc("id")
list, paging := services.QuickReplyService.FindPageByCnd(cnd)
results := make([]response.QuickReplyResponse, 0, len(list))
for _, item := range list {
results = append(results, response.QuickReplyResponse{
ID: item.ID,
GroupName: item.GroupName,
Title: item.Title,
Content: item.Content,
Status: item.Status,
SortNo: item.SortNo,
})
}
httpx.WriteJSON(ctx, &web.PageResult{Results: results, Page: paging})
}
func QuickReplyGetList_all(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionQuickReplyView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
list := services.QuickReplyService.Find(sqls.NewCnd().Eq("status", enums.StatusOk).Asc("sort_no").Desc("id"))
results := make([]response.QuickReplyResponse, 0, len(list))
for _, item := range list {
results = append(results, response.QuickReplyResponse{
ID: item.ID,
GroupName: item.GroupName,
Title: item.Title,
Content: item.Content,
Status: item.Status,
SortNo: item.SortNo,
})
}
httpx.WriteJSON(ctx, results)
}
func QuickReplyPostCreate(ctx *gin.Context) {
user, err := services.AuthService.RequirePermission(ctx, constants.PermissionQuickReplyCreate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.CreateQuickReplyRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item, err := services.QuickReplyService.CreateQuickReply(req, user)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, &response.QuickReplyResponse{
ID: item.ID,
GroupName: item.GroupName,
Title: item.Title,
Content: item.Content,
Status: item.Status,
SortNo: item.SortNo,
})
}
func QuickReplyPostUpdate(ctx *gin.Context) {
user, err := services.AuthService.RequirePermission(ctx, constants.PermissionQuickReplyUpdate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.UpdateQuickReplyRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.QuickReplyService.UpdateQuickReply(req, user); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func QuickReplyPostDelete(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionQuickReplyDelete); err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.DeleteQuickReplyRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.QuickReplyService.DeleteQuickReply(req.ID); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
+210
View File
@@ -0,0 +1,210 @@
package dashboard
import (
"cs-agent/internal/pkg/constants"
"cs-agent/internal/pkg/dto/request"
"cs-agent/internal/pkg/dto/response"
"cs-agent/internal/pkg/httpx"
"cs-agent/internal/services"
"cs-agent/internal/pkg/httpx/params"
"github.com/gin-gonic/gin"
"github.com/mlogclub/simple/sqls"
"github.com/mlogclub/simple/web"
)
func RoleAnyList(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionRoleView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
cnd := params.NewPagedSqlCnd(ctx,
params.QueryFilter{ParamName: "status"},
params.QueryFilter{ParamName: "code", Op: params.Like},
).Asc("sort_no").Desc("id")
list, paging := services.RoleService.FindPageByCnd(cnd)
results := make([]response.RoleResponse, 0, len(list))
for _, item := range list {
results = append(results, response.RoleResponse{
ID: item.ID,
Name: item.Name,
Code: item.Code,
Status: item.Status,
IsSystem: item.IsSystem,
SortNo: item.SortNo,
})
}
httpx.WriteJSON(ctx, &web.PageResult{Results: results, Page: paging})
}
func RoleGetList_all(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionRoleView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
list := services.RoleService.Find(sqls.NewCnd().Asc("sort_no").Desc("id"))
results := make([]response.RoleResponse, 0, len(list))
for _, item := range list {
results = append(results, response.RoleResponse{
ID: item.ID,
Name: item.Name,
Code: item.Code,
Status: item.Status,
IsSystem: item.IsSystem,
SortNo: item.SortNo,
})
}
httpx.WriteJSON(ctx, results)
}
func RoleGetBy(ctx *gin.Context) {
id, ok := httpx.GetPathInt64(ctx, "id")
if !ok {
return
}
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionRoleView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item := services.RoleService.Get(id)
if item == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("角色不存在"))
return
}
permissionCodes := make([]string, 0)
list := services.RolePermissionService.Find(sqls.NewCnd().Eq("role_id", item.ID))
for _, relation := range list {
permission := services.PermissionService.Get(relation.PermissionID)
if permission != nil {
permissionCodes = append(permissionCodes, permission.Code)
}
}
httpx.WriteJSON(ctx, &response.RoleResponse{
ID: item.ID,
Name: item.Name,
Code: item.Code,
Status: item.Status,
IsSystem: item.IsSystem,
SortNo: item.SortNo,
Permissions: permissionCodes,
})
}
func RolePostCreate(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionRoleCreate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.CreateRoleRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
role, err := services.RoleService.CreateRole(req, operator)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, &response.RoleResponse{
ID: role.ID,
Name: role.Name,
Code: role.Code,
Status: role.Status,
IsSystem: role.IsSystem,
SortNo: role.SortNo,
})
}
func RolePostUpdate(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionRoleUpdate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.UpdateRoleRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.RoleService.UpdateRole(req, operator); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func RolePostDelete(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionRoleDelete); err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.DeleteRoleRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.RoleService.DeleteRole(req.ID); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func RolePostUpdate_status(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionRoleUpdate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.UpdateRoleStatusRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.RoleService.UpdateStatus(req.ID, req.Status, operator); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func RolePostAssign_permission(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionRoleAssignPermission)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.AssignPermissionRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.RoleService.AssignPermissions(req.RoleID, req.PermissionIDs, operator); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func RolePostUpdate_sort(ctx *gin.Context) {
var ids []int64
if err := params.ReadJSON(ctx, &ids); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.RoleService.UpdateSort(ids); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
@@ -0,0 +1,86 @@
package dashboard
import (
"cs-agent/internal/pkg/constants"
"cs-agent/internal/pkg/dto/request"
"cs-agent/internal/pkg/dto/response"
"cs-agent/internal/pkg/httpx"
"cs-agent/internal/pkg/utils"
"cs-agent/internal/services"
"time"
"cs-agent/internal/pkg/httpx/params"
"github.com/gin-gonic/gin"
"github.com/mlogclub/simple/web"
)
func SessionAnyList(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionSessionView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
cnd := params.NewPagedSqlCnd(ctx,
params.QueryFilter{ParamName: "userId"},
params.QueryFilter{ParamName: "clientType"},
).Desc("id")
list, paging := services.LoginSessionService.FindPageByCnd(cnd)
results := make([]response.SessionResponse, 0, len(list))
for _, item := range list {
username := ""
if user := services.UserService.Get(item.UserID); user != nil {
username = user.Username
}
results = append(results, response.SessionResponse{
ID: item.ID,
UserID: item.UserID,
Username: username,
ClientType: item.ClientType,
ClientIP: item.ClientIP,
UserAgent: item.UserAgent,
ExpiredAt: item.ExpiredAt.Format(time.DateTime),
RevokedAt: utils.FormatTimePtr(item.RevokedAt),
LastSeenAt: utils.FormatTimePtr(item.LastSeenAt),
})
}
httpx.WriteJSON(ctx, &web.PageResult{Results: results, Page: paging})
}
func SessionPostRevoke(ctx *gin.Context) {
user, err := services.AuthService.RequirePermission(ctx, constants.PermissionSessionRevoke)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.RevokeSessionRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.LoginSessionService.Revoke(req.ID, user.UserID, user.Username); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func SessionPostRevokeByUser(ctx *gin.Context) {
user, err := services.AuthService.RequirePermission(ctx, constants.PermissionSessionRevoke)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.RevokeUserSessionsRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.LoginSessionService.RevokeByUser(req.UserID, user.UserID, user.Username); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
@@ -0,0 +1,274 @@
package dashboard
import (
"context"
"cs-agent/internal/pkg/httpx"
"strings"
"time"
"cs-agent/internal/builders"
"cs-agent/internal/pkg/constants"
"cs-agent/internal/pkg/dto/request"
"cs-agent/internal/pkg/dto/response"
"cs-agent/internal/pkg/enums"
"cs-agent/internal/services"
"cs-agent/internal/pkg/httpx/params"
"github.com/gin-gonic/gin"
"github.com/mlogclub/simple/web"
)
func SkillDefinitionAnyList(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionSkillDefinitionView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
cnd := params.NewPagedSqlCnd(ctx,
params.QueryFilter{ParamName: "status"},
params.QueryFilter{ParamName: "name", Op: params.Like},
params.QueryFilter{ParamName: "code", Op: params.Like},
).Desc("id")
if _, ok := params.Get(ctx, "status"); !ok {
cnd.Where("status <> ?", enums.StatusDeleted)
}
list, paging := services.SkillDefinitionService.FindPageByCnd(cnd)
results := make([]response.SkillDefinitionResponse, 0, len(list))
for _, item := range list {
results = append(results, builders.BuildSkillDefinitionResponse(&item))
}
httpx.WriteJSON(ctx, &web.PageResult{Results: results, Page: paging})
}
func SkillDefinitionGetList_all(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionSkillDefinitionView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
cnd := params.NewSqlCnd(ctx,
params.QueryFilter{ParamName: "status"},
).Desc("id")
if status, ok := params.Get(ctx, "status"); !ok || strings.TrimSpace(status) == "" {
cnd.Where("status <> ?", enums.StatusDeleted)
}
list := services.SkillDefinitionService.Find(cnd)
results := make([]response.SkillDefinitionResponse, 0, len(list))
for _, item := range list {
results = append(results, builders.BuildSkillDefinitionResponse(&item))
}
httpx.WriteJSON(ctx, results)
}
func SkillDefinitionGetBy(ctx *gin.Context) {
id, ok := httpx.GetPathInt64(ctx, "id")
if !ok {
return
}
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionSkillDefinitionView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item := services.SkillDefinitionService.Get(id)
if item == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("Skill 不存在"))
return
}
httpx.WriteJSON(ctx, builders.BuildSkillDefinitionResponse(item))
}
func SkillDefinitionPostCreate(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionSkillDefinitionCreate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.CreateSkillDefinitionRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item, err := services.SkillDefinitionService.CreateSkillDefinition(req, operator)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, builders.BuildSkillDefinitionResponse(item))
}
func SkillDefinitionPostUpdate(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionSkillDefinitionUpdate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.UpdateSkillDefinitionRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.SkillDefinitionService.UpdateSkillDefinition(req, operator); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func SkillDefinitionPostUpdate_status(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionSkillDefinitionUpdate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.UpdateSkillDefinitionStatusRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if req.ID <= 0 {
httpx.WriteJSON(ctx, web.JsonErrorMsg("Skill ID 不合法"))
return
}
if !enums.IsValidStatus(req.Status) {
httpx.WriteJSON(ctx, web.JsonErrorMsg("状态值不合法"))
return
}
item := services.SkillDefinitionService.Get(req.ID)
if item == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("Skill 不存在"))
return
}
if item.Status == enums.StatusDeleted {
httpx.WriteJSON(ctx, web.JsonErrorMsg("已删除的 Skill 不能直接修改状态,请先恢复"))
return
}
if req.Status == int(enums.StatusDeleted) {
httpx.WriteJSON(ctx, web.JsonErrorMsg("请使用删除接口处理删除状态"))
return
}
if err := services.SkillDefinitionService.Updates(req.ID, map[string]any{
"status": req.Status,
"update_user_id": operator.UserID,
"update_user_name": operator.Username,
"updated_at": time.Now(),
}); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func SkillDefinitionPostDelete(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionSkillDefinitionDelete)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.DeleteSkillDefinitionRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if req.ID <= 0 {
httpx.WriteJSON(ctx, web.JsonErrorMsg("Skill ID 不合法"))
return
}
if services.SkillDefinitionService.Get(req.ID) == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("Skill 不存在"))
return
}
if err := services.SkillDefinitionService.Updates(req.ID, map[string]any{
"status": enums.StatusDeleted,
"update_user_id": operator.UserID,
"update_user_name": operator.Username,
"updated_at": time.Now(),
}); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func SkillDefinitionPostRestore(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionSkillDefinitionDelete)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.RestoreSkillDefinitionRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if req.ID <= 0 {
httpx.WriteJSON(ctx, web.JsonErrorMsg("Skill ID 不合法"))
return
}
item := services.SkillDefinitionService.Get(req.ID)
if item == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("Skill 不存在"))
return
}
if item.Status != enums.StatusDeleted {
httpx.WriteJSON(ctx, web.JsonErrorMsg("仅已删除的 Skill 支持恢复"))
return
}
if err := services.SkillDefinitionService.Updates(req.ID, map[string]any{
"status": enums.StatusDisabled,
"update_user_id": operator.UserID,
"update_user_name": operator.Username,
"updated_at": time.Now(),
}); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func SkillDefinitionPostDebug_run(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionSkillDefinitionView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.SkillDebugRunRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
resp, err := services.SkillRuntimeService.DebugRun(context.Background(), req)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, resp)
}
func SkillDefinitionPostDebug_resume(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionSkillDefinitionView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.SkillDebugResumeRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
resp, err := services.SkillRuntimeService.DebugResume(context.Background(), req)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, resp)
}
+147
View File
@@ -0,0 +1,147 @@
package dashboard
import (
"cs-agent/internal/builders"
"cs-agent/internal/pkg/constants"
"cs-agent/internal/pkg/dto/request"
"cs-agent/internal/pkg/httpx"
"cs-agent/internal/services"
"cs-agent/internal/pkg/httpx/params"
"github.com/gin-gonic/gin"
"github.com/mlogclub/simple/web"
)
func TagAnyList(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionTagView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
list, paging := services.TagService.FindPageByCnd(params.NewPagedSqlCnd(ctx,
params.QueryFilter{ParamName: "status"},
params.QueryFilter{ParamName: "parentId"},
params.QueryFilter{ParamName: "name", Op: params.Like},
).Asc("sort_no").Desc("id"))
results := builders.BuildTagResponses(list)
httpx.WriteJSON(ctx, &web.PageResult{Results: results, Page: paging})
}
func TagGetList_all(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionTagView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
list := services.TagService.FindAll()
results := builders.BuildTagTreeResponses(list)
httpx.WriteJSON(ctx, results)
}
func TagGetBy(ctx *gin.Context) {
id, ok := httpx.GetPathInt64(ctx, "id")
if !ok {
return
}
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionTagView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item := services.TagService.Get(id)
if item == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("标签不存在"))
return
}
result := builders.BuildTagResponse(item)
httpx.WriteJSON(ctx, &result)
}
func TagPostCreate(ctx *gin.Context) {
user, err := services.AuthService.RequirePermission(ctx, constants.PermissionTagCreate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.CreateTagRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item, err := services.TagService.CreateTag(req, user)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
result := builders.BuildTagResponse(item)
httpx.WriteJSON(ctx, &result)
}
func TagPostUpdate(ctx *gin.Context) {
user, err := services.AuthService.RequirePermission(ctx, constants.PermissionTagUpdate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.UpdateTagRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.TagService.UpdateTag(req, user); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func TagPostDelete(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionTagDelete); err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.DeleteTagRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.TagService.DeleteTag(req.ID); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func TagPostUpdate_sort(ctx *gin.Context) {
var ids []int64
if err := params.ReadJSON(ctx, &ids); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.TagService.UpdateSort(ids); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func TagPostUpdate_status(ctx *gin.Context) {
user, err := services.AuthService.RequirePermission(ctx, constants.PermissionTagUpdate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.UpdateTagStatusRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.TagService.UpdateStatus(req.ID, req.Status, user); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
@@ -0,0 +1,287 @@
package dashboard
import (
"cs-agent/internal/pkg/httpx"
"strings"
"cs-agent/internal/builders"
"cs-agent/internal/pkg/constants"
"cs-agent/internal/pkg/dto/request"
"cs-agent/internal/services"
"cs-agent/internal/pkg/httpx/params"
"github.com/gin-gonic/gin"
"github.com/mlogclub/simple/sqls"
"github.com/mlogclub/simple/web"
)
func TicketAnyList(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionTicketView)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
cnd := params.NewPagedSqlCnd(ctx,
params.QueryFilter{ParamName: "status"},
params.QueryFilter{ParamName: "currentAssigneeId"},
params.QueryFilter{ParamName: "customerId"},
params.QueryFilter{ParamName: "conversationId"},
params.QueryFilter{ParamName: "source"},
params.QueryFilter{ParamName: "channel"},
).Desc("updated_at").Desc("id")
if keyword, _ := params.Get(ctx, "keyword"); strings.TrimSpace(keyword) != "" {
keyword = "%" + strings.TrimSpace(keyword) + "%"
cnd.Where("ticket_no LIKE ? OR title LIKE ? OR description LIKE ?", keyword, keyword, keyword)
}
if tagID, _ := params.GetInt64(ctx, "tagId"); tagID > 0 {
cnd.Where("id IN (SELECT ticket_id FROM t_ticket_tag WHERE tag_id = ?)", tagID)
}
if mine, _ := params.Get(ctx, "mine"); mine == "1" || strings.EqualFold(mine, "true") {
cnd.Eq("current_assignee_id", operator.UserID)
}
if unassigned, _ := params.Get(ctx, "unassigned"); unassigned == "1" || strings.EqualFold(unassigned, "true") {
cnd.Eq("current_assignee_id", 0)
}
if staleHoursValue, _ := params.Get(ctx, "staleHours"); strings.TrimSpace(staleHoursValue) != "" {
staleHours, _ := params.GetInt(ctx, "staleHours")
services.TicketService.ApplyStaleFilter(cnd, staleHours)
}
aggregate, err := services.TicketService.FindPageAggregateByCnd(cnd, operator.UserID)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, &web.PageResult{
Results: builders.BuildTicketListWithContext(aggregate.List, &builders.TicketBuildContext{
TagsByTicketID: aggregate.TagsByTicketID,
Users: aggregate.Users,
Customers: aggregate.Customers,
}),
Page: aggregate.Paging,
})
}
func TicketAnySummary(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionTicketView)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
staleHours, _ := params.GetInt(ctx, "staleHours")
httpx.WriteJSON(ctx, builders.BuildTicketSummary(services.TicketService.GetSummary(operator, staleHours)))
}
func TicketAnyView_list(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionTicketView)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, builders.BuildTicketViewList(services.TicketViewService.ListByUser(operator.UserID)))
}
func TicketPostSave_view(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionTicketView)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.SaveTicketViewRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item, err := services.TicketViewService.Save(req, operator)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, builders.BuildTicketView(item))
}
func TicketPostDelete_view(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionTicketView)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.DeleteTicketViewRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.TicketViewService.Delete(req.ID, operator); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func TicketGetBy(ctx *gin.Context) {
id, ok := httpx.GetPathInt64(ctx, "id")
if !ok {
return
}
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionTicketView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
detail, err := services.TicketService.GetDetail(id)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, builders.BuildTicketDetail(detail))
}
func TicketPostCreate(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionTicketCreate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.CreateTicketRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item, err := services.TicketService.CreateTicket(req, operator)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, builders.BuildTicket(item))
}
func TicketPostCreate_from_conversation(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionTicketCreate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.CreateTicketFromConversationRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item, err := services.TicketService.CreateFromConversation(req, operator)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, builders.BuildTicket(item))
}
func TicketPostUpdate(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionTicketUpdate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.UpdateTicketRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.TicketService.UpdateTicket(req, operator); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func TicketPostLink_customer(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionTicketUpdate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.LinkTicketCustomerRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.TicketService.LinkCustomer(req.TicketID, req.CustomerID, operator); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func TicketPostAssign(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionTicketAssign)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.AssignTicketRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.TicketService.AssignTicket(req, operator); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func TicketPostChange_status(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionTicketChangeStatus)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.ChangeTicketStatusRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.TicketService.ChangeStatus(req, operator); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func TicketAnyProgressList(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionTicketView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
ticketID, _ := params.GetInt64(ctx, "ticketId")
if ticketID <= 0 {
httpx.WriteJSON(ctx, web.JsonErrorMsg("工单不存在"))
return
}
if services.TicketService.Get(ticketID) == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("工单不存在"))
return
}
progresses := services.TicketProgressService.Find(sqls.NewCnd().Eq("ticket_id", ticketID).Asc("id"))
httpx.WriteJSON(ctx, builders.BuildTicketProgressList(progresses))
}
func TicketPostProgressCreate(ctx *gin.Context) {
ticketCreateProgress(ctx)
}
func ticketCreateProgress(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionTicketProgress)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.CreateTicketProgressRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item, err := services.TicketService.AddProgress(req, operator)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, builders.BuildTicketProgress(item))
}
+227
View File
@@ -0,0 +1,227 @@
package dashboard
import (
"cs-agent/internal/builders"
"cs-agent/internal/pkg/constants"
"cs-agent/internal/pkg/dto/request"
"cs-agent/internal/pkg/dto/response"
"cs-agent/internal/pkg/enums"
"cs-agent/internal/pkg/httpx"
"cs-agent/internal/services"
"cs-agent/internal/pkg/httpx/params"
"github.com/gin-gonic/gin"
"github.com/mlogclub/simple/web"
)
func UserAnyList(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionUserView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
cnd := params.NewPagedSqlCnd(ctx,
params.QueryFilter{ParamName: "status"},
params.QueryFilter{ParamName: "username", Op: params.Like},
params.QueryFilter{ParamName: "nickname", Op: params.Like},
).Desc("id")
cnd.Where("status <> ?", enums.StatusDeleted)
list, paging := services.UserService.FindPageByCnd(cnd)
results := builders.BuildUserList(list, builders.UserBuildOptions{
Roles: true,
Permissions: false,
})
httpx.WriteJSON(ctx, &web.PageResult{Results: results, Page: paging})
}
func UserAnyList_all(ctx *gin.Context) {
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionUserView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
cnd := params.NewSqlCnd(ctx,
params.QueryFilter{ParamName: "status"},
params.QueryFilter{ParamName: "username", Op: params.Like},
params.QueryFilter{ParamName: "nickname", Op: params.Like},
).Desc("id")
cnd.Where("status <> ?", enums.StatusDeleted)
list := services.UserService.Find(cnd)
results := builders.BuildUserList(list, builders.UserBuildOptions{
Roles: true,
Permissions: false,
})
httpx.WriteJSON(ctx, results)
}
func UserGetBy(ctx *gin.Context) {
id, ok := httpx.GetPathInt64(ctx, "id")
if !ok {
return
}
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionUserView); err != nil {
httpx.WriteJSON(ctx, err)
return
}
item := services.UserService.Get(id)
if item == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("用户不存在"))
return
}
httpx.WriteJSON(ctx, builders.BuildUserResponse(item, builders.UserBuildOptions{
Roles: true,
Permissions: true,
}))
}
func UserPostCreate(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionUserCreate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.CreateUserRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
user, generatedPassword, err := services.UserService.CreateUser(req, operator)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, &response.CreateUserResultResponse{
User: builders.BuildUserResponse(user, builders.UserBuildOptions{Roles: true, Permissions: true}),
Password: generatedPassword,
})
}
func UserPostUpdate(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionUserUpdate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.UpdateUserRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.UserService.UpdateUser(req, operator); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func UserPostDelete(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionUserDelete)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.DeleteUserRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.UserService.DeleteUser(req.ID, operator); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func UserPostUpdate_status(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionUserUpdate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.UpdateUserStatusRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.UserService.UpdateStatus(req.ID, req.Status, operator); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func UserPostReset_password(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionUserUpdate)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
var req struct {
UserID int64 `json:"userId"`
}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
password, err := services.UserService.ResetPassword(req.UserID, operator)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, map[string]any{
"password": password,
})
}
func UserPostChange_password(ctx *gin.Context) {
principal := services.AuthService.GetAuthPrincipal(ctx)
if principal == nil {
if _, err := services.AuthService.Authenticate(ctx); err != nil {
httpx.WriteJSON(ctx, err)
return
}
principal = services.AuthService.GetAuthPrincipal(ctx)
}
if principal == nil {
httpx.WriteJSON(ctx, web.JsonErrorMsg("未登录或登录已过期"))
return
}
req := request.ChangePasswordRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.UserService.ChangeOwnPassword(req.Password, principal); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
func UserPostAssign_role(ctx *gin.Context) {
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionUserAssignRole)
if err != nil {
httpx.WriteJSON(ctx, err)
return
}
req := request.AssignRoleRequest{}
if err := params.ReadJSON(ctx, &req); err != nil {
httpx.WriteJSON(ctx, err)
return
}
if err := services.UserService.AssignRoles(req.UserID, req.RoleIDs, operator); err != nil {
httpx.WriteJSON(ctx, err)
return
}
httpx.WriteJSON(ctx, nil)
}
+64
View File
@@ -0,0 +1,64 @@
package third
import (
"cs-agent/internal/pkg/httpx/params"
"cs-agent/internal/wxwork"
"io"
"net/http"
"github.com/gin-gonic/gin"
"github.com/silenceper/wechat/v2/work/kf"
)
// GetCallback GET请求用于校验回调是否配置正确
func WechatGetCallback(ctx *gin.Context) {
cli, err := wxwork.GetWorkCli().GetKF()
if err != nil {
ctx.AbortWithError(http.StatusInternalServerError, err)
return
}
options := kf.SignatureOptions{}
if err := params.ReadForm(ctx, &options); err != nil {
ctx.AbortWithError(http.StatusUnauthorized, err)
return
}
// 调用VerifyURL方法校验当前请求,如果合法则把解密后的内容作为响应返回给微信服务器
echo, err := cli.VerifyURL(options)
if err == nil {
ctx.String(http.StatusOK, echo)
} else {
ctx.AbortWithError(http.StatusUnauthorized, err)
}
}
// PostCallback POST请求用于接收回调
func WechatPostCallback(ctx *gin.Context) {
cli, err := wxwork.GetWorkCli().GetKF()
if err != nil {
ctx.AbortWithError(http.StatusInternalServerError, err)
return
}
var (
message kf.CallbackMessage
body []byte
)
// 读取原始消息内容
body, err = io.ReadAll(ctx.Request.Body)
if err != nil {
ctx.AbortWithError(http.StatusInternalServerError, err)
return
}
// 解析原始数据
message, err = cli.GetCallbackMessage(body)
if err != nil {
ctx.AbortWithError(http.StatusInternalServerError, err)
return
}
if err := wxwork.ConsumeCallback(message); err != nil {
ctx.AbortWithError(http.StatusInternalServerError, err)
return
}
ctx.String(http.StatusOK, "ok")
}