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

文章大纲
一、第三天课程计划
二、使用域名访问后台系统(选用)
三、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实现图片上传(自动生成)

  1. Js实现逻辑
    在这里插入图片描述在这里插入图片描述
    在这里插入图片描述

KindEditor 4.x 文档
http://kindeditor.net/doc.php
上传图片使用kindeditor的上传组件实现。
在这里插入图片描述

  1. 上传图片请求url:
    在这里插入图片描述

  2. 返回值
    参考文档:
    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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值