一、什么是CRM(客戶關系管理)
1.客戶關系管理是指企業為提高核心競爭力,利用相應的信息技術以及互聯網技術協調企業與顧客間在銷售、營銷和服務上的交互,從而提升其管理方式,向客戶提供創新式的個性化的客戶交互和服務的過程。其最終目標是吸引新客戶、保留老客戶以及將已有客戶轉為忠實客戶,增加市場。(百度百科)
2.客戶關系管理(CRM)有三層含義:
(1)體現為新態企業管理的指導思想和理念
(2)是創新的企業管理模式和運營機制
(3)是企業管理中信息技術、軟硬件系統集成的管理方法和應用解決方案的總和。(百度百科)
圖片來源:https://www.zhihu.com/question/30124083
圖片來源:https://www.zhihu.com/question/30124083
二、CRM項目組成
1.rbac權限組件
2.stark通用增刪改查組件
3.CRM業務
三、rbac權限組件
3.1 rbac權限組件的由來
1.為什么程序需要權限控制?
- 在日常生活當中,我們每個人都扮演者自己的角色,在我們的程序當中,我們也要對登錄進來的用戶進行角色的划分,這樣就能方便我們對不同的用戶分配不同的權限,這時候我們就需要權限控制。
2.為什么我們要將這樣一個程序開發成組件的形式
- 在我們開發生涯中,可能會遇到一些功能類似,邏輯結構稍有不用,架構基本相同的業務,我們就可以將頻繁使用到的功能模塊開發成一個可以重復使用的組件
- 開發組件的過程雖然繁瑣,但是一旦開發完成,對於我們以后開發類似的業務時,只需要利用這個組件進行一些配置,就可以快速開發出我們想要的功能
3.在我們系統當中,什么是權限控制
- 說白了,權限控制就是我們對用戶訪問的url進行控制,我們通過代碼對用戶請求的url進行過濾,將用戶擁有的權限給予,將用戶沒有的權限去掉,這樣我們就能控制用戶的訪問行為。
3.2 rbac權限組件表結構設計
# 基於角色的權限控制
用戶表:
ID Name
角色表
ID title
用戶角色關系表:
ID 用戶ID 角色ID
權限表:
ID Url含正則的url
角色權限關系表:
ID 角色ID 權限ID
菜單表:
ID Title菜單名稱 Icon菜單圖標
3.3 rbac權限系統實現流程
1. 如何實現的權限系統?
粒度控制到按鈕級別的權限控制
- 用戶登陸成功之后,將權限和菜單信息放入session
- 每次請求時,在中間件中做權限校驗
- inclusion_tag實現的動態菜單
2. 如何實現控制到按鈕的呢?
用戶登陸時,用戶所擁有的權限 別名==django 路由name 構造成一個字典;
在頁面中寫了一個 django模板的filter來進行判斷是否顯示;
3. 為什么要在中間件中做校驗呢?
所有請求在到達視圖函數之前,必須經過中間件,所以在中間件中對請求做處理比較簡單;
4. 修改權限之后,如想應用最新權限
- 我們:需要重新登陸。
- 不用重新登陸,如何完成?更新涉及的所有用戶的session信息
- 當用戶登錄前,對用戶訪問的url進行白名單驗證,用戶登錄后將用戶擁有的權限初始化,存入到session當中,然后通過中間鍵過濾掉不能訪問的權限
3.4 技術點總結
- orm查詢
- 去空,去除沒有的權限
- 去重,去除重復的權限
- 中間件
- 利用中間能輕松對請求進行管理
- inclusion_tag
- 創建
- from django.template import Library
- register = Library()
- @register.inclusion_tag('rbac/menu.html')
- def menu(request):
- pass
- 使用
- {% load 'rbac' %}
- {{ menu request }}
- 母版
- layout.html
- {% block %}{% endblock %}
- 引入靜態文件
- {% load staticfiles%}
- {% static '' %}
- session
- 利用session能方便的存取權限信息
- filter
- 有序字典
- Python 3.5(含)以前字典不能保證有序
- from collections import OrderedDict
- dict1 = OrderedDDict()
- settings配置
- 利用settings設置session存儲的key,這樣我們就不需要頻繁改動
- namespace
- 在多個app中有相同的視圖函數或者url時,我們可以通過設置命名空間來區分不同app下的相同的視圖函數名的視圖
- 構造數據結構
- permission_dict = {'permission_name': {'id': 1, 'title': 'LLL'...}}
- 父子結構
- menu_dict = {'menu_id': {'title': 'rrrr', 'icon': 'asda', 'url': 'asdasd', 'children': [menu_node]}}
- ModelForm
- 利用ModelForm能快速根據表中的字段生成我們想要的表單
- 表單的驗證
- admin
- django自帶的后代管理,我們可以通過admin快速管理我們的數據庫
- icon爬蟲
- 利用爬蟲爬取fontawesome網站的圖標,為我們菜單提供圖標庫
- make-safe
- 保證我們向前端傳遞一個前端需要去渲染的html格式的文本能夠被瀏覽器識別成安全的html字符串
- 瀏覽器默認開啟了預防xss攻擊
- 下載文件
- formset
- 批量表單處理
點擊查看rbac使用手冊
使用rbac組件時,應用遵循以下規則:
-
清除rbac/migrations目錄下所有數據庫遷移記錄(保留__init__.py)
-
在項目路由系統中注冊rabc相關的路由信息,如:
urlpatterns = [
...
url(r'^rbac/', include('rbac.urls',namespace='rbac')),
]
-
注冊app
-
讓業務的用戶表繼承權限的UserInfo表
如:
rbac:
class UserInfo(models.Model):
"""
用戶表
"""
username = models.CharField(verbose_name='用戶名', max_length=32)
password = models.CharField(verbose_name='密碼', max_length=64)
email = models.CharField(verbose_name='郵箱', max_length=32)
roles = models.ManyToManyField(verbose_name='擁有的所有角色', to=Role, blank=True)
class Meta:
abstract = True
crm:
from rbac.models import UserInfo as RbacUserInfo
class UserInfo(RbacUserInfo):
"""
員工表
"""
name = models.CharField(verbose_name='真實姓名', max_length=16)
phone = models.CharField(verbose_name='手機號', max_length=32)
gender_choices = (
(1,'男'),
(2,'女'),
)
gender = models.IntegerField(verbose_name='性別',choices=gender_choices,default=1)
depart = models.ForeignKey(verbose_name='部門', to="Department")
def __str__(self):
return self.name
-
數據庫遷移
-
rbac提供URL
urlpatterns = [
url(r'^menu/list/$', permission.menu_list, name='menu_list'), # rbac:menu_list
url(r'^menu/add/$', permission.menu_add, name='menu_add'),
url(r'^menu/edit/(?P<pk>\d+)/$', permission.menu_edit, name='menu_edit'),
url(r'^menu/del/(?P<pk>\d+)/$', permission.menu_del, name='menu_del'),
url(r'^permission/add/$', permission.permission_add, name='permission_add'),
url(r'^permission/edit/(?P<pk>\d+)/$', permission.permission_edit, name='permission_edit'),
url(r'^permission/del/(?P<pk>\d+)/$', permission.permission_del, name='permission_del'),
url(r'^multi/permissions/$', permission.multi_permissions, name='multi_permissions'),
url(r'^distribute/permissions/$', permission.distribute_permissions, name='distribute_permissions'),
url(r'^role/list/$', permission.role_list, name='role_list'),
url(r'^role/edit/(?P<pk>\d+)/$', permission.role_edit, name='role_edit'),
url(r'^role/del/(?P<pk>\d+)/$', permission.role_del, name='role_del'),
]
- 配置文件寫上用戶表的類的路徑
USER_MODEL_PATH = "crm.models.UserInfo"
- 錄入權限信息
http://127.0.0.1:8000/rbac/menu/list/
http://127.0.0.1:8000/rbac/multi/permissions/?type=update
- 權限分配
創建角色:http://127.0.0.1:8000/rbac/role/list/
權限分配:http://127.0.0.1:8000/rbac/distribute/permissions/
- 權限驗證+自動生成菜單
a.權限信息初始化
init_permission(user_obj,request)
MENU_SESSION_KEY = "u8fkksjdfkjsf"
PERMISSION_SESSION_KEY = "u8fkkfffffsjdfkjsf"
b.中間件請求進行權限校驗
'rbac.middleware.rbac.RbacMiddleware'
PERMISSION_VALID_URL = [
'/login/',
'/admin/.*',
]
c.inclusion_tag
{% load rbac %}
{% menu request %}
{% breadcrumb request %}
d.引入css和js
<link rel="stylesheet" href="{% static 'rbac/css/rbac.css' %} "/>
<script src="{% static 'rbac/js/rbac.js' %} "></script>
- 按鈕控制
方式一:在前端頁面判斷
{% load rbac %}
{% if request|has_permission:'xxx:xxx_x_x' %}
<a>添加</a>
{% endif %}
方式二:在stark組件中通過基類的方式實現
權限類:
from django.conf import settings
from stark.service.stark import StarkConfig
class RbacPermission(object):
def get_add_btn(self):
name = "%s:%s" %(self.site.namespace,self.get_add_url_name,)
permission_dict = self.request.session.get(settings.PERMISSION_SESSION_KEY)
if name in permission_dict:
return super().get_add_btn()
def get_list_display(self):
val = super().get_list_display()
permission_dict = self.request.session.get(settings.PERMISSION_SESSION_KEY)
edit_name = "%s:%s" %(self.site.namespace,self.get_change_url_name,)
del_name = "%s:%s" %(self.site.namespace,self.get_del_url_name,)
if edit_name not in permission_dict:
val.remove(StarkConfig.display_edit)
if del_name not in permission_dict:
val.remove(StarkConfig.display_del)
return val
配置類:
class CourseConfig(RbacPermission,StarkConfig):
list_display = ['id','name']
site.register(models.Course,CourseConfig)
四、stark組件
4.1 簡介
- stark組件能夠幫助我們快速開發出快速實現數據庫表的增刪改查的組件。
- 組件基於django中admin源碼開發
4.2 組成
4.2.1 django運行機制
# 我們新建一個名為stark(自己隨便取)的app
# 在這個app下的apps.py中,只要我們寫下以下代碼,django每次運行之前,都會去每個APP下找名為stark的py文件並去執行它
class StarkConfig(AppConfig):
name = 'stark'
def ready(self):
from django.utils.module_loading import autodiscover_modules
# 當程序啟動時,去每個app目錄下找stark.py並加載。
autodiscover_modules('stark')
4.2.2 基於包導入的方式實現單例模式
- 我們在stark app下新建一個service目錄來存放我們的主要功能
class StarkConfig(objects):
def __init__(self, name, age):
self.name = name
self.age = age
def func(self):
print(self.name, self.age)
class AdminSite(objects):
def __init__(self, model_class):
self.registry = {}
self.model_class = model_class
def register(self, stark_config=None):
if not stark_config:
stark_config = StarkConfig
site = AdminSite()
- 我們新建另外一個app01,然后在其目錄下新建一個stark.py文件
from stark.service.stark import site
from app01 import model
site.register(model.UserInfo)
4.2.3 django路由原理
test1 urls.py
urlpatterns = [
url(r'^index/', views.index),
url(r'^rbac/', include('rbac.urls)),
]
test2.py
urlpatterns = [
url(r'^login/', views.login)
]
# 沒加namespace
rbac/login
# 加namespaece
rbac:rbac/login
- include本質
([], app_name, namespace)
點擊查看stark使用手冊
使用stark組件需要完成一下幾個步驟:
- 拷貝stark app到任何系統。
- 在目標project中注冊stark app,如:
INSTALLED_APPS = [
...
'stark.apps.StarkConfig',
]
- 如果想要使用stark組件,則需要在目標app的根目錄中創建 stark.py
- 配置路由信息
from stark.service.stark import site
urlpatterns = [
...
url(r'^stark/', site.urls),
]
- 接下來就可以使用stark組件進行快速增刪改查,示例:
from crm import models
from stark.service.stark import site, StarkConfig
from django.utils.safestring import mark_safe
from django.conf.urls import url
from django.shortcuts import HttpResponse
from django.urls import reverse
from crm.config.class_list import ClassListConfig
class UserInfoConfig(StarkConfig):
def display_gender(self, row=None, header=False):
if header:
return '性別'
return row.get_gender_display()
def display_detail(self,row=None, header=False):
if header:
return '查看詳細'
return mark_safe('<a href="%s">%s</a>' %(reverse('stark:crm_userinfo_detail',kwargs={'pk':row.id}),row.name,))
list_display = [
display_detail,
display_gender,
'phone',
'email',
'depart',
StarkConfig.display_edit,
StarkConfig.display_del
]
def extra_url(self):
info = self.model_class._meta.app_label, self.model_class._meta.model_name
urlpatterns = [
url(r'^(?P<pk>\d+)/detail/$', self.wrapper(self.detail_view), name='%s_%s_detail' % info),
]
return urlpatterns
def detail_view(self,request,pk):
"""
查看詳細頁面
:param request:
:param pk:
:return:
"""
return HttpResponse('詳細頁面...')
search_list = ['name','depart__title']
site.register(models.UserInfo, UserInfoConfig)
site.register(models.UserInfo, UserInfoConfig,prev='pri')
- 組件內部擴展:
list_display
get_list_display
action_list
order_by
model_form_class
....