parent_id 表示节点的父节点。 上表的设计可以展示为树形结构展开类目。
在taotao-manager-web项目中的jsp下的item-add.jsp中:
我们在该文件中使用 【Ctrl + F】 进行搜索,没有找到事件绑定的样式class(selectItemCat),那我们在Eclipse使用【文件搜索】功能,步骤:Search --> File..,如下:
我们找到了class绑定事件元素处理的js文件common.js,如下:
我们发现:在item-add.jsp中我们并没有引用common.js,但由于在首页index.jsp中我们已经引入了,所以item-add.jsp作为首页index.jsp一个片段
,所以在item-add.jsp中我们同样可以使用common.js。
展示商品分类列表,使用EasyUI的tree控件展示。如下:
详解如下:
初始化tree请求的url:/item/cat/list 参数:id 父节点的id值。 初始化tree时只需要把第一级节点展示,子节点异步加载展示。 返回值:json格式数据 [{ "id": 1, "text": "Node 1", "state": "closed" },{ "id": 2, "text": "Node 2", "state": "closed" }] state:如果节点下有子节点则state的值为"closed",如果节点下没有子节点则state的值为"open"。
所以我们需要创建一个pojo来描述tree的节点信息,包含三个属性id、text、state。 放到taotao-common工程中。由于是服务端响应回来的pojo数据,所以需要实现序列化接口。 EasyUITreeNode.java
package com.taotao.common.pojo; import java.io.Serializable; /** * 类目查询时的返回的数据类 * <p>Title: EasyUITreeNode</p> * <p>Description: 用于描述tree的节点信息</p> * <p>Company: www.itheima.com</p> * @author 黑泽 * @date 2018年11月12日下午5:01:23 * @version 1.0 */ public class EasyUITreeNode implements Serializable { private static final long serialVersionUID = 1L; private Long id; private String text; private String state; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getText() { return text; } public void setText(String text) { this.text = text; } public String getState() { return state; } public void setState(String state) { this.state = state; } @Override public String toString() { return "EasyUITreeNode [id=" + id + ", text=" + text + ", state=" + state + "]"; } }
查询的表: tb_item_cat 查询的列: id、name、is_parent 查询条件: parentId
Dao层只需要查询商品分类表tb_item_cat即可,属于单表查询,单表查询我们没有必要自己写Mapper了,使用Mybatis逆向工程生成的Mapper即可。
参数:
Long parentId
业务逻辑:
1、根据parentId查询节点列表。
2、转换成EasyUITreeNode列表。
3、返回。
返回值:
List<EasyUITreeNode>
先写接口,在taotao-manager-interface工程中:
package com.taotao.service; import java.util.List; import com.taotao.common.pojo.EasyUITreeNode; /** * 商品类目管理接口 * <p>Title: ItemCatService</p> * <p>Description: </p> * <p>Company: www.itheima.com</p> * @author 黑泽 * @date 2018年11月12日下午8:15:24 * @version 1.0 */ public interface ItemCatService { /** * 根据商品类目的父节点id,查询该节点的子类目列表 * <p>Title: getItemCatList</p> * <p>Description: </p> * @param parentId * @return */ List<EasyUITreeNode> getItemCatList(Long parentId); }
再写实现类,在taotao-manager-service工程中:
package com.taotao.service.impl; import java.util.ArrayList; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.taotao.common.pojo.EasyUITreeNode; import com.taotao.mapper.TbItemCatMapper; import com.taotao.pojo.TbItemCat; import com.taotao.pojo.TbItemCatExample; import com.taotao.pojo.TbItemCatExample.Criteria; import com.taotao.service.ItemCatService; /** * 商品类目管理Service * <p>Title: ItemCatServiceImpl</p> * <p>Description: </p> * <p>Company: www.itheima.com</p> * @author 黑泽 * @date 2018年11月12日下午8:15:58 * @version 1.0 */ @Service public class ItemCatServiceImpl implements ItemCatService { @Autowired private TbItemCatMapper itemCatMapper; @Override public List<EasyUITreeNode> getItemCatList(Long parentId) { TbItemCatExample example = new TbItemCatExample(); // 设置查询条件 Criteria criteria = example.createCriteria(); criteria.andParentIdEqualTo(parentId); List<TbItemCat> list = itemCatMapper.selectByExample(example); // 将list转换成EasyUITreeNode列表 List<EasyUITreeNode> resultList = new ArrayList<>(); for (TbItemCat tbItemCat : list) { EasyUITreeNode node = new EasyUITreeNode(); node.setId(tbItemCat.getId()); node.setText(tbItemCat.getName()); // 如果节点下有子节点则state的值为"closed",如果节点下没有子节点则state的值为"open" node.setState(tbItemCat.getIsParent() ? "closed" : "open"); // 将节点添加到list集合(列表) resultList.add(node); } return resultList; } }
在taotao-manager-service中的applicationContext-service.xml中发布服务:
在taotao-manager-web中的springmvc.xml中引用服务:
初始化tree请求的url:
/item/cat/list
参数:
Long id(父节点id,表现层需要使用注解@RequestParam进行映射成parentId)
返回值:json格式的数据,使用注解@ResponseBody
List<EasyUITreeNode>
package com.taotao.controller; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import com.taotao.common.pojo.EasyUITreeNode; import com.taotao.service.ItemCatService; /** * 商品类目管理Controller * <p>Title: ItemCatController</p> * <p>Description: </p> * <p>Company: www.itheima.com</p> * @author 黑泽 * @date 2018年11月12日下午9:02:03 * @version 1.0 */ @Controller public class ItemCatController { @Autowired private ItemCatService itemCatService; @RequestMapping("/item/cat/list") @ResponseBody public List<EasyUITreeNode> getItemCatList(@RequestParam(value="id", defaultValue="0") Long parentId) { // 注意:第一次请求是没有参数传过来的,我们给id一个默认值0,defaultValue="0" List<EasyUITreeNode> list = itemCatService.getItemCatList(parentId); return list; } }
比如:产品经理提供需求 商品属性如下:
商品价格设置为Long ,就可以避免对小数点的处理。例如:
public static void main(String[] args) { float a = 1.3f; double b = 1.3d; float aa = a * 3; double bb = b * 3; System.out.println(aa); System.out.println(bb); } 打印结果是: 3.8999999 3.9000000000000004
存在的问题: 前端显示是元,两位小数,保存在数据库是分,所以保存数据需要把显示的数据乘以100。
商品描述被拆分成另一张表。 商品描述的特点: 数据量大 修改的频率低 所以需要拆分。
传统方式:
集群环境:
解决方案:
搭建一个图片服务器,专门保存图片。可以使用分布式文件系统FastDFS
。
图片服务器的要求:
1、存储空间可扩展。
2、提供一个统一的访问方式。
3、如果想安装,可参考文档,暂时不推荐自己搭建(耗费时间很长,有空再搭建),直接使用提供的虚拟机。
使用FastDFS,分布式文件系统。存储空间可以横向扩展,可以实现服务器的高可用
。支持每个节点有备份机
。
FastDFS是用
c语言编写
的一款开源的分布式文件系统
(国产软件)。FastDFS为互联网量身定制,充分考虑了冗余备份(高可用)
、负载均衡(高并发量)
、线性扩容(添加服务器或者磁盘)
等机制,并注重高可用、高性能等指标,使用FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。
FastDFS架构包括
Tracker server
和Storage server
。客户端请求 Tracker server 进行文件上传、下载,通过Tracker server调度最终由 Storage server 完成文件上传和下载。 Tracker server 作用是负载均衡和调度
,通过 Tracker server 在文件上传时可以根据一些策略找到 Storage server 提供文件上传服务。可以将 tracker 称为追踪服务器
或调度服务器
。 Storage server 作用是文件存储
,客户端上传的文件最终存储在Storage服务器上,Storage server 没有实现自己的文件系统而是利用操作系统的文件系统来管理文件
。可以将storage称为存储服务器
。 FastDFS架构图如下所示:
详解如下:
服务端两个角色: Tracker:管理集群,tracker也可以实现集群。每个tracker节点地位平等(没有主从的概念)。 收集Storage集群的状态。 Storage:实际保存文件。 Storage分为多个组(卷),每个组之间保存的文件是不同的。每个组内部可以有多个成员,组内的成员内部保存的内容是一样的,组成员的地位是一致的,没有主从的概念。
客户端上传文件后存储服务器将文件ID
返回给客户端,此文件ID用于以后访问该文件的索引信息。
文件索引信息包括:组名,虚拟磁盘路径,数据两级目录,文件名。
暂时不推荐自己搭建FastDFS图片服务器(耗费时间很长,有空再搭建),直接使用提供的虚拟机。 使用提供的虚拟机,需要注意几个问题,如下图所示:
由于两台虚拟机的ip网段不一样,所以我们还得改一下【虚拟机网络编辑器】,将他们改为在同一网段192.168.25.xxx。如下: 在Vmmare上选择【编辑(E)】--> 【虚拟机网络编辑器】
选择【更改设置(C)】
注意:以后所有学习中演示的例子都是用网段192.168.25.xxx。 图片服务器ip地址:192.168.25.133 用户名:root、itcast 密码:itcast
目前我们使用的是maven工程,不能直接添加jar包,需要创建maven工程,安装到本地,再在使用的工程中添加依赖。 Maven环境:
导入FastDFS的Java客户端工程的POM文件如下:
我们将FastDFS的Java客户端的坐标添加依赖到taotao-manager-web中的pom.xml中:
1、加载配置文件,配置文件中的内容就是tracker服务的地址。 配置文件内容:tracker_server=192.168.25.133:22122 2、创建一个TrackerClient对象。直接new一个。 3、使用TrackerClient对象创建连接,获得一个TrackerServer对象。 4、创建一个StorageServer的引用,值为null。 5、创建一个StorageClient对象,需要两个参数TrackerServer对象、StorageServer的引用。 6、使用StorageClient对象上传图片。 7、返回数组。包含组名和图片的路径。
/** * 使用DastDFS的Java客户端上传图片 * <p>Title: FastDFSTest</p> * <p>Description: </p> * <p>Company: www.itheima.com</p> * @author 黑泽 * @date 2018年11月13日下午4:14:01 * @version 1.0 */ public class FastDFSTest { @Test public void FileUploadTest() throws Exception { // 0、向工程中添加jar包。目前我们使用的是maven工程,不能直接添加jar包,需要创建maven工程,安装到本地,再在使用的工程中添加依赖。 // 1、加载配置文件,配置文件中的内容就是tracker服务的地址。配置文件内容:tracker_server=192.168.25.133:22122 ClientGlobal.init("D:/learn/Java/eclipse-jee-mars-2-win32_x64/eclipse-workspace/taotao/taotao-manager-web/src/main/resources/resource/fdfs_client.conf"); // 2、创建一个TrackerClient对象,我们直接new一个。 TrackerClient trackerClient = new TrackerClient(); // 3、使用TrackerClient对象创建连接,获得一个TrackerServer对象。 TrackerServer trackerServer = trackerClient.getConnection(); // 4、创建一个StorageServer的引用(不用new出来),值为null。不创建这个引用也可以,直接引用。 StorageServer storageServer = null; // 5、创建一个StorageClient对象,该对象需要两个参数:TrackerServer对象、StorageServer的引用 StorageClient storageClient = new StorageClient(trackerServer, storageServer); // 6、使用StorageClient对象上传图片。 // 扩展名不带“.” String[] strings = storageClient.upload_file("C:/Users/Bruce/Desktop/我的头像.jpg", "jpg", null); // 7、返回数组。包含组名和图片的路径。 for (String string : strings) { System.out.println(string); } } }
工具类位置:
工具类代码如下:
package com.taotao.common.util; import org.csource.common.NameValuePair; import org.csource.fastdfs.ClientGlobal; import org.csource.fastdfs.StorageClient1; import org.csource.fastdfs.StorageServer; import org.csource.fastdfs.TrackerClient; import org.csource.fastdfs.TrackerServer; /** * DastDFS的Java客户端上传图片工具类 * <p>Title: FastDFSClientUtils</p> * <p>Description: 用于上传图片至DastDFS服务器</p> * <p>Company: www.itheima.com</p> * @author 黑泽 * @date 2018年11月13日下午4:58:48 * @version 1.0 */ public class FastDFSClientUtil { private TrackerClient trackerClient = null; private TrackerServer trackerServer = null; private StorageServer storageServer = null; private StorageClient1 storageClient = null; public FastDFSClientUtil(String conf) throws Exception { if (conf.contains("classpath:")) { conf = conf.replace("classpath:", this.getClass().getResource("/").getPath()); } ClientGlobal.init(conf); trackerClient = new TrackerClient(); trackerServer = trackerClient.getConnection(); storageServer = null; storageClient = new StorageClient1(trackerServer, storageServer); } /** * 上传文件方法(文件方式) * <p>Title: uploadFile</p> * <p>Description: </p> * @param fileName 文件全路径 * @param extName 文件扩展名,不包含(.) * @param metas 文件扩展信息 * @return * @throws Exception */ public String uploadFile(String fileName, String extName, NameValuePair[] metas) throws Exception { String result = storageClient.upload_file1(fileName, extName, metas); return result; } public String uploadFile(String fileName) throws Exception { return uploadFile(fileName, null, null); } public String uploadFile(String fileName, String extName) throws Exception { return uploadFile(fileName, extName, null); } /** * 上传文件方法(字节方式) * <p>Title: uploadFile</p> * <p>Description: </p> * @param fileContent 文件的内容,字节数组 * @param extName 文件扩展名 * @param metas 文件扩展信息 * @return * @throws Exception */ public String uploadFile(byte[] fileContent, String extName, NameValuePair[] metas) throws Exception { String result = storageClient.upload_file1(fileContent, extName, metas); return result; } public String uploadFile(byte[] fileContent) throws Exception { return uploadFile(fileContent, null, null); } public String uploadFile(byte[] fileContent, String extName) throws Exception { return uploadFile(fileContent, extName, null); } }
分析还是有几个地方需要用到图片上传的,所以将工具类拷贝到:taotao-common工程下,如图:
测试代码:
@Test public void FastDFSClientUtilsTest() throws Exception { // 生产环境下我们使用classpath,现在学习阶段使用全路径名,由于工具类写的不够好 FastDFSClientUtil fastDFSClientUtils = new FastDFSClientUtil("D:/learn/Java/eclipse-jee-mars-2-win32_x64/eclipse-workspace/taotao/taotao-manager-web/src/main/resources/resource/fdfs_client.conf"); String string = fastDFSClientUtils.uploadFile("C:/Users/Bruce/Desktop/我的头像.jpg"); System.out.println(string); }
使用的是KindEditor的多图片上传插件。 KindEditor 4.x 文档 文档网址:http://kindeditor.net/doc.php
请求的url:/pic/upload 参数:MultiPartFile uploadFile 返回值:
可以创建一个pojo对应返回值。也可以使用Map集合。
图片上传跟服务层没有什么关系,表现层就可以将这件事做了。
表现层使用的是SpringMVC,需要加入文件上传的jar包,如果没有添加jar包,则需要把commons-io
、fileupload
的jar包添加到taotao-manager-web工程中。
在taotao-manager-web工程中的springmvc.xml中添加如下:
<!-- 配置文件上传解析器 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- 设定默认编码 --> <property name="defaultEncoding" value="UTF-8"></property> <!-- 设定文件上传的最大值5MB,5*1024*1024 --> <property name="maxUploadSize" value="5242880"></property> </bean>
开发controller实现图片上传。 将图片服务器的前缀放入属性文件中:
在taotao-manager-web工程中的springmvc.xml中加载该属性文件:
这样spring容器加载的时候,我们使用注解@Value("${TAOTAO_IMAGE_SERVER_URL}")取出配置文件的值。 代码如下:
@Controller public class PictureController { @Value("${TAOTAO_IMAGE_SERVER_URL}") // 使用注解取出配置文件的值 private String TAOTAO_IMAGE_SERVER_URL; /** * 上传图片 * <p>Title: PictureUpload</p> * <p>Description: </p> * @param uploadFile * @return */ @RequestMapping(value="/pic/upload") @ResponseBody // 在后台,把JavaBean强制转换成json格式数据返回给前台页面。 public Map<String, Object> PictureUpload(MultipartFile uploadFile) { try { // 1、取出文件的扩展名 String originalFilename = uploadFile.getOriginalFilename(); String extName = originalFilename.substring(originalFilename.lastIndexOf(".") + 1); // 2、使用工具类创建一个FastDFS的客户端 FastDFSClientUtil fastDFSClientUtil = new FastDFSClientUtil("classpath:resource/fdfs_client.conf"); // 3、执行上传处理,返回的字符串:group1/M00/00/01/wKgZhVjnAd6AKj_RAAvqH_kipG8211.jpg String path = fastDFSClientUtil.uploadFile(uploadFile.getBytes(), extName); // 4、拼接返回的url和ip地址,拼装成完整的url // String url = "http://192.168.25.133/" + path; String url = TAOTAO_IMAGE_SERVER_URL + path; // 5、返回map,设置上传成功后的图片的路径 Map<String, Object> result = new HashMap<>(); result.put("error", 0); result.put("url", url); // 6、返回 return result; } catch (Exception e) { e.printStackTrace(); // 5、返回map,设置上传失败错误信息 Map<String, Object> result = new HashMap<>(); result.put("error", 1); result.put("message", "图片上传失败"); return result; } } }
KindEditor的图片上传插件,对浏览器兼容性不好,不能使用JSON响应。
使用@ResponseBody注解返回java对象实际上是JSON格式的数据。
返回JSON格式的数据时的Content-Type为:application/json;charset=UTF-8 在火狐上测试不行。
返回字符串时的Content-Type为:text/plain;charset=UTF-8 它是可以成功返回的。火狐浏览器支持。
可以通过produces指定响应结果的content-type:
@RequestMapping(value="/pic/upload",produces=MediaType.TEXT_PLAIN_VALUE+";charset=utf-8")
需要使用JsonUtils工具类
将对象转换成JSON格式的字符串
。将其拷贝到taotao-common
项目中,安装到本地仓库
。
修改代码如下:
@Controller public class PictureController { @Value("${TAOTAO_IMAGE_SERVER_URL}") // 使用注解取出配置文件的值 private String TAOTAO_IMAGE_SERVER_URL; /** * 上传图片 * <p>Title: PictureUpload</p> * <p>Description: </p> * @param uploadFile * @return */ @RequestMapping(value="/pic/upload", produces=MediaType.TEXT_PLAIN_VALUE + ";charset=UTF-8") @ResponseBody // 在后台,把JavaBean强制转换成json格式数据返回给前台页面。 public String PictureUpload(MultipartFile uploadFile) { try { // 1、取出文件的扩展名 String originalFilename = uploadFile.getOriginalFilename(); String extName = originalFilename.substring(originalFilename.lastIndexOf(".") + 1); // 2、使用工具类创建一个FastDFS的客户端 FastDFSClientUtil fastDFSClientUtil = new FastDFSClientUtil("classpath:resource/fdfs_client.conf"); // 3、执行上传处理,返回的字符串:group1/M00/00/01/wKgZhVjnAd6AKj_RAAvqH_kipG8211.jpg String path = fastDFSClientUtil.uploadFile(uploadFile.getBytes(), extName); // 4、拼接返回的url和ip地址,拼装成完整的url // String url = "http://192.168.25.133/" + path; String url = TAOTAO_IMAGE_SERVER_URL + path; // 5、返回map,设置上传成功后的图片的路径 Map<String, Object> result = new HashMap<>(); result.put("error", 0); result.put("url", url); // 6、返回 return JsonUtils.objectToJson(result); } catch (Exception e) { e.printStackTrace(); // 5、返回map,设置上传失败错误信息 Map<String, Object> result = new HashMap<>(); result.put("error", 1); result.put("message", "图片上传失败"); return JsonUtils.objectToJson(result); } } }
KindEditor http://kindeditor.net/ UEditor:百度编辑器 http://ueditor.baidu.com/website/ CKEditor http://ckeditor.com/ 富文本编辑器纯js开发,跟后台语言没有关系。
textarea控件
。是一个富文本编辑器的载体
。类似数据源。
common.js
表单提交之前,把富文本编辑器的内容同步
到textarea控件中。 即将编辑器中的数据放到textarea
中,最终提交数据是textarea提交到后台。
提交表单:
要求pojo的属性名称和input的name属性值要一致
。
使用TbItem对象
接收表单的商品基本数据
,使用字符串
接收表单中的商品描述的数据
。
参数如下:TbItem item, String desc
返回值:json格式的数据。应该包含一个status的属性。`一般而言,我们响应的json格式数据都是通过一个对象转化而来!`
我们可以使用提供的工具类TaotaoResult。Incr命令
。推荐使用。暂时还没用到。
可以使用工具类IDUtils
生成商品id。安装到本地仓库。
2、补全TbItem对象的属性
3、向商品表插入数据
4、创建一个TbItemDesc对象
5、补全TbItemDesc的属性
6、向商品描述表插入数据
7、TaotaoResult.ok()向tb_item, tb_item_desc表中插入数据,可以使用逆向工程生成的代码。
/** * 根据商品的基础数据和商品的描述信息插入商品(插入商品表和商品描述表) * <p>Title: insertItem</p> * <p>Description: </p> * @param item * @param desc * @return */ TaotaoResult saveItem(TbItem item, String desc);
@Override public TaotaoResult saveItem(TbItem item, String desc) { // 1、生成商品id,本例中使用工具类IDUtils生成商品id Long itemId = IDUtils.genItemId(); item.setId(itemId); // 2、补全商品表TbItem的其他属性 // 商品状态,1-正常,2-下架,3-删除 item.setStatus((byte) 1); Date date = new Date(); item.setCreated(date); item.setUpdated(date); // 3、向商品表中插入数据 itemMapper.insert(item); // 4、创建一个商品描述表TbItemDesc对象 TbItemDesc itemDesc = new TbItemDesc(); // 5、补全商品描述表TbItemDesc的其他属性 itemDesc.setItemId(itemId); itemDesc.setItemDesc(desc); itemDesc.setCreated(date); itemDesc.setUpdated(date); // 6、向商品描述表中插入数据 itemDescMapper.insert(itemDesc); // 7、返回TaotaoResult.ok() return TaotaoResult.ok(); }
在taotao-manager-service工程中的applicationContext-service.xml中发布服务:
在taotao-manager-web工程中的springmvc.xml中引用服务:
请求的url:/item/save 参数:TbItem item, String desc 返回值:TaotaoResult
/** * 根据商品的基础数据和商品的描述信息添加商品,返回插入成功的响应状态 * <p>Title: svetItem</p> * <p>Description: </p> * @param item * @param desc * @return */ @RequestMapping(value="/item/save", method=RequestMethod.POST) @ResponseBody public TaotaoResult savetItem(TbItem item, String desc) { TaotaoResult result = itemService.saveItem(item, desc); return result; }
商品修改、商品删除、上架下架。
本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。
我来说两句