Files
kim.dev.6789 b7f8db7d08 复制项目
2026-01-14 22:35:45 +08:00

1664 lines
46 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package admin
import (
"context"
"crypto/md5"
"encoding/hex"
"encoding/json"
"fmt"
"net/http"
"strconv"
"strings"
"time"
"git.imall.cloud/openim/chat/internal/api/util"
"git.imall.cloud/openim/chat/pkg/common/apistruct"
"git.imall.cloud/openim/chat/pkg/common/config"
"git.imall.cloud/openim/chat/pkg/common/imapi"
"git.imall.cloud/openim/chat/pkg/common/mctx"
"git.imall.cloud/openim/chat/pkg/common/xlsx"
"git.imall.cloud/openim/chat/pkg/common/xlsx/model"
"git.imall.cloud/openim/chat/pkg/protocol/admin"
"git.imall.cloud/openim/chat/pkg/protocol/chat"
"git.imall.cloud/openim/protocol/constant"
"git.imall.cloud/openim/protocol/sdkws"
"git.imall.cloud/openim/protocol/user"
wrapperspb "git.imall.cloud/openim/protocol/wrapperspb"
"github.com/gin-gonic/gin"
"github.com/openimsdk/tools/a2r"
"github.com/openimsdk/tools/apiresp"
"github.com/openimsdk/tools/errs"
"github.com/openimsdk/tools/log"
"github.com/openimsdk/tools/utils/datautil"
"github.com/openimsdk/tools/utils/encrypt"
)
func New(chatClient chat.ChatClient, adminClient admin.AdminClient, imApiCaller imapi.CallerInterface, api *util.Api) *Api {
return &Api{
Api: api,
chatClient: chatClient,
adminClient: adminClient,
imApiCaller: imApiCaller,
}
}
type Api struct {
*util.Api
chatClient chat.ChatClient
adminClient admin.AdminClient
imApiCaller imapi.CallerInterface
}
func (o *Api) AdminLogin(c *gin.Context) {
req, err := a2r.ParseRequest[admin.LoginReq](c)
if err != nil {
apiresp.GinError(c, err)
return
}
// if req.Version == "" {
// apiresp.GinError(c, errs.New("openim-admin-front version too old, please use new version").Wrap())
// return
// }
loginResp, err := o.adminClient.Login(c, req)
if err != nil {
apiresp.GinError(c, err)
return
}
imAdminUserID := o.GetDefaultIMAdminUserID()
imToken, err := o.imApiCaller.ImAdminTokenWithDefaultAdmin(c)
if err != nil {
apiresp.GinError(c, err)
return
}
var resp apistruct.AdminLoginResp
if err := datautil.CopyStructFields(&resp, loginResp); err != nil {
apiresp.GinError(c, err)
return
}
resp.ImToken = imToken
resp.ImUserID = imAdminUserID
apiresp.GinSuccess(c, resp)
}
func (o *Api) ResetUserPassword(c *gin.Context) {
req, err := a2r.ParseRequest[chat.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)
}
func (o *Api) AdminUpdateInfo(c *gin.Context) {
req, err := a2r.ParseRequest[admin.AdminUpdateInfoReq](c)
if err != nil {
apiresp.GinError(c, err)
return
}
resp, err := o.adminClient.AdminUpdateInfo(c, req)
if err != nil {
apiresp.GinError(c, err)
return
}
imAdminUserID := o.GetDefaultIMAdminUserID()
imToken, err := o.imApiCaller.GetAdminTokenCache(c, imAdminUserID)
if err != nil {
log.ZError(c, "AdminUpdateInfo ImAdminTokenWithDefaultAdmin", err, "imAdminUserID", imAdminUserID)
return
}
if err := o.imApiCaller.UpdateUserInfo(mctx.WithApiToken(c, imToken), imAdminUserID, resp.Nickname, resp.FaceURL, 1, ""); err != nil {
log.ZError(c, "AdminUpdateInfo UpdateUserInfo", err, "userID", resp.UserID, "nickName", resp.Nickname, "faceURL", resp.FaceURL)
}
apiresp.GinSuccess(c, nil)
}
func (o *Api) AdminInfo(c *gin.Context) {
a2r.Call(c, admin.AdminClient.GetAdminInfo, o.adminClient)
}
func (o *Api) ChangeAdminPassword(c *gin.Context) {
a2r.Call(c, admin.AdminClient.ChangeAdminPassword, o.adminClient)
}
func (o *Api) ChangeOperationPassword(c *gin.Context) {
a2r.Call(c, admin.AdminClient.ChangeOperationPassword, o.adminClient)
}
func (o *Api) SetGoogleAuthKey(c *gin.Context) {
a2r.Call(c, admin.AdminClient.SetGoogleAuthKey, o.adminClient)
}
func (o *Api) AddAdminAccount(c *gin.Context) {
a2r.Call(c, admin.AdminClient.AddAdminAccount, o.adminClient)
}
func (o *Api) AddUserAccount(c *gin.Context) {
req, err := a2r.ParseRequest[chat.AddUserAccountReq](c)
if err != nil {
apiresp.GinError(c, err)
return
}
ip, err := o.GetClientIP(c)
if err != nil {
apiresp.GinError(c, err)
return
}
imToken, err := o.imApiCaller.ImAdminTokenWithDefaultAdmin(c)
if err != nil {
apiresp.GinError(c, err)
return
}
ctx := o.WithAdminUser(mctx.WithApiToken(c, imToken))
err = o.registerChatUser(ctx, ip, []*chat.RegisterUserInfo{req.User})
if err != nil {
return
}
apiresp.GinSuccess(c, nil)
}
func (o *Api) DelAdminAccount(c *gin.Context) {
a2r.Call(c, admin.AdminClient.DelAdminAccount, o.adminClient)
}
func (o *Api) SearchAdminAccount(c *gin.Context) {
a2r.Call(c, admin.AdminClient.SearchAdminAccount, o.adminClient)
}
func (o *Api) GetStatistics(c *gin.Context) {
a2r.Call(c, admin.AdminClient.GetStatistics, o.adminClient)
}
func (o *Api) AddDefaultFriend(c *gin.Context) {
a2r.Call(c, admin.AdminClient.AddDefaultFriend, o.adminClient)
}
func (o *Api) DelDefaultFriend(c *gin.Context) {
a2r.Call(c, admin.AdminClient.DelDefaultFriend, o.adminClient)
}
func (o *Api) SearchDefaultFriend(c *gin.Context) {
a2r.Call(c, admin.AdminClient.SearchDefaultFriend, o.adminClient)
}
func (o *Api) FindDefaultFriend(c *gin.Context) {
a2r.Call(c, admin.AdminClient.FindDefaultFriend, o.adminClient)
}
func (o *Api) AddDefaultGroup(c *gin.Context) {
req, err := a2r.ParseRequest[admin.AddDefaultGroupReq](c)
if err != nil {
apiresp.GinError(c, err)
return
}
imToken, err := o.imApiCaller.ImAdminTokenWithDefaultAdmin(c)
if err != nil {
apiresp.GinError(c, err)
return
}
groups, err := o.imApiCaller.FindGroupInfo(mctx.WithApiToken(c, imToken), req.GroupIDs)
if err != nil {
apiresp.GinError(c, err)
return
}
if len(req.GroupIDs) != len(groups) {
apiresp.GinError(c, errs.ErrArgs.WrapMsg("group id not found"))
return
}
resp, err := o.adminClient.AddDefaultGroup(c, &admin.AddDefaultGroupReq{
GroupIDs: req.GroupIDs,
})
if err != nil {
apiresp.GinError(c, err)
return
}
apiresp.GinSuccess(c, resp)
}
func (o *Api) DelDefaultGroup(c *gin.Context) {
a2r.Call(c, admin.AdminClient.DelDefaultGroup, o.adminClient)
}
func (o *Api) FindDefaultGroup(c *gin.Context) {
a2r.Call(c, admin.AdminClient.FindDefaultGroup, o.adminClient)
}
func (o *Api) SearchDefaultGroup(c *gin.Context) {
req, err := a2r.ParseRequest[admin.SearchDefaultGroupReq](c)
if err != nil {
apiresp.GinError(c, err)
return
}
searchResp, err := o.adminClient.SearchDefaultGroup(c, req)
if err != nil {
apiresp.GinError(c, err)
return
}
resp := apistruct.SearchDefaultGroupResp{
Total: searchResp.Total,
Groups: make([]*sdkws.GroupInfo, 0, len(searchResp.GroupIDs)),
}
if len(searchResp.GroupIDs) > 0 {
imToken, err := o.imApiCaller.ImAdminTokenWithDefaultAdmin(c)
if err != nil {
apiresp.GinError(c, err)
return
}
groups, err := o.imApiCaller.FindGroupInfo(mctx.WithApiToken(c, imToken), searchResp.GroupIDs)
if err != nil {
apiresp.GinError(c, err)
return
}
groupMap := make(map[string]*sdkws.GroupInfo)
for _, group := range groups {
groupMap[group.GroupID] = group
}
for _, groupID := range searchResp.GroupIDs {
if group, ok := groupMap[groupID]; ok {
resp.Groups = append(resp.Groups, group)
} else {
resp.Groups = append(resp.Groups, &sdkws.GroupInfo{
GroupID: groupID,
})
}
}
}
apiresp.GinSuccess(c, resp)
}
func (o *Api) AddInvitationCode(c *gin.Context) {
a2r.Call(c, admin.AdminClient.AddInvitationCode, o.adminClient)
}
func (o *Api) GenInvitationCode(c *gin.Context) {
a2r.Call(c, admin.AdminClient.GenInvitationCode, o.adminClient)
}
func (o *Api) DelInvitationCode(c *gin.Context) {
a2r.Call(c, admin.AdminClient.DelInvitationCode, o.adminClient)
}
func (o *Api) SearchInvitationCode(c *gin.Context) {
a2r.Call(c, admin.AdminClient.SearchInvitationCode, o.adminClient)
}
func (o *Api) AddUserIPLimitLogin(c *gin.Context) {
a2r.Call(c, admin.AdminClient.AddUserIPLimitLogin, o.adminClient)
}
func (o *Api) SearchUserIPLimitLogin(c *gin.Context) {
a2r.Call(c, admin.AdminClient.SearchUserIPLimitLogin, o.adminClient)
}
func (o *Api) DelUserIPLimitLogin(c *gin.Context) {
a2r.Call(c, admin.AdminClient.DelUserIPLimitLogin, o.adminClient)
}
func (o *Api) SearchIPForbidden(c *gin.Context) {
a2r.Call(c, admin.AdminClient.SearchIPForbidden, o.adminClient)
}
func (o *Api) AddIPForbidden(c *gin.Context) {
a2r.Call(c, admin.AdminClient.AddIPForbidden, o.adminClient)
}
func (o *Api) DelIPForbidden(c *gin.Context) {
a2r.Call(c, admin.AdminClient.DelIPForbidden, o.adminClient)
}
func (o *Api) ParseToken(c *gin.Context) {
a2r.Call(c, admin.AdminClient.ParseToken, o.adminClient)
}
func (o *Api) BlockUser(c *gin.Context) {
req, err := a2r.ParseRequest[admin.BlockUserReq](c)
if err != nil {
apiresp.GinError(c, err)
return
}
resp, err := o.adminClient.BlockUser(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)
}
func (o *Api) UnblockUser(c *gin.Context) {
a2r.Call(c, admin.AdminClient.UnblockUser, o.adminClient)
}
func (o *Api) SearchBlockUser(c *gin.Context) {
a2r.Call(c, admin.AdminClient.SearchBlockUser, o.adminClient)
}
func (o *Api) SetClientConfig(c *gin.Context) {
a2r.Call(c, admin.AdminClient.SetClientConfig, o.adminClient)
}
func (o *Api) DelClientConfig(c *gin.Context) {
a2r.Call(c, admin.AdminClient.DelClientConfig, o.adminClient)
}
func (o *Api) GetClientConfig(c *gin.Context) {
a2r.Call(c, admin.AdminClient.GetClientConfig, o.adminClient)
}
func (o *Api) AddApplet(c *gin.Context) {
a2r.Call(c, admin.AdminClient.AddApplet, o.adminClient)
}
func (o *Api) DelApplet(c *gin.Context) {
a2r.Call(c, admin.AdminClient.DelApplet, o.adminClient)
}
func (o *Api) UpdateApplet(c *gin.Context) {
a2r.Call(c, admin.AdminClient.UpdateApplet, o.adminClient)
}
func (o *Api) SearchApplet(c *gin.Context) {
a2r.Call(c, admin.AdminClient.SearchApplet, o.adminClient)
}
func (o *Api) LoginUserCount(c *gin.Context) {
a2r.Call(c, chat.ChatClient.UserLoginCount, o.chatClient)
}
// OnlineUserCount 在线人数统计
func (o *Api) OnlineUserCount(c *gin.Context) {
imToken, err := o.imApiCaller.ImAdminTokenWithDefaultAdmin(c)
if err != nil {
apiresp.GinError(c, err)
return
}
resp, err := o.imApiCaller.OnlineUserCount(mctx.WithApiToken(c, imToken))
if err != nil {
apiresp.GinError(c, err)
return
}
apiresp.GinSuccess(c, resp)
}
// OnlineUserCountTrend 在线人数走势统计
func (o *Api) OnlineUserCountTrend(c *gin.Context) {
req, err := a2r.ParseRequest[imapi.OnlineUserCountTrendReq](c)
if err != nil {
apiresp.GinError(c, err)
return
}
imToken, err := o.imApiCaller.ImAdminTokenWithDefaultAdmin(c)
if err != nil {
apiresp.GinError(c, err)
return
}
resp, err := o.imApiCaller.OnlineUserCountTrend(mctx.WithApiToken(c, imToken), req)
if err != nil {
apiresp.GinError(c, err)
return
}
apiresp.GinSuccess(c, resp)
}
// UserSendMsgCount 用户发送消息总数统计
func (o *Api) UserSendMsgCount(c *gin.Context) {
req, err := a2r.ParseRequest[imapi.UserSendMsgCountReq](c)
if err != nil {
apiresp.GinError(c, err)
return
}
imToken, err := o.imApiCaller.ImAdminTokenWithDefaultAdmin(c)
if err != nil {
apiresp.GinError(c, err)
return
}
resp, err := o.imApiCaller.UserSendMsgCount(mctx.WithApiToken(c, imToken), req)
if err != nil {
apiresp.GinError(c, err)
return
}
apiresp.GinSuccess(c, resp)
}
// UserSendMsgCountTrend 用户发送消息走势统计
func (o *Api) UserSendMsgCountTrend(c *gin.Context) {
req, err := a2r.ParseRequest[imapi.UserSendMsgCountTrendReq](c)
if err != nil {
apiresp.GinError(c, err)
return
}
imToken, err := o.imApiCaller.ImAdminTokenWithDefaultAdmin(c)
if err != nil {
apiresp.GinError(c, err)
return
}
resp, err := o.imApiCaller.UserSendMsgCountTrend(mctx.WithApiToken(c, imToken), req)
if err != nil {
apiresp.GinError(c, err)
return
}
apiresp.GinSuccess(c, resp)
}
// UserSendMsgQuery 用户发送消息查询
func (o *Api) UserSendMsgQuery(c *gin.Context) {
req, err := a2r.ParseRequest[imapi.UserSendMsgQueryReq](c)
if err != nil {
apiresp.GinError(c, err)
return
}
imToken, err := o.imApiCaller.ImAdminTokenWithDefaultAdmin(c)
if err != nil {
apiresp.GinError(c, err)
return
}
resp, err := o.imApiCaller.UserSendMsgQuery(mctx.WithApiToken(c, imToken), req)
if err != nil {
apiresp.GinError(c, err)
return
}
apiresp.GinSuccess(c, resp)
}
func (o *Api) NewUserCount(c *gin.Context) {
req, err := a2r.ParseRequest[user.UserRegisterCountReq](c)
if err != nil {
apiresp.GinError(c, err)
return
}
imToken, err := o.imApiCaller.ImAdminTokenWithDefaultAdmin(c)
if err != nil {
apiresp.GinError(c, err)
return
}
dateCount, total, err := o.imApiCaller.UserRegisterCount(mctx.WithApiToken(c, imToken), req.Start, req.End)
if err != nil {
apiresp.GinError(c, err)
return
}
apiresp.GinSuccess(c, &apistruct.NewUserCountResp{
DateCount: dateCount,
Total: total,
})
}
func (o *Api) ImportUserByXlsx(c *gin.Context) {
formFile, err := c.FormFile("data")
if err != nil {
apiresp.GinError(c, err)
return
}
ip, err := o.GetClientIP(c)
if err != nil {
apiresp.GinError(c, err)
return
}
file, err := formFile.Open()
if err != nil {
apiresp.GinError(c, err)
return
}
defer file.Close()
var users []model.User
if err := xlsx.ParseAll(file, &users); err != nil {
apiresp.GinError(c, errs.ErrArgs.WrapMsg("xlsx file parse error "+err.Error()))
return
}
us, err := o.xlsx2user(users)
if err != nil {
apiresp.GinError(c, err)
return
}
imToken, err := o.imApiCaller.ImAdminTokenWithDefaultAdmin(c)
if err != nil {
apiresp.GinError(c, err)
return
}
ctx := o.WithAdminUser(mctx.WithApiToken(c, imToken))
apiresp.GinError(c, o.registerChatUser(ctx, ip, us))
}
func (o *Api) ImportUserByJson(c *gin.Context) {
req, err := a2r.ParseRequest[struct {
Users []*chat.RegisterUserInfo `json:"users"`
}](c)
if err != nil {
apiresp.GinError(c, err)
return
}
ip, err := o.GetClientIP(c)
if err != nil {
apiresp.GinError(c, err)
return
}
imToken, err := o.imApiCaller.ImAdminTokenWithDefaultAdmin(c)
if err != nil {
apiresp.GinError(c, err)
return
}
ctx := o.WithAdminUser(mctx.WithApiToken(c, imToken))
apiresp.GinError(c, o.registerChatUser(ctx, ip, req.Users))
}
func (o *Api) xlsx2user(users []model.User) ([]*chat.RegisterUserInfo, error) {
chatUsers := make([]*chat.RegisterUserInfo, len(users))
for i, info := range users {
if info.Nickname == "" {
return nil, errs.ErrArgs.WrapMsg("nickname is empty")
}
if info.AreaCode == "" || info.PhoneNumber == "" {
return nil, errs.ErrArgs.WrapMsg("areaCode or phoneNumber is empty")
}
if info.Password == "" {
return nil, errs.ErrArgs.WrapMsg("password is empty")
}
if !strings.HasPrefix(info.AreaCode, "+") {
return nil, errs.ErrArgs.WrapMsg("areaCode format error")
}
if _, err := strconv.ParseUint(info.AreaCode[1:], 10, 16); err != nil {
return nil, errs.ErrArgs.WrapMsg("areaCode format error")
}
gender, _ := strconv.Atoi(info.Gender)
chatUsers[i] = &chat.RegisterUserInfo{
UserID: info.UserID,
Nickname: info.Nickname,
FaceURL: info.FaceURL,
Birth: o.xlsxBirth(info.Birth).UnixMilli(),
Gender: int32(gender),
AreaCode: info.AreaCode,
PhoneNumber: info.PhoneNumber,
Email: info.Email,
Account: info.Account,
Password: encrypt.Md5(info.Password),
}
}
return chatUsers, nil
}
func (o *Api) xlsxBirth(s string) time.Time {
if s == "" {
return time.Now()
}
var separator byte
for _, b := range []byte(s) {
if b < '0' || b > '9' {
separator = b
}
}
arr := strings.Split(s, string([]byte{separator}))
if len(arr) != 3 {
return time.Now()
}
year, _ := strconv.Atoi(arr[0])
month, _ := strconv.Atoi(arr[1])
day, _ := strconv.Atoi(arr[2])
t := time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.Local)
if t.Before(time.Date(1900, 0, 0, 0, 0, 0, 0, time.Local)) {
return time.Now()
}
return t
}
func (o *Api) registerChatUser(ctx context.Context, ip string, users []*chat.RegisterUserInfo) error {
if len(users) == 0 {
return errs.ErrArgs.WrapMsg("users is empty")
}
for _, info := range users {
respRegisterUser, err := o.chatClient.RegisterUser(ctx, &chat.RegisterUserReq{Ip: ip, User: info, Platform: constant.AdminPlatformID})
if err != nil {
return err
}
exMap := make(map[string]interface{})
exMap["userType"] = info.UserType
exMap["userFlag"] = info.UserFlag
ex, err := json.Marshal(exMap)
if err != nil {
return err
}
userInfo := &sdkws.UserInfo{
UserID: respRegisterUser.UserID,
Nickname: info.Nickname,
FaceURL: info.FaceURL,
UserType: info.UserType, // 使用传入的用户类型如果没有则默认为0
UserFlag: info.UserFlag, // 使用传入的用户标签
Ex: string(ex),
}
if err = o.imApiCaller.RegisterUser(ctx, []*sdkws.UserInfo{userInfo}); err != nil {
return err
}
if resp, err := o.adminClient.FindDefaultFriend(ctx, &admin.FindDefaultFriendReq{}); err == nil {
_ = o.imApiCaller.ImportFriend(ctx, respRegisterUser.UserID, resp.UserIDs)
}
if resp, err := o.adminClient.FindDefaultGroup(ctx, &admin.FindDefaultGroupReq{}); err == nil {
_ = o.imApiCaller.InviteToGroup(ctx, respRegisterUser.UserID, resp.GroupIDs)
}
}
return nil
}
func (o *Api) BatchImportTemplate(c *gin.Context) {
md5Sum := md5.Sum(config.ImportTemplate)
md5Val := hex.EncodeToString(md5Sum[:])
if c.GetHeader("If-None-Match") == md5Val {
c.Status(http.StatusNotModified)
return
}
c.Header("Content-Disposition", "attachment; filename=template.xlsx")
c.Header("Content-Transfer-Encoding", "binary")
c.Header("Content-Description", "File Transfer")
c.Header("Content-Length", strconv.Itoa(len(config.ImportTemplate)))
c.Header("ETag", md5Val)
c.Data(http.StatusOK, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", config.ImportTemplate)
}
func (o *Api) SetAllowRegister(c *gin.Context) {
a2r.Call(c, chat.ChatClient.SetAllowRegister, o.chatClient)
}
func (o *Api) GetAllowRegister(c *gin.Context) {
a2r.Call(c, chat.ChatClient.GetAllowRegister, o.chatClient)
}
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)
}
func (o *Api) AddApplicationVersion(c *gin.Context) {
a2r.Call(c, admin.AdminClient.AddApplicationVersion, o.adminClient)
}
func (o *Api) UpdateApplicationVersion(c *gin.Context) {
a2r.Call(c, admin.AdminClient.UpdateApplicationVersion, o.adminClient)
}
func (o *Api) DeleteApplicationVersion(c *gin.Context) {
a2r.Call(c, admin.AdminClient.DeleteApplicationVersion, o.adminClient)
}
// ==================== 敏感词管理相关 API ====================
// 敏感词管理
func (o *Api) AddSensitiveWord(c *gin.Context) {
a2r.Call(c, admin.AdminClient.AddSensitiveWord, o.adminClient)
}
func (o *Api) UpdateSensitiveWord(c *gin.Context) {
a2r.Call(c, admin.AdminClient.UpdateSensitiveWord, o.adminClient)
}
func (o *Api) DeleteSensitiveWord(c *gin.Context) {
a2r.Call(c, admin.AdminClient.DeleteSensitiveWord, o.adminClient)
}
func (o *Api) GetSensitiveWord(c *gin.Context) {
a2r.Call(c, admin.AdminClient.GetSensitiveWord, o.adminClient)
}
func (o *Api) SearchSensitiveWords(c *gin.Context) {
a2r.Call(c, admin.AdminClient.SearchSensitiveWords, o.adminClient)
}
func (o *Api) BatchAddSensitiveWords(c *gin.Context) {
a2r.Call(c, admin.AdminClient.BatchAddSensitiveWords, o.adminClient)
}
func (o *Api) BatchUpdateSensitiveWords(c *gin.Context) {
a2r.Call(c, admin.AdminClient.BatchUpdateSensitiveWords, o.adminClient)
}
func (o *Api) BatchDeleteSensitiveWords(c *gin.Context) {
a2r.Call(c, admin.AdminClient.BatchDeleteSensitiveWords, o.adminClient)
}
// 敏感词分组管理
func (o *Api) AddSensitiveWordGroup(c *gin.Context) {
a2r.Call(c, admin.AdminClient.AddSensitiveWordGroup, o.adminClient)
}
func (o *Api) UpdateSensitiveWordGroup(c *gin.Context) {
a2r.Call(c, admin.AdminClient.UpdateSensitiveWordGroup, o.adminClient)
}
func (o *Api) DeleteSensitiveWordGroup(c *gin.Context) {
a2r.Call(c, admin.AdminClient.DeleteSensitiveWordGroup, o.adminClient)
}
func (o *Api) GetSensitiveWordGroup(c *gin.Context) {
a2r.Call(c, admin.AdminClient.GetSensitiveWordGroup, o.adminClient)
}
func (o *Api) GetAllSensitiveWordGroups(c *gin.Context) {
a2r.Call(c, admin.AdminClient.GetAllSensitiveWordGroups, o.adminClient)
}
// 敏感词配置管理
func (o *Api) GetSensitiveWordConfig(c *gin.Context) {
a2r.Call(c, admin.AdminClient.GetSensitiveWordConfig, o.adminClient)
}
func (o *Api) UpdateSensitiveWordConfig(c *gin.Context) {
a2r.Call(c, admin.AdminClient.UpdateSensitiveWordConfig, o.adminClient)
}
// 敏感词日志管理
func (o *Api) GetSensitiveWordLogs(c *gin.Context) {
a2r.Call(c, admin.AdminClient.GetSensitiveWordLogs, o.adminClient)
}
func (o *Api) DeleteSensitiveWordLogs(c *gin.Context) {
a2r.Call(c, admin.AdminClient.DeleteSensitiveWordLogs, o.adminClient)
}
// 用户登录记录管理
func (o *Api) GetUserLoginRecords(c *gin.Context) {
a2r.Call(c, admin.AdminClient.GetUserLoginRecords, o.adminClient)
}
// 敏感词统计
func (o *Api) GetSensitiveWordStats(c *gin.Context) {
req := &admin.GetSensitiveWordStatsReq{}
resp, err := o.adminClient.GetSensitiveWordStats(c, req)
if err != nil {
apiresp.GinError(c, err)
return
}
apiresp.GinSuccess(c, resp)
}
func (o *Api) GetSensitiveWordLogStats(c *gin.Context) {
a2r.Call(c, admin.AdminClient.GetSensitiveWordLogStats, o.adminClient)
}
// ==================== 定时任务管理相关 API ====================
// GetScheduledTasks 获取定时任务列表
func (o *Api) GetScheduledTasks(c *gin.Context) {
a2r.Call(c, admin.AdminClient.GetScheduledTasks, o.adminClient)
}
// DeleteScheduledTask 删除定时任务
func (o *Api) DeleteScheduledTask(c *gin.Context) {
a2r.Call(c, admin.AdminClient.DeleteScheduledTask, o.adminClient)
}
// ==================== 系统配置管理相关 API ====================
// CreateSystemConfig 创建系统配置
func (o *Api) CreateSystemConfig(c *gin.Context) {
// 自定义处理,支持 value 字段为多种类型
var req struct {
Key string `json:"key" binding:"required"`
Title string `json:"title"`
Value interface{} `json:"value"` // 支持多种类型string, number, bool, object
ValueType int32 `json:"valueType"`
Description string `json:"description"`
Enabled bool `json:"enabled"`
ShowInApp interface{} `json:"showInApp"` // 是否在APP端展示
}
if err := c.ShouldBindJSON(&req); err != nil {
apiresp.GinError(c, errs.ErrArgs.WrapMsg("invalid request: "+err.Error()))
return
}
// 如果未设置值类型,默认为字符串类型
valueType := req.ValueType
if valueType == 0 {
valueType = 1 // ConfigValueTypeString
}
// 将 value 转换为字符串
valueStr := ""
if req.Value != nil {
// 根据 valueType 转换
switch valueType {
case 1: // String
if str, ok := req.Value.(string); ok {
valueStr = str
} else {
valueStr = fmt.Sprintf("%v", req.Value)
}
case 2: // Number
switch v := req.Value.(type) {
case string:
valueStr = v
case float64:
valueStr = strconv.FormatFloat(v, 'f', -1, 64)
case float32:
valueStr = strconv.FormatFloat(float64(v), 'f', -1, 32)
case int:
valueStr = strconv.Itoa(v)
case int64:
valueStr = strconv.FormatInt(v, 10)
case int32:
valueStr = strconv.FormatInt(int64(v), 10)
default:
valueStr = fmt.Sprintf("%v", v)
}
case 3: // Bool
switch v := req.Value.(type) {
case string:
// 对于字符串,先尝试解析为布尔值,如果失败则报错
if v == "" {
apiresp.GinError(c, errs.ErrArgs.WrapMsg("boolean value cannot be empty, must be 'true' or 'false'"))
return
}
// 标准化布尔值字符串(支持 "1", "t", "T", "true", "TRUE", "True" 等)
if parsed, err := strconv.ParseBool(v); err == nil {
valueStr = strconv.FormatBool(parsed)
} else {
apiresp.GinError(c, errs.ErrArgs.WrapMsg(fmt.Sprintf("invalid boolean value '%s', must be 'true' or 'false'", v)))
return
}
case bool:
valueStr = strconv.FormatBool(v)
default:
// 对于其他类型,尝试转换为字符串后再解析
strVal := fmt.Sprintf("%v", v)
if strVal == "" {
apiresp.GinError(c, errs.ErrArgs.WrapMsg("boolean value cannot be empty, must be 'true' or 'false'"))
return
}
if parsed, err := strconv.ParseBool(strVal); err == nil {
valueStr = strconv.FormatBool(parsed)
} else {
apiresp.GinError(c, errs.ErrArgs.WrapMsg(fmt.Sprintf("invalid boolean value '%s', must be 'true' or 'false'", strVal)))
return
}
}
case 4: // JSON
switch v := req.Value.(type) {
case string:
valueStr = v
default:
// 序列化为 JSON
jsonBytes, err := json.Marshal(v)
if err != nil {
apiresp.GinError(c, errs.ErrArgs.WrapMsg("invalid JSON value"))
return
}
valueStr = string(jsonBytes)
}
default:
valueStr = fmt.Sprintf("%v", req.Value)
}
}
// 如果 value 未提供,根据 valueType 设置默认值
if valueStr == "" {
switch valueType {
case 3: // Bool
// 布尔类型默认值为 false
valueStr = "false"
case 2: // Number
// 数字类型默认值为 0
valueStr = "0"
case 4: // JSON
// JSON 类型默认值为空对象
valueStr = "{}"
// String 类型允许空字符串,保持 valueStr = ""
}
}
// 验证转换后的值
if err := o.validateValueByType(valueStr, valueType); err != nil {
apiresp.GinError(c, err)
return
}
// 处理 showInApp 字段(默认为 false
showInApp := false
if req.ShowInApp != nil {
if sa, ok := req.ShowInApp.(bool); ok {
showInApp = sa
}
}
// 构建 protobuf 请求
pbReq := &admin.CreateSystemConfigReq{
Key: req.Key,
Title: req.Title,
Value: valueStr,
ValueType: valueType,
Description: req.Description,
Enabled: req.Enabled,
ShowInApp: showInApp,
}
// 调用 RPC
resp, err := o.adminClient.CreateSystemConfig(c, pbReq)
if err != nil {
apiresp.GinError(c, err)
return
}
apiresp.GinSuccess(c, resp)
}
// validateValueByType 验证值是否符合类型要求API 层辅助函数)
func (o *Api) validateValueByType(value string, valueType int32) error {
switch valueType {
case 1: // String
return nil
case 2: // Number
if _, err := strconv.ParseFloat(value, 64); err != nil {
return errs.ErrArgs.WrapMsg("value must be a valid number")
}
return nil
case 3: // Bool
if value == "" {
return errs.ErrArgs.WrapMsg("boolean value cannot be empty, must be 'true' or 'false'")
}
if _, err := strconv.ParseBool(value); err != nil {
return errs.ErrArgs.WrapMsg(fmt.Sprintf("invalid boolean value '%s', must be 'true' or 'false' (also accepts: '1', '0', 't', 'f', 'T', 'F')", value))
}
return nil
case 4: // JSON
var js interface{}
if err := json.Unmarshal([]byte(value), &js); err != nil {
return errs.ErrArgs.WrapMsg("value must be a valid JSON")
}
return nil
default:
return errs.ErrArgs.WrapMsg("invalid value type")
}
}
// GetSystemConfig 获取系统配置详情
func (o *Api) GetSystemConfig(c *gin.Context) {
// 使用自定义处理,转换返回的 value 类型
var req admin.GetSystemConfigReq
if err := c.ShouldBindJSON(&req); err != nil {
apiresp.GinError(c, errs.ErrArgs.WrapMsg("invalid request"))
return
}
resp, err := o.adminClient.GetSystemConfig(c, &req)
if err != nil {
apiresp.GinError(c, err)
return
}
// 转换 value 为对应类型
convertedValue := o.convertValueFromString(resp.Config.Value, resp.Config.ValueType)
// 构建响应
result := map[string]interface{}{
"key": resp.Config.Key,
"title": resp.Config.Title,
"value": convertedValue,
"valueType": resp.Config.ValueType,
"description": resp.Config.Description,
"enabled": resp.Config.Enabled,
"showInApp": resp.Config.ShowInApp,
"createTime": resp.Config.CreateTime,
"updateTime": resp.Config.UpdateTime,
}
apiresp.GinSuccess(c, map[string]interface{}{"config": result})
}
// 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
}
}
// GetAllSystemConfigs 获取所有系统配置(分页)
func (o *Api) GetAllSystemConfigs(c *gin.Context) {
var req admin.GetAllSystemConfigsReq
if err := c.ShouldBindJSON(&req); err != nil {
apiresp.GinError(c, errs.ErrArgs.WrapMsg("invalid request"))
return
}
resp, err := o.adminClient.GetAllSystemConfigs(c, &req)
if err != nil {
apiresp.GinError(c, err)
return
}
// 转换列表中的 value 为对应类型
list := make([]map[string]interface{}, 0, len(resp.List))
for _, config := range resp.List {
convertedValue := o.convertValueFromString(config.Value, config.ValueType)
list = append(list, map[string]interface{}{
"key": config.Key,
"title": config.Title,
"value": convertedValue,
"valueType": config.ValueType,
"description": config.Description,
"enabled": config.Enabled,
"showInApp": config.ShowInApp,
"createTime": config.CreateTime,
"updateTime": config.UpdateTime,
})
}
apiresp.GinSuccess(c, map[string]interface{}{
"total": resp.Total,
"list": list,
})
}
// UpdateSystemConfig 更新系统配置
func (o *Api) UpdateSystemConfig(c *gin.Context) {
// 自定义处理,支持 value 字段为多种类型
var req struct {
Key string `json:"key" binding:"required"`
Title interface{} `json:"title"`
Value interface{} `json:"value"` // 支持多种类型
ValueType interface{} `json:"valueType"`
Description interface{} `json:"description"`
Enabled interface{} `json:"enabled"`
ShowInApp interface{} `json:"showInApp"` // 是否在APP端展示
}
if err := c.ShouldBindJSON(&req); err != nil {
apiresp.GinError(c, errs.ErrArgs.WrapMsg("invalid request"))
return
}
// 获取当前配置以确定 valueType
currentConfig, err := o.adminClient.GetSystemConfig(c, &admin.GetSystemConfigReq{Key: req.Key})
if err != nil {
apiresp.GinError(c, err)
return
}
// 确定要使用的 valueType
valueType := currentConfig.Config.ValueType
if req.ValueType != nil {
// 处理 JSON 中的数字类型(可能是 float64
switch v := req.ValueType.(type) {
case float64:
valueType = int32(v)
case int:
valueType = int32(v)
case int32:
valueType = v
}
}
// 构建 protobuf 请求
pbReq := &admin.UpdateSystemConfigReq{
Key: req.Key,
}
// 处理可选字段
if req.Title != nil {
if titleStr, ok := req.Title.(string); ok {
pbReq.Title = &wrapperspb.StringValue{Value: titleStr}
}
}
if req.Description != nil {
if descStr, ok := req.Description.(string); ok {
pbReq.Description = &wrapperspb.StringValue{Value: descStr}
}
}
if req.Enabled != nil {
if enabled, ok := req.Enabled.(bool); ok {
pbReq.Enabled = &wrapperspb.BoolValue{Value: enabled}
}
}
if req.ShowInApp != nil {
if showInApp, ok := req.ShowInApp.(bool); ok {
pbReq.ShowInApp = &wrapperspb.BoolValue{Value: showInApp}
}
}
if req.ValueType != nil {
if vt, ok := req.ValueType.(float64); ok {
pbReq.ValueType = &wrapperspb.Int32Value{Value: int32(vt)}
}
}
// 如果提供了 value转换为字符串
if req.Value != nil {
valueStr := o.convertValueToString(req.Value, valueType)
// 验证转换后的值
if err := o.validateValueByType(valueStr, valueType); err != nil {
apiresp.GinError(c, err)
return
}
pbReq.Value = &wrapperspb.StringValue{Value: valueStr}
}
// 调用 RPC
resp, err := o.adminClient.UpdateSystemConfig(c, pbReq)
if err != nil {
apiresp.GinError(c, err)
return
}
apiresp.GinSuccess(c, resp)
}
// convertValueToString 将任意类型的值转换为字符串
func (o *Api) convertValueToString(value interface{}, valueType int32) string {
if value == nil {
return ""
}
switch valueType {
case 1: // String
if str, ok := value.(string); ok {
return str
}
return fmt.Sprintf("%v", value)
case 2: // Number
switch v := value.(type) {
case string:
return v
case float64:
return strconv.FormatFloat(v, 'f', -1, 64)
case float32:
return strconv.FormatFloat(float64(v), 'f', -1, 32)
case int:
return strconv.Itoa(v)
case int64:
return strconv.FormatInt(v, 10)
case int32:
return strconv.FormatInt(int64(v), 10)
default:
return fmt.Sprintf("%v", v)
}
case 3: // Bool
switch v := value.(type) {
case string:
return v
case bool:
return strconv.FormatBool(v)
default:
return fmt.Sprintf("%v", v)
}
case 4: // JSON
switch v := value.(type) {
case string:
return v
default:
jsonBytes, _ := json.Marshal(v)
return string(jsonBytes)
}
default:
return fmt.Sprintf("%v", value)
}
}
// UpdateSystemConfigValue 更新系统配置值
func (o *Api) UpdateSystemConfigValue(c *gin.Context) {
var req struct {
Key string `json:"key" binding:"required"`
Value interface{} `json:"value"` // 支持多种类型
}
if err := c.ShouldBindJSON(&req); err != nil {
apiresp.GinError(c, errs.ErrArgs.WrapMsg("invalid request"))
return
}
// 获取当前配置以确定 valueType
currentConfig, err := o.adminClient.GetSystemConfig(c, &admin.GetSystemConfigReq{Key: req.Key})
if err != nil {
apiresp.GinError(c, err)
return
}
// 转换为字符串
valueStr := o.convertValueToString(req.Value, currentConfig.Config.ValueType)
// 验证转换后的值
if err := o.validateValueByType(valueStr, currentConfig.Config.ValueType); err != nil {
apiresp.GinError(c, err)
return
}
// 调用 RPC
resp, err := o.adminClient.UpdateSystemConfigValue(c, &admin.UpdateSystemConfigValueReq{
Key: req.Key,
Value: valueStr,
})
if err != nil {
apiresp.GinError(c, err)
return
}
apiresp.GinSuccess(c, resp)
}
// UpdateSystemConfigEnabled 更新系统配置启用状态
func (o *Api) UpdateSystemConfigEnabled(c *gin.Context) {
a2r.Call(c, admin.AdminClient.UpdateSystemConfigEnabled, o.adminClient)
}
// DeleteSystemConfig 删除系统配置
func (o *Api) DeleteSystemConfig(c *gin.Context) {
a2r.Call(c, admin.AdminClient.DeleteSystemConfig, o.adminClient)
}
// GetEnabledSystemConfigs 获取所有已启用的配置
func (o *Api) GetEnabledSystemConfigs(c *gin.Context) {
resp, err := o.adminClient.GetEnabledSystemConfigs(c, &admin.GetEnabledSystemConfigsReq{})
if err != nil {
apiresp.GinError(c, err)
return
}
// 转换列表中的 value 为对应类型
list := make([]map[string]interface{}, 0, len(resp.List))
for _, config := range resp.List {
convertedValue := o.convertValueFromString(config.Value, config.ValueType)
list = append(list, map[string]interface{}{
"key": config.Key,
"title": config.Title,
"value": convertedValue,
"valueType": config.ValueType,
"description": config.Description,
"enabled": config.Enabled,
"showInApp": config.ShowInApp,
"createTime": config.CreateTime,
"updateTime": config.UpdateTime,
})
}
apiresp.GinSuccess(c, map[string]interface{}{"list": list})
}
// ==================== 钱包管理相关 API ====================
// GetUserWallet 获取用户钱包信息
func (o *Api) GetUserWallet(c *gin.Context) {
var req admin.GetUserWalletReq
if err := c.ShouldBindJSON(&req); err != nil {
apiresp.GinError(c, errs.ErrArgs.WrapMsg("invalid request"))
return
}
resp, err := o.adminClient.GetUserWallet(c, &req)
if err != nil {
apiresp.GinError(c, err)
return
}
// 构建响应
result := map[string]interface{}{
"userID": resp.Wallet.UserID,
"balance": resp.Wallet.Balance,
"withdrawAccount": resp.Wallet.WithdrawAccount,
"withdrawReceiveAccount": resp.Wallet.WithdrawReceiveAccount,
"hasPaymentPassword": resp.Wallet.HasPaymentPassword,
"createTime": resp.Wallet.CreateTime,
"updateTime": resp.Wallet.UpdateTime,
}
// 添加实名认证信息(如果存在)
if resp.Wallet.RealNameAuth != nil {
result["realNameAuth"] = map[string]interface{}{
"idCard": resp.Wallet.RealNameAuth.IdCard,
"name": resp.Wallet.RealNameAuth.Name,
"idCardPhotoFront": resp.Wallet.RealNameAuth.IdCardPhotoFront,
"idCardPhotoBack": resp.Wallet.RealNameAuth.IdCardPhotoBack,
"auditStatus": resp.Wallet.RealNameAuth.AuditStatus,
}
}
apiresp.GinSuccess(c, map[string]interface{}{"wallet": result})
}
// UpdateUserWalletBalance 更新用户余额(后台充值/扣款)
func (o *Api) UpdateUserWalletBalance(c *gin.Context) {
a2r.Call(c, admin.AdminClient.UpdateUserWalletBalance, o.adminClient)
}
// BatchUpdateWalletBalance 批量更新用户余额(后台批量充值/扣款)
func (o *Api) BatchUpdateWalletBalance(c *gin.Context) {
a2r.Call(c, admin.AdminClient.BatchUpdateWalletBalance, o.adminClient)
}
// GetWallets 获取钱包列表
func (o *Api) GetWallets(c *gin.Context) {
a2r.Call(c, admin.AdminClient.GetWallets, o.adminClient)
}
// GetUserWalletBalanceRecords 获取用户余额变动记录列表
func (o *Api) GetUserWalletBalanceRecords(c *gin.Context) {
var req admin.GetUserWalletBalanceRecordsReq
if err := c.ShouldBindJSON(&req); err != nil {
apiresp.GinError(c, errs.ErrArgs.WrapMsg("invalid request"))
return
}
resp, err := o.adminClient.GetUserWalletBalanceRecords(c, &req)
if err != nil {
apiresp.GinError(c, err)
return
}
// 转换列表格式Ant Design Pro 标准格式)
list := make([]map[string]interface{}, 0, len(resp.List))
for _, record := range resp.List {
list = append(list, 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,
"list": list,
})
}
// UpdateUserPaymentPassword 修改用户支付密码(后台)
func (o *Api) UpdateUserPaymentPassword(c *gin.Context) {
a2r.Call(c, admin.AdminClient.UpdateUserPaymentPassword, o.adminClient)
}
// SetUserWithdrawAccount 设置用户提款账号(后台)
func (o *Api) SetUserWithdrawAccount(c *gin.Context) {
a2r.Call(c, admin.AdminClient.SetUserWithdrawAccount, o.adminClient)
}
// ==================== 提现管理相关 API操作 withdraw_applications====================
// GetWithdraw 获取提现申请详情
func (o *Api) GetWithdraw(c *gin.Context) {
var req admin.GetWithdrawReq
if err := c.ShouldBindJSON(&req); err != nil {
apiresp.GinError(c, errs.ErrArgs.WrapMsg("invalid request"))
return
}
resp, err := o.adminClient.GetWithdraw(c, &req)
if err != nil {
apiresp.GinError(c, err)
return
}
// 构建响应
result := map[string]interface{}{
"id": resp.Withdraw.Id,
"userID": resp.Withdraw.UserID,
"amount": resp.Withdraw.Amount,
"withdrawAccount": resp.Withdraw.WithdrawAccount,
"status": resp.Withdraw.Status,
"auditorID": resp.Withdraw.AuditorID,
"auditTime": resp.Withdraw.AuditTime,
"auditRemark": resp.Withdraw.AuditRemark,
"ip": resp.Withdraw.Ip,
"deviceID": resp.Withdraw.DeviceID,
"platform": resp.Withdraw.Platform,
"deviceModel": resp.Withdraw.DeviceModel,
"deviceBrand": resp.Withdraw.DeviceBrand,
"osVersion": resp.Withdraw.OsVersion,
"appVersion": resp.Withdraw.AppVersion,
"createTime": resp.Withdraw.CreateTime,
"updateTime": resp.Withdraw.UpdateTime,
}
apiresp.GinSuccess(c, map[string]interface{}{"withdraw": result})
}
// GetUserWithdraws 获取用户的提现申请列表
func (o *Api) GetUserWithdraws(c *gin.Context) {
var req admin.GetUserWithdrawsReq
if err := c.ShouldBindJSON(&req); err != nil {
apiresp.GinError(c, errs.ErrArgs.WrapMsg("invalid request"))
return
}
resp, err := o.adminClient.GetUserWithdraws(c, &req)
if err != nil {
apiresp.GinError(c, err)
return
}
// 转换列表格式
list := make([]map[string]interface{}, 0, len(resp.List))
for _, withdraw := range resp.List {
item := map[string]interface{}{
"id": withdraw.Id,
"userID": withdraw.UserID,
"amount": withdraw.Amount,
"withdrawAccount": withdraw.WithdrawAccount,
"status": withdraw.Status,
"auditorID": withdraw.AuditorID,
"auditTime": withdraw.AuditTime,
"auditRemark": withdraw.AuditRemark,
"ip": withdraw.Ip,
"deviceID": withdraw.DeviceID,
"platform": withdraw.Platform,
"deviceModel": withdraw.DeviceModel,
"deviceBrand": withdraw.DeviceBrand,
"osVersion": withdraw.OsVersion,
"appVersion": withdraw.AppVersion,
"createTime": withdraw.CreateTime,
"updateTime": withdraw.UpdateTime,
}
// 添加用户实名认证信息(如果存在)
if withdraw.RealNameAuth != nil {
item["realNameAuth"] = map[string]interface{}{
"idCard": withdraw.RealNameAuth.IdCard,
"name": withdraw.RealNameAuth.Name,
"idCardPhotoFront": withdraw.RealNameAuth.IdCardPhotoFront,
"idCardPhotoBack": withdraw.RealNameAuth.IdCardPhotoBack,
"auditStatus": withdraw.RealNameAuth.AuditStatus,
}
}
list = append(list, item)
}
apiresp.GinSuccess(c, map[string]interface{}{
"total": resp.Total,
"list": list,
})
}
// GetWithdraws 获取提现申请列表(后台,支持按状态筛选)
func (o *Api) GetWithdraws(c *gin.Context) {
var req admin.GetWithdrawsReq
if err := c.ShouldBindJSON(&req); err != nil {
apiresp.GinError(c, errs.ErrArgs.WrapMsg("invalid request"))
return
}
resp, err := o.adminClient.GetWithdraws(c, &req)
if err != nil {
apiresp.GinError(c, err)
return
}
// 转换列表格式
list := make([]map[string]interface{}, 0, len(resp.List))
for _, withdraw := range resp.List {
item := map[string]interface{}{
"id": withdraw.Id,
"userID": withdraw.UserID,
"amount": withdraw.Amount,
"withdrawAccount": withdraw.WithdrawAccount,
"status": withdraw.Status,
"auditorID": withdraw.AuditorID,
"auditTime": withdraw.AuditTime,
"auditRemark": withdraw.AuditRemark,
"ip": withdraw.Ip,
"deviceID": withdraw.DeviceID,
"platform": withdraw.Platform,
"deviceModel": withdraw.DeviceModel,
"deviceBrand": withdraw.DeviceBrand,
"osVersion": withdraw.OsVersion,
"appVersion": withdraw.AppVersion,
"createTime": withdraw.CreateTime,
"updateTime": withdraw.UpdateTime,
}
// 添加用户实名认证信息(如果存在)
if withdraw.RealNameAuth != nil {
item["realNameAuth"] = map[string]interface{}{
"idCard": withdraw.RealNameAuth.IdCard,
"name": withdraw.RealNameAuth.Name,
"idCardPhotoFront": withdraw.RealNameAuth.IdCardPhotoFront,
"idCardPhotoBack": withdraw.RealNameAuth.IdCardPhotoBack,
"auditStatus": withdraw.RealNameAuth.AuditStatus,
}
}
list = append(list, item)
}
apiresp.GinSuccess(c, map[string]interface{}{
"total": resp.Total,
"list": list,
})
}
// AuditWithdraw 批量审核提现申请
func (o *Api) AuditWithdraw(c *gin.Context) {
var req admin.AuditWithdrawReq
if err := c.ShouldBindJSON(&req); err != nil {
apiresp.GinError(c, errs.ErrArgs.WrapMsg("invalid request"))
return
}
resp, err := o.adminClient.AuditWithdraw(c, &req)
if err != nil {
apiresp.GinError(c, err)
return
}
// 构建响应
result := map[string]interface{}{
"successCount": resp.SuccessCount,
"failCount": resp.FailCount,
"failedIDs": resp.FailedIDs,
}
apiresp.GinSuccess(c, result)
}
// GetRealNameAuths 获取实名认证列表(支持按审核状态筛选)
func (o *Api) GetRealNameAuths(c *gin.Context) {
var req admin.GetRealNameAuthsReq
if err := c.ShouldBindJSON(&req); err != nil {
apiresp.GinError(c, errs.ErrArgs.WrapMsg("invalid request"))
return
}
resp, err := o.adminClient.GetRealNameAuths(c, &req)
if err != nil {
apiresp.GinError(c, err)
return
}
// 转换列表格式
list := make([]map[string]interface{}, 0, len(resp.List))
for _, auth := range resp.List {
list = append(list, map[string]interface{}{
"userID": auth.UserID,
"nickname": auth.Nickname,
"faceURL": auth.FaceURL,
"idCard": auth.IdCard,
"name": auth.Name,
"idCardPhotoFront": auth.IdCardPhotoFront,
"idCardPhotoBack": auth.IdCardPhotoBack,
"auditStatus": auth.AuditStatus,
"createTime": auth.CreateTime,
"updateTime": auth.UpdateTime,
})
}
apiresp.GinSuccess(c, map[string]interface{}{
"total": resp.Total,
"list": list,
})
}
// AuditRealNameAuth 审核实名认证(通过/拒绝)
func (o *Api) AuditRealNameAuth(c *gin.Context) {
var req admin.AuditRealNameAuthReq
if err := c.ShouldBindJSON(&req); err != nil {
apiresp.GinError(c, errs.ErrArgs.WrapMsg("invalid request"))
return
}
resp, err := o.adminClient.AuditRealNameAuth(c, &req)
if err != nil {
apiresp.GinError(c, err)
return
}
apiresp.GinSuccess(c, resp)
}