Skip to content

Commit

Permalink
Merge pull request eee555#52 from putianyi889/debug-4
Browse files Browse the repository at this point in the history
增加管理员操作接口与界面
  • Loading branch information
eee555 authored Jun 28, 2024
2 parents 42c72bd + 2a772b3 commit ded84e0
Show file tree
Hide file tree
Showing 10 changed files with 176 additions and 54 deletions.
5 changes: 2 additions & 3 deletions back_end/saolei/userprofile/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,13 @@
path('register/', views.user_register, name='register'),
path('retrieve/', views.user_retrieve, name='retrieve'),
path('set_staff/', views.set_staff, name='set_staff'),
path('set_banned/', views.set_banned, name='set_banned'),
path('del_user_info/', views.del_user_info, name='del_user_info'),
# path('delete/<int:id>/', views.user_delete, name='delete'),
path('captcha/', include('captcha.urls')),
path('refresh_captcha/',views.refresh_captcha),
path('get_email_captcha/',views.get_email_captcha),
path('modify_realname/',views.modify_realname_n),
path('modify/',views.modify_n),
path('get/',views.get_userProfile),
path('set/',views.set_userProfile),

# path('captcha/captcha', views.captcha, name='captcha'),
# path('edit/<int:id>/', views.profile_edit, name='edit'),
Expand Down
81 changes: 33 additions & 48 deletions back_end/saolei/userprofile/views.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import logging
logger = logging.getLogger(__name__)
from django.contrib.auth import authenticate, login, logout
from django.http import HttpResponse, JsonResponse
from django.http import HttpResponse, JsonResponse, HttpResponseBadRequest, HttpResponseForbidden, HttpResponseNotFound
from .forms import UserLoginForm, UserRegisterForm, UserRetrieveForm, EmailForm
from captcha.models import CaptchaStore
import json
Expand All @@ -15,7 +15,6 @@
from django.conf import settings
from config.flags import EMAIL_SKIP


# Create your views here.


Expand Down Expand Up @@ -204,29 +203,6 @@ def set_staff(request):
else:
return HttpResponse("别瞎玩")

# 【管理员】封禁用户。封禁后,用户可以登录,但不能上传录像、不能改任何个人信息
# http://127.0.0.1:8000/userprofile/set_banned/?id=1&is_banned=True
def set_banned(request):
if request.user.is_staff and request.method == 'GET':
user = UserProfile.objects.get(id=request.GET["id"])
if user.is_staff and not request.user.is_superuser:
return HttpResponse("没有封禁管理员的权限!")
# user.is_banned = request.GET["is_banned"]
logger.info(f'{request.user.id} set_banned {request.GET["id"]} {request.GET["is_banned"]}')
if request.GET["is_banned"] == "True":
user.is_banned = True
user.save()
return HttpResponse(f'封禁用户"{user.realname}"成功!')
elif request.GET["is_banned"] == "False":
user.is_banned = False
user.save()
return HttpResponse(f'解封用户"{user.realname}"成功!')
else:
return HttpResponse('失败!is_banned需要为"True"或"False"')
else:
return HttpResponse("别瞎玩")


# 【管理员】删除用户的个人信息,从服务器磁盘上完全删除,但不影响是否封禁
# http://127.0.0.1:8000/userprofile/del_user_info/?id=1
def del_user_info(request):
Expand Down Expand Up @@ -309,28 +285,37 @@ def judge_captcha(captchaStr, captchaHashkey):
return False


# 【管理员】给用户增加1次修改姓名的机会
# http://127.0.0.1:8000/userprofile/modify_realname?id=1
def modify_realname_n(request):
if request.user.is_staff and request.method == 'GET':
user = UserProfile.objects.get(id=request.GET["id"])
user.left_realname_n += 1
logger.info(f'{request.user.id} add left_realname_n for {request.GET["id"]} ({user.left_realname_n})')
return HttpResponse(f"为用户\"{user.realname}\"(id: {user.id})增加一次修改姓名的次数成功!")
else:
return HttpResponse("别瞎玩")

get_userProfile_fields = ["id", "userms__designators", "userms__video_num_limit", "username", "first_name", "last_name", "email", "realname", "signature", "country", "left_realname_n", "left_avatar_n", "left_signature_n", "is_banned"]

# 【站长】给用户增加x、y、z次(对应)修改姓名、头像和签名的机会
# http://127.0.0.1:8000/userprofile/modify?id=1&x=0&y=1&z=200
def modify_n(request):
if request.user.is_superuser and request.method == 'GET':
user = UserProfile.objects.get(id=request.GET["id"])
user.left_realname_n += request.GET["x"]
user.left_avatar_n += request.GET["y"]
user.left_signature_n += request.GET["z"]
logger.info(f'{request.user.id}(superuser) modify_n for {request.GET["id"]} ({user.left_realname_n}, {user.left_avatar_n}, {user.left_signature_n})')
return HttpResponse(f"为用户\"{user.realname}\"(id: {user.id})增加修改姓名、头像、签名的次数成功!目前剩余({user.left_realname_n}, {user.left_avatar_n}, {user.left_signature_n})")
def get_userProfile(request):
if request.method != 'GET':
return HttpResponseBadRequest()
if request.user.is_staff:
list = UserProfile.objects.filter(id=request.GET["id"]).values(*get_userProfile_fields)
if len(list) == 0:
return HttpResponseNotFound()
return JsonResponse(list[0])
else:
return HttpResponse("别瞎玩")

return HttpResponseForbidden()

set_userProfile_fields = ["id", "userms__designators", "userms__video_num_limit", "username", "first_name", "last_name", "email", "realname", "signature", "country", "left_realname_n", "left_avatar_n", "left_signature_n", "is_banned"]
def set_userProfile(request):
if request.method == 'POST':
if not request.user.is_staff:
return HttpResponseForbidden() # 非管理员不能使用该api
userid = request.POST.get("id")
user = UserProfile.objects.get(id=userid)
if user.is_staff and user != request.user:
return HttpResponseForbidden() # 不能修改除自己以外管理员的信息
field = request.POST.get("field")
if field not in set_userProfile_fields:
return HttpResponseForbidden() # 只能修改特定的域
if field == "is_banned" and user.is_superuser:
return HttpResponseForbidden() # 站长不可被封禁
value = request.POST.get("value")
logger.info(f'{request.user.id}(staff) changes {userid}.{field} from {getattr(user, field)} to {value}')
setattr(user, field, value)
user.save()
return HttpResponse()
else:
return HttpResponseBadRequest()
2 changes: 1 addition & 1 deletion front_end/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"@types/node": "^20.5.1",
"axios": "^1.7.2",
"echarts": "^5.5.0",
"element-plus": "^2.2.32",
"element-plus": "^2.7.0",
"flag-icon-css": "^4.1.7",
"highlight.js": "^11.9.0",
"image-conversion": "^2.1.1",
Expand Down
2 changes: 1 addition & 1 deletion front_end/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ body {
height: v-bind("local.menu_height + 'px'");
position: fixed;
width: 100%;
z-index: 2010;
z-index: 1010; //message的z索引为2015
user-select: none;
overflow-x: auto;
overflow-y: hidden;
Expand Down
13 changes: 13 additions & 0 deletions front_end/src/i18n/locales/zh-cn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ export const zhCn = {
local: 'zh-cn',
name: '简体中文',
common: {
action: {
getUserProfile: '查询用户',
setUserProfile: '修改用户',
},
hide: '隐藏',
level: {
b: '初级',
Expand All @@ -17,6 +21,8 @@ export const zhCn = {
dg: '递归'
},
msg: {
actionFail: '{0}失败!',
actionSuccess: '{0}成功',
agreeTAC: '请同意用户协议!',
confirmPasswordFail: '两次输入的密码不一致!',
connectionFail: '无法连接到服务器!',
Expand Down Expand Up @@ -46,6 +52,13 @@ export const zhCn = {
timems: '用时',
upload_time: '上传时间',
},
response: {
OK: '',
BadRequest: '无法识别的请求',
Forbidden: '权限不足',
InternalServerError: '后端发生错误',
NotFound: '找不到数据',
},
show: '显示',
toDo: '敬请期待',
},
Expand Down
5 changes: 5 additions & 0 deletions front_end/src/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ const routes: Array<RouteRecordRaw> = [
name: 'upload',
component: () => import('../views/UploadView.vue')
},
{
path: '/staff',
name: 'staff',
component: () => import('../views/StaffView.vue')
},
]

const router = createRouter({
Expand Down
1 change: 1 addition & 0 deletions front_end/src/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export const useLocalStore = defineStore('local', {
menu_font_size: 18,
menu_height: 60,
menu_icon: false,
notification_duration: 4500,
}),
persist: true,
})
23 changes: 23 additions & 0 deletions front_end/src/utils/system/status.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { useLocalStore } from "@/store";
import { ElNotification } from "element-plus"
const local = useLocalStore();

const notificationType = ['', '', 'success', '', 'error', 'error'];
const notificationTitle = ['', '', 'common.msg.actionSuccess', '', 'common.msg.actionFail', 'common.msg.actionFail'];
const notificationMessage: { [code: number]: string} = {
200: 'common.response.OK',
400: 'common.response.BadRequest',
403: 'common.response.Forbidden',
404: 'common.response.NotFound',
500: 'common.response.InternalServerError',
};

export function generalNotification(t: any, status: number, action: string) {
let type = Math.floor(status / 100);
ElNotification({
title: t.t(notificationTitle[type], [action]),
message: t.t(notificationMessage[status]),
type: notificationType[type],
duration: local.notification_duration,
})
}
8 changes: 7 additions & 1 deletion front_end/src/views/SettingView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@
<el-descriptions-item label="菜单高度">
<el-slider v-model="local.menu_height" size="small" :min="20" :max="60" style="width: 100px; display: inline-block; height: 9px"></el-slider>
</el-descriptions-item>
<el-descriptions-item label="菜单字号"><el-input-number v-model="local.menu_font_size" size="small" :min="10"></el-input-number></el-descriptions-item>
<el-descriptions-item label="菜单字号"><el-input-number v-model="local.menu_font_size" size="small" :min="10"/></el-descriptions-item>
<el-descriptions-item label="通知关闭"><el-tooltip>
<template #content>
通知自动关闭的时间,单位毫秒。<br/>值为0则不会自动关闭。
</template>
<el-input-number v-model="local.notification_duration" size="small" :min="0" :step="1000"/>
</el-tooltip></el-descriptions-item>
</el-descriptions>
<el-descriptions v-if="store.login_status == LoginStatus.IsLogin" title="个人信息" :column="3">
<el-descriptions-item label="用户id">{{ store.user.id }}</el-descriptions-item>
Expand Down
90 changes: 90 additions & 0 deletions front_end/src/views/StaffView.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<template>
<div>
用户ID
<el-input-number v-model="userid" :controls="false" :min="0"></el-input-number>
<el-button @click="getUser">查询</el-button>
</div>
<div>
域<el-select v-model="userfield">
<el-option v-for="field in descriptionitems" :value="field"></el-option>
</el-select>
</div>
<div>
值<el-input v-model="uservalue"></el-input>
</div>
<div>
<el-button @click="setUser(userid, userfield, uservalue)">修改</el-button>
</div>
<el-descriptions title="UserProfile">
<el-descriptions-item v-for="item in descriptionitems" :label="item">{{ userprofile[item] }}</el-descriptions-item>
</el-descriptions>
</template>

<script lang="ts" setup>
import useCurrentInstance from '@/utils/common/useCurrentInstance';
import { generalNotification } from '@/utils/system/status';
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
const t = useI18n();
const { proxy } = useCurrentInstance();
const userid = ref(0);
const userfield = ref("");
const uservalue = ref("");
const descriptionitems = ["username", "first_name", "last_name", "email", "realname", "country", "is_banned", "left_realname_n", "left_avatar_n", "left_signature_n"]
interface UserProfile {
userms__designators: Array<String>;
userms__video_num_limit: Number;
username: String;
first_name: String;
last_name: String;
email: String;
realname: String;
signature: String;
country: String;
is_banned: Boolean;
left_realname_n: Number;
left_avatar_n: Number;
left_signature_n: Number;
}
const userprofile = ref<UserProfile>({
userms__designators: [],
userms__video_num_limit: 0,
username: "",
first_name: "",
last_name: "",
email: "",
realname: "",
signature: "",
country: "",
is_banned: false,
left_realname_n: 0,
left_avatar_n: 0,
left_signature_n: 0,
});
const getUser = () => {
proxy.$axios.get('userprofile/get', {params: {id: userid.value}}).then(
function (response: any) {
userprofile.value = response.data;
}
).catch(error => {
generalNotification(t, error.response.status, t.t('common.action.getUserProfile'))
})
}
const setUser = (id: number, field: string, value: string) => {
proxy.$axios.post('userprofile/set/', {id: id, field: field, value: value}).then(
function (response: any) {
generalNotification(t, response.status, t.t('common.action.setUserProfile'));
getUser();
}
)
}
</script>

0 comments on commit ded84e0

Please sign in to comment.