Django 项目CRM总结

时间:2024-11-05 20:34:08

0. 项目说明:

1. 销售自动分配客户资源:
给销售分配权重及承单数量,创建权重表,通过销售权重进行从大到小进行排序
以承单数循环添加到列表,承单数是多少列表添加就添加多少次
考虑到如果服务重启,或多台服务器同时运行,数据分配不统一问题: 将列表中的数据放到redis中,每分配一个客户,就从redis中pop出一个销售id; 当从redis中取出所有销售id时,在redis中添加一条状态,判断状态是否为True
如果为True删除备份数据,重新到数据库中取出销售id并且存放一份备份数据
并且返回True 销售人员也可以在CRM中自己添加客户信息,如果3天未跟进,15天未成单,这个用户资源将
变成公共资源,其他销售人员就可以在公共资源中进行抢单更近,但是该销售人员不能再次去
抢单这个用户,防止3天或15天到期,该销售第一时间去抢该客户公司规定。 公司运营部门和销售总监,可以在CRM中批量导入客户资源信息,CRM程序在后台自动分配到每一个销售人员 2.增加权限系统
权限系统有5个类七张表,用户表,角色表,菜单表,菜单组表,权限表,用户角色表,角色权限表
给每一个公司内部用添加角色,每个角色关联一条或多条权限
通过中间件,当公司人员登录到CRM中,中间件进行登录验证,在session中获取登录用信息,通过ORM
查询登录用户的权限,在CRM后台中进行页面展示。譬如管理员有所有权限,并且可以添加权限,修改部门用户信息等操作
运营部门和销售总监可以批量导入客户资源。 3.微信,邮件,等发送消息组件

1. 为什么开发CRM:

为了客户的管理

给自己公司用:原来人员少通过excel保存
给公司客户用:原来人员少通过excel保存
现在公司人员越来越多,业务量也慢慢增加,使用之前的excel记录数据方式已经远远不能满足现在的工作需求
操作复杂,想要增加功能也无法增加,所以公司就需要这样的CRM后台进行对公司内部人员的管理,销售业务的
分配任务管理,用户资源管理,资源分配,公司权限分配等等操作功能。

2. 开发周期  

- 开发周期:预计2周,技术点有些不太确定,先进行评估然后再给老大明确答案
- crm开发周期:开发2个月,2个月持续还在做:修复bug和新功能的开发 开发阶段:
- 只开发业务,快速实现想要的功能
项目维护和扩展
- 抽离组件以后方便其他系统快速应用

3. 技术点: 

- 表和字段

- 权限控制
5个类七张表:
  User类,Role类,Menu类菜单,Group类菜单组,Permission类权限
    用户表,角色表,菜单表,菜单组表,权限表,用户角色表,角色权限表
    
from django.db import models

class Menu(models.Model):
"""
菜单组
"""
title = models.CharField(max_length=32) def __str__(self):
return self.title
class Group(models.Model):
"""
权限组
"""
caption = models.CharField(verbose_name='组名称',max_length=16)
menu = models.ForeignKey(verbose_name='所属菜单',to='Menu',default=1) def __str__(self):
return self.caption
class Permission(models.Model):
"""
权限表
"""
title = models.CharField(verbose_name='标题',max_length=32)
url = models.CharField(verbose_name="含正则URL",max_length=64) menu_gp = models.ForeignKey(verbose_name='组内菜单',to='Permission',null=True,blank=True,related_name='x1') code = models.CharField(verbose_name="代码",max_length=16)
group = models.ForeignKey(verbose_name='所属组',to="Group") class Meta:
verbose_name_plural = "权限表" def __str__(self):
return self.title class User(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:
verbose_name_plural = "用户表" def __str__(self):
return self.username class Role(models.Model):
"""
角色表
"""
title = models.CharField(max_length=32)
permissions = models.ManyToManyField(verbose_name='具有的所有权限',to='Permission',blank=True)
class Meta:
verbose_name_plural = "角色表" def __str__(self):
return self.title
- CRM业务
  13个类16张表
  
from django.db import models
from rbac import models as rbac_model class Department(models.Model):
"""
部门表
市场部 1000
销售 1001
"""
title = models.CharField(verbose_name='部门名称', max_length=16)
code = models.IntegerField(verbose_name='部门编号',unique=True,null=False) def __str__(self):
return self.title class UserInfo(models.Model):
"""
员工表
"""
auth = models.OneToOneField(verbose_name='用户权限', to=rbac_model.User,null=True,blank=True)
name = models.CharField(verbose_name='员工姓名', max_length=16)
username = models.CharField(verbose_name='用户名', max_length=32)
password = models.CharField(verbose_name='密码', max_length=64)
email = models.EmailField(verbose_name='邮箱', max_length=64) depart = models.ForeignKey(verbose_name='部门', to="Department",to_field="code") def __str__(self):
return self.name class Course(models.Model):
"""
课程表
如:
Linux基础
Linux架构师
Python自动化开发精英班
Python自动化开发架构师班
"""
name = models.CharField(verbose_name='课程名称', max_length=32) def __str__(self):
return self.name class School(models.Model):
"""
校区表
如:
北京海淀校区
北京昌平校区
上海虹口校区
广州白云山校区
"""
title = models.CharField(verbose_name='校区名称', max_length=32) def __str__(self):
return self.title class ClassList(models.Model):
"""
班级表
如:
Python全栈 面授班 5期 10000 2017-11-11 2018-5-11
"""
school = models.ForeignKey(verbose_name='校区', to='School')
course = models.ForeignKey(verbose_name='课程名称', to='Course') semester = models.IntegerField(verbose_name="班级(期)")
price = models.IntegerField(verbose_name="学费")
start_date = models.DateField(verbose_name="开班日期")
graduate_date = models.DateField(verbose_name="结业日期", null=True, blank=True)
memo = models.CharField(verbose_name='说明', max_length=256, blank=True, null=True, )
teachers = models.ManyToManyField(verbose_name='任课老师', to='UserInfo', related_name='teach_classes')
tutor = models.ForeignKey(verbose_name='班主任', to='UserInfo', related_name='classes') def __str__(self):
return "{0}({1}期)".format(self.course.name, self.semester) class Customer(models.Model):
"""
客户表
"""
qq = models.CharField(verbose_name='qq', max_length=64, unique=True, help_text='QQ号必须唯一') name = models.CharField(verbose_name='学生姓名', max_length=16)
gender_choices = ((1, '男'), (2, '女'))
gender = models.SmallIntegerField(verbose_name='性别', choices=gender_choices) education_choices = (
(1, '重点大学'),
(2, '普通本科'),
(3, '独立院校'),
(4, '民办本科'),
(5, '大专'),
(6, '民办专科'),
(7, '高中'),
(8, '其他')
)
education = models.IntegerField(verbose_name='学历', choices=education_choices, blank=True, null=True, )
graduation_school = models.CharField(verbose_name='毕业学校', max_length=64, blank=True, null=True)
major = models.CharField(verbose_name='所学专业', max_length=64, blank=True, null=True) experience_choices = [
(1, '在校生'),
(2, '应届毕业'),
(3, '半年以内'),
(4, '半年至一年'),
(5, '一年至三年'),
(6, '三年至五年'),
(7, '五年以上'),
]
experience = models.IntegerField(verbose_name='工作经验', blank=True, null=True, choices=experience_choices)
work_status_choices = [
(1, '在职'),
(2, '无业')
]
work_status = models.IntegerField(verbose_name="职业状态", choices=work_status_choices, default=1, blank=True,
null=True)
company = models.CharField(verbose_name="目前就职公司", max_length=64, blank=True, null=True)
salary = models.CharField(verbose_name="当前薪资", max_length=64, blank=True, null=True) source_choices = [
(1, "qq群"),
(2, "内部转介绍"),
(3, "官方网站"),
(4, "百度推广"),
(5, "360推广"),
(6, "搜狗推广"),
(7, "腾讯课堂"),
(8, "广点通"),
(9, "高校宣讲"),
(10, "渠道代理"),
(11, "51cto"),
(12, "智汇推"),
(13, "网盟"),
(14, "DSP"),
(15, "SEO"),
(16, "其它"),
]
source = models.SmallIntegerField('客户来源', choices=source_choices, default=1)
referral_from = models.ForeignKey(
'self',
blank=True,
null=True,
verbose_name="转介绍自学员",
help_text="若此客户是转介绍自内部学员,请在此处选择内部学员姓名",
related_name="internal_referral"
)
course = models.ManyToManyField(verbose_name="咨询课程", to="Course") status_choices = [
(1, "已报名"),
(2, "未报名")
]
status = models.IntegerField(
verbose_name="状态",
choices=status_choices,
default=2,
help_text=u"选择客户此时的状态"
)
consultant = models.ForeignKey(verbose_name="课程顾问", to='UserInfo', related_name='consultant',limit_choices_to={'depart_id':1001})
recv_date = models.DateField(verbose_name='接客时间', null=True, blank=True)
date = models.DateField(verbose_name="咨询日期", auto_now_add=True)
last_consult_date = models.DateField(verbose_name="最后跟进日期", auto_now_add=True) def __str__(self):
return "姓名:{0},QQ:{1}".format(self.name, self.qq, ) class CustomerDistribution(models.Model):
"""
客户分配表
"""
user = models.ForeignKey(verbose_name='客户顾问',to='UserInfo',related_name='cds',limit_choices_to={'depart_id':1001})
customer = models.ForeignKey(verbose_name='客户',to='Customer',related_name='dealers')
ctime = models.DateField()
status_choices = (
(1,'正在跟进'),
(2,'已成单'),
(3,'3天未跟进'),
(4,'15天未跟进'),
)
status = models.IntegerField(verbose_name='状态',choices=status_choices,default=1)
memo = models.CharField(verbose_name='更多信息',max_length=255,null=True) class SaleRank(models.Model):
"""
销售权重和数量
"""
user = models.ForeignKey(to='UserInfo',limit_choices_to={'depart_id':1001})
num = models.IntegerField(verbose_name='数量')
weight = models.IntegerField(verbose_name='权重') class ConsultRecord(models.Model):
"""
客户跟进记录
"""
customer = models.ForeignKey(verbose_name="所咨询客户", to='Customer')
consultant = models.ForeignKey(verbose_name="跟踪人", to='UserInfo')
date = models.DateField(verbose_name="跟进日期", auto_now_add=True)
note = models.TextField(verbose_name="跟进内容...") class PaymentRecord(models.Model):
"""
缴费记录
"""
customer = models.ForeignKey(Customer, verbose_name="客户") class_list = models.ForeignKey(verbose_name="班级", to="ClassList", blank=True, null=True) pay_type_choices = [
(1, "订金/报名费"),
(2, "学费"),
(3, "转班"),
(4, "退学"),
(5, "退款"),
]
pay_type = models.IntegerField(verbose_name="费用类型", choices=pay_type_choices, default=1)
paid_fee = models.IntegerField(verbose_name="费用数额", default=0)
turnover = models.IntegerField(verbose_name="成交金额", blank=True, null=True)
quote = models.IntegerField(verbose_name="报价金额", blank=True, null=True)
note = models.TextField(verbose_name="备注", blank=True, null=True)
date = models.DateTimeField(verbose_name="交款日期", auto_now_add=True)
consultant = models.ForeignKey(verbose_name="负责老师", to='UserInfo', help_text="谁签的单就选谁") class Student(models.Model):
"""
学生表(已报名)
"""
customer = models.OneToOneField(verbose_name='客户信息', to='Customer') username = models.CharField(verbose_name='用户名', max_length=32)
password = models.CharField(verbose_name='密码', max_length=64)
emergency_contract = models.CharField(max_length=32, blank=True, null=True, verbose_name='紧急联系人')
class_list = models.ManyToManyField(verbose_name="已报班级", to='ClassList', blank=True) company = models.CharField(verbose_name='公司', max_length=128, blank=True, null=True)
location = models.CharField(max_length=64, verbose_name='所在区域', blank=True, null=True)
position = models.CharField(verbose_name='岗位', max_length=64, blank=True, null=True)
salary = models.IntegerField(verbose_name='薪资', blank=True, null=True)
welfare = models.CharField(verbose_name='福利', max_length=256, blank=True, null=True)
date = models.DateField(verbose_name='入职时间', help_text='格式yyyy-mm-dd', blank=True, null=True)
memo = models.CharField(verbose_name='备注', max_length=256, blank=True, null=True) def __str__(self):
return self.username class CourseRecord(models.Model):
"""
上课记录表
"""
class_obj = models.ForeignKey(verbose_name="班级", to="ClassList")
day_num = models.IntegerField(verbose_name="节次", help_text=u"此处填写第几节课或第几天课程...,必须为数字")
teacher = models.ForeignKey(verbose_name="讲师", to='UserInfo')
date = models.DateField(verbose_name="上课日期", auto_now_add=True) course_title = models.CharField(verbose_name='本节课程标题', max_length=64, blank=True, null=True)
course_memo = models.TextField(verbose_name='本节课程内容概要', blank=True, null=True)
has_homework = models.BooleanField(default=True, verbose_name="本节有作业")
homework_title = models.CharField(verbose_name='本节作业标题', max_length=64, blank=True, null=True)
homework_memo = models.TextField(verbose_name='作业描述', max_length=500, blank=True, null=True)
exam = models.TextField(verbose_name='踩分点', max_length=300, blank=True, null=True) def __str__(self):
return "{0} day{1}".format(self.class_obj, self.day_num) class StudyRecord(models.Model):
course_record = models.ForeignKey(verbose_name="第几天课程", to="CourseRecord")
student = models.ForeignKey(verbose_name="学员", to='Student')
record_choices = (('checked', "已签到"),
('vacate', "请假"),
('late', "迟到"),
('noshow', "缺勤"),
('leave_early', "早退"),
)
record = models.CharField("上课纪录", choices=record_choices, default="checked", max_length=64)
score_choices = ((100, 'A+'),
(90, 'A'),
(85, 'B+'),
(80, 'B'),
(70, 'B-'),
(60, 'C+'),
(50, 'C'),
(40, 'C-'),
(0, ' D'),
(-1, 'N/A'),
(-100, 'COPY'),
(-1000, 'FAIL'),
)
score = models.IntegerField("本节成绩", choices=score_choices, default=-1)
homework_note = models.CharField(verbose_name='作业评语', max_length=255, blank=True, null=True)
note = models.CharField(verbose_name="备注", max_length=255, blank=True, null=True) homework = models.FileField(verbose_name='作业文件', blank=True, null=True, default=None)
stu_memo = models.TextField(verbose_name='学员备注', blank=True, null=True)
date = models.DateTimeField(verbose_name='提交作业日期', auto_now_add=True) def __str__(self):
return "{0}-{1}".format(self.course_record, self.student)
- 满意度调查
  七个类七张表
  员工表,班级列表,学生列表,问卷调查表,问题表,单选题选项表,回卷表
  
from django.db import models

class UserInfo(models.Model):
"""
员工表
"""
username = models.CharField(max_length=32)
password = models.CharField(max_length=32) class Meta:
verbose_name_plural = '员工表' def __str__(self):
return self.username class ClassList(models.Model):
"""
班级表
"""
title = models.CharField(max_length=32) class Meta:
verbose_name_plural = '班级列表' def __str__(self):
return self.title class Student(models.Model):
"""
学生表
"""
user = models.CharField(max_length=32)
pwd = models.CharField(max_length=32)
cls = models.ForeignKey(to='ClassList') class Meta:
verbose_name_plural = '学生列表' def __str__(self):
return self.user class Questionnaire(models.Model):
"""
问卷表
"""
title = models.CharField(max_length=64)
cls = models.ForeignKey(to=ClassList,verbose_name='所调查问卷班级')
creator = models.ForeignKey(to='UserInfo',verbose_name='创建人') count_answer = models.IntegerField(default=0,verbose_name='统计回答问卷的学生数') class Meta:
verbose_name_plural = '问卷调查表' def __str__(self):
return self.title class Question(models.Model):
"""
问题
"""
caption = models.CharField(max_length=64, verbose_name='问题内容') question_types = (
(1, '打分'),
(2, '单选'),
(3, 'c'),
)
tp = models.IntegerField(choices=question_types)
naire = models.ForeignKey(to=Questionnaire, default=1)
class Meta:
verbose_name_plural = '问题表' def __str__(self):
return self.caption class Option(models.Model):
"""
单选题的选项
"""
name = models.CharField(verbose_name='选项名称', max_length=32)
score = models.IntegerField(verbose_name='选项对应的分值')
qs = models.ForeignKey(to='Question', verbose_name='管理问题') class Meta:
verbose_name_plural = '单选题选项' def __str__(self):
return self.name class Answer(models.Model):
"""
回答
"""
stu = models.ForeignKey(to='Student')
question = models.ForeignKey(to='Question') option = models.ForeignKey(to="Option", null=True, blank=True,verbose_name='所回答的问题')
val = models.IntegerField(null=True, blank=True)
content = models.CharField(max_length=255, null=True, blank=True) class Meta:
verbose_name_plural = '回卷表'
- 会议室预定
  3个类3张表
  员工表,会议室表,预定表
  
from django.db import models

# Create your models here.
class UserInfo(models.Model):
"""
员工表
"""
username = models.CharField(max_length=32,verbose_name='用户')
password = models.CharField(max_length=32,verbose_name='密码') class Meta:
verbose_name_plural = '员工表' def __str__(self):
return self.username class Room(models.Model):
'''
会议室表
'''
caption = models.CharField(max_length=32,verbose_name='会议室') class Booking(models.Model):
user = models.ForeignKey(to='UserInfo',verbose_name='用户')
room = models.ForeignKey(to='Room',verbose_name='会议室')
booking_date = models.DateField(verbose_name='预定日期') # 年月日 DateTimeField:年月日 时分秒
time_choices = (
(1, '8:00'),
(2, '9:00'),
(3, '10:00'),
(4, '11:00'),
(5, '12:00'),
(6, '13:00'),
(7, '14:00'),
(8, '15:00'),
(9, '16:00'),
(10, '17:00'),
(11, '18:00'),
(12, '19:00'),
(13, '20:00'),
)
booking_time = models.IntegerField(verbose_name='预定时间段', choices=time_choices) class Meta:
unique_together = (
('booking_date', 'booking_time', 'room') # 一个日期中的时间段只能预定一个会议室
)

 

- 有没有遇到坑?令你印象深刻的事情?你觉得写的比较吊的功能?
- 组合搜索时,生产URL,__iter__方法
- requset.GET
- 深拷贝
- 可迭代对象
- yield
- 面向对象封装
- popup
- 回调函数
- window.open('','name')
- opener.xxxx回调函数x()
- FK时,可以使用limit_choice_to(引用另一张表,给另一张添加条件),可以是字典和Q对象
- related_name和model_name
- 获取所有的反向关联字典,获取limit_choice_to字段
- 查询
- excel批量导入
- 路由系统
- 动态的生产url,增删改查
- 看Admin源码(include)
- /xx/ -> ([
'xxx',],namespace)
- 开发组件时,最开始看admin源码不太理解,但是当和权限系统和配合是,才明白开发的组件用途太广。

4.其他:

1. 通过ChangList封装好多数据
2. 销售中功能资源:Q实现,3天/15天
3. 使用yield实现:
- 对数据二次加工处理(生成器函数)
- 对应对象进行循环__iter__和yield配合 4. 获取Model类中的字段对应的对象
class Foo(model.Model):
xx = models.CharField()
Foo.get_field('xx')
5. 模糊搜索功能
6. Type创建类,动态创建ModelForm
7. 自动派单
- 根据权重,从大到小进行排序,通过承担量进行循环次数添加到列表中
- 原来在内存中实现,问题:重启和多进程是都会存在问题
- 使用redis解决问题,
- 状态
- 原来的数据(权重表 权重和数量)
- pop数据(pop完再copy原来的数据)
8. 使用 list_display配置
list_display=[函数名,字段名]
9. reverse 方向生成URL
10. 母板
11. 静态文件查找顺序
12. 定制ready方法,起始文件
13. inclusion_tag
14. 中间件的使用
中间件最多有几个方法:
最多5个,常用的就2个process_request,process_response
使用权限登录,权限验证
crsf_token,内部使用process_view方法
15. importlib + getattr
16. FilterOption,lambda表达式
17. QueryDict
- 原条件的保留
- filter
18. ModelForm处理数据保留外键pk
19. 面向对象的 @property -- 操作不用加括号 @classmethod 自动分配客户id
20. make_safe() 或在前端 添加safe
21. xss攻击,抽象方法,抽象类+raise 抛出异常
22. 组件中的装饰器,实现self.request = request
23. 封装model类的属性
24. js自执行函数
(function(arg)){}('self')
25. URL的钩子函数
26. 多继承
27. 批量插入和批量导入,xlrd
28. redis连接池
29. 工厂模式
settting.py
MSG_PATH= 'path.Email' class XXFactory(object):
@classmethod
def get_msg(cls):
settting.MSG_PATH
# rsplit
# importlib
# getattr
return obj # 示例
MESSAGE_CLASSES = [
'utils.message.email.Email',
'utils.message.msg.Msg',
'utils.message.wx.WeChat',
'utils.message.dingding.DingDing',
]
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,)
class Email(object):
def send ....
class WeChat(object):
def send ....
class Msg(object):
def send .... 30. Models类中自定义save方法 31. 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 32. 深浅拷贝