项目描述
系统基于权限管理的使用,对系统人员进行权限控制对学校、老师、班级和销售进行统一管理, 其中涉及销售的报表和跟进记录,并对销售人员成单比进行统计,通过highchart做出图表。 以及班级和评分的管理
主要业务是"销售管理": 主要针对的是公司销售部门的工作管理,主要用于工作安排, 销售进度的跟进以及为业绩考核提供数据支持。 首先公司拿到一批资源,通过单条输入,或批量导入的方式添加 数据到客户表,批量导入支持Excel导入。 添加到数据库后系统自动按销售的权重将客户分派给销售(radis), 并发送短信,微信,邮箱提醒销售。 销售进行跟进,客户分派表新增数据,客户跟进记录表新增数据。 销售拿到客户进行跟进,若15天未成单或3天未跟进(定时任务), 客户将变成公共客户,销售可以在公共客户中进行抢单,但不能抢 课程顾问是自己的,抢单成功,客户分派表新增一条记录, 销售拿到客户进行跟进。
还有"学校管理": 基于Thanos组件,保存上课记录,学生考勤,记录学生成绩,图表展示学生成绩等。 除此之外还有:"员工管理","课程管理","班级管理(班级评分,作业管理)"等模块 还有会议室预定,调查问卷,基于角色粒度精确到按钮的权限控制。
项目需求
这套系统主要是给公司内部使用的,公司刚成立的时候,人员少,存储数据都用Excel保存,
但随着公司的扩大,业务的发展,客户的增多Excel无法满足公司的需要,
需要一套,方便快捷管理公司业务的系统。
项目功能
1: 基于Session和极验滑块验证的登录认证功能,以及注册,注销,找回密码等功能。 2:基于中间件和session实现权限组件,粒度到按钮级别。 3:基于BootStrap实现页面展示。 4:使用xlrd实现excel批量操作。。 5:基于HighChart对销售业绩进行可视化显示。 6:参考Django.Admin源码 实现Thanos组件开发,用于快速实现大量的增删改查功能。 7:基于Form实现可定制的调查问卷 8:基于BootStrap datetimepicker(选时间的那个日历)实现会议室。 9: 基于redis的销售自动分配。
项目问答
问题:Django-Admin源码流程?
1:在Django项目启动时,扫描每个APP项目下的admin.py文件的文件,
创建admin.site中的对象,site = AdminSite(),本质实例化一个对象,以后不管谁来调用都使用这个对象,
执行对象的register方法,目的将注册类添加到_register中,
admin.site是一个对象(单例模式创建),其中封装了_register。
2:再次调用admin.site的urls属性。
返回了一个元组,元组有三个元素,self.get_urls(),'admin',self.name。
第一个元素是一个函数返回的是一个列表,列表中是url,是循环admin.site中的_register(ruanzhiste),
中的注册类,生成url,放在列表中。为每个注册类生成一级URL,其次调用类的样式对象下的get_url_func(self)
函数,生成二级URL,同时为每一个增删改查URL创建别名,用于反向解析,每个url对应一个视图函数。
问题:怎样实现粒度精确到按钮的?
答:
用户登录成功之后,获取所有权限,并且保存在session中。
获取session中的信息,循环进行正则匹配,如果匹配成功,说明有权限。
匹配不成功则无权限。
在做这个认证的时候,因为很多的视图函数都要进行认证,所以我是用中间件来处理的。
匹配成功的时候,把code[权限1,权限2]保存在request里面,循环判断有没有相应的权限。
问题:权限管理总共几张表?
答:五个类,七张表。
用户表,角色表,权限表,权限组表,菜单表。
一个用户可以有多个角色,一个角色有多个权限。
一个"菜单"下面有多个"权限组"。
一个"菜单"下面有多个"权限"。
问题:订单分配如何实现的?
答:
根据对销售人员的以往业绩的分析制定每个销售人员的销售任务,综合多方面数据对销售人员进行权重的评定。
在工作分配时,通过权重进行排序,权重大的优先安排任务。
首先,获取权重表里面的所有销售人员并根据权重进行降序排序,根据权重和转换人数将销售的id依次循环放入列表中。
循环一次加一人,直销售人员的分配人数等于可以接收的转化人数,就跳过该销售人员。
若所有销售人员都达到目标值则将剩余销售任务重复之前的方案进行分配。
当循环完以后,调用reset()重置即可。
创建一个类,获取权重表中的所有销售ID,并根据权重降序排列,
根据权重和每个销售可以接收的数量,将销售ID依次循环,循环一次添加一条资源(客户),
直到销售的资源等于可以接收的数量,跳出循环。
问题:使用Redis的原因?
答:主要使用Redis来完成(原因:减轻内存压力,其次Redis可以有效的处理多进程问题,支持持久化)。
问题:销售管理的具体实现?
答:
客户来源主要有三项:运营部门根据权重进行分配的。
在公司的公共资源中抢单,刚进公司的销售没有权重,可以在公共资源中抢单,转化成功,分配权重。
自己寻找资源,自己录入。
销售人员得到任务后对应的客户状态改为开始接洽,记录起始时间,
客户状态状态从公司资源更改为销售人员的个人资源,其他人在订单转移前不可接触订单信息。
销售人员在跟进订单时,每一次与客户接洽都会在数据库中生成一条记录。
若客户在十五日内被销售人员转化成功,则将该客户的状态由待转化变为转化成功,
并在正式客户表中生成该客户的记录。
在销售人员的订单记录中将这笔订单的状态改为转化成功。
若当前与客户接洽的销售人员三天未跟进订单或十五天内未促成交易。
则相关客户信息会被移动到公司公共资源中,并且原先跟进订单的销售人员不可以选择继续跟进,
直至该客户再次被移入公司公共资源。
原销售人员的订单跟进记录中会显示有一单未能转化,并显示原因,重新接手该订单后即使转化成功,本条记录不会被覆盖。
其他销售可以在公司公共客户资源中进行抢单,抢单成功,将该用户添加到我的客户,默认显示正在跟进,
客户跟进记录表中再新增一条记录,获取当前时间为接单时间。进行跟进。
销售人员所接触过的每一个客户,不管什么来源,是否转换成功都会保存起来,为以后的权重划分和绩效考核为依据。
通过实现查看我的客户就可以一目了然的看到该销售人员的所有客户
在销售人员的订单记录中记录了销售人员从第一笔业务到最近一笔业务的所有信息。
可以为销售人员的业绩考核提供:接单数,转化率,订单未转化原因等数据。
问题:学校管理业如何实现的?
答:
对教学班级,校区,课程,学生等进行管理,主要用于班级的成员管理、课堂出勤情况记录以及学员成绩记录。
销售人员与客户接洽完毕,将客户转化为学员后,根据其选择的校区、课程、班级将其信息录入学员的数据库中。
初始化该学员的账号和密码,以便其进入教学管理系统查看自己的成绩以及出勤记录。
若该学员因某些原因中途退学或进入其他班级,则将其记录删除,新增一条数据。
课堂出勤情况记录:
每日上课前由班主任或当日讲师初始化当日的考勤信息,初始化时默认全部全员正常出勤。
如有学员存在:迟到、旷课、早退或请假等情况。可由班主任或当日讲师修改其考勤状况(支持批量修改)。
若有学生中途进入班级,进班前的考勤记录不予生成。若有学生中途离开教学班级,离班前的考勤记录不予删除。
上课教师和班主任对学生进行考勤管理,考勤直接影响这节课的成绩,考勤种类为已签到,迟到,缺勤,早退,请假.
初始化实现原理:
点击复选框选中要初始化的当天班级课程,点击action中学生初始化对学生完成初始化 默认全部出勤,
初始化管理:
如果有个别学生出现违规情况,在studyrecord中对该学生进行操作
实现原理:
教师和班主任在课程记录页面点击考勤管理,调转到该班级的学习记录页面,列出该班级的所有学生,
利用action对学生进行批量的
考勤管理
如果有个别学生出现违规情况,在studyrecord中对该学生进行操作。
实现原理:
教师和班主任在课程记录页面点击考勤管理,调转到该班级的学习记录页面,
列出该班级的所有学生,利用action对学生进行批量的。
学员成绩记录:
在班主任或当日讲进行初始化考勤信息操作时可以选择当日是否有作业(支持修改)。
若当日有作业则开放学生作业提交的功能,学生须在提交时间内提交,提交时间结束关闭该功能。
学生提交作业后在提交时间内允许撤销提交并重新提交。提交时间结束后,导师可以下载学员提交文件,
并进行打分评定。打分和评定评语结束后可立即上传至教学管理系统,
学生及学生家长可以通过教学管理系统进行查询(支持批量导入)。
若有学生中途进入班级,进班前的成绩记录不与不予生成。若有学生中途离开教学班级,离班前的成绩记录不予删除。
原理:
在课程记录页面点击成绩录入调到到成绩录入页面,
主要是根据当前趁机记录的id获取该节课所有的学生记录对象,发送到前端,
其中由于成绩打分要严格区分,所以成绩和评语有type动态生成ModelForm对象传到前端,
fields字段分别是score_学生记录id,homework_note_学生记录对象id,post传到后端
的时候在courseRecordConfig中。
问题:批量导入如何实现的?
答:基于xlrd实现的。
问题:项目中遇到问题,如何解决的?
答:
1:我们一开始做订单分配,使用的是__iter__方法,返回一个迭代器,每next一下取一个值。由于重启或求进程的时候会有问题。
所以我们用redis来实现自动分配,首先根据权重进行排序,权重大的优先安排任务,
分配任务的时候,按照权重排序从高到底循环分配,根据每个销售可以接收的数量进行分配。
如果那个销售可以接收的数量达到目标值,则跳过,将剩余的资源继续进行分配,
若全部销售达到目标值,还有资源,则在原来的基础上添加,也就是重复上面的操作。
2:popup本身很简单,就一段js代码,但是在页面跳转时,indow.open("","name")打开一个页面,第二个参数不能重名,
否则打开多个页面。执行回调函数。
2:在FK时,可以使用limit_choice_to(引用另外一张表中的数据时,对另外一张表添加条件) 条件可以是字典和Q对象,
不能通过传参的方式做,只能用两个类的字段,用related_name和model_name传递过去,
获取所有的反向关联字段,获取limit_choice_to字段,再进行查询。
3:路由系统动态生成url:
看了Admain源码实现的。参考Admin我知道了:用include进行路由分发,一个url对应一个元组,
元组中是一个列表,后面是app名称和namespace.
4:开发组件时,最开始看到admin源码不太理解,当和权限系统配合时,才领悟其中真谛。预留的钩子,是和权限搭配的。
开始想的只要用add_btn=True,show_searche=True等等就可以了,为什么还要用get_add_btn()
和get_show_search等等,后来开发组件进行权限管理时才明白,这都是预留给权限用的,
根据继承的先后顺序和登录用户所拥有的权限判断是否显示按钮等。
5:学生录入成绩时,为了区分是给那个学生录成绩,
并且在后台获取的时候能够区分这个成绩和评语是给那个学生的利用了type动态生成form还有动态生成field字段。
问题:慧聪管理系统总共几张表?都有什么表?都有哪些字段?
答:12张:员工信息表,部门表,课程表,校区表,班级表,
客户表,客户分配表,销售和权重表,缴费记录表,学生表,上课记录表,成绩考勤表。
字段具体记不清了
问题:有那些类,他们的继承关系?
问题:调查问卷?
答:
调查问卷是为了调查学生对学校设备和讲师讲课的满意程度,
以及他没有什么困难等,获取他们的意见以方便我们进行改进,
问卷只能有班主任和教务总监发起,并明确的规定班级和起始日期和结束时间,
并且只有本班学生才能填写,调查问卷的题型有三种,填写内容(建议),单选,打分。
学生打分后点击提交即可完成调查问卷,学生提交后,在首页可显示答卷的人数。
问题:会议室预定?
答:公司人员增多,空间有限,会议室需要被预定才可使用。
每个会议室从早上八点-晚上八点可以预定,一小时划分,
如果该会议室当前时间被预定了,如果预定的人是自己,再点击则取消,如果是别人预定的则不可以点击,
如果没有没预定点击则预定。
问题:权限管理如何实现的?
答:
权限的管理,是基于角色的访问控制。用户通过角色与权限进行关联。
一个用户拥有若干角色,每一个角色拥有若干权限,这样,就构造成“用户-角色-权限”的授权模型。
另外还有菜单和权限组,一个"菜单"下面有多个"权限组",一个"权限组"下面有多个"权限"。
流程:
当用户登录成功的时候设置session。判断有没有相应的权限,(查看,添加,删除,编辑)粒度到了按钮。
登录成功之后获取用户所有的权限信息并且保存到session中,
获取session中保存的信息,循环url进行正则匹配,如果匹配成功,
表示有权限;如果匹配不成功,说明没有权限。
整个认证的过程,有很多的视图,每一个都要进行认证,所以我们把认证写在了一个中间件里面。
当匹配成功的时候,把code保存在了request里面,方便以后判断有没有相应的权限。
并且让这些菜单分级显示默认展开的组内菜单,如果是非菜单,默认选中原菜单。
在设计表的时候设计了一个组内菜单(自关联),当是NULL的时候就说明是可以作为菜单的。
在初始化的时候,初始化权限信息,获取权限并放置到session中,
去session中获取到菜单相关信息,匹配url,生成菜单。在这里渲染页面的时候,
我们用了自定义的标签@register.includsion_tag("xxx.html")
(用includsion_tag自动会读取到这个文件并把返回值在页面上渲染) “在母版中:{%menu_html request%}
request是参数,记得要加上{% load rbac %}
问题:Thanos组件?
答:
Thanos我们是模仿Django.admin,实现对表的url分配管理,解决大量的增删改查操作。
主要实现了动态生成url,每个url对应一个视图函数。每个注册类对应四个url,p快速实现增删改查。
搜索模糊匹配,组合搜索,批量操作,popup跳转,用Query_Dict实现原搜索条件的保留,自定制的分页器组件
而且内置了多个钩子函数,用于对程序功能进行扩展。
问题:技术点?
1.:通过ChangeList封装好多数据,
change_listview列表页面代码太多,而却有好几个功能,
代码结构不清晰,修改代码麻烦,传到前端的东西过多,
封装后代码结构清晰,解耦性提高,便于维护,而且在changelist_view中只需要传入changelist对象即可。
2. 销售中公共资源:Q查询,3天 15天
销售接单后开始记录时间,如果三天 未跟进十五天未成单,
该客户进入公司的公共资源池,并且当前销售人员不能在公共资源池里面对该客户没有任何权限.
3. 使用yield实现(前端需要循环生成数据时),生成器函数,对数据进行加工处理__iter__和yield配合
组合搜索时用到先在ChangList中的get_combine_seach_filter()中返回row对象,
然后在FilterRow类中创建__iter__方法 yied生成每个组合搜索所对应的url
4. 获取Model类中的字段对应的对象,
Foo.get_field('xx')---------------------------------------------获取字段
model.UserInfo._meta.app_label----------------------------------获取当前app的名称
model.UserInfo._meta.model_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字段
5. 模糊搜索功能
用到Q查询
根据show_search_form判断是否显示模糊搜索框,search_fileds=[]代表可以以什么搜索
6. Type创建类
主要用于动态生成modelForm时用到,在调查问卷和成绩录入是用到
Type中第一个参数是类名,第二个是继承的类,第三个是字典,其中我们操作主要是在字典中进行操作
成绩录入
7. 自动派单
原来在内存中实现,问题:重启和多进程时,都有问题。后面用redis。
8. 使用 list_diplay配置
list_display = [函数名,字段名。。。。]
9. reverse反向生成URL
根据url中name字段的值利用reverse生成,
如果有namespace则需要在最前面加上,并用“:””分隔,url中有参数还需要传参args=[]
反向生成url
10. 母版
模板的继承
模板中 {%block body%}{%endblock%}
子版中最顶行{% extends '母版的路径' %}
{%block body%}{%endblock%}
11. ready方法定制起始文件
文件导入实现单例模式,stark.apps
12. 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)
14. importlib + getattr
在发送消息是用到,参考django源码可知,中间件也是采用这种方法
15. FilterOption,lambda表达式
目的是为了判断关联表的关联字段是不是主键还是其他字段
16. QueryDict原条件的保留
17. ModelForm可以自定义也可以使用satrkcofig中的type生成ModelForm。
18. 面向对象的 @property @classmethod
19. mark_safe在后台写的html传到前端能够正常显示,目的是为了防止xss攻击还有一种类似的方法,
直接在前端 {{aaa|safe}}
20. 组件中的装饰器,实现self.request = request
21. js自执行函数
(function(arg){
})('sf')
22. 多继承
python3中都是新式类,遵从广度优先
python2中既有经典类和新式类,经典类是指当前类和父类都没有继承obj类,
新式类是指当前类或其父类只要有继承了obj类就算新式类
经典类遵循深度优先
新式类遵循广度优先
23. 批量导入,xlrd
24. redis连接池
25. 工厂模式
工厂模式实际上包含了3中设计模式,简单工厂,工厂和抽象工厂,关键点如下:
简单工厂通过构造时传入的标识来生产产品,不同产品都在同一个工厂中生产,
这种判断会随着产品的增加而增加,给扩展和维护带来麻烦。
工厂模式无法解决产品族和产品等级结构的问题
抽象工厂模式中,一个工厂生产多个产品,它们是一个产品族,
不同的产品族的产品派生于不同的抽象产品(或产品接口)。
1. 使用了接口来表达抽象工厂或者抽象产品,那么可以用抽象类吗?有何区别?
从功能上说,完全可以,甚至可以用接口来定义行为,用抽象类来抽象属性。
抽象类更加偏向于属性的抽象,而用接口更加偏向行为的规范与统一。
使用接口有更好的可扩展性和可维护性,更加灵活实现松散耦合,所以编程原则中有一条是针对接口编程而不是针对类编程。
2. 到底何时应该用工厂模式
根据具体业务需求。不要认为简单工厂是用switch case就觉得一无是处,
也不要觉得抽象工厂比较高大上就到处套。我们使用设计模式是为了解决问题而不是炫技,
所以根据三种工厂模式的特质,以及对未来扩展的预期,来确定使用哪种工厂模式。
3.说说你在项目中工厂模式的应用
crm项目中发送消息是用到,因为我们要同时发短信,微信,钉钉,和邮件信息,
我们把他包装在一个baseMessage中,使用时直接调用baseMessage的send()即可