1.如何做一個(gè)電商系統(tǒng)
1.1.目標(biāo)
需求:完成商品添加業(yè)務(wù)蒲每。
第一步:理解商品模塊的業(yè)務(wù)(通過(guò)ER圖理解)
考核的知識(shí)點(diǎn),通過(guò)數(shù)據(jù)庫(kù)結(jié)構(gòu)快速生成ER圖喻括。同自己的理解畫好關(guān)系邀杏。
問(wèn)題:為什么數(shù)據(jù)庫(kù)表不建外鍵約束?
答:外鍵約束確保了數(shù)據(jù)的完整性唬血,但是也約束數(shù)據(jù)的靈活性望蜡。如果將外鍵在數(shù)據(jù)里創(chuàng)建,不適合需求多變的項(xiàng)目拷恨。
第二步:查詢商品類目(以樹(shù)形結(jié)構(gòu)顯示脖律,UI設(shè)計(jì)的要求)
考核的知識(shí)點(diǎn),就是如何封裝一個(gè)樹(shù)狀的數(shù)據(jù)結(jié)構(gòu)腕侄。
第三步:實(shí)現(xiàn)圖片的上傳(要求:上傳到指定的FTP服務(wù)器)
考核的知識(shí)點(diǎn):
1. Linux系統(tǒng)的使用
2. tengine 純HTTP的web服務(wù)器
3. SpringMVC的上傳功能
4. FTP的數(shù)據(jù)傳到
第四步:設(shè)置類目的參數(shù)規(guī)格模板
考核的知識(shí)點(diǎn):JSON數(shù)據(jù)格式轉(zhuǎn)換小泉。
第五步:商品的保存
考核的知識(shí)點(diǎn):使用MybatisPlus插入數(shù)據(jù)
1.2.功能分析
1.2.1.相關(guān)數(shù)據(jù)表
說(shuō)明:與商品模塊有關(guān)的表,總共有5張兜挨。關(guān)系如下:
1.2.2.實(shí)現(xiàn)的思路
(1)每個(gè)商品都有一個(gè)分類膏孟,所以要實(shí)現(xiàn)商品類目選擇功能。
(2)商品有一個(gè)圖片屬性拌汇,所以要實(shí)現(xiàn)圖片上傳的功能。
(3)每個(gè)商品都有規(guī)格參數(shù)弊决,所以要實(shí)現(xiàn)商品規(guī)格參數(shù)編輯功能噪舀。
(4)將商品的規(guī)格參數(shù)、商品詳情飘诗、商品信息分別保存到三張表中与倡。
2.第一部分:實(shí)現(xiàn)商品類目選擇功能
2.1.需求分析
在商品頁(yè)面,點(diǎn)擊”選擇類目”按鈕昆稿,生成商品類目異步樹(shù)纺座。
對(duì)應(yīng)的數(shù)據(jù)庫(kù)表為tb_item_cat,表結(jié)構(gòu)為:
實(shí)現(xiàn)的思路:
業(yè)務(wù)理解:在加載樹(shù)控件的時(shí)候溉潭,將所有頂級(jí)的類目顯示出來(lái)净响。所以的子節(jié)點(diǎn)在展開(kāi)的時(shí)候傳入節(jié)點(diǎn)對(duì)應(yīng)的類目編號(hào)(ID),查詢對(duì)應(yīng)的類目數(shù)據(jù)喳瓣。
根據(jù)業(yè)務(wù)理解:
(1)加載樹(shù)控件馋贤。(本項(xiàng)目使用的是easyui-tree插件,第一次傳遞的cid=0)
(2)確定異步樹(shù)請(qǐng)求的參數(shù)及返回的節(jié)點(diǎn)結(jié)構(gòu)畏陕。(要構(gòu)建easyui-tree對(duì)應(yīng)的業(yè)務(wù)模型VO配乓,id、text、status)
(3)請(qǐng)求數(shù)據(jù)庫(kù)犹芹,生成樹(shù)結(jié)構(gòu)崎页。(根據(jù)parent_id字段查詢子節(jié)點(diǎn)實(shí)現(xiàn)。)
2.2.實(shí)現(xiàn)步驟
2.2.1.第一步:加載樹(shù)控件
(1)定義類目選擇的按鈕腰埂。(點(diǎn)擊按鈕实昨,加載異步樹(shù)控件)
(2)加載異步樹(shù)控件
查看EasyUI的API文檔,我們知道:url是請(qǐng)求路徑盐固。
2.2.2.第二步:確定加載樹(shù)請(qǐng)求的參數(shù)
查看API文檔荒给,我們知道請(qǐng)求的參數(shù)名是id,是當(dāng)前節(jié)點(diǎn)的id值刁卜。
2.2.3.第三步:確定樹(shù)節(jié)點(diǎn)結(jié)構(gòu)
查看API文檔志电,節(jié)點(diǎn)包括id、text蛔趴、state三個(gè)基本屬性挑辆。
2.2.4.第四步:java代碼實(shí)現(xiàn)異步樹(shù)
2.2.4.1.Step1:代碼結(jié)構(gòu)
Controller:負(fù)載從頁(yè)面接收節(jié)點(diǎn)的id,返回該節(jié)點(diǎn)的所有子節(jié)點(diǎn)孝情;
Service:實(shí)現(xiàn)查詢邏輯鱼蝉,根據(jù)父節(jié)點(diǎn)id,查詢所有的子節(jié)點(diǎn)
Mapper:基于BASEMapper實(shí)現(xiàn)
2.2.4.2.Step2:請(qǐng)求響應(yīng)格式
<colgroup><col style="width: 130px;"><col style="width: 358px;"></colgroup>
請(qǐng)求路徑 /item/cat/list
請(qǐng)求參數(shù) id=nodeId(首次加載生成一級(jí)目錄時(shí)箫荡,默認(rèn)id=0)
響應(yīng)格式 {“id”:”1” “text”:”node1” “state”:”open}
2.2.4.3.Step3:創(chuàng)建EUTreeNode類
在ego-base工程中創(chuàng)建魁亦。
//自定義異步樹(shù)節(jié)點(diǎn)結(jié)構(gòu)
public class EUTreeNode {
private long id;
private String text;
private String state;
//補(bǔ)全get、set方法
}
2.2.4.4.Step4:創(chuàng)建ItemCat類
--在ego-base中創(chuàng)建
@TableName(value="tb_item_cat")
public class ItemCat {
@TableId(value="id",type=IdType.AUTO)
private Long id;
@TableField(value="parent_id")
private Long parentId;
private String name;
private int status;
@TableField(value="sort_order")
private int sortOrder;
@TableField(value="is_parent")
private byte isParent;
private Date created;
private Date updated;
public ItemCat() {
super();
}
public Long getId() {
return id;
}
// 補(bǔ)全get羔挡、set方法
}
2.2.4.5.Step5:創(chuàng)建ItemCatMapper接口
--在ego-base中創(chuàng)建
package cn.gzsxt.base.mapper;
import com.baomidou.mybatisplus.mapper.BaseMapper;
import cn.gzsxt.base.pojo.ItemCat;
public interface ItemCatMapper extends BaseMapper<ItemCat>{
}
2.2.4.6.Step6:創(chuàng)建ItemCatService接口及實(shí)現(xiàn)類
在ego-manager項(xiàng)目中創(chuàng)建洁奈。
package cn.gzsxt.manager.service.impl;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import cn.gzsxt.base.mapper.ItemCatMapper;
import cn.gzsxt.base.pojo.ItemCat;
import cn.gzsxt.base.vo.EUTreeNode;
import cn.gzsxt.manager.service.ItemCatService;
@Service
public class ItemCatServiceImpl extends ServiceImpl<ItemCatMapper, ItemCat> implements ItemCatService{
@Override
public List<EUTreeNode> getByParentId(Long parentId) {
List<EUTreeNode> nodes = new ArrayList<>();
EntityWrapper<ItemCat> ew = new EntityWrapper<>();
ew.eq("parent_id", parentId);
List<ItemCat> selectList = selectList(ew);
EUTreeNode node = null;
for (ItemCat itemCat : selectList) {
node = new EUTreeNode();
node.setId(itemCat.getId());
node.setText(itemCat.getName());
if(1==itemCat.getIsParent()){
node.setState("closed");
}else{
node.setState("open");
}
nodes.add(node);
}
return nodes;
}
}
2.2.4.7.Step7:創(chuàng)建ItemCatController類
package cn.gzsxt.manager.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 cn.gzsxt.common.pojo.EUTreeNode;
import cn.gzsxt.manager.service.ItemCatService;
@Controller
@RequestMapping("/item/cat")
public class ItemCatController {
@Autowired
private ItemCatService catService;
@RequestMapping(value="/list")
@ResponseBody
public List<EUTreeNode> initTreeByParentId(@RequestParam(defaultValue="0")Long id){
List<EUTreeNode> list = catService.getByParantId(id);
return list;
}
}
2.3.保存類目id到頁(yè)面表單
說(shuō)明:當(dāng)點(diǎn)擊葉子節(jié)點(diǎn)時(shí),將該節(jié)點(diǎn)的id值绞灼,保存到頁(yè)面表單中利术。
類目id的值,保存在頁(yè)面表單的位置:
3.第二部分:實(shí)現(xiàn)商品圖片上傳功能
3.1.傳統(tǒng)上傳方式的問(wèn)題
在傳統(tǒng)上傳方式中低矮,在項(xiàng)目的跟目錄下創(chuàng)建upload目錄印叁,將圖片上傳到tomcat服務(wù)器中。
但是在分布式環(huán)境下军掂,是有多個(gè)Tomcat存在的轮蜕,當(dāng)把圖片直接上傳到Tomcat服務(wù)器時(shí),容易出現(xiàn)圖片丟失的問(wèn)題良姆。
3.2.分布式系統(tǒng)圖片上傳方案
3.2.1.思路分析
直接將圖片上傳到一個(gè)指定的目錄肠虽,訪問(wèn)、下載圖片都訪問(wèn)這個(gè)目錄玛追。
由于項(xiàng)目最終是要部署到Linux環(huán)境税课,所以直接將圖片上傳到Linux服務(wù)器闲延。
問(wèn)題:那如何將圖片上傳到Linux呢?
答:使用vsftpd組件韩玩,實(shí)現(xiàn)文件傳輸垒玲。
3.2.2.vsftpd簡(jiǎn)介
問(wèn)題1:vsftpd是什么?
答:ftp(File Transfer Protocol)文件傳輸協(xié)議找颓。(實(shí)現(xiàn)不同操作系統(tǒng)之間文件的傳輸??
vsftpd是一個(gè)基于ftp協(xié)議的文件傳輸服務(wù)器軟件合愈。
問(wèn)題2:vsftpd作用是什么?
答:傳輸文件的文件服務(wù)器击狮。(跨平臺(tái)佛析、跨操作系統(tǒng))
問(wèn)題3:如何使用?
答:服務(wù)端:在linux安裝vsftpd軟件彪蓬,開(kāi)啟服務(wù)寸莫。
客戶端:通過(guò)FtpClient客戶端建立和服務(wù)器的連接,向服務(wù)器發(fā)送請(qǐng)求档冬。
3.3.實(shí)現(xiàn)步驟說(shuō)明
(1)在Linux上安裝vsftpd服務(wù)膘茎。
(2)根據(jù)圖片的地址訪問(wèn)圖片。(最終保存到數(shù)據(jù)庫(kù)的是圖片的路徑)
(3)web工程中實(shí)現(xiàn)圖片上傳酷誓。
3.4.實(shí)現(xiàn)步驟
3.4.1.第一部分:在Linux上部署vsftpd服務(wù)
思路 :(1)安裝軟件
(2)測(cè)試服務(wù)是否可用
3.4.1.1.第一步:安裝vsftpd軟件
[root@node0719 ~]# yum -y install vsftpd
3.4.1.2.第二步:關(guān)閉匿名訪問(wèn)
修改vsftpd配置文件 vim /etc/vsftpd/vsftpd.conf
3.4.1.3.第三步:添加一個(gè)FTP用戶
創(chuàng)建一個(gè)用戶披坏,專門用來(lái)訪問(wèn)vsftpd服務(wù)。
[root@node0719 ~]# useradd ftpuser
[root@node0719 ~]# passwd ftpuser
3.4.1.4.第四步:設(shè)置防火墻
vsftpd服務(wù)默認(rèn)端口號(hào)為21盐数,修改防火墻棒拂,開(kāi)放此端口,重啟防火墻娘扩。
[root@node0719 ~]# vim /etc/sysconfig/iptables
[root@node0719 ~]# service iptables restart
12.第五步:修改selinux(Linux安全內(nèi)核系統(tǒng))
(1)先查看selinux着茸,默認(rèn)是禁用了ftp訪問(wèn)的。
[root@bogon ~]# getsebool -a | grep ftp
allow_ftpd_anon_write --> off
allow_ftpd_full_access --> off
allow_ftpd_use_cifs --> off
allow_ftpd_use_nfs --> off
ftp_home_dir --> off
ftpd_connect_db --> off
ftpd_use_passive_mode --> off
httpd_enable_ftp_server --> off
tftp_anon_write --> off
(2)修改selinux琐旁,開(kāi)放ftp訪問(wèn)權(quán)限
[root@bogon ~]# setsebool -P allow_ftpd_full_access on
[root@bogon ~]# setsebool -P ftp_home_dir on
13.第六步:?jiǎn)?dòng)vsftpd服務(wù)
[root@node0719 vsftpd]# service vsftpd start
為 vsftpd 啟動(dòng) vsftpd: [確定]
14.第七步:通過(guò)瀏覽器訪問(wèn)測(cè)試
訪問(wèn)地址:ftp://192.168.23.12:21,發(fā)現(xiàn)無(wú)法訪問(wèn)猜绣。
原因:被動(dòng)模式下灰殴,數(shù)據(jù)傳輸服務(wù)被防火墻攔截了。
(1)被動(dòng)模式
第二次請(qǐng)求過(guò)程中掰邢,客戶端跟服務(wù)端建立數(shù)據(jù)通道牺陶;
服務(wù)端被動(dòng)將數(shù)據(jù)響應(yīng)給客戶端。
第二次請(qǐng)求數(shù)據(jù)傳輸辣之,會(huì)隨機(jī)生成一個(gè)服務(wù)端口掰伸。被防火墻禁用。
(2)主動(dòng)模式
服務(wù)端主動(dòng)向客戶端發(fā)送數(shù)據(jù)怀估,會(huì)被客戶端的防火墻禁掉狮鸭。
多數(shù)客戶端不支持主動(dòng)模式合搅,不安全。
3.4.1.8.第八步:配置被動(dòng)模式
(1)編輯/etc/vsftpd/vsftpd.conf文件
[root@bogon ~]# vim /etc/vsftpd/vsftpd.conf
(2)添加防火墻范圍設(shè)置(在文件尾部添加即可):
pasv_min_port=30000
pasv_max_port=30999
(3)修改防火墻歧蕉,開(kāi)啟30000:30999之間所有的端口灾部。
(4)重啟防火墻。
(5)重啟vsftpd服務(wù)
再次訪問(wèn)瀏覽器惯退,發(fā)現(xiàn)可以正常連接了赌髓。
3.4.1.9.第九步:java代碼測(cè)試上傳功能
Java代碼中,是通過(guò)FtpClient客戶端建立和服務(wù)端的連接的催跪。在ego-base工程中測(cè)試锁蠕。
(1)在ego-base中添加ftp服務(wù)的依賴。
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
</dependency>
(2)創(chuàng)建測(cè)試類
說(shuō)明:使用ftpuser用戶上傳懊蒸。指定上從目錄/home/ftpuser/ego/images
注意:為了保證ftpuser有這個(gè)目錄下的寫權(quán)限荣倾,我們要用ftpuser用戶創(chuàng)建這個(gè)目錄。
su命令:切換用戶
[root@node0719 ~]#su ftpuser
[ftpuser@node0719 ~]#mkdir -p /home/ftpuser/ego/images
測(cè)試類TestFtp
package cn.gzsxt.manager.test;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.SocketException;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
public class TestFtp {
static String baseUrl = "/home/ftpuser/ego/images";
public static void main(String[] args) {
//1榛鼎、建立和服務(wù)端的連接
FTPClient client = new FTPClient();
try {
client.connect("192.168.23.12", 21);
//2逃呼、身份認(rèn)證
client.login("ftpuser", "ftpuser");
//3、指定源文件
File file = new File("F:\\圖片\\5b7a8115N89613314.jpg");
InputStream local = new FileInputStream(file);
//4者娱、指定文件上傳的方式 二進(jìn)制字節(jié)碼
client.setFileType(FTP.BINARY_FILE_TYPE);
//5抡笼、指定上傳目錄 默認(rèn)是/home/ftpuser,即ftpuser用戶的家目錄
// 切換到ftpuser用戶來(lái)創(chuàng)建目錄黄鳍。 /home/ftpuser/ego/images/
client.changeWorkingDirectory("/home/ftpuser/ego/images");
//6推姻、設(shè)置文件上傳的模式,指定為被動(dòng)模式
client.enterLocalPassiveMode();
boolean flag = client.storeFile("test.jpg", local);
if(flag){
System.out.println("上傳成功");
}else{
System.out.println("上傳失敗");
}
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.4.1.10.封裝FTPUtils工具類
package cn.gzsxt.base.utils;
import java.io.IOException;
import java.io.InputStream;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
public class FtpUtils {
FTPClient client = null;
/**
* 文件上傳
* @param hostName ftp主機(jī)名
* @param port ftp主機(jī)端口
* @param username 上傳用戶名
* @param password 上傳用戶密碼
* @param basePath 上傳基礎(chǔ)路徑
* @param filePath 文件存放路徑
* @param remoteFileName 上傳后文件名稱
* @param in 文件輸入流
* @return
*/
public static boolean upload(String hostName,int port,String username,String password,String basePath,
String filePath,String remoteFileName,InputStream in){
//1框沟、創(chuàng)建客戶端
FTPClient client = new FTPClient();
try {
//2藏古、建立和服務(wù)端的鏈接
client.connect(hostName, port);
//3、登陸服務(wù)端
client.login(username, password);
//4忍燥、指定圖片上傳的方式為二進(jìn)制拧晕,即字節(jié)流
client.setFileType(FTP.BINARY_FILE_TYPE);
//5、指定上傳的訪問(wèn)模式為被動(dòng)模式 說(shuō)明:大部分的操作系統(tǒng)梅垄,默認(rèn)的都是被動(dòng)模式厂捞,并且禁用了主動(dòng)了模式
client.enterLocalPassiveMode();
//6、指定上傳的目錄 默認(rèn)目錄 是當(dāng)前ftpuser用戶的家目錄
boolean flag = client.changeWorkingDirectory(basePath+filePath);
//如果切換目錄失敗队丝,則創(chuàng)建指定的目錄
if(!flag){
//創(chuàng)建目錄失敗靡馁,則可能是存在沒(méi)有創(chuàng)建的父目錄
if(!client.makeDirectory(basePath+filePath)){
String tempPath = basePath;
String[] split = filePath.split("/");
for (String string : split) {
if(null!=string && !"".equals(string)){
tempPath = tempPath+"/"+string;
//先判斷第一層路徑是否存在,如果不存在机久,則創(chuàng)建
if(!client.changeWorkingDirectory(tempPath)){
//如果創(chuàng)建第一層路徑成功臭墨,則判斷是否能切換到這一層路徑
if(client.makeDirectory(tempPath)){
//切換失敗,則返回false
if(!client.changeWorkingDirectory(tempPath)){
return false;
}
//如果創(chuàng)建第一層路徑失敗膘盖,則直接返回false
}else{
return false;
}
}
//如果有空路徑胧弛,則直接跳過(guò)
}else{
continue;
}
}
}else{
//創(chuàng)建成功尤误,則直接切換到指定的目錄
if(!client.changeWorkingDirectory(basePath+filePath)){
return false;
}
}
}
//8、上傳
boolean result = client.storeFile(remoteFileName, in);
return result;
} catch (Exception e) {
e.printStackTrace();
return false;
}finally {
//9叶圃,退出登錄袄膏,并關(guān)閉連接
try {
if(client.logout()){
client.disconnect();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
3.4.2.第二部分:搭建圖片服務(wù)器訪問(wèn)圖片
我們知道,圖片等靜態(tài)資源需要服務(wù)器加載掺冠,才能被訪問(wèn)到沉馆。
這里我們選擇Tengine做服務(wù)器,來(lái)加載圖片德崭。
問(wèn)題1:Tengine是什么斥黑?
答:Tengine是web服務(wù)器。
問(wèn)題2:web服務(wù)器常用種類眉厨?
答:apache锌奴、IIS、nginx
問(wèn)題3:web服務(wù)器和web應(yīng)用服務(wù)器的區(qū)別憾股?
答:web應(yīng)用服務(wù)器鹿蜀,是用來(lái)處理動(dòng)態(tài)請(qǐng)求,常見(jiàn)的以tomcat服球、jetty等servlet容器為代表????????????????????
web服務(wù)器茴恰,只能處理靜態(tài)資源請(qǐng)求。
如果要處理動(dòng)態(tài)請(qǐng)求斩熊,需要通過(guò)其動(dòng)態(tài)代理功能實(shí)現(xiàn)往枣。
問(wèn)題3:為什么不用Tomcat呢?
答:(1)Tomcat是servlet容器粉渠,處理靜態(tài)資源的速度遠(yuǎn)低于Tengine分冈。
(2)Tomcat的并發(fā)連接數(shù),遠(yuǎn)遠(yuǎn)低于Tengine霸株。
所以碗旅,這里我們選擇Tengine做圖片服務(wù)器恭理。
搭建步驟說(shuō)明:
(1)安裝Tengine赖舟。(源碼安裝)
(2)配置圖片服務(wù)宠能。
3.4.2.1.第一步:上傳、解壓
[root@node0719 ~]# tar -zxvf tengine-2.1.0.tar.gz
3.4.2.2.第二步:預(yù)編譯
預(yù)編譯作用:檢查編譯過(guò)程中所需要的依賴箫攀、環(huán)境。
依次安裝預(yù)編譯過(guò)程中幼衰,所需要的環(huán)境靴跛。(根據(jù)個(gè)人虛擬機(jī)安裝所缺環(huán)境)
[root@node07192 ~]# cd tengine-2.1.0
[root@node07192 tengine-2.1.0]# ./configure
(1)缺少c編譯環(huán)境
[root@node07192 tengine-2.1.0]# yum -y install gcc-c++
(2)缺少pcre環(huán)境
[root@node07192 tengine-2.1.0]# yum -y install pcre-devel
(3)缺少openssl環(huán)境
[root@node07192 tengine-2.1.0]# yum install -y openssl openssl-devel
(4)缺少zlib環(huán)境
[root@node07192 tengine-2.1.0]# yum install -y zlib zlib-devel
3.4.2.3.第三步:編譯
[root@node07192 tengine-2.1.0]# make
3.4.2.4.第四步:安裝
默認(rèn)安裝路徑/usr/local/nginx/
[root@node07192 tengine-2.1.0]# make install
3.4.2.5.第五步:?jiǎn)?dòng)Tengine服務(wù)器
[root@node07192 tengine-2.1.0]# cd /usr/local/nginx/sbin/
[root@node07192 sbin]# ./nginx
23.第六步:訪問(wèn)測(cè)試
(1)查看配置文件。默認(rèn)服務(wù)端口是80
[root@node07192 sbin]# cd ../conf
[root@node07192 conf]# vim nginx.conf
(2)修改防火墻渡嚣,開(kāi)發(fā)80端口梢睛。重啟防火墻
[root@node07192 conf]# vim /etc/sysconfig/iptables
[root@node07192 conf]# service iptables restart
(3)瀏覽器訪問(wèn)地址 http://192.168.23.12:80
3.4.2.7.第七步:配置圖片服務(wù)
(1)修改/conf/nginx.conf文件肥印。指定圖片根路徑和服務(wù)端口
(2)重啟tengine服務(wù)器
[root@node07192 sbin]# ./nginx -s reload
(3)瀏覽器訪問(wèn)圖片
注意:服務(wù)器加載的根路徑是/home/ftpuser/ego
所以瀏覽器中訪問(wèn)圖片的目錄為/images/+圖片名稱.jpg
(4)解決訪問(wèn)圖片的權(quán)限問(wèn)題
在第六步中,我們?cè)L問(wèn)的頁(yè)面是/html/index.html
所以:我們只需要將圖片的權(quán)限修改為index.html一致即可绝葡。
查看/html/index.html的權(quán)限
修改ftpuser目錄的權(quán)限為可讀深碱、可執(zhí)行
[root@node07192 nginx]# chmod 705 /home/ftpuser
(5)重新訪問(wèn)圖片,成功!!!
圖片訪問(wèn)路徑說(shuō)明:
圖片真實(shí)目錄時(shí) /home/ftpuser/ego/images
在Tengine中藏畅,設(shè)置得圖片資源的根目錄為 /home/ftpuser/ego
意味著敷硅,我們每次請(qǐng)求圖片的時(shí)候,是直接到/home/ftpuser/ego這個(gè)目錄下愉阎,找圖片的绞蹦。因此圖片的訪問(wèn)路徑中,/home/ftpuser/ego這個(gè)路徑是要省掉的榜旦。
3.4.3.第三部分:SpringMVC實(shí)現(xiàn)上傳
3.4.3.1.思路
(1)使用Springmvc上傳組件幽七,從頁(yè)面表單接收?qǐng)D片
(2)使用vsftpd組件,將圖片上傳到Linux服務(wù)器溅呢。
(a)澡屡、服務(wù)端:在Linux上安裝ftp服務(wù)端vsftpd軟件,并開(kāi)啟服務(wù)咐旧。
(b)驶鹉、客戶端:在java代碼中使用FtpClient客戶端建立與服務(wù)器的連接
(3)返回值:返回圖片上傳之后的訪問(wèn)路徑。
為什么休偶?
因?yàn)楸4鎴D片到數(shù)據(jù)庫(kù)的時(shí)候梁厉,保存的就是圖片的訪問(wèn)路徑。
3.4.3.2.前端js實(shí)現(xiàn)
前端使用kindeditor踏兜,初始化上傳組件
調(diào)用上傳組件的初始化方法:
上傳組件在common.js中定義
上傳組件的初始化方法init
3.4.3.3.后臺(tái)java實(shí)現(xiàn)
3.4.3.3.1.代碼結(jié)構(gòu)
Controller:從表單接收?qǐng)D片词顾,返回圖片的回調(diào)地址
Service:創(chuàng)建FtpClient客戶端,將圖片直接上傳到Linux服務(wù)器
3.4.3.3.2.請(qǐng)求響應(yīng)格式
請(qǐng)求路徑 /pic/upload
請(qǐng)求方式 Post
請(qǐng)求參數(shù) uploadFile
返回值結(jié)構(gòu) 參考Kindeditor官方文檔(http://kindeditor.net/docs/upload.html)
Kindeditor官方文檔要求的返回格式類型
3.4.3.3.3.定義返回值類型
在ego-base工程中定義碱妆。
package cn.gzsxt.base.pojo;
/**
* KindEditer文件上傳返回格式
* @author ccnulyq
*
*/
public class UploadResult {
private int error; //0 表示成功 1表示失敗
private String url; //成功時(shí)肉盹,圖片的訪問(wèn)地址
private String message; //失敗時(shí),錯(cuò)誤信息
public PictureResult() {
super();
}
//補(bǔ)充get疹尾、set方法
}
3.4.3.3.4.在ego-manager工程中添加Springmvc上傳組件及Pom依賴
(1)上忍、修改spring-mvc.xml,添加上傳組件
<!-- 定義文件上傳解析器 -->
<bean name="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 設(shè)定默認(rèn)編碼 -->
<property name="defaultEncoding" value="UTF-8"></property>
<!-- 設(shè)定文件上傳的最大值5MB纳本,5*1024*1024 -->
<property name="maxUploadSize" value="5242880"></property>
</bean>
(2)窍蓝、修改pom.xml,添加上傳依賴common-fileupload.jar
<!-- 文件上傳組件 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
</dependency>
(3)將vsftpd服務(wù)端請(qǐng)求參數(shù)寫到properties配置文件中
#圖片上傳基本配置
FTP_HOST=192.168.4.253
FTP_PORT=21
FTP_USER=ftpuser
FTP_PASSWD=ftpuser
FTP_BASE_URL=/home/ftpuser/ego/images
PICTURE_BASE_URL=http://192.168.4.253/images
3.4.3.3.5.Service層代碼實(shí)現(xiàn)
--創(chuàng)建UploadService接口及其實(shí)現(xiàn)類
package cn.gzsxt.manager.service.impl;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import cn.gzsxt.base.utils.FtpUtils;
import cn.gzsxt.base.utils.IDUtils;
import cn.gzsxt.base.vo.UploadResult;
import cn.gzsxt.manager.service.UploadService;
@Service
public class UploadServiceImpl implements UploadService{
/*
* FTP_HOST=192.168.4.253
FTP_PORT=21
FTP_USERNAME=ftpuser
FTP_PASSWORD=ftpuser
FTP_BASE_URL=/home/ftpuser/ego/images
PICTURE_BASE_URL=http://192.168.4.253/images
*/
@Value("${FTP_HOST}")
private String FTP_HOST;
@Value("${FTP_PORT}")
private Integer FTP_PORT;
@Value("${FTP_USERNAME}")
private String FTP_USERNAME;
@Value("${FTP_PASSWORD}")
private String FTP_PASSWORD;
@Value("${FTP_BASE_URL}")
private String FTP_BASE_URL;
@Value("${PICTURE_BASE_URL}")
private String PICTURE_BASE_URL;
@Override
public UploadResult upload(MultipartFile file) {
UploadResult result = new UploadResult();
//需求:將上傳的圖片按日期來(lái)分類 /2019/02/25/1.jpg
Date date = new Date();
//獲取日期的目錄格式
String filePath = "/"+ new SimpleDateFormat("yyyy").format(date)+
"/"+new SimpleDateFormat("MM").format(date)+
"/"+new SimpleDateFormat("dd").format(date);
//獲取圖片的類型 .jpg .png
String originalFilename = file.getOriginalFilename();
String filtType = originalFilename.substring(originalFilename.lastIndexOf("."));
String remoteFileName = IDUtils.getImageName()+filtType;
try {
boolean upload = FtpUtils.upload(FTP_HOST, FTP_PORT, FTP_USERNAME, FTP_PASSWORD, FTP_BASE_URL, filePath, remoteFileName, file.getInputStream());
if(upload){
result.setError(0);
// 192.168.4.253/images /2019/02/25 / 111111.jpg
result.setUrl(PICTURE_BASE_URL+filePath+"/"+remoteFileName);
}else{
result.setError(1);
result.setMessage("上傳失敗繁成,請(qǐng)稍后再試吓笙!");
}
} catch (IOException e) {
e.printStackTrace();
result.setError(1);
result.setMessage("上傳失敗,請(qǐng)稍后再試巾腕!");
}
return result;
}
}
3.4.3.3.6.ID生成工具類
package org.ranger.base.utils;
import java.util.Random;
/**
* 各種id生成策略
*/
public class IDUtils {
/**
* 圖片名生成
*/
public static String getImageName() {
//取當(dāng)前時(shí)間的長(zhǎng)整形值包含毫秒
long millis = System.currentTimeMillis();
//long millis = System.nanoTime();
//加上三位隨機(jī)數(shù)
Random random = new Random();
int end3 = random.nextInt(999);
//如果不足三位前面補(bǔ)0
String str = millis + String.format("%03d", end3);
return str;
}
/**
* 商品id生成
*/
public static long getItemId() {
//取當(dāng)前時(shí)間的長(zhǎng)整形值包含毫秒
long millis = System.currentTimeMillis();
//long millis = System.nanoTime();
//加上兩位隨機(jī)數(shù)
Random random = new Random();
int end2 = random.nextInt(99);
//如果不足兩位前面補(bǔ)0
String str = millis + String.format("%02d", end2);
long id = new Long(str);
return id;
}
}
3.4.3.3.7.Controller層代碼實(shí)現(xiàn)
--創(chuàng)建UploadController類
package cn.gzsxt.manager.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.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import cn.gzsxt.base.vo.UploadResult;
import cn.gzsxt.manager.service.UploadService;
@Controller
public class UploadController {
@Autowired
private UploadService uploadService;
@RequestMapping(value="/pic/upload",method=RequestMethod.POST)
@ResponseBody
public UploadResult upload(MultipartFile uploadFile){
UploadResult result = uploadService.upload(uploadFile);
return result;
}
}
3.4.3.3.8.測(cè)試結(jié)果面睛,上傳成功!!!
3.4.3.4.將上傳結(jié)果保存到頁(yè)面表單域
頁(yè)面效果
4.第三部分:kindEditor編輯商品屬性
純前端js實(shí)現(xiàn)絮蒿,不需要java后臺(tái)代碼支持。
原理:內(nèi)置了一個(gè)HTML編輯器叁鉴,將HTML頁(yè)面轉(zhuǎn)換成文本類型土涝,將值傳給指定的元素。
5.第四部分:商品規(guī)格參數(shù)
5.1.格式
規(guī)格分組1
|-規(guī)格項(xiàng)1:規(guī)格值1
|-規(guī)格項(xiàng)2:規(guī)格值2
|-規(guī)格項(xiàng)n:規(guī)格值n
規(guī)格分組2
|-規(guī)格項(xiàng)11:規(guī)格值11
|-規(guī)格項(xiàng)22:規(guī)格值22
|-規(guī)格項(xiàng)nn:規(guī)格值nn
規(guī)格分組3
|-規(guī)格項(xiàng)112:規(guī)格值112
|-規(guī)格項(xiàng)222:規(guī)格值222
|-規(guī)格項(xiàng)nnn:規(guī)格值nnn
5.2.特點(diǎn)
(1)每一類商品的規(guī)格分組是相同的幌墓。
(2)每一個(gè)規(guī)格分組對(duì)應(yīng)多個(gè)規(guī)格項(xiàng)但壮。
(3)每一個(gè)商品的規(guī)格值不同。
5.3.設(shè)計(jì)思路
(1)給商品的每一個(gè)分類創(chuàng)建一個(gè)規(guī)格參數(shù)模板克锣。(tb_item_param)
(2)添加商品的時(shí)候茵肃,根據(jù)該類商品的參數(shù)模板,填寫規(guī)格值袭祟。
(3)將頁(yè)面填寫的規(guī)格值验残,保存到數(shù)據(jù)庫(kù)。(tb_item_param_item)
5.4.實(shí)現(xiàn)流程
(1)添加商品規(guī)格參數(shù)模板
(2)根據(jù)規(guī)格參數(shù)模板生成規(guī)格值
5.4.1.第一部分:創(chuàng)建規(guī)格參數(shù)模板
5.4.1.1.第一步:判斷是否已經(jīng)添加規(guī)格參數(shù)模板
(1)js實(shí)現(xiàn)
(2)請(qǐng)求響應(yīng)格式
請(qǐng)求路徑 /item/param/query/itemcatid/{itemCatId}
請(qǐng)求方式 GET
請(qǐng)求參數(shù) /{itemCatId} 路徑變量巾乳,商品類目id
響應(yīng)結(jié)果 {status:200 data:data}
(3)創(chuàng)建ItemParam類
package cn.gzsxt.base.pojo;
import java.util.Date;
import com.baomidou.mybatisplus.annotations.TableField;
import com.baomidou.mybatisplus.annotations.TableId;
import com.baomidou.mybatisplus.annotations.TableName;
import com.baomidou.mybatisplus.enums.IdType;
@TableName(value="tb_item_param")
public class ItemParam {
@TableId(value="id",type=IdType.AUTO)
private Long id;
@TableField(value="item_cat_id")
private long itemCatId;
@TableField(value="param_data")
private String paramData;
private Date created;
private Date updated;
public ItemParam() {
super();
}
//補(bǔ)全get您没、set方法
}
(4)創(chuàng)建EgoResult返回值類
--說(shuō)明:在ego-base中定義,并修改pom文件胆绊,添加json依賴
package cn.gzsxt.base.vo;
import java.util.List;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* 好易購(gòu)商城自定義響應(yīng)結(jié)構(gòu)
*/
public class EgoResult {
// 定義jackson對(duì)象
private static final ObjectMapper MAPPER = new ObjectMapper();
// 響應(yīng)業(yè)務(wù)狀態(tài)
private Integer status;
// 響應(yīng)消息
private String msg;
// 響應(yīng)中的數(shù)據(jù)
private Object data;
public static EgoResult build(Integer status, String msg, Object data) {
return new EgoResult(status, msg, data);
}
public static EgoResult ok(Object data) {
return new EgoResult(data);
}
public static EgoResult ok() {
return new EgoResult(null);
}
public EgoResult() {
}
public static EgoResult build(Integer status, String msg) {
return new EgoResult(status, msg, null);
}
public EgoResult(Integer status, String msg, Object data) {
this.status = status;
this.msg = msg;
this.data = data;
}
public EgoResult(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結(jié)果集轉(zhuǎn)化為EgoResult對(duì)象
*
* @param jsonData json數(shù)據(jù)
* @param clazz EgoResult中的object類型
* @return
*/
public static EgoResult formatToPojo(String jsonData, Class<?> clazz) {
try {
if (clazz == null) {
return MAPPER.readValue(jsonData, EgoResult.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;
}
}
/**
* 沒(méi)有object對(duì)象的轉(zhuǎn)化
*
* @param json
* @return
*/
public static EgoResult format(String json) {
try {
return MAPPER.readValue(json, EgoResult.class);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Object是集合轉(zhuǎn)化
*
* @param jsonData json數(shù)據(jù)
* @param clazz 集合中的類型
* @return
*/
public static EgoResult 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;
}
}
}
(5)創(chuàng)建ItemParamMapper接口
--說(shuō)明:在ego-base中創(chuàng)建
package cn.gzsxt.base.mapper;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import com.baomidou.mybatisplus.mapper.BaseMapper;
import cn.gzsxt.base.pojo.ItemParam;
public interface ItemParamMapper extends BaseMapper<ItemParam>{
}
(6)Service層實(shí)現(xiàn)
--創(chuàng)建ItemParamService接口及其實(shí)現(xiàn)類
package cn.gzsxt.manager.service.impl;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import cn.gzsxt.base.mapper.ItemParamMapper;
import cn.gzsxt.base.pojo.ItemParam;
import cn.gzsxt.base.vo.EUDataGridResult;
import cn.gzsxt.base.vo.EgoResult;
import cn.gzsxt.manager.service.ItemParamService;
@Service
public class ItemParamServiceImpl implements ItemParamService{
@Autowired
private ItemParamMapper itemParamMapper;
@Override
public EgoResult getByItemCatId(long catId) {
EntityWrapper<ItemParam> ew = new EntityWrapper<>();
ew.eq("item_cat_id", catId);
List<ItemParam> selectList = itemParamMapper.selectList(ew);
if(null!=selectList && selectList.size()>0){
return EgoResult.ok(selectList.get(0));
}
return EgoResult.build(400, "沒(méi)有查到該類商品的模板");
}
}
(4)Controller層實(shí)現(xiàn)
--創(chuàng)建ItemParamController類
package cn.gzsxt.manager.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import cn.gzsxt.base.vo.EUDataGridResult;
import cn.gzsxt.base.vo.EgoResult;
import cn.gzsxt.manager.service.ItemParamService;
@Controller
@RequestMapping("/item/param")
public class ItemParamController {
@Autowired
private ItemParamService service;
@RequestMapping("/query/itemcatid/{itemcatid}")
@ResponseBody
public EgoResult selectByCatId(@PathVariable("itemcatid")Long itemCatId){
EgoResult result = service.getByItemCatId(itemCatId);
return result;
}
}
5.4.1.2.第二步:添加規(guī)格參數(shù)模板
(1)前端js實(shí)現(xiàn)
(2)后臺(tái)java代碼實(shí)現(xiàn)
請(qǐng)求路徑 /item/param/save/{cid}
請(qǐng)求方式 POST
請(qǐng)求參數(shù) /{cid} (類目id) 氨鹏;paramData (json格式)
響應(yīng)結(jié)果 EgoResult
(3)Service層實(shí)現(xiàn)
--修改ItemParamService接口及其實(shí)現(xiàn)類,添加保存方法
//只有配置了rollbackFor = Exception.class压状,在service對(duì)異常進(jìn)行處理時(shí)仆抵,才會(huì)有回滾
@Transactional(rollbackFor = Exception.class)
@Override
public EgoResult save(Long itemCatId, String paramData) {
try {
ItemParam entity = new ItemParam();
entity.setItemCatId(itemCatId);
entity.setParamData(paramData);
entity.setCreated(new Date());
entity.setUpdated(entity.getCreated());
itemParamMapper.insert(entity);
return EgoResult.ok();
} catch (Exception e) {
e.printStackTrace();
return EgoResult.build(400, "保存失敗");
}
}
(4)Controller層實(shí)現(xiàn)
--修改ItemParamController,添加保存方法
@RequestMapping("/save/{cid}")
@ResponseBody
public EgoResult saveItemParam(@PathVariable Long cid, String paramData) {
EgoResult result = itemParamService.saveItemParam(cid, paramData);
}
5.4.2.第二部分:根據(jù)參數(shù)模板生成商品規(guī)格參數(shù)值表單
新增商品 --> 選擇類目 --> 查找類目所對(duì)應(yīng)的模板 --> 生成表單
(1)前端js實(shí)現(xiàn)
2. java后臺(tái)(已實(shí)現(xiàn))
5.4.2.1.第一步:修改Controller代碼
@RequestMapping("/save/{catId}")
@ResponseBody
public EgoResult save(@PathVariable("catId")Long catId,String paramData){
EgoResult result = service.save(catId, paramData);
return result;
}
5.4.2.2.第二步:修改Service代碼
@Override
public EgoResult save(Long catId, String paramData) {
ItemParam param = new ItemParam();
param.setItemCatId(catId);
param.setParamData(paramData);
param.setCreated(new Date());
param.setUpdated(param.getCreated());
mapper.insert(param);
return EgoResult.ok();
}
6.第五部分:保存商品
保存商品种冬,需要同時(shí)保存商品信息镣丑、商品的描述信息和商品的規(guī)格參數(shù),分別對(duì)應(yīng)表tb_item娱两、tb_item_desc莺匠、tb_item_param_item三張表。
6.1.前端js實(shí)現(xiàn)
6.1.2.使用KindEditor富文本編輯器十兢,編輯商品描述信息
6.1.3.將商品規(guī)格參數(shù)表單數(shù)據(jù)趣竣,轉(zhuǎn)換成json格式
6.1.4.提交保存商品請(qǐng)求
6.2.后臺(tái)java實(shí)現(xiàn)
6.2.1.請(qǐng)求響應(yīng)格式
請(qǐng)求路徑 /item/save
請(qǐng)求方式 POST
請(qǐng)求參數(shù) TbItem、desc旱物、itemParams
響應(yīng)格式 {“status”:200 data:data} 參考http響應(yīng)格式
6.2.2.代碼結(jié)構(gòu)
Controller:從表單接收數(shù)據(jù)遥缕,封裝到JavaBean中
Service:實(shí)現(xiàn)保存邏輯,防止事務(wù)一致性問(wèn)題宵呛。
Mapper:Mybatis-plus實(shí)現(xiàn)
6.2.3.創(chuàng)建pojo
--在ego-base工程中創(chuàng)建
(1)創(chuàng)建ItemDesc類
package cn.gzsxt.base.pojo;
import java.util.Date;
import com.baomidou.mybatisplus.annotations.TableField;
import com.baomidou.mybatisplus.annotations.TableId;
import com.baomidou.mybatisplus.annotations.TableName;
import com.baomidou.mybatisplus.enums.IdType;
@TableName(value="tb_item_desc")
public class ItemDesc {
@TableId(value="item_id",type=IdType.INPUT)
private Long itemId;
@TableField(value="item_desc")
private String itemDesc;
private Date created;
private Date updated;
public ItemDesc() {
super();
}
// 補(bǔ)全get通砍、set方法
}
(2)創(chuàng)建ItemParamItem類
package cn.gzsxt.base.pojo;
import java.util.Date;
import com.baomidou.mybatisplus.annotations.TableField;
import com.baomidou.mybatisplus.annotations.TableId;
import com.baomidou.mybatisplus.annotations.TableName;
import com.baomidou.mybatisplus.enums.IdType;
/**商品規(guī)格參數(shù)值表
*
* 商品的規(guī)格參數(shù)(商品的描述信息)
* 做了水平拆表的處理。
*
* 好處:減小商品的表的體積,讓商品表查詢效率更高
*
* 什么情況下需要做水平拆表封孙?
* (1)大文本的字段。
* (2)這個(gè)大文本的字段不常用
*
* @author ccnulyq
*
*/
@TableName(value="tb_item_param_item")
public class ItemParamItem {
@TableId(value="id",type=IdType.AUTO)
private Long id;
@TableField(value="item_id")
private long itemId;
@TableField(value="param_data")
private String paramData;
private Date created;
private Date updated;
public ItemParamItem() {
super();
}
// 補(bǔ)全get讽营、set方法
}
6.2.4.創(chuàng)建對(duì)應(yīng)的Mapper
--說(shuō)明:在ego-base工程中創(chuàng)建
(1)創(chuàng)建ItemParamItemMapper接口
package cn.gzsxt.base.mapper;
import com.baomidou.mybatisplus.mapper.BaseMapper;
import cn.gzsxt.base.pojo.ItemParamItem;
public interface ItemParamItemMapper extends BaseMapper<ItemParamItem>{
}
(2)創(chuàng)建ItemDescMapper接口
package cn.gzsxt.base.mapper;
import com.baomidou.mybatisplus.mapper.BaseMapper;
import cn.gzsxt.base.pojo.ItemDesc;
public interface ItemDescMapper extends BaseMapper<ItemDesc>{
}
6.2.5.Service代碼實(shí)現(xiàn)
--修改ItemService接口及其實(shí)現(xiàn)類虎忌,新增save方法
--注意:注入ItemDescMapper、ItemParamItemMapper
@Service
public class ItemServiceImpl extends ServiceImpl<ItemMapper, Item> implements ItemService{
@Autowired
private ItemDescMapper descMapper;
@Autowired
private ItemParamItemMapper itemParamMapper;
@Transactional(rollbackFor=Exception.class)
@Override
public EgoResult save(Item item, String desc, String paramData) {
try {
long itemId = IDUtils.getItemId();
item.setStatus((byte) 1);
item.setId(itemId);
item.setCreated(new Date());
item.setUpdated(item.getCreated());
this.baseMapper.insert(item);
//保存商品的描述信息
ItemDesc itemDesc = new ItemDesc();
itemDesc.setItemId(itemId);
itemDesc.setItemDesc(desc);
itemDesc.setCreated(item.getCreated());
itemDesc.setUpdated(item.getUpdated());
descMapper.insert(itemDesc);
//保存商品的規(guī)格參數(shù)值
ItemParamItem paramItem = new ItemParamItem();
paramItem.setItemId(itemId);
paramItem.setParamData(paramData);
paramItem.setCreated(item.getCreated());
paramItem.setUpdated(item.getCreated());
itemParamMapper.insert(paramItem);
return EgoResult.ok();
} catch (Exception e) {
e.printStackTrace();
}
return EgoResult.build(400, "保存失敗橱鹏,請(qǐng)稍后再試");
}
}
6.2.6.Controller代碼實(shí)現(xiàn)
--修改ItemController類膜蠢,新增save方法
@RequestMapping(value="/save",method=RequestMethod.POST)
@ResponseBody
public EgoResult save(Item item,String desc,String itemParams){
EgoResult result = itemService.save(item, desc, itemParams);
return result;
}
7.商品規(guī)格參數(shù)列表實(shí)現(xiàn)
7.1.思路
商品規(guī)格參數(shù)列表的數(shù)據(jù),分別存在了tb_item_param和tb_item_cat兩張表中莉兰,因此在mapper層挑围,需要自定義查詢方法,并分頁(yè)
7.2.前端js實(shí)現(xiàn)
使用的是easyu-datagrid插件糖荒,使用方法參考商品列表實(shí)現(xiàn)(第一天內(nèi)容)杉辙。
7.3.后臺(tái)代碼實(shí)現(xiàn)
7.3.1.確定請(qǐng)求響應(yīng)格式
請(qǐng)求路徑 /item/param/list
請(qǐng)求方式 Get
請(qǐng)求參數(shù) page、rows(分頁(yè))
返回值類型 EUDataGridResult類型
7.3.2.Mapper實(shí)現(xiàn)
--說(shuō)明:連表查詢下捶朵,需要自定義查詢方法蜘矢,基于注解實(shí)現(xiàn)
--修改ItemParamMapper接口,新增查詢方法
public interface ItemParamMapper extends BaseMapper<ItemParam>{
@Select(value="select p.id,p.item_cat_id as itemCatId,t.name as itemCatName,p.param_data as paramData,p.created,p.updated "
+ "from tb_item_param p left join tb_item_cat t on p.item_cat_id = t.id "
+ "limit ${start},${pageSize}")
List<Map<String, Object>> listAndPage(@Param("start")int start,@Param("pageSize")int pageSize);
}
7.3.3.Service層實(shí)現(xiàn)
--修改ItemParamService接口及其實(shí)現(xiàn)類
@Override
public EUDataGridResult listAndPage(int curPage, int pageSize) {
List<Map<String, Object>> list = itemParamMapper.listAndPage((curPage-1)*pageSize, pageSize);
Integer count = itemParamMapper.selectCount(null);
EUDataGridResult result = new EUDataGridResult();
result.setRows(list);
result.setTotal(count);
return result;
}
7.3.4.Controller層實(shí)現(xiàn)
--修改ItemParamController接口
@RequestMapping("/list")
@ResponseBody
public EUDataGridResult listAndPage(Integer page,Integer rows){
EUDataGridResult result = service.listAndPage(page, rows);
return result;
}
7.4.訪問(wèn)測(cè)試
規(guī)格參數(shù)列表實(shí)現(xiàn)W劭础F犯埂!