暢購商城 04商品發佈

第4章 商品發佈

1 SPU與SKU

1.1 SPU與SKU概念

SPU = Standard Product Unit (標準產品單位)

  • 概念 : SPU 是商品信息聚合的最小單位,是一組可複用、易檢索的標準化信息的集合,該集合描述了一個產品的特性。通俗點講,屬性值、特性相同的貨品就可以稱爲一個 SPU.

    同款商品的公共屬性抽取

    例如:小米10就是一個SPU

SKU=stock keeping unit( 庫存量單位)

  • SKU 即庫存進出計量的單位, 可以是以件、盒、托盤等爲單位。

  • SKU 是物理上不可分割的最小存貨單元。在使用時要根據不同業態,不同管理模式來處理。

    某個庫存單位的商品獨有屬性(某個商品的獨有屬性)

    例如:小米10 紅色 256G 就是一個 SKU

1.2 表結構

tb_spu 表 (SPU表)

字段名稱 字段含義 字段類型 備註
id 主鍵 BIGINT
sn 貨號 VARCHAR
name SPU名 VARCHAR
caption 副標題 VARCHAR
brand_id 品牌ID INT
category1_id 一級分類 INT
category2_id 二級分類 INT
category3_id 三級分類 INT
template_id 模板ID INT
freight_id 運費模板id INT
image 圖片 VARCHAR
images 圖片列表 VARCHAR
sale_service 售後服務 VARCHAR
introduction 介紹 TEXT
spec_items 規格列表 VARCHAR
para_items 參數列表 VARCHAR
sale_num 銷量 INT
comment_num 評論數 INT
is_marketable 是否上架 CHAR
is_enable_spec 是否啓用規格 CHAR
is_delete 是否刪除 CHAR
status 審覈狀態 CHAR

tb_sku 表(SKU商品表)

字段名稱 字段含義 字段類型 備註
id 商品id BIGINT
sn 商品條碼 VARCHAR
name SKU名稱 VARCHAR
price 價格(分) INT
num 庫存數量 INT
alert_num 庫存預警數量 INT
image 商品圖片 VARCHAR
images 商品圖片列表 VARCHAR
weight 重量(克) INT
create_time 創建時間 DATETIME
update_time 更新時間 DATETIME
spu_id SPUID BIGINT
category_id 類目ID INT
category_name 類目名稱 VARCHAR
brand_name 品牌名稱 VARCHAR
spec 規格 VARCHAR
sale_num 銷量 INT
comment_num 評論數 INT
status 商品狀態 1-正常,2-下架,0-刪除 CHAR

2 新增和修改商品

2.1 需求分析

實現商品的新增與修改功能。

(1)選擇添加的商品所屬分類
在這裏插入圖片描述
首先查詢頂級分類,也就是pid=0,然後根據用戶選擇的分類,將選擇的分類作爲pid查詢子分類。

(2)填寫SPU的信息
在這裏插入圖片描述
(3)填寫SKU信息
1559294162036
先進入選擇商品分類 再填寫商品的信息 填寫商品的屬性添加商品。

2.2 實現思路

前端傳遞給後端的數據格式 是一個spu對象和sku列表組成的JSON對象

{
  "spu": {
    "name": "這個是商品名稱",
    "caption": "這個是副標題",
    "brandId": 12,
    "category1Id": 558,
    "category2Id": 559,
    "category3Id": 560,
    "freightId": 10,
    "image": "http://www.baidu.com/image/1.jpg",
    "images": "http://www.baidu.com/image/1.jpg,http://www.baidu.com/image/2.jpg",
    "introduction": "這個是商品詳情,html代碼",
    "paraItems": {
      "出廠年份": "2019",
      "贈品": "充電器"
    },
    "saleService": "七天包退,閃電退貨",
    "sn": "020102331",
    "specItems": {
      "顏色": [
        "紅",
        "綠"
      ],
      "機身內存": [
        "64G",
        "8G"
      ]
    },
    "templateId": 42
  },
  "skuList": [
    {
      "sn": "10192010292",
      "num": 100,
      "alertNum": 20,
      "price": 900000,
      "spec": {
        "顏色": "紅",
        "機身內存": "64G"
      },
      "image": "http://www.baidu.com/image/1.jpg",
      "images": "http://www.baidu.com/image/1.jpg,http://www.baidu.com/image/2.jpg",
      "status": "1",
      "weight": 130
    },
    {
      "sn": "10192010293",
      "num": 100,
      "alertNum": 20,
      "price": 600000,
      "spec": {
        "顏色": "綠",
        "機身內存": "8G"
      },
      "image": "http://www.baidu.com/image/1.jpg",
      "images": "http://www.baidu.com/image/1.jpg,http://www.baidu.com/image/2.jpg",
      "status": "1",
      "weight": 130
    }
  ]
}

2.4 代碼實現

2.4.1 查詢分類

2.4.1.1 分析

在這裏插入圖片描述
在實現商品增加之前,需要先選擇對應的分類,選擇分類的時候,首選選擇一級分類,然後根據選中的分類,將選中的分類作爲查詢的父ID,再查詢對應的子分類集合,因此我們可以在後臺編寫一個方法,根據父類ID查詢對應的分類集合即可。

2.4.1.2 代碼實現

(1)Service層

修改com.changgou.goods.service.CategoryService添加根據父類ID查詢所有子節點,代碼如下:

    /**
     * 根據父節點ID查詢
     *
     * @param pid 父節點ID
     * @return List<Category>
     * @author Thankson
     * @date 2020年03月16日 00:13:15
     */
    List<Category> findByParentId(Integer pid);

修改com.changgou.goods.service.impl.CategoryServiceImpl添加上面的實現,代碼如下:

    @Override
    public List<Category> findByParentId(Integer pid) {
        Category category = new Category();
        category.setParentId(pid);
        return categoryMapper.select(category);
    }

(2)Controller層

修改com.changgou.goods.controller.CategoryController添加根據父ID查詢所有子類集合,代碼如下:

    /**
     * 通過父節點查詢Category
     *
     * @param pid 父節點id
     * @return Result<Category>
     * @author Thankson
     * @date 2020年03月16日 00:13:15
     */
    @RequestMapping(value = "/list/{pid}")
    public Result<Category> findByParentId(@PathVariable(value = "pid") Integer pid) {
        //防止傳入其他參數
        if(pid<0){
            pid=0;
        }
        //根據父節點id查詢
        List<Category> list = categoryService.findByParentId(pid);
        return new Result<>(true, StatusCode.SUCCESS, "查詢成功", list);
    }

測試:http://localhost:10101/category/list/0
在這裏插入圖片描述

2.4.2 查詢分類品牌數據

2.4.2.1 分析

1564378095781
用戶每次選擇了分類之後,根據用戶選擇的分類到tb_category_brand表中查詢指定的品牌集合ID,然後根據品牌集合ID查詢對應的品牌集合數據,再將品牌集合數據展示到頁面即可。

2.4.2.2 代碼實現

(1)Dao實現

修改com.changgou.goods.dao.BrandMapper添加根據分類ID查詢對應的品牌數據,代碼如下:

public interface BrandMapper extends Mapper<Brand> {

    /**
     * 查詢分類對應的品牌集合
     *
     * @param categoryId 分類id
     * @return List<Brand>
     * @author Thankson
     * @date 2020年03月16日 00:13:15
     */
    @Select("SELECT brand.* FROM tb_category_brand category_brand,tb_brand brand WHERE category_brand.category_id=#{categoryId} AND brand.id=category_brand.brand_id")
    List<Brand> findByCategory(Integer categoryId);

}

(2)Service層

修改com.changgou.goods.service.BrandService,添加根據分類ID查詢指定的品牌集合方法,代碼如下:

    /**
     * 根據分類id查詢品牌集合
     *
     * @param categoryId 分類id
     * @return List<Brand>
     * @author Thankson
     * @date 2020年03月16日 00:13:15
     */
    List<Brand> findByCategory(Integer categoryId);

修改com.changgou.goods.service.impl.BrandServiceImpl添加上面方法的實現,代碼如下:

    @Override
    public List<Brand> findByCategory(Integer categoryId) {
        return brandMapper.findByCategory(categoryId);
    }

(3)Controller層

修改com.changgou.goods.controller.BrandController,添加根據分類ID查詢對應的品牌數據方法,代碼如下:

    /**
     * 根據分類實現品牌列表查詢
     *
     * @param categoryId 分類id
     * @return Result<List < Brand>>
     * @author Thankson
     * @date 2020年03月16日 00:13:15
     */
    @GetMapping(value = "/category/{id}")
    public Result<List<Brand>> findBrandByCategory(@PathVariable(value = "id") Integer categoryId) {
        //調用Service查詢品牌數據
        List<Brand> categoryList = brandService.findByCategory(categoryId);
        return new Result<>(true, StatusCode.SUCCESS, "查詢成功!", categoryList);
    }

測試:http://localhost:10101/brand/category/558
在這裏插入圖片描述

2.4.3 模板查詢(規格參數組)

2.4.3.1 分析

在這裏插入圖片描述
商品信息填寫完畢後,開始進行商品屬性的編輯。在編輯商品屬性之前要通過分類id查詢出對應的商品模板數據。不同的商品會有不同的模板

2.4.3.2 代碼實現

(1)Service層

修改com.changgou.goods.service.TemplateService接口,添加如下方法根據分類ID查詢模板:

    /**
     * 根據分類ID查詢模板信息
     *
     * @param id 分類id
     * @return
     * @author Thankson
     * @date 2020年03月16日 00:13:15
     */
    Template findByCategoryId(Integer id);

修改com.changgou.goods.service.impl.TemplateServiceImpl添加上面方法的實現:

	@Autowired
	private CategoryMapper categoryMapper;
    @Override
    public Template findByCategoryId(Integer id) {
        //查詢分類信息
        Category category = categoryMapper.selectByPrimaryKey(id);
        //根據模板Id查詢模板信息
        return templateMapper.selectByPrimaryKey(category.getTemplateId());
    }

(2)Controller層

修改com.changgou.goods.controller.TemplateController,添加根據分類ID查詢模板數據:

/**
     * 根據分類ID查詢模板信息
     *
     * @param id 分類id
     * @return
     * @author Thankson
     * @date 2020年03月16日 00:13:15
     */
    @GetMapping(value = "/category/{id}")
    public Result<Template> findByCategoryId(@PathVariable(value = "id") Integer id) {
        //調用Service查詢
        Template template = templateService.findByCategoryId(id);
        return new Result<>(true, StatusCode.SUCCESS, "查詢成功", template);
    }

測試:http://localhost:10101/brand/category/558
在這裏插入圖片描述

2.4.4 商品屬性

2.4.4.1 分析

1564350812642
當模板信息成功獲取後,開始通過分類id來獲取商品的規格信息。

2.4.4.2 代碼實現

(1)Service層

修改com.changgou.goods.service.SpecService添加根據分類ID查詢規格列表,代碼如下:

    /**
     * 根據分類ID查詢規格列表
     *
     * @param categoryId 分類Id
     * @return List<Spec>
     * @author Thankson
     * @date 2020年03月16日 00:13:15
     */
    List<Spec> findByCategoryId(Integer categoryId);

修改com.changgou.goods.service.impl.SpecServiceImpl添加上面方法的實現,代碼如下:

	@Autowired
	private CategoryMapper categoryMapper;

    @Override
    public List<Spec> findByCategoryId(Integer categoryId) {
        //查詢分類
        Category category = categoryMapper.selectByPrimaryKey(categoryId);
        //根據分類的模板ID查詢規格
        Spec spec = new Spec();
        spec.setTemplateId(category.getTemplateId());
        return specMapper.select(spec);
    }

(2)Controller層

修改com.changgou.goods.controller.SpecController添加根據分類ID查詢規格數據,代碼如下:

    /**
     * 根據分類ID查詢對應的規格列表
     *
     * @param categoryId 分類ID
     * @return Result<List < Spec>>
     * @author Thankson
     * @date 2020年03月16日 00:13:15
     */
    @GetMapping(value = "/category/{id}")
    public Result<List<Spec>> findByCategoryId(@PathVariable(value = "id") Integer categoryId) {
        //調用Service查詢
        List<Spec> specs = specService.findByCategoryId(categoryId);
        return new Result<>(true, StatusCode.SUCCESS, "查詢成功", specs);
    }

測試:http://localhost:10101/spec/category/558
在這裏插入圖片描述

2.4.5 參數列表查詢

2.4.5.1 分析

在這裏插入圖片描述
當用戶選中分類後,需要根據分類ID查詢對應的商品參數列表

2.4.5.2 代碼實現

(1)Service層

修改com.changgou.goods.service.ParaService添加根據分類ID查詢參數列表,代碼如下:

    /**
     * 根據分類ID查詢參數列表
     *
     * @param categoryId 分類ID
     * @return List<Para>
     * @author Thankson
     * @date 2020年03月16日 00:13:15
     */
    List<Para> findByCategoryId(Integer categoryId);

修改com.changgou.goods.service.impl.ParaServiceImpl添加上面方法的實現,代碼如下:

	@Autowired
	private CategoryMapper categoryMapper;

    @Override
    public List<Para> findByCategoryId(Integer id) {
        //查詢分類信息
        Category category = categoryMapper.selectByPrimaryKey(id);
        //根據分類的模板ID查詢參數列表
        Para para = new Para();
        para.setTemplateId(category.getTemplateId());
        return paraMapper.select(para);
    }

(2)Controller層

修改com.changgou.goods.controller.ParaController,添加根據分類ID查詢參數列表,代碼如下:

    /**
     * 根據分類ID查詢參數列表
     *
     * @param categoryId 分類ID
     * @return Result<List < Para>>
     * @author Thankson
     * @date 2020年03月16日 00:13:15
     */
    @GetMapping(value = "/category/{id}")
    public Result<List<Para>> getByCategoryId(@PathVariable(value = "id") Integer categoryId) {
        //根據分類ID查詢對應的參數信息
        List<Para> paras = paraService.findByCategoryId(categoryId);
        return new Result<>(true, StatusCode.SUCCESS, "查詢商品參數成功!", paras);
    }

測試:http://localhost:10101/para/category/558
在這裏插入圖片描述

2.4.6 商品提交

2.4.6.1 分析

保存商品數據的時候,需要保存SPU和SKU,一個SPU對應多個SKU,先構建一個Goods對象,將SpuList<Sku>組合到一起,前端將2者數據提交過來,再實現添加操作。

2.4.62 代碼實現

(1)pojo

修改changgou-service-goods-api工程創建組合實體類,創建com.changgou.goods.pojo.Goods,代碼如下:

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Goods implements Serializable {
    private Spu spu;
    private List<Sku> skuList;
}

(2) 業務層

修改com.changgou.goods.service.SpuService接口,添加保存Goods方法,代碼如下:

    /**
     * 添加商品
     *
     * @param goods 商品類
     * @return boolean
     * @author Thankson
     * @date 2020年03月16日 00:13:15
     */
    boolean saveGoods(Goods goods);

修改com.changgou.goods.service.impl.SpuServiceImpl類,添加保存Goods的方法實現,代碼如下:

	@Autowired
	private IdWorker idWorker;
	
	@Autowired
	private CategoryMapper categoryMapper;
	
	@Autowired
	private BrandMapper brandMapper;
	
	@Autowired
	private SkuMapper skuMapper;

	@Override
    public boolean saveGoods(Goods goods) {
        //增加Spu
        Spu spu = goods.getSpu();
        spu.setId(idWorker.nextId()+"");
        spuMapper.insertSelective(spu);

        //增加Sku
        Date date = new Date();
        Category category = categoryMapper.selectByPrimaryKey(spu.getCategory3Id());
        Brand brand = brandMapper.selectByPrimaryKey(spu.getBrandId());
        //獲取Sku集合
        List<Sku> skuList = goods.getSkuList();
        //循環將數據加入到數據庫
        for (Sku sku : skuList) {
            //構建SKU名稱,採用SPU+規格值組裝
            if(StringUtils.isEmpty(sku.getSpec())){
                sku.setSpec("{}");
            }
            //獲取Spu的名字
            StringBuilder name = new StringBuilder(spu.getName());

            //將規格轉換成Map
            Map<String,String> specMap = JSON.parseObject(sku.getSpec(), Map.class);
            //循環組裝Sku的名字
            for (Map.Entry<String, String> entry : specMap.entrySet()) {
                name.append("  ").append(entry.getValue());
            }
            sku.setName(name.toString());
            //ID
            sku.setId(idWorker.nextId()+"");
            //SpuId
            sku.setSpuId(spu.getId());
            //創建日期
            sku.setCreateTime(date);
            //修改日期
            sku.setUpdateTime(date);
            //商品分類ID
            sku.setCategoryId(spu.getCategory3Id());
            //分類名字
            sku.setCategoryName(category.getName());
            //品牌名字
            sku.setBrandName(brand.getName());
            //增加
            skuMapper.insertSelective(sku);
        }
        return true;
    }

Idwork代碼如下

public class IdWorker {

    /**
     * 時間起始標記點,作爲基準,一般取系統的最近時間(一旦確定不能變動)
     */
    private final static long twepoch = 1288834974657L;

    /**
     * 機器標識位數
     */
    private final static long workerIdBits = 5L;

    /**
     * 數據中心標識位數
     */
    private final static long datacenterIdBits = 5L;

    /**
     * 機器ID最大值
     */
    private final static long maxWorkerId = -1L ^ (-1L << workerIdBits);

    /**
     * 數據中心ID最大值
     */
    private final static long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);

    /**
     * 毫秒內自增位
     */
    private final static long sequenceBits = 12L;

    /**
     * 機器ID偏左移12位
     */
    private final static long workerIdShift = sequenceBits;

    /**
     * 數據中心ID左移17位
     */
    private final static long datacenterIdShift = sequenceBits + workerIdBits;

    /**
     * 時間毫秒左移22位
     */
    private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

    /**
     * 序列掩碼
     */
    private final static long sequenceMask = -1L ^ (-1L << sequenceBits);

    /**
     * 上次生產id時間戳
     */
    private static long lastTimestamp = -1L;

    /**
     * 0,併發控制
     */
    private long sequence = 0L;

    /**
     * id
     */
    private final long workerId;

    /**
     *數據標識id部分
     */
    private final long datacenterId;

    public IdWorker() {
        this.datacenterId = getDatacenterId(maxDatacenterId);
        this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);
    }

    /**
     * @param workerId     工作機器ID
     * @param datacenterId 序列號
     */
    public IdWorker(long workerId, long datacenterId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
        }
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }

    /**
     * 獲取下一個ID
     *
     * @return
     */
    public synchronized long nextId() {
        long timestamp = timeGen();
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }

        if (lastTimestamp == timestamp) {
            // 當前毫秒內,則+1
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                // 當前毫秒內計數滿了,則等待下一秒
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }
        lastTimestamp = timestamp;
        // ID偏移組合生成最終的ID,並返回ID
        long nextId = ((timestamp - twepoch) << timestampLeftShift)
                | (datacenterId << datacenterIdShift)
                | (workerId << workerIdShift) | sequence;

        return nextId;
    }

    private long tilNextMillis(final long lastTimestamp) {
        long timestamp = this.timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = this.timeGen();
        }
        return timestamp;
    }

    private long timeGen() {
        return System.currentTimeMillis();
    }

    /**
     * <p>
     * 獲取 maxWorkerId
     * </p>
     */
    protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) {
        StringBuffer mpid = new StringBuffer();
        mpid.append(datacenterId);
        String name = ManagementFactory.getRuntimeMXBean().getName();
        if (!name.isEmpty()) {
            /*
             * GET jvmPid
             */
            mpid.append(name.split("@")[0]);
        }
        /*
         * MAC + PID 的 hashcode 獲取16個低位
         */
        return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
    }

    /**
     * <p>
     * 數據標識id部分
     * </p>
     */
    protected static long getDatacenterId(long maxDatacenterId) {
        long id = 0L;
        try {
            InetAddress ip = InetAddress.getLocalHost();
            NetworkInterface network = NetworkInterface.getByInetAddress(ip);
            if (network == null) {
                id = 1L;
            } else {
                byte[] mac = network.getHardwareAddress();
                id = ((0x000000FF & (long) mac[mac.length - 1])
                        | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;
                id = id % (maxDatacenterId + 1);
            }
        } catch (Exception e) {
            System.out.println(" getDatacenterId: " + e.getMessage());
        }
        return id;
    }


    public static void main(String[] args) {

        /*IdWorker idWorker = new IdWorker(0, 0);

        for (int i = 0; i < 10000; i++) {
            long nextId = idWorker.nextId();
            System.out.println(nextId);
        }*/
    }

}

需要在啓動類中添加如下方法

@Bean
public IdWorker idWorker(){
    return new IdWorker(0,0);
}

(3)控制層

修改com.changgou.goods.controller.SpuController,增加保存Goods方法,代碼如下:

	/**
     * 添加商品
     *
     * @param goods 商品類
     * @return Result<Object>
     * @author Thankson
     * @date 2020年03月16日 00:13:15
     */
    @PostMapping("/save")
    public Result<Object> save(@RequestBody Goods goods) {
        spuService.saveGoods(goods);
        return new Result<>(true, StatusCode.SUCCESS, "保存成功");
    }

測試:http://localhost:10101/spu/save
參數

{
  "skuList": [{
    "alertNum": 10,
    "brandName": "小米",
    "categoryId": 64,
    "commentNum": 0,
    "image": "http://www.baidu.com",
    "images": "",
    "name": "小米10",
    "num": 5,
    "price": 1000,
    "saleNum": 0,
    "sn": "No1001",
    "spec": "{\"顏色\":\"紅色\",\"網絡\":\"移動3G\"}",
    "weight": 0
  },{
    "alertNum": 10,
    "brandName": "小米",
    "categoryId": 64,
    "commentNum": 0,
    "image": "http://www.baidu.com",
    "images": "http://www.baidu.com",
    "name": "小米10",
    "num": 5,
    "price": 1000,
    "saleNum": 0,
    "sn": "No1001",
    "spec": "{\"顏色\":\"紅色\",\"網絡\":\"移動3G\"}",
    "weight": 0
  }],
  "spu": {
    "brandId": 18374,
    "caption": "小米手機",
    "category1Id": 558,
    "category2Id": 559,
    "category3Id": 560,
    "commentNum": 0,
    "freightId": 0,
    "images": "http://www.baidu.com",
    "introduction": "爲發燒而生",
    "isEnableSpec": "1",
    "isMarketable": "1",
    "name": "小米",
    "specItems": "{\"顏色\":[\"白色\",\"紅色\"],\"機身內存\":[\"64G\",\"8G\"]}",
    "paraItems": "{\"贈品\":\"充電器\",\"出廠年份\":\"2019\"}",
    "saleNum": 0,
    "saleService": "一年包換",
    "sn": "No10001",
    "status": "1",
    "templateId": 42
  }
}

在這裏插入圖片描述

2.4.7 根據ID查詢商品

2.4.7.1 需求分析

需求:根據id 查詢SPU和SKU列表

2.4.7.2 代碼實現

(1)業務層

修改com.changgou.goods.service.SpuService接口,添加根據ID查找方法

    /**
     * 根據SPU的ID查找SPU以及對應的SKU集合
     *
     * @param spuId SPU編碼
     * @return Goods
     * @author Thankson
     * @date 2020年03月16日 00:13:15
     */
    Goods findGoodsById(String spuId);

修改com.changgou.goods.service.impl.SpuServiceImpl類,添加上面方法的實現:

    @Override
    public Goods findGoodsById(String spuId) {
        //查詢Spu
        Spu spu = spuMapper.selectByPrimaryKey(spuId);
        //查詢List<Sku>
        Sku sku = new Sku();
        sku.setSpuId(spuId);
        List<Sku> skuList = skuMapper.select(sku);
        //封裝Goods
        Goods goods = new Goods();
        goods.setSkuList(skuList);
        goods.setSpu(spu);
        return goods;
    }

(2)控制層

修改com.changgou.goods.controller.SpuController,添加根據SPU查詢商品數據:

    /**
     * 根據SPU的ID查找SPU以及對應的SKU集合
     *
     * @param spuId SPU編碼
     * @return Goods
     * @author Thankson
     * @date 2020年03月16日 00:13:15
     */
    @GetMapping("/goods/{id}")
    public Result<Goods> findGoodsById(@PathVariable String spuId) {
        //根據ID查詢Goods(SPU+SKU)信息
        Goods goods = spuService.findGoodsById(spuId);
        return new Result<>(true, StatusCode.SUCCESS, "查詢成功", goods);
    }

測試:http://localhost:10101/spu/goods/1240327082832035840
在這裏插入圖片描述

2.4.8 保存修改

修改changgou-service-goods.service.impl.SpuServiceImpl中saveGoods方法

    @Override
    public boolean saveGoods(Goods goods) {
        //增加Spu
        Spu spu = goods.getSpu();
        if(spu.getId()==null){
            //增加
            spu.setId(idWorker.nextId()+"");
            spuMapper.insertSelective(spu);
        }else{
            //修改數據
            spuMapper.updateByPrimaryKeySelective(spu);
            //刪除該Spu的Sku
            Sku sku = new Sku();
            sku.setSpuId(spu.getId());
            skuMapper.delete(sku);
        }
        //增加Sku
        Date date = new Date();
        Category category = categoryMapper.selectByPrimaryKey(spu.getCategory3Id());
        Brand brand = brandMapper.selectByPrimaryKey(spu.getBrandId());
        //獲取Sku集合
        List<Sku> skuList = goods.getSkuList();
        //循環將數據加入到數據庫
        for (Sku sku : skuList) {
            //構建SKU名稱,採用SPU+規格值組裝
            if(StringUtils.isEmpty(sku.getSpec())){
                sku.setSpec("{}");
            }
            //獲取Spu的名字
            StringBuilder name = new StringBuilder(spu.getName());

            //將規格轉換成Map
            Map<String,String> specMap = JSON.parseObject(sku.getSpec(), Map.class);
            //循環組裝Sku的名字
            for (Map.Entry<String, String> entry : specMap.entrySet()) {
                name.append("  ").append(entry.getValue());
            }
            sku.setName(name.toString());
            //ID
            sku.setId(idWorker.nextId()+"");
            //SpuId
            sku.setSpuId(spu.getId());
            //創建日期
            sku.setCreateTime(date);
            //修改日期
            sku.setUpdateTime(date);
            //商品分類ID
            sku.setCategoryId(spu.getCategory3Id());
            //分類名字
            sku.setCategoryName(category.getName());
            //品牌名字
            sku.setBrandName(brand.getName());
            //增加
            skuMapper.insertSelective(sku);
        }
        return true;
    }

測試省略

3 商品審覈與上下架

3.1 需求分析

商品新增後,審覈狀態爲0(未審覈),默認爲下架狀態。

審覈商品,需要校驗是否是被刪除的商品,如果未刪除則修改審覈狀態爲1,並自動上架

下架商品,需要校驗是否是被刪除的商品,如果未刪除則修改上架狀態爲0

上架商品,需要審覈通過的商品

3.2 實現思路

(1)按照ID查詢SPU信息

(2)判斷修改審覈、上架和下架狀態

(3)保存SPU

3.3 代碼實現

3.3.1 商品審覈

審覈通過,自動上架。

(1)業務層

修改com.changgou.goods.service.SpuService接口,添加審覈方法,代碼如下:

    /**
     * 商品審覈
     *
     * @param spuId SPU編碼
     * @return boolean
     * @author Thankson
     * @date 2020年3月17日
     */
    boolean audit(Long spuId);

修改com.changgou.goods.service.impl.SpuServiceImpl添加上面方法的實現,代碼如下:

    @Override
    public boolean audit(Long spuId) {
        //查詢商品
        Spu spu = spuMapper.selectByPrimaryKey(spuId);
        //判斷商品是否已經刪除
        if (spu.getIsDelete().equalsIgnoreCase("1")) {
            throw new RuntimeException("該商品已經刪除!");
        }
        //實現上架和審覈
        spu.setStatus("1"); //審覈通過
        spu.setIsMarketable("1"); //上架
        int result = spuMapper.updateByPrimaryKeySelective(spu);
        return result > 0;
    }

(2)控制層

修改com.changgou.goods.controller.SpuController,新增audit方法,代碼如下:

    /**
     * 審覈
     *
     * @param spuId SPU編碼
     * @return Goods
     * @author Thankson
     * @date 2020年03月16日 00:13:15
     */
    @PutMapping("/audit/{id}")
    public Result<Object> audit(@PathVariable(value = "id") Long spuId) {
        spuService.audit(spuId);
        return new Result<>(true, StatusCode.SUCCESS, "審覈成功");
    }

測試:http://localhost:10101/spu/audit/1240327082832035840
在這裏插入圖片描述

3.3.2 下架商品

(1)業務層

修改com.changgou.goods.service.SpuService接口,添加pull方法,用於商品下架,代碼如下:

    /**
     * 商品下架
     *
     * @param spuId SPU編碼
     * @return boolean
     * @author Thankson
     * @date 2020年03月16日 00:13:15
     */
    boolean pull(Long spuId);

修改com.changgou.goods.service.impl.SpuServiceImpl,添加上面方法的實現,代碼如下:

    @Override
    public boolean pull(Long spuId) {
        Spu spu = spuMapper.selectByPrimaryKey(spuId);
        if (spu.getIsDelete().equals("1")) {
            throw new RuntimeException("此商品已刪除!");
        }
        //下架狀態
        spu.setIsMarketable("0");
        int result = spuMapper.updateByPrimaryKeySelective(spu);
        return result > 0;
    }

(2)控制層

修改com.changgou.goods.controller.SpuController,添加pull方法,代碼如下:

    /**
     * 下架
     *
     * @param spuId SPU編碼
     * @return Result<Object>
     * @author Thankson
     * @date 2020年03月16日 00:13:15
     */
    @PutMapping("/pull/{id}")
    public Result<Object> pull(@PathVariable(value = "id") Long spuId) {
        spuService.pull(spuId);
        return new Result<>(true, StatusCode.SUCCESS, "下架成功");
    }

測試:http://localhost:10101/spu/pull/1240327082832035840
在這裏插入圖片描述

3.3.3 上架商品

必須是通過審覈的商品才能上架

(1)業務層

修改com.changgou.goods.service.SpuService,添加put方法,代碼如下:

    /**
     * 商品上架
     *
     * @param spuId SPU編碼
     * @return boolean
     * @author Thankson
     * @date 2020年03月16日 00:13:15
     */
    boolean put(Long spuId);

修改com.changgou.goods.service.impl.SpuServiceImpl,添加上面方法的實現,代碼如下:

    @Override
    public boolean put(Long spuId) {
        Spu spu = spuMapper.selectByPrimaryKey(spuId);
        //檢查是否刪除的商品
        if (spu.getIsDelete().equals("1")) {
            throw new RuntimeException("此商品已刪除!");
        }
        if (!spu.getStatus().equals("1")) {
            throw new RuntimeException("未通過審覈的商品不能!");
        }
        //上架狀態
        spu.setIsMarketable("1");
        int result = spuMapper.updateByPrimaryKeySelective(spu);
        return result > 0;
    }

(2)控制層

修改com.changgou.goods.controller.SpuController,添加put方法代碼如下:

    /**
     * 下架
     *
     * @param spuId SPU編碼
     * @return Result<Object>
     * @author Thankson
     * @date 2020年03月16日 00:13:15
     */
    @PutMapping("/put/{id}")
    public Result<Object> put(@PathVariable(value = "id") Long spuId) {
        spuService.put(spuId);
        return new Result<>(true, StatusCode.SUCCESS, "上架成功");
    }

測試:http://localhost:10101/spu/put/1240327082832035840
在這裏插入圖片描述

3.3.4 批量上架

前端傳遞一組商品ID集合,後端進行批量上架處理

(1)業務層

修改com.changgou.goods.service.SpuService接口,添加putMany方法,代碼如下:

    /**
     * 批量上架
     *
     * @param supIdList SPU編碼集合
     * @return int
     * @author Thankson
     * @date 2020年03月16日 00:13:15
     */
    int putMany(Long[] supIdList);

修改com.changgou.goods.service.impl.SpuServiceImpl,添加上面方法的實現,代碼如下:

    @Override
    public boolean putMany(Long[] ids) {
        Spu spu = new Spu();
        //上架
        spu.setIsMarketable("1");
        //批量修改
        Example example = new Example(Spu.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andIn("id", Arrays.asList(ids));
        //下架
        criteria.andEqualTo("isMarketable", "0");
        //審覈通過的
        criteria.andEqualTo("status", "1");
        //非刪除的
        criteria.andEqualTo("isDelete", "0");
        return spuMapper.updateByExampleSelective(spu, example);
    }

(2)控制層

修改com.changgou.goods.controller.SpuController,添加批量上架方法,代碼如下:

    /**
     * 批量上架
     *
     * @param spuIdList SPU編碼集合
     * @return Result<Object>
     * @author Thankson
     * @date 2020年03月16日 00:13:15
     */
    @PutMapping("/put/many")
    public Result<Object> putMany(@RequestBody Long[] spuIdList) {
        int count = spuService.putMany(spuIdList);
        return new Result<>(true, StatusCode.SUCCESS, "成功上架" + count + "個商品");
    }

測試:http://localhost:10101/spu/put/many
參數

["1240327082832035840","123","0"]

在這裏插入圖片描述

3.3.5 批量下架

前端傳遞一組商品ID集合,後端進行批量下架處理

(1)業務層

修改com.changgou.goods.service.SpuService接口,添加pullMany方法,代碼如下:

    /**
     * 批量下架
     *
     * @param supIdList SPU編碼集合
     * @return int
     * @author Thankson
     * @date 2020年03月16日 00:13:15
     */
    int pullMany(Long[] supIdList);

修改com.changgou.goods.service.impl.SpuServiceImpl,添加上面方法的實現,代碼如下:

    @Override
    public boolean pullMany(Long[] ids) {
        Spu spu = new Spu();
        //下架
        spu.setIsMarketable("0");
        //批量修改
        Example example = new Example(Spu.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andIn("id", Arrays.asList(ids));
        //上架
        criteria.andEqualTo("isMarketable", "1");
        //審覈通過的
        criteria.andEqualTo("status", "1");
        //非刪除的
        criteria.andEqualTo("isDelete", "0");
        return spuMapper.updateByExampleSelective(spu, example);
    }

(2)控制層

修改com.changgou.goods.controller.SpuController,添加批量上架方法,代碼如下:

    /**
     * 批量下架
     *
     * @param spuIdList SPU編碼集合
     * @return Result<Object>
     * @author Thankson
     * @date 2020年03月16日 00:13:15
     */
    @PutMapping("/pull/many")
    public Result<Object> pullMany(@RequestBody Long[] spuIdList) {
        int count = spuService.putMany(spuIdList);
        return new Result<>(true, StatusCode.SUCCESS, "成功下架" + count + "個商品");
    }

測試:http://localhost:10101/spu/pull/many
參數

["1240327082832035840","123","0"]

在這裏插入圖片描述

4 刪除與還原商品

4.1 需求分析

商品列表中的刪除商品功能採用邏輯刪除,並非真正的刪除,而是將刪除標記的字段設置爲1。

在回收站中有恢復商品的功能,將刪除標記的字段設置爲0

在回收站中有刪除商品的功能,是真正的物理刪除。

4.2 實現思路

邏輯刪除商品,修改spu表is_delete字段爲1

商品回收站顯示spu表is_delete字段爲1的記錄

回收商品,修改spu表is_delete字段爲0

4.3 代碼實現

4.3.1 邏輯刪除商品

(1)業務層

修改com.changgou.goods.service.SpuService接口,增加logicDelete方法,代碼如下:

    /**
     * 邏輯刪除
     *
     * @param spuId SPU編碼集合
     * @return boolean
     * @author Thankson
     * @date 2020年03月16日 00:13:15
     */
    boolean logicDelete(Long spuId);

修改com.changgou.goods.service.impl.SpuServiceImpl,添加上面方法的實現,代碼如下:

    @Override
    public boolean logicDelete(Long spuId) {
        Spu spu = spuMapper.selectByPrimaryKey(spuId);
        //檢查是否下架的商品
        if (!spu.getIsMarketable().equals("0")) {
            throw new RuntimeException("必須先下架再刪除!");
        }
        //刪除
        spu.setIsDelete("1");
        //未審覈
        spu.setStatus("0");
        int result = spuMapper.updateByPrimaryKeySelective(spu);
        return result > 0;
    }

(2)控制層

修改com.changgou.goods.controller.SpuController,添加logicDelete方法,如下:

    /**
     * 邏輯刪除
     *
     * @param spuId SPU編碼集合
     * @return Result<Object>
     * @author Thankson
     * @date 2020年03月16日 00:13:15
     */
    @DeleteMapping("/logic/delete/{id}")
    public Result<Object> logicDelete(@PathVariable(value = "id") Long spuId) {
        spuService.logicDelete(spuId);
        return new Result<>(true, StatusCode.SUCCESS, "邏輯刪除成功!");
    }

測試:http://localhost:10101/spu/logic/delete/1240327082832035840
在這裏插入圖片描述

4.3.2 還原被刪除的商品

(1)業務層

修改com.changgou.goods.service.SpuService接口,添加restore方法代碼如下:

    /**
     * 還原被刪除商品
     *
     * @param spuId SPU編碼集合
     * @return boolean
     * @author Thankson
     * @date 2020年03月16日 00:13:15
     */
    boolean restore(Long spuId);

修改com.changgou.goods.service.impl.SpuServiceImpl類,添加上面方法的實現,代碼如下:

    @Override
    public boolean restore(Long spuId) {
        Spu spu = spuMapper.selectByPrimaryKey(spuId);
        //檢查是否刪除的商品
        if (!spu.getIsDelete().equals("1")) {
            throw new RuntimeException("此商品未刪除!");
        }
        //未刪除
        spu.setIsDelete("0");
        //未審覈
        spu.setStatus("0");
        int result = spuMapper.updateByPrimaryKeySelective(spu);
        return result > 0;
    }

(2)控制層

修改com.changgou.goods.controller.SpuController,添加restore方法,代碼如下:

/**
 * 恢復數據
 * @param id
 * @return
 */
@PutMapping("/restore/{id}")
public Result restore(@PathVariable Long id){
    spuService.restore(id);
    return new Result(true,StatusCode.OK,"數據恢復成功!");
}

測試:http://localhost:10101/spu/restore/1240327082832035840
在這裏插入圖片描述

4.3.3 物理刪除商品

修改com.changgou.goods.service.impl.SpuServiceImpl的delete方法,代碼如下:

    @Override
    public boolean delete(String id) {
        Spu spu = spuMapper.selectByPrimaryKey(id);
        //檢查是否被邏輯刪除  ,必須先邏輯刪除後才能物理刪除
        if (!spu.getIsDelete().equals("1")) {
            throw new RuntimeException("此商品不能刪除!");
        }
        int result = spuMapper.deleteByPrimaryKey(id);
        return result > 0;
    }

5 商品列表

5.1 需求分析

如圖所示 展示商品的列表。並實現分頁。
在這裏插入圖片描述
根據查詢的條件 分頁查詢 並返回分頁結果即可。
分頁查詢採用 pagehelper,條件查詢 通過map進行封裝傳遞給後臺即可。

5.2 代碼實現

控制層:
修改com.changgou.goods.controller.SpuController,添加restore方法,代碼如下:

    /**
     * Spu分頁搜索實現
     *
     * @param page 當前頁數
     * @param size 每頁顯示多少條
     * @return Result<PageInfo>
     * @author Thankson
     * @date 2020年03月16日 00:13:15
     */
    @GetMapping(value = "/search/{page}/{size}")
    public Result<PageInfo<Spu>> findPage(@PathVariable int page, @PathVariable int size) {
        PageInfo<Spu> pageInfo = spuService.findPage(page, size);
        return new Result<>(true, StatusCode.SUCCESS, "查詢成功", pageInfo);
    }

測試:http://localhost:10101/spu/search/0/10
在這裏插入圖片描述

其他每層代碼,代碼生成器已經生成,這裏就不再列出來了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章