当前位置: 首页 > news >正文

phpcms 手机网站后台九网互联怎么建设网站

phpcms 手机网站后台,九网互联怎么建设网站,上海企业服务云下载,网站规划名词解释思想 对于三层架构#xff0c;一个模块对应一个controller#xff0c;controller实际就是Servlet#xff1b;一张表对应一个domain类对应一个dao接口对应一个mapper文件#xff1b;service层没有严格规定#xff0c;如果两张表内容相近#xff0c;用一个service接口也可以…思想 对于三层架构一个模块对应一个controllercontroller实际就是Servlet一张表对应一个domain类对应一个dao接口对应一个mapper文件service层没有严格规定如果两张表内容相近用一个service接口也可以如ActicityService也可以用俩。 **web目录下有controller、filter、listener。这三个都是web顶层内容都可以根据需求访问任何业务层service。如在市场活动模态窗口中展示用户列表同时在业务层service也可能调多个dao层接口。只要知道那个模块发的请求那个模块的controller接收根据前端要的数据控制器调不同业务层去完成业务层根据要查询的数据去调不同表的dao层。**此外controller层对业务层变量命名用简写不会重复。业务层对dao接口要写全因为可能用到多张表即多个dao层 controller层尽可能少写代码即少处理业务接收的即使是个数组也不要在这里封装数据而是接收到啥就发给业务层啥让业务层去处理业务控制器只要结果前端要啥控制器问业务层要啥。 MVC思想只有控制器里能用request和response。不要把这俩传给业务层业务层只是处理业务。业务层需要的数据在控制器中就拿出来只传数据。 在前端写完ajax请求时就应该考虑需要什么数据前端需要什么数据后端发送什么数据并且确定数据的格式是JSON数组还是JSON还是JSON里又套了个JSON数组。同理后端controller也要考虑问service层要什么数据是java对象还是普通类型。这些都想明白了在往下走。 不是一个模块一个包结构而是按照大功能分包结构。如系统设置settings分个包结构workbench分个包结构。workbench下有市场活动、线索等模块这些模块在一个包下只不过他们的controller不同但都在一个包下。整个项目共用的等级高如utils、vo等。 后端数据可以封装成map/vo响应给前端具体选取依据就是看这个数据使用率高不高就用VO以后别的模块也能用到并且一般整个模块的VO类用泛型 在实际开发中有关“改”操作的代码直接复制“增”的代码再其基础上进行修改。因为大部分代码一致。 使用ajax请求数据以json格式响应前端处理json数据使用传统请求可以将数据保存在域对象中前端使用jstl、el表达式更方便操作数据。但是要注意jq中使用EL表达式必须带双引号。EL表达式的xxxScope可以忽略默认从页面域pageContext找、request、session、application。能用小的用小的。 对于动态拼接出来dom元素注意一般用主键id绑定保证唯一。用onclick绑定事件执行某个函数或直接发传统请求给后台。 实际开发中一般将不变的数据如池、数据字典保存在服务器缓存中当服务器启动时将这些数据保存在application域对象当中只要服务器正常启动就直接从application中取数据。这样的优点是不走数据库快。但是不是所有数据都适合放缓存中只有不变的数据以及池才适合放缓存中因为缓存中的数据只有服务器重启时会更新将经常变化的数据放进去会导致每次取得都是旧数据。 对于多对多三张表关系表俩外键。一般处理关系表中的业务时可以随便用关联的两张表的service层不过一般从那个模块发出请求用那个模块的service层。如tbl_clue_activity_relation。在通过clueId获取activityList时从线索模块发出就用ClueService。 单元测试junit组件junit是多线程一起测方法比main好用main中测试代码不好管理。junit一起测若模块之间的耦合出现问题也能测出来例如改了A模块B可以受影响那就一起测没问题就说明改动是可以的。而main中不行因为main只是一个线程不能一起测。断言提前预言结果如果是对的拿junit测试就是对的否则就错的语法Assert.assertEquals(flag, true)。junit是注解式开发注解Annotation可以看作代码。 请求与响应 请求的方式有两种ajax请求传统请求。使用局部刷必定用ajax请求如果刷整个页面通常发传统请求 发ajax请求可以发json串也可以自己拼个url以namevalue的方式发这种一般用在复选框即有多个相同的name时。如根据id批量删除。发传统请求也有多种方式如果不携带数据或少量数据id通常直接绑定一个单击事件该地址栏window.location.href“url”。如果数据多通常以form表单的形式发数据主要表单中需要发送的数据要有name。 响应的方式有三种如果前端发的是ajax请求后端必定响应json字符串response.getWriter().print(json)如果前端发的是传统请求后端用转发或重定向使用哪种判断如下 数据如果响应需要携带数据将数据保存在request域中以转发的方式响应给前端。不需要携带数据直接重定向。路径转发后的路径是当前路径xxx.do这样前端每刷一次就会过一次后台。而使用重定向后的路径通常是xxx.jsp这样每一次刷新不会过后台只是刷页面。具体选择应该看该页面有没有修改操作例如在页面修改完刷新直接过后台然后重新取数据前端铺数据保证是最新的数据。简单判断就是携带数据用转发否则一律重定向。 提示性信息可能性对于stage-possibility这种数量少的对应关系保存在application中而不是在数据库中。并且这种数据也是不会变的一般将对应关系写在properties文件中通过ResourceBundle去解析该文件将解析出的数据保存在map集合中。可以将该map集合直接保存在application域中也可以将该map集合通过jackson转化为json串保存在域中目的都是在项目其他模块中使用。两者各有优点以map集合形式保存在后端容易获取数据以json串前端容易操作通过EL就可以。注意可以将这种信息设置为一个类的扩展属性不在业务层与dao层操作只是在控制器中获取对应的值然后set就可以这样的好处是前端取值统一。但是扩展属性慎用一个domain类中不能超过3个否则会破坏实体类结构 MapString, String pMap new HashMap();//使用ResourceBundle工具类处理properties文件注意路径没有.propertiesResourceBundle bundle ResourceBundle.getBundle(Stage2Possibility);//获取properties文件中所有的keyEnumerationString stageList bundle.getKeys();while(stageList.hasMoreElements()){String stage stageList.nextElement();String possibility bundle.getString(stage);pMap.put(stage, possibility);}application.setAttribute(pMap, pMap);//在这里使用jackson将pMap转化为json串保存在application域中。ObjectMapper om new ObjectMapper();try {String possibilityMap om.writeValueAsString(pMap);//前端直接从该json串中取值就可以application.setAttribute(possibilityMap, possibilityMap);} catch (JsonProcessingException e) {e.printStackTrace(); }功能介绍 系统设置模块settings 用户模块:登录操作涉及到数据字典模块信息的查询 工作台核心业务workbench 市场活动模块activity 点击创建按钮,打开模态窗口添加操作展现市场活动信息列表(结合条件查询分页查询)全选/反选执行删除操作(可批量删除)点击修改按钮打开修改操作的模态窗口执行修改操作点击市场活动名称跳转到详细信息页,展现详细信息详细信息页加载完毕后展现备注信息列表备注添加,修改,删除 线索模块: clue整个CRM项目最重要的线索就是潜在客户 点击创建按钮打开添加操作模态窗口窗口中对于下拉框的处理有服务器缓存中的数据字典来填充)点击线索名称进入到详细信息页展现线索的详细信息在页面加载完毕后展现关联的市场活动列表解除关联操作关联市场活动操作点击转换按钮跳转到线索转换页面执行线索转换的操作(可同时添加交易) 交易模块: transaction 点击创建按钮,跳转到交易添加页点击交易名称进入到交易详细信息页展现详细信息在页面加载完毕后展现交易历史列表动态展现交易阶段内容及图标点击阶段图标更改交易阶段 统计图表 交易阶段统计图Echarts 登录模块 为了提高用户体验一般在页面加载完毕后将光标定位在账号输入框。使用$(“#id“).focus()用户点击登录按钮可以发送请求同时敲回车也可以发送。综上两条应该将login抽成一个函数发生以上任意事件调用login函数 $(document).keydown(function(event){if(event.keyCode 13){login();} })登录失败的具体信息也要返回给前端后端采用异常的方法将失败信息抛出。注意代理类对异常的捕捉会导致controller接收不到异常从而导致错误。throw e.getCause() 前端需要注意的是如果往这种单个的标签里赋值用val()。如果给 这种中间夹着的地方赋值用text()或html() 使用window.location.href可以修改网页地址栏达到跳转的目的。js里写EL表达式${sessionScope.user.name} sessionScope可忽略。jq中EL必须被括起来 登录页面要每次以顶级窗口形式出现 if(window.top ! window){window.top.location window.location; }整个项目中的资源需要防翻墙即加拦截器拦*.do与*.jsp以及使用过滤器滤所有.do将所有请求与响应的字符集进行统一设置。url写某一类文件只能*.xxx不能a/*.xxx* 关于令牌访问任何后台资源.do/.jsp都先过拦截器要令牌session除了与login相关的有令牌提供服务没有直接返回登录页面。用户登录成功创建一个session并保存在session域中之后用户每次请求都有令牌。session保存在后端默认30分钟。Cookie保存在前端浏览器缓存除了用户有10天免登录等操作。否则用户只要关闭浏览器Cookie释放导致用户再次访问需要重新登陆。但是只要用户不关闭浏览器就还是一次会话Cookie和Session都在。 Session做令牌的原理用户登录成功后端创建一个Session对象创建Session对象的同时会创建一个Cookie对象这个Cookie的name是“JSESSIONID”其value是“32位序列号全球唯一”。之后服务器将Cookie响应给前端并将Session对象与“32位序列号”绑定保存在session列表中。拦截器具体操作前端访问任何.do/.jsp资源经过拦截器拦截器要session这个底层其实是看前端有没有发来一个name为JSESSION的Cookie。如果发了然后会去那这个Cookie的value即32位的序列号去后端session列表中进行equals如果返回true代表找到了绑定的session对象即获取到了session放行。除了login相关操作不要令牌其它没令牌的全重定向到login.jsp。 关于乱码前端发请求后端发响应都会经过过滤器增强即设置字符编码但是一般只滤*.do。因为jsp文件一般用% page contentType“” %设置 市场活动模块 页面中的所有按钮、窗口都应该由我们来控制改它的id我们js来触发。 模态窗口 模态窗口的出现是KaTeX parse error: Expected EOF, got # at position 3: (“#̲xxxModal”).moda…(“#xxxModal”).modal(“hide”)清空模态窗口内容$(“#activityAddForm”)[0].reset(); 注意要清空模态窗口中内容用reset函数重置模态窗口里的表单并且reset函数是dom对象的函数要先将jq对象转换为dom对象 下拉列表默认选中一项KaTeX parse error: Expected EOF, got # at position 3: (#̲create-owner).…{user.id}); //${user.id}是EL表达式注意在jq中使用EL表达式必须是字符串 日历控件 填写日期一般情况下都是需要相关的日历控件的。这里使用的是bootstrap datetimepicker日期拾取器步骤导入日历控件库-引入库-直接cv日期控件代码在模态窗口弹出前-在需要的地方添加class 分页查询 分页查询有多个入口查询、创建、修改、删除、分页组件分页插件写好了调用方法、市场活动。因此要将列表展示抽成一个函数pageList()数据处理完毕后结合分页插件展示列表达到分页的效果。分页代码直接CV。 分页查询的步骤是点击入口、发送请求、获取列表、分页展示到相应位置。 分页需要用到分页插件pagination只要使用插件就要将插件资源导入项目在需要用到分页插件的页面在引入资源的路径注意顺序。如pagination分页插件是bootstrap下的因此必须放到bootstrap的引入资源下面。凡是使用到插件都要导入插件、引入路径 参数问题 前端发送pageNo、pageSize。接收 data:{“total”:total, “dataList”:[{市场活动1},{2}]} 其中total是分页插件、dataList拼活动列表需要 分页插件需要的两个参数totalPages总页数、pageSize每页展示记录数。totalPagesMath.ceil(data.total/pageSize) 后端sql语句分页需要两个参数skipCount、pageSize。skipCount(pageNo-1)*pageSize total是数据库查出来的总记录条数totalPages是拿到总记录条数算出来的skipCount是根据前端的参数在后端算出来给sql语句用的 分页存在问题 在查询框中输入内容不点击查询按钮点击分页按钮结果为查询框中的内容生效了在查询框中输入内容点击查询按钮再在查询框中输入内容不点击查询按钮点击分页按钮结果为新的查询框中的内容生效了 隐藏域解决上述问题 将查询条件放到隐藏域当中每一次翻页的时候条件都从隐藏域当中取。点击查询按钮的时候将查询框中的内容更新保存内容到隐藏域执行pageList的时候将隐藏域中的内容更新到查询框。 注意 使用分页插件前要把前端模型中原有的分页插件干掉引入一个div我们把自己引入的分页插件写div里 展示的列表都是在ajax执行成功后根据响应的数据拼出来的。分页是在这之后的操作。 复选框的全选和取消全选 使用jQuery实现。jQuery支持多个元素一块处理。很多地方是不需要使用each遍历的。为后期ajax动态生成的元素绑定事件必须用on。 //为全选的复选框绑定事件触发全选操作 $(#qx).click(function (){//input[namexz]代表选中input标签中所有name为xd的dom对象prop函数是设置或返回被选元素的属性和值。$(input[namexz]).prop(checked, this.checked);})//以下代码错误 /* $(input[namexz]).click(function (){alert(123); }) */ /*因为动态生成的元素是不能以普通绑定事件的形式来操作动态生成的元素要以on的形式来触发事件语法$(需要绑定元素的有效的外层元素).on(绑定事件,需要绑定的jquery对象,回调函数) */$(#activityBody).on(click, $(input[namexz]), function (){//比较原理xz框总数量与xz框checked的数量一致就选中qx$(#qx).prop(checked, $(input[namexz]).length $(input[namexz]:checked).length); })注意 **动态生成的元素是不能以普通绑定事件的形式来操作动态生成的元素要以on的形式来触发事件。**语法$(需要绑定元素的有效的外层元素).on(绑定事件,需要绑定的jquery对象,回调函数)“需要绑定元素的有效的外层元素”是指不是动态生成的元素。如果是动态生成的元素外层还是动态生成的那就再往外找。 CURD**最重要的是id后台查数据只根据id** 创建市场活动 点击“创建”按钮-走后台取数据-将数据平铺到模态窗口中-展示模态窗口点击保存-将“创建”模态窗口中的所有数据以及创建人的id发送给后台-控制器生成该市场活动的uuid并将数据封装发送-数据库作insert操作 删除市场活动 由于tbl_activity和tbl_activity_remark表存在父子关系因此要删除市场活动首先要删除掉它该市场活动下的所有市场活动备注。删除市场活动最重要的是发送市场活动的id但是一次可能删多条因此发送的是一个key都为id的串这个串不能用json拼因为json要求key不相同因此只能自己拼形式“idvalueidvalue”但是发送的还是ajax请求只不过data直接填这个串Param就可以。市场活动controller通过getParameterValues(“key”)获取ids数组将ids数组作为参数发送给service业务层删除市场活动前先进行验证。查询要删除的市场活动备注的总数count1获取删除市场活动备注的总数count2如果两者相等删除该市场活动。因此要查询和删除市场活动备注表就要调市场活动备注表的dao接口删除市场活动就调市场活动的dao接口。 修改市场活动 在实际开发中有关“改”操作的代码直接复制“增”的代码再其基础上进行修改。因为大部分代码一致。点击“修改”按钮-走后台取数据-将数据平铺到模态窗口中-展示模态窗口修改最终要的是获取待修改市场活动的id这个id保存在模态窗口的form表单的隐藏域当中提交表单时一并提交因为id不需要用户知道所以放隐藏域里。同时应注意将所有者的id也要发送给后台因为后端owner保存的是uuid而前端展示的是真实姓名。 注意问题 对于CURD而言id是很重要的数据必须发给后台这样才知道操作的那条记录。市场活动id的处理创建时后台创建一个uuid删除时id就是每条记录前checkbox的value修改时直接从隐藏域里获取uuid就可以。所有者id的处理创建时将当前用户的id赋值给下拉列表删除时所有者id不重要修改时获取下拉列表的value。总之只要保证下拉列表赋值的是所有者的uuid后面直接获取下拉列表的value发送就可以了。 //拼用户下拉列表 var html option/option; $.each(data.userList, function (i, n){html option valuen.idn.name/option; }) $(#create-owner).val(40f6cdea0bd34aceb77492a1656d9fb3); //给下拉列表赋值uuid展示的是name var id $.trim($(#edit-id).val()); //只要下拉列表的value是uuid直接获取就好数据库中也是uuid。不能是html()CURD操作后有关分页插件的pageList()函数参数的设置 新建市场活动后应该回到第一页维持每页展示的记录数删除市场活动后应该回到第一页维持每页展示的记录数修改市场活动后应该维持当前页维持每页展示的记录数搜索市场活动后应该回到第一页维持每页展示的记录数 修改后停留在当前页,修改后维持已经设置好的每页展示的记录数 pageList($(#activityPage).bs_pagination(getOption, currentPage),$(#activityPage).bs_pagination(getOption, rowsPerPage));市场活动备注 点击市场活动的名称跳转到detail.do市场活动名称是在pageList中动态拼出来的对于动态拼出来的对象想要绑定事件采用onclick的方式。注意这也是要走后台的因为detail.jsp页面需要该市场活动的信息。后台取到数据保存在request域然后转发到detail.jsp。在detail.jsp中用EL表达式赋值。 onclickwindow.location.href\workbench/activity/detail.do?idn.id\; 注意应该发送的传统请求原因如下 市场活动备注是一个新的页面是整个子页面的刷新不需要局部刷新该页面下有备注列表备注列表有CURD操作并且需要局部刷新。可以将备注列表与页面一同发ajax展示出来但是之后每对备注进行一次CURD就要刷新上面不变的部分。因此好的方式是页面用传统请求展示备注列表用ajax请求。当页面加载完毕自动调一次ajax把该市场活动的备注列表刷出来。 备注列表的展示写到一个函数里showRemarkList()如同pageList。因为展示该列表有多个入口如CURD操作也要刷新备注列表。并且如果备注里列表的外层div里有不能删的代码如前端动画等就不能用val的方式赋值。解决办法有两个方案1在div里再建一个div起个id对这个div操作将拼好的备注列表放里面方案2再前面动画的div里用append或者在最后的div里用before追加。 //remarkDiv是最后一个div在它之间追加。 $(#remarkDiv).before(html);注意备注列表是动态拼出来的而备注div中的图标也是动态拼出来的。对于动态拼出来的dom对象要注意 在遍历中动态生成的dom对象都采用直接触发的方式 onclick。通常在onclick中写一个回调函数注意函数中的参数必须在字符串中。 //最外层是先转移保证在字符串中然后再拼串外层拼串用的是因此拼串也用 onclickdeleteRemark(\n.id\);动态拼出来的dom对象一般用该“对象的id”作为其id。因为动态拼出来的不能写死。 //注意这里不用将id写在字符串里只有函数中的参数需要写在字符串中 div idn.id classremarkDiv styleheight: 60px;一个超链接的href“javascript:void(0);”表示将超链接禁用只能以触发事件的形式来操作。 CURDCURD步骤都差不多不过要注意的是对于备注列表CRUD完不能调showRemarkList()因为该函数是通过before追加上去的。因此创建时直接用before追加删除时删除当前备注修改修改当前备注操作当前备注时要通过id绑定事件而当前备注是通过动态拼出来的因此id不能写死一般用该条备注信息的id作为绑定的id即$(“#xxx”); ​ ​ ​ 线索模块 线索模块所用数据库表结构及关系 线索表tbl_clue、线索备注表tbl_clue_remark、线索市场活动关系表tbl_clue_activity_relation tbl_clue与tbl_clue_remark多是一对多关系tbl_clue与tbl_clue_activity是多对多关系 客户表tbl_customer、客户备注表tbl_customer_remark tbl_customer与tbl_customer_remark是一对多关系 联系人表tbl_contacts、联系人备注表tbl_contacts_remark、联系人市场活动关系表tbl_contacts_activity_relation tbl_contacts与tbl_contacts_remark是一对多tbl_contacts与tbl_activity是多对多 数据字典所用到的表及关系并将数据字典中数据导入 字典类型表tbl_dic_type、字典值表tbl_dic_value tbl_dic_type与tbl_dic_value是一对多关系 将“线索”、“客户”、“联系人”、“交易”模块的html修改为jsp解决404错误。包括内部超链接的404问题 搭建“线索”、“客户”、“联系人”、“交易”相关后端结构(domain,dao,service,controller) 数据字典是指将一些固定不变的数据作为数据字典。常将数据字典保存在服务器缓存中每次取数据不走数据库快。一般表单中有关选择的相关的数据下拉框单选框复选框都从数据字典中取。例如城市下拉列表中的城市职位下拉列表中的职位。对于表单元素中选择的数据一定都是要写活的来自数据字典。注意如何取数据字典数据字典保存在map中MapString, ListString代表DicType有几个类型就有几个List。在将map里的value放到application域中name就是DicType。 使用jstl语法将application域中的数据拿出来拼到下拉列表里。jstl和el表达式一起使用方便对域对象中的数据操作 select classform-control idcreate-statusoption/optionc:forEach items${clueStateList} varcoption value${c.value}${c.text}/option/c:forEach /select点击线索走后台获取线索列表拼到列表表格中这个是动态生成的要给每条线索的名称、复选框绑定该线索的id。与市场活动相同。 点击线索上的名称发传统请求走后台注意该传统请求是拼在动态生成的线索列表中的想要触发动态生成的元素只能用on的方式并将该条线索的id发给后台后台根据id查单条线索并将线索保存到request域中转发至详情页面detail.jsp在该页面用el表达式从request域中取出数据拼到对应位置。 onclickwindow.location.href\workbench/clue/detail.do?idn.id\;n.fullname/a/td;注意线索模块的CURD没做全以及备注没做只做了线索的创建、拼线索列表、点名称发传统请求、最终转发到详情页面。 线索模块的核心 线索模块核心页面是detail.jsp。对于CURD不练了 根据clueId获取市场活动列表通过线索id去线索市场活动关系表中查出所有该线索下的市场活动将其拼到市场活动列表中。线索与市场活动是多对多的关系这个功能主要练的是3张表联查即tbl_activity、tbl_clue_activity_relation、tbl_user。本质上还是CURD找到市场活动列表拼串。注意该功能是在detail.jsp页面加载时就走后台然后拼好展示。 解除关联点击”解除关联“超链接可以将该条市场活动与当前线索解除关联。解除关联的本质就是在tbl_clue_activity_relation删去对应的关系记录。而市场活动列表是动态拼出来的给该超链接绑定一个事件里面是unbund(n.id)注意动态生成的函数内的参数要写在字符串中并且该id是关系表的的id不是市场记录的id这样方便操作在查市场活动列表时car.id as id。解除关联可以直接remove该记录也可以重新刷一下市场活动列表因为该列表是在中 关联市场活动重点 点击“关联市场活动超链接”在模态窗口出现前过后台取出所有与该线索非关联的市场活动重要。并且在该模态窗口中可以根据市场活动的name模糊查询。查询时发两个参数clueId、name。关联时发两个参数clueId、avtivityId。 注意该模态窗口有根据市场活动name模糊查询敲回车触发。在模态窗口中敲回车会触发默认刷新页面事件使用“return fasle”解决。 select idgetActivityListByNameAndNotClueId resultTypecom.xd.workbench.domain.Activityselecta.id,u.name as owner,a.name,a.startDate,a.endDatefrom tbl_activity ajoin tbl_user uon a.owneru.id/*这里模糊查询不需要用动态sql如果没有条件就是都查。注意该子查询即要查询出的市场活动的id不被clueId所关联*/where a.name like % #{name} % and a.id not in(selectactivityIdfrom tbl_clue_activity_relationwhere clueId#{clueId})/select因为要批量关联所以前端发的数据是“cidxxxaidxxxaidxxx”后端获取到cid还有aids数组后还要生成根据aids数组的长度生成UUID注意要封装到ClueActivityRelation类最终发给dao层一个List类型的carList。sql语句如下 insert idbundinsert into tbl_clue_activity_relation(id,clueId,activityId)valuesforeach collectionlist itemcar separator,(#{car.id},#{car.clueId},#{car.activityId})/foreach /insert//service public boolean bund(String cid, String[] aids) {boolean flag true;ListClueActivityRelation carList new ArrayList();for(String aid : aids){ClueActivityRelation car new ClueActivityRelation();car.setId(UUIDUtil.getUUID());car.setClueId(cid);car.setActivityId(aid);carList.add(car);}int count clueActivityRelationdao.bund(carList);if(count ! aids.length){flag false;}return flag; }注意 封装成List的好处是只访问一次数据库后端foreach拼出多个插入的值。也可以不用List即没生成的car对象就走一次dao层不过不推荐。 controller层尽可能少写代码即少处理业务接收的即使是个aids数组也不要在这里封装数据而是接收到啥就发给业务层啥让业务层去处理业务控制器只要结果前端要啥控制器要啥。 //controller private void bund(HttpServletRequest request, HttpServletResponse response) {System.out.println(通过clueId和activityId创建关联);//cidxxxaidxxxaidxxxaidxxxString cid request.getParameter(cid);String[] aids request.getParameterValues(aid);ClueService cs (ClueService) ServiceFactory.getService(new ClueServiceImpl());boolean flag cs.bund(cid, aids);PrintJson.printJsonFlag(response, flag); }线索转换重点 线索转换的核心将线索转换真正客户即将线索中与公司相关的信息转化为客户与人相关的信息转化人联系人。 转换步骤 前端 在线索的详细页detail.jsp点击转换发送传统请求因为是子页面的刷新不需要过后台。传参的格式 传参数的方式转发不用过后台。要求这种方式传的信息不能涉及用户隐私字符长度不能很长传统get请求传参对字符数有限制。 //参数从request域中取request域保存了该条clue的所有信息 onclickwindow.location.hrefworkbench/clue/convert.jsp?id${c.id}fullname${c.fullname}appellation${c.appellation}company${c.company}owner${c.owner}转换页convert可以直接用EL表达式从参数中取值。也可以用jsp取值jsp本质就是servlet有9大内置对象。 //EL表达式取数据使用该方式 ${param.id} ${param.fullname} ${param.appellation} ${param.company}//JSP中取 %var id request.getParameter(id); % %id% 注意 EL表达式从域对象中取值可以省略前面的xxxScope默认从小开始找。而从参数中取值不能省略格式${param.参数名}jsp九大内置对象的考点 列写9大内置对象pageContext、request、session、application、page、response、out、exception、configPageContext的作用当成普通页面域对象用、可以随时随处变成其它域对象用、PageContext域对象可以取得另外8个内置对象 转换页面convert可以为用户创建交易也可以不创建。但是无论怎样都需要过后台因为线索转换要涉及多张表。因此要根据用户时候选中创建交易复选框去发不同的请求。 给“创建交易”的表单的“日期”添加日历插件“阶段”从数据字典中用“jstlel”取值 % taglib urihttp://java.sun.com/jsp/jstl/core prefixc% select idstage classform-control namestageoption/optionc:forEach items${stageList} varsoption value${s.value}${s.text}/option/c:forEach /select“市场活动源”的放大镜绑事件发ajax请求将市场活动名字作为参数后端根据name进行模糊查询将查询到的市场活动相应给前端前端将结果拼到市场活动列表中。这个展示列表应该抽出一个函数即在点击放大镜是调该函数在搜索框输入内容敲回车后也调该函数。每一条市场活动前面的单选框绑定了该市场活动的id。点击保存按钮将市场活动名称保存在“市场活动源”的文本框中将市场活动id放到“交易表单”的隐藏域中。 给“转换”按钮绑定事件。这里需要考虑有没有创建交易如果没有创建交易只用发个传统请求把clueId发过去就可以如果创建了交易就要将交易信息发过去这里再考虑“交易”表单中的信息虽然不涉及隐私长度不长但是将来可能扩展将这个交易信息都填写。因此如果创建了交易就不能发get请求就必须发post请求而想要用传统请求发post请求只有一种方式表单。这样的好处是表单是现成的提交表单直接用submit就可以。 $(#convertBtn).click(function (){//prop函数可以获取复选框的选中状态也可以为复选框设置选中状态if($(#isCreateTransaction).prop(checked)){//创建交易提交form表单$(#tranForm).submit();}else{//没有创建交易直接将clueId作为参数发给后台window.location.href workbench/clue/convert.do?clueId${param.id};}})注意由于无论是否创建了交易走的都是“workbench/clue/convert.do”这个请求路径只不过后面的参数不同。但是要让后台知道是否创建了交易需要在form表单中给一个标识不能用“有没有值”去判断因为交易中有些信息可能是没有填写。 !--以表单的方式提交这里要想到以后该表单可能需要扩展比如要把一个交易的完整内容都展示在表单里这时候传统get请求就不行了因为对参数长度都要求。这样就使用form表单提交数据这样的好处是1、方便只用设置name提交表单就把参数都提交了。2、传统请求中只有form表单可以设置请求方式为post对长度不限制。workbench/clue/convert.do?clueIdxxxmoneyxxx...-- form actionworkbench/clue/convert.do methodpost idtranForm!--标识--input typehidden nameflag valuea/input typehidden nameclueId value${param.id}/div classform-group stylewidth: 400px; position: relative; left: 20px;label foramountOfMoney金额/labelinput typetext classform-control idamountOfMoney namemoney ... /form后端 获取到线索id通过线索id获取线索对象线索对象当中封装了线索的信息 通过线索对象提取客户信息当该客户不存在的时候新建客户根据公司的名称精确匹配判断该客户是否存在因为该客户之前可能存在 通过线索对象提取联系人信息保存联系人 线索备注转换到客户备注以及联系人备注 “线索和市场活动”的关系转换到“联系人和市场活动”的关系 如果有创建交易需求判断tran是否为null不为null创建交易创建一条交易 如果创建了交易则创建一条该交易下的交易历史 删除线索备注 删除线索和市场活动的关系 删除线索 注意代码很简单但是要对业务逻辑清晰即线索转换都需要创建那些记录删除线索要先删除那些信息。 ​ 交易模块 处理交易添加页 搭建后台结构TranController、TranService、TranServiceImpl 点击创建跳转到添加页。注意发送的是一个传统请求到add.do过后台取用户列表然后讲用户列表保存在request域转发到添加页save.jsp 在save.jsp页面加载时使用jstl、el将“用户列表”、“阶段”、“类型”、“来源”下拉列表拼好。其中“用户列表”是从request域取其它是数据字典中的从application中取将“预计成交日期”、“下次联系时间”添加日期控件“市场活动源”与“联系人名称”可以过后台从tbl_activity与tbl_contacts中取然后动态拼到下拉列表这里写死。线索模块练过。 注意用户列表是通过转发发给前端的前端通过jstl和el取默认选择当前用户用el表达式的三目运算符。以往是点在模态窗口打开前过后台取用户列表然后拼下拉列表再将下拉列表的默认值设置为当前用户的id。 c:forEach items${userList} varu!--注意EL表达式中${selected}的意思就是把字符串selected输出到浏览器如果三目运算符为ture就代表option value${u.id} selected${u.name}/option 即符合条件选中该条--option value${u.id} ${user.id eq u.id ? selected : }${u.name}/option /c:forEach“客户名称”支持自动补全使用bootstrap下的插件。使用步骤导包、引入、cv代码。注意该插件只要绑定了对象在框里输入就会发请求不过有延迟。延迟建议1500慢了用户体验差快了没必要体验也差。发getCustomerName.do就是根据输入的name模糊匹配查所有客户名称类型List $(#create-customerName).typeahead({source: function (query, process) {$.get(workbench/transaction/getCustomerName.do,{ name : query },function (data) {//alert(data);// data [{客户名称1},{客户名称2}]process(data);},json);},delay: 1500 });注意 只要发ajax请求后端响应的数据一定是json格式数据。前端不一定非要发json数据。例如之前的根据id删多条市场活动因为json中key不能相同前端发传统请求后端不能发json数据一般将数据保存在request域中通过转发的方式响应给前端前端通过jstlel处理 “阶段”下拉列表选取阶段自动生成“可能性”。 阶段和可能性是一种一一对应的关系一个阶段对应一个可能性因此以key-value键值对的形式保存。阶段为key通过选中的阶段触发可能性value。对于这种数据数据量不是很大存在一种键值对的对应关系。将这样的数据保存在properties配置文件中而不是数据库中。 #注意properties文件中最好不出现中文因为有的ide不支持一般将中文用jdk的native2ascii.exe转换为ASCII码保存2表示To 01\u8D44\u8D28\u5BA1\u67E510 02\u9700\u6C42\u5206\u679025 ...stage2Possibility.properties这个文件表示的是阶段和键值对之间的对应关系我们通过stage以及对应关系来取得可能性这个值这种需求在交易模块中需要大量的使用到。因此我们就需要将该文件解析在服务器缓存中application.setAttribute(stage2Possibility.properties文件内容)。在系统初始化监听器中之前监听application对象的创建并将数据字典保存在application域中。下来再处理stage2Possibility.properties文件将该文件的kv关系解析出来保存在map集合中再通过jackson将其转为json串把这个json串保存在application域中。 MapString, String pMap new HashMap();//使用ResourceBundle工具类处理properties文件注意路径没有.propertiesResourceBundle bundle ResourceBundle.getBundle(Stage2Possibility);//获取properties文件中所有的keyEnumerationString stageList bundle.getKeys();while(stageList.hasMoreElements()){String stage stageList.nextElement();String possibility bundle.getString(stage);pMap.put(stage, possibility);}//在这里使用jackson将pMap转化为json串保存在application域中。ObjectMapper om new ObjectMapper();try {String possibilityMap om.writeValueAsString(pMap);//前端直接从该json串中取值就可以application.setAttribute(possibilityMap, possibilityMap);} catch (JsonProcessingException e) {e.printStackTrace(); }前端直接通过EL表达式从applicationScope中获取possibilityMap这个json{“01资质审查”:10,“02需求分析”:25…}直接根据stage获取对应的possibility。注意json字符串就是以 key-value 键值对的形式保存数据json中根据key获取value有两种方式 var value json.key;var value json[key]; 一般通过第一种方式获取value但是如果key是可变的变量例如stage就是在下拉列表中选取不同的就要通过json[key]的方式取值 //给阶段下拉列表绑定变化事件当下拉列表变化自动给可能性拦添加数据。 $(#create-stage).change(function (){//取得阶段var stage $(#create-stage).val();//从application域中取得possibilityMap的json串{01资质审查:10,02需求分析:25...}var json ${possibilityMap};//根据key从json中取得valuevar possibility json[stage];//为可能性的文本框赋值$(#create-possibility).val(possibility); })点击保存以form表单形式发送传统post请求到save.do后台进行数据保存。注意 controller表单中提交的是客户名称customerName因此在控制器中取到数据后先不要对customerId赋值将交易对象t和customerName作为参数发给serivce。此外业务层有大量的添加操作如客户的创建、交易的创建、交易历史的创建。如果业务层涉及到大量的创建还需要在控制器发给业务层createBy创建一定要给createBy与createTime赋值。但是这里createBy在交易对象t中不需要额外传直接t.getCreateBy()。控制器从业务层获取flag为true代表保存成功。这时候使用重定向到index.jsp。原因如下 数据save不需要给前端响应数据保存成功直接跳转index.jsp页面就可以。不给前端传数据就用重定向。路径重定向后路径为index.jsp如果是转发就是save.do这样每刷一次就会过后端添加一次。 service业务层首先要根据customerName精确查询客户如果没有查到就要新建一个Customer对象并完成customerDao.save(cus)有了用户对象后将其id保存在交易对象t中即t.setCustomerId(cus.getId()之后保存交易tranDao.save(t)每创建一条交易就要对应生成一条交易历史因此要创建交易历史对象从交易中那值然后tranHistoryDao.save(th)。以上都成功返回控制器flagtrue。daodao层作对应的save操作。 交易模块详细信息页处理 点击交易列表的名称以url的方式发送传统请求到detail.do同时将该条线索的id发送给后台。后端根据id查单条然后将交易对象t保存在request域中以转发的方式响应给前端detail.jsp。注意 发传统请求是因为这个页面刷新了直接跳转到了detail.jsp并且要发id 后端的sql语句需要注意因为tran中的属性是owner、customerId、activityId、contactsId而页面需要展示的是name因此sql中要五表联查。使用name as 对应的id。同时需要注意创建交易时activityId、contactsId是非必填项因此不能用内连接要用外连接否则如果没有这俩信息会导致tran也查不出来。 select iddetail resultTypecom.xd.workbench.domain.Transelecttran.id,user.name as owner,tran.money,tran.name,tran.expectedDate,cus.name as customerId,tran.stage,tran.type,tran.source,act.name as activityId,con.fullname as contactsId,tran.createBy,tran.createTime,tran.editBy,tran.editTime,tran.description,tran.contactSummary,tran.nextContactTimefrom tbl_tran tranjoin tbl_user useron tran.owneruser.idjoin tbl_customer cuson tran.customerIdcus.idleft join tbl_activity acton tran.activityIdact.idleft join tbl_contacts conon tran.contactsIdcon.idwhere tran.id#{id} /select这里后端响应数据是以转发的方式原因如下 数据使用转发因为是传统请求因此将数据保存在request域中。request域中有数据用转发路径此外转发后的路径还是该路径即detail.do这样前端在详细页进行了修改只要刷新就会调detail.do过后台。 detail.jsp中用EL表达式从request域中取数据将数据铺到页面上。 关于可能性的处理有多种处理方式 可能性没有保存在数据库中它以json串的形式保存在了application域中这里要根据该条交易的阶段stage将对应的可能性显示在前端。 div stylewidth: 300px;position: relative; left: 450px; top: -40px; color: gray;阶段/div div stylewidth: 300px;position: relative; left: 650px; top: -60px; idstageb${t.stage}/b/divdiv stylewidth: 300px;position: relative; left: 450px; top: -40px; color: gray;可能性/divdiv stylewidth: 300px;position: relative; left: 650px; top: -60px; idpossibilityb%--后台将stage-possibility的对应关系转化成了json串保存在了application域中这里先获取json串由于stage是动态的所以从json中取数据以json[stage]的方式然后将取到的数据保存在对应的框中。这样的优点是jsp文件中没有出现java代码不需要写java脚本。--%script typetext/javascriptvar stage $(#stage).text();var json ${possibilityMap};var possibility json[stage];$(#possibility).text(possibility);/script /b/div在application创建时将对应关系的map集合保存在application域中控制器从域中取出数据与当前要响应的交易对象t的stage属性进行equals获取对应的possibility。可以将它直接保存在request域中响应给前端以 p o s s i b i l i t y 获取数据。也可以给交易类添加一个扩展属性 p o s s i b i l i t y 将该值赋值给扩展属性然后前端通过 {possibility}获取数据。也可以给交易类添加一个扩展属性possibility将该值赋值给扩展属性然后前端通过 possibility获取数据。也可以给交易类添加一个扩展属性possibility将该值赋值给扩展属性然后前端通过{t.possibility}获取这样的优点是前端统一都是${t.possibility}获取数据。但是要注意扩展实体类的属性一定要少否则会影响到实体类的结构。 TranService ts (TranService) ServiceFactory.getService(new TranServiceImpl());Tran t ts.detail(id);ServletContext application request.getServletContext();MapString, String pMap (MapString, String) application.getAttribute(pMap);SetString sets pMap.keySet();for(String stage : sets){if(stage.equals(t.getStage())){String possibility pMap.get(stage);request.setAttribute(possibility, possibility);}}展示交易阶段历史列表在detail.jsp页面加载时发送ajax请求走后台获取历史列表通过tranId去tbl_tran_history中查交易历史列表按照stage降序查。由于交易历史列表中也有“可能性”信息这里采用给TranHistory类添加扩展属性possibility的方式要在控制器中从application域中获取pMap集合然后遍历历史列表取stage通过stage去pMap中获取对应的possibility将其赋值给交易历史th对象。然后前端就通过${n.possibility}获取与其它属性一致。 ListTranHistory tranHistoryList ts.getTranHistoryList(tranId);MapString, String pMap (MapString, String)request.getServletContext().getAttribute(pMap); for(TranHistory th : tranHistoryList){String possibility pMap.get(th.getStage());th.setPossibility(possibility); }PrintJson.printJsonObj(response, tranHistoryList);动态展现交易阶段内容及图标 先对图标进行逻辑设计使其动态化。这里使用的是java脚本因为用js对图标的样式支持不是很好每次先从当前页面的request域中获取到交易t对象然后获取其stage在从application域中获取到pMap集合根据stage获取当前阶段的可能性。然后就是逻辑判断给每个图标的div块绑定id和事件当点击图标会调响应的函数changeStage()发送参数stage与ii代表当前阶段的下标。在函数内发送ajax请求走后台更改数据库tbl_tran表中的stage字段并且还要创建一条交易历史记录。将结果t响应给前端前端从t中取值将修改后的stage、possibility、editBy、editTime修改并且修改阶段图标调changeIcon()函数参数也是stage与i。在changeIcon()函数中也是先获取当前阶段的可能性根据可能性去刷新图标。 Echarts统计图 Echarts统计图是由百度研发出来的现在已经被apache运维echarts是现在最好的统计图绘制工具可以上官网上copy模板然后该响应的代码就可以需要注意的是echarts统计图需要的数据是一个json数据因此要发ajax请求过后台拼串并且除了dataList之外还需要total总数与分页工具差不多一般用统计图展示数据对客服更友好人们跟喜欢看图。因此一个项目中通常有多个统计图所以后端数据一般封装到一个VO类中 重点 条件查询和分页查询的市场活动列表 select idgetActivityListByCondition resultTypecom.xd.workbench.domain.Activityselecta.id,a.name,u.name as owner,a.startDate,a.endDatefrom tbl_activity a join tbl_user u on a.owneru.idwhereif testname!null and name!a.name like % #{name} %/ifif testowner!null and owner!and u.name like % #{owner} %/ifif teststartDate!null and startDate!and a.startDate gt; #{startDate}/ifif testendDate!null and endDate!and a.endDate lt; #{endDate}/if/whereorder by a.createTime desclimit #{skipCount},#{pageSize}/select重点关联市场活动查询出与当前线索未关联的市场活动
http://www.zqtcl.cn/news/799404/

相关文章:

  • 河北网站seo外包网站嵌入百度地图
  • 公司怎么开网站WordPress有哪些工具
  • 一流专业建设网站原平新闻头条最新消息
  • 网站开发文档模板 开源北京保障房建设项目网站
  • 营销型网站分类网站关键词如何快速上首页
  • 帝国和WordPress比较wordpress文章页标题优化
  • 宁晋网站建设温岭新站seo
  • 大学科研项目做网站成都免费建站模板
  • 兰州网站开发企业在微信公众号发布wordpress
  • 网站信息化建设总体情况网站建设介绍ppt模板下载
  • 广州 建网站asp.net.网站开发
  • 装修网站模板国家正规现货交易平台
  • 福州高端网站制作网站建设项目单子来源
  • 网站制作的行业广州网站推广方案
  • 网站主域名建设通怎么样
  • 网站是如何建立的广告设计与制作工作内容
  • 网站优化课程培训公司取名生成器免费
  • 如何设立网站做外国网站买域名
  • 惠州网站建设公司排名聊城专业网站设计公司
  • 网站建设龙岗电子商务有限公司官网
  • 分栏型网站服装设计网站模板
  • 建设网站备案与不备案区别企业网站怎么做排名
  • php mysql的网站开发html网站制作答辩ppt
  • 网站制作有名 乐云践新专家网页制作公司需要什么资质
  • 织梦怎么用框架实现在浏览器的地址栏只显示网站的域名而不显示出文件名电脑网站模板
  • 北京网络营销网站品牌营销增长公司哪家好
  • 网站反链有好处吗稷山网站建设
  • 廊坊网站群发关键词怎么在百度上推广自己的公司信息
  • 网站开发 沈阳免费模板建站
  • 商业性质网站建设步骤wordpress电影网盘