淘淘商城第六天
讲师:入云龙
1 首页大广告位的实现分析
首页的内容需要动态管理,需要后台管理功能。
抽取首页展示内容的共性:
1、有一张图片
2、有一个连接
3、有一个标题
4、有链接的提示
5、价格
需要把内容进行分类,分类应该是一个树形结构。
在展示首页时,可以根据分类取内容信息,把内容展示到页面。
在后台管理内容及内容分类的系统就叫做cms系统。
2 Cms系统
先实现内容的分类管理再实现内容管理。
2.1 内容分类管理
2.1.1 内容分类初始化
2.1.1.1 需求分析
初始化树形视图的url:/content/category/list
参数是id,当前节点id属性,应该根据此id查询子节点列表。
返回值:包含id、text、state三个属性的json数据列表
2.1.1.2 Dao层
需要一张表存储内容数据:字段:标题、url、子标题、image、内容分类id等等
需要一张表存储内容的分类信息,树形结构的表。
表结构:
内容分类表
内容表
Sql语句:
根据parentid查询节点列表
SELECT * FROM `tb_content_category` WHEREparent_id = 30;
单表查询可以实现逆向工程生成的代码。
2.1.1.3 Service层
功能:接收parentid。根据parentid查询节点列表,返回返回一个EasyUI异步Tree要求的节点列表。每个节点包含三个属性id、text、state三个属性。可以使用EUTreeNode。
参数:id
返回值:List<EUTreeNode>
@Service public class ContentCategoryServiceImpl implements ContentCategoryService {
@Autowired private TbContentCategoryMappercontentCategoryMapper; @Override public List<EUTreeNode> getCategoryList(longparentId) { //根据parentid查询节点列表 TbContentCategoryExample example =new TbContentCategoryExample(); Criteria criteria = example.createCriteria(); criteria.andParentIdEqualTo(parentId); //执行查询 List<TbContentCategory> list =contentCategoryMapper.selectByExample(example); List<EUTreeNode> resultList =new ArrayList<>(); for (TbContentCategorytbContentCategory : list) { //创建一个节点 EUTreeNode node =new EUTreeNode(); node.setId(tbContentCategory.getId()); node.setText(tbContentCategory.getName()); node.setState(tbContentCategory.getIsParent()?"closed":"open");
resultList.add(node); } returnresultList; }
} |
2.1.1.4 Controller
接收页面传递过来的parentid,根据parentid查询节点列表。返回List<EUTreeNode>。需要响应json数据。
@Controller @RequestMapping("/content/category") public class ContentCategoryController {
@Autowired private ContentCategoryServicecontentCategoryService;
@RequestMapping("/list") @ResponseBody public List<EUTreeNode> getContentCatList(@RequestParam(value="id", defaultValue="0")LongparentId) { List<EUTreeNode> list =contentCategoryService.getCategoryList(parentId); returnlist; } } |
2.1.2 内容分类添加
2.1.2.1 需求分析
请求的url:/content/category/create
参数:
1、parentId父节点id
2、name:当前节点的名称
返回值:TaotaoResult。其中包含节点pojo对象。
2.1.2.2 Dao层
可以使用逆向工程生成的代码
2.1.2.3 Service层
功能:接收两个参数parentId父节点id、name:当前节点的名称。向tb_content_category表中添加一条记录。返回TaoTaoResult包含记录的pojo对象。
需要返回主键信息:
需要修改mapper文件,返回主键信息。
方式一:
方式二:
<insert id="insert" parameterType="com.taotao.pojo.TbContentCategory" useGeneratedKeys="true"keyProperty="id"> insert into tb_content_category (id, parent_id, name, status, sort_order, is_parent, created, updated) values (#{id,jdbcType=BIGINT}, #{parentId,jdbcType=BIGINT}, #{name,jdbcType=VARCHAR}, #{status,jdbcType=INTEGER}, #{sortOrder,jdbcType=INTEGER}, #{isParent,jdbcType=BIT}, #{created,jdbcType=TIMESTAMP}, #{updated,jdbcType=TIMESTAMP}) </insert> |
@Override public TaotaoResult insertContentCategory(longparentId, String name) {
//创建一个pojo TbContentCategory contentCategory =new TbContentCategory(); contentCategory.setName(name); contentCategory.setIsParent(false); //'状态。可选值:1(正常),2(删除)', contentCategory.setStatus(1); contentCategory.setParentId(parentId); contentCategory.setSortOrder(1); contentCategory.setCreated(new Date()); contentCategory.setUpdated(new Date()); //添加记录 contentCategoryMapper.insert(contentCategory); //查看父节点的isParent列是否为true,如果不是true改成true TbContentCategory parentCat =contentCategoryMapper.selectByPrimaryKey(parentId); //判断是否为true if(!parentCat.getIsParent()) { parentCat.setIsParent(true); //更新父节点 contentCategoryMapper.updateByPrimaryKey(parentCat); } //返回结果 return TaotaoResult.ok(contentCategory); } |
2.1.2.4 Controller层
接收两个参数parentid、name。调用Service添加记录。返回TaotaoResult。应该返回json数据。
@RequestMapping("/create") @ResponseBody public TaotaoResult createContentCategory(LongparentId, String name) { TaotaoResult result =contentCategoryService.insertContentCategory(parentId,name); returnresult; } |
2.1.3 内容分类删除
需求分析:
请求的url:
/content/category/delete/
参数:
1、parentId
2、Id
返回值:TaotaoResult
业务逻辑:
接收parentid、id两个参数。删除id对应的记录。需要判断parentid对应的记录下是否有子节点。如果没有子节点。需要把parentid对应的记录的isparent改成false。
注意:删除直接是物理删除。
2.1.4 重命名节点
1、
2、当编辑完成后会触发onAfterEdit事件。
请求的url:/content/category/update
参数:id、name
返回值:返回TaotaoResult。Json格式
业务逻辑:根据id更新记录的name列即可。
2.2 内容管理
内容管理表:
2.2.1 内容列表
需求分析
请求url:/content/query/list
参数:page、rows、categoryId
返回值:EUDataGridResult
Total、rows:内容pojo列表。
业务逻辑:
根据内容分类id查询内容列表。需要实现分页。返回EUDataGridResult
2.2.2 内容添加
需求分析:
图片上传初始化:
内容表单提交:
请求的url:/content/save
请求的方法:post
请求内容:表单中的内容。
返回的结果:TaotaoResult。
2.2.3 Dao层
向tb_content表中插入数据。可以使用逆向工程生成的代码。
2.2.4 Service层
接收表tb_content对应的pojo对象。把pojo对象插入到tb_content表中。
返回TaotaoResult。
@Service public class ContentServiceImpl implements ContentService {
@Autowired private TbContentMapper contentMapper;
@Override public TaotaoResult insertContent(TbContentcontent) { //补全pojo内容 content.setCreated(new Date()); content.setUpdated(new Date()); contentMapper.insert(content);
return TaotaoResult.ok(); }
} |
2.2.5 Controller层
接收表单中的内容,使用pojo接收。要求pojo的属性要和表单中的name一致。调用Service插入内容信息。返回TaotaoResult。Json格式的数据。
@Controller @RequestMapping("/content") public class ContentController {
@Autowired private ContentServicecontentService;
@RequestMapping("/save") @ResponseBody public TaotaoResult insertContent(TbContent content) { TaotaoResult result =contentService.insertContent(content); returnresult; } } |
3 展示商城首页大广告位
3.1 首页大广告方案
前端系统获取后端系统提供的接口,如何获取?
3.1.1 方案1
jsonp跨域请求
需要当首页加载完毕后,大广告位就应该显示。没有触发事件。不是太合适。
优点:不需要二次请求,页面直接加载内容数据。减少门户系统的压力。
缺点:需要延迟加载。不利于seo优化。
3.1.2 第二种方案:
优点:有利于seo优化。可以在taotao-portal中对数据进行加工。
缺点:系统直接需要调用服务查询内容信息。多了一次http请求。
系统直接服务的调用,需要使用httpclient来实现。Taotao-portal和taotao-rest是在同一个局域网内部。速度非常快,调用时间可以忽略不计。
展示首页内容功能,使用方案二实现。
3.2 展示流程
3.3 内容服务发布
3.3.1 需求分析
根据内容的分类id查询内容列表,从tb_content表中查询。服务是一个restFul形式的服务。使用http协议传递json格式的数据。
3.3.2 Dao层
从tb_content表中查询,根据内容分类id查询。是单表查询。可以使用逆向工程生成的代码。
3.3.3 Service层
接收内容分类id,根据分类id查询分类列表。返回一个内容pojo列表。
参数:分类id
返回值:pojo列表
@Service public class ContentServiceImpl implements ContentService {
@Autowired private TbContentMappercontentMapper; @Override public List<TbContent> getContentList(longcontentCid) { //根据内容分类id查询内容列表 TbContentExample example =new TbContentExample(); Criteria criteria = example.createCriteria(); criteria.andCategoryIdEqualTo(contentCid); //执行查询 List<TbContent> list =contentMapper.selectByExample(example);
returnlist; }
} |
3.3.4 Controller层
发布服务。接收查询参数。Restful风格内容分类id应该从url中取。
/rest/content/list/{contentCategoryId}
从url中取内容分类id,调用Service查询内容列表。返回内容列表。返回一个json格式的数据。可以使用TaotaoResult包装此列表。
@Controller @RequestMapping("/content") public class ContentController {
@Autowired private ContentServicecontentService;
@RequestMapping("/list/{contentCategoryId}") @ResponseBody public TaotaoResult getContentList(@PathVariable LongcontentCategoryId) { try { List<TbContent> list =contentService.getContentList(contentCategoryId); return TaotaoResult.ok(list); } catch (Exceptione) { e.printStackTrace(); return TaotaoResult.build(500, ExceptionUtil.getStackTrace(e)); } } } |
3.4 Httpclient的使用
3.4.1 什么是httpclient
HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。
HTTP 协议可能是现在 Internet 上使用得最多、最重要的协议了,越来越多的 Java 应用程序需要直接通过 HTTP 协议来访问网络资源。虽然在 JDK 的 java net包中已经提供了访问 HTTP 协议的基本功能,但是对于大部分应用程序来说,JDK 库本身提供的功能还不够丰富和灵活。HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。
下载地址:
以下列出的是 HttpClient 提供的主要的功能,要知道更多详细的功能可以参见 HttpClient 的主页。
(1)实现了所有 HTTP 的方法(GET,POST,PUT,HEAD 等)
(2)支持自动转向
(3)支持 HTTPS 协议
(4)支持代理服务器等
3.4.2 添加依赖
需要把httpclient的jar包添加到工程中。只需要在工程中添加httpclient的依赖。
3.4.3 使用方法
3.4.3.1 使用httpclient执行get请求
@Test public void doGet() throws Exception { //创建一个httpclient对象 CloseableHttpClient httpClient = HttpClients.createDefault(); //创建一个GET对象 HttpGet get = new HttpGet("http://www.sogou.com"); //执行请求 CloseableHttpResponse response =httpClient.execute(get); //取响应的结果 intstatusCode = response.getStatusLine().getStatusCode(); System.out.println(statusCode); HttpEntity entity = response.getEntity(); String string = EntityUtils.toString(entity,"utf-8"); System.out.println(string); //关闭httpclient response.close(); httpClient.close(); } |
3.4.3.2 执行get请求带参数
@Test public void doGetWithParam() throws Exception{ //创建一个httpclient对象 CloseableHttpClient httpClient = HttpClients.createDefault(); //创建一个uri对象 URIBuilder uriBuilder = new URIBuilder("http://www.sogou.com/web"); uriBuilder.addParameter("query", "花千骨"); HttpGet get = new HttpGet(uriBuilder.build()); //执行请求 CloseableHttpResponse response =httpClient.execute(get); //取响应的结果 intstatusCode = response.getStatusLine().getStatusCode(); System.out.println(statusCode); HttpEntity entity = response.getEntity(); String string = EntityUtils.toString(entity,"utf-8"); System.out.println(string); //关闭httpclient response.close(); httpClient.close(); } |
3.4.3.3 使用httpclient执行post请求
@Test public void doPost() throws Exception { CloseableHttpClient httpClient = HttpClients.createDefault();
//创建一个post对象 HttpPost post = new HttpPost("http://localhost:8082/httpclient/post.html"); //执行post请求 CloseableHttpResponse response =httpClient.execute(post); String string = EntityUtils.toString(response.getEntity()); System.out.println(string); response.close(); httpClient.close();
} |
3.4.3.4 带参数post请求
@Test public void doPostWithParam() throws Exception{ CloseableHttpClient httpClient = HttpClients.createDefault();
//创建一个post对象 HttpPost post = new HttpPost("http://localhost:8082/httpclient/post.html"); //创建一个Entity。模拟一个表单 List<NameValuePair>kvList = new ArrayList<>(); kvList.add(new BasicNameValuePair("username","zhangsan")); kvList.add(new BasicNameValuePair("password","123"));
//包装成一个Entity对象 StringEntity entity = new UrlEncodedFormEntity(kvList,"utf-8"); //设置请求的内容 post.setEntity(entity);
//执行post请求 CloseableHttpResponse response =httpClient.execute(post); String string = EntityUtils.toString(response.getEntity()); System.out.println(string); response.close(); httpClient.close(); } |
3.4.4 Httpclient封装成工具类
其他项目也可能会用到httpclient,所以把工具类放到taotao-common中。
public class HttpClientUtil {
public static String doGet(String url, Map<String, String> param) {
// 创建Httpclient对象 CloseableHttpClient httpclient = HttpClients.createDefault();
String resultString = ""; CloseableHttpResponse response = null; try { // 创建uri URIBuilder builder = new URIBuilder(url); if (param != null) { for (String key : param.keySet()) { builder.addParameter(key, param.get(key)); } } URI uri = builder.build();
// 创建http GET请求 HttpGet httpGet = new HttpGet(uri);
// 执行请求 response = httpclient.execute(httpGet); // 判断返回状态是否为200 if (response.getStatusLine().getStatusCode() == 200) { resultString = EntityUtils.toString(response.getEntity(), "UTF-8"); } } catch (Exception e) { e.printStackTrace(); } finally { try { if (response != null) { response.close(); } httpclient.close(); } catch (IOException e) { e.printStackTrace(); } } return resultString; }
public static String doGet(String url) { return doGet(url, null); }
public static String doPost(String url, Map<String, String> param) { // 创建Httpclient对象 CloseableHttpClient httpClient = HttpClients.createDefault(); CloseableHttpResponse response = null; String resultString = ""; try { // 创建Http Post请求 HttpPost httpPost = new HttpPost(url); // 创建参数列表 if (param != null) { List<NameValuePair> paramList = new ArrayList<>(); for (String key : param.keySet()) { paramList.add(new BasicNameValuePair(key, param.get(key))); } // 模拟表单 UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList); httpPost.setEntity(entity); } // 执行http请求 response = httpClient.execute(httpPost); resultString = EntityUtils.toString(response.getEntity(), "utf-8"); } catch (Exception e) { e.printStackTrace(); } finally { try { response.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
return resultString; }
public static String doPost(String url) { return doPost(url, null); }
public static String doPostJson(String url, String json) { // 创建Httpclient对象 CloseableHttpClient httpClient = HttpClients.createDefault(); CloseableHttpResponse response = null; String resultString = ""; try { // 创建Http Post请求 HttpPost httpPost = new HttpPost(url); // 创建请求内容 StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON); httpPost.setEntity(entity); // 执行http请求 response = httpClient.execute(httpPost); resultString = EntityUtils.toString(response.getEntity(), "utf-8"); } catch (Exception e) { e.printStackTrace(); } finally { try { response.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
return resultString; } } |
4 大广告位展示
4.1 需求分析
需要创建一个json字符串传递给jsp:使用EL表达式${ad1} 接收
Json字符串如何传递给jsp:使用modelAndView对象把json字符串传递给jsp。
如何获得json字符串:获得一个广告位对应的内容列表,需要调用taotao-rest的服务。把列表转换成json数据格式要求的pojo对象列表。
需要使用httpclient调用taotao-rest的服务。
4.2 Dao层
没有
4.3 Service层
根据内容分类id查询分类的内容列表,需要使用httpclient调用taotao-rest的服务。得到一个json字符串。需要把字符串转换成java对象taotaoResult对象。从taotaoResult对象中取data属性,得到内容列表。把内容列表转换成jsp页面要求的json格式。返回一个json字符串。
参数:没有参数
返回值:json字符串。
@Service public class ContentServiceImpl implements ContentService {
@Value("${REST_BASE_URL}") private StringREST_BASE_URL; @Value("${REST_INDEX_AD_URL}") private StringREST_INDEX_AD_URL;
@Override public String getContentList() { //调用服务层的服务 String result = HttpClientUtil.doGet(REST_BASE_URL +REST_INDEX_AD_URL); //把字符串转换成TaotaoResult try { TaotaoResult taotaoResult = TaotaoResult.formatToList(result, TbContent.class); //取内容列表 List<TbContent> list =(List<TbContent>) taotaoResult.getData(); List<Map> resultList =new ArrayList<>(); //创建一个jsp页码要求的pojo列表 for (TbContenttbContent : list) { Map map =new HashMap<>(); map.put("src",tbContent.getPic()); map.put("height", 240); map.put("width", 670); map.put("srcB",tbContent.getPic2()); map.put("widthB", 550); map.put("heightB", 240); map.put("href",tbContent.getUrl()); map.put("alt",tbContent.getSubTitle()); resultList.add(map); } return JsonUtils.objectToJson(resultList); } catch (Exceptione) { e.printStackTrace(); }
returnnull; }
} |
4.4 Controller
展示首页返回一个逻辑视图,需要把首页大广告位的json数据传递给jsp。
@RequestMapping("/index") public String showIndex(Modelmodel) { String adJson = contentService.getContentList(); model.addAttribute("ad1", adJson);
return"index"; } |
效果