复制项目
This commit is contained in:
904
internal/api/chat/chat.go
Normal file
904
internal/api/chat/chat.go
Normal file
@@ -0,0 +1,904 @@
|
||||
// Copyright © 2023 OpenIM open source community. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package chat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
apiuserutil "git.imall.cloud/openim/chat/internal/api/util"
|
||||
|
||||
"strconv"
|
||||
|
||||
"git.imall.cloud/openim/chat/pkg/common/apistruct"
|
||||
"git.imall.cloud/openim/chat/pkg/common/imapi"
|
||||
"git.imall.cloud/openim/chat/pkg/common/mctx"
|
||||
"git.imall.cloud/openim/chat/pkg/eerrs"
|
||||
"git.imall.cloud/openim/chat/pkg/protocol/admin"
|
||||
chatpb "git.imall.cloud/openim/chat/pkg/protocol/chat"
|
||||
constantpb "git.imall.cloud/openim/protocol/constant"
|
||||
"git.imall.cloud/openim/protocol/sdkws"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"github.com/openimsdk/tools/a2r"
|
||||
"github.com/openimsdk/tools/apiresp"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/log"
|
||||
)
|
||||
|
||||
func New(chatClient chatpb.ChatClient, adminClient admin.AdminClient, imApiCaller imapi.CallerInterface, api *apiuserutil.Api) *Api {
|
||||
return &Api{
|
||||
Api: api,
|
||||
chatClient: chatClient,
|
||||
adminClient: adminClient,
|
||||
imApiCaller: imApiCaller,
|
||||
}
|
||||
}
|
||||
|
||||
type Api struct {
|
||||
*apiuserutil.Api
|
||||
chatClient chatpb.ChatClient
|
||||
adminClient admin.AdminClient
|
||||
imApiCaller imapi.CallerInterface
|
||||
}
|
||||
|
||||
// operationIDKey 用于 context.WithValue 的自定义类型,避免使用字符串导致类型冲突
|
||||
type operationIDKey struct{}
|
||||
|
||||
// ################## ACCOUNT ##################
|
||||
|
||||
func (o *Api) SendVerifyCode(c *gin.Context) {
|
||||
req, err := a2r.ParseRequest[chatpb.SendVerifyCodeReq](c)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
ip, err := o.GetClientIP(c)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
req.Ip = ip
|
||||
resp, err := o.chatClient.SendVerifyCode(c, req)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
apiresp.GinSuccess(c, resp)
|
||||
}
|
||||
|
||||
func (o *Api) VerifyCode(c *gin.Context) {
|
||||
a2r.Call(c, chatpb.ChatClient.VerifyCode, o.chatClient)
|
||||
}
|
||||
|
||||
func (o *Api) GetCaptchaImage(c *gin.Context) {
|
||||
// 获取或生成operationID(图片请求可能没有operationID header)
|
||||
operationID := c.GetHeader("operationID")
|
||||
if operationID == "" {
|
||||
// 如果没有operationID,生成一个UUID
|
||||
operationID = uuid.New().String()
|
||||
}
|
||||
|
||||
// 将operationID设置到context中,确保RPC调用能获取到
|
||||
// 使用自定义类型作为 key,避免使用字符串导致类型冲突
|
||||
ctx := context.WithValue(c.Request.Context(), operationIDKey{}, operationID)
|
||||
|
||||
// 调用RPC方法获取验证码(6位数字)
|
||||
resp, err := o.chatClient.GetCaptchaImage(ctx, &chatpb.GetCaptchaImageReq{})
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 在API层生成验证码图片
|
||||
imgBytes, err := apiuserutil.GenerateCaptchaImageFromCode(resp.Code)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 验证图片数据
|
||||
if len(imgBytes) == 0 {
|
||||
apiresp.GinError(c, errs.ErrInternalServer.WrapMsg("generated captcha image is empty"))
|
||||
return
|
||||
}
|
||||
|
||||
// 检查GIF文件头
|
||||
if len(imgBytes) < 6 {
|
||||
apiresp.GinError(c, errs.ErrInternalServer.WrapMsg("generated captcha image data too short"))
|
||||
return
|
||||
}
|
||||
|
||||
// 设置响应头,返回GIF图片
|
||||
c.Header("Content-Type", "image/gif")
|
||||
c.Header("Content-Length", fmt.Sprintf("%d", len(imgBytes)))
|
||||
c.Header("Cache-Control", "no-cache, no-store, must-revalidate")
|
||||
c.Header("Pragma", "no-cache")
|
||||
c.Header("Expires", "0")
|
||||
// 将验证码ID放在响应头中,客户端可用于后续验证
|
||||
c.Header("X-Captcha-ID", resp.CaptchaID)
|
||||
|
||||
// 返回图片数据
|
||||
c.Data(200, "image/gif", imgBytes)
|
||||
}
|
||||
|
||||
func (o *Api) RegisterUser(c *gin.Context) {
|
||||
req, err := a2r.ParseRequest[chatpb.RegisterUserReq](c)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
ip, err := o.GetClientIP(c)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
req.Ip = ip
|
||||
|
||||
imToken, err := o.imApiCaller.ImAdminTokenWithDefaultAdmin(c)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
apiCtx := mctx.WithApiToken(c, imToken)
|
||||
rpcCtx := o.WithAdminUser(c)
|
||||
|
||||
checkResp, err := o.chatClient.CheckUserExist(rpcCtx, &chatpb.CheckUserExistReq{User: req.User})
|
||||
if err != nil {
|
||||
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
if checkResp.IsRegistered {
|
||||
isUserNotExist, err := o.imApiCaller.AccountCheckSingle(apiCtx, checkResp.Userid)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
// if User is not exist in SDK server. You need delete this user and register new user again.
|
||||
if isUserNotExist {
|
||||
_, err := o.chatClient.DelUserAccount(rpcCtx, &chatpb.DelUserAccountReq{UserIDs: []string{checkResp.Userid}})
|
||||
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// H5注册场景(提供了registerToken)默认自动登录
|
||||
if req.RegisterToken != "" && !req.AutoLogin {
|
||||
req.AutoLogin = true
|
||||
}
|
||||
|
||||
respRegisterUser, err := o.chatClient.RegisterUser(c, req)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
userInfo := &sdkws.UserInfo{
|
||||
UserID: respRegisterUser.UserID,
|
||||
Nickname: req.User.Nickname,
|
||||
FaceURL: req.User.FaceURL,
|
||||
CreateTime: time.Now().UnixMilli(),
|
||||
UserType: 0, // 设置默认用户类型
|
||||
UserFlag: "",
|
||||
}
|
||||
err = o.imApiCaller.RegisterUser(apiCtx, []*sdkws.UserInfo{userInfo})
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if resp, err := o.adminClient.FindDefaultFriend(rpcCtx, &admin.FindDefaultFriendReq{}); err == nil {
|
||||
_ = o.imApiCaller.ImportFriend(apiCtx, respRegisterUser.UserID, resp.UserIDs)
|
||||
}
|
||||
if resp, err := o.adminClient.FindDefaultGroup(rpcCtx, &admin.FindDefaultGroupReq{}); err == nil {
|
||||
_ = o.imApiCaller.InviteToGroup(apiCtx, respRegisterUser.UserID, resp.GroupIDs)
|
||||
}
|
||||
var resp apistruct.UserRegisterResp
|
||||
if req.AutoLogin {
|
||||
resp.ImToken, err = o.imApiCaller.GetUserToken(apiCtx, respRegisterUser.UserID, req.Platform)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
resp.ChatToken = respRegisterUser.ChatToken
|
||||
resp.UserID = respRegisterUser.UserID
|
||||
apiresp.GinSuccess(c, &resp)
|
||||
}
|
||||
|
||||
func (o *Api) Login(c *gin.Context) {
|
||||
req, err := a2r.ParseRequest[chatpb.LoginReq](c)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
ip, err := o.GetClientIP(c)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
req.Ip = ip
|
||||
|
||||
resp, err := o.chatClient.Login(c, req)
|
||||
|
||||
if err != nil {
|
||||
// 调试日志:打印错误的详细信息
|
||||
errStr := err.Error()
|
||||
log.ZError(c, "Login error detected", err,
|
||||
"errorString", errStr,
|
||||
"errorType", fmt.Sprintf("%T", err),
|
||||
"contains20015", strings.Contains(errStr, "20015"),
|
||||
"containsAccountBlocked", strings.Contains(errStr, "AccountBlocked"),
|
||||
"contains封禁", strings.Contains(errStr, "账户已被封禁"))
|
||||
|
||||
// 检查是否是账户被封禁的错误,确保返回明确的错误提示
|
||||
// 需要检查错误字符串中是否包含封禁相关的关键词,即使错误被包装了
|
||||
if strings.Contains(errStr, "20015") || strings.Contains(errStr, "AccountBlocked") || strings.Contains(errStr, "账户已被封禁") {
|
||||
log.ZInfo(c, "Detected AccountBlocked error, returning explicit error", "originalError", errStr)
|
||||
apiresp.GinError(c, eerrs.ErrAccountBlocked.WrapMsg("账户已被封禁"))
|
||||
return
|
||||
}
|
||||
|
||||
// 如果错误被包装成 ServerInternalError,尝试从错误链中提取原始错误
|
||||
// 检查错误消息中是否包含封禁相关的堆栈信息
|
||||
if strings.Contains(errStr, "ServerInternalError") && (strings.Contains(errStr, "20015") || strings.Contains(errStr, "AccountBlocked")) {
|
||||
log.ZInfo(c, "Detected AccountBlocked error in wrapped ServerInternalError, returning explicit error", "originalError", errStr)
|
||||
apiresp.GinError(c, eerrs.ErrAccountBlocked.WrapMsg("账户已被封禁"))
|
||||
return
|
||||
}
|
||||
|
||||
log.ZError(c, "Login error not AccountBlocked, returning original error", err)
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
adminToken, err := o.imApiCaller.ImAdminTokenWithDefaultAdmin(c)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
apiCtx := mctx.WithApiToken(c, adminToken)
|
||||
imToken, err := o.imApiCaller.GetUserToken(apiCtx, resp.UserID, req.Platform)
|
||||
if err != nil {
|
||||
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
apiresp.GinSuccess(c, &apistruct.LoginResp{
|
||||
ImToken: imToken,
|
||||
UserID: resp.UserID,
|
||||
ChatToken: resp.ChatToken,
|
||||
})
|
||||
}
|
||||
|
||||
func (o *Api) ResetPassword(c *gin.Context) {
|
||||
a2r.Call(c, chatpb.ChatClient.ResetPassword, o.chatClient)
|
||||
}
|
||||
|
||||
func (o *Api) ChangePassword(c *gin.Context) {
|
||||
req, err := a2r.ParseRequest[chatpb.ChangePasswordReq](c)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
resp, err := o.chatClient.ChangePassword(c, req)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
imToken, err := o.imApiCaller.ImAdminTokenWithDefaultAdmin(c)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
err = o.imApiCaller.ForceOffLine(mctx.WithApiToken(c, imToken), req.UserID)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
apiresp.GinSuccess(c, resp)
|
||||
}
|
||||
|
||||
// ################## USER ##################
|
||||
|
||||
func (o *Api) UpdateUserInfo(c *gin.Context) {
|
||||
req, err := a2r.ParseRequest[chatpb.UpdateUserInfoReq](c)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 检查req是否为nil
|
||||
if req == nil {
|
||||
apiresp.GinError(c, errs.ErrArgs.WrapMsg("request is nil"))
|
||||
return
|
||||
}
|
||||
|
||||
// 检查必要参数
|
||||
if req.UserID == "" {
|
||||
apiresp.GinError(c, errs.ErrArgs.WrapMsg("userID is required"))
|
||||
return
|
||||
}
|
||||
|
||||
respUpdate, err := o.chatClient.UpdateUserInfo(c, req)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
var imToken string
|
||||
imToken, err = o.imApiCaller.ImAdminTokenWithDefaultAdmin(c)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 检查token是否为空
|
||||
if imToken == "" {
|
||||
apiresp.GinError(c, errs.ErrInternalServer.WrapMsg("failed to get admin token"))
|
||||
return
|
||||
}
|
||||
|
||||
// 检查imApiCaller是否为nil
|
||||
if o.imApiCaller == nil {
|
||||
apiresp.GinError(c, errs.ErrInternalServer.WrapMsg("imApiCaller is nil"))
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
nickName string
|
||||
faceURL string
|
||||
userFlag string
|
||||
)
|
||||
if req.Nickname != nil {
|
||||
nickName = req.Nickname.Value
|
||||
} else {
|
||||
nickName = respUpdate.NickName
|
||||
}
|
||||
if req.FaceURL != nil {
|
||||
faceURL = req.FaceURL.Value
|
||||
} else {
|
||||
faceURL = respUpdate.FaceUrl
|
||||
}
|
||||
// 处理UserFlag字段
|
||||
if req.UserFlag != nil {
|
||||
userFlag = req.UserFlag.Value
|
||||
}
|
||||
|
||||
// 确保字符串参数不为nil(虽然这里应该是安全的,但添加额外保护)
|
||||
if nickName == "" {
|
||||
nickName = ""
|
||||
}
|
||||
if faceURL == "" {
|
||||
faceURL = ""
|
||||
}
|
||||
|
||||
err = o.imApiCaller.UpdateUserInfo(mctx.WithApiToken(c, imToken), req.UserID, nickName, faceURL, req.UserType, userFlag)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 构造 ex 字段的 JSON 数据(包含 userFlag 和 userType)
|
||||
type UserEx struct {
|
||||
UserFlag string `json:"userFlag"`
|
||||
UserType int32 `json:"userType"`
|
||||
}
|
||||
userEx := UserEx{
|
||||
UserFlag: userFlag,
|
||||
UserType: req.UserType,
|
||||
}
|
||||
exBytes, err := json.Marshal(userEx)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, errs.ErrInternalServer.WrapMsg("failed to marshal user ex"))
|
||||
return
|
||||
}
|
||||
|
||||
// 调用 UpdateUserInfoEx 更新 ex 字段
|
||||
err = o.imApiCaller.UpdateUserInfoEx(mctx.WithApiToken(c, imToken), req.UserID, string(exBytes))
|
||||
if err != nil {
|
||||
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
apiresp.GinSuccess(c, apistruct.UpdateUserInfoResp{})
|
||||
}
|
||||
|
||||
func (o *Api) FindUserPublicInfo(c *gin.Context) {
|
||||
a2r.Call(c, chatpb.ChatClient.FindUserPublicInfo, o.chatClient)
|
||||
}
|
||||
|
||||
func (o *Api) FindUserFullInfo(c *gin.Context) {
|
||||
a2r.Call(c, chatpb.ChatClient.FindUserFullInfo, o.chatClient)
|
||||
}
|
||||
|
||||
func (o *Api) SearchUserFullInfo(c *gin.Context) {
|
||||
a2r.Call(c, chatpb.ChatClient.SearchUserFullInfo, o.chatClient)
|
||||
}
|
||||
|
||||
func (o *Api) SearchUserPublicInfo(c *gin.Context) {
|
||||
a2r.Call(c, chatpb.ChatClient.SearchUserPublicInfo, o.chatClient)
|
||||
}
|
||||
|
||||
func (o *Api) GetTokenForVideoMeeting(c *gin.Context) {
|
||||
a2r.Call(c, chatpb.ChatClient.GetTokenForVideoMeeting, o.chatClient)
|
||||
}
|
||||
|
||||
// ################## APPLET ##################
|
||||
|
||||
func (o *Api) FindApplet(c *gin.Context) {
|
||||
a2r.Call(c, admin.AdminClient.FindApplet, o.adminClient)
|
||||
}
|
||||
|
||||
// ################## CONFIG ##################
|
||||
|
||||
func (o *Api) GetClientConfig(c *gin.Context) {
|
||||
a2r.Call(c, admin.AdminClient.GetClientConfig, o.adminClient)
|
||||
}
|
||||
|
||||
// ################## CALLBACK ##################
|
||||
|
||||
func (o *Api) OpenIMCallback(c *gin.Context) {
|
||||
body, err := io.ReadAll(c.Request.Body)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
req := &chatpb.OpenIMCallbackReq{
|
||||
Command: c.Query(constantpb.CallbackCommand),
|
||||
Body: string(body),
|
||||
}
|
||||
if _, err := o.chatClient.OpenIMCallback(c, req); err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
apiresp.GinSuccess(c, nil)
|
||||
}
|
||||
|
||||
func (o *Api) SearchFriend(c *gin.Context) {
|
||||
req, err := a2r.ParseRequest[struct {
|
||||
UserID string `json:"userID"`
|
||||
chatpb.SearchUserInfoReq
|
||||
}](c)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
if req.UserID == "" {
|
||||
req.UserID = mctx.GetOpUserID(c)
|
||||
}
|
||||
imToken, err := o.imApiCaller.ImAdminTokenWithDefaultAdmin(c)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
userIDs, err := o.imApiCaller.FriendUserIDs(mctx.WithApiToken(c, imToken), req.UserID)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
if len(userIDs) == 0 {
|
||||
apiresp.GinSuccess(c, &chatpb.SearchUserInfoResp{})
|
||||
return
|
||||
}
|
||||
req.SearchUserInfoReq.UserIDs = userIDs
|
||||
resp, err := o.chatClient.SearchUserInfo(c, &req.SearchUserInfoReq)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
apiresp.GinSuccess(c, resp)
|
||||
}
|
||||
|
||||
func (o *Api) AddFriend(c *gin.Context) {
|
||||
req, err := a2r.ParseRequest[chatpb.AddFriendReq](c)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
// 获取当前用户ID(发起添加好友的用户)
|
||||
ownerUserID := mctx.GetOpUserID(c)
|
||||
|
||||
// 获取用户信息,检查 userType
|
||||
userInfoResp, err := o.chatClient.FindUserFullInfo(c, &chatpb.FindUserFullInfoReq{UserIDs: []string{ownerUserID}})
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
if len(userInfoResp.Users) == 0 || userInfoResp.Users[0].UserType != 1 {
|
||||
apiresp.GinError(c, errs.ErrNoPermission.WrapMsg("only userType=1 users can add friends directly"))
|
||||
return
|
||||
}
|
||||
|
||||
// 调用 chat-deploy 的 ImportFriends
|
||||
imToken, err := o.imApiCaller.ImAdminTokenWithDefaultAdmin(c)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 调用 ImportFriend,传入要添加的好友ID
|
||||
err = o.imApiCaller.ImportFriend(mctx.WithApiToken(c, imToken), ownerUserID, []string{req.UserID})
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
apiresp.GinSuccess(c, nil)
|
||||
}
|
||||
|
||||
func (o *Api) LatestApplicationVersion(c *gin.Context) {
|
||||
a2r.Call(c, admin.AdminClient.LatestApplicationVersion, o.adminClient)
|
||||
}
|
||||
|
||||
func (o *Api) PageApplicationVersion(c *gin.Context) {
|
||||
a2r.Call(c, admin.AdminClient.PageApplicationVersion, o.adminClient)
|
||||
}
|
||||
|
||||
// ==================== 敏感词检测相关 API ====================
|
||||
|
||||
// GetSensitiveWords 获取敏感词列表
|
||||
func (o *Api) GetSensitiveWords(c *gin.Context) {
|
||||
a2r.Call(c, chatpb.ChatClient.GetSensitiveWords, o.chatClient)
|
||||
}
|
||||
|
||||
// CheckSensitiveWords 检测敏感词
|
||||
func (o *Api) CheckSensitiveWords(c *gin.Context) {
|
||||
a2r.Call(c, chatpb.ChatClient.CheckSensitiveWords, o.chatClient)
|
||||
}
|
||||
|
||||
// ==================== 收藏相关 API ====================
|
||||
|
||||
// CreateFavorite 创建收藏
|
||||
func (o *Api) CreateFavorite(c *gin.Context) {
|
||||
a2r.Call(c, chatpb.ChatClient.CreateFavorite, o.chatClient)
|
||||
}
|
||||
|
||||
// GetFavorite 获取收藏详情
|
||||
func (o *Api) GetFavorite(c *gin.Context) {
|
||||
a2r.Call(c, chatpb.ChatClient.GetFavorite, o.chatClient)
|
||||
}
|
||||
|
||||
// GetFavorites 获取收藏列表
|
||||
func (o *Api) GetFavorites(c *gin.Context) {
|
||||
a2r.Call(c, chatpb.ChatClient.GetFavorites, o.chatClient)
|
||||
}
|
||||
|
||||
// SearchFavorites 搜索收藏
|
||||
func (o *Api) SearchFavorites(c *gin.Context) {
|
||||
a2r.Call(c, chatpb.ChatClient.SearchFavorites, o.chatClient)
|
||||
}
|
||||
|
||||
// UpdateFavorite 更新收藏
|
||||
func (o *Api) UpdateFavorite(c *gin.Context) {
|
||||
a2r.Call(c, chatpb.ChatClient.UpdateFavorite, o.chatClient)
|
||||
}
|
||||
|
||||
// DeleteFavorite 删除收藏
|
||||
func (o *Api) DeleteFavorite(c *gin.Context) {
|
||||
a2r.Call(c, chatpb.ChatClient.DeleteFavorite, o.chatClient)
|
||||
}
|
||||
|
||||
// GetFavoritesByTags 根据标签获取收藏
|
||||
func (o *Api) GetFavoritesByTags(c *gin.Context) {
|
||||
a2r.Call(c, chatpb.ChatClient.GetFavoritesByTags, o.chatClient)
|
||||
}
|
||||
|
||||
// GetFavoriteCount 获取收藏数量
|
||||
func (o *Api) GetFavoriteCount(c *gin.Context) {
|
||||
a2r.Call(c, chatpb.ChatClient.GetFavoriteCount, o.chatClient)
|
||||
}
|
||||
|
||||
// ==================== 定时任务相关 API ====================
|
||||
|
||||
// CreateScheduledTask 创建定时任务
|
||||
func (o *Api) CreateScheduledTask(c *gin.Context) {
|
||||
a2r.Call(c, chatpb.ChatClient.CreateScheduledTask, o.chatClient)
|
||||
}
|
||||
|
||||
// GetScheduledTask 获取定时任务详情
|
||||
func (o *Api) GetScheduledTask(c *gin.Context) {
|
||||
a2r.Call(c, chatpb.ChatClient.GetScheduledTask, o.chatClient)
|
||||
}
|
||||
|
||||
// GetScheduledTasks 获取定时任务列表
|
||||
func (o *Api) GetScheduledTasks(c *gin.Context) {
|
||||
a2r.Call(c, chatpb.ChatClient.GetScheduledTasks, o.chatClient)
|
||||
}
|
||||
|
||||
// UpdateScheduledTask 更新定时任务
|
||||
func (o *Api) UpdateScheduledTask(c *gin.Context) {
|
||||
a2r.Call(c, chatpb.ChatClient.UpdateScheduledTask, o.chatClient)
|
||||
}
|
||||
|
||||
// DeleteScheduledTask 删除定时任务
|
||||
func (o *Api) DeleteScheduledTask(c *gin.Context) {
|
||||
a2r.Call(c, chatpb.ChatClient.DeleteScheduledTask, o.chatClient)
|
||||
}
|
||||
|
||||
// ==================== 系统配置相关 API ====================
|
||||
|
||||
// GetAppSystemConfigs 获取APP端配置(返回所有 show_in_app=true 且 enabled=true 的配置)
|
||||
func (o *Api) GetAppSystemConfigs(c *gin.Context) {
|
||||
resp, err := o.chatClient.GetAppSystemConfigs(c, &chatpb.GetAppSystemConfigsReq{})
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 转换为对象格式,key 作为对象的键,value 作为值
|
||||
configMap := make(map[string]interface{})
|
||||
for _, config := range resp.Configs {
|
||||
convertedValue := o.convertValueFromString(config.Value, config.ValueType)
|
||||
configMap[config.Key] = convertedValue
|
||||
}
|
||||
|
||||
apiresp.GinSuccess(c, configMap)
|
||||
}
|
||||
|
||||
// convertValueFromString 将字符串值转换为对应类型(用于返回给前端)
|
||||
func (o *Api) convertValueFromString(value string, valueType int32) interface{} {
|
||||
switch valueType {
|
||||
case 1: // String
|
||||
return value
|
||||
case 2: // Number
|
||||
// 尝试解析为数字
|
||||
if num, err := strconv.ParseFloat(value, 64); err == nil {
|
||||
// 如果是整数,返回整数;否则返回浮点数
|
||||
if num == float64(int64(num)) {
|
||||
return int64(num)
|
||||
}
|
||||
return num
|
||||
}
|
||||
return value
|
||||
case 3: // Bool
|
||||
if b, err := strconv.ParseBool(value); err == nil {
|
||||
return b
|
||||
}
|
||||
return value
|
||||
case 4: // JSON
|
||||
var js interface{}
|
||||
if err := json.Unmarshal([]byte(value), &js); err == nil {
|
||||
return js
|
||||
}
|
||||
return value
|
||||
default:
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 钱包相关 API ====================
|
||||
|
||||
// GetWalletBalance 获取钱包余额
|
||||
func (o *Api) GetWalletBalance(c *gin.Context) {
|
||||
resp, err := o.chatClient.GetWalletBalance(c, &chatpb.GetWalletBalanceReq{})
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 将余额从分转换为元(前端显示用)
|
||||
apiresp.GinSuccess(c, map[string]interface{}{
|
||||
"balance": resp.Balance, // 余额(单位:分)
|
||||
})
|
||||
}
|
||||
|
||||
// GetWalletInfo 获取钱包详细信息
|
||||
func (o *Api) GetWalletInfo(c *gin.Context) {
|
||||
resp, err := o.chatClient.GetWalletInfo(c, &chatpb.GetWalletInfoReq{})
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 构建响应
|
||||
result := map[string]interface{}{
|
||||
"balance": resp.Balance, // 余额(单位:分)
|
||||
"withdrawAccount": resp.WithdrawAccount,
|
||||
"withdrawAccountType": resp.WithdrawAccountType,
|
||||
"withdrawReceiveAccount": resp.WithdrawReceiveAccount,
|
||||
"hasPaymentPassword": resp.HasPaymentPassword,
|
||||
}
|
||||
|
||||
// 添加实名认证信息(如果存在)
|
||||
if resp.RealNameAuth != nil {
|
||||
result["realNameAuth"] = map[string]interface{}{
|
||||
"idCard": resp.RealNameAuth.IdCard,
|
||||
"idCardPhotoFront": resp.RealNameAuth.IdCardPhotoFront,
|
||||
"idCardPhotoBack": resp.RealNameAuth.IdCardPhotoBack,
|
||||
"name": resp.RealNameAuth.Name,
|
||||
"auditStatus": resp.RealNameAuth.AuditStatus,
|
||||
}
|
||||
}
|
||||
|
||||
apiresp.GinSuccess(c, result)
|
||||
}
|
||||
|
||||
// GetWalletBalanceRecords 获取余额明细
|
||||
func (o *Api) GetWalletBalanceRecords(c *gin.Context) {
|
||||
var req chatpb.GetWalletBalanceRecordsReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
apiresp.GinError(c, errs.ErrArgs.WrapMsg("invalid request"))
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := o.chatClient.GetWalletBalanceRecords(c, &req)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 转换为响应格式
|
||||
records := make([]map[string]interface{}, 0, len(resp.Records))
|
||||
for _, record := range resp.Records {
|
||||
records = append(records, map[string]interface{}{
|
||||
"id": record.Id,
|
||||
"userID": record.UserID,
|
||||
"amount": record.Amount,
|
||||
"type": record.Type,
|
||||
"beforeBalance": record.BeforeBalance,
|
||||
"afterBalance": record.AfterBalance,
|
||||
"orderID": record.OrderID,
|
||||
"transactionID": record.TransactionID,
|
||||
"redPacketID": record.RedPacketID,
|
||||
"remark": record.Remark,
|
||||
"createTime": record.CreateTime,
|
||||
})
|
||||
}
|
||||
|
||||
apiresp.GinSuccess(c, map[string]interface{}{
|
||||
"total": resp.Total,
|
||||
"records": records,
|
||||
})
|
||||
}
|
||||
|
||||
// SetPaymentPassword 设置支付密码
|
||||
func (o *Api) SetPaymentPassword(c *gin.Context) {
|
||||
var req chatpb.SetPaymentPasswordReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
apiresp.GinError(c, errs.ErrArgs.WrapMsg("invalid request"))
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := o.chatClient.SetPaymentPassword(c, &req)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
apiresp.GinSuccess(c, resp)
|
||||
}
|
||||
|
||||
// SetWithdrawAccount 设置提现账号
|
||||
func (o *Api) SetWithdrawAccount(c *gin.Context) {
|
||||
var req chatpb.SetWithdrawAccountReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
apiresp.GinError(c, errs.ErrArgs.WrapMsg("invalid request"))
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := o.chatClient.SetWithdrawAccount(c, &req)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
apiresp.GinSuccess(c, resp)
|
||||
}
|
||||
|
||||
// CreateWithdrawApplication 申请提现
|
||||
func (o *Api) CreateWithdrawApplication(c *gin.Context) {
|
||||
req, err := a2r.ParseRequest[chatpb.CreateWithdrawApplicationReq](c)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 从请求中获取客户端IP
|
||||
ip, err := o.GetClientIP(c)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, errs.ErrArgs.WrapMsg("无法获取客户端IP"))
|
||||
return
|
||||
}
|
||||
req.Ip = ip
|
||||
|
||||
resp, err := o.chatClient.CreateWithdrawApplication(c, req)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
apiresp.GinSuccess(c, map[string]interface{}{
|
||||
"applicationID": resp.ApplicationID,
|
||||
})
|
||||
}
|
||||
|
||||
// GetWithdrawApplications 获取提现申请列表
|
||||
func (o *Api) GetWithdrawApplications(c *gin.Context) {
|
||||
var req chatpb.GetWithdrawApplicationsReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
apiresp.GinError(c, errs.ErrArgs.WrapMsg("invalid request"))
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := o.chatClient.GetWithdrawApplications(c, &req)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 转换为响应格式
|
||||
applications := make([]map[string]interface{}, 0, len(resp.Applications))
|
||||
for _, app := range resp.Applications {
|
||||
applications = append(applications, map[string]interface{}{
|
||||
"id": app.Id,
|
||||
"userID": app.UserID,
|
||||
"amount": app.Amount,
|
||||
"withdrawAccount": app.WithdrawAccount,
|
||||
"withdrawAccountType": app.WithdrawAccountType,
|
||||
"status": app.Status,
|
||||
"auditorID": app.AuditorID,
|
||||
"auditTime": app.AuditTime,
|
||||
"auditRemark": app.AuditRemark,
|
||||
"ip": app.Ip,
|
||||
"deviceID": app.DeviceID,
|
||||
"platform": app.Platform,
|
||||
"deviceModel": app.DeviceModel,
|
||||
"deviceBrand": app.DeviceBrand,
|
||||
"osVersion": app.OsVersion,
|
||||
"appVersion": app.AppVersion,
|
||||
"remark": app.Remark,
|
||||
"createTime": app.CreateTime,
|
||||
"updateTime": app.UpdateTime,
|
||||
})
|
||||
}
|
||||
|
||||
apiresp.GinSuccess(c, map[string]interface{}{
|
||||
"total": resp.Total,
|
||||
"applications": applications,
|
||||
})
|
||||
}
|
||||
|
||||
// RealNameAuth 实名认证
|
||||
func (o *Api) RealNameAuth(c *gin.Context) {
|
||||
req, err := a2r.ParseRequest[chatpb.RealNameAuthReq](c)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := o.chatClient.RealNameAuth(c, req)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
apiresp.GinSuccess(c, map[string]interface{}{
|
||||
"success": resp.Success,
|
||||
"message": resp.Message,
|
||||
"idCardPhotoFront": resp.IdCardPhotoFront,
|
||||
"idCardPhotoBack": resp.IdCardPhotoBack,
|
||||
})
|
||||
}
|
||||
197
internal/api/chat/start.go
Normal file
197
internal/api/chat/start.go
Normal file
@@ -0,0 +1,197 @@
|
||||
package chat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
chatmw "git.imall.cloud/openim/chat/internal/api/mw"
|
||||
"git.imall.cloud/openim/chat/internal/api/util"
|
||||
"git.imall.cloud/openim/chat/pkg/common/config"
|
||||
"git.imall.cloud/openim/chat/pkg/common/imapi"
|
||||
"git.imall.cloud/openim/chat/pkg/common/kdisc"
|
||||
disetcd "git.imall.cloud/openim/chat/pkg/common/kdisc/etcd"
|
||||
adminclient "git.imall.cloud/openim/chat/pkg/protocol/admin"
|
||||
chatclient "git.imall.cloud/openim/chat/pkg/protocol/chat"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/openimsdk/tools/discovery/etcd"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/mw"
|
||||
"github.com/openimsdk/tools/system/program"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
"github.com/openimsdk/tools/utils/runtimeenv"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
ApiConfig config.API
|
||||
Discovery config.Discovery
|
||||
Share config.Share
|
||||
Redis config.Redis
|
||||
|
||||
RuntimeEnv string
|
||||
}
|
||||
|
||||
func Start(ctx context.Context, index int, cfg *Config) error {
|
||||
cfg.RuntimeEnv = runtimeenv.PrintRuntimeEnvironment()
|
||||
|
||||
if len(cfg.Share.ChatAdmin) == 0 {
|
||||
return errs.New("share chat admin not configured")
|
||||
}
|
||||
apiPort, err := datautil.GetElemByIndex(cfg.ApiConfig.Api.Ports, index)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client, err := kdisc.NewDiscoveryRegister(&cfg.Discovery, cfg.RuntimeEnv, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
chatConn, err := client.GetConn(ctx, cfg.Discovery.RpcService.Chat, grpc.WithTransportCredentials(insecure.NewCredentials()), mw.GrpcClient())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
adminConn, err := client.GetConn(ctx, cfg.Discovery.RpcService.Admin, grpc.WithTransportCredentials(insecure.NewCredentials()), mw.GrpcClient())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
chatClient := chatclient.NewChatClient(chatConn)
|
||||
adminClient := adminclient.NewAdminClient(adminConn)
|
||||
im := imapi.New(cfg.Share.OpenIM.ApiURL, cfg.Share.OpenIM.Secret, cfg.Share.OpenIM.AdminUserID)
|
||||
base := util.Api{
|
||||
ImUserID: cfg.Share.OpenIM.AdminUserID,
|
||||
ProxyHeader: cfg.Share.ProxyHeader,
|
||||
ChatAdminUserID: cfg.Share.ChatAdmin[0],
|
||||
}
|
||||
adminApi := New(chatClient, adminClient, im, &base)
|
||||
mwApi := chatmw.New(adminClient)
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
engine := gin.New()
|
||||
engine.Use(gin.Recovery(), mw.CorsHandler(), mw.GinParseOperationID())
|
||||
SetChatRoute(engine, adminApi, mwApi)
|
||||
|
||||
var (
|
||||
netDone = make(chan struct{}, 1)
|
||||
netErr error
|
||||
)
|
||||
server := http.Server{Addr: fmt.Sprintf(":%d", apiPort), Handler: engine}
|
||||
go func() {
|
||||
err = server.ListenAndServe()
|
||||
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
netErr = errs.WrapMsg(err, fmt.Sprintf("api start err: %s", server.Addr))
|
||||
netDone <- struct{}{}
|
||||
}
|
||||
}()
|
||||
if cfg.Discovery.Enable == kdisc.ETCDCONST {
|
||||
cm := disetcd.NewConfigManager(client.(*etcd.SvcDiscoveryRegistryImpl).GetClient(),
|
||||
[]string{
|
||||
config.ChatAPIChatCfgFileName,
|
||||
config.DiscoveryConfigFileName,
|
||||
config.ShareFileName,
|
||||
config.LogConfigFileName,
|
||||
},
|
||||
)
|
||||
cm.Watch(ctx)
|
||||
}
|
||||
shutdown := func() error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer cancel()
|
||||
err := server.Shutdown(ctx)
|
||||
if err != nil {
|
||||
return errs.WrapMsg(err, "shutdown err")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
disetcd.RegisterShutDown(shutdown)
|
||||
|
||||
sigs := make(chan os.Signal, 1)
|
||||
signal.Notify(sigs, syscall.SIGTERM)
|
||||
select {
|
||||
case <-sigs:
|
||||
program.SIGTERMExit()
|
||||
if err := shutdown(); err != nil {
|
||||
return err
|
||||
}
|
||||
case <-netDone:
|
||||
close(netDone)
|
||||
return netErr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func SetChatRoute(router gin.IRouter, chat *Api, mw *chatmw.MW) {
|
||||
account := router.Group("/account")
|
||||
account.GET("/captcha", chat.GetCaptchaImage) // Get captcha image
|
||||
account.POST("/code/send", chat.SendVerifyCode) // Send verification code
|
||||
account.POST("/code/verify", chat.VerifyCode) // Verify the verification code
|
||||
account.POST("/register", mw.CheckAdminOrNil, chat.RegisterUser) // Register
|
||||
account.POST("/login", chat.Login) // Login
|
||||
account.POST("/password/reset", chat.ResetPassword) // Forgot password
|
||||
account.POST("/password/change", mw.CheckToken, chat.ChangePassword) // Change password
|
||||
|
||||
user := router.Group("/user", mw.CheckToken)
|
||||
user.POST("/update", chat.UpdateUserInfo) // Edit personal information
|
||||
user.POST("/find/public", chat.FindUserPublicInfo) // Get user's public information
|
||||
user.POST("/find/full", chat.FindUserFullInfo) // Get all information of the user
|
||||
user.POST("/search/full", chat.SearchUserFullInfo) // Search user's public information
|
||||
user.POST("/search/public", chat.SearchUserPublicInfo) // Search all information of the user
|
||||
user.POST("/rtc/get_token", chat.GetTokenForVideoMeeting) // Get token for video meeting for the user
|
||||
|
||||
router.POST("/friend/search", mw.CheckToken, chat.SearchFriend)
|
||||
router.POST("/friend/add", mw.CheckToken, chat.AddFriend)
|
||||
|
||||
router.Group("/applet").POST("/find", mw.CheckToken, chat.FindApplet) // Applet list
|
||||
|
||||
router.Group("/client_config").POST("/get", chat.GetClientConfig) // Get client initialization configuration
|
||||
|
||||
applicationGroup := router.Group("application")
|
||||
applicationGroup.POST("/latest_version", chat.LatestApplicationVersion)
|
||||
applicationGroup.POST("/page_versions", chat.PageApplicationVersion)
|
||||
|
||||
router.Group("/callback").POST("/open_im", chat.OpenIMCallback) // Callback
|
||||
|
||||
// 系统配置相关接口(客户端)
|
||||
systemConfig := router.Group("/system_config")
|
||||
systemConfig.POST("/get_app_configs", chat.GetAppSystemConfigs) // 获取APP端配置(show_in_app=true 且 enabled=true)
|
||||
|
||||
// 钱包相关接口(客户端)
|
||||
wallet := router.Group("/wallet", mw.CheckToken)
|
||||
wallet.POST("/balance", chat.GetWalletBalance) // 获取钱包余额
|
||||
wallet.POST("/info", chat.GetWalletInfo) // 获取钱包详细信息
|
||||
wallet.POST("/balance_records", chat.GetWalletBalanceRecords) // 获取余额明细
|
||||
wallet.POST("/payment_password/set", chat.SetPaymentPassword) // 设置支付密码(首次设置或修改)
|
||||
wallet.POST("/withdraw_account/set", chat.SetWithdrawAccount) // 设置提现账号
|
||||
wallet.POST("/withdraw/apply", chat.CreateWithdrawApplication) // 申请提现
|
||||
wallet.POST("/withdraw/list", chat.GetWithdrawApplications) // 获取提现申请列表
|
||||
wallet.POST("/real_name_auth", chat.RealNameAuth) // 实名认证
|
||||
|
||||
// 敏感词相关接口(客户端)
|
||||
sensitive := router.Group("/sensitive_word", mw.CheckToken)
|
||||
sensitive.POST("/get", chat.GetSensitiveWords) // 获取敏感词列表
|
||||
sensitive.POST("/check", chat.CheckSensitiveWords) // 检测敏感词
|
||||
|
||||
// 收藏相关接口
|
||||
favorite := router.Group("/favorite", mw.CheckToken)
|
||||
favorite.POST("/create", chat.CreateFavorite) // 创建收藏
|
||||
favorite.POST("/get", chat.GetFavorite) // 获取收藏详情
|
||||
favorite.POST("/list", chat.GetFavorites) // 获取收藏列表
|
||||
favorite.POST("/search", chat.SearchFavorites) // 搜索收藏
|
||||
favorite.POST("/update", chat.UpdateFavorite) // 更新收藏
|
||||
favorite.POST("/delete", chat.DeleteFavorite) // 删除收藏
|
||||
favorite.POST("/tags", chat.GetFavoritesByTags) // 根据标签获取收藏
|
||||
favorite.POST("/count", chat.GetFavoriteCount) // 获取收藏数量
|
||||
|
||||
// 定时任务相关接口
|
||||
scheduledTask := router.Group("/scheduled_task", mw.CheckToken)
|
||||
scheduledTask.POST("/create", chat.CreateScheduledTask) // 创建定时任务
|
||||
scheduledTask.POST("/get", chat.GetScheduledTask) // 获取定时任务详情
|
||||
scheduledTask.POST("/list", chat.GetScheduledTasks) // 获取定时任务列表
|
||||
scheduledTask.POST("/update", chat.UpdateScheduledTask) // 更新定时任务
|
||||
scheduledTask.POST("/delete", chat.DeleteScheduledTask) // 删除定时任务
|
||||
}
|
||||
Reference in New Issue
Block a user