在上一篇博客中,我们对项目结构进行了一些调整,并且实现了查看考勤功能。今天我们来补全一些用户系统的功能:用户管理、批准用户、注销用户、用户登出这4个功能。
9 用户管理、批准、注销及登出
此功能的作用是批准用户的注册,并同时调整其用户组。只有得到了批准的用户在之后的权限系统中才能使用相关功能。该功能的页面如下:
在这个页面,我们可以看到所有的用户(除了Root用户外),也会显示出每个用户的邮箱、所属用户组、状态、注册时间和最后登录时间。在每行的最后,可以对用户进行注销或批准的操作。从图上可知,test用户和test1用户已经被批准,状态为Approved,最后的操作栏显示为注销;test2用户被注销,状态为Logoff;而test3用户为待批准状态,所以其最后的操作按钮显示批准。 用户组的下拉框将列出除Root和Visitor之外的所有用户组。
我们打开user_app.py,来实现UserManage的get请求:
# util/users/userutil.py
def getallusergroup():
usergroup = session.query(UserGroup).filter(and_(UserGroup.groupname != 'Root',UserGroup.groupname != 'Visitor'))
return usergroup
# user_app.py
class UserManage(BaseHandler):
def get(self):
usermanagepath = gettemplatepath('usermanage.html')
users = session.query(User).filter(User.username != 'Root')
usergroups = getallusergroup()
userInfos = []
for user in users:
userInfo = {}
usergrouplist = []
userInfo['username'] = user.username
userInfo['email'] = user.email
userInfo['usergroup'] = user.usergroup
if user.usergroup not in usergrouplist:
usergrouplist.append(user.usergroup)
userInfo['state'] = user.state
userInfo['registerdate'] = user.registerdate
userInfo['lastlogintime'] = user.lastlogintime
if user.state == 'Approved':
userInfo['submit'] = '/logoff'
else:
userInfo['submit'] = '/approve'
userInfo['formid'] = user.id
userInfo['groupid'] = str(user.id)+'_group'
for othergroup in usergroups:
if othergroup.groupname not in usergrouplist:
usergrouplist.append(othergroup.groupname)
userInfo['usergrouplist'] = usergrouplist
userInfos.append(userInfo)
self.render(usermanagepath,userInfos=userInfos,usergroups=usergrouplist)
# main.py
routelist = [
# ...
(r"/usermanage",UserManage),
# ...
]
在UserManage这个RequestHandler中,只包含一个get方法,它会把每个用户的相关信息组成字典,存入userInfos这个list中。值得注意的是formid和groupid这两个值,由于我们在一个页面上有多个表单,因此需要构造formid和groupid来区分每个表单,否则在获取表单值的时候会取错数据;此外,我们还要根据用户状态的不同来决定submit所对应的url是批准还是注销。
前端usermanage.html的代码如下:
<!--usermanage.html-->
{% block content %}
<div class="page-wrapper">
<!-- ============================================================== -->
<!-- Container fluid -->
<!-- ============================================================== -->
<div class="container-fluid">
<!-- ============================================================== -->
<!-- Bread crumb and right sidebar toggle -->
<!-- ============================================================== -->
<div class="row page-titles">
<div class="col-md-6 col-8 align-self-center">
<h3 class="text-themecolor m-b-0 m-t-0">用户审批</h3>
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/">Home</a></li>
<li class="breadcrumb-item active">用户审批</li>
</ol>
</div>
</div>
<!-- ============================================================== -->
<!-- End Bread crumb and right sidebar toggle -->
<!-- ============================================================== -->
<!-- ============================================================== -->
<!-- Start Page Content -->
<!-- ============================================================== -->
<div class="row">
<!-- column -->
<div class="col-sm-12">
<div class="card">
<div class="card-block">
<h4 class="card-title">用户审批</h4>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>#</th>
<th>用户名</th>
<th>邮箱</th>
<th>用户组</th>
<th>状态</th>
<th>注册时间</th>
<th>最后登录时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for userinfo in userInfos %}
<tr>
<form method="post" id="{{ userinfo['formid'] }}" action="{{ escape(userinfo['submit']) }}">
<td><input type="text" value="{{ userinfo['formid'] }}" readonly=true id='userid' name='userid'/></td>
<td><input type="text" value="{{ escape(userinfo['username']) }}" readonly=true id='username' name='username'/></td>
<td>{{ escape(userinfo['email']) }}</td>
<td><select class="form-control form-control-line" name="{{ escape(userinfo['groupid']) }}" id="{{ escape(userinfo['groupid']) }}" >
{% for usergroup in userinfo['usergrouplist'] %}
<option>{{ escape(usergroup) }}</option>
{% end %}
</select>
</td>
<td>{{ escape(userinfo['state']) }}</td>
<td>{{ userinfo['registerdate'] }}</td>
<td>{{ userinfo['lastlogintime'] }}</td>
{% if escape(userinfo['state']) == 'Approved' %}
<td><button type="submit" class="btn btn-danger">注销</button></td>
{% else %}
<td><button type="submit" class="btn btn-success">批准</button></td>
{% end %}
</form>
</tr>
{% end %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- ============================================================== -->
<!-- End PAge Content -->
<!-- ============================================================== -->
</div>
<!-- ============================================================== -->
<!-- End Container fluid -->
<!-- ============================================================== -->
<!-- ============================================================== -->
<!-- footer -->
<!-- ============================================================== -->
<footer class="footer text-center">
© 2020 Tornado考勤系统
</footer>
<!-- ============================================================== -->
<!-- End footer -->
<!-- ============================================================== -->
</div>
{% end %}
这里我们遍历userInfos这个list,为每个用户建立一个表单,注意我们的表单的action值也是从userInfo里获得的,对应批准或注销操作。
我们来看要如何批准或注销一个用户。批准用户或注销用户其实很简单,就是将用户的state字段的值改为Approved或Logoff;此外,我们还希望当用户得到批准时,向用户的邮箱发送一封email来提示用户,因此我们需要实现一个email模块。
我们在util包下建立email包,再在其中建立emailutil.py,开始实现发送email的功能:
from email.header import Header
from email.mime.text import MIMEText
import smtplib
from setting.globalsettings import getconfig
class Email():
def __init__(self,toaddr):
self.__smtpserver = getconfig('SMTPSERVER')
self.__fromaddr = getconfig('EMAIL')
self.__toaddr = toaddr
self.__password = 'ilegxhawxgchbfcc'
def sendemail(self,subject,content):
msg = MIMEText(content,'html','utf-8')
msg['From'] = self.__fromaddr
msg['To'] = self.__toaddr
subject = '[Tornado考勤系统] ' + subject
msg['Subject'] = Header(subject,'utf-8').encode()
server = smtplib.SMTP(self.__smtpserver, 25)
server.login(self.__fromaddr, self.__password)
try:
server.sendmail(self.__fromaddr, [self.__toaddr], msg.as_string())
except Exception as e:
print(e)
finally:
server.quit()
def sendapprovemail(username,usergroup,email):
approvemail = Email(email)
subject = '[Tornado考勤系统] 用户批准'
content = '''
<html>
<body>
<p>Dear %s:</p>
<p>您的注册已被批准,用户组为%s</p>
</body>
</html>
''' % (username,usergroup)
approvemail.sendemail(subject,content)
由于我没有outlook,因此这里使用传统的smtp库来发送email,如果大家有outlook的话,也可以使用outlook的相关win32包来发送邮件,比使用smtp的代码更加简洁。在Email类中,我们需要提供SMTP服务器、发件人、收件人以及密码。其中,SMTP服务器和发件人的邮箱我们要配置在globalsetting.ini文件中。在这里需要注意的是,这里的密码既不是邮箱的登录密码,也不是邮箱的独立密码(仅限QQ邮箱),而是在邮箱中开启SMTP服务时由供应商提供的一个密码。如果这里写成其他的密码,是无法发送邮件的。
sendemail函数接收subject(主题)和content(内容)作为参数。我们要使用MIMEText构造出邮件的结构,然后将From、To和Subject分别填入发件人、收件人以及主题。最后,使用SMTP服务器地址和25端口建立服务器,登录并发送邮件。sendapprovemail则是直接调用这个类,发送给用户一个告知批准的邮件。
有了email模块后,我们就可以写approveuser和logoffuser这两个util函数了。打开userutil.py,输入以下代码:
# util/users/userutil.py
def approveuser(username,usergroup):
user = session.query(User).filter(User.username == username).first()
result = 'Fail'
if type(user) is User:
user.state = 'Approved'
user.usergroup = usergroup
result = insertdata(user)
if result == 'Success':
sendapprovemail(user.username,usergroup,user.email)
return result
def logoffuser(username):
user = session.query(User).filter(User.username == username).first()
result = 'Fail'
if type(user) is User:
user.state = 'Logoff'
result = insertdata(user)
return result
approveuser包含两个参数:username和usergroup。函数功能是将用户的state改为Approved,并将用户组改为参数传入的用户组,在最后发送approve的邮件给用户;logoffuser仅包含用户名,操作也只是把用户状态改为Logoff。
回到user_app.py,输入以下代码实现Approve和Logoff操作:
# user_app.py
class Approve(BaseHandler):
def post(self):
userid = self.get_argument('userid')
username = self.get_argument('username')
usergroupid = userid + '_group'
usergroup = self.get_argument(usergroupid)
result = 'Fail'
result = approveuser(username,usergroup)
resultpath = gettemplatepath('result.html')
if result == 'Success':
self.redirect('/usermanage')
else:
result = '操作失败!'
self.render(resultpath,result=result)
class LogOff(BaseHandler):
def post(self):
username = self.get_argument('username')
result = 'Fail'
result = logoffuser(username)
resultpath = gettemplatepath('result.html')
if result == 'Success':
self.redirect('/usermanage')
else:
result = '操作失败!'
self.render(resultpath,result=result)
class LogOut(BaseHandler):
def get(self):
self.clear_cookie('currentuser')
self.redirect('/')
# main.py
routelist = [
# ...
(r"/approve",Approve),
(r"/logoff",LogOff),
(r"/logout",LogOut),
# ...
]
由于LogOut比较简单,因此在这里一并列出。Approve和LogOff两个操作均只有post,因为它们不需要显示任何界面。在Approve中,会从表单中获取id,用户名,用户组,并调用approveuser函数来完成批准,最后重定向到用户管理页面;在LogOff中,只需取得用户名便可完成注销用户;而LogOut则是将名为currentuser的cookie清空,并重定向到主页,完成用户登出的操作。
在这篇博客中,我们补全了一些关键的用户系统的功能:用户审批、用户注销以及登出用户,还实现了一个email模块用于发送审批邮件。在下一篇博客中,我们将实现审批考勤以及设置上下级用户的功能,希望大家继续关注~