文章大纲
一、第三天课程计划
二、使用域名访问后台系统(选用)
三、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. 使用域名访问后台系统
2. 使用Nginx反相代理服务器
3. 实现商品类目选择
4. 图片上传
5. kindeditor(富文本编辑器)的使用
6. 实现新增商品
由于我现在没有域名,就不再学修改这一模块。
现在访问的路径:http://127.0.0.1:8081/rest/page/index
存在的问题:
1. 开发环境和测试环境的ip不一样,每次环境变化时,都需要修改访问地址。
2. 页面加载资源文件,有可能使用url的全路径,一旦更换环境(ip变了),资源文件就无法加载了。
3. ip地址没有意义,不容易记忆,用户不会通过ip访问,一般通过域名访问
如果手动修改hosts文件,会非常麻烦,环境的切换非常复杂,企业中一般不这么干
SwitchHosts帮助我们管理hosts文件,其实就是管理我们自定义ip和域名的映射
所在位置:
可以增加多个方案,淘淘商城开发环境和淘淘商城测试环境,可以方便的切换
存在的问题
实现通过域名访问后,还存在端口号的问题
用户是直接输入域名,不会填写端口号。
这个模块后面会在Linux系统上安装,第十天还会有详细用法。现在主要了解:
我们使用Nginx反相代理,虚拟主机,负载均衡
反向代理是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端。此时代理服务器对外就表现为一个反向代理服务器。
启动cmd命令行输入命令
三个命令
启动: start nginx.exe
停止: nginx.exe –s stop
重载: nginx.exe –s reload
启动报错,查看日志文件发现80端口被占用
使用命令netstat -ano命令查看端口占用情况
发现有程序占用80端口,根据pid查看是哪个进程
打开任务管理器,根据pid查询到是eclipse的Tomcat占用80端口
停止程序即可
1、 按钮添加点击事件,弹出窗口,加载数据显示tree
2、 将选择类目的组件封装起来,通过TT.iniit()初始化,最终调用initItemCat()方法进行初始化
3、 创建数据库、以及tb _item_cat表,初始化数据
4、 编写Controller、Service、Mapper
数据结构中必须包含:
Id:节点id
Text:节点名称
State:如果不是叶子节点就是close,叶子节点就是open。Close的节点点击后会在此发送请求查询子项目。
可以根据parentid查询分类列表。
Sql:select * from tb_item_cat where parent_id=2
单表查询,可以使用逆向工程生成代码.
使用逆向工程生成的mapper文件。
功能: 接收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
List<TbItemCat> list = catmapper.selectByExample(example);
//5.转成需要的数据类型List
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;
}
}
功能:接收页面请求的参数,名为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;
}
}
参考我的踩坑博客
Maven项目启动遇见Error creating bean with name ‘itemCatController‘:Injection of autowired dependencies错误
运行taotao-manager,会报错
需要安装一下taotao-common
再次运行taotao-manager
安装taotao-manager
启动taoato-manager-web,运行成功
查看效果
图片服务器
传统项目中,可以在web项目中添加一个文件夹,来存放上传的图片。例如在工程的根目录WebRoot下创建一个images文件夹。把图片存放在此文件夹中就可以直接使用在工程中引用。
优点:引用方便,便于管理
缺点:
1、如果是分布式环境图片引用会出现问题。
2、图片的下载会给服务器增加额外的压力
传统图片管理方式在分布式环境中的问题:
分布式环境一般都有一个专门的图片服务器存放图片。
我们使用虚拟机搭建一个专门的服务器来存放图片。在此服务器上安装一个nginx来提供http服务,安装一个ftp服务器来提供图片上传服务。
第一步:安装vsftpd提供ftp服务
详见:vsftpd安装手册.doc
第二步:安装nginx提供http服务
详见:nginx安装手册.doc
参考博客:安装Nginx&搭建图片服务器&踩到的坑
上传思路:
第一步:
导入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>
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;
}
}
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);
}
}
在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);
}
}
KindEditor 4.x 文档
http://kindeditor.net/doc.php
上传图片使用kindeditor的上传组件实现。
返回格式(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;
/**
* 错误时的错误消息
*/
}
参考博客:调用Dubbo服务做文件上传时,出现com.alibaba.dubbo.rpc.RpcException: Failed to invoke the method
3、调用TT.createEditor
将编辑器的内容设置到原来的textarea控件里。
editor.sync();
//提交表单
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
//ajax的post方式提交表单
//$("#itemAddForm").serialize()将表单序列号为key-value形式的字符串
$.post("/item/save",$("#itemAddForm").serialize(), function(data){
if(data.status == 200){
$.messager.alert('提示','新增商品成功!');
}
});
}
$("#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;
}
}
}
临时主键生成策略:
/**
* 商品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;
}
调用mapper的insert方法添加商品信息
@Override
public void saveItem(TbItem item, String desc, String itemParams) throws Exception {
Date date = new Date();
//获得商品id
long id = IDUtils.genItemId();
//添加商品信息
item.setId(id);
//商品状态,1-正常,2-下架,3-删除
item.setStatus((byte) 1);
item.setCreated(date);
item.setUpdated(date);
itemMapper.insert(item);
//添加商品描述
//创建TbItemDesc对象
TbItemDesc itemDesc = new TbItemDesc();
//获得一个商品id
itemDesc.setItemId(id);
itemDesc.setItemDesc(desc);
itemDesc.setCreated(date);
itemDesc.setUpdated(date);
//插入数据
itemDescMapper.insert(itemDesc);
}
@RequestMapping("/save")
@ResponseBody
public TaotaoResult saveItem(TbItem item, String desc) throws Exception {
//添加商品信息
itemService.saveItem(item, desc, null);
return TaotaoResult.ok();
}
ssm(Spring、Springmvc、Mybatis)实战之淘淘商城-第三天(非原创)