第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信息
先進入選擇商品分類 再填寫商品的信息 填寫商品的屬性添加商品。
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 分析
用戶每次選擇了分類之後,根據用戶選擇的分類到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 分析
當模板信息成功獲取後,開始通過分類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對象,將Spu
和List<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
其他每層代碼,代碼生成器已經生成,這裏就不再列出來了。