收货地址列表展示 1.收货地址列表展示-持久层 1.1数据库数据的查询操作:
1 select * from t_address where uid= ? order by is_default DESC ,created_time DESC
1.2 接口和抽象方法
1 2 3 4 5 6 List<Address> findByUid (Integer uid) ;
1.3在xml文件中添加对应的sql语句映射。
1 2 3 4 5 6 <select id ="findByUid" resultMap ="AddressEntityMap" > select * from stores.t_address where uid=? order by is_default DESC, created_time DESC </select >
1.4单元测试
1 2 3 4 5 @Test public void findByUid () { List<Address> list = addressMapper.findByUid(4 ); System.out.println(list); }
2.收货地址列表展示-业务层 1.不用抛出相关的异常,不需要进行异常的设计。
2.设计业务层的接口IAddressService
和抽象方法
1 List<Address> getByUid (Integer uid) ;
3.需要在实现类中实现此方法的逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Override public List<Address> getByUid (Integer uid) { List<Address> addressList = addressMapper.findByUid(uid); for (Address address : addressList) { address.setAid(null ); address.setUid(null ); address.setProvinceCode(null ); address.setCityCode(null ); address.setAreaCode(null ); address.setTel(null ); address.setCreatedTime(null ); address.setCreatedUser(null ); address.setModifiedTime(null ); address.setModifiedUser(null ); } return addressList; }
4.单元测试
省略
3.收货地址列表展示-控制层 1.请求设计
1 2 3 4 /addresses HttpSession session GET JsonResult<List<Address>>
2.请求方法的编写
1 2 3 4 5 6 @RequestMapping({"/", ""}) public JsonResult<List<Address>> getByUid(HttpSession session){ Integer uid = getuidFromSession(session); List<Address> data = addressService.getByUid(uid); return new JsonResult<>(OK,data); }
3.先登录,在访问请求地址进行数据的测试。
http://localhost:8080/addresses
4.收货地址列表展示-前端页面 在address.html页面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 <script type="text/javascript" > $(document ).ready(function ( ) { showAddressList(); }); function showAddressList ( ) { $.ajax({ url :"/addresses/" , type :"GET" , dataType :"JSON" , success :function (json ) { if (json.state==200 ){ let list=json.data; for (let i=0 ;i<list.length;i++){ let tr='<tr>\n' + '<td>#{tag}</td>\n' + '<td>#{name}</td>\n' + '<td>#{address}</td>\n' + '<td>#{phone}</td>\n' + '<td><a class="btn btn-xs btn-info"><span className="fa fa-edit"></span> 修改</a></td>\n' + '<td><a class="btn btn-xs add-del btn-info"><span\n' + 'className="fa fa-trash-o"></span> 删除</a></td>\n' + '<td><a class="btn btn-xs add-def btn-default">设为默认</a></td>\n' + '</tr>' tr = tr.replace(/#{tag}/g ,list[i].tag) tr = tr.replace(/#{name}/g ,list[i].name) tr = tr.replace("#{address}" ,list[i].address) tr = tr.replace("#{phone}" ,list[i].phone) $("#address-list" ).append(tr); } $(".add-def:eq(0)" ).hide(); }else { alert("用户收货地址数据加载失败" ); } } }); } </script>
设置默认收货地址 1.持久层 1.1 SQL语句的规划 1.检测当前用户向设置为默认收货地址的这条数据是否存在。
1 select * from t_address where aid= ?
2.在修改用户的收货默认地址之前,先将所有的收货地址设置为非默认(1 默认,0 非默认)。
1 update t_address set is_default= 0 where uid= ?
3.将用户当前选中的这条记录设置为默认收货地址。
1 update t_address set is_default= 1 ,modified_user= ?,modified_time= ? where aid= ?
1.2 设计抽象方法 在AddressMapper接口中进行定义和声明。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Address findByAid (Integer aid) ;Integer updateNonDefault (Integer uid) ;Integer updateDefaultByAid (Integer aid, String modifiedUser, Date modifiedTime) ;
1.3 配置SQL映射 AddressMapper.xml文件中进行配置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <select id ="findByAid" resultMap ="AddressEntityMap" > select * from stores.t_address where aid = #{aid} </select > <update id ="updateNonDefault" > update stores.t_address set is_default=0 where uid = #{uid} </update > <update id ="updateDefaultByAid" > update stores.t_address set is_default=1, modified_user=#{modifiedUser}, modified_time=#{modifiedTime} where aid = #{aid} </update >
单元测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Test public void findByAid () { System.out.println(addressMapper.findByAid(3 )); } @Test public void updateNonDefault () { addressMapper.updateNonDefault(1 ); } @Test public void updateDefaultByAid () { addressMapper.updateDefaultByAid(6 ,"安梦赫" ,new Date()); }
2. 业务层 2.1 规划异常 1.在执行设置默认收货地址之前,需要先检查该收货地址数据是否存在,如果不存在则抛出AddressNotFoundException异常。
2.然后还需要检查数据归属是否正确,也就是不可以操作他人的数据,如果该数据中记录的uid与当前登录的用户的uid不一致,则抛出AccessDeniedException异常。
3.检查通过后先全部设置为非默认,然后将指定的收货地址设置为默认;这两种操作都是更新数据的操作,则可能抛出UpdateException异常(已创建无需重复创建)。
4.在com.amh.service.ex包下创建AddressNotFoundException和AccessDeniedException异常类。
1 2 3 4 5 6 package com.amh.service.ex;public class AddressNotFoundException extends ServiceException { }
1 2 3 4 5 6 package com.amh.service.ex;public class AccessDeniedException extends ServiceException { }
2.2 接口与抽象方法 在IAddressService接口中添加setDefault(Integer aid, Integer uid, String username)抽象方法。
1 2 3 4 5 6 7 void setDefault (Integer aid, Integer uid, String username) ;
2.3 实现抽象方法 1.在AddressServiceImpl类中重写setDefault(Integer aid, Integer uid, String username)方法。该方法需要添加@Transactional注解。
事务:基于Spring JDBC的事务(Transaction)处理,使用事务可以保证一系列的增删改操作,要么全部执行成功,要么全部执行失败。@Transactional注解可以用来修饰类也可以用来修饰方法。如果添加在业务类之前,则该业务类中的方法均以事务的机制运行,但是一般并不推荐这样处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 @Transactional @Override public void setDefault (Integer aid, Integer uid, String username) { Address result = addressMapper.findByAid(aid); if (result == null ) { throw new AddressNotFoundException("尝试访问的收货地址数据不存在" ); } if (!result.getUid().equals(uid)) { throw new AccessDeniedException("非法访问的异常" ); } Integer rows = addressMapper.updateNonDefault(uid); if (rows < 1 ) { throw new UpdateException("设置默认收货地址时出现未知错误[1]" ); } rows = addressMapper.updateDefaultByAid(aid, username, new Date()); if (rows != 1 ) { throw new UpdateException("设置默认收货地址时出现未知错误[2]" ); } }
单元测试
1 2 3 4 @Test public void setDefault () { addressService.setDefault(5 ,1 ,"赫" ); }
3 默认收货地址-控制器 3.1 处理异常 在BaseController类中添加处理AddressNotFoundException和AccessDeniedException的异常。
1 2 3 4 5 6 7 8 9 else if (e instanceof AddressNotFoundException) { result.setState(4004 ); result.setMessage(e.getMessage()); } else if (e instanceof AccessDeniedException) { result.setState(4005 ); result.setMessage(e.getMessage()); }
3.2 设计请求 设计用户提交的请求,并设计响应的方式。
1 2 3 4 请求路径:/addresses/{aid}/set_ default 请求参数:@PathVaraible("aid") Integer aid, HttpSession sesion 请求类型:POST 响应结果:JsonResult<Void>
REST即表述性状态传递(Representational State Transfer,简称REST)是Roy Fielding博士在2000年他的博士论文中提出来的一种软件架构风格。它是一种针对网络应用的设计和开发方式,可以降低开发的复杂性,提高系统的可伸缩性。
3.3 处理请求 1.在AddressController类中添加处理请求的setDefault(@PathVariable(“aid”) Integer aid, HttpSession session)方法。
1 2 3 4 5 6 7 @RequestMapping("{aid}/set_default") public JsonResult<Void> setDefault (@PathVariable("aid") Integer aid, HttpSession session) { Integer uid = getuidFromSession(session); String username = getUsernameFromSession(session); addressService.setDefault(aid, uid, username); return new JsonResult<Void>(OK); }
2.完成后启动项目,打开浏览器先登录,再访问http://localhost:8080/addresses/13/set_default进行测试。
4 默认收货地址-前端页面 1.给设置默认收货地址按钮添加一个onclick属性,指向一个方法的调用,在这个方法中来完成ajax请求的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 let list=json.data;for (let i=0 ;i<list.length;i++){ let tr='<tr>\n' + '<td>#{tag}</td>\n' + '<td>#{name}</td>\n' + '<td>#{address}</td>\n' + '<td>#{phone}</td>\n' + '<td><a class="btn btn-xs btn-info"><span className="fa fa-edit"></span> 修改</a></td>\n' + '<td><a class="btn btn-xs add-del btn-info"><span\n' + 'className="fa fa-trash-o"></span> 删除</a></td>\n' + '<td><a class="btn btn-xs add-def btn-default" onclick="setDefault(#{aid}})">设为默认</a></td>\n' + '</tr>' tr = tr.replace(/#{tag}/g ,list[i].tag) tr = tr.replace(/#{name}/g ,list[i].name) tr = tr.replace("#{address}" ,list[i].address) tr = tr.replace("#{phone}" ,list[i].phone) tr = tr.replace("#{aid}" ,list[i].aid) $("#address-list" ).append(tr); }
address.html页面点击“设置默认” 按钮,来发送ajax请求。完成setDefault()方法的声明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function setDefault (aid ) { $.ajax({ url : "/addresses/" + aid + "/set_default" , type : "POST" , dataType : "JSON" , success : function (json ) { if (json.state == 200 ) { $("#address-list" ).empty(); showAddressList(); } else { alert("设置默认收货地址失败!" + json.message); } }, error : function (xhr ) { alert("您的登录信息已经过期,请重新登录!HTTP响应码:" + xhr.status); location.href = "login.html" ; } }); }
删除收货地址 1 删除收货地址-持久层 1.1 规划需要执行的SQL语句 1.在删除之前,需检查数据是否存在,数据归属是否正确。此功能已完成,无需再次开发。
2.删除指定的收货地址的SQL语句大致是。
1 delete from t_address where aid=?
3.如果删除的这条数据是默认收货地址,则应该将剩余的收货地址中的某一条设置为默认收货地址,可以设定规则“将最近修改的设置为默认收货地址”,要实现此功能就必须要知道“最近修改的收货地址的id是多少”。则通过以下查询语句完成。
1 select * from t_address where uid=? order by modified_time desc limit 0,1
4.在执行以上操作之前,还需检查该用户的收货地址数据的数量,如果删除的收货地址是最后一条收货地址,则删除成功后无需再执行其他操作。统计收货地址数量的功能此前已经完成,无需再次开发。
1.2 接口与抽象方法 在AddressMapper接口中添加抽象方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 Integer deleteByAid (Integer aid) ;Address findLastModified (Integer uid) ;
1.3 配置SQL映射 1.在AddressMapper.xml文件中添加以上两个抽象方法的映射。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <delete id ="deleteByAid" > DELETE FROM t_address WHERE aid=#{aid} </delete > <select id ="findLastModified" resultMap ="AddressEntityMap" > SELECT * FROM t_address WHERE uid=#{uid} ORDER BY modified_time DESC LIMIT 0,1 </select >
2.在AddressMapperTests测试类中添加单元测试方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 @Test public void deleteByAid () { Integer aid = 4 ; Integer rows = addressMapper.deleteByAid(aid); System.out.println("rows=" + rows); } @Test public void findLastModified () { Integer uid = 30 ; Address result = addressMapper.findLastModified(uid); System.out.println(result); }
2 删除收货地址-业务层 2.1 规划异常 在执行删除操作时,可能会删除数据失败,此时抛出DeleteException异常。在创建com.amh.service.ex.DeleteException异常类,并继承自ServiceException类。
1 2 3 4 5 6 package com.cy.store.service.ex;public class DeleteException extends ServiceException { }
2.2 接口与抽象方法 在IAddressService接口中添加删除收货地址的抽象方法。
1 2 3 4 5 6 7 void delete (Integer aid, Integer uid, String username) ;
2.3 实现抽象方法 1.在AddressServiceImpl实现类中实现以上两个抽象方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 @Transactional @Override public void delete (Integer aid, Integer uid, String username) { Address result = addressMapper.findByAid(aid); if (result == null ) { throw new AddressNotFoundException("尝试访问的收货地址数据不存在" ); } if (!result.getUid().equals(uid)) { throw new AccessDeniedException("非常访问" ); } Integer rows1 = addressMapper.deleteByAid(aid); if (rows1 != 1 ) { throw new DeleteException("删除收货地址数据时出现未知错误,请联系系统管理员" ); } if (result.getIsDefault() == 0 ) { return ; } Integer count = addressMapper.countByUid(uid); if (count == 0 ) { return ; } Address lastModified = addressMapper.findLastModified(uid); Integer lastModifiedAid = lastModified.getAid(); Integer rows2 = addressMapper.updateDefaultByAid(lastModifiedAid, username, new Date()); if (rows2 != 1 ) { throw new UpdateException("更新收货地址数据时出现未知错误,请联系系统管理员" ); } }
2.在AddressServiceTests测试类中添加单元测试方法。
1 2 3 4 5 6 7 8 @Test public void delete () { Integer aid = 6 ; Integer uid = 4 ; String username = "明明" ; addressService.delete(aid, uid, username); System.out.println("OK" ); }
3 删除收货地址-控制器 3.1 处理异常 在BaseController类中添加DeleteException异常的处理。
1 2 3 4 5 6 else if (e instanceof DeleteException) { result.setState(5002 ); result.setMessage(e.getMessage()); }
3.2 设计请求 设计用户提交的请求,并设计响应的方式。
请求路径:/addresses/{aid}/delete
请求参数:@PathVariable("aid") Integer aid, HttpSession session
请求类型:POST
响应结果:JsonResult<Void>
3.3 处理请求 1.在AddressController类中添加处理请求的delete()方法。
1 2 3 4 5 6 7 @RequestMapping("{aid}/delete") public JsonResult<Void> delete (@PathVariable("aid") Integer aid, HttpSession session) { Integer uid = getUidFromSession(session); String username = getUsernameFromSession(session); addressService.delete(aid, uid, username); return new JsonResult<Void>(OK); }
2.完成后启动项目,打开浏览器先登录,再访问http://localhost:8080/addresses/26/delete进行测试。
4 删除收货地址-前端页面 1.在address.html页面中body标签内部的script标签内,添加设置用户删除收货地址的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function deleteByAid (aaid ) { $.ajax({ url : "/addresses/" + aaid + "/delete" , type : "POST" , dataType : "JSON" , success : function (json ) { if (json.state == 200 ) { showAddressList(); } else { alert("删除收货地址失败!" + json.message); } }, error : function (json ) { alert("您的登录信息已经过期,请重新登录!HTTP响应码:" + json.status); location.href = "login.html" ; } }); }
2.给showAddressList()方法中的“设为默认”超链接按钮添加设置默认收货地址的点击事件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 for (let i=0 ;i<list.length;i++){ let tr='<tr>\n' + '<td>#{tag}</td>\n' + '<td>#{name}</td>\n' + '<td>#{address}</td>\n' + '<td>#{phone}</td>\n' + '<td><a class="btn btn-xs btn-info"><span className="fa fa-edit"></span> 修改</a></td>\n' + '<td><a onclick="deleteByAid(#{aaid})" class="btn btn-xs add-del btn-info"><span\n' + 'className="fa fa-trash-o"></span> 删除</a></td>\n' + '<td><a onclick="setDefault(#{aid})" class="btn btn-xs add-def btn-default">设为默认</a></td>\n' + '</tr>' tr = tr.replace(/#{tag}/g ,list[i].tag) tr = tr.replace(/#{name}/g ,list[i].name) tr = tr.replace("#{address}" ,list[i].address) tr = tr.replace("#{phone}" ,list[i].phone) tr = tr.replace("#{aaid}" ,list[i].aid) tr = tr.replace("#{aid}" ,list[i].aid) $("#address-list" ).append(tr); }
修改收货地址 1.修改收货地址-持久层 1.1 SQL语句的规划 1 2 UPDATE t_address SET name= ? province_name= ?, province_code= ?, city_name= ?, city_code= ?, area_name= ?,area_code= ?, zip= ?,address= ?, phone= ?, tel= ?, tag= ?,modified_user= ?,modified_time= ? WHERE aid= ?
1.2 接口与抽象方法 在AddressMapper接口中添加抽象方法。
已编写过
1 2 3 4 5 6 > >Address findByAid (Integer aid) ;
1 2 3 4 5 6 Integer updateAddressByAid (Address address) ;
1.3 配置SQL映射 1.在AddressMapper.xml文件中添加以上一个抽象方法的映射。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <update id ="updateAddressByAid" > UPDATE stores.t_address SET <if test ="name!=null" > name=#{name},</if > <if test ="provinceName!=null" > province_name=#{provinceName},</if > <if test ="provinceCode!=null" > province_code=#{provinceCode},</if > <if test ="cityName!=null" > city_name=#{cityName},</if > <if test ="cityCode!=null" > city_code=#{cityCode},</if > <if test ="areaName!=null" > area_name=#{areaName},</if > <if test ="areaCode!=null" > area_code=#{areaCode},</if > <if test ="zip!=null" > zip=#{zip},</if > <if test ="address!=null" > address=#{address},</if > <if test ="phone!=null" > phone=#{phone},</if > <if test ="tel!=null" > tel=#{tel},</if > <if test ="tag!=null" > tag=#{tag},</if > modified_user=#{modifiedUser}, modified_time=#{modifiedTime} WHERE aid=#{aid} </update >
单元测试
1 2 3 4 5 6 7 8 9 10 11 12 @Test public void updateAddressByAid () { Address address=new Address(); String username="赫" ; address.setAid(5 ); address.setName("梦赫" ); address.setProvinceName("陕西" ); address.setModifiedUser(username); address.setModifiedTime(new Date()); addressMapper.updateAddressByAid(address); }
2.修改收货地址-业务层 2.1 规划异常 1.在删除之前,需检查数据是否存在,数据归属是否正确。此功能已完成,无需再次开发。
2.更新数据时产生未知的异常UpdateException,不需要再重复的创建。
2.2 接口与抽象方法 在IAddressService接口中添加根据aid查询收货地址,修改收货地址的抽象方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 Address selectAddressAid (Integer aid) ; void updateAddressByAid (Integer uid,String username,Address address) ;
实现抽象方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 @Override public Address selectAddressAid (Integer aid) { return addressMapper.findByAid(aid); } @Transactional @Override public void updateAddressByAid (Integer uid, String username, Address address) { Address result = addressMapper.findByAid(address.getAid()); if (result == null ) { throw new AddressNotFoundException("尝试访问的收货地址数据不存在" ); } if (!result.getUid().equals(uid)) { throw new AccessDeniedException("非常访问" ); } address.setModifiedUser(username); address.setModifiedTime(new Date()); Integer row = addressMapper.updateAddressByAid(address); if (row != 1 ) { throw new UpdateException("修改收货地址数据时出现未知错误,请联系系统管理员" ); } }
单元测试
1 2 3 4 5 6 7 8 @Test public void updateAddressByAid () { Address address=new Address(); address.setAid(3 ); address.setName("梦赫" ); address.setProvinceName("湖南" ); addressService.updateAddressByAid(5 ,"梦想" ,address); }
3 修改收货地址-控制器 3.1 处理异常
已处理
3.2 设计请求 1.跳到修改页面就发送根据当前aid的查询
1 2 3 4 请求路径:{uAid}/selectAid 请求参数:Integer aid 请求类型:GET 响应结果:JsonResult<Address>
2.设计用户提交的请求,并设计响应的方式。
1 2 3 4 请求路径:/update 请求参数:Address address, HttpSession session 请求类型:POST 响应结果:JsonResult<Void>
3.3 处理请求 1.在AddressController类中添加处理请求的update()方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 @RequestMapping("{uAid}/selectAid") public JsonResult<Address> selectAid (@PathVariable("uAid") Integer aid) { Address address = addressService.selectAddressAid(aid); return new JsonResult<>(OK,address); } @RequestMapping("update") public JsonResult<Void> update (Address address, HttpSession session) { Integer uid = getuidFromSession(session); String username = getUsernameFromSession(session); addressService.updateAddressByAid(uid, username,address); return new JsonResult<Void>(OK); }
4 修改收货地址-前端页面 给对应组件如果没有id值则加id值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <input type ="hidden" id ="aid" name ="aid" > <input id ="name" name ="name" type ="text" class ="form-control" placeholder ="请输入收货人姓名" > <input id ="zip" name ="zip" type ="text" class ="form-control" placeholder ="请输入邮政编码" > <textarea id ="address" name ="address" class ="form-control" rows ="3" placeholder ="输入详细的收货地址,小区名称、门牌号等" > </textarea > <input id ="phone" name ="phone" type ="text" class ="form-control" placeholder ="请输入手机号码" > <input id ="tel" name ="tel" type ="text" class ="form-control" placeholder ="请输入固定电话号码" > <input id ="tag" name ="tag" type ="text" class ="form-control" placeholder ="请输入地址类型,如:家、公司或者学校" >
在address.html页面中body标签内部的script标签内,添加设置用户修改收货地址的代码。
首先需要加入
1 2 <!--获取url的参数($.getUrlParam("aid" ))--> <script type ="text/javascript" src ="../js/jquery-getUrlParam.js" > </script >
修改ready的条件
1 2 3 4 5 6 7 8 9 10 11 12 $(document ).ready(function ( ) { let aid = $.getUrlParam("aid" ); console .log("ready-aid=" + aid); if (aid!=null ){ updateAid(); } showProvinceList(); $("#city-list" ).append(defaultOption); $("#area-list" ).append(defaultOption); });
编写updateAid()方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 function updateAid ( ) { let aid = $.getUrlParam("aid" ); console .log("aid=" + aid); $.ajax({ url : "/addresses/" + aid + "/selectAid" , type : "GET" , dataType : "JSON" , success : function (json ) { if (json.state == 200 ) { $("#aid" ).val(json.data.aid) $("#name" ).val(json.data.name); $("#province-list" ).append("<option value='" + json.data.provinceCode + "'>" + json.data.provinceName + "</option>" ); $("#city-list" ).append("<option value='" + json.data.cityCode + "'>" + json.data.cityName + "</option>" ); $("#area-list" ).append("<option value='" + json.data.areaCode + "'>" + json.data.areaName + "</option>" ); $("#zip" ).val(json.data.zip); $("#address" ).val(json.data.address); $("#phone" ).val(json.data.phone); $("#tel" ).val(json.data.tel); $("#tag" ).val(json.data.tag); }else { alert("获取信息失败!" + json.message); } } }); }
提交按钮的条件判断
是新增数据,还是修改数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 let aid = $.getUrlParam("aid" );if (aid==null ){ $("#btn-add-new-address" ).click(function ( ) { $.ajax({ url : "/addresses/add_new_address" , type : "POST" , data : $("#form-add-new-address" ).serialize(), dataType : "JSON" , success : function (json ) { if (json.state == 200 ) { alert(json.message); } else { alert(json.message); } }, error : function (xhr ) { alert("新增收货地址产生未知的错!" + xhr.status) } }); }); }else { $("#btn-add-new-address" ).click(function ( ) { $.ajax({ url : "/addresses/update" , type : "POST" , data : $("#form-add-new-address" ).serialize(), dataType : "JSON" , success : function (json ) { if (json.state == 200 ) { alert(json.message); } else { alert(json.message); } }, error : function (xhr ) { alert("新增收货地址产生未知的错!" + xhr.status) } }); }); }
商品 商品-数据表的创建 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 CREATE TABLE t_product ( id int (20 ) NOT NULL COMMENT '商品id' , category_id int (20 ) DEFAULT NULL COMMENT '分类id' , item_type varchar (100 ) DEFAULT NULL COMMENT '商品系列' , title varchar (100 ) DEFAULT NULL COMMENT '商品标题' , sell_point varchar (150 ) DEFAULT NULL COMMENT '商品卖点' , price bigint (20 ) DEFAULT NULL COMMENT '商品单价' , num int (10 ) DEFAULT NULL COMMENT '库存数量' , image varchar (500 ) DEFAULT NULL COMMENT '图片路径' , status int (1 ) DEFAULT '1' COMMENT '商品状态 1:上架 2:下架 3:删除' , priority int (10 ) DEFAULT NULL COMMENT '显示优先级' , created_time datetime DEFAULT NULL COMMENT '创建时间' , modified_time datetime DEFAULT NULL COMMENT '最后修改时间' , created_user varchar (50 ) DEFAULT NULL COMMENT '创建人' , modified_user varchar (50 ) DEFAULT NULL COMMENT '最后修改人' , PRIMARY KEY (id) ) ENGINE= InnoDB DEFAULT CHARSET= utf8;
2 商品-创建实体类 创建com.amh.entity.Product类,并继承自BaseEntity类。在类中声明与数据表中对应的属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package com.amh.entity;@Data @AllArgsConstructor @NoArgsConstructor public class Product extends BaseEntity implements Serializable { private Integer id; private Integer categoryId; private String itemType; private String title; private String sellPoint; private Long price; private Integer num; private String image; private Integer status; private Integer priority; }
3 商品-热销排行-持久层 3.1 规划需要执行的SQL语句 查询热销商品列表的SQL语句大致是。
1 SELECT * FROM t_product WHERE status=1 ORDER BY priority DESC LIMIT 0,4
3.2 接口与抽象方法 在com.amh.mapper包下创建ProductMapper接口并在接口中添加查询热销商品findHotList()的方法。
1 2 3 4 5 6 7 8 /** 处理商品数据的持久层接口 */ public interface ProductMapper { /** * 查询热销商品的前四名 * @return 热销商品前四名的集合 */ List<Product> findHotList(); }
3.3 配置SQL映射 1.在main\resources\mapper文件夹下创建ProductMapper.xml文件,并在文件中配置findHotList()方法的映射。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="com.cy.store.mapper.ProductMapper" > <resultMap id ="ProductEntityMap" type ="com.cy.store.entity.Product" > <id column ="id" property ="id" /> <result column ="category_id" property ="categoryId" /> <result column ="item_type" property ="itemType" /> <result column ="sell_point" property ="sellPoint" /> <result column ="created_user" property ="createdUser" /> <result column ="created_time" property ="createdTime" /> <result column ="modified_user" property ="modifiedUser" /> <result column ="modified_time" property ="modifiedTime" /> </resultMap > <select id ="findHotList" resultMap ="ProductEntityMap" > SELECT * FROM t_product WHERE status=1 ORDER BY priority DESC LIMIT 0,4 </select > </mapper >
2.在com.cy.store.mapper包下创建ProductMapperTests测试类,并添加测试方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @RunWith(SpringRunner.class) @SpringBootTest public class ProductMapperTests { @Autowired private ProductMapper productMapper; @Test public void findHotList () { List<Product> list = productMapper.findHotList(); System.out.println("count=" + list.size()); for (Product item : list) { System.out.println(item); } } }
4 商品-热销排行-业务层 4.1 规划异常
说明 :无异常。
4.2 接口与抽象方法 创建com.amh.service.IProductService接口,并在接口中添加findHotList()方法。
1 2 3 4 5 6 7 8 public interface IProductService { List<Product> findHotList () ; }
4.3 实现抽象方法 1.创建com.amh.service.impl.ProductServiceImpl类,并添加@Service注解;在类中声明持久层对象以及实现接口中的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Service public class ProductServiceImpl implements IProductService { @Autowired private ProductMapper productMapper; @Override public List<Product> findHotList () { List<Product> list = productMapper.findHotList(); for (Product product : list) { product.setPriority(null ); product.setCreatedUser(null ); product.setCreatedTime(null ); product.setModifiedUser(null ); product.setModifiedTime(null ); } return list; } }
2.在com.cy.store.service包下创建测试类ProductServiceTests,并编写测试方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @RunWith(SpringRunner.class) @SpringBootTest public class ProductServiceTests { @Autowired private IProductService productService; @Test public void findHotList () { try { List<Product> list = productService.findHotList(); System.out.println("count=" + list.size()); for (Product item : list) { System.out.println(item); } } catch (ServiceException e) { System.out.println(e.getClass().getSimpleName()); System.out.println(e.getMessage()); } } }
5 商品-热销排行-控制器 5.1 处理异常
说明 :无异常。
5.2 设计请求 1.设计用户提交的请求,并设计响应的方式。
请求路径:/products/hot_list
请求参数:无
请求类型:GET
响应结果:JsonResult<List<Product>>
是否拦截:否,需要将index.html和products/**添加到白名单
2.在LoginInterceptorConfigurer类中将index.html页面和products/**请求添加到白名单。
1 2 patterns.add("/web/index.html" ); patterns.add("/products/**" );
5.3 处理请求 1.创建com.cy.controller.ProductController类继承自BaseController类,类添加@RestController和@RequestMapping(“products”)注解,并在类中添加业务层对象。
1 2 3 4 5 6 @RestController @RequestMapping("products") public class ProductController extends BaseController { @Autowired private IProductService productService; }
2.在类中添加处理请求的getHotList()方法。
1 2 3 4 5 @RequestMapping("hot_list") public JsonResult<List<Product>> getHotList() { List<Product> data = productService.findHotList(); return new JsonResult<List<Product>>(OK, data); }
3.完成后启动项目,直接访问http://localhost:8080/products/hot_list进行测试。
6 商品-热销排行-前端页面 1.在index.html页面给“热销排行”列表的div标签设置id属性值。
1 2 3 <div id ="hot-list" class ="panel-body panel-item" > </div >
2.在index.html页面中body标签内部的最后,添加展示热销排行商品的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <script type="text/javascript" > $(document ).ready(function ( ) { showHotList(); }); function showHotList ( ) { $("#hot-list" ).empty(); $.ajax({ url : "/products/hot_list" , type : "GET" , dataType : "JSON" , success : function (json ) { let list = json.data; console .log("count=" + list.length); for (let i = 0 ; i < list.length; i++) { console .log(list[i].title); let html = '<div class="col-md-12">' + '<div class="col-md-7 text-row-2"><a href="product.html?id=#{id}">#{title}</a></div>' + '<div class="col-md-2">¥#{price}</div>' + '<div class="col-md-3"><img src="..#{image}collect.png" class="img-responsive" /></div>' + '</div>' ; html = html.replace(/#{id}/g , list[i].id); html = html.replace(/#{title}/g , list[i].title); html = html.replace(/#{price}/g , list[i].price); html = html.replace(/#{image}/g , list[i].image); $("#hot-list" ).append(html); } } }); } </script>
3.完成后启动项目,直接访问http://localhost:8080/web/index.html进行测试。
显示商品详情 1 商品-显示商品详情-持久层 1.1 规划需要执行的SQL语句 根据商品id显示商品详情的SQL语句大致是。
1 SELECT * FROM t_product WHERE id=?
1.2 接口与抽象方法 在ProductMapper接口中添加抽象方法。
1 2 3 4 5 6 Product findById (Integer id) ;
1.3 配置SQL映射 1.在ProductMapper.xml文件中配置findById(Integer id)方法的映射。
1 2 3 4 5 6 7 8 9 <select id ="findById" resultMap ="ProductEntityMap" > SELECT * FROM t_product WHERE id=#{id} </select >
2.在ProductMapperTests测试类中添加测试方法。
1 2 3 4 5 6 @Test public void findById () { Integer id = 10000017 ; Product result = productMapper.findById(id); System.out.println(result); }
2 商品-显示商品详情-业务层 2.1 规划异常 如果商品数据不存在,应该抛出ProductNotFoundException,需要创建com.amh.service.ex.ProductNotFoundException异常。
1 2 3 4 public class ProductNotFoundException extends ServiceException { }
2.2 接口与抽象方法 在业务层IProductService接口中添加findById(Integer id)抽象方法。
1 2 3 4 5 6 Product findById (Integer id) ;
2.3 实现抽象方法 1.在ProductServiceImpl类中,实现接口中的findById(Integer id)抽象方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Override public Product findById (Integer id) { Product product = productMapper.findById(id); if (product == null ) { throw new ProductNotFoundException("尝试访问的商品数据不存在" ); } product.setPriority(null ); product.setCreatedUser(null ); product.setCreatedTime(null ); product.setModifiedUser(null ); product.setModifiedTime(null ); return product; }
2.在ProductServiceTests测试类中编写测试方法。
1 2 3 4 5 6 7 8 9 10 11 @Test public void findById () { try { Integer id = 100000179 ; Product result = productService.findById(id); System.out.println(result); } catch (ServiceException e) { System.out.println(e.getClass().getSimpleName()); System.out.println(e.getMessage()); } }
3 商品-显示商品详情-控制器 3.1 处理异常 在BaseController类中的handleException()方法中添加处理ProductNotFoundException的异常。
1 2 3 4 5 else if (e instanceof ProductNotFoundException) { result.setState(4006 ); }
3.2 设计请求 设计用户提交的请求,并设计响应的方式。
请求路径:/products/{id}/details
请求参数:@PathVariable("id") Integer id
请求类型:GET
响应结果:JsonResult<Product>
3.3 处理请求 1.在ProductController类中添加处理请求的getById()方法。
1 2 3 4 5 6 7 @GetMapping("{id}/details") public JsonResult<Product> getById (@PathVariable("id") Integer id) { Product data = productService.findById(id); return new JsonResult<Product>(OK, data); }
2.完成后启动项目,直接访问http://localhost:8080/products/10000017/details进行测试。
4 商品-显示商品详情-前端页面 1.检查在product.html页面body标签内部的最后是否引入jquery-getUrlParam.js文件,如果引入无需重复引入。
1 2 <!--获取url的参数($.getUrlParam("aid" ))--> <script type="text/javascript" src="../js/jquery-getUrlParam.js" ></script>
2.在product.html页面中body标签内部的最后添加获取当前商品详情的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <script type="text/javascript" > let id = $.getUrlParam("id" ); console .log("id=" + id);$(document ).ready(function ( ) { $.ajax({ url : "/products/" + id + "/details" , type : "GET" , dataType : "JSON" , success : function (json ) { if (json.state == 200 ) { console .log("title=" + json.data.title); $("#product-title" ).html(json.data.title); $("#product-sell-point" ).html(json.data.sellPoint); $("#product-price" ).html(json.data.price); for (let i = 1 ; i <= 5 ; i++) { $("#product-image-" + i + "-big" ).attr("src" , ".." + json.data.image + i + "_big.png" ); $("#product-image-" + i).attr("src" , ".." + json.data.image + i + ".jpg" ); } } else if (json.state == 4006 ) { location.href = "index.html" ; } else { alert("获取商品信息失败!" + json.message); } } }); }); </script>
3.完成后启动项目,先访问http://localhost:8080/web/index.html页面,然后点击“热销排行”中的某个子项,将跳转到product.html商品详情页,观察页面是否加载的是当前的商品信息。
加入购物车 1 购物车-创建数据表 .在store数据库中创建t_cart用户数据表。
1 2 3 4 5 6 7 8 9 10 11 12 CREATE TABLE t_cart ( cid INT AUTO_INCREMENT COMMENT '购物车数据id', uid INT NOT NULL COMMENT '用户id', pid INT NOT NULL COMMENT '商品id', price BIGINT COMMENT '加入时商品单价', num INT COMMENT '商品数量', created_user VARCHAR(20) COMMENT '创建人', created_time DATETIME COMMENT '创建时间', modified_user VARCHAR(20) COMMENT '修改人', modified_time DATETIME COMMENT '修改时间', PRIMARY KEY (cid) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2 购物车-创建实体类 在com.cy.store.entity包下创建购物车的Cart实体类。
1 2 3 4 5 6 7 8 9 10 public class Cart extends BaseEntity implements Serializable { private Integer cid; private Integer uid; private Integer pid; private Long price; private Integer num; }
3 购物车-添加购物车-持久层 3.1 规划需要执行的SQL语句 向购物车表中插入商品数据的SQL语句大致是:
1 insert into t_cart (除了cid以外的字段列表) values (匹配的值列表);
如果用户曾经将某个商品加入到购物车过,则点击“加入购物车”按钮只会对购物车中相同商品数量做递增操作。
1 update t_cart set num=? where cid=?
关于判断“到底应该插入数据,还是修改数量”,可以通过“查询某用户是否已经添加某商品到购物车”来完成。如果查询到某结果,就表示该用户已经将该商品加入到购物车了,如果查询结果为null,则表示该用户没有添加过该商品。
1 select * from t_cart where uid=? and pid=?
3.2 接口与抽象方法 在com.cy.store.mapper包下创建CartMapper接口,并添加抽象相关的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public interface CartMapper { Integer insert (Cart cart) ; Integer updateNumByCid ( @Param("cid") Integer cid, @Param("num") Integer num, @Param("modifiedUser") String modifiedUser, @Param("modifiedTime") Date modifiedTime) ; Cart findByUidAndPid ( @Param("uid") Integer uid, @Param("pid") Integer pid) ;}
3.3 配置SQL映射 1.在resources.mapper文件夹下创建CartMapper.xml文件,并在文件中配置以上三个方法的映射。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="com.amh.mapper.CartMapper" > <resultMap id ="CartEntityMap" type ="com.amh.entity.Cart" > <id column ="cid" property ="cid" /> <result column ="created_user" property ="createdUser" /> <result column ="created_time" property ="createdTime" /> <result column ="modified_user" property ="modifiedUser" /> <result column ="modified_time" property ="modifiedTime" /> </resultMap > <insert id ="insert" useGeneratedKeys ="true" keyProperty ="cid" > INSERT INTO stores.t_cart (uid, pid, price, num, created_user, created_time, modified_user, modified_time) VALUES (#{uid}, #{pid}, #{price}, #{num}, #{createdUser}, #{createdTime}, #{modifiedUser}, #{modifiedTime}) </insert > <update id ="updateNumByCid" > UPDATE stores.t_cart SET num=#{num}, modified_user=#{modifiedUser}, modified_time=#{modifiedTime} WHERE cid=#{cid} </update > <select id ="findByUidAndPid" resultMap ="CartEntityMap" > SELECT * FROM stores.t_cart WHERE uid=#{uid} AND pid=#{pid} </select > </mapper >
2.单元测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 @RunWith(SpringRunner.class) @SpringBootTest public class CartMapperTests { @Autowired private CartMapper cartMapper; @Test public void insert () { Cart cart = new Cart(); cart.setUid(1 ); cart.setPid(2 ); cart.setNum(3 ); cart.setPrice(4L ); Integer rows = cartMapper.insert(cart); System.out.println("rows=" + rows); } @Test public void updateNumByCid () { Integer cid = 1 ; Integer num = 10 ; String modifiedUser = "购物车管理员" ; Date modifiedTime = new Date(); Integer rows = cartMapper.updateNumByCid(cid, num, modifiedUser, modifiedTime); System.out.println("rows=" + rows); } @Test public void findByUidAndPid () { Integer uid = 1 ; Integer pid = 2 ; Cart result = cartMapper.findByUidAndPid(uid, pid); System.out.println(result); } }
4 购物车-添加购物车-业务层 4.1 规划异常 在插入数据时,可能抛出InsertException异常;在修改数据时,可能抛出UpdateException异常。如果不限制购物车中的记录的数量,则没有其它异常。
4.2 接口与抽象方法 在com.cy.store.service包下创建ICartService接口,并添加抽象方法。
1 2 3 4 5 6 7 8 9 10 11 public interface ICartService { void addToCart (Integer uid, Integer pid, Integer amount, String username) ; }
4.3 实现抽象方法 1.创建com.cy.store.service.impl.CartServiceImpl类,并实现ICartService接口,并在类的定义前添加@Service注解。在类中声明CartMapper持久层对象和IProductService处理商品数据的业务对象,并都添加@Autowired注修饰。
1 2 3 4 5 6 7 8 @Service public class CartServiceImpl implements ICartService { @Autowired private CartMapper cartMapper; @Autowired private IProductService productService; }
2.在CartServiceImpl类中实现业务层ICartService接口中定义的抽象方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Override public void addToCart (Integer uid, Integer pid, Integer amount, String username) { }
3.addToCart(Integer uid, Integer pid, Integer amount, String username)方法的代码具体实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 @Override public void addToCart (Integer uid, Integer pid, Integer amount, String username) { Cart result = cartMapper.findByUidAndPid(uid, pid); Integer cid = result.getCid(); Date now = new Date(); if (result == null ) { Cart cart = new Cart(); cart.setUid(uid); cart.setPid(pid); cart.setNum(amount); Product product = productService.findById(pid); cart.setPrice(product.getPrice()); cart.setCreatedUser(username); cart.setCreatedTime(now); cart.setModifiedUser(username); cart.setModifiedTime(now); Integer rows = cartMapper.insert(cart); if (rows != 1 ) { throw new InsertException("插入商品数据时出现未知错误,请联系系统管理员" ); } } else { Integer cid = result.getCid(); Integer num = result.getNum() + amount; Integer rows = cartMapper.updateNumByCid(cid, num, username, now); if (rows != 1 ) { throw new InsertException("修改商品数量时出现未知错误,请联系系统管理员" ); } } }
4.单元测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @RunWith(SpringRunner.class) @SpringBootTest public class CartServiceTests { @Autowired private ICartService cartService; @Test public void addToCart () { try { Integer uid = 2 ; Integer pid = 10000007 ; Integer amount = 1 ; String username = "Tom" ; cartService.addToCart(uid, pid, amount, username); System.out.println("OK." ); } catch (ServiceException e) { System.out.println(e.getClass().getSimpleName()); System.out.println(e.getMessage()); } } }
5 购物车-添加购物车-控制器 5.1 处理异常
说明 :无异常。
5.2 设计请求 设计用户提交的请求,并设计响应的方式。
请求路径:/carts/add_to_cart
请求参数:Integer pid, Integer amount, HttpSession session
请求类型:POST
响应结果:JsonResult<Void>
5.3 处理请求 1.在com.amh.controller包下创建CartController类并继承自BaseController类,添加@RequestMapping(“carts”)和@RestController注解;在类中声明ICartService业务对象,并使用@Autowired注解修饰。
1 2 3 4 5 6 7 @RestController @RequestMapping("carts") public class CartController extends BaseController { @Autowired private ICartService cartService; }
2.在CartController类中添加处理请求的addToCart()方法。
1 2 3 4 5 6 7 8 9 10 @RequestMapping("add_to_cart") public JsonResult<Void> addToCart (Integer pid, Integer amount, HttpSession session) { Integer uid = getUidFromSession(session); String username = getUsernameFromSession(session); cartService.addToCart(uid, pid, amount, username); return new JsonResult<Void>(OK); }
3.完成后启动项目,先登录再访问http://localhost:8080/carts/add_to_cart?pid=10000017&amount=3进行测试。
6 购物车-添加购物车-前端页面 1.在product.html页面中的body标签内的script标签里为“加入购物车”按钮添加点击事件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 $("#btn-add-to-cart" ).click(function ( ) { $.ajax({ url : "/carts/add_to_cart" , type : "POST" , data : { "pid" : id, "amount" : $("#num" ).val() }, dataType : "JSON" , success : function (json ) { if (json.state == 200 ) { alert("增加成功!" ); } else { alert("增加失败!" + json.message); } }, error : function (xhr ) { alert("您的登录信息已经过期,请重新登录!HTTP响应码:" + xhr.status); location.href = "login.html" ; } }); });
$.ajax函数中参数data提交请参数的方式:
1 2 3 4 5 6 7 8 9 10 11 // 1.适用于参数较多,且都在同一个表单中 data: $("#form表单id属性值").serialize() // 2.仅适用于上传文件 data: new FormData($("##form表单id属性值")[0]) // 3.参数拼接形式提交 data: "pid=10000005&amount=3" // 4.使用JSON格式提交参数 data: { "pid": 10000005, "amount": 3 }
2.完成后启动项目,先登录再访问http://localhost:8080/web/index.html页面进行测试。
显示购物车列表 1 购物车-显示列表-持久层 1.1 规划需要执行的SQL语句 显示某用户的购物车列表数据的SQL语句大致是。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 SELECT cid, uid, pid, t_cart.price, t_cart.num, t_product.title, t_product.price AS realPrice, t_product.image FROM t_cart LEFT JOIN t_product ON t_cart.pid = t_product.id WHERE uid = #{uid} ORDER BY t_cart.created_time DESC
1.2 接口与抽象方法 1.由于涉及多表关联查询,必然没有哪个实体类可以封装此次的查询结果,因此需要创建VO类。创建com.amh.vo.CartVO类。
1 2 3 4 5 6 7 8 9 10 11 12 13 public class CartVO implements Serializable { private Integer cid; private Integer uid; private Integer pid; private Long price; private Integer num; private String title; private Long realPrice; private String image; }
2.在CartMapper接口中添加抽象方法。
1 2 3 4 5 6 List<CartVO> findVOByUid (Integer uid) ;
1.3 配置SQL映射 1.在CartMapper.xml文件中添加findVOByUid()方法的映射。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <select id ="findVOByUid" resultType ="com.cy.store.vo.CartVO" > SELECT cid, uid, pid, t_cart.price, t_cart.num, t_product.title, t_product.price AS realPrice, t_product.image FROM t_cart LEFT JOIN t_product ON t_cart.pid = t_product.id WHERE uid = #{uid} ORDER BY t_cart.created_time DESC </select >
2.在CartMapperTests测试类中添加findVOByUid()方法的测试。
1 2 3 4 5 @Test public void findVOByUid () { List<CartVO> list = cartMapper.findVOByUid(31 ); System.out.println(list); }
2 购物车-显示列表-业务层 2.1 规划异常
说明 :无异常。
2.2 接口与抽象方法 在ICartService接口中添加findVOByUid()抽象方法。
1 2 3 4 5 6 List<CartVO> getVOByUid (Integer uid) ;
2.3 实现抽象方法 1.在CartServiceImpl类中重写业务接口中的抽象方法。
1 2 3 4 @Override public List<CartVO> getVOByUid (Integer uid) { return cartMapper.findVOByUid(uid); }
2.在CartServiceTests测试类中添加getVOByUid()测试方法。
1 2 3 4 5 6 7 8 @Test public void getVOByUid () { List<CartVO> list = cartService.getVOByUid(31 ); System.out.println("count=" + list.size()); for (CartVO item : list) { System.out.println(item); } }
3 购物车-显示列表-控制器 3.1 处理异常
说明 :无异常。
3.2 设计请求 设计用户提交的请求,并设计响应的方式。
请求路径:/carts/
请求参数:HttpSession session
请求类型:GET
响应结果:JsonResult<List<CartVO>>
3.3 处理请求 1.在CartController类中编写处理请求的代码。
1 2 3 4 5 6 7 8 9 @GetMapping({"", "/"}) public JsonResult<List<CartVO>> getVOByUid(HttpSession session) { Integer uid = getUidFromSession(session); List<CartVO> data = cartService.getVOByUid(uid); return new JsonResult<List<CartVO>>(OK, data); }
2.完成后启动项目,先登录再访问http://localhost:8080/carts请求进行测试。
4 购物车-显示列表-前端页面 1.将cart.html页面的head头标签内引入的cart.js文件注释掉。
1 <!-- <script src ="../js/cart.js" type ="text/javascript" charset ="utf-8" > </script > -->
2.给form标签添加action=”orderConfirm.html”属性、tbody标签添加id=”cart-list”属性、结算按钮的类型改为type=”button”值。如果以上属性值已经添加过无需重复添加。
3.在cart.html页面body标签内的script标签中编写展示购物车列表的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 $(document ).ready(function ( ) { showCartList(); }); function showCartList ( ) { $("#cart-list" ).empty(); $.ajax({ url : "/carts" , type : "GET" , dataType : "JSON" , success : function (json ) { let list = json.data; for (let i = 0 ; i < list.length; i++) { let tr = '<tr>' + '<td>' + '<input name="cids" value="#{cid}" type="checkbox" class="ckitem" />' + '</td>' + '<td><img src="..#{image}collect.png" class="img-responsive" /></td>' + '<td>#{title}#{msg}</td>' + '<td>¥<span id="price-#{cid}">#{realPrice}</span></td>' + '<td>' + '<input type="button" value="-" class="num-btn" onclick="reduceNum(1)" />' + '<input id="num-#{cid}" type="text" size="2" readonly="readonly" class="num-text" value="#{num}">' + '<input class="num-btn" type="button" value="+" onclick="addNum(#{cid})" />' + '</td>' + '<td>¥<span id="total-price-#{cid}">#{totalPrice}</span></td>' + '<td>' + '<input type="button" onclick="delCartItem(this)" class="cart-del btn btn-default btn-xs" value="删除" />' + '</td>' + '</tr>' ; tr = tr.replace(/#{cid}/g , list[i].cid); tr = tr.replace(/#{title}/g , list[i].title); tr = tr.replace(/#{image}/g , list[i].image); tr = tr.replace(/#{realPrice}/g , list[i].realPrice); tr = tr.replace(/#{num}/g , list[i].num); tr = tr.replace(/#{totalPrice}/g , list[i].realPrice * list[i].num); if (list[i].realPrice < list[i].price) { tr = tr.replace(/#{msg}/g , "比加入时降价" + (list[i].price - list[i].realPrice) + "元" ); } else { tr = tr.replace(/#{msg}/g , "" ); } $("#cart-list" ).append(tr); } } }); }
4.完成后启动项目,先登录再访问http://localhost:8080/web/cart.html页面进行测试。
增加/减少商品数量 1 购物车-增加/减少商品数量-持久层 1.1 规划需要执行的SQL语句 1.首先进行查询需要操作的购物车数据信息。
1 SELECT * FROM t_cart WHERE cid=?
2.然后计算出新的商品数量值,如果满足更新条件则执行更新操作。此SQL语句无需重复开发。
1 UPDATE t_cart SET num=?, modified_user=?, modified_time=? WHERE cid=?
1.2 接口与抽象方法 在CartMapper接口中添加抽象方法。
1 2 3 4 5 6 Cart findByCid (Integer cid) ;
1.3 配置SQL映射 1.在CartMapper文件中添加findByCid(Integer cid)方法的映射。
1 2 3 4 5 6 7 8 9 <select id ="findByCid" resultMap ="CartEntityMap" > SELECT * FROM t_cart WHERE cid = #{cid} </select >
2.在CartMapperTests测试类中添加findByCid()测试方法。
1 2 3 4 5 6 @Test public void findByCid () { Integer cid = 6 ; Cart result = cartMapper.findByCid(cid); System.out.println(result); }
2 购物车-增加/减少商品数量-业务层 2.1 规划异常 1.如果尝试访问的购物车数据不存在,则抛出CartNotFoundException异常。创建com.amh.service.ex.CartNotFoundException类。
1 2 3 4 public class CartNotFoundException extends ServiceException { }
2.如果尝试访问的数据并不是当前登录用户的数据,则抛出AccessDeniedException异常。此异常类无需再次创建。
3.最终执行更新操作时,可能会抛出UpdateException异常。此异常类无需再次创建。
2.2 接口与抽象方法 在业务层ICartService接口中添加addNum()抽象方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Integer addNum (Integer cid, Integer uid, String username) ;Integer reduceNum (Integer cid, Integer uid, String username) ;
2.3 实现抽象方法 1.在CartServiceImpl类中,实现接口中的抽象方法并规划业务逻辑。
2.实现addNum()方法和reduceNum()方法中的业务逻辑代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 @Override public Integer addNum (Integer cid, Integer uid, String username) { Cart result = cartMapper.findByCid(cid); if (result == null ) { throw new CartNotFoundException("尝试访问的购物车数据不存在" ); } if (!result.getUid().equals(uid)) { throw new AccessDeniedException("非法访问" ); } Integer num = result.getNum() + 1 ; Date now = new Date(); Integer rows = cartMapper.updateNumByCid(cid, num, username, now); if (rows != 1 ) { throw new InsertException("修改商品数量时出现未知错误,请联系系统管理员" ); } return num; } @Override public Integer reduceNum (Integer cid, Integer uid, String username) { Cart result = cartMapper.findByCid(cid); if (result == null ) { throw new CartNotFoundException("尝试访问的购物车数据不存在" ); } if (!result.getUid().equals(uid)) { throw new AccessDeniedException("非法访问" ); } if (result.getNum()==0 ){ return 0 ; } Integer num = result.getNum() - 1 ; Date now = new Date(); Integer rows = cartMapper.updateNumByCid(cid, num, username, now); if (rows != 1 ) { throw new InsertException("修改商品数量时出现未知错误,请联系系统管理员" ); } return num; }
3.在CartServiceTests测试类中添加addNum()测试方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 @Test public void addNum () { try { Integer cid = 6 ; Integer uid = 31 ; String username = "管理员" ; Integer num = cartService.addNum(cid, uid, username); System.out.println("OK. New num=" + num); } catch (ServiceException e) { System.out.println(e.getClass().getSimpleName()); System.out.println(e.getMessage()); } } @Test public void reduceNum () { try { Integer cid = 6 ; Integer uid = 31 ; String username = "管理员" ; Integer num = cartService.reduceNum(cid, uid, username); System.out.println("OK. New num=" + num); } catch (ServiceException e) { System.out.println(e.getClass().getSimpleName()); System.out.println(e.getMessage()); } }
3 购物车-增加/减少商品数量-控制器 3.1 处理异常 在BaseController类中添加CartNotFoundException异常类的统一管理。
1 2 3 4 5 else if (e instanceof CartNotFoundException) { result.setState(4007 ); }
3.2 设计请求 设计用户提交的请求,并设计响应的方式。
增加数量
请求路径:/carts/{cid}/num/add
请求参数:@PathVariable("cid") Integer cid, HttpSession session
请求类型:POST
响应结果:JsonResult<Integer>
减少数量
请求路径:/carts/{cid}/num/reduce
请求参数:@PathVariable("cid") Integer cid, HttpSession session
请求类型:POST
响应结果:JsonResult<Integer>
3.3 处理请求 1.在CartController类中添加处理请求的addNum()方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @RequestMapping("{cid}/num/add") public JsonResult<Integer> addNum (@PathVariable("cid") Integer cid, HttpSession session) { Integer uid = getUidFromSession(session); String username = getUsernameFromSession(session); Integer data = cartService.addNum(cid, uid, username); return new JsonResult<Integer>(OK, data); } @RequestMapping("{cid}/num/reduce") public JsonResult<Integer> reduceNum (@PathVariable("cid") Integer cid, HttpSession session) { Integer uid = getuidFromSession(session); String username = getUsernameFromSession(session); Integer data = cartService.reduceNum(cid, uid, username); return new JsonResult<Integer>(OK, data); }
2.完成后启动项目,先登录再访问http://localhost:8080/carts/6/num/add页面进行测试。
4 购物车-增加/减少商品数量-前端页面 1.首先确定在cart.html页面中showCartList()函数中动态拼接的增加购物车按钮是绑定了addNum()事件,如果已经添加无需重复添加。
1 2 3 <input type="button" value="-" class ="num-btn" onclick="reduceNum(#{cid}) <input class=" num-btn" type=" button" value=" +" onclick=" addNum(#{cid})" />
2.在script标签中定义addNum()函数并编写增加购物车数量的逻辑代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 function addNum (cid ) { $.ajax({ url : "/carts/" + cid + "/num/add" , type : "POST" , dataType : "JSON" , success : function (json ) { if (json.state == 200 ) { $("#num-" + cid).val(json.data); let price = $("#price-" + cid).html(); let totalPrice = price * json.data; $("#total-price-" + cid).html(totalPrice); } else { alert("增加商品数量失败!" + json.message); } }, error : function (xhr ) { alert("您的登录信息已经过期,请重新登录!HTTP响应码:" + xhr.status); location.href = "login.html" ; } }); } function reduceNum (cid ) { $.ajax({ url : "/carts/" + cid + "/num/reduce" , type : "POST" , dataType : "JSON" , success : function (json ) { if (json.state == 200 ) { $("#num-" + cid).val(json.data); let price = $("#price-" + cid).html(); let totalPrice = price * json.data; $("#total-price-" + cid).html(totalPrice); } else { alert("减少商品数量失败!" + json.message); } }, error : function (xhr ) { alert("您的登录信息已经过期,请重新登录!HTTP响应码:" + xhr.status); location.href = "login.html" ; } }); }
3.完成后启动项目,先登录再访问http://localhost:8080/web/cart.html页面点击“+”按钮进行测试。
显示勾选的购物车数据 1 显示确认订单页-显示勾选的购物车数据-持久层 1.1 规划需要执行的SQL语句 在“确认订单页”显示的商品信息,应来自前序页面(购物车列表)中勾选的数据,所以显示的信息其实是购物车中的数据。到底需要显示哪些取决于用户的勾选操作,当用户勾选了若干条购物车数据后,这些数据的id应传递到当前“确认订单页”中,该页面根据这些id获取需要显示的数据列表。
所以在持久层需要完成“根据若干个不确定的id值,查询购物车数据表,显示购物车中的数据信息”。则需要执行的SQL语句大致是。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 SELECT cid, uid, pid, t_cart.price, t_cart.num, t_product.title, t_product.price AS realPrice, t_product.image FROM t_cart LEFT JOIN t_product ON t_cart.pid = t_product.id WHERE cid IN (?, ?, ?) ORDER BY t_cart.created_time DESC
1.2 接口与抽象方法 在CartMapper接口中添加findVOByCids(Integer[] cids)方法。
1 2 3 4 5 6 List<CartVO> findVOByCids (@Param("cids") Integer[] cids) ;
1.3 配置SQL映射 1.在CartMapper.xml文件中添加SQL语句的映射配置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <select id ="findVOByCids" resultType ="com.cy.store.vo.CartVO" > SELECT cid, uid, pid, t_cart.price, t_cart.num, t_product.title, t_product.price AS realPrice, t_product.image FROM stores.t_cart LEFT JOIN stores.t_product ON t_cart.pid = t_product.id WHERE cid IN <foreach collection ="cids" item ="cid" separator ="," open ="(" close =")" > #{cid} </foreach > ORDER BY t_cart.created_time DESC </select >
2.在CartMapperTests测试类中添加findVOByCids()测试方法。
1 2 3 4 5 6 7 8 9 @Test public void findVOByCids () { Integer[] cids = {1 , 2 }; List<CartVO> list = cartMapper.findVOByCids(cids); System.out.println("count=" + list.size()); for (CartVO item : list) { System.out.println(item); } }
2 显示确认订单页-显示勾选的购物车数据-业务层 2.1 规划异常
说明 :无异常。
2.2 接口与抽象方法 在ICartService接口中添加getVOByCids()抽象方法。
1 2 3 4 5 6 7 List<CartVO> getVOByCids (Integer uid, Integer[] cids) ;
2.3 实现抽象方法 1.在CartServiceImpl类中重写业务接口中的抽象方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Override public List<CartVO> getVOByCids (Integer uid, Integer[] cids) { List<CartVO> list = cartMapper.findVOByCids(cids); Iterator<CartVO> it = list.iterator(); while (it.hasNext()) { CartVO cart = it.next(); if (!cart.getUid().equals(uid)) { it.remove(); } } return list; }
2.在CartServiceTests测试类中添加getVOByCids()测试方法。
1 2 3 4 5 6 7 8 9 10 @Test public void getVOByCids () { Integer[] cids = {1 , 2 }; Integer uid = 31 ; List<CartVO> list = cartService.getVOByCids(uid, cids); System.out.println("count=" + list.size()); for (CartVO item : list) { System.out.println(item); } }
3 显示确认订单页-显示勾选的购物车数据-控制器 3.1 处理异常 说明 :无异常。
3.2 设计请求 设计用户提交的请求,并设计响应的方式。
请求路径:/carts/list
请求参数:Integer[] cids, HttpSession session
请求类型:GET
响应结果:JsonResult<List<CartVO>>
3.3 处理请求 1.在CartController类中添加处理请求的getVOByCids()方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 @GetMapping("list") public JsonResult<List<CartVO>> getVOByCids(Integer[] cids, HttpSession session) { if (cids==null ){ cids= new Integer[]{0 }; } Integer uid = getUidFromSession(session); List<CartVO> data = cartService.getVOByCids(uid, cids); return new JsonResult<>(OK, data); }
2.完成后启动项目,先登录再访问http://localhost:8080/carts/list?cids=7&cids=8&cids=13&cids=14&cids=17地址进行测试。
4 显示确认订单页-前端页面 4.1 显示勾选的购物车数据-前端页面 1.在orderConfirm.html页面的head标签里注释掉引入外部的orderConfirm.js文件。
1 <!-- <script src ="../js/orderConfirm.js" type ="text/javascript" charset ="utf-8" > </script > -->
2.在orderConfirm.html页面中检查必要控件的属性是否添加,如果已添加无需重复添加。
3.在orderConfirm.html页面中的body标签内的最后添加srcipt标签并在标签内部添加处理购物车“订单商品信息”列表展示的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 <script type="text/javascript" > $(document ).ready(function ( ) { showCartList(); }); function showCartList ( ) { $("#cart-list" ).empty(); $.ajax({ url : "/carts/list" , data : location.search.substr(1 ), type : "GET" , dataType : "JSON" , success : function (json ) { let list = json.data; console .log("count=" + list.length); let allCount = 0 ; let allPrice = 0 ; for (let i = 0 ; i < list.length; i++) { console .log(list[i].title); let tr = '<tr>' + '<td><img src="..#{image}collect.png" class="img-responsive" /></td>' + '<td><input type="hidden" name="cids" value="#{cid}" />#{title}</td>' + '<td>¥<span>#{realPrice}</span></td>' + '<td>#{num}</td>' + '<td>¥<span>#{totalPrice}</span></td>' + '</tr>' ; tr = tr.replace(/#{cid}/g , list[i].cid); tr = tr.replace(/#{image}/g , list[i].image); tr = tr.replace(/#{title}/g , list[i].title); tr = tr.replace(/#{realPrice}/g , list[i].realPrice); tr = tr.replace(/#{num}/g , list[i].num); tr = tr.replace(/#{totalPrice}/g , list[i].realPrice * list[i].num); $("#cart-list" ).append(tr); allCount += list[i].num; allPrice += list[i].realPrice * list[i].num; } $("#all-count" ).html(allCount); $("#all-price" ).html(allPrice); } }); } </script>
4.完成后启动项目,先登录再访问http://localhost:8080/web/cart.html页面,勾选商品再点击“结算”按钮进行测试。
4.2 显示选择收货地址-前端页面 1.在orderConfirm.html页面中的body标签内的srcipt标签中添加获取收货地址列表方法的定义。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 function showAddressList ( ) { $("#address-list" ).empty(); $.ajax({ url : "/addresses" , type : "GET" , dataType : "JSON" , success : function (json ) { let list = json.data; console .log("count=" + list.length); for (let i = 0 ; i < list.length; i++) { console .log(list[i].name); let opt = '<option value="#{aid}">#{name} | #{tag} | #{province}#{city}#{area}#{address} | #{phone}</option>' ; opt = opt.replace(/#{aid}/g , list[i].aid); opt = opt.replace(/#{tag}/g , list[i].tag); opt = opt.replace("#{name}" , list[i].name); opt = opt.replace("#{province}" , list[i].provinceName); opt = opt.replace("#{city}" , list[i].cityName); opt = opt.replace("#{area}" , list[i].areaName); opt = opt.replace("#{address}" , list[i].address); opt = opt.replace("#{phone}" , list[i].phone); $("#address-list" ).append(opt); } } }); }
2.在orderConfirm.html页面中的body标签内的srcipt标签中添加展示收货地址列表方法的调用。
1 2 3 4 5 6 <script type="text/javascript" > $(document ).ready(function ( ) { showAddressList(); showCartList(); }); </script>
3.完成后启动项目,先登录再访问http://localhost:8080/web/orderConfirm.html页面进行测试。
创建订单 1 订单-创建数据表 1.使用use命令先选中store数据库。
2.在store数据库中创建t_order和t_order_item数据表。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 CREATE TABLE t_order ( oid INT AUTO_INCREMENT COMMENT '订单id', uid INT NOT NULL COMMENT '用户id', recv_name VARCHAR(20) NOT NULL COMMENT '收货人姓名', recv_phone VARCHAR(20) COMMENT '收货人电话', recv_province VARCHAR(15) COMMENT '收货人所在省', recv_city VARCHAR(15) COMMENT '收货人所在市', recv_area VARCHAR(15) COMMENT '收货人所在区', recv_address VARCHAR(50) COMMENT '收货详细地址', total_price BIGINT COMMENT '总价', status INT COMMENT '状态:0-未支付,1-已支付,2-已取消,3-已关闭,4-已完成', order_time DATETIME COMMENT '下单时间', pay_time DATETIME COMMENT '支付时间', created_user VARCHAR(20) COMMENT '创建人', created_time DATETIME COMMENT '创建时间', modified_user VARCHAR(20) COMMENT '修改人', modified_time DATETIME COMMENT '修改时间', PRIMARY KEY (oid) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE t_order_item ( id INT AUTO_INCREMENT COMMENT '订单中的商品记录的id', oid INT NOT NULL COMMENT '所归属的订单的id', pid INT NOT NULL COMMENT '商品的id', title VARCHAR(100) NOT NULL COMMENT '商品标题', image VARCHAR(500) COMMENT '商品图片', price BIGINT COMMENT '商品价格', num INT COMMENT '购买数量', created_user VARCHAR(20) COMMENT '创建人', created_time DATETIME COMMENT '创建时间', modified_user VARCHAR(20) COMMENT '修改人', modified_time DATETIME COMMENT '修改时间', PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2 订单-创建实体类 1.在com.amh.entity包下创建Order实体类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Order extends BaseEntity implements Serializable { private Integer oid; private Integer uid; private String recvName; private String recvPhone; private String recvProvince; private String recvCity; private String recvArea; private String recvAddress; private Long totalPrice; private Integer status; private Date orderTime; private Date payTime; }
2.在com.amh.entity包下创建OrderItem实体类。
1 2 3 4 5 6 7 8 9 10 11 12 public class OrderItem extends BaseEntity implements Serializable { private Integer id; private Integer oid; private Integer pid; private String title; private String image; private Long price; private Integer num; }
3 订单-持久层 3.1 规划需要执行的SQL语句 1.插入订单数据的SQL语句大致是。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 INSERT INTO t_order ( uid, recv_name, recv_phone, recv_province, recv_city, recv_area, recv_address, total_price, status, order_time, pay_time, created_user, created_time, modified_user, modified_time ) VALUES ( #对应字段的值列表 )
2.插入订单商品数据的SQL语句大致是。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 INSERT INTO t_order_item ( oid, pid, title, image, price, num, created_user, created_time, modified_user, modified_time ) VALUES ( #对应字段的值列表 )
3.2 接口与抽象方法 在com.amh.mapper包下创建OrderMapper接口并在接口中添加抽象方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public interface OrderMapper { Integer insertOrder (Order order) ; Integer insertOrderItem (OrderItem orderItem) ; }
3.3 配置SQL映射 1.在main\resources\mapper文件夹下创建OrderMapper.xml文件,并添加抽象方法的映射。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="com.cy.store.mapper.OrderMapper" > <insert id ="insertOrder" useGeneratedKeys ="true" keyProperty ="oid" > INSERT INTO t_order ( uid, recv_name, recv_phone, recv_province, recv_city, recv_area, recv_address, total_price,status, order_time, pay_time, created_user, created_time, modified_user, modified_time ) VALUES ( #{uid}, #{recvName}, #{recvPhone}, #{recvProvince}, #{recvCity}, #{recvArea}, #{recvAddress}, #{totalPrice}, #{status}, #{orderTime}, #{payTime}, #{createdUser}, #{createdTime}, #{modifiedUser}, #{modifiedTime} ) </insert > <insert id ="insertOrderItem" useGeneratedKeys ="true" keyProperty ="id" > INSERT INTO t_order_item ( oid, pid, title, image, price, num, created_user, created_time, modified_user, modified_time ) VALUES ( #{oid}, #{pid}, #{title}, #{image}, #{price}, #{num}, #{createdUser}, #{createdTime}, #{modifiedUser}, #{modifiedTime} ) </insert > </mapper >
2.在com.amh.mapper包下创建OrderMapperTests测试类,并添加测试方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 @RunWith(SpringRunner.class) @SpringBootTest public class OrderMapperTests { @Autowired private OrderMapper orderMapper; @Test public void insertOrder () { Order order = new Order(); order.setUid(31 ); order.setRecvName("小王" ); Integer rows = orderMapper.insertOrder(order); System.out.println("rows=" + rows); } @Test public void insertOrderItem () { OrderItem orderItem = new OrderItem(); orderItem.setOid(1 ); orderItem.setPid(2 ); orderItem.setTitle("高档铅笔" ); Integer rows = orderMapper.insertOrderItem(orderItem); System.out.println("rows=" + rows); } }
4 订单-业务层 4.1 规划异常
说明 :无异常。
4.2 接口与抽象方法 1.由于处理过程中还需要涉及收货地址数据的处理,所以需要先在IAddressService接口中添加getByAid()方法。
1 2 3 4 5 6 7 Address getByAid (Integer aid, Integer uid) ;
2.在AddressServiceImpl类中实现接口中的getByAid()抽象方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Override public Address getByAid (Integer aid, Integer uid) { Address address = addressMapper.findByAid(aid); if (address == null ) { throw new AddressNotFoundException("尝试访问的收货地址数据不存在" ); } if (!address.getUid().equals(uid)) { throw new AccessDeniedException("非法访问" ); } address.setProvinceCode(null ); address.setCityCode(null ); address.setAreaCode(null ); address.setCreatedUser(null ); address.setCreatedTime(null ); address.setModifiedUser(null ); address.setModifiedTime(null ); return address; }
3.在com.amh.service包下创建IOrderService业务层接口并添加抽象方法。
1 2 3 4 5 6 7 8 9 10 11 12 public interface IOrderService { Order create (Integer aid, Integer[] cids, Integer uid, String username) ; }
4.3 实现抽象方法 1.在com.amh.service.impl包下创建OrderServiceImpl业务层实现类并实现IOrderService接口;在类定义之前添加@Service注解,在类中添加OrderMapper订单持久层对象、IAddressService处理收货地址对象、ICartService购物车数据对象,并都添加@Autowired注解进行修饰。
1 2 3 4 5 6 7 8 9 10 11 12 @Service public class OrderServiceImpl implements IOrderService { @Autowired private OrderMapper orderMapper; @Autowired private IAddressService addressService; @Autowired private ICartService cartService; }
2.在OrderServiceImpl类中重写父接口中的create()抽象方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 @Transactional @Override public Order create (Integer aid, Integer[] cids, Integer uid, String username) { }
3.OrderServiceImpl类中的create()方法具体逻辑代码实现见下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 @Transactional @Override public Order create (Integer aid, Integer[] cids, Integer uid, String username) { Date now = new Date(); List<CartVO> carts = cartService.getVOByCids(uid, cids); long totalPrice = 0 ; for (CartVO cart : carts) { totalPrice += cart.getRealPrice() * cart.getNum(); } Order order = new Order(); order.setUid(uid); Address address = addressService.getByAid(aid, uid); order.setRecvName(address.getName()); order.setRecvPhone(address.getPhone()); order.setRecvProvince(address.getProvinceName()); order.setRecvCity(address.getCityName()); order.setRecvArea(address.getAreaName()); order.setRecvAddress(address.getAddress()); order.setTotalPrice(totalPrice); order.setStatus(0 ); order.setOrderTime(now); order.setCreatedUser(username); order.setCreatedTime(now); order.setModifiedUser(username); order.setModifiedTime(now); Integer rows1 = orderMapper.insertOrder(order); if (rows1 != 1 ) { throw new InsertException("插入订单数据时出现未知错误,请联系系统管理员" ); } for (CartVO cart : carts) { OrderItem item = new OrderItem(); item.setOid(order.getOid()); item.setPid(cart.getPid()); item.setTitle(cart.getTitle()); item.setImage(cart.getImage()); item.setPrice(cart.getRealPrice()); item.setNum(cart.getNum()); item.setCreatedUser(username); item.setCreatedTime(now); item.setModifiedUser(username); item.setModifiedTime(now); Integer rows2 = orderMapper.insertOrderItem(item); if (rows2 != 1 ) { throw new InsertException("插入订单商品数据时出现未知错误,请联系系统管理员" ); } } return order; }
4.在com.cy.store.service测试包下创建OrderServiceTests测试类,并添加create()方法进行功能测试。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @RunWith(SpringRunner.class) @SpringBootTest public class OrderServiceTests { @Autowired private IOrderService orderService; @Test public void create () { try { Integer aid = 21 ; Integer[] cids = {4 , 5 , 6 ,7 }; Integer uid = 31 ; String username = "订单管理员" ; Order order = orderService.create(aid, cids, uid, username); System.out.println(order); } catch (ServiceException e) { System.out.println(e.getClass().getSimpleName()); System.out.println(e.getMessage()); } } }
5 订单-控制器层 5.1 处理异常
说明 :无异常。
5.2 设计请求 设计用户提交的请求,并设计响应的方式。
请求路径:/orders/create
请求参数:Integer aid, Integer[] cids, HttpSession session
请求类型:POST
响应结果:JsonResult<Order>
5.3 处理请求 1.在com.amh.controller包下创建OrderController类,并继承自BaseController类;并在类前添加@RequestMapping(“orders”)注解和@RestController注解;在类中声明IOrderService业务对象,然后添加@Autowired注解修饰;最后在类中添加处理请求的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @RestController @RequestMapping("orders") public class OrderController extends BaseController { @Autowired private IOrderService orderService; @RequestMapping("create") public JsonResult<Order> create (Integer aid, Integer[] cids, HttpSession session) { Integer uid = getUidFromSession(session); String username = getUsernameFromSession(session); Order data = orderService.create(aid, cids, uid, username); return new JsonResult<Order>(OK, data); } }
2.完成后启动项目,先登录再访问http://localhost:8080/orders/create?aid=21&cids=4&cids=5&cids=6&cids=7进行测试。
6 订单-前端页面 1.在orderConfirm.xml页面中的body标签内的script标签内添加“在线支付”按钮的点击时间。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 $("#btn-create-order" ).click(function ( ) { $.ajax({ url : "/orders/create" , data : $("#form-create-order" ).serialize(), type : "POST" , dataType : "JSON" , success : function (json ) { if (json.state == 200 ) { alert("创建订单成功!" ); console .log(json.data); } else { alert("创建订单失败!" + json.message); } }, error : function (xhr ) { alert("您的登录信息已经过期,请重新登录!HTTP响应码:" + xhr.status); location.href = "login.html" ; } }); });
2.完成后启动项目,先登录再访问http://localhost:8080/web/cart.html页面,勾选购车中的商品,再点击“结算”按钮,最后在订单确认页中点击“在线支付”按钮进行功能的测试。
AOP 1 Spring AOP AOP:面向切面(Aspect)编程。AOP并不是Spring框架的特性,只是Spring很好的支持了AOP。
如果需要在处理每个业务时,都执行特定的代码,则可以假设在整个数据处理流程中存在某个切面,切面中可以定义某些方法,当处理流程执行到切面时,就会自动执行切面中的方法。最终实现的效果就是:只需要定义好切面方法,配置好切面的位置(连接点),在不需要修改原有数据处理流程的代码的基础之上,就可以使得若干个流程都执行相同的代码。
2 切面方法 1.切面方法的访问权限是public。
2.切面方法的返回值类型可以是void或Object,如果使用的注解是@Around时,必须使用Object作为返回值类型,并返回连接点方法的返回值;如果使用的注解是@Before或@After等其他注解时,则自行决定。
3.切面方法的名称可以自定义。
4.切面方法的参数列表中可以添加ProceedingJoinPoint接口类型的对象,该对象表示连接点,也可以理解调用切面所在位置对应的方法的对象,如果使用的注解是@Around时,必须添加该参数,反之则不是必须添加。
3 统计业务方法执行时长 1.在使用Spring AOP编程时,需要先在pom.xml文件中添加两个关于AOP的依赖aspectjweaver和aspectjtools。
1 2 3 4 5 6 7 8 <dependency > <groupId > org.aspectj</groupId > <artifactId > aspectjweaver</artifactId > </dependency > <dependency > <groupId > org.aspectj</groupId > <artifactId > aspectjtools</artifactId > </dependency >
2.在com.cy.store.aop包下创建TimerAspect切面类,在类之前添加@Aspect和@Component注解修饰。
1 2 3 4 5 6 7 8 9 10 11 package com.cy.store.aop;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.springframework.stereotype.Component;@Aspect @Component public class TimerAspect {}
3.在类中添加切面方法around(ProceedingJoinPoint pjp)。
1 2 3 4 5 6 7 8 9 10 11 12 public Object around (ProceedingJoinPoint pjp) throws Throwable { long start = System.currentTimeMillis(); Object result = pjp.proceed(); long end = System.currentTimeMillis(); System.err.println("耗时:" + (end - start) + "ms." ); return result; }
4.最后需要在方法之前添加@Around注解,以配置连接点,即哪些方法需要应用该切面。
1 @Around("execution(* com.cy.store.service.impl.*.*(..))")
5.启动项目,在前端浏览器访问任意一个功能模块进行功能的测试。
完结撒花~~~