第04项目:淘淘商城(SpringMVC+Spring+Mybatis)【第九天】(商品详情页面实现)
https://pan.baidu.com/s/1bptYGAb#list/path=%2F&parentPath=%2Fsharelink389619878-229862621083040
第04项目:淘淘商城(SpringMVC+Spring+Mybatis) 的学习实践总结【第五天】
第04项目:淘淘商城(SpringMVC+Spring+Mybatis) 的学习实践总结【第六天】
第04项目:淘淘商城(SpringMVC+Spring+Mybatis)【第七天】(redis缓存)
第04项目:淘淘商城(SpringMVC+Spring+Mybatis)【第八天】(solr服务器搭建、搜索功能实现)
第04项目:淘淘商城(SpringMVC+Spring+Mybatis)【第九天】(商品详情页面实现)
1 课程计划
今天的内容:
1、在taotao-portal工程中调用taotao-search工程发布的服务。实现商品搜索功能。
2、点击商品的图片,打开商品详情页面
a) 商品基本信息
b) 延迟加载商品详情。延迟一秒加载使用ajax
c) 商品的规格参数。按需加载,当用户点击商品规格参数tab页,加载ajax。
需求分析
用户在首页中输入查询条件,点击查询向taotao-portal发送请求,参数就是查询的条件,页码。Taoto-portal调用taotao-search发布的服务进行搜索,参数应该是查询条件和页码及每页显示的记录数(参数可选)。Taotao-search返回一个json格式的数据(TaotaoResult包装一个SearchResult对象)。Taotao-portal接收json数据需要把json数据转换成java对象。把java对象传递给jsp页面,jsp渲染java对象得到商品查询结果页面。
2.3 Service层
接收两个参数1、查询条件2、页码。调用taotao-search的搜索服务。接收返回的json数据,把json转换成java对象返回SearchResult对象。
@Service public class SearchServiceImpl implements SearchService { @Value("${SEARCH_BASE_URL}") private String SEARCH_BASE_URL; @Override public SearchResult search(String queryString, int page) { // 调用taotao-search的服务 // 查询参数 Map<String, String> param = new HashMap<>(); param.put("q", queryString); param.put("page", page + ""); try { // 调用服务得到JSON格式 String json = HttpClientUtil.doGet(SEARCH_BASE_URL, param); // 把字符串转换成java对象 TaotaoResult taotaoResult = TaotaoResult.formatToPojo(json, SearchResult.class); if (taotaoResult.getStatus() == 200) { SearchResult result = (SearchResult) taotaoResult.getData(); return result; } } catch (Exception e) { e.printStackTrace(); } // 程序正常执行走try{}代码块,必然会return rsult; // 此处的return null;只是为了这个方法返回值类型检查不报错 return null; } }
2.4 Controller层
功能:接收请求的参数查询条件和页码。调用Service查询商品列表得到SearchResult对象。
需要把
Query:回显的查询条件
totalPages:总页数
itemList:商品列表
Page:当前页码
传递到页面。返回一个逻辑视图search字符串。
//首页商品搜索Controller @Controller public class PortalSearchController { @Autowired private SearchService searchService; @RequestMapping("/search") public String search(@RequestParam("q")String queryString, @RequestParam(defaultValue="1")Integer page, Model model) { if (queryString != null) { try { //防止字符串乱码 queryString = new String(queryString.getBytes("iso8859-1"), "utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } SearchResult searchResult = searchService.search(queryString, page); //向页面传递参数 model.addAttribute("query", queryString); model.addAttribute("totalPages", searchResult.getPageCount()); model.addAttribute("itemList", searchResult.getItemList()); model.addAttribute("page", page); return "search"; } }
2.5 存在的问题
搜索结果中图片展示不出来,image字段中存储的图片是多张,使用逗号分隔。
修改方法:
Pojo:
package com.taotao.common.pojo; public class Item { private String id; private String title; private String sell_point; private long price; private String image; private String category_name; private String item_des; public String[] getImages() { if(image != null) { String[] images = image.split(","); return images; } return null; }
3 商品详情页面展示
3.1 需求分析
需要在taotao-portal中调用taotao-rest发布的服务,查询商品详情。
1、商品的基本信息
2、商品的描述
3、商品的规格
当用户请求商品详情页面时,只需要把商品基本信息展示出来,为了快速响应用户。商品的描述可以延迟加载,延迟一秒钟加载。商品的规格参数按需加载,当用户点击商品规格参数的标签页此时再加载。
3.2 服务发布
需要在taotao-rest工程中发布服务
1、取商品基本信息的服务
2、取商品描述的服务
3、取商品规格的服务
需要把商品信息添加到缓存中。设置商品的过期时间,过期时间为一天。需要缓存同步。
3.2.1 取商品基本信息
Dao层
查询的表tb_item
Service层
接收商品id,根据商品id查询商品基本信息。返回一个商品的pojo,使用taotaoResult包装返回。
/** * 商品信息管理Service * * @author kangy * */ @Service public class ItemServiceImpl implements ItemService { @Autowired private TbItemMapper itemMapper; @Override public TaotaoResult getItemBaseInfo(long itemId) { // 根据商品ID查询商品信息 TbItem item = itemMapper.selectByPrimaryKey(itemId); // 使用TaoTaoresult包装一下 return TaotaoResult.ok(item); } }
Controller层
接收商品id调用Service查询商品信息,返回商品对象,使用TaotaoResult包装。
Url:/rest/item/info/{itemId}
@RestController @RequestMapping("/item") public class ItemController { @Autowired private ItemService itemService; @RequestMapping("/info/{itemId}") public TaotaoResult getItemBaseInfo(@PathVariable Long itemId) { TaotaoResult result = itemService.getItemBaseInfo(itemId); return result; } }
添加缓存逻辑
Redis的hash类型中的key是不能设置过期时间。如果还需要对key进行分类可以使用折中的方案。
Key的命名方式:
Itheima:javaee16:01=刘备
Itheima:javaee16:02=张飞
商品key的定义:
基本信息:
REDIS_ITEM_KEY:商品id:base=json
描述:
REDIS_ITEM_KEY:商品id:desc=json
规格参数:
REDIS_ITEM_KEY:商品id:param=json
/** * 商品信息管理Service * * @author kangy * */ @Service public class ItemServiceImpl implements ItemService { @Autowired private TbItemMapper itemMapper; @Value("${REDIS_ITEM_KEY}") private String REDIS_ITEM_KEY; @Value("${REDIS_ITEM_EXPIRE}") private Integer REDIS_ITEM_EXPIRE; @Autowired private JedisClient jedisClient; @Override public TaotaoResult getItemBaseInfo(long itemId) { try { // 添加缓存逻辑 // 从缓存中取商品信息,商品id对应的信息 String json = jedisClient.get(REDIS_ITEM_KEY + ":" + itemId + ":base"); // 判断是否有值 // commons.lang3.StringUtils if (!StringUtils.isBlank(json)) { // 把json转换成java对象 TbItem item = JsonUtils.jsonToPojo(json, TbItem.class); return TaotaoResult.ok(item); } } catch (Exception e) { e.printStackTrace(); } // 根据商品ID查询商品信息 TbItem item = itemMapper.selectByPrimaryKey(itemId); // 使用TaoTaoresult包装一下 try { // 把商品信息写入缓存 jedisClient.set(REDIS_ITEM_KEY + ":" + itemId + ":base", JsonUtils.objectToJson(item)); // 设置key的有效期 jedisClient.expire(REDIS_ITEM_KEY + ":" + itemId + ":base", REDIS_ITEM_EXPIRE); } catch (Exception e) { e.printStackTrace(); } return TaotaoResult.ok(item); } }
3.2.2 取商品描述信息
根据商品id取商品描述信息。单表查询tb_item_desc。
Dao层
使用逆向工程
Service层
接收商品id根据商品id查询商品描述。返回商品描述的pojo。使用TaotaoResult包装。
需要添加缓存逻辑。
@Override public TaotaoResult getItemDesc(long itemId) { // 添加缓存逻辑 try { // 1.从缓存中取商品描述,商品id对应的信息 String json = jedisClient.get(REDIS_ITEM_KEY + ":" + itemId + ":desc"); // 判断是否有值 // commons.lang3.StringUtils if (!StringUtils.isBlank(json)) { // 把json转换成java对象 TbItemDesc itemDesc = JsonUtils.jsonToPojo(json, TbItemDesc.class); return TaotaoResult.ok(itemDesc); } } catch (Exception e) { e.printStackTrace(); } // 2.根据商品id查询商品描述 TbItemDesc itemDesc = itemDescMapper.selectByPrimaryKey(itemId); try { // 3.把商品描述写入缓存 jedisClient.set(REDIS_ITEM_KEY + ":" + itemId + ":desc", JsonUtils.objectToJson(itemDesc)); // 设置key的有效期 jedisClient.expire(REDIS_ITEM_KEY + ":" + itemId + ":desc", REDIS_ITEM_EXPIRE); } catch (Exception e) { e.printStackTrace(); } // 4.使用TaoTaoresult包装一下 return TaotaoResult.ok(itemDesc); }
3.2.3 取商品规格参数
需要从tb_item_param_item表中根据商品id取出商品的规格参数信息。返回pojo对象,使用TaotaoResult包装。
Service层
接收商品id调用mapper查询商品规格参数,返回规格参数pojo使用TaotaoResult包装。
添加缓存逻辑。
@Override public TaotaoResult getItemParam(long itemId) { // 添加缓存 try { // 添加缓存逻辑 // 从缓存中取商品信息,商品id对应的信息 String json = jedisClient.get(REDIS_ITEM_KEY + ":" + itemId + ":param"); // 判断是否有值 if (!StringUtils.isBlank(json)) { // 把json转换成java对象 TbItemParamItem paramItem = JsonUtils.jsonToPojo(json, TbItemParamItem.class); return TaotaoResult.ok(paramItem); } } catch (Exception e) { e.printStackTrace(); } // 根据商品id查询规格参数 // 设置查询条件 TbItemParamItemExample example = new TbItemParamItemExample(); TbItemParamItemExample.Criteria criteria = example.createCriteria(); criteria.andItemIdEqualTo(itemId); // 执行查询 List<TbItemParamItem> list = itemParamItemMapper.selectByExampleWithBLOBs(example); if (list != null && list.size() > 0) { TbItemParamItem paramItem = list.get(0); try { // 把商品信息写入缓存 jedisClient.set(REDIS_ITEM_KEY + ":" + itemId + ":param", JsonUtils.objectToJson(paramItem)); // 设置key的有效期 jedisClient.expire(REDIS_ITEM_KEY + ":" + itemId + ":param", REDIS_ITEM_EXPIRE); } catch (Exception e) { e.printStackTrace(); } return TaotaoResult.ok(paramItem); } return TaotaoResult.build(400, "无此商品规格"); }
3.3 使用taotao-portal调用服务
需求分析
当用户访问商品详情页面时,需要加载商品基本信息。延迟加载商品描述、按需加载商品的规格参数。
商品基本信息的查询
当商品页面展示时,数据已经到位。
请求的url:/item/{itemId}.html
接收商品id,调用taotao-rest的服务,查询商品的基本信息。得到一个json字符串。需要把json转换成java对象。然后在jsp页面渲染。
@Service public class ItemServiceImpl implements ItemService { @Value("${REST_BASE_URL}") private String REST_BASE_URL; @Value("${ITEM_INFO_URL}") private String ITEM_INFO_URL; @Override public TbItem getItemById(Long itemId) { try { //调用rest的服务查询商品基本信息 String json = HttpClientUtil.doGet(REST_BASE_URL + ITEM_INFO_URL + itemId); if(!StringUtils.isBlank(json)) { TaotaoResult taotaoResult = TaotaoResult.formatToPojo(json, ItemInfo.class); //需要判断并强转变量类型 if(taotaoResult.getStatus() == 200) { TbItem item = (TbItem) taotaoResult.getData(); return item; } } } catch (Exception e) { e.printStackTrace(); } return null; } }
Controller层
接收页面传递过来的商品id,调用Service查询商品基本信息。传递给jsp页面。返回逻辑视图,展示商品详情页面。
@Controller public class ItemController { @Autowired private ItemService itemService; @RequestMapping("/item/{itemId}") public String showItem(@PathVariable Long itemId, Model model) { TbItem item = itemService.getItemById(itemId); model.addAttribute("item", item); return "item"; } }
商品POJO:
package com.taotao.portal.pojo; import com.taotao.pojo.TbItem; public class ItemInfo extends TbItem { public String[] getImages() { String image = getImage(); if (image != null) { String[] images = image.split(","); return images; } return null; } }
商品描述延迟加载
当商品详情页面加载完毕后延迟一秒钟ajax请求商品详情。
请求的URL:/item/desc/{itemId}.html
参数:商品id
返回值:商品描述信息(html片段)
Service层
接收商品id,调用taotao-rest的服务根据商品id查询商品描述信息。得到json数据。把json转换成java对象。从java对象中把商品描述取出来。返回商品描述字符串。
参数:商品id
返回值:字符串(商品描述的html片段)
/** * 取商品描述 */ @Override public String getItemDescById(Long itemId) { try { // 调用rest的服务查询商品描述基本信息 String json = HttpClientUtil.doGet(REST_BASE_URL + ITEM_DESC_URL + itemId); // 转换成java对象 TaotaoResult taotaoResult = TaotaoResult.formatToPojo(json, TbItemDesc.class); if (taotaoResult.getStatus() == 200) { TbItemDesc itemDesc = (TbItemDesc) taotaoResult.getData(); // 取商品描述信息 String result = itemDesc.getItemDesc(); return result; } } catch (Exception e) { e.printStackTrace();// TODO: handle exception } return null; }
Controller层
接收商品id,调用Service查询商品的描述信息,返回一个字符串,是商品描述的片段。需要使用@ResponseBody。
@RequestMapping(value="/item/desc/{itemId}", produces=MediaType.TEXT_HTML_VALUE+";charset=utf-8") @ResponseBody public String getItemDesc(@PathVariable Long itemId) { String string = itemService.getItemDescById(itemId); return string; }
商品规格参数展示
按需加载。当用户点击规格参数tab页时触发一个单击事件,在事件中异步加载规格参数信息。规格参数内容是html片段。返回字符串。
@Override public String getItemParam(Long itemId) { try { String json = HttpClientUtil.doGet(REST_BASE_URL + ITEM_PARAM_URL + itemId); //把json转换成java对象 TaotaoResult taotaoResult = TaotaoResult.formatToList(json, TbItemParamItem.class); if (taotaoResult.getStatus() == 200) { TbItemParamItem itemParamItem = (TbItemParamItem) taotaoResult.getData(); String paramData = itemParamItem.getParamData(); //生成html // 把规格参数json数据转换成java对象 List<Map> jsonList = JsonUtils.jsonToList(paramData, Map.class); StringBuffer sb = new StringBuffer(); sb.append("<table cellpadding=\"0\" cellspacing=\"1\" width=\"100%\" border=\"0\" class=\"Ptable\">\n"); sb.append(" <tbody>\n"); for(Map m1:jsonList) { sb.append(" <tr>\n"); sb.append(" <th class=\"tdTitle\" colspan=\"2\">"+m1.get("group")+"</th>\n"); sb.append(" </tr>\n"); List<Map> list2 = (List<Map>) m1.get("params"); for(Map m2:list2) { sb.append(" <tr>\n"); sb.append(" <td class=\"tdTitle\">"+m2.get("k")+"</td>\n"); sb.append(" <td>"+m2.get("v")+"</td>\n"); sb.append(" </tr>\n"); } } sb.append(" </tbody>\n"); sb.append("</table>"); //返回html片段 return sb.toString(); } } catch (Exception e) { e.printStackTrace(); } return ""; }
Controller层
页面的ajax请求Controller,请求的url://item/param/{itemId}.html
响应一个字符串。规格参数的片段。@ResponseBody。
@RequestMapping(value="/item/param/{itemId}", produces=MediaType.TEXT_HTML_VALUE+";charset=utf-8") @ResponseBody public String getItemParam(@PathVariable Long itemId) { String string = itemService.getItemParam(itemId); return string; }
5.2 用户注册
5.2.1 需求分析
请求方法为post,客户端提交表单。包含
username //用户名
password //密码
phone //手机号
email //邮箱
接收参数调用mapper向user表中添加记录。返回taotaoResult对象。如果成功200失败400异常500.
5.2.2 Dao层
可以使用逆向工程生成代码
5.2.3 Service层
接收TbUser对象,补全user的属性。向tb_user表插入记录。返回taoTaoResult。
@Override public TaotaoResult createUser(TbUser user) { user.setUpdated(new Date()); user.setCreated(new Date()); //spring框架的MD5工具加密 user.setPassword(DigestUtils.md5DigestAsHex(user.getPassword().getBytes())); userMapper.insert(user); return TaotaoResult.ok(); }
Controller层
//创建用户的方法 @RequestMapping("/register") public TaotaoResult createUser(TbUser user) { try { TaotaoResult result = userService.createUser(user); return result; } catch (Exception e) { return TaotaoResult.build(500, ExceptionUtil.getStackTrace(e)); } }
==========================================================
参考资料:
end