feat: add user ban for admin

pull/80/head
ROC 2 years ago
parent 315060233b
commit c1ed0dfb4a

@ -0,0 +1,25 @@
package middleware
import (
"github.com/gin-gonic/gin"
"github.com/rocboss/paopao-ce/internal/model"
"github.com/rocboss/paopao-ce/pkg/app"
"github.com/rocboss/paopao-ce/pkg/errcode"
)
func Admin() gin.HandlerFunc {
return func(c *gin.Context) {
if user, exist := c.Get("USER"); exist {
if userModel, ok := user.(*model.User); ok {
if userModel.Status == model.UserStatusNormal && userModel.IsAdmin {
c.Next()
return
}
}
}
response := app.NewResponse(c)
response.ToErrorResponse(errcode.NoAdminPermission)
c.Abort()
}
}

@ -251,6 +251,36 @@ func BindUserPhone(c *gin.Context) {
response.ToResponse(nil)
}
// 修改用户状态
func ChangeUserStatus(c *gin.Context) {
param := service.ChangeUserStatusReq{}
response := app.NewResponse(c)
valid, errs := app.BindAndValid(c, &param)
if !valid {
logrus.Errorf("app.BindAndValid errs: %v", errs)
response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...))
return
}
if param.Status != model.UserStatusNormal && param.Status != model.UserStatusClosed {
response.ToErrorResponse(errcode.InvalidParams)
return
}
user, err := service.GetUserByID(param.ID)
if err != nil {
logrus.Errorf("service.GetUserByID err: %v\n", err)
response.ToErrorResponse(errcode.NoExistUsername)
return
}
// 执行更新
user.Status = param.Status
service.UpdateUserInfo(user)
response.ToResponse(nil)
}
func GetUserProfile(c *gin.Context) {
response := app.NewResponse(c)
username := c.Query("username")

@ -76,6 +76,7 @@ func NewRouter() *gin.Engine {
// 鉴权路由组
authApi := r.Group("/").Use(middleware.JWT())
privApi := r.Group("/").Use(middleware.JWT()).Use(middleware.Priv())
adminApi := r.Group("/").Use(middleware.JWT()).Use(middleware.Admin())
{
// 同步索引
authApi.GET("/sync/index", api.SyncSearchIndex)
@ -173,6 +174,8 @@ func NewRouter() *gin.Engine {
// 删除评论回复
privApi.DELETE("/post/comment/reply", api.DeletePostCommentReply)
// 管理·禁言/解封用户
adminApi.POST("/admin/user/status", api.ChangeUserStatus)
}
// 默认404
e.NoRoute(func(c *gin.Context) {

@ -52,6 +52,11 @@ type ChangeAvatarReq struct {
Avatar string `json:"avatar" form:"avatar" binding:"required"`
}
type ChangeUserStatusReq struct {
ID int64 `json:"id" form:"id" binding:"required"`
Status int `json:"status" form:"status" binding:"required"`
}
const LOGIN_ERR_KEY = "PaoPaoUserLoginErr"
const MAX_LOGIN_ERR_TIMES = 10
@ -226,6 +231,20 @@ func GetUserInfo(param *AuthRequest) (*model.User, error) {
return nil, errcode.UnauthorizedAuthNotExist
}
func GetUserByID(id int64) (*model.User, error) {
user, err := ds.GetUserByID(id)
if err != nil {
return nil, err
}
if user.Model != nil && user.ID > 0 {
return user, nil
}
return nil, errcode.NoExistUsername
}
func GetUserByUsername(username string) (*model.User, error) {
user, err := ds.GetUserByUsername(username)

@ -22,6 +22,7 @@ var (
MaxPhoneCaptchaUseTimes = NewError(20019, "手机验证码已达最大使用次数")
NicknameLengthLimit = NewError(20020, "昵称长度2~12")
NoExistUsername = NewError(20021, "用户不存在")
NoAdminPermission = NewError(20022, "无管理权限")
GetPostsFailed = NewError(30001, "获取动态列表失败")
CreatePostFailed = NewError(30002, "动态发布失败")

@ -1,35 +1,40 @@
<template>
<n-config-provider :theme="theme">
<n-message-provider>
<div
class="app-container"
:class="{ dark: theme?.name === 'dark' }"
>
<div has-sider class="main-wrap" position="static">
<!-- -->
<sidebar />
<n-dialog-provider>
<div
class="app-container"
:class="{ dark: theme?.name === 'dark' }"
>
<div has-sider class="main-wrap" position="static">
<!-- -->
<sidebar />
<div class="content-wrap">
<router-view class="app-wrap" v-slot="{ Component }">
<keep-alive>
<div class="content-wrap">
<router-view
class="app-wrap"
v-slot="{ Component }"
>
<keep-alive>
<component
v-if="$route.meta.keepAlive"
:is="Component"
/>
</keep-alive>
<component
v-if="$route.meta.keepAlive"
v-if="!$route.meta.keepAlive"
:is="Component"
/>
</keep-alive>
<component
v-if="!$route.meta.keepAlive"
:is="Component"
/>
</router-view>
</div>
</router-view>
</div>
<!-- -->
<rightbar />
<!-- -->
<rightbar />
</div>
<!-- / -->
<auth />
</div>
<!-- / -->
<auth />
</div>
</n-dialog-provider>
</n-message-provider>
<n-global-style />
</n-config-provider>
@ -39,17 +44,18 @@
import { computed } from 'vue';
import { useStore } from 'vuex';
import { darkTheme } from 'naive-ui';
import { version, buildTime } from "../build/info.json";
import { version, buildTime } from '../build/info.json';
const store = useStore();
const theme = computed(() => (store.state.theme === 'dark' ? darkTheme : null));
console.log(`%c Release Build Info
console.log(
`%c Release Build Info
%cVersion v${version}
BuildTime ${buildTime}`
, "background:#000;color:#FFF;font-weight:bold;"
, "background:#FFF;color:#000;"
)
BuildTime ${buildTime}`,
'background:#000;color:#FFF;font-weight:bold;',
'background:#FFF;color:#000;'
);
</script>
<style lang="less">

@ -232,3 +232,16 @@ export const getAttachment = (params: NetParams.UserGetAttachment): Promise<NetR
params
});
};
/**
* ·/
* @param {Object} data
* @returns Promise
*/
export const changeUserStatus = (data: NetParams.UserStatusReq): Promise<NetReq.UserChangeStatus> => {
return request({
method: 'post',
url: '/v1/admin/user/status',
data
});
};

@ -66,6 +66,11 @@ declare module NetParams {
page_size: number
}
interface UserStatusReq {
id: number,
status: number
}
interface UserReqRecharge {
amount: number
}
@ -181,7 +186,7 @@ declare module NetParams {
content: string
}
interface PostDeleteCommentReply{
interface PostDeleteCommentReply {
id: number
}

@ -86,13 +86,17 @@ declare module NetReq {
}
interface UserChangeNickname {
}
interface UserChangePassword {
}
interface UserChangeStatus {
}
type PostGetPost = Item.PostProps
interface PostGetPosts {
@ -140,19 +144,19 @@ declare module NetReq {
type PostCreatePost = Item.PostProps
interface PostDeletePost {
}
type PostCreateComment = Item.CommentProps
interface PostDeleteComment {
}
type PostCreateCommentReply = Item.ReplyProps
interface PostDeleteCommentReply{
interface PostDeleteCommentReply {
}
}

@ -16,7 +16,7 @@ declare module Item {
/** 用户余额(分) */
balance?: number,
/** 用户状态 */
status?: 0 | 1
status?: 1 | 2
}
/** 评论内容 */
@ -160,7 +160,7 @@ declare module Item {
/** 内容列表 */
contents: PostItemProps[],
/** 标签列表 */
tags: {[key: string]: number} | string,
tags: { [key: string]: number } | string,
/** 是否锁定 */
is_lock: number,
/** 是否置顶 */

@ -17,8 +17,8 @@
<div class="uid">UID. {{ user.id }}</div>
</div>
<div class="user-opts">
<n-space vertical>
<div class="user-opts" v-if="store.state.userInfo.id > 0">
<n-space>
<n-button
size="small"
secondary
@ -27,6 +27,15 @@
>
</n-button>
<n-button
v-if="store.state.userInfo.is_admin"
size="small"
secondary
:type="user.status === 1 ? 'error' : 'warning'"
@click="banUser"
>
{{ user.status === 1 ? '' : '' }}
</n-button>
</n-space>
</div>
</div>
@ -72,17 +81,22 @@
import { ref, reactive, watch, onMounted } from 'vue';
import { useStore } from 'vuex';
import { useRoute } from 'vue-router';
import { getUserProfile, getUserPosts } from '@/api/user';
import { getUserProfile, getUserPosts, changeUserStatus } from '@/api/user';
import { useDialog, useMessage } from 'naive-ui';
const message = useMessage();
const dialog = useDialog();
const store = useStore();
const route = useRoute();
const loading = ref(false);
const user = reactive({
const user = reactive<Item.UserInfo>({
id: 0,
avatar: '',
username: '',
nickname: '',
is_admin: false,
status: 1,
});
const userLoading = ref(false);
const showWhisper = ref(false);
@ -121,6 +135,8 @@ const loadUser = () => {
user.avatar = res.avatar;
user.username = res.username;
user.nickname = res.nickname;
user.is_admin = res.is_admin;
user.status = res.status;
loadPosts();
})
.catch((err) => {
@ -135,12 +151,37 @@ const updatePage = (p: number) => {
};
const openWhisper = () => {
// window.$message.warning('您尚未获得私信权限');
showWhisper.value = true;
};
const whisperSuccess = () => {
showWhisper.value = false;
};
const banUser = () => {
dialog.warning({
title: '',
content:
'' +
(user.status === 1 ? '' : '') +
'',
positiveText: '',
negativeText: '',
onPositiveClick: () => {
userLoading.value = true;
changeUserStatus({
id: user.id,
status: user.status === 1 ? 2 : 1,
})
.then((res) => {
userLoading.value = false;
loadUser();
})
.catch((err) => {
userLoading.value = false;
console.log(err);
});
},
});
};
watch(
() => ({
path: route.path,
@ -186,6 +227,12 @@ onMounted(() => {
opacity: 0.75;
}
}
.user-opts {
position: absolute;
top: 16px;
right: 16px;
}
}
.pagination-wrap {

Loading…
Cancel
Save