如何做一個(gè)電商系統(tǒng)(二)

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)系如下:

image.png

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ù)纺座。

image.png

對(duì)應(yīng)的數(shù)據(jù)庫(kù)表為tb_item_cat,表結(jié)構(gòu)為:

image.png

實(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ù)控件)

image.png

(2)加載異步樹(shù)控件

image.png

查看EasyUI的API文檔,我們知道:url是請(qǐng)求路徑盐固。

image.png

2.2.2.第二步:確定加載樹(shù)請(qǐng)求的參數(shù)

查看API文檔荒给,我們知道請(qǐng)求的參數(shù)名是id,是當(dāng)前節(jié)點(diǎn)的id值刁卜。

image.png

2.2.3.第三步:確定樹(shù)節(jié)點(diǎn)結(jié)構(gòu)

查看API文檔志电,節(jié)點(diǎn)包括id、text蛔趴、state三個(gè)基本屬性挑辆。

image.png

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è)面表單中利术。

image.png

類目id的值,保存在頁(yè)面表單的位置:

image.png

3.第二部分:實(shí)現(xiàn)商品圖片上傳功能

3.1.傳統(tǒng)上傳方式的問(wèn)題

在傳統(tǒng)上傳方式中低矮,在項(xiàng)目的跟目錄下創(chuàng)建upload目錄印叁,將圖片上傳到tomcat服務(wù)器中。

image.png

但是在分布式環(huán)境下军掂,是有多個(gè)Tomcat存在的轮蜕,當(dāng)把圖片直接上傳到Tomcat服務(wù)器時(shí),容易出現(xiàn)圖片丟失的問(wèn)題良姆。

image.png

3.2.分布式系統(tǒng)圖片上傳方案

3.2.1.思路分析

直接將圖片上傳到一個(gè)指定的目錄肠虽,訪問(wèn)、下載圖片都訪問(wèn)這個(gè)目錄玛追。

由于項(xiàng)目最終是要部署到Linux環(huán)境税课,所以直接將圖片上傳到Linux服務(wù)器闲延。

image.png

問(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

image.png

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ù)端口掰伸。被防火墻禁用。

image.png

(2)主動(dòng)模式

服務(wù)端主動(dòng)向客戶端發(fā)送數(shù)據(jù)怀估,會(huì)被客戶端的防火墻禁掉狮鸭。

多數(shù)客戶端不支持主動(dòng)模式合搅,不安全。

image.png

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ù)

image.png

再次訪問(wèn)瀏覽器惯退,發(fā)現(xiàn)可以正常連接了赌髓。

image.png

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)境

image.png
[root@node07192 tengine-2.1.0]# yum -y install gcc-c++

(2)缺少pcre環(huán)境

image.png
[root@node07192 tengine-2.1.0]# yum -y install pcre-devel

(3)缺少openssl環(huán)境

image.png
[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
image.png

(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

image.png

3.4.2.7.第七步:配置圖片服務(wù)

(1)修改/conf/nginx.conf文件肥印。指定圖片根路徑和服務(wù)端口

image.png

(2)重啟tengine服務(wù)器

[root@node07192 sbin]# ./nginx -s reload

(3)瀏覽器訪問(wèn)圖片

注意:服務(wù)器加載的根路徑是/home/ftpuser/ego

所以瀏覽器中訪問(wèn)圖片的目錄為/images/+圖片名稱.jpg

image.png

(4)解決訪問(wèn)圖片的權(quán)限問(wèn)題

在第六步中,我們?cè)L問(wèn)的頁(yè)面是/html/index.html

所以:我們只需要將圖片的權(quán)限修改為index.html一致即可绝葡。

查看/html/index.html的權(quán)限

image.png
image.png

修改ftpuser目錄的權(quán)限為可讀深碱、可執(zhí)行

[root@node07192 nginx]# chmod 705 /home/ftpuser

(5)重新訪問(wèn)圖片,成功!!!

image.png

圖片訪問(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踏兜,初始化上傳組件

image.png

調(diào)用上傳組件的初始化方法:

image.png

上傳組件在common.js中定義

image.png

上傳組件的初始化方法init

image.png

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官方文檔要求的返回格式類型

image.png
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é)果面睛,上傳成功!!!
image.png

3.4.3.4.將上傳結(jié)果保存到頁(yè)面表單域

image.png
image.png

頁(yè)面效果

image.png

4.第三部分:kindEditor編輯商品屬性

純前端js實(shí)現(xiàn)絮蒿,不需要java后臺(tái)代碼支持。

原理:內(nèi)置了一個(gè)HTML編輯器叁鉴,將HTML頁(yè)面轉(zhuǎn)換成文本類型土涝,將值傳給指定的元素。

image.png

5.第四部分:商品規(guī)格參數(shù)

image.png

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ì)思路

image.png

(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)

image.png

(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)

image.png
image.png

(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)

image.png
image.png

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富文本編輯器十兢,編輯商品描述信息

image.png

6.1.3.將商品規(guī)格參數(shù)表單數(shù)據(jù)趣竣,轉(zhuǎn)換成json格式

image.png

6.1.4.提交保存商品請(qǐng)求

image.png

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è)

image.png

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犯埂!

image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末红碑,一起剝皮案震驚了整個(gè)濱河市舞吭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌析珊,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件唾琼,死亡現(xiàn)場(chǎng)離奇詭異兄春,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)锡溯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門赶舆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人祭饭,你說(shuō)我怎么就攤上這事芜茵。” “怎么了倡蝙?”我有些...
    開(kāi)封第一講書人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵九串,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng)猪钮,這世上最難降的妖魔是什么品山? 我笑而不...
    開(kāi)封第一講書人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮烤低,結(jié)果婚禮上肘交,老公的妹妹穿的比我還像新娘。我一直安慰自己扑馁,他們只是感情好涯呻,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著腻要,像睡著了一般复罐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上雄家,一...
    開(kāi)封第一講書人閱讀 51,679評(píng)論 1 305
  • 那天效诅,我揣著相機(jī)與錄音,去河邊找鬼咳短。 笑死填帽,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的咙好。 我是一名探鬼主播篡腌,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼勾效!你這毒婦竟也來(lái)了嘹悼?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤层宫,失蹤者是張志新(化名)和其女友劉穎杨伙,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體萌腿,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡限匣,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了毁菱。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片米死。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖贮庞,靈堂內(nèi)的尸體忽然破棺而出峦筒,到底是詐尸還是另有隱情,我是刑警寧澤窗慎,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布物喷,位于F島的核電站卤材,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏峦失。R本人自食惡果不足惜扇丛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望宠进。 院中可真熱鬧晕拆,春花似錦、人聲如沸材蹬。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)堤器。三九已至,卻和暖如春末贾,著一層夾襖步出監(jiān)牢的瞬間闸溃,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工拱撵, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留辉川,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓拴测,卻偏偏與公主長(zhǎng)得像乓旗,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子集索,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355