Django項目——CRM
一、開發背景
由於公司人員的增多,原來通過excel表格存取方式過於繁瑣,而且對於公司人員的調配和績效考核等不能做到精確處理,所以開發crm系統,開始開發只是針對銷售人員和客戶,後面陸續加上一些操作,如學校管理和教師管理,課程管理等,
二、開發周期
開發2個月,2個月後持續還在做:修復bug和新功能的開發。
最初只是開發了業務,因為時間比較緊,後面由於維護和和更有利於新功能的擴展,重新抽取組件,如stark組件和分頁組件,rbac(權限組件)
三、表設計
crm rbac 調查問卷 會議室預定四、 主要業務
銷售業務
銷售業務說明(customer,SaleRank權重表 CustomerDistribution客戶分配表)
一、目的根據銷售人員的權重進行數據分配 單個導入和批量導入
主要針對的是公司銷售部門的工作管理,主要用於工作安排、銷售進度的跟進以及為業績考核提供數據支持。
二、業務機制
1.工作安排原則(分配訂單原則)
根據對銷售人員的以往業績的分析制定每個銷售人員的銷售任務,綜合多方面數據對銷售人員進行權重的評定。在工作分配時,
通過權重進行排序,權重大的優先安排任務。分配任務時,按照權重排序從高到低順位循環分配。(
若銷售人員被分配任務以達到目標值則跳過,若所有銷售人員都達到目標值則將剩余銷售任務重復之前的方案進行分配)。
實現原理:
創建權重表,包含字段:user,權重,轉化人數,創建一個類,獲取權重表裏面的所有銷售人員並根據權重進行降序排序,根據權重和轉換人數將銷售的id依次循環放入列表中,直到等於銷售人員的轉化人數就跳過該銷售人員,在後臺調用即可,當循環完以後,調用
reset()重置即可,主要使用Redis來完成(原因:減輕內存壓力,普通放到內存中關機或斷電就沒了,這樣不公平,Redis可以處理這個麻煩,其次Redis可以有效的處理多進程問題)
公共資源 搶單
2.銷售進度狀態
銷售人員得到任務後對應的客戶狀態改為開始接洽,記錄起始時間,訂單狀態從公司資源更改為銷售人員的個人資源,其他人在訂單轉移前不可接觸訂單信息。
銷售人員在跟進訂單時,每一次與客戶接洽都會在數據庫中生成一條記錄。
若訂單在十五日內被銷售人員轉化成功,則將該客戶的狀態由待轉化變為轉化成功,並在正式客戶表中生成該客戶的記錄。在銷售人員的訂單記錄中將這筆訂單的狀態改為轉化成功。若當前與客戶接洽的銷售人員三天未跟進訂單或是在十五天內未促成交易。則相關訂單信息會被移動到公司公共資源中,並且原先跟進訂單的銷售人員不可以選擇繼續跟進(直至該訂單再次被移入公司公共資源)。原銷售人員的訂單跟進記錄中會顯示有一單未能轉化,並顯示原因(重新接手該訂單後即使轉化成功,本條記錄不會被覆蓋)。
實現原理:
a.客戶來源:
1.來源一:運營部所分配的,權重分配一樣(具體操作一樣見上面)
2.來源二:搶單,從公司公共資源池裏面獲取:條件這個人不能是自己轉化過de,或者是其他銷售接單的但是接單超過15天
或超過3天沒有聯系的,點擊搶單進行獲取,將該用戶添加到我的客戶中(在客戶分配表中創建一條新的記錄),並且status默認
為正在根進,將該客戶在客戶表中的跟進人換成當前登錄用戶,接單時間和最近跟進時間換成當前時間即可
3.來源三:自己錄入的
b.未轉換成功(訂單失效):當接單超過15天或者連續三天沒有聯系字該客戶自動進入公共資源池,定時器完成
c.轉換成功:轉換成功後 加入我的客戶中(在客戶分配表中創建一條新的記錄),並且status默認
為已轉換
3.銷售數據我的客戶
銷售人員所接觸過的每一個客戶,不管什麽來源,轉換成功以否都會保存起來,為以後的權重劃分和績效考核為依據,
實現原理:
通過實現查看我的客戶就可以一目了然的看到該銷售人員的所有客戶
4.業績考核
在銷售人員的訂單記錄中記錄了銷售人員從第一筆業務到最近一筆業務的所有信息。可以為銷售人員的業績考核提供:接單數,轉化率,訂單未轉化原因等數據。
學校管理業務說明 (courserecord,studyrecord)
一、系統用途
主要服務於教育機構,對教學班級,校區,課程,學生等進行管理,主要用於班級的成員管理、課堂出勤情況記錄以及學員成績記錄。
二、業務機制
1.成員管理
銷售人員與客戶接洽完畢,將客戶轉化為學員後,根據其選擇的校區、課程、班級將其信息錄入學員的數據庫中。初始化該學員的賬號
和密碼,以便其進入教學管理系統查看自己的成績以及出勤記錄。若該學員因某些原因中途退學或進入其他班級,則將其記錄刪除
(出勤記錄與成績記錄詳見2、3條說明)。
2.課堂出勤情況記錄
每日上課前由班主任或當日講師初始化當日的考勤信息,初始化時默認全部全員正常出勤。如有學員存在:遲到、曠到、早退或請假等情況。可由班主任或當日講師修改其考勤狀況(支持批量修改)。
若有學生中途進入班級,進班前的考勤記錄不與不予生成。若有學生中途離開教學班級,離班前的考勤記錄不予刪除。
上課教師和班主任對學生進行考勤管理,考勤直接影響這節課的成績,考勤種類為已簽到,遲到,缺勤,早退,請假.
初始化實現原理:初始化管理
點擊復選框選中要初始化的當天班級課程,點擊action中學生初始化對學生完成初始化 默認全部出勤,
如果有個別學生出現違規情況,在studyrecord中對該學生進行操作考勤管理
實現原理:
教師和班主任在課程記錄頁面點擊考勤管理,調轉到該班級的學習記錄頁面,列出該班級的所有學生,利用action對學生進行批量的
考勤管理
3.學員成績記錄
在班主任或當日講師進行初始化考勤信息操作時可以選擇當日是否有作業(支持修改)。若當日有作業則開放學生作業提交的功能,
學生須在提交時間內提交,提交時間結束關閉該功能。
學生提交作業後在提交時間內允許撤銷提交並重新提交。提交時間結束後,助教可以下載學員提交文件,並進行打分評定。
打分和評定評語結束後可立即上傳至教學管理系統,學生可以通過教學管理系統進行查詢(支持批量導入)。
若有學生中途進入班級,進班前的成績記錄不與不予生成。若有學生中途離開教學班級,離班前的成績記錄不予刪除。
成績錄入實現原理:
在課程記錄頁面點擊成績錄入調到成績錄入頁面,主要是根據當前趁機記錄的id獲取該節課所有的學生記錄對象,發送dao前端,
其中由於成績打分要嚴格區分,所欲成績和評語有type動態生成ModelForm對象傳到前端,fields字段分別是score_學生記錄id
,homework_note_學生記錄對象id,post傳到後端 的時候在courseRecordConfig中看代碼成績錄入
錄入成績 錄入成績html
會議室預定 業務
公司人員增多,空間有限,會議室需要被預定才可使用,每個會議室從早上八點-晚上八點可以預定,一小時劃分,如果該會議室當前時間被預定了,如果預定的人是自己,再點擊則取消,如果是別人預定的則不可以點擊
,如果沒有沒預定點擊則預定,
調查問卷 業務
調查問卷是為了調查學生對學校設備和講師講課的滿意程度,以及他沒有什麽困難等,獲取他們的意見以方便我們進行改進,問卷只能有班主任和教務總監發起,並明確的規定班級和起始日期和結束時間,並且只有本班學生才能填寫,調查問卷的題型有三種,填寫內容(建議),單選,打分。學生打分後點擊提交即可完成調查問卷,學生提交後,在首頁可顯示答卷的人數。
技術點:
1. 通過ChangeList封裝好多數據
原因:
change_listview列表頁面代碼太多啦 而卻有好幾個功能,代碼結構不清晰,修改代碼麻煩,傳到前端的東西過多,封裝後代碼結構清晰,解耦性提高,便於維護,而且在changelist_view中只需要傳入changelist對象即可。
2. 銷售中公共資源:Q查詢,3天 15天
銷售接單後開始記錄時間,如果三天 未跟進十五天未成單,該客戶進入公司的公共資源池,並且當前銷售人員不能在公共資源池裏面對該客戶沒有任何權限.
View Code3. 使用yield實現(前端需要循環生成數據時)
- 生成器函數,對數據進行加工處理
- __iter__和yield配合
組合搜索時用到先在ChangList中的get_combine_seach_filter()中返回row對象,然後在FilterRow類中創建__iter__方法 yied生成每個組合搜索所對應的url
View Code View Code4. 獲取Model類中的字段對應的對象
class Foo(model.Model):
xx = models.CharField()
Foo.get_field(‘xx‘)
根據類名獲取相關字段
model.UserInfo model.UserInfo._meta.app_label#獲取當前app的名稱 model.UserInfo._meta.model_name#獲取當前類名小寫 model.UserInfo._meta.get_field(‘username‘)#獲取字段 model.UserInfo._meta.get_field(‘username‘).verbose_name#獲取verbose_name model.UserInfo._meta.get_field(‘外鍵或多對多字段‘).rel.to #得到關聯的model類 - models.UserInfo._meta.get_field(‘name‘) # 根據字段名稱,獲取字段對象 - models.UserInfo._meta.fields # 獲取類中所有的字段 - models.UserInfo._meta._get_fields() # 獲取類中所有的字段(包含反向關聯的字段) - models.UserInfo._meta.many_to_many # 獲取m2m字段
獲取當前類的對象所反向關聯的字段
obj所得到的對象 related_fileds=obj._meta.related_objects #得到當前對象的反向關聯的所有字段 for related_field in fileds: _model_name=related_field.field.model._meta.model_name#獲取當前關聯字段所屬的類名 _related_name=related_field.related_name#獲取當前字段中的_related_name(反向查找的別名) _field_name=related_field.field_name#獲取當前字段跟其他表所關聯的字段(to_field=‘‘) _limit_choices_to=related_obj.limit_choices_to
5. 模糊搜索功能
用到Q查詢
根據show_search_form判斷是否顯示模糊搜索框,search_fileds=[]代表可以以什麽搜索
Strkconfig中 ChangeList中 前端6. Type創建類
主要用於動態生成modelForm時用到,在調查問卷和成績錄入是用到
Type中第一個參數是類名,第二個是繼承的類,第三個是字典,其中我們操作主要是在字典中進行操作
成績錄入7. 自動派單
- 原來在內存中實現,問題:重啟和多進程時,都有問題。
- redis
- 狀態
- 原來數據(權重表 權重和個數)
- pop數據
上面 業務中有具體體說明
8. 使用 list_diplay配置
list_display = [函數名,字段名。。。。]
9. reverse反向生成URL
根據url中name字段的值利用reverse生成,如果有namespace則需要在最前面加上,並用“:””分隔,url中有參數還需要傳參args=[]
反向生成url10. 母版
模板的繼承
模板中 {%block body%}{%endblock%}
子版中最頂行{% extends ‘母版的路徑‘ %}
{%block body%}{%endblock%}
11. ready方法定制起始文件
- 文件導入實現單例模式
stark.apps12. inclusion_tag
在權限管理生成菜單和crm中生成popup數據時用到
當前所裝飾的函數所得到的值,傳到inclusion_tag中的html中使用,(這個html一般是一個子版),如果有模板需要用到這個html模板,則需要在當前模板中
{% inclusion_tag所修飾的函數名 參數一 參數二....%}
13. 中間件的使用
登錄和權限管理用到,
需要繼承MiddlewareMixin,有五個方法:
- process_request(self,request)
- process_response(self, request, response
- process_view(self, request, callback, callback_args, callback_kwargs)
- process_template_response(self,request,response)
- process_exception(self, request, exception)
15. importlib + getattr
在發送消息是用到,參考django源碼可知,中間件也是采用這種方法
settings.pyimport importlib for cls_path in settings.MESSAGE_CLASSES: # cls_path是字符串 module_path,class_name = cls_path.rsplit(‘.‘,maxsplit=1) m = importlib.import_module(module_path) obj = getattr(m,class_name)() obj.send(subject,body,to,name)
16. FilterOption,lambda表達式
目的是為了判斷關聯表的關聯字段是不是主鍵還是其他字段
17. QueryDict
- 原條件的保留
- filter
http://www.cnblogs.com/ctztake/p/8076003.html
18. ModelForm
可以自定義也可以使用satrkcofig中的
type生成ModelForm
這裏重點說form的循環
先看一下這個 View Code19. 面向對象的 @property @classmethod
20. mark_safe
在後臺寫的html傳到前端能夠正常顯示,目的是為了防止xss攻擊
還有一種類似的方法,直接在前端 {{aaa|safe}}
21. 抽象方法抽象類+raise Im...
在發送消息時用到,要求所有繼承BaseMessage的類都必須實現send()方法,沒有繼承則拋出異常
class BaseMessage(object): def send(self, subject, body, to, name): raise NotImplementedError(‘未實現send方法‘)
當然也可以用接口實現,但相對於上面那種方法過於繁瑣,推薦使用上面的方法
22. 組件中的裝飾器,實現self.request = request
23. js自執行函數
(function(arg){
})(‘sf‘)
24. URL的鉤子函數
25. 多繼承
python3中都是新式類,遵從廣度優先
python2中既有經典類和新式類,經典類是指當前類和父類都沒有繼承obj類,新式類是指當前類或其父類只要有繼承了obj類就算新式類
經典類遵循深度優先
新式類遵循廣度優先
26. 批量導入,xlrd
27. redis連接池
28. 工廠模式
工廠模式實際上包含了3中設計模式,簡單工廠,工廠和抽象工廠,關鍵點如下: 一、三種工廠的實現是越來越復雜的 二、簡單工廠通過構造時傳入的標識來生產產品,不同產品都在同一個工廠中生產,這種判斷會隨著產品的增加而增加,給擴展和維護帶來麻煩 三、工廠模式無法解決產品族和產品等級結構的問題 四、抽象工廠模式中,一個工廠生產多個產品,它們是一個產品族,不同的產品族的產品派生於不同的抽象產品(或產品接口)。 好了,如果你能理解上面的關鍵點,說明你對工廠模式已經理解的很好了,基本上面試官問你工廠模式,你可以昂頭挺胸的說一番。但是,面試官怎麽可能會放過每一次虐人的機會?你仍然可能面臨下面的問題: 1. 在上面的代碼中,都使用了接口來表達抽象工廠或者抽象產品,那麽可以用抽象類嗎?有何區別? 從功能上說,完全可以,甚至可以用接口來定義行為,用抽象類來抽象屬性。抽象類更加偏向於屬性的抽象,而用接口更加偏向行為的規範與統一。使用接口有更好的可擴展性和可維護性,更加靈活實現松散耦合,所以編程原則中有一條是針對接口編程而不是針對類編程。 2. 到底何時應該用工廠模式 根據具體業務需求。不要認為簡單工廠是用switch case就覺得一無是處,也不要覺得抽象工廠比較高大上就到處套。我們使用設計模式是為了解決問題而不是炫技,所以根據三種工廠模式的特質,以及對未來擴展的預期,來確定使用哪種工廠模式。 3.說說你在項目中工廠模式的應用 crm項目中發送消息是用到,因為我們要同時發短信,微信,釘釘,和郵件信息,我們把他包裝在一個baseMessage中,使用時直接調用baseMessage的send()即可
settings.py
MSG_PATH = "path.Email"
class XXFactory(object):
@classmethod
def get_obj(cls):
settings.MSG_PATH
# rsplit
# importlib
# getattr
return obj
class Email(object):
def send ...
class WeChat(object):
def send ...
class Msg(object):
def send ...
29. Models類中自定義save方法
30. django admin中註冊models時候
from django.contrib import admin
from . import models
# 方式一
class UserConfig(admin.ModelAdmin):
pass
admin.site.register(models.UserInfo,UserConfig)
# 方式二
@admin.register(models.UserInfo)
class UserConfig(admin.ModelAdmin):
pass
31. 深淺拷貝
1.對於數字
和字符串
而言,賦值、淺拷貝和深拷貝無意義,因為他們的值永遠都會指向同一個內存地址。
2.對於字典、元祖、列表 而言,進行賦值、淺拷貝和深拷貝時,其內存地址的變化是不同的。
賦值,只是創建一個變量,該變量指向原來內存地址
淺拷貝,在內存中只額外創建第一層數據
# 導入拷貝模塊 >>> import copy >>> var1 = {"k1": "1", "k2": 2, "k3": ["abc", 456]} # 使用淺拷貝的方式 >>> var2 = copy.copy(var1) # 兩個變量的內存地址是不一樣的 >>> id(var1) 2084726354952 >>> id(var2) 2084730248008 # 但是他們的元素內存地址是一樣的 >>> id(var1["k1"]) 2084726207464 >>> id(var2["k1"]) 2084726207464
深拷貝,在內存中將所有的數據重新創建一份(排除最後一層,即:python內部對字符串和數字的優化)
# 導入拷貝模塊 >>> import copy >>> var1 = {"k1": "1", "k2": 2, "k3": ["abc", 456]} # 使用深拷貝的方式把var1的內容拷貝給var2 >>> var2 = copy.deepcopy(var1) # var1和var2的內存地址是不相同的 >>> id(var1) 1706383946760 >>> id(var2) 1706389852744 # var1和var2的元素"k3"內存地址是不相同的 >>> id(var1["k3"]) 1706389853576 >>> id(var2["k3"]) 1706389740744 # var1和var2的"k3"元素的內存地址是相同的 >>> id(var1["k3"][1]) 1706383265744 >>> id(var2["k3"][1]) 1706383265744
印象深刻的東西
- 組合搜索時,生成URL: - request.GET - 深拷貝 - 可叠代對象 - yield - 面向對象封裝 - popup - window.open("",‘name‘) - opener.xxxxxxx() - FK時,可以使用 limit_choice_to 可以是字典和Q對象 - related_name和model_name - 獲取所有的反向關聯字段,獲取limit_choice_to字段 - 查詢 - excel批量導入 - 路由系統 - 動態生成URL - 看Admin源碼(include) - /xx/ -> ([ ‘xxx‘, ],namespace) - 開發組件時,最開始看到admin源碼不太理解,當和權限系統配合時,才領悟其中真諦。開始想的只要用add_btn=True,show_searche=True等等就可以了,為什麽還要用get_add_btn()
和get_show_search等等,後來開發組件進行權限管理時才明白,這都是預留給權限用的,根據繼承的先後順序和登錄用戶所擁有的權限判斷是否顯示按鈕等。
-學生錄入成績時,為了區分是給那個學生錄成績,並且在後臺獲取的時候能夠區分這個成績和評語是給那個學生的 利用了type動態生成form還有 動態生成field字段
Django項目——CRM