crm-01
1、搭建开发环境
* 配置eclipse工作区字符集:UTF-8
* 新建一个web项目(生成web.xml文件),我们这里使用的是Servlet3.1版本。
* 将原型拷贝到WebContent目录下。
* 部署项目,启动服务器,测试。
* 引入jar包,配置文件,工具类。
crm-02
1、设计数据库表,工具:PowerDesigner(PDM建模:物理数据模型)
tbl_dic_type
tbl_dic_value
数据库表设计的时候注意事项:
* 一般情况下为了开发方便,能用字符串类型尽量用字符串类型:Varchar/Char....(java:String)
* 在实际开发中为了保证程序的执行效率,一般是不建议添加外键约束的(FK一般不添加)。
添加了外键约束之后,会让检索效率降低。例如A表使用了B表的某个字段作为外键,在进行A
表数据检索的时候,不管是否取B表中的数据,总会关联去B表中进行检索,这样效率很低,为了
提高程序的执行效率,不添加外键约束,不添加外键约束,怎么保证数据的有效性和完整性呢?
在前端页面设计的时候,使用下拉列表方式。
执行sql脚本:
mysql> source D:\course\14-CRM\01-基础数据\crm-1.sql
在实际开发中,sql脚本文件可能很大,比如:64MB(sql脚本文件的大小)
记事本以及普通的文本编辑器,包括navicat都是无法打开的,这种文件需要使用source命令来执行脚本。
2、把字典类型和字典值模块的html修改为jsp,解决所有的404错误。
* html修改为jsp的顺序:
先打开html,添加<%@page contentType="text/html;charset=UTF-8"%>
再重命名。
base标签设置了base路径之后,当前页面自动从项目的根路径下开始查找资源。
* 添加base标签
标签属于html的语法,不属于JSP的语法。
标签用来设置当前页面的基础路径,当前页面中凡是不以“/”开始的路径都会自动添加base。
当前页面中以“/”开始的路径和base没有关系。
* 将所有jsp中的../全部替换成空白。
* 解决404
3、实现保存字典类型。
3.1、字典类型表单验证。(以下描述你们将在“详细设计文档”中看到。)
编码不能为空(前端验证)
编码只能由数字和字母组成(前端验证)
编码在数据库表当中是主键,要求具有唯一性(后台验证:ajax)
失去焦点则验证
验证消息显示在控件的下方
消息要求红色字体,12号。
获得焦点之后,自动清除错误信息,并且文本框中的内容不合法的时候,文本框的内容自动清空。
用户最终保存按钮点击之后,必须保证所有的表单项都是合法的方可提交。
注意:使用javascript代码怎么弹出modal,怎么隐藏modal?
// 显示modal窗口
$("#modalId").modal("show");
// 隐藏modal窗口
$("#modalId").modal("hide");
4、使用jquery发送ajax请求:
第一种方式:通用的方式,比较灵活
$.ajax({
type : "get/post", 请求的方式
url : "url", url
data : {}或者"", 请求发送的数据(当请求发送的数据是字符串的时候,需要满足什么格式?name=value&name=value&name=value)
beforeSend : function(){ ajax请求发送之前的回调函数
// return true; // 表示继续发送该ajax请求。
// return false; // 表示放弃发送该ajax请求。
if(表单在最后提交的时候,验证所有的表单项,当所有的表单项都合法的时候){
return true;
}
return false;
},
success : function(json){
// 响应正常结束之后的回调函数。
// 通常在这里解析json,刷新前端数据。
},
dataType : "", // 用来指定响应的数据类型
async : false, // 设置ajax请求为同步方式(默认是异步方式,异步是并发,同步是排队。)
cache : false, // 解决get请求的缓存问题。自动在请求url后面添加时间戳。保证每一次请求路径不一样,这样浏览器就不会走缓存了。
.....
});
第二种方式:专门发送post,基于$.ajax()实现的
$.post(url,data,function(json){},dataType);
第三种方式:专门发送get,基于$.ajax()实现的
$.get(url,data+时间戳,function(json){},dataType);
第四种方式:发送get ajax请求,并且一定返回json
$.getJSON(url,data,function(json){});
....
5、get请求和post请求如何选择?
post(邮寄,邮递,传送):
- 请求发送的数据当中有敏感信息
- 请求参数过大
- 请求发送的不是普通字符串,例如:文件、图片、视频等
- 主要目的是为了向服务器传送数据,而不是获取数据时采用post
get(获取):
- get请求的最终响应结果会被浏览器自动cache。(怎么避免get缓存,时间戳)
- get请求发送的数据在请求行的url后面,所以最终会显示到浏览器的地址栏上。
- get请求只能发送普通字符串。
- get请求不能提交大量的数据。
- get请求最终的目的是为了从服务器端获取资源,到浏览器上展示数据。
增 删 改一般都属于post。
查 属于get。
http协议中这样规定:
post请求适合于浏览器客户端向服务器传送数据。专门负责传送数据的。
get请求适合于从服务器端索取数据到浏览器上,并且显示数据。所以get请求应该是设计为支持缓存的。
6、包名:
com.wkcto.crm.settings.web.controller
公司域名倒序 + 项目名 + 模块名 + 功能名
一个模块对应一个Controller
一个业务对应一个service
一张表对应一个dao
crm-03
1、保存字典类型。
* 保存之前必须验证所有的表单项是合法的。
* 这里的保存只要提交form表单就行。可以不用ajax。
2、重点:什么时候ajax使用同步方式?
在表单验证的时候,其中某个或者某些表单项需要ajax验证,那么要求这些ajax请求必须是同步请求。
crm-04
1、添加字符集过滤器,保证post请求不出现乱码。
crm-05
1、保存字典值:
1.1、用户点击“创建”按钮,跳转到save.jsp页面,字典类型编码动态显示。
c:forEach
1.2、表单验证:
字典类型编码不能为空
字典值不能为空
在“同一个字典类型编码”下“字典值”具有唯一性。
排序号可以为空,但不为空的时候必须是正整数。
先把每一项的name和id全部加上。
添加三个span。
crm-06
1、封装一个响应json的工具类。
crm-07
1、对部门表进行设计。
2、将部门模块的html修改为jsp,解决404错误。
3、发送ajax post请求,保存部门。
注意:
重置表单之后,再弹出modal窗口。
crm-08
1、将用户维护模块相关的html修改为jsp,解决404错误。
2、弹出“保存用户”的modal窗口。
当用户点击“创建”按钮的时候,发送ajax get请求,取出所有的部门,解析json,拼接html。
var html = "";
$.each(array , function(i , n){
html += "";
});
$("#").html(html);
3、设计数据库表。
tbl_user
特殊字段:
部门编号(外键)
失效时间
允许访问的IP
锁定状态
登录密码(MD5)
创建人和修改人(登录账号)
4、用户点击保存按钮的时候,发送ajax post请求,提交用户信息,保存用户。
5、“自学”了一个bootstrap datetimepicker插件。(日历控件)
crm-09
1、实现用户登录功能:(使用ajax post方式,进行登录。)
* 将login.html修改为jsp,解决404错误。
* 登录页面加载完毕之后,用户名文本框自动获得焦点。
* 用户点击登录按钮登录,回车同样可以登录。
* 登录请求发送之前,必须保证用户名和密码不能为空。
crm-10
1、十天内免登录
* 用户登录的时候选择了“十天内免登录”,登录成功之后,关闭浏览器,在十天之内,使用该版本的浏览器打开crm项目,出现在
用户浏览器页面上的是“工作台”。
* 怎么实现十天内免登录?
使用Cookie机制。
登录成功之后,假设用户选择了十天内免登录,那么应该创建十天有效的Cookie,将用户的登录信息存储到Cookie当中,将
Cookie发送给浏览器,浏览器将Cookie保存在硬盘文件当中,只要不清除Cookie,只要还是当前版本的浏览器,下一次再
访问该web站点的时候,浏览器自动提交Cookie给服务器,服务器从Cookie中获取用户的登录信息,验证信息通过之后,直接
跳转到“工作台”。
* 回顾Cookie:
- Cookie只有在javaweb中有吗,其他语言的web开发中有Cookie吗?
有,并且Cookie不属于java,属于HTTP协议的一部分。只要是web项目,只要是B/S架构,一定离不开:cookie 和 session。
- Cookie和Session存在的目的是什么?
是为了补充http协议的不足,HTTP协议的不足是什么?
http协议是无连接的无状态的协议。
我们可以使用Cookie和Session来保存会话的状态。
其中Cookie可以将会话的状态保存在浏览器客户端上。
其中Session可以将会话的状态保存在服务器端上。
- Session实现机制中是依赖Cookie的:
服务器端的session列表当中的每一个session对象都有sessionId,而SessionId是存储在Cookie当中的。
sessionId最后存储在Cookie中,Cookie放在浏览器的缓存当中,只要不关闭浏览器,下一次访问会自动提交
带有SessionId的Cookie给服务器,服务器接收该sessionId用来查找对应的Session对象。
- HTTP为什么是无连接无状态协议?
这是为了降低服务器的压力。只有用户请求的过程中是连接的,响应结束之后,浏览器与服务器之间的连接就断开了。
- 由于HTTP协议是一种无连接无状态协议,所以关闭浏览器的时候,服务器是不知道的,那么Session对象也不能直接
给“客户”保留着,毕竟Session对象也会占用JVM的内存空间,所以在Servlet规范中,使用Session超时机制来销毁
Session对象。默认的配置是:30分钟,常见的MIS系统一般设置为120分钟,安全级别较高的系统,时间会很短,例如,
网银系统一般是用户在3分钟之内没有再次操作网银页面,网银系统会自动销毁Session对象。
- Cookie是怎么保存会话状态的,经典的案例有哪些?
* 购物车的实现:
没登录的情况下,向购物车添加商品,实际上是将商品的编号放到了Cookie当中,Cookie被保存在浏览器客户端
硬盘文件中,下一次访问该购物网站的时候,自动读取关联的Cookie,获取商品编号,初始化购物车。这种会话
状态的保存是不稳定的,当清除Cookie之后,会话状态丢失。
在登录的情况下呢:向购物车当中添加商品,实际上这个商品信息关联客户的信息,一并存储到数据库表当中。这种
会话状态的保留是稳定的。不会因为更换浏览器版本而丢失。
* 十天内免登录。
- Cookie禁用之后,一些web站点的功能就无法使用了。
- 当Cookie禁用之后,怎么实现session机制呢?
URL重写机制。
可以在URL后面添加:jsessionid..
url;jsessionid=32位长度的字符串?name=value&name=value&name=value....
但这种方式会大大提高编程复杂度,每一个URL后面都需要动态的添加jsessionid
所以99%的web站点都会提示你开启浏览器Cookie功能。
- 在servlet中Cookie的API?
* 创建Cookie
Cookie cookie = new Cookie(String name,String value);
* 设置有效时长
cookie.setMaxAge(秒);
秒 > 0 存储到硬盘文件
秒 = 0 删除该Cookie
秒 < 0 不被存储
不设置有效时长,默认是:存储到浏览器缓存当中,直到浏览器关闭之后销毁Cookie。
* 设置Cookie的关联路径
cookie.setPath("/crm");
以上代码的含义表示:以后浏览器只有发送/crm请求的时候,该Cookie就会被自动发送给服务器。
* 获取Cookie的name和value
String name = cookie.getName();
String value = cookie.getValue();
* 将Cookie发送到浏览器客户端
response.addCookie(cookie);
* 服务器获取浏览器客户端提交的Cookie
Cookie[] cookies = request.getCookies();
crm-11
1、常量类:
项目中多次出现:request.getSession().setAttribute("user", user);
其中"user"这个字符串多次编写,每一次程序员编写这个"user"字符串的时候都需要很谨慎的编写。
因为在编写过程中,当错误的时候,编译器也不会提示错误信息。
其实,我们开发有一个原则:错误越早发现越好,尽可能让所有的错误发生在编译阶段。
编译器能够检测出来的错误尽可能交给编译器来完成。
2、登录成功之后,在工作台右上角显示当前登录的用户。
workbench/index.html修改为jsp,然后在jsp中使用EL表达式。
${sessionScope.user.name}
${user.name}
3、登录拦截过滤器:
拦截什么?
*.jsp
*.do
什么不能拦截?
/login.jsp
/login.do
/welcome.do
用户已经登录则不需要拦截(登录的标志:session中有user)
crm-12
1、将市场活动相关html修改为jsp,解决404错误。
2、设计数据库表:市场活动模块相关的表。
tbl_activity
tbl_activity_remark
3、弹出创建市场活动的modal窗口。
- 所有者动态(动态的下拉列表)
* select * from tbl_user where loginAct != 'admin' (系统账号超级管理员admin一般是内置账号,前端显示的所有者不应该显示该账号。)
* 关于下拉列表定位的问题:用户要求,张三登录,则所有者默认选择张三。
想让下拉列表的某个选项选中,可以通过设置下拉列表的“value”即可。
设置下拉列表的value是zhangsan,则zhangsan会被自动选中。
- 开始日期和结束日期添加日历控件。
4、保存市场活动。
用户点击保存按钮,发送ajax post,保存市场活动信息。
crm-13
1、实现分页查询的后台功能。(先不实现前端的)
crm-14
1、市场活动列表页面加载完毕之后,显示第一页数据。
crm-15
1、给分页查询添加翻页功能:
翻页使用bootstrap pagination插件来完成。
2、用户点击查询按钮,分页查询。
3、翻页的时候条件是怎么处理的?
隐藏域 hidden
点击“查询”的时候,将四个条件放到隐藏域当中。
每一次分页查询的时候,条件都从隐藏域当中取。
每一次翻页的时候,再将隐藏域当中的条件更新到页面的文本框中。
4、分页查询面试官都会问哪些问题呢?
* 分页查询你是怎么实现的,说一下?
重点要说的是:
- mybatis的动态sql(where if)
- sql语句的limit
- 封装了一个分页查询专用的VO对象(PaginationVO)
- 最后捎带着说一下,前端使用了一个分页的组件:pagination
* 翻页的时候查询条件是如何处理的?
隐藏域。
crm-16
1、当前CRM项目当中有很多位置都需要分页查询,目前service类当中的Map集合用来存储展示的数据:
Map pageMap = new HashMap<>();
pageMap.put("total", activityDao.getTotalByCondition(conditionMap));
pageMap.put("dataList", activityDao.getDataListByCondition(conditionMap));
以上由于功能不止一个,建议使用一个专门的类进行封装,所有的分页查询功能都使用这个封装的类,
该类由于存储了前端展示的数据,所以这个类我们成为VO(View Object:视图对象)
VO 、 DAO等都属于JavaEE设计模式。
VO对象中的数据将来最终是负责向前端浏览器展示数据的。
DTO
PO
POJO
BO
.....
crm-17
1、复选框的全选和取消全选
$("#firstChk").click(function(){
$(":checkbox[name='id']").prop("checked" , this.checked);
});
$("#activityTbody").on("click" , ":checkbox[name='id']" , function(){
$("#firstChk").prop("checked" , $(":checkbox[name='id']").size() == $(":checkbox[name='id']:checked").size());
});
2、删除市场活动。
* 删除之前要提示用户是否确认删除。
* 一次可以删除多个市场活动。
crm-18
1、修改市场活动。
* 第一步:弹出修改市场活动的modal窗口。
* 第二步:用户点击修改按钮,发送ajax post请求。在哪一页修改还回到那一页。
crm-19
1、展示市场活动的明细。
detail.jsp
crm-20
1、备注列表先修改一下样式。
鼠标移动到div当中,备注的修改和删除按钮显示为红色。
2、市场活动的明细页面加载完毕之后,展示该市场活动的备注列表。
页面加载完毕之后发送ajax get请求,根据市场活动id获取备注列表。
crm-21
1、保存市场活动备注。
* 保存市场活动的时候备注信息不能为空。
* 保存成功之后,清除文本域中的“备注信息”。
crm-22
1、删除备注。
crm-23
1、修改备注。
* 弹出修改备注的modal窗口。
注意目的:设置id,id重复了添加前缀。
crm-24
1、关于市场活动的导入和导出。
* 这里所描述的导入和导出指的是excel。
导出:将数据库表当中的数据导出到excel文件当中。
导入:将excel文件中的数据导入到数据库表当中。
* 我们这里先来看一下导出市场活动。
在java开发中要实现excel的导出,可以使用java当中的一个开源组件:apache POI
* 在使用POI组件之前,我们最好要对excel文件结构有一定的认识。
一个excel文件的组成是怎样的呢?
excel文件:WorkBook 工作簿
一个WorkBook工作簿当中可以容纳多个:Sheet 表格
一个Sheet表格当中有多个行:Row 行
一个Row行上面有多个单元格:Cell 单元格
WorkBook --> Sheet --> Row --> Cell
apache POI对excel文件的操作主要使用的是以上的几个对象(因为java语言本身就是完全面向对象的。)
* 我们这里先实现excel的导出功能,导出excel:使用POI写excel文件。
我们这里采用网络搜索的方式,搜索相关的资料或者博客,找到简单的示例,先运行起来,然后在简单程序的基础之上
进一步学习,然后改造程序,最终达到效果。(这个过程不容易的。)
2、市场活动的导出:
提供两个操作:一个是导出选中,另一个是导出全部。
crm-25
1、导入市场活动。(先不使用异步的方式,使用传统方式导入:提交form表单的方式。)
第一步:文件上传。(apache commons fileupload)
第二步:读excel。(POI)
crm-26
1、导入市场活动。(采用异步ajax方式先完成文件上传,然后再导入数据。)
$.ajax({
...
contentType : false, // 设置encType为multipart/form-data
processData : false, // 设置以对象的形式提交数据(提交文件)
data : new FormData().append("myfile" , $("#activityFile")[0].files[0]),
...
});
crm-27
1、将剩下的所有页面修改为jsp,解决404错误。
2、将tbl_dic_type和tbl_dic_value数据通过执行sql脚本的方式初始化数据。
3、思考:系统当中有很多位置需要下拉列表,每一次需要下拉列表都要从数据库中查询吗?有没有什么好的方案?
下拉列表中的数据有这样的几个特点:
第一:数据量较小
第二:所有用户共享
第三:一般不会轻易的修改
当满足以上的三个条件的时候,可以考虑将其存储到application域当中。
application域可以看做是一个缓存:cache。
使用缓存机制,减少IO操作,是提高程序执行效率的重要手段。
在实际开发中,遇到优化瓶颈的时候,首先要想到缓存机制。
将字典值查询出来,存储到一个什么样数据结构当中,application域当中存储什么?
application.setAttribute("appellationList" , appellationList);
application.setAttribute("clueStateList" , clueStateList);
.....
写一个监听器,在服务器启动阶段,调用service返回一个Map集合,然后遍历Map集合,将数据存储到application域当中。
Map>
key value
----------------------------------------
"appellationList" appellationList
"clueStateList" clueStateList
....
4、将“线索、客户、联系人、交易”模块的所有表设计出来。
线索模块
tbl_clue 线索表(pk)
tbl_clue_remark 线索备注表(pk + fk[clueId])
tbl_clue_activity_relation 线索和市场活动关系表(pk + fk[clueId] + fk[activityId])
客户模块
tbl_customer 客户表(pk)
tbl_customer_remark 客户备注表(pk + fk[customerId])
联系人模块
tbl_contacts 联系人表(pk + fk[customerId])
tbl_contacts_remark 联系人备注表(pk + fk[contactsId])
tbl_contacts_activity_relation 联系人和市场活动关系表(pk + fk[contactsId] + fk[activityId])
交易模块
tbl_tran 交易表(pk + fk[customerId] + fk[activityId] + fk[contactsId])
tbl_tran_remark 交易备注表(pk + fk[tranId])
tbl_tran_history 交易历史表(pk + fk[tranId])
crm-28
1、弹出创建线索的modal窗口:
* 所有者动态(下拉列表定位)
* 线索来源和线索状态下拉列表从application域当中取出forEach
* 下次联系时间日历控件。 (设置控件的显示位置:pickerPosition : "top-right")
2、发送ajax post请求保存线索。
crm-29
1、查看线索的明细。
crm-30
1、展示该线索关联的市场活动列表。
前提是知道:线索id(clueId=77544dcfc4774e70a95ac1ba32912a92)
tbl_clue_activity_relation car
tbl_activity a
tbl_user u
select
a.name, a.startDate, a.endDate, u.name as owner, car.id as relationId
from
tbl_activity a
join
tbl_clue_activity_relation car
on
car.activityId = a.id
join
tbl_user u
on
a.owner = u.id
where
car.clueId = #{clueId}
crm-31
1、解除关联。
* 发送ajax post请求,提交关系id,删除数据库中的数据,前端删除一个tr。
crm-32
1、当前线索关联市场活动:
1.1、弹出关联市场活动的modal窗口:
* 清空查询条件
* 第一个复选框取消选中
* 清空tbody
1.2、根据市场活动名称查询市场活动。支持模糊查询!
回车键的时候查。(keyCode==13)
bootstrap当中的modal窗口只要接收到回车键,则modal窗口自动关闭。
JS中的事件会冒泡,作用到子对象上的事件,会继续传递给父对象,没有阻止会一直传递下去。
怎么防止js的事件冒泡?
方式一:event.stopPropagation();
$("#div1").mousedown(function(event){
event.stopPropagation();
});
方式二:return false;
$("#div1").mousedown(function(event){
return false;
});
这里防止业务上出现漏洞?已关联过的市场活动不能查出来。(sql语句子查询。)
1.3、删除市场活动的时候应该级联删除对应的备注信息。
crm-33
1、关联市场活动:
* 全选和取消全选。
* 用户点击关联按钮的时候:
至少关联一个。可以一次关联多个。
关联实际上就是往关系表插入数据:
tbl_clue_activity_relation
id clueId activityId
----------------------------------------------
UUID 前端提交 前端提交
crm-34
1、在detail.jsp页面上点击"转换"按钮跳转到convert.jsp页面:
两个jsp直接传送数据?
detail.jsp?name=value&name=value&name=value....
convert.jsp中怎么取数据{
${param.name}
${param.name}
${param.name}
${param.name}
${param.name}
${param.name}
${param.name}
${param.name}
${param.name}
}
重点:EL表达式当中有一个隐含对象,叫做:param
用来代替:
<%=request.getParameter("name")%>
2、分析:转换涉及到哪些表?
tbl_clue
tbl_clue_activity_relation
tbl_clue_remark
tbl_customer
tbl_customer_remark
tbl_contacts
tbl_contacts_activity_relation
tbl_contacts_remark
tbl_tran
tbl_tran_history
tbl_tran_remark
3、分析:转换的实现步骤(共11张表)?(线索id已知)-----> 目前我们先不考虑交易。
1、根据线索id查询线索信息。
2、从线索对象中提取客户信息,保存客户信息(判断该客户的信息是否已经存在:根据公司名称查询,精确匹配)。
3、从线索对象中提取联系人信息,保存联系人信息。(这里不考虑去除重复记录,直接保存)
4、将“线索和市场活动的关系”转换到“联系人和市场活动的关系”
5、将“线索备注”转换到“客户备注”、“联系人备注”
6、删除线索对应的备注
7、删除线索和市场活动的关系
8、删除线索
4、注意事务,必须同时成功或者同时失败,也就是说必须只能调用一次service方法。
crm-35
1、线索转换的时候用户选择了创建交易:
* 保存交易
* 保存交易历史
* 保存交易备注(线索备注有的话,可以转换成交易备注。)
crm-36
1、展示创建交易的页面:
* 预计成交日期和下次联系时间(日历控件)
* “阶段”动态显示
* “类型”动态显示
* “来源”动态显示
* “市场活动源”和“联系人名称”固定(不再实现动态效果)
* 所有者动态显示,并且定位下拉列表选项。
2、下拉列表定位:
第一种方式:页面加载完毕之后定位。
$(function(){
$("#owner").val("${user.id}");
});
第二种方式:EL表达式当中使用三目运算符。
${userObj.id eq user.id ? "selected" : ""}
注意:
第一:EL表达式是支持三目运算符的。
第二:eq是EL表达式当中的运算符,底层实际上调用了java语言中的equals方法。
另外:
${"user"}和${user}有什么区别?
${"user"} 表示把"user"当做一个普通的字符串直接打印输出到浏览器。
${user} 表示从四个域当中检索数据,找到数据之后输出到浏览器。
3、选择不同的阶段,生成不同的可能性。
* change事件。
* “阶段”对应的“可能性”,他们之间的对应关系应该在配置文件中体现出来:
这里选择属性配置文件。
Stage2Possibility.properties
Stage To Possibility:阶段对应的可能性。
log4j : logger for Java
dom4j : dom for Java
注意:属性资源文件中不能直接存储中文,高版本的eclipse自动将中文转换成unicode码。
那么低版本的eclipse怎么办?程序员可以手动转换:native2ascii.exe
* 在服务器启动阶段:SystemInitListener:
读取Stage2Possibility.properties,将读取到数据放到Map集合当中,将Map集合放到application域当中。
crm-37
1、客户名称支持自动补全。
bootstrap typeahead
crm-38
1、保存交易。
提交form表单。
crm-39
1、查看交易的明细:
五张表连接。
crm-40
1、查看交易的历史列表。
页面加载完毕之后发送ajax get请求,根据交易id获取交易历史列表。
crm-41
1、展示阶段图标。
* 修改页面:失败的图标都以“叉号”显示。
* 实现什么样的效果?
第一种情况:当前阶段处于“正常期”:
当前阶段的图标则:glyphicon glyphicon-map-marker (颜色:绿色)
当前阶段之前的图标则:glyphicon glyphicon-ok-circle(颜色:绿色)
当前阶段之后的图标则:glyphicon glyphicon-record(颜色:黑色)
失败的图标则:glyphicon glyphicon-remove-circle(颜色:黑色)
第二种情况:当前阶段处于“失败期”:
当前失败的阶段图标则:glyphicon glyphicon-remove-circle(颜色:红色)
其它失败的阶段图标则:glyphicon glyphicon-remove-circle(颜色:黑色)
其它正常的图标则:glyphicon glyphicon-record(颜色:黑色)
怎么判定当前阶段是失败期还是正常期?
主要依据是可能性是否为0
crm-42
1、点击图标,更新当前交易的阶段。(这个版本不实现图标的更新。)
* 提交什么?
tranId
stage
money
expectedDate
* 返回什么?
{"stage":"","possibility":"","editTime":"","editBy":""}
crm-43
1、更新图标。
窍门:使用下标i作为每一个图标的id。
crm-44
1、统计图表:
实现交易的统计。(使用百度的ECharts完成统计图表的展示。)
对什么进行统计呢?
对“每个阶段”的“交易数量”进行统计。
共100笔交易:
50笔成交
10笔因竞争丢失而关闭
20笔01资质审查
10笔02需求分析
.....
客户可能会按照月进行统计,也可能是按照季度进行统计,还可能按照年统计。
也可能统计所有数据(自公司成立以来!)
这个统计图表我们准备使用:漏斗图。
统计图表非常多:
柱状图
饼状图
漏斗图
曲线图
....
2、扫尾:
* 自己实现发送邮件功能。(javamail)
* 系统当中有很多位置都有“下次联系时间”:
当“下次联系时间”到了之后,服务器端应该自动推送消息给浏览器,提示业务员。
推送技术可以使用:pushlets
反向AJAX。
CRM项目中有没有实时性效果的技术呢?
有,下次联系时间到了之后,服务器自动推送信息,推送信息使用的是:pushlets
* CRM项目中还有权限管理模块:
权限管理模块使用的是:apache shiro框架实现的(shiro框架底层是基于RBAC:Role Based Access Control:基于角色的访问控制。)
面试官问:CRM中有权限吗?
有,当时我们好像使用的是apache shiro。但这块不是我负责的。
除了shiro之外还有其它的,例如spring框架下的子框架:spring security
* 面试官可能还会问,CRM项目中有没有“流程管理的功能”/“工作流”相关的内容?
有,我们使用的是Activiti框架实现的工作流。
jBPM5与Activiti5都是工作流框架:
jBPM5老
Activiti5新(使用这个比较多)
流程管理是一个非常通用独立的模块。例如:学生请假、报销差旅费....
3、面试之前一定要将CRM项目的核心业务记忆一下。
4、使用单元测试工具junit4进行单元测试。