第04项目:淘淘商城(SpringMVC+Spring+Mybatis)【第九天】(商品详情页面实现)

https://pan.baidu.com/s/1bptYGAb#list/path=%2F&parentPath=%2Fsharelink389619878-229862621083040html

第04项目:淘淘商城(SpringMVC+Spring+Mybatis) 的学习实践总结【第五天】java

第04项目:淘淘商城(SpringMVC+Spring+Mybatis) 的学习实践总结【第六天】ajax

第04项目:淘淘商城(SpringMVC+Spring+Mybatis)【第七天】(redis缓存)redis

第04项目:淘淘商城(SpringMVC+Spring+Mybatis)【第八天】(solr服务器搭建、搜索功能实现)spring

第04项目:淘淘商城(SpringMVC+Spring+Mybatis)【第九天】(商品详情页面实现)json

 


 

1   课程计划

今天的内容:缓存

一、在taotao-portal工程中调用taotao-search工程发布的服务。实现商品搜索功能。服务器

二、点击商品的图片,打开商品详情页面app

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对象获得商品查询结果页面。

请求url:http://localhost:8082/search.html?q=查询条件

2.3   Service层

接收两个参数一、查询条件二、页码。调用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发布的服务,查询商品详情。

一、商品的基本信息

二、商品的描述

三、商品的规格

 

当用户请求商品详情页面时,只须要把商品基本信息展现出来,为了快速响应用户。商品的描述能够延迟加载,延迟一秒钟加载。商品的规格参数按需加载,当用户点击商品规格参数的标签页此时再加载。

 

3.2    服务发布

须要在taotao-rest工程中发布服务

一、取商品基本信息的服务

二、取商品描述的服务

三、取商品规格的服务

须要把商品信息添加到缓存中。设置商品的过时时间,过时时间为一天。须要缓存同步。

 

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);
    }

}
View Code ItemServiceImpl

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);
    }

}
public class ItemServiceImpl

 

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