SSM分布式项目之淘淘商城-第三天(IDEA)

tech2025-06-06  10

文章大纲 一、第三天课程计划 二、使用域名访问后台系统(选用) 三、Nginx介绍(选用) 四、实现商品类目选择 五、图片上传 六、kindeditor(富文本编辑器)的使用 七、实现新增商品 八、参考文章

淘淘商城课程大纲

一共14天课程 (1)第一天:电商介绍–互联网术语-SOA-分布式-集群介绍-环境配置-框架搭建 (2)第二天:Dubbo介绍_dubbo框架整合_商品列表查询实现_分页_逆向工程 (3)第三天:Git,Nginx,类目选择,图片上传,新增商品 (4)第四天:门户网站介绍&商城首页搭建&内容系统创建&CMS实现 (5)第五天:首页轮播图显示实现,Redis环境搭建,Redis实现缓存 (6)第六天:solr索引库搭建&solr搜索功能实现&图片显示问题解决 (7)第七天:solr集群搭建_全局异常处理 (8)第八天:activeMQ介绍_搭建_解决同步索引库问题 (9)第九天:FreeMark入门_静态化页面标签介绍_静态化页面实现 (10)第十天:Nginx代理详解…单点登录系统工程搭建_接口文档讲解 (11)第十一天:SSO系统的搭建&单点登录系统实现_用户名回显_cookie跨域问题 (12)第十二天:购物车订单系统的实现。 (13)第十三天:订单提交的功能实现&项目的部署&服务器的域名规划。 (14)项目总结。

1. 第三天课程计划

1. 使用域名访问后台系统 2. 使用Nginx反相代理服务器 3. 实现商品类目选择 4. 图片上传 5. kindeditor(富文本编辑器)的使用 6. 实现新增商品

2. 使用域名访问后台系统(选用)

2.1 现在的方式存在的问题

由于我现在没有域名,就不再学修改这一模块。

现在访问的路径:http://127.0.0.1:8081/rest/page/index 存在的问题:

1. 开发环境和测试环境的ip不一样,每次环境变化时,都需要修改访问地址。 2. 页面加载资源文件,有可能使用url的全路径,一旦更换环境(ip变了),资源文件就无法加载了。 3. ip地址没有意义,不容易记忆,用户不会通过ip访问,一般通过域名访问

2.2 使用域名进行访问

可以通过修改hosts文件的方式,增加ip地址和域名的映射

使用域名访问的步骤

如果手动修改hosts文件,会非常麻烦,环境的切换非常复杂,企业中一般不这么干

2.3 使用SwitchHosts管理

SwitchHosts帮助我们管理hosts文件,其实就是管理我们自定义ip和域名的映射 所在位置:

功能说明:

可以增加多个方案,淘淘商城开发环境和淘淘商城测试环境,可以方便的切换

存在的问题 实现通过域名访问后,还存在端口号的问题 用户是直接输入域名,不会填写端口号。

3. Nginx介绍(选用)

这个模块后面会在Linux系统上安装,第十天还会有详细用法。现在主要了解:

我们使用Nginx反相代理,虚拟主机,负载均衡

3.1 反相代理

反向代理是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端。此时代理服务器对外就表现为一个反向代理服务器。

3.2 Nginx目录结构(Windows系统,不推荐)

解压后的目录结构

3.3 Nginx配置文件

修改配置文件:

3.4 Nginx三个命令

启动cmd命令行输入命令

三个命令 启动: start nginx.exe 停止: nginx.exe –s stop 重载: nginx.exe –s reload

启动成功:有且仅有两个线程

启动报错,查看日志文件发现80端口被占用 使用命令netstat -ano命令查看端口占用情况

发现有程序占用80端口,根据pid查看是哪个进程

打开任务管理器,根据pid查询到是eclipse的Tomcat占用80端口 停止程序即可

3.5 Nginx访问流程

4. 实现商品类目选择功能

4.1 需求

在商品添加页面,点击“选择类目”显示商品类目列表:

4.2 实现步骤:

1、 按钮添加点击事件,弹出窗口,加载数据显示tree 2、 将选择类目的组件封装起来,通过TT.iniit()初始化,最终调用initItemCat()方法进行初始化 3、 创建数据库、以及tb _item_cat表,初始化数据 4、 编写Controller、Service、Mapper

4.3 EasyUI tree数据结构

数据结构中必须包含: Id:节点id Text:节点名称 State:如果不是叶子节点就是close,叶子节点就是open。Close的节点点击后会在此发送请求查询子项目。 可以根据parentid查询分类列表。

4.4 功能实现分析

查看common.js,类目选择按钮初始化,注意url.

4.4 Dao

Sql:select * from tb_item_cat where parent_id=2 单表查询,可以使用逆向工程生成代码. 使用逆向工程生成的mapper文件。

4.5 Service

功能: 接收parentid参数,根据parentid查询子类目类别.返回一个分类列表,可以创建一个pojo来描述一个节点的格式,返回一个pojo列表. 包含,id,text,state属性.因为其他工程也有可能用到pojo,所以应该放到taotao-common工程中. 创建pojo:EasyUITreeNode,注意需要实现Serializable 创建接口:ItemCatService

创建实现类ItemCatServiceImpl

/** * 商品分类管理 */ @Service public class ItemCatServiceImpl implements ItemCatService { @Autowired private TbItemCatMapper catmapper; @Override public List<EasyUITreeNode> getItemCatListByParentId(Long parentId) { //1.注入mapper //2.创建example TbItemCatExample example = new TbItemCatExample(); //3.设置查询的条件 Criteria criteria = example.createCriteria(); criteria.andParentIdEqualTo(parentId);//select *from tbitemcat where parentId=1 //4.执行查询 list<ibitemCat> List<TbItemCat> list = catmapper.selectByExample(example); //5.转成需要的数据类型List<EasyUITreeNode> List<EasyUITreeNode> nodes = new ArrayList<>(); for (TbItemCat cat : list) { EasyUITreeNode node = new EasyUITreeNode(); node.setId(cat.getId()); node.setText(cat.getName()); node.setState(cat.getIsParent()?"closed":"open");//"open",closed nodes.add(node); } //6.返回 return nodes; } }

4.6 Controller

功能:接收页面请求的参数,名为id.调用service查询分类列表,返回json格式列表 需要使用@ResponseBody注解

@Controller @RequestMapping("/item/cat") public class ItemCatController { @Autowired private ItemCatService itemCatService; //url:'/item/cat/list', //参数:id //返回值:json //method:get post @RequestMapping("/list") @ResponseBody public List<EasyUITreeNode> getItemCatList(@RequestParam(value = "id", defaultValue = "0") Long parentId) { //1.引入服务 //2.注入服务 //3.调用方法 List<EasyUITreeNode> list = itemCatService.getItemCatListByParentId(parentId); return list; } }

4.7 发布服务

参考我的踩坑博客 Maven项目启动遇见Error creating bean with name ‘itemCatController‘:Injection of autowired dependencies错误

4.8 测试

运行taotao-manager,会报错 需要安装一下taotao-common 再次运行taotao-manager 安装taotao-manager 启动taoato-manager-web,运行成功 查看效果

5. 图片上传

图片服务器

5.1 传统项目中的图片管理

传统项目中,可以在web项目中添加一个文件夹,来存放上传的图片。例如在工程的根目录WebRoot下创建一个images文件夹。把图片存放在此文件夹中就可以直接使用在工程中引用。 优点:引用方便,便于管理 缺点: 1、如果是分布式环境图片引用会出现问题。 2、图片的下载会给服务器增加额外的压力

传统图片管理方式在分布式环境中的问题:

5.2 分布式环境的图片管理

分布式环境一般都有一个专门的图片服务器存放图片。 我们使用虚拟机搭建一个专门的服务器来存放图片。在此服务器上安装一个nginx来提供http服务,安装一个ftp服务器来提供图片上传服务。

5.3 搭建图片服务器

第一步:安装vsftpd提供ftp服务 详见:vsftpd安装手册.doc 第二步:安装nginx提供http服务 详见:nginx安装手册.doc

参考博客:安装Nginx&搭建图片服务器&踩到的坑

5.5 SpringMVC中实现图片上传

上传思路: 第一步: 导入common-fileupload的依赖

<!-- 文件上传组件 --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> </dependency>

第二步: 在SpringMVC配置文件中添加文件上传解析器

<!-- 定义文件上传解析器 --> <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>

5.6 工具类编写

taotao-common项目中编写上传图片的返回结果的实体类PictureResult.java

public class PictureResult { private int error;//判断是否成功 0位成功,1为失败 private String url;//如果成功 该参数为图片的请求地址 失败则为null private String message;//如果失败,该参数是描述原因,如果成功,则为null private PictureResult(int error, String url, String message) { this.error = error; this.url = url; this.message = message; } //成功时调用的方法 public static PictureResult ok(String url) { return new PictureResult(0, url, null); } //失败时调用的方法 public static PictureResult error(String message) { return new PictureResult(1, null, message); } public int getError() { return error; } public void setError(int error) { this.error = error; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }

导入这些工具类

5.7 在Service中获取资源配置:

taotao-manager-web项目编写资源文件resource.properties,用于存放于FTP相关的变量 taotao-manager-interface项目中编写上传图片处理接口PictureService.java 注意:我的不知道是什么原因,没有办法引入MultipartFile的包,解决办法是 添加Spring依赖 参考博客:https://blog.csdn.net/qq_43966129/article/details/108441131

/** * 上传图片处理 */ public interface PictureService { PictureResult uploadPicture(MultipartFile uploadFile); }

taotao-manager-service项目中编写上传图片处理实现类PictureServiceImpl.java

/** * 上传图片处理服务实现类 */ @Service public class PictureServiceImpl implements PictureService { //使用@Value注解时候,当配置文件中内容修改时候,映射过来的内容会自动更改的 @Value("${FTP_ADDRESS}") private String FTP_ADDRESS; @Value("${FTP_PORT}") private Integer FTP_PORT; @Value("${FTP_USER_NAME}") private String FTP_USER_NAME; @Value("${FTP_PASSWORD}") private String FTP_PASSWORD; @Value("${FTP_BASE_PATH}") private String FTP_BASE_PATH; @Value("${IMAGE_BASE_URL}") private String IMAGE_BASE_URL; @Override public PictureResult uploadPicture(MultipartFile uploadFile) { //判断上传图片是否为空 if (null == uploadFile || uploadFile.isEmpty()) { return PictureResult.error("上传图片为空"); } //取文件扩展名 String originalFilename = uploadFile.getOriginalFilename(); String ext = originalFilename.substring(originalFilename.lastIndexOf(".")); //生成新文件名 //可以使用uuid生成新文件名。 //UUID.randomUUID() //可以是时间+随机数生成文件名 String imageName = IDUtils.genImageName(); //把图片上传到ftp服务器(图片服务器) //需要把ftp的参数配置到配置文件中 //文件在服务器的存放路径,应该使用日期分隔的目录结构 DateTime dateTime = new DateTime(); String filePath = dateTime.toString("/yyyy/MM/dd"); try { FtpUtil.uploadFile(FTP_ADDRESS, FTP_PORT, FTP_USER_NAME, FTP_PASSWORD, FTP_BASE_PATH, filePath, imageName + ext, uploadFile.getInputStream()); } catch (Exception e) { e.printStackTrace(); return PictureResult.error(ExceptionUtil.getStackTrace(e)); } //返回结果,生成一个可以访问到图片的url返回 return PictureResult.ok(IMAGE_BASE_URL + filePath + "/" + imageName + ext); } }

5.7 Controller实现

在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>

在taotao-manager-web项目中编写接收的图片的PictureController.java

package com.taotao.controller; 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.ResponseBody; import org.springframework.web.multipart.MultipartFile; import com.taotao.common.pojo.PictureResult; import com.taotao.common.utils.JsonUtils; import com.taotao.service.PictureService; /** * 图片上传controller */ @Controller public class PictureController { @Autowired private PictureService pictureService; @RequestMapping("/pic/upload") @ResponseBody public String upload(MultipartFile uploadFile) { PictureResult result = pictureService.uploadPicture(uploadFile); //将对象转化成json字符串 return JsonUtils.objectToJson(result); } }

5.8 前端JS实现图片上传(自动生成)

Js实现逻辑

KindEditor 4.x 文档 http://kindeditor.net/doc.php 上传图片使用kindeditor的上传组件实现。

上传图片请求url:

返回值 参考文档: http://kindeditor.net/docs/upload.html添加链接描述

返回格式(JSON)

//成功时 { "error" : 0, "url" : "http://www.example.com/path/to/file.ext" } //失败时 { "error" : 1, "message" : "错误信息" }

返回值数据类型:

public class PictureResult { /** * 上传图片返回值,成功:0 失败:1 */ private Integer error; /** * 回显图片使用的url */ private String url; /** * 错误时的错误消息 */ }

5.9 个人报错问题解决

参考博客:调用Dubbo服务做文件上传时,出现com.alibaba.dubbo.rpc.RpcException: Failed to invoke the method

5.10 效果

6. kindeditor(富文本编辑器)的使用

6.1 kindeditor的使用过程:

1、导入js:

2、定义多行文本(不可见、给定name)

3、调用TT.createEditor

4、效果

6.2 取文本编辑器中的内容

将编辑器的内容设置到原来的textarea控件里。

editor.sync();

7. 新增商品实现

7.1 js编写逻辑

//提交表单 function submitForm(){ //有效性验证 if(!$('#itemAddForm').form('validate')){ $.messager.alert('提示','表单还未填写完成!'); return ; } //取商品价格,单位为“分” $("#itemAddForm [name=price]").val(eval($("#itemAddForm [name=priceView]").val()) * 100); //同步文本框中的商品描述 itemAddEditor.sync(); //取商品的规格 /* var paramJson = []; $("#itemAddForm .params li").each(function(i,e){ var trs = $(e).find("tr"); var group = trs.eq(0).text(); var ps = []; for(var i = 1;i<trs.length;i++){ var tr = trs.eq(i); ps.push({ "k" : $.trim(tr.find("td").eq(0).find("span").text()), "v" : $.trim(tr.find("input").val()) }); } paramJson.push({ "group" : group, "params": ps }); }); //把json对象转换成字符串 paramJson = JSON.stringify(paramJson); $("#itemAddForm [name=itemParams]").val(paramJson); */ //ajax的post方式提交表单 //$("#itemAddForm").serialize()将表单序列号为key-value形式的字符串 $.post("/item/save",$("#itemAddForm").serialize(), function(data){ if(data.status == 200){ $.messager.alert('提示','新增商品成功!'); } }); }

7.2 提交请求的数据格式

$("#itemAddForm").serialize()将表单序列号为key-value形式的字符串 以post 的形式将表单的内容提交。

请求的url: /item/save

返回的结果: 淘淘自定义返回结果: 1、状态码 2、响应的消息 3、响应的数据

/** * 淘淘商城自定义响应结构 */ public class TaotaoResult { // 定义jackson对象 private static final ObjectMapper MAPPER = new ObjectMapper(); // 响应业务状态 private Integer status; // 响应消息 private String msg; // 响应中的数据 private Object data; public static TaotaoResult build(Integer status, String msg, Object data) { return new TaotaoResult(status, msg, data); } public static TaotaoResult ok(Object data) { return new TaotaoResult(data); } public static TaotaoResult ok() { return new TaotaoResult(null); } public TaotaoResult() { } public static TaotaoResult build(Integer status, String msg) { return new TaotaoResult(status, msg, null); } public TaotaoResult(Integer status, String msg, Object data) { this.status = status; this.msg = msg; this.data = data; } public TaotaoResult(Object data) { this.status = 200; this.msg = "OK"; this.data = data; } // public Boolean isOK() { // return this.status == 200; // } public Integer getStatus() { return status; } public void setStatus(Integer status) { this.status = status; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } /** * 将json结果集转化为TaotaoResult对象 * * @param jsonData json数据 * @param clazz TaotaoResult中的object类型 * @return */ public static TaotaoResult formatToPojo(String jsonData, Class<?> clazz) { try { if (clazz == null) { return MAPPER.readValue(jsonData, TaotaoResult.class); } JsonNode jsonNode = MAPPER.readTree(jsonData); JsonNode data = jsonNode.get("data"); Object obj = null; if (clazz != null) { if (data.isObject()) { obj = MAPPER.readValue(data.traverse(), clazz); } else if (data.isTextual()) { obj = MAPPER.readValue(data.asText(), clazz); } } return build(jsonNode.get("status").intValue(), jsonNode.get("msg").asText(), obj); } catch (Exception e) { return null; } } /** * 没有object对象的转化 * * @param json * @return */ public static TaotaoResult format(String json) { try { return MAPPER.readValue(json, TaotaoResult.class); } catch (Exception e) { e.printStackTrace(); } return null; } /** * Object是集合转化 * * @param jsonData json数据 * @param clazz 集合中的类型 * @return */ public static TaotaoResult formatToList(String jsonData, Class<?> clazz) { try { JsonNode jsonNode = MAPPER.readTree(jsonData); JsonNode data = jsonNode.get("data"); Object obj = null; if (data.isArray() && data.size() > 0) { obj = MAPPER.readValue(data.traverse(), MAPPER.getTypeFactory().constructCollectionType(List.class, clazz)); } return build(jsonNode.get("status").intValue(), jsonNode.get("msg").asText(), obj); } catch (Exception e) { return null; } } }

7.3 获得商品id

临时主键生成策略:

/** * 商品id生成 */ public static long genItemId() { //取当前时间的长整形值包含毫秒 long millis = System.currentTimeMillis(); //long millis = System.nanoTime(); //加上两位随机数 Random random = new Random(); int end2 = random.nextInt(99); //如果不足两位前面补0 String str = millis + String.format("%02d", end2); long id = new Long(str); return id; }

7.4 编写service

需要编写接口和实现类 在taotao-manager-interface子工程编写接口

在taotao-manager-service实现方法

@Override public TaotaoResult creatItem(TbItem item,String desc) { //生成商品的id long itemId = IDUtils.genItemId(); //1.补全item 的其他属性 item.setId(itemId); item.setCreated(new Date()); //1-正常,2-下架,3-删除', item.setStatus((byte) 1); item.setUpdated(item.getCreated()); //2.插入到item表 商品的基本信息表 mapper.insertSelective(item); //3.补全商品描述中的属性 TbItemDesc desc2 = new TbItemDesc(); desc2.setItemDesc(desc); desc2.setItemId(itemId); desc2.setCreated(item.getCreated()); desc2.setUpdated(item.getCreated()); //4.插入商品描述数据 //注入tbitemdesc的mapper descmapper.insertSelective(desc2); //5.返回taotaoresult return TaotaoResult.ok(); }

7.5 Controller实现

@RequestMapping(value = "/item/save", method = RequestMethod.POST) @ResponseBody public TaotaoResult creatItem(TbItem item,String desc){ TaotaoResult result = itemservice.creatItem(item,desc); return result; }

注意一定要加上参数,商品描述的Desc,不然做到第六天会出现错误,我就是来填坑了…

7.6 效果

数据添加成功,提示添加成功 查看数据库最后一页,会有新添加的

8. 参考博客

ssm(Spring、Springmvc、Mybatis)实战之淘淘商城-第三天(非原创)

最新回复(0)