用户角色权限(案例)

时间:2022-07-18 15:23:08
#权限管理:rbac:Role Based access control(角色访问控制(RBAC,Role-Based Access Control))
        # 一 根据用户获取权限, session中 中间件实现权限控制
# 二 菜单管理
#默认展开
#只显示当前用户菜单

一、权限用户表

a:SQL表结构

用户角色权限(案例)用户角色权限(案例)
from django.db import models

# Create your models here.
class User(models.Model):
"""
用户表
"""
username = models.CharField(verbose_name='用户名', max_length=32)
password
= models.CharField(verbose_name='密码', max_length=64)
email
= models.EmailField(verbose_name='邮箱')

def __str__(self):
return self.username


class Role(models.Model):
"""
角色表
"""
caption = models.CharField(verbose_name='角色', max_length=32)

def __str__(self):
return self.caption


class User2Role(models.Model):
"""
用户角色关系表
"""
user = models.ForeignKey(User, verbose_name='用户', related_name='roles')
role
= models.ForeignKey(Role, verbose_name='角色', related_name='users')

def __str__(self):
return '%s-%s' % (self.user.username, self.role.caption,)


class Menu(models.Model):
"""
菜单表
"""
caption = models.CharField(verbose_name='菜单名称', max_length=32)
parent
= models.ForeignKey('self', verbose_name='父菜单', related_name='p', null=True, blank=True)

def __str__(self):
prev
= ""
parent
= self.parent
while True:
if parent:
prev
= prev + '-' + str(parent.caption)
parent
= parent.parent
else:
break
return '%s-%s' % (prev, self.caption,)


class Permission(models.Model):
"""
权限
"""
caption = models.CharField(verbose_name='权限', max_length=32)
url
= models.CharField(verbose_name='URL正则', max_length=128)
menu
= models.ForeignKey(Menu, verbose_name='所属菜单', related_name='permissions',null=True,blank=True)

def __str__(self):
return "%s-%s" % (self.caption, self.url,)


class Action(models.Model):
"""
操作:增删改查
"""
caption = models.CharField(verbose_name='操作标题', max_length=32)
code
= models.CharField(verbose_name='方法', max_length=32)

def __str__(self):
return self.caption


class Permission2Action2Role(models.Model):
"""
权限操作关系表
"""
permission = models.ForeignKey(Permission, verbose_name='权限URL', related_name='actions')
action
= models.ForeignKey(Action, verbose_name='操作', related_name='permissions')
role
= models.ForeignKey(Role, verbose_name='角色', related_name='p2as')

class Meta:
unique_together
= (
(
'permission', 'action', 'role'),
)

def __str__(self):
return "%s-%s-%s" % (self.permission, self.action, self.role,)

models.py
models.py

用户角色权限(案例)

b.输出菜单

用户角色权限(案例)用户角色权限(案例)
urlpatterns = [
url(r
'^auth-menu.html$',view2.menu),

]
urls.py
用户角色权限(案例)用户角色权限(案例)
from django.contrib import admin

# Register your models here.
from app02 import models

admin.site.register(models.User)
admin.site.register(models.Role)
admin.site.register(models.User2Role)
admin.site.register(models.Menu)
admin.site.register(models.Permission)
admin.site.register(models.Action)
admin.site.register(models.Permission2Action2Role)
app02/admin.py
用户角色权限(案例)用户角色权限(案例)
def menu(request):

"""
需要用户名或用户ID,产出:用户关联所有菜单
:param request:
:
return:
"""

# 所有菜单:处理成当前用关联的菜单
all_menu_list
= models.Menu.objects.all().values('id', 'caption', 'parent_id')
"""
[
{
'id':1, 'caption':'菜单1', parent_id:None},
{
'id':2, 'caption':'菜单2', parent_id:None},
{
'id':3, 'caption':'菜单3', parent_id:None},
{
'id':4, 'caption':'菜单1-1', parent_id:1},
]

{
1:{'id':1, 'caption':'菜单1', parent_id:None,status:False,opened:False,child:[]},
2:{'id':2, 'caption':'菜单2', parent_id:None,status:False,opened:False,child:[]},
3:{'id':3, 'caption':'菜单3', parent_id:None,status:False,opened:False,child:[]},
5:{'id':4, 'caption':'菜单1-1', parent_id:1,status:False,opened:False,child:[]},
}
"""
user = models.User.objects.filter(username='alex').first()
role_list
= models.Role.objects.filter(users__user=user)
permission_list
= models.Permission2Action2Role.objects.filter(role__in=role_list).values('permission__id',
'permission__url',
'permission__menu_id',
'permission__caption').distinct()



"""

[
{
'permission__url':'/order.html','permission__caption': '订单管理','permission__menu_id': 1 },
{
'permission__url':'/order.html','permission__caption': '订单管理','permission__menu_id': 2 },
{
'permission__url':'/order.html','permission__caption': '订单管理','permission__menu_id': 3 },
{
'permission__url':'/order.html','permission__caption': '订单管理','permission__menu_id': 4 },
]
"""
##### 将权限挂靠到菜单上 ########
all_menu_dict
= {}
for row in all_menu_list:
row[
'child'] = [] # 添加孩子
row[
'status'] = False # 是否显示菜单
row[
'opened'] = False # 是否默认打开
all_menu_dict[row[
'id']] = row




for per in permission_list:
if not per['permission__menu_id']:
continue

item
= {
'id': per['permission__id'],
'caption': per['permission__caption'],
'parent_id': per['permission__menu_id'],
'url': per['permission__url'],
'status': True,
'opened': False
}

# print(item[
"url"])
if re.match(per['permission__url'],request.path_info):
#
if re.match(per['permission__url'], "/orders.html"):
item[
'opened'] = True
pid
= item['parent_id']
all_menu_dict[pid][
'child'].append(item)

# 将当前权限前辈status
=True
temp
= pid # 1.父亲ID
while not all_menu_dict[temp]['status']:
all_menu_dict[temp][
'status'] = True
temp
= all_menu_dict[temp]['parent_id']
if not temp:
break

# 将当前权限前辈opened
=True
if item['opened']:
temp1
= pid # 1.父亲ID
while not all_menu_dict[temp1]['opened']:
all_menu_dict[temp1][
'opened'] = True
temp1
= all_menu_dict[temp1]['parent_id']
if not temp1:
break
# ############ 处理菜单和菜单之间的等级关系 ############
"""
all_menu_dict = {
1:{'id':1, 'caption':'菜单1', parent_id:None,status:False,opened:False,child:[{'permission__url':'/order.html','permission__caption': '订单管理','permission__menu_id': 1 },]},
2:{'id':2, 'caption':'菜单2', parent_id:None,status:False,opened:False,child:[]},
3:{'id':3, 'caption':'菜单3', parent_id:None,status:False,opened:False,child:[]},
5:{'id':4, 'caption':'菜单1-1', parent_id:1,status:False,opened:False,child:[]},
}


all_menu_list
= [
{
'id':1, 'caption':'菜单1', parent_id:None,status:False,opened:False,child:[{'permission__url':'/order.html','permission__caption': '订单管理','permission__menu_id': 1 }, {'id':4, 'caption':'菜单1-1', parent_id:1,status:False,opened:False,child:[]},]},
{
'id':2, 'caption':'菜单2', parent_id:None,status:False,opened:False,child:[]},
{
'id':3, 'caption':'菜单3', parent_id:None,status:False,opened:False,child:[]},

]
"""

result
= []
for row in all_menu_list:
pid
= row['parent_id']
if pid:
all_menu_dict[pid][
'child'].append(row)
else:
result.append(row)


##################### 结构化处理结果 #####################
# print(result)
#
for row in result:
# # print(row[
'caption'], row['status'], row['opened'], )
# print(row)

##################### 通过结构化处理结果,生成菜单开始 #####################

def menu_tree(menu_list):
tpl1
= """
<div class='menu-item'>
<div class='menu-header'>{0}</div>
<div class='menu-body {2}'>{1}</div>
</div>
"""
tpl2 = """
<a href='{0}' class='{1}'>{2}</a>
"""

menu_str
= ""
for menu in menu_list:
if not menu['status']:
continue
# menu: 菜单,权限(url)
if menu.get('url'):
# 权限
menu_str
+= tpl2.format(menu['url'],'active' if menu['opened'] else "",menu['caption'])
else:
# 菜单
if menu['child']:
child_html
= menu_tree(menu['child'])
else:
child_html
= ""
menu_str
+= tpl1.format(menu['caption'], child_html,"" if menu['opened'] else 'hide')

return menu_str

menu_html
= menu_tree(result)




return render(request, "menu_html.html", {"menu_html":menu_html})
app02/views.py
用户角色权限(案例)用户角色权限(案例)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.menu
-body{
margin
-left: 20px;
}
.menu
-body a{
display: block;
}
.menu
-body a.active{
color: red;
}
.hide{
display: none;
}
</style>
</head>
<body>

{{ menu_html
|safe }}

<script src="/static/jquery-3.2.1.js"></script>
<script>
$(function () {
$(
".menu-header").click(function () {
$(
this).next().removeClass("hide").parent().siblings().find(".menu-body").addClass("hide")
})
})
</script>
</body>
</html>
menu_html

c.result 结果递归 debug调试

result =
[
{
'opened': True, 'parent_id': None, 'caption': '菜单一', 'id': 1, 'status': True,
'child': [
{
'opened': False, 'parent_id': 1, 'caption': '帅哥管理', 'id': 5, 'status': True, 'url': '/shuaige.html'},
{
'opened': True, 'parent_id': 1, 'caption': '菜单一 一', 'id': 4, 'status': True,
'child': [
{
'opened': True, 'parent_id': 4, 'caption': '用户管理', 'id': 1, 'status': True, 'url': '/users.html'},
{
'opened': False, 'parent_id': 4, 'caption': '订单管理', 'id': 2, 'status': True, 'url': '/orders.html'}
]
},
{
'opened': False, 'parent_id': 1, 'caption': '菜单一 二', 'id': 5, 'status': False, 'child': []},
{
'opened': False, 'parent_id': 1, 'caption': '菜单一 三', 'id': 6, 'status': False, 'child': []}
]
},
{
'opened': False, 'parent_id': None, 'caption': '菜单二', 'id': 2, 'status': False, 'child': []},
{
'opened': False, 'parent_id': None, 'caption': '菜单三', 'id': 3, 'status': False, 'child': []}
] 

 

result = [{'opened': True, 'parent_id': None, 'caption': '菜单一', 'id': 1, 'status': True, 'child': [{'opened': False, 'parent_id': 1, 'caption': '帅哥管理', 'id': 5, 'status': True, 'url': '/shuaige.html'}, {'opened': True, 'parent_id': 1, 'caption': '菜单一  一', 'id': 4, 'status': True, 'child': [{'opened': True, 'parent_id': 4, 'caption': '用户管理', 'id': 1, 'status': True, 'url': '/users.html'}, {'opened': False, 'parent_id': 4, 'caption': '订单管理', 'id': 2, 'status': True, 'url': '/orders.html'}]}, {'opened': False, 'parent_id': 1, 'caption': '菜单一 二', 'id': 5, 'status': False, 'child': []}, {'opened': False, 'parent_id': 1, 'caption': '菜单一  三', 'id': 6, 'status': False, 'child': []}]}, {'opened': False, 'parent_id': None, 'caption': '菜单二', 'id': 2, 'status': False, 'child': []}, {'opened': False, 'parent_id': None, 'caption': '菜单三', 'id': 3, 'status': False, 'child': []}]


def menu_tree(menu_list):
tpl1
= """
<div class='menu-item'>
<div class='menu-header'>{0}</div>
<div class='menu-body {2}'>{1}</div>
</div>
"""
tpl2 = """
<a href='{0}' class='{1}'>{2}</a>
"""

menu_str
= ""
for menu in menu_list:
if not menu['status']:
continue
# menu: 菜单,权限(url)
if menu.get('url'):
# 权限
menu_str
+= tpl2.format(menu['url'], 'active' if menu['opened'] else "", menu['caption'])
print(
"***", menu_str)
else:
# 菜单
if menu['child']:
child_html
= menu_tree(menu['child'])
print(
"----", child_html)
else:
child_html
= ""
print(
"111")
menu_str
+= tpl1.format(menu['caption'], child_html, "" if menu['opened'] else 'hide')
print(
"AAAAA", menu_str)
print(
123)
return menu_str


menu_html
= menu_tree(result)
print(
"xx", menu_html)

 

二、组件

生成公共app
- 权限限制
- 生成菜单
python3 manage.py startapp rbac  

配置文件:

用户角色权限(案例)用户角色权限(案例)
复制代码
#白名单url, 不验证

VALID_URL
= [
'/app01/.*',
'/app02/.*'
'/login.html'
'/logout.html'
]
config.py

rbac models:

用户角色权限(案例)用户角色权限(案例)
from django.db import models


class User(models.Model):
"""
用户表
"""
username = models.CharField(verbose_name='用户名', max_length=32)
password
= models.CharField(verbose_name='密码', max_length=64)
email
= models.EmailField(verbose_name='邮箱')

def __str__(self):
return self.username


class Role(models.Model):
"""
角色表
"""
caption = models.CharField(verbose_name='角色', max_length=32)

def __str__(self):
return self.caption


class User2Role(models.Model):
"""
用户角色关系表
"""
user = models.ForeignKey(User, verbose_name='用户', related_name='roles')
role
= models.ForeignKey(Role, verbose_name='角色', related_name='users')

def __str__(self):
return '%s-%s' % (self.user.username, self.role.caption,)


class Menu(models.Model):
"""
菜单表
"""
caption = models.CharField(verbose_name='菜单名称', max_length=32)
parent
= models.ForeignKey('self', verbose_name='父菜单', related_name='p', null=True, blank=True)

def __str__(self):
prev
= ""
parent
= self.parent
while True:
if parent:
prev
= prev + '-' + str(parent.caption)
parent
= parent.parent
else:
break
return '%s-%s' % (prev, self.caption,)


class Permission(models.Model):
"""
权限
"""
caption = models.CharField(verbose_name='权限', max_length=32)
url
= models.CharField(verbose_name='URL正则', max_length=128)
menu
= models.ForeignKey(Menu, verbose_name='所属菜单', related_name='permissions',null=True,blank=True)

def __str__(self):
return "%s-%s" % (self.caption, self.url,)


class Action(models.Model):
"""
操作:增删改查
"""
caption = models.CharField(verbose_name='操作标题', max_length=32)
code
= models.CharField(verbose_name='方法', max_length=32)

def __str__(self):
return self.caption


class Permission2Action2Role(models.Model):
"""
权限操作关系表
"""
permission = models.ForeignKey(Permission, verbose_name='权限URL', related_name='actions')
action
= models.ForeignKey(Action, verbose_name='操作', related_name='permissions')
role
= models.ForeignKey(Role, verbose_name='角色', related_name='p2as')

class Meta:
unique_together
= (
(
'permission', 'action', 'role'),
)

def __str__(self):
return "%s-%s-%s" % (self.permission, self.action, self.role,)
models.py

中间件认证

用户角色权限(案例)用户角色权限(案例)
#验证中间件


from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse
from rbac import config
import re


class RbacMiddleware(MiddlewareMixin):

def process_request(self,request,
*args,**kwargs):
for pattern in config.VALID_URL:
if re.match(pattern,request.path_info):
return None

action
= request.GET.get('md') # GET
user_permission_dict
= request.session.get('user_permission_dict')
if not user_permission_dict:
return HttpResponse('无权限')

# action_list
= user_permission_dict.get(request.path_info)
flag
= False
for k,v in user_permission_dict.items():
if re.match(k,request.path_info):
if action in v:
flag
= True
break
if not flag:
return HttpResponse('无权限')
/middleware/md.py

后端代码

用户角色权限(案例)用户角色权限(案例)
import re
from rbac import models
from django.utils.safestring import mark_safe

def permission_session(user_id,request):
"""

:param user_id: rbac中的user表中一条数据id
:param request:
:
return:
"""
# obj = models.User.objects.filter(username='杨明').first()
#
# # x
= models.User2Role.objects.filter(user_id=obj.id)
# # [User2Role,User2Role,User2Role]
#
# role_list
= models.Role.objects.filter(users__user_id=obj.id)
# # [Role,]
#
from django.db.models import Count
# # permission_list
= models.Permission2Action2Role.objects.filter(role__in=role_list).values('permission__url','action__code').annotate(c=Count('id'))
# permission_list
= models.Permission2Action2Role.objects.filter(role__in=role_list).values('permission__url','action__code').distinct()
"""
[
{permission_url:
'/index.html', action_code:'GET'},
{permission_url:
'/index.html', action_code:'POST'},
{permission_url:
'/index.html', action_code:'DEL'},
{permission_url:
'/index.html', action_code:'Edit'},
{permission_url:
'/order.html', action_code:'GET'},
{permission_url:
'/order.html', action_code:'POST'},
{permission_url:
'/order.html', action_code:'DEL'},
{permission_url:
'/order.html', action_code:'Edit'},
]
放在Session中
/index.html?md=GET

{
'/index.html': [GET,POST,DEL,Edit],
'/order.html': [GET,POST,DEL,Edit],
}

"""

user_permission_dict
= {
'/ah-index.html': ["GET","POST","DEL","Edit"],
'/order.html': ["GET","POST","DEL","Edit"],
'/index-(\d+).html': ["GET","POST","DEL","Edit"],
}

request.session[
'user_permission_dict'] = user_permission_dict


def menu(user_id,current_url):
"""
根据用户ID,当前URL:获取用户所有菜单以及权限,是否显示,是否打开
:param user_id:
:param current_url:
:
return:
"""
# 所有菜单:处理成当前用关联的菜单
all_menu_list
= models.Menu.objects.all().values('id','caption','parent_id')
user
= models.User.objects.filter(id=user_id).first()
role_list
= models.Role.objects.filter(users__user=user)
permission_list
= models.Permission2Action2Role.objects.filter(role__in=role_list).values('permission__id','permission__url','permission__menu_id','permission__caption').distinct()
##### 将权限挂靠到菜单上 ########
all_menu_dict
= {}
for row in all_menu_list:
row[
'child'] = [] # 添加孩子
row[
'status'] = False # 是否显示菜单
row[
'opened'] = False # 是否默认打开
all_menu_dict[row[
'id']] = row

for per in permission_list:
if not per['permission__menu_id']:
continue

item
= {
'id':per['permission__id'],
'caption':per['permission__caption'],
'parent_id':per['permission__menu_id'],
'url': per['permission__url'],
'status': True,
'opened': False
}
if re.match(per['permission__url'],current_url):
item[
'opened'] = True
pid
= item['parent_id']
all_menu_dict[pid][
'child'].append(item)

# 将当前权限前辈status
=True
temp
= pid # 1.父亲ID
while not all_menu_dict[temp]['status']:
all_menu_dict[temp][
'status'] = True
temp
= all_menu_dict[temp]['parent_id']
if not temp:
break

# 将当前权限前辈opened
=True
if item['opened']:
temp1
= pid # 1.父亲ID
while not all_menu_dict[temp1]['opened']:
all_menu_dict[temp1][
'opened'] = True
temp1
= all_menu_dict[temp1]['parent_id']
if not temp1:
break
# ############ 处理菜单和菜单之间的等级关系 ############
result
= []
for row in all_menu_list:
pid
= row['parent_id']
if pid:
all_menu_dict[pid][
'child'].append(row)
else:
result.append(row)


##################### 结构化处理结果 #####################
for row in result:
print(row[
'caption'],row['status'],row['opened'],row)


def menu_tree(menu_list):
tpl1
= """
<div class='menu-item'>
<div class='menu-header'>{0}</div>
<div class='menu-body {2}'>{1}</div>
</div>
"""
tpl2 = """
<a href='{0}' class='{1}'>{2}</a>
"""

menu_str
= ""
for menu in menu_list:
if not menu['status']:
continue
# menu: 菜单,权限(url)
if menu.get('url'):
# 权限
menu_str
+= tpl2.format(menu['url'],'active' if menu['opened'] else "",menu['caption'])
else:
# 菜单
if menu['child']:
child_html
= menu_tree(menu['child'])
else:
child_html
= ""
menu_str
+= tpl1.format(menu['caption'], child_html,"" if menu['opened'] else 'hide')

return menu_str
menu_html
= menu_tree(result)
return menu_html


# simple_tag
def css():
v
= """
<style>
.hide{
display: none;
}
.menu
-body{
margin
-left: 20px;
}
.menu
-body a{
display: block;
}
.menu
-body a.active{
color: red;
}
</style>
"""
return v

# simple_tag
def js():
v
= """
<script>
$(function(){

$(
'.menu-header').click(function(){
$(
this).next().removeClass('hide').parent().siblings().find('.menu-body').addClass('hide');

})

})
</script>
"""
return v
service.py

a:封装以后调用

加入中间件

用户角色权限(案例)用户角色权限(案例)
#自定义的中间件加入到setting

MIDDLEWARE
= [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'rbac.middleware.md.RbacMiddleware',
]
settings.py
#后端
from rbac import service

1. 用户登录后,拿到用户的ID,调用permission_session()函数(函数代码还没写)
函数获取用户角色的权限,格式如:
“”“
user_permission_dict
= {
'/ah-index.html': ["GET","POST","DEL","Edit"],
'/order.html': ["GET","POST","DEL","Edit"],
'/index-(\d+).html': ["GET","POST","DEL","Edit"],
}
”“”

def login():
  permission_session(用户ID,request)
  
return .....

2.setting中加入中间件,如上
  

3.#获取菜单
current_url
= request.pathinfo
menu_list
= service.menu(用户ID,current_url)

4.尽量用simple_tag
css
= servicr.css()
js
= servicr.js()

5.前端

{{ css
|safe }}
{{ menu_list
|safe }}
{{ js
|safe }}  

 a. RBAC原理:

用户角色权限(案例)用户角色权限(案例)
原理:
1. 简单管理
角色单一,无需使用权限管理

2. 角色多管理(权限)
a. 登录
session放置用户信息(检测是否已经登录)
session放置权限信息(检测是否有权访问)
{
'/index.html':[GET,EDIT],
'/order.html':[GET,EDIT],
'/xxx.html':[GET,EDIT...],
'/xxx.html':[GET,EDIT...],
'/xxx.html':[GET,EDIT...],
'/xxx.html':[GET,EDIT...],
'/xxx.html':[GET,EDIT...],
'/xxx.html':[GET,EDIT...],
'/xxx.html':[GET,EDIT...],
'/xxx.html':[GET,EDIT...],
'/xxx.html':[GET,EDIT...],
'/xxx.html':[GET,EDIT...],
'/xxx.html':[GET,EDIT...],
}

session放置菜单权限信息(用于生成动态多级菜单)

b. 访问网站其他功能: http:
//www.baiuc.om/xxx.hmtl
- 获取当前访问的URL, request.path_info
-
/xxx.hmtl?md=get
匹配1
/xxx.hmtl
session放置权限信息(检测是否有权访问)
{
'/index.html':[GET,EDIT],
'/order.html':[GET,EDIT],
'/xxx.html':[GET,EDIT...],
}

匹配2
/xxx.hmtl
session放置权限信息(检测是否有权访问)
{
'/index.html':[GET,EDIT],
'/order.html':[GET,EDIT],
'/xxx.html':[GET,EDIT...],
}

request.permission_code
= "EDIT"
request.permission_code_list
= [GET,EDIT...]


PS: 中间件实现

c. 视图函数

def xxx(request):
request.permission_code
= "EDIT" # 业务逻辑的编写
request.permission_code_list
= [GET,EDIT...] # 前端显示功能按钮

d. 模板


e. 创建动态菜单【多级菜单】
session中获取菜单权限信息(用于生成动态多级菜单)
- 当前用户权限
- 所有菜单
1. 权限挂到菜单上
2. 菜单父子关系处理
3. 递归生成菜单

辅助:
css
js


推荐:simple_tag



使用:【详细见README】
1. 导入rbac
-
- 中间件
- service
- simple_tag

2. 注册app

3. 用户登录
初始化权限信息: service.initail_permission(request,user_id)


4. settings中配置中间件

5. {% rbac_menu reqeust %}


其他配置:
...
RBAC(基于角色的权限管理原理及其使用)
用户角色权限(案例)用户角色权限(案例)
具体功能:
临时工:
报障单 trouble.html
查看列表 look
查看详细 detail

普通员工:
报障单 trouble.html
创建 post
删除 del
修改 edit
查看列表 look
查看详细 detail

运维人员:
报账单
解决报障单 trouble
-kill.html

总监:
报障单
解决报障单
报表 report.html
b. 应用实例