CRM項目總結

? ? ? ?

? ? CRM項目總結

?

?

???? 一:開發背景

?

  在公司日益擴大的過程中,不可避免的會伴隨著更多問題出現。

  對外 : 如何更好的管理客戶與公司的關系?如何更及時的了解客戶日益發展的需求變化?公司的產品是否真的符合客戶需求?以及公司新產品信息是否更有針對性的及時推送給客戶?

  對內 : 公司發展壯大,部門越來越多,如何明確每個部門的權限?如何合理的調配公司人員?如何合理的分配客戶資源?如何精確的處理績效考核?以及更重要的在銷售管理中,如何更及時的了解是什么阻礙了公司的發展?是什么影響了銷售的業績?銷售的服務是否讓客戶滿意?

面對這么多的問題,這個時候來一套完整的CRM就顯得很有必要了。

?

二:開發周期

?

  1. 計劃在兩個月內完成項目的初步設計以及功能的初步實現。
  2. 基礎功能完成后,會預留兩個月時間來完成bug修復以及微調根據公司業務發展的業務需求。
  3. 項目上線后,開發人員持續跟進項目。根據公司業務發展和管理體系實時調整。

?

三:功能

?

  1 from django.db import models
  2 from rbac import models as rbac_model
  3 # Create your models here.
  4 
  5 
  6 class Department(models.Model):
  7     """
  8     部門表
  9     市場部     1000
 10     銷售      1001
 11     """
 12     title = models.CharField(verbose_name='部門名稱', max_length=16)
 13     code = models.IntegerField(verbose_name='部門編號',unique=True,null=False)
 14 
 15     def __str__(self):
 16         return self.title
 17 
 18 
 19 class UserInfo(models.Model):
 20     """
 21     員工表
 22     """
 23     auth = models.OneToOneField(verbose_name='用戶權限', to=rbac_model.User,null=True,blank=True)
 24     name = models.CharField(verbose_name='員工姓名', max_length=16)
 25     username = models.CharField(verbose_name='用戶名', max_length=32)
 26     password = models.CharField(verbose_name='密碼', max_length=64)
 27     email = models.EmailField(verbose_name='郵箱', max_length=64)
 28     openid = models.CharField(verbose_name='微信唯一ID', max_length=64, null=True, blank=True)
 29     depart = models.ForeignKey(verbose_name='部門', to="Department",to_field="code")
 30 
 31     def __str__(self):
 32         return self.name
 33 
 34 
 35 class Course(models.Model):
 36     """
 37     課程表
 38     """
 39     name = models.CharField(verbose_name='課程名稱', max_length=32)
 40 
 41     def __str__(self):
 42         return self.name
 43 
 44 
 45 class School(models.Model):
 46     """
 47     校區表
 48 
 49     """
 50     title = models.CharField(verbose_name='校區名稱', max_length=32)
 51 
 52     def __str__(self):
 53         return self.title
 54 
 55 
 56 class ClassList(models.Model):
 57     """
 58     班級表
 59     如:
 60         燒餅  打餅班  1期  10000  2017-11-11  2018-5-11
 61     """
 62     school = models.ForeignKey(verbose_name='校區', to='School')
 63     course = models.ForeignKey(verbose_name='課程名稱', to='Course')
 64 
 65     semester = models.IntegerField(verbose_name="班級(期)")
 66     price = models.IntegerField(verbose_name="學費")
 67     start_date = models.DateField(verbose_name="開班日期")
 68     graduate_date = models.DateField(verbose_name="結業日期", null=True, blank=True)
 69     memo = models.CharField(verbose_name='說明', max_length=256, blank=True, null=True, )
 70     teachers = models.ManyToManyField(verbose_name='任課老師', to='UserInfo', related_name='teach_classes',limit_choices_to={'depart_id__in':[1003,1004]})
 71     tutor = models.ForeignKey(verbose_name='班主任', to='UserInfo', related_name='classes',limit_choices_to={'depart_id':1002})
 72 
 73     def __str__(self):
 74         return "{0}({1}期)".format(self.course.name, self.semester)
 75 
 76 
 77 class Customer(models.Model):
 78     """
 79     客戶表
 80     """
 81     qq = models.CharField(verbose_name='qq', max_length=64, unique=True, help_text='QQ號必須唯一')
 82 
 83     name = models.CharField(verbose_name='學生姓名', max_length=16)
 84     gender_choices = ((1, ''), (2, ''))
 85     gender = models.SmallIntegerField(verbose_name='性別', choices=gender_choices)
 86 
 87     education_choices = (
 88         (1, '重點大學'),
 89         (2, '普通本科'),
 90         (3, '獨立院校'),
 91         (4, '民辦本科'),
 92         (5, '大專'),
 93         (6, '民辦專科'),
 94         (7, '高中'),
 95         (8, '其他')
 96     )
 97     education = models.IntegerField(verbose_name='學歷', choices=education_choices, blank=True, null=True, )
 98     graduation_school = models.CharField(verbose_name='畢業學校', max_length=64, blank=True, null=True)
 99     major = models.CharField(verbose_name='所學專業', max_length=64, blank=True, null=True)
100 
101     experience_choices = [
102         (1, '在校生'),
103         (2, '應屆畢業'),
104         (3, '半年以內'),
105         (4, '半年至一年'),
106         (5, '一年至三年'),
107         (6, '三年至五年'),
108         (7, '五年以上'),
109     ]
110     experience = models.IntegerField(verbose_name='工作經驗', blank=True, null=True, choices=experience_choices)
111     work_status_choices = [
112         (1, '在職'),
113         (2, '無業')
114     ]
115     work_status = models.IntegerField(verbose_name="職業狀態", choices=work_status_choices, default=1, blank=True,
116                                       null=True)
117     company = models.CharField(verbose_name="目前就職公司", max_length=64, blank=True, null=True)
118     salary = models.CharField(verbose_name="當前薪資", max_length=64, blank=True, null=True)
119 
120     source_choices = [
121         (1, "qq群"),
122         (2, "內部轉介紹"),
123         (3, "官方網站"),
124         (4, "百度推廣"),
125         (5, "360推廣"),
126         (6, "搜狗推廣"),
127         (7, "騰訊課堂"),
128         (8, "廣點通"),
129         (9, "高校宣講"),
130         (10, "渠道代理"),
131         (11, "51cto"),
132         (12, "智匯推"),
133         (13, "網盟"),
134         (14, "DSP"),
135         (15, "SEO"),
136         (16, "其它"),
137     ]
138     source = models.SmallIntegerField('客戶來源', choices=source_choices, default=1)
139     referral_from = models.ForeignKey(
140         'self',
141         blank=True,
142         null=True,
143         verbose_name="轉介紹自學員",
144         help_text="若此客戶是轉介紹自內部學員,請在此處選擇內部學員姓名",
145         related_name="internal_referral"
146     )
147     course = models.ManyToManyField(verbose_name="咨詢課程", to="Course")
148 
149     status_choices = [
150         (1, "已報名"),
151         (2, "未報名")
152     ]
153     status = models.IntegerField(
154         verbose_name="狀態",
155         choices=status_choices,
156         default=2,
157         help_text=u"選擇客戶此時的狀態"
158     )
159     consultant = models.ForeignKey(verbose_name="課程顧問", to='UserInfo', related_name='consultant',limit_choices_to={'depart_id':1005})
160     date = models.DateField(verbose_name="咨詢日期", auto_now_add=True)
161 
162     recv_date = models.DateField(verbose_name='接單時間',null=True,blank=True)
163 
164     last_consult_date = models.DateField(verbose_name="最后跟進日期", auto_now_add=True)
165 
166     def __str__(self):
167         return "姓名:{0},QQ:{1}".format(self.name, self.qq, )
168 
169 
170 class CustomerDistribution(models.Model):
171     """客戶分配表"""
172     user = models.ForeignKey(verbose_name="當前客戶顧問",to='UserInfo',limit_choices_to={'depart_id':1005},related_name="cds",null=True,blank=True)
173     customer = models.ForeignKey(verbose_name="客戶",to="Customer",related_name="dealers",null=True,blank=True)
174     ctime = models.DateField(auto_now_add=True,null=True,blank=True)
175     status_choices = (
176         (1,'正在跟進'),
177         (2,'已成單'),
178         (3,'三天未跟進'),
179         (4,'十五天未成單'),
180     )
181     status = models.IntegerField(verbose_name="狀態",choices=status_choices,default=1)
182     memo = models.CharField(verbose_name="更多信息",max_length=255,null=True,blank=True)
183 
184 
185 class SaleRank(models.Model):
186     """銷售權重與分配"""
187     user = models.ForeignKey(to="UserInfo",verbose_name='課程顧問',limit_choices_to={'depart':1005})
188     num = models.IntegerField(verbose_name='數量')
189     weight = models.IntegerField(verbose_name='權重')
190     def __str__(self):
191         return '權重:{0}?;num:{1}'.format(self.weight,self.num)
192 
193 
194 class ConsultRecord(models.Model):
195     """
196     客戶跟進記錄
197     """
198     customer = models.ForeignKey(verbose_name="所咨詢客戶", to='Customer')
199     consultant = models.ForeignKey(verbose_name="跟蹤人", to='UserInfo',limit_choices_to={'depart_id':1005})
200     date = models.DateField(verbose_name="跟進日期", auto_now_add=True)
201     note = models.TextField(verbose_name="跟進內容...")
202 
203 
204 class PaymentRecord(models.Model):
205     """
206     繳費記錄
207     """
208     customer = models.ForeignKey(Customer, verbose_name="客戶")
209 
210     class_list = models.ForeignKey(verbose_name="班級", to="ClassList", blank=True, null=True)
211 
212     pay_type_choices = [
213         (1, "訂金/報名費"),
214         (2, "學費"),
215         (3, "轉班"),
216         (4, "退學"),
217         (5, "退款"),
218     ]
219     pay_type = models.IntegerField(verbose_name="費用類型", choices=pay_type_choices, default=1)
220     paid_fee = models.IntegerField(verbose_name="費用數額", default=0)
221     turnover = models.IntegerField(verbose_name="成交金額", blank=True, null=True)
222     quote = models.IntegerField(verbose_name="報價金額", blank=True, null=True)
223     note = models.TextField(verbose_name="備注", blank=True, null=True)
224     date = models.DateTimeField(verbose_name="交款日期", auto_now_add=True)
225     consultant = models.ForeignKey(verbose_name="負責老師", to='UserInfo', help_text="誰簽的單就選誰")
226 
227 
228 class Student(models.Model):
229     """
230     學生表(已報名)
231     """
232     customer = models.OneToOneField(verbose_name='客戶信息', to='Customer')
233 
234     username = models.CharField(verbose_name='用戶名', max_length=32)
235     password = models.CharField(verbose_name='密碼', max_length=64)
236     emergency_contract = models.CharField(max_length=32, blank=True, null=True, verbose_name='緊急聯系人')
237     class_list = models.ManyToManyField(verbose_name="已報班級", to='ClassList', blank=True)
238 
239     company = models.CharField(verbose_name='公司', max_length=128, blank=True, null=True)
240     location = models.CharField(max_length=64, verbose_name='所在區域', blank=True, null=True)
241     position = models.CharField(verbose_name='崗位', max_length=64, blank=True, null=True)
242     salary = models.IntegerField(verbose_name='薪資', blank=True, null=True)
243     welfare = models.CharField(verbose_name='福利', max_length=256, blank=True, null=True)
244     date = models.DateField(verbose_name='入職時間', help_text='格式yyyy-mm-dd', blank=True, null=True)
245     memo = models.CharField(verbose_name='備注', max_length=256, blank=True, null=True)
246 
247     def __str__(self):
248         return self.username
249 
250 
251 class CourseRecord(models.Model):
252     """
253     上課記錄表
254     """
255     class_obj = models.ForeignKey(verbose_name="班級", to="ClassList")
256     day_num = models.IntegerField(verbose_name="節次", help_text=u"此處填寫第幾節課或第幾天課程...,必須為數字")
257     teacher = models.ForeignKey(verbose_name="講師", to='UserInfo',limit_choices_to={'depart_id__in':[1003,1004]})
258     date = models.DateField(verbose_name="上課日期", auto_now_add=True)
259 
260     course_title = models.CharField(verbose_name='本節課程標題', max_length=64, blank=True, null=True)
261     course_memo = models.TextField(verbose_name='本節課程內容概要', blank=True, null=True)
262     has_homework = models.BooleanField(default=True, verbose_name="本節有作業")
263     homework_title = models.CharField(verbose_name='本節作業標題', max_length=64, blank=True, null=True)
264     homework_memo = models.TextField(verbose_name='作業描述', max_length=500, blank=True, null=True)
265     exam = models.TextField(verbose_name='踩分點', max_length=300, blank=True, null=True)
266 
267     def __str__(self):
268         return "{0} day{1}".format(self.class_obj, self.day_num)
269 
270 
271 class StudyRecord(models.Model):
272     course_record = models.ForeignKey(verbose_name="第幾天課程", to="CourseRecord")
273     student = models.ForeignKey(verbose_name="學員", to='Student')
274     record_choices = (('checked', "已簽到"),
275                       ('vacate', "請假"),
276                       ('late', "遲到"),
277                       ('noshow', "缺勤"),
278                       ('leave_early', "早退"),
279                       )
280     record = models.CharField("上課紀錄", choices=record_choices, default="checked", max_length=64)
281     score_choices = ((100, 'A+'),
282                      (90, 'A'),
283                      (85, 'B+'),
284                      (80, 'B'),
285                      (70, 'B-'),
286                      (60, 'C+'),
287                      (50, 'C'),
288                      (40, 'C-'),
289                      (0, ' D'),
290                      (-1, 'N/A'),
291                      (-100, 'COPY'),
292                      (-1000, 'FAIL'),
293                      )
294     score = models.IntegerField("本節成績", choices=score_choices, default=-1)
295     homework_note = models.CharField(verbose_name='作業評語', max_length=255, blank=True, null=True)
296     note = models.CharField(verbose_name="備注", max_length=255, blank=True, null=True)
297 
298     homework = models.FileField(verbose_name='作業文件', blank=True, null=True, default=None)
299     stu_memo = models.TextField(verbose_name='學員備注', blank=True, null=True)
300     date = models.DateTimeField(verbose_name='提交日期', auto_now_add=True)
301 
302     def __str__(self):
303         return "{0}-{1}".format(self.course_record, self.student)
CRM表結構

?

?

  1. 銷售

    a) ?綜合一段時間的銷售業績。根據每個銷售的權重比,自動分配客戶資源,最大化客戶轉化率。

? ? ? ? ? ?   ?i. ?實現機制:

          根據權重表中數據合理分配客戶資源,配合客戶表和客戶分配表。

          分配時,按權重降序排序。按照分配人員數量,生成單次循環列表。每次分配列表結束,重新獲取分配列表。

          在客戶資源分配時,使用到redis來配合完成。好處是:減少數據庫查詢消耗和I/O開銷,提高數據處理速度。并且使用事務綁定,建立回滾機制,確保合理分配。

 1 import redis
 2 from app01 import models
 3 from oomph_2.settings import SALE_ID_LIST, SALE_ID_LIST_ORIGIN, SALE_ID_RESET
 4 
 5 POOL = redis.ConnectionPool(host='192.168.20.150',port=6379)
 6 # host是服務端的,這個port是redis固定的,只要是連接redis的這個port就固定是6379,密碼是服務端設置的。
 7 CONN = redis.Redis(connection_pool=POOL)
 8 
 9 
10 class AutoSale(object):
11 
12     @classmethod
13     def fetch_users(cls):
14         # [obj(銷售顧問id,num),obj(銷售顧問id,num),obj(銷售顧問id,num),obj(銷售顧問id,num),]
15         sales = models.SaleRank.objects.all().order_by('-weight')
16         print(sales)
17         sale_id_list = []
18         count = 0
19         while True:
20             flag = False
21             for row in sales:
22                 if count < row.num:
23                     sale_id_list.append(row.user_id)
24                     flag = True
25             count += 1
26             if not flag:
27                 break
28         print('SALE_ID_LIST===',sale_id_list)
29         if sale_id_list:
30             CONN.rpush(SALE_ID_LIST,*sale_id_list)        # 用來操作的數據
31             CONN.rpush(SALE_ID_LIST_ORIGIN,*sale_id_list) # 保留一份源數據
32             return True
33         return False
34 
35 
36     @classmethod
37     def get_sale_id(cls):
38         # 查看原來數據是否存在
39         
40         sale_id_origin_count = CONN.llen(SALE_ID_LIST_ORIGIN)
41         if not sale_id_origin_count:
42             # 取數據庫中獲取數據,并且賦值list_origin,pop數據
43             status = cls.fetch_users()
44             if not status:
45                 return None
46             user_id = CONN.lpop(SALE_ID_LIST)
47             if user_id:
48                 return user_id
49 
50             reset = CONN.get(SALE_ID_RESET)
51             if reset:
52                 CONN.delete(SALE_ID_LIST_ORIGIN)
53                 status = cls.fetch_users()
54                 if not status:
55                     return None
56                 CONN.delete(SALE_ID_RESET)
57                 return CONN.lpop(SALE_ID_LIST)
58             else:
59                 ct = CONN.llen(SALE_ID_LIST_ORIGIN)
60                 for i in range(ct):
61                     v = CONN.lindex(SALE_ID_LIST_ORIGIN,i)
62                     CONN.rpush(SALE_ID_LIST,v)
63                 return CONN.lpop('sale_list_id')
64         # if cls.rollback_list:
65         #     """
66         #     可能存在已經取到了銷售,也分配了,可是在寫進數據庫的過程中出現了問題,
67         #     在customer.py里面,我們使用了事務,雖然事務回滾了,可是因為我們使用的是生成器,所以自己寫了rollback方法
68         #     """
69         #     return cls.rollback_list.pop()
70         #
71         # if not cls.users:
72         #     cls.fetch_users()
73         # # 如果沒有課程顧問,就返回None
74         # if not cls.users:
75         #     return None
76         #
77         # if not cls.iter_users:
78         #     cls.iter_users = iter(cls.users)
79         # try:
80         #     user_id = next(cls.iter_users)
81         # except StopIteration as e:
82         #     if cls.reset_status:
83         #         cls.fetch_users()
84         #         cls.reset_status = False
85         #     cls.iter_users = iter(cls.users)
86         #     user_id = cls.get_sale_id()
87         # return user_id
88     @classmethod
89     def reset(cls):
90         CONN.set(SALE_ID_RESET,1)
91 
92     @classmethod
93     def rollback(cls,nid):
94         # cls.rollback_list.insert(0,nid)
95         CONN.lpush(SALE_ID_LIST,nid)   # callback的id,往前面放
自動分配

    

    b)?????? 銷售與客戶關系處理。

  該功能下,包括銷售人員得到任務后對應的客戶狀態改為開始接洽,記錄起始時間,訂單狀態從公司資源更改為銷售人員的個人資源,其他人在訂單轉移前不可接觸訂單信息。
銷售人員在跟進訂單時,每一次與客戶接洽都會在數據庫中生成一條記錄。
  若訂單在十五日內被銷售人員轉化成功,則將該客戶的狀態由待轉化變為轉化成功,并在正式客戶表中生成該客戶的記錄。在銷售人員的訂單記錄中將這筆訂單的狀態改為轉化成功。
若當前與客戶接洽的銷售人員三天未跟進訂單或是在十五天內未促成交易。則相關訂單信息會被移動到公司公共資源中,并且原先跟進訂單的銷售人員不可以選擇繼續跟進(直至該訂單再次被移入公司公共資源)。原銷售人員的訂單跟進記錄中會顯示有一單未能轉化,并顯示原因(重新接手該訂單后即使轉化成功,本條記錄不會被覆蓋)。

  在我的客戶的視圖中,是可以查看當前銷售所有的銷售記錄(已成單,未成單,以及未成單的訂單流失原因)。

    實現機制:

      包含公共資源,搶單功能,當前銷售個人客戶界面,單條和批量導入客戶信息。

      

import json,datetime
from django.conf.urls import url
from django.db.models import Q
from django.http import StreamingHttpResponse
from django.urls import reverse
from django.utils.safestring import mark_safe
from django.shortcuts import render,HttpResponse,redirect
from django.forms import ModelForm
from app01 import models
from cccccccccccccc import AutoSale
from stark.service import v1
from utils import message
from django.db import transaction
from app01.stark import BasePermissionclass SingleModelForm(ModelForm):class Meta:model = models.Customerexclude = ['consultant','status','recv_date','last_consult_date']class CustomerConfig(BasePermission,v1.StarkConfig):order_by = ['-status']def display_gender(self,obj=None,is_header=None):if is_header:return '性別'return obj.get_gender_display()def display_education(self,obj=None,is_header=None):if is_header:return '學歷'return obj.get_education_display()def display_source(self,obj=None,is_header=None):if is_header:return '客戶來源'return obj.get_source_display()def display_course(self,obj=None,is_header=None):if is_header:return '咨詢課程'html = []obj_list = obj.course.all()for obj1 in obj_list:temp = "<a style='display:inline-block;padding:3px 5px;border:2px solid red;margin:2px;' href='/stark/app01/customer/%s/%s/dc/'>%s <span class='glyphicon glyphicon-trash'></span></a>" %(obj.pk,obj1.pk,obj1.name)html.append(temp)return mark_safe(''.join(html))def display_status(self,obj=None,is_header=None):"""客戶狀態是可以點擊修改的:param obj::param is_header::return:"""if is_header:return '客戶狀態'# print('obj......',obj)   姓名:yy,QQ:123456return obj.get_status_display()def record(self,obj=None,is_header=None):"""客戶跟進記錄,http://127.0.0.1:8000/stark/app01/consultrecord/?customer=1"""if is_header:return '客戶跟進記錄'return mark_safe("<a href='/stark/app01/consultrecord/?customer=%s'>查看跟進記錄</a>"%(obj.pk))list_display = ['name','referral_from',display_gender,display_education,display_source,display_course,display_status,record]edit_link = ['name']def delete_course(self,request,customer_id,course_id):"""刪除當前用戶感興趣的課程:param request::param customer_id::param course_id::return:"""# print('self.model_class=,=',self.model_class.objects.all())# < QuerySet[ < Customer: 姓名:騷偉, QQ: 123456 >] >customer_obj = self.model_class.objects.filter(pk=customer_id).first()# 在多對多字段中可以remove,
        customer_obj.course.remove(course_id)# ####################作業:  刪除完成跳轉回來的時候,帶著走的時候的url# self.request.GET# self._query_param_key# 構造QueryDict# urlencode()return redirect(self.get_list_url())def extra_url(self):app_model_name = (self.model_class._meta.app_label,self.model_class._meta.model_name,)patterns = [url(r'^(\d+)/(\d+)/dc/$', self.wrap(self.delete_course), name="%s_%s_dc" %app_model_name),url(r'^public/$', self.wrap(self.public_view), name="%s_%s_public" %app_model_name),url(r'^user/$', self.wrap(self.user_view), name="%s_%s_user" %app_model_name),url(r'^(\d+)/competition/$', self.wrap(self.competition_view),name = "%s_%s_competition" % app_model_name),url(r'^single/$', self.wrap(self.single_view), name="%s_%s_single" % app_model_name),url(r'^multi/$', self.wrap(self.multi_view), name="%s_%s_multi" % app_model_name),# url(r'^loadfiles/$', self.wrap(self.get_loadfiles_view), name="%s_%s_loadfiles" % app_model_name),
        ]return patternsdef public_view(self,request):"""公共客戶資源, 未報名 & (15天未接單 or 三天未跟進)"""current_user_id = 9# 當前日期current_date = datetime.datetime.now().date()# 最后接單時間no_deal = current_date - datetime.timedelta(days=15)# 最后跟進日期no_follow_date = current_date - datetime.timedelta(days=3)"""公共客戶"""# 方法一:# customer_list = models.Customer.objects.filter(Q(recv_date__lt=no_deal)|Q(last_consult_date__lt=no_follow),status=2)# customer_list = models.Customer.objects.filter(Q(recv_date__lt=no_deal)|Q(last_consult_date__lt=no_follow_date),status=2)# print('9999999999',customer_list)# 方法二:con = Q()q1 = Q(status=2)q2 = Q()q2.connector = 'OR'q2.children.append(('recv_date__lt', no_deal))q2.children.append(('last_consult_date__lt', no_follow_date))con.add(q1,'AND')con.add(q2,'AND')customer_list = models.Customer.objects.filter(con)return render(request, 'public_view.html', {'customer_list':customer_list, "current_user_id":current_user_id})def competition_view(self,request,cid):"""搶單表:param request::param cid: customer_id 來自于公共資源里面的搶單選項,public_view:return:"""current_user_id = 9 # 這個是從session中拿的"""搶單之后,它會修改客戶表里面的: recv_date & last_consult_date & 課程顧問可以搶單的前提條件是: 之前的顧問不是自己,狀態必須是未報名,并且滿足3/15的要求"""current_date = datetime.datetime.now().date()no_deal = current_date - datetime.timedelta(days=15) # 最后接單日期no_follow_date = current_date - datetime.timedelta(days=3) # 最后跟進日期# 更新數據row_count = models.Customer.objects.filter(Q(recv_date__lt=no_deal) | Q(last_consult_date__lt=no_follow_date),status=2,id=cid).exclude(consultant_id=current_user_id).update(recv_date=current_date,last_consult_date=current_date,consultant_id=current_user_id)if not row_count:return HttpResponse('配嘛...')# 如果存在的話,我們就把它添加到客戶分配表CustomerDistribution里面models.CustomerDistribution.objects.create(ctime=current_date,customer_id=cid,user_id=current_user_id)return HttpResponse('嗯嗯,歸你歸你')def user_view(self,request):"""當前登錄用戶的所有的客戶(在我這成單的,以及我正在跟進的)"""current_user_id = 9customer_list = models.CustomerDistribution.objects.filter(user_id=current_user_id).order_by('status')return render(request, 'user_view.html', {'customer_list':customer_list})def single_view(self,request):"""單條錄入客戶信息"""if request.method == 'GET':form = SingleModelForm()return render(request, 'single_view.html', {'form':form})else:current_date = datetime.datetime.now().date()form = SingleModelForm(request.POST)if form.is_valid():print(form.cleaned_data)sale_id = AutoSale.get_sale_id()if not sale_id:return HttpResponse('暫無課程顧問,請添加后再分配')try:with transaction.atomic():# 客戶表保存form.instance.consultant_id = sale_idform.instance.recv_date = current_dateform.instance.last_consult_date = current_datenew_customer = form.save()  #  將數據添加到客戶表。這就算創建完成了# 將關系添加到客戶分配表models.CustomerDistribution.objects.create(customer=new_customer,ctime=current_date,user_id=sale_id)# 發送郵件信息# message.send_message('17701335022@163.com','saofei','fk','fk you')except Exception as e:# 創建客戶和分配銷售異常
                    AutoSale.rollback(sale_id)return HttpResponse('錄入異常')return  HttpResponse('錄入成功')else:return render(request, 'single_view.html', {'form':form})def multi_view(self,request):"""批量導入"""if request.method == 'GET':return render(request, 'multi_view.html')else:from django.core.files.uploadedfile import InMemoryUploadedFilefile_obj = request.FILES.get('exfile')# 老方法: 對應218# print(file_obj,type(file_obj))# with open('submit.xlsx','wb') as f :#     for chunk in file_obj:#         f.write(chunk)import xlrd# 不再創建xlsx文件workbook = xlrd.open_workbook(file_contents=file_obj.read())# 老方法: 先寫再讀# workbook = xlrd.open_workbook('submit.xlsx')sheet = workbook.sheet_by_index(0) # 這個當前sheet索引為0的 表單頁# print(sheet.nrows) # 當前共有多少行maps = {0 : 'name',1 : 'qq',}row_dict = {}for index in range(1,sheet.nrows): # 第一行是標題,從索引為1的開始拿數據row = sheet.row(index)# [text: 'zz', number: 1123.0]# [text: 'aa', number: 23.0]# 拿到的是列表,可是我們要轉化成字典# {text: 'zz', number: 1123.0}for i in range(len(maps)):key = maps[i]cell = row[i]row_dict[key] = cell.valueprint(row_dict)# 獲取客戶id,錄入客戶表,錄入分配表current_date = datetime.datetime.now().date()sale_id = AutoSale.get_sale_id()print('236',sale_id)if not sale_id:return HttpResponse('暫無課程顧問,請添加后再執行操作!')try:with transaction.atomic():# 客戶表保存new_customer = models.Customer.objects.create(**row_dict,consultant_id=sale_id,recv_date=current_date,last_consult_date=current_date)print(new_customer)# 將關系添加到客戶分配表models.CustomerDistribution.objects.create(customer=new_customer,ctime=current_date,user_id=sale_id)# 發送郵件信息# message.send_message('17701335022@163.com','saofei','fk','fk you')print(new_customer)except Exception as e:# 創建客戶和分配銷售異常
                    AutoSale.rollback(sale_id)return HttpResponse('錄入異常')return  HttpResponse('錄入成功')else:return render(request, 'multi_view.html')# # 為用戶提供模版# def download_file(request):#     # do something#
    #     the_file_name='11.png'             #顯示在彈出對話框中的默認的下載文件名#     filename='media/uploads/11.png'    #要下載的文件路徑#     response=StreamingHttpResponse(readFile(filename))#     response['Content-Type']='application/octet-stream'#     response['Content-Disposition']='attachment;filename="{0}"'.format(the_file_name)#     return response# def readFile(filename,chunk_size=512):#     with open(filename,'rb') as f:#         while True:#             c=f.read(chunk_size)#             if c:#                 yield c#             else:#                 break

v1.site.register(models.Customer,CustomerConfig)
公共資源、搶單、當前個人客戶、單條和批量導入(信息提醒、使用excel導入客戶記錄)

?

2. 管理

  涉及到兩張表,StudyRecord & CourseRecord。參照表結構,查看相關字段。

   a) ?在成功的將客戶轉化為學員后,根據其所填選的具體信息如:校區,課程,以及初填信息。并且初始化學員賬號信息,方便學員可以登錄該系統查看自己的學分以及考勤記錄(此處用到了Highcharts,任課老師也可查看學生相關信息)。

  

 1 {% load staticfiles %}
 2 <!DOCTYPE html>
 3 <html lang="en">
 4 <head>
 5     <meta charset="UTF-8">
 6     <title>Title</title>
 7 </head>
 8 <body>
 9 
10 <h2>所在班級</h2>
11 <ul class="fa-list-ul" id="classList" sid="{{ obj.sid }}">
12     {% for obj in class_list %}
13         <li cid="{{ obj.id }}">{{ obj.course.name }}({{ obj.semester }}期)</li>
14     {% endfor %}
15 </ul>
16 <h2>成績圖</h2>
17 <div id="container" style="width:600px;height:400px"></div>
18 
19 <script src="{% static '/stark/bootstrap/js/jquery-3.1.1.js' %}"></script>
20 <script src="{% static 'highcharts.js' %}"></script>
21 <script>
22 $(function () {
23     $('#classList li').click(function () {
24         var cid = $(this).attr('cid');
25         var sid = $('#classList').attr('sid');
26         $.ajax({
27             url :' /stark/app01/student/chart',
28             type : 'get',
29             data : {'cid':cid,'sid':sid},
30             dataType : 'JSON',
31             success:function (arg) {
32                         var config = {
33                             chart: {
34                                 type: 'column'
35                             },
36                             title: {
37                                 text: '學生成績'
38                             },
39                             subtitle: {
40                                 text: '數據截止 2017-03,來源: <a href="https://en.wikipedia.org/wiki/List_of_cities_proper_by_population">Wikipedia</a>'
41                             },
42                             xAxis: {
43                                 type: 'category',
44                                 labels: {
45                                     rotation: -45,
46                                     style: {
47                                         fontSize: '13px',
48                                         fontFamily: 'Verdana, sans-serif'
49                                     }
50                                 }
51                             },
52                             yAxis: {
53                                 min: 0,
54                                 title: {
55                                     text: '分數'
56                                 }
57                             },
58                             legend: {
59                                 enabled: false
60                             },
61                             tooltip: {
62                                 pointFormat: '成績: <b>{point.y} </b>'
63                             },
64                             series: [{
65                                 name: '成績',
66                                 data: [],
67                                 dataLabels: {
68                                     enabled: true,
69                                     rotation: -90,
70                                     color: '#FFFFFF',
71                                     align: 'right',
72                                     format: '{point.y}', // one decimal
73                                     y: 10, // 10 pixels down from the top
74                                     style: {
75                                         fontSize: '13px',
76                                         fontFamily: 'Verdana, sans-serif'
77                                     }
78                                 }
79                             }]
80                         };
81 
82                         config.series[0].data =arg.data;
83 
84                         //$('#container').highcharts(config);
85                         Highcharts.chart('container',config);
86 
87                     }
88                 })
89             })
90         })
91 
92 
93 </script>
94 </body>
95 </html>
scores_view

?

   b) 出勤記錄:任課教師可初始化學員出勤信息,并支持批量修改。

    學員哪天進入班級,就從哪天開始生成記錄。考勤與成績掛鉤。

    實現機制:

      

 1     def mutil_init(self,request):
 2         """自定義批量初始化方法"""
 3         # 上課記錄id列表
 4         pk_list = request.POST.getlist('pk')
 5         # 上課記錄對象列表
 6         record_list = models.CourseRecord.objects.filter(id__in=pk_list)
 7         # print(record_list)
 8         # # 這種是,遍歷每一個學生,查看是否存在記錄。
 9         # for record in record_list:
10         #     student_list = models.Student.objects.filter(class_list=record.class_obj)
11         #     bulk_list = []
12         #     for student in student_list:
13         #         exists = models.StudyRecord.objects.filter(student=student,course_record=record).exists()
14         #         if exists:
15         #             continue
16         #         bulk_list.append(models.StudyRecord(student=student,course_record=record))
17         #     models.StudyRecord.objects.bulk_create(bulk_list)
18 
19         # 下面這種是,只要有當天的學習記錄,后面不管還有沒有學生來,都不能添加
20         for record in record_list:
21             if models.StudyRecord.objects.filter(course_record=record).exists():
22                 continue
23             student_list = models.Student.objects.filter(class_list=record.class_obj)
24             # 為每一個學生創建dayn的學習記錄
25             bulk_list = []
26             for student in student_list:
27                 bulk_list.append(models.StudyRecord(student=student,course_record=record))
28             models.StudyRecord.objects.bulk_create(bulk_list)
29         # return redirect('/stark/app01/courserecord/')
30         return HttpResponse('初始化成功!')
31 
32     show_actions = True
33     mutil_init.short_desc = "學生初始化"
34     actions = [mutil_init,] # 因為這個是批量操作,咱們需要寫點方法,里面是我們要實現的東西,所以函數
批量初始化

  c) 錄入成績:

    這里是通過點擊在課程記錄頁面,我們手動生成的查看成績的a標簽,進入到成績錄入界面的。通過當前點擊的課程記錄的id,拿到學習記錄對象,再進行編輯。

    并且,因為要使前端拿到每個學習記錄id,我們就需要想辦法將值傳過去。

    在這使用了type來動態創建類。

    fields字段,寫了一個score_學習記錄id,還有一個home_note_學習記錄id。該方法下,其實是由默認值的,so 在傳值的時候回有initial。并且post請求時,是用的update

 1     def score_list(self,request,record_id):
 2         """
 3         錄入成績頁面
 4         :param request:
 5         :param record_id: 老師上課記錄ID
 6         :return:
 7         """
 8         if request.method == 'GET':
 9             from django.forms import Form
10             from django.forms import fields
11             from django.forms import widgets
12             ##方式一
13             # study_record_list = models.StudyRecord.objects.filter(course_record_id=record_id)  #這一天上課的所有的學生的學習記錄
14             # score_choices = models.StudyRecord.score_choices
15             # return render(request,"score_list.html",{"study_record_list":study_record_list,"score_choices":score_choices})
16 
17             # 改款
18             # class TestForm(Form):
19             #     score = fields.ChoiceField(choices=models.StudyRecord.record_choices)
20             #     homeword_note = fields.CharField(widget=widgets.Textarea())
21 
22             # 因為前端要拿到id和對象,所以使用type來創建,來自定義字段
23             study_record_list = models.StudyRecord.objects.filter(course_record_id=record_id)
24             data = []
25             for obj in study_record_list:
26                 TestForm = type('TempForm',(Form,),{
27                     'score_%s'%obj.pk : fields.ChoiceField(choices=models.StudyRecord.score_choices),
28                     'homework_note_%s'%obj.pk : fields.CharField(widget=widgets.Textarea())
29                 })
30                 data.append({'obj':obj,'form':TestForm(initial={'score_%s' %obj.pk:obj.score,'homework_note_%s' %obj.pk:obj.homework_note})})
31             return render(request,'score_list.html',{'data':data})
32         else:
33             data_dict = {}
34             """
35             構造這樣的字典,目的是保存更新數據庫里面的數據,字典的結構的
36             {
37                 3:{"score":2,"homework_note":2}
38                 4:{"score":4,"homework_note":4}
39             }
40             """
41             for key, value in request.POST.items():
42                 if key == "csrfmiddlewaretoken":
43                     continue
44                 name, nid = key.rsplit('_', 1)
45                 if nid in data_dict:
46                     data_dict[nid][name] = value
47                 else:
48                     data_dict[nid] = {name: value}
49 
50             for nid, update_dict in data_dict.items():
51                 print(data_dict.items())
52                 models.StudyRecord.objects.filter(id=nid).update(**update_dict)
53 
54             return redirect(request.path_info)
score_list
 1 {% load staticfiles %}
 2 <!DOCTYPE html>
 3 <html lang="en">
 4 <head>
 5     <meta charset="UTF-8">
 6     <title>成績錄入</title>
 7     <link rel="stylesheet" href="{% static 'stark/bootstrap/css/bootstrap.css' %}">
 8 </head>
 9 <body>
10 <h2>自定義成績錄入</h2>
11 <form method="post">
12     {% csrf_token %}
13     <table class="table">
14         {% for row in data %}
15             <tr>
16             <td>{{ row.obj.course_record }}</td>
17             <td>{{ row.obj.student }}</td>
18             <td>{{ row.obj.get_record_display }}</td>
19             {% for field in row.form %}
20                 <td>{{ field }}</td>
21             {% endfor %}
22             </tr>
23         {% endfor %}
24 
25     </table>
26     <input type="submit" value="保存">
27 </form>
28 
29 </body>
30 </html>
score_list.HTML

?

3. 會議室預定

  開發背景:公司在壯大,各部門大小事也變得更多。你們部門要不要開個會互相解決一群大齡男青年的問題嘛。要的話,你要不要有次序的來用會議室嘛?要的話,你是不是要預定一下嘛?要的話,會議室預定要不要做一個嘛。。。來,交給程序來完成。

  

會議室預定表結構設計

  具體實現: ? ?記得要綁定事務!!!

       第一步: 獲取當天的預定信息。

       第二步: 生成會議室信息。 在生成會議室信息里面,需要區分當前預定是否是自己預定的。自己訂的可以取消,但是不能取消非自己預定的會議室信息。

       第三步提交: 點擊提交后,首先要判斷日期是否正確,只能預定大于當前時間之后的會議室信息。并且,要區分一下是新增還是修改。

       第四部刪除: 這里需要用到Q查詢,組合多個條件來實現。

   

 1 # 刪除會議室預定信息
 2             from django.db.models import Q
 3             remove_booking = Q()
 4             for room_id, time_id_list in post_data['DEL'].items():
 5                 for time_id in time_id_list:
 6                     temp = Q()
 7                     temp.connector = 'AND'
 8                     temp.children.append(('user_id',2,))
 9                     temp.children.append(('date', choice_date))
10                     temp.children.append(('room_id', room_id,))
11                     temp.children.append(('time_id', time_id,))
12 
13                     remove_booking.add(temp, 'OR')
14             if remove_booking:
15                 models.Book.objects.filter(remove_booking).delete()

?

4: 調查問卷

參照:Django_調查問卷

?

5:最騷的東西:RBAC ? 會單獨總結。

  已經在寫了。

  鏈接:RBAC?? 編寫流程在ReadMe里面。

?

轉載于:https://www.cnblogs.com/ugfly/p/8199269.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/255755.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/255755.shtml
英文地址,請注明出處:http://en.pswp.cn/news/255755.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【面經——《速騰聚創科技有限公司——深度學習算法工程師》】

自我介紹 實習項目 1&#xff09;項目主要應用的領域&#xff1f; 2&#xff09;難點在哪&#xff1f;——機械臂吸盤大小和目標大小之間坐標的協調 3&#xff09;難點不在于算法&#xff0c;在于數據的處理和均衡性&#xff1f;對于數據均衡方面有什么理解&#xf…

js變量和數據類型

轉載于:https://www.cnblogs.com/songyinan/p/6181421.html

offline .net3.5

1.加載虛擬光驅 2.dism.exe /online /enable-feature /featurename:netfx3 /Source:D:\sources\sxs轉載于:https://www.cnblogs.com/BillLei/p/5294082.html

(九)模板方法模式詳解(包含與類加載器不得不說的故事)

作者&#xff1a;zuoxiaolong8810&#xff08;左瀟龍&#xff09;&#xff0c;轉載請注明出處&#xff0c;特別說明&#xff1a;本博文來自博主原博客&#xff0c;為保證新博客中博文的完整性&#xff0c;特復制到此留存&#xff0c;如需轉載請注明新博客地址即可。 模板方法模…

阿里云openapi接口使用,PHP,視頻直播

1.下載sdk放入項目文件夾中 核心就是aliyun-php-sdk-core&#xff0c;它的配置文件會自動加載相應的類 2.引入文件 include_once LIB_PATH . ORG/aliyun-openapi/aliyun-php-sdk-core/Config.php; 3.配置客戶端對象,需要Access Key ID,Access Key Secret $iClientProfile Defa…

【面經——《廣州敏視數碼科技有限公司》——圖像處理算法工程師-深度學習方向】

目錄 筆試 HR面 專業面——60多分鐘 主管面 反問&#xff1a; 筆試 8道題——簡答題 1道編程 蘋果、香蕉、梨、菠蘿&#xff0c;彩色圖像如何進行分類&#xff1f;一輛帶車牌的汽車&#xff0c;圖像亮度整體呈現偏亮狀態&#xff0c;如何…

Android之網絡編程利用PHP操作MySql插入數據(四)

因為最近在更新我的項目&#xff0c;就想著把自己在項目中用到的一些的簡單的與網絡交互的方法總結一下&#xff0c;所以最近Android網絡編程方面的博文會比較多一些&#xff0c;我盡量以最簡單的方法給大家分享&#xff0c;讓大家明白易懂。如果有什么不對的地方&#xff0c;還…

RAPID 信號的互鎖和同步 WaitTestAndSet 和 TestAndSet

RAPID 信號的互鎖和同步 WaitTestAndSet 指令等待指定的持久型 BOOL 變量變成 FALSE.當變量值變為 FALSE, 該指令將設置變量為 TRUE 并繼續執行. 該持久型變量可被作為同步或者互斥時的一個 BOOL 信號量。 這個指令與 TestAndSet 有著同樣的基本功能。但是 WaitTestAnd…

【常用網址】——opencv等

opencv官網Releases - OpenCVhttps://opencv.org/releases/

(五):C++分布式實時應用框架——微服務架構的演進

C分布式實時應用框架——微服務架構的演進 技術交流合作QQ群&#xff1a;436466587 歡迎討論交流 上一篇&#xff1a;(四)&#xff1a;C分布式實時應用框架——狀態中心模塊 版權聲明:本文版權及所用技術歸屬smartguys團隊所有&#xff0c;對于抄襲&#xff0c;非經同意轉載等…

如何通過軟件項目開發來提高自身的實力。

在我們這個專業&#xff0c;大多數人都不會將軟件開發當作自己的事業&#xff0c;因為若要在這個行業上能夠立足&#xff0c;得需要一個好的基礎&#xff0c;但是由于這個東西并不是可以通過書本能夠徹底的理解和 掌握的&#xff0c;隨著時間的變化&#xff0c;我們身邊的科技也…

夢回JavaScript--數據類型之undefined

undefined類型只有一個值&#xff0c;即undefined。在使用var聲明變量但未對其加以初始化時&#xff0c;這個變量的值就是undefined&#xff1b; var mes; alert(mes undefined) //true如果變量沒有聲明就會出現錯誤 var mes; alert(mes) //undefined alert(a)//error 然而有一…

Robot Application Builder

軟件開發工具包 Robot Application Builder是安裝在PC機&#xff08;Windows 2000或Windows XP操作系統&#xff09;上的一種獨立開發工具&#xff0c;可用于創建運行于ABB FlexPendant示教器或PC機上的定制化操作界面。為此&#xff0c;該軟件包由以下兩部分組成&#xff1a;…

asp.net model 驗證和取出 ErrorMessage 信息

為什么80%的碼農都做不了架構師&#xff1f;>>> public class Users{public int Id { get; set; }public string Name { get; set; }[Required(ErrorMessage "郵箱不能為空")][EmailAddressAttribute(ErrorMessage "郵箱格式不正確")]public…

this

作者&#xff1a;李挺鏈接&#xff1a;https://www.zhihu.com/question/19636194/answer/123274198來源&#xff1a;知乎著作權歸作者所有&#xff0c;轉載請聯系作者獲得授權。關于 this 的描述&#xff0c;曾經在 stackoverflow 上看到了一篇回答寫的非常詳盡&#xff0c;下面…

DeviceNet 消息類型

DeviceNet是一種低成本的通訊總線鏈接&#xff0c;具有開放現場網絡標準&#xff0c;規范和協議都是開放的。DeviceNet將控制和數據融合在一起&#xff0c;信息具有數據標識區&#xff0c;網絡利用標識區進行優先級仲裁&#xff0c;可以高效傳送I/O數據。 DeviceNet有兩種不同類…

【pyqt5學習——信號與槽】實例計時器(解決界面卡頓問題)

目錄 一、方法一&#xff1a;另開線程 1、什么是信號與槽 1&#xff09;GUI控件&#xff08;信號&#xff09;與槽 2&#xff09;自定義信號與槽 2、實戰1&#xff1a;計時器&#xff08;不自定義信號槽和不使用多線程&#xff09; 1&#xff09;界面設計——利用qt-desi…

【轉】為什么螺絲都是六角的?

6邊形的螺絲擰60度就可以圖形還原&#xff08;不知道表述清楚沒&#xff0c;見討論中的解釋&#xff09;&#xff09; 如果空間比較狹小&#xff0c;只要扳手能擰動60度就能安裝上螺絲 這是在擰動角度和邊長相互妥協后的產物 試想 如果是正方形&#xff0c;邊長夠長了&#xff…

用PHP和Websocket實現實時通訊

說到websocket大家一定不會陌生&#xff0c;WebSocket是HTML5一種新的協議。它實現了瀏覽器與服務器全雙工通信(full-duplex)。一開始的握手需要借助HTTP請求完成&#xff0c;當瀏覽器和服務器握手成功后&#xff0c;瀏覽器和服務器之間就形成了一條快速通道。兩者之間就直接可…