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. 深浅拷贝