樂優商場開發第九天筆記
0.學習目標
- 瞭解商品規格資料結構設計思路
- 實現商品規格查詢
- 瞭解SPU和SKU資料結構設計思路
- 實現商品查詢
- 瞭解商品新增的頁面實現
- 獨立編寫商品新增後臺功能
1.商品規格資料結構
樂優商城是一個全品類的電商網站,因此商品的種類繁多,每一件商品,其屬性又有差別。為了更準確描述商品及細分差別,抽象出兩個概念:SPU和SKU,瞭解一下:
1.1.SPU和SKU
SPU:Standard Product Unit (標準產品單位) ,一組具有共同屬性的商品集
SKU:Stock Keeping Unit(庫存量單位),SPU商品集因具體特性不同而細分的每個商品
以圖為例來看:
- 本頁的 華為Mate10 就是一個商品集(SPU)
- 因為顏色、記憶體等不同,而細分出不同的Mate10,如亮黑色128G版。(SKU)
可以看出:
- SPU是一個抽象的商品集概念,為了方便後臺的管理。
- SKU才是具體要銷售的商品,每一個SKU的價格、庫存可能會不一樣,使用者購買的是SKU而不是SPU
1.2.資料庫設計分析
1.2.1.思考並發現問題
弄清楚了SPU和SKU的概念區分,接下來我們一起思考一下該如何設計資料庫表。
首先來看SPU,大家一起思考下SPU應該有哪些欄位來描述?
id:主鍵 title:標題 description:描述 specification:規格 packaging_list:包裝 after_service:售後服務 comment:評價 category_id:商品分類 brand_id:品牌
似乎並不複雜,但是大家仔細思考一下,商品的規格欄位你如何填寫?
不同商品的規格不一定相同,資料庫中要如何儲存?
再看下SKU,大家覺得應該有什麼欄位?
id:主鍵
spu_id:關聯的spu
price:價格
images:圖片
stock:庫存
顏色?
記憶體?
硬碟?
碰到難題了,不同的商品分類,可能屬性是不一樣的,比如手機有記憶體,衣服有尺碼,我們是全品類的電商網站,這些不同的商品的不同屬性,如何設計到一張表中?
1.2.2.分析規格引數
仔細檢視每一種商品的規格你會發現:
雖然商品規格千變萬化,但是同一類商品(如手機)的規格是統一的,有圖為證:
華為的規格:
三星的規格:
也就是說,商品的規格引數應該是與分類繫結的。每一個分類都有統一的規格引數模板,但不同商品其引數值可能不同。
如下圖所示:
1.2.3.SKU的特有屬性
SPU中會有一些特殊屬性,用來區分不同的SKU,我們稱為SKU特有屬性。如華為META10的顏色、記憶體屬性。
不同種類的商品,一個手機,一個衣服,其SKU屬性不相同。
同一種類的商品,比如都是衣服,SKU屬性基本是一樣的,都是顏色、尺碼等。
這樣說起來,似乎SKU的特有屬性也是與分類相關的?事實上,仔細觀察你會發現,SKU的特有屬性是商品規格引數的一部分:
也就是說,我們沒必要單獨對SKU的特有屬性進行設計,它可以看做是規格引數中的一部分。這樣規格引數中的屬性可以標記成兩部分:
- 所有sku共享的規格屬性(稱為全域性屬性)
- 每個sku不同的規格屬性(稱為特有屬性)
1.2.4.搜尋屬性
開啟一個搜尋頁,我們來看看過濾的條件:
你會發現,過濾條件中的螢幕尺寸、執行記憶體、網路、機身記憶體、電池容量、CPU核數等,在規格引數中都能找到:
也就是說,規格引數中的資料,將來會有一部分作為搜尋條件來使用。我們可以在設計時,將這部分屬性標記出來,將來做搜尋的時候,作為過濾條件。要注意的是,無論是SPU的全域性屬性,還是SKU的特有屬性,都有可能作為搜尋過濾條件的,並不衝突,而是有一個交集:
1.3.規格引數表
1.3.1.表結構
我們看下規格引數的格式:
可以看到規格引數是分組的,每一組都有多個引數鍵值對。不過對於規格引數的模板而言,其值現在是不確定的,不同的商品值肯定不同,模板中只要儲存組資訊、組內參數資訊即可。
因此我們設計了兩張表:
- tb_spec_group:組,與商品分類關聯
- tb_spec_param:引數名,與組關聯,一對多
1.3.2.規格組
規格引數分組表:tb_spec_group
CREATE TABLE `tb_spec_group` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`cid` bigint(20) NOT NULL COMMENT '商品分類id,一個分類下有多個規格組',
`name` varchar(50) NOT NULL COMMENT '規格組的名稱',
PRIMARY KEY (`id`),
KEY `key_category` (`cid`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8 COMMENT='規格引數的分組表,每個商品分類下有多個規格引數組';
規格組有3個欄位:
- id:主鍵
- cid:商品分類id,一個分類下有多個模板
- name:該規格組的名稱。
1.3.2.規格引數
規格引數表:tb_spec_param
CREATE TABLE `tb_spec_param` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`cid` bigint(20) NOT NULL COMMENT '商品分類id',
`group_id` bigint(20) NOT NULL,
`name` varchar(255) NOT NULL COMMENT '引數名',
`numeric` tinyint(1) NOT NULL COMMENT '是否是數字型別引數,true或false',
`unit` varchar(255) DEFAULT '' COMMENT '數字型別引數的單位,非數字型別可以為空',
`generic` tinyint(1) NOT NULL COMMENT '是否是sku通用屬性,true或false',
`searching` tinyint(1) NOT NULL COMMENT '是否用於搜尋過濾,true或false',
`segments` varchar(1000) DEFAULT '' COMMENT '數值型別引數,如果需要搜尋,則新增分段間隔值,如CPU頻率間隔:0.5-1.0',
PRIMARY KEY (`id`),
KEY `key_group` (`group_id`),
KEY `key_category` (`cid`)
) ENGINE=InnoDB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8 COMMENT='規格引數組下的引數名';
按道理來說,我們的規格引數就只需要記錄引數名、組id、商品分類id即可。但是這裡卻多出了很多欄位,為什麼?
還記得我們之前的分析吧,規格引數中有一部分是 SKU的通用屬性,一部分是SKU的特有屬性,而且其中會有一些將來用作搜尋過濾,這些資訊都需要標記出來。
通用屬性
用一個布林型別欄位來標記是否為通用:
- generic來標記是否為通用屬性:
- true:代表通用屬性
- false:代表sku特有屬性
搜尋過濾
與搜尋相關的有兩個欄位:
- searching:標記是否用作過濾
- true:用於過濾搜尋
- false:不用於過濾
- segments:某些數值型別的引數,在搜尋時需要按區間劃分,這裡提前確定好劃分區間
- 比如電池容量,02000mAh,2000mAh3000mAh,3000mAh~4000mAh
數值型別
某些規格引數可能為數值型別,這樣的資料才需要劃分區間,我們有兩個欄位來描述:
- numberic:是否為數值型別
- true:數值型別
- false:不是數值型別
- unit:引數的單位
2.商品規格引數管理
2.1.頁面佈局
2.1.1.整體佈局
開啟規格引數頁面,看到如下內容:
商品分類樹我們之前已經做過,所以這裡可以直接展示出來。
因為規格是跟商品分類繫結的,因此首先會展現商品分類樹,並且提示你要選擇商品分類,才能看到規格引數的模板。一起了解下頁面的實現:
頁面結構:
這裡使用了v-layout
來完成頁面佈局,並且添加了row屬性,代表接下來的內容是行佈局(左右)。
可以看出頁面分成2個部分:
<v-flex xs3>
:左側,內部又分上下兩部分:商品分類樹及標題v-card-title
:標題部分,這裡是提示資訊,告訴使用者要先選擇分類,才能看到模板v-tree
:這裡用到的是我們之前講過的樹元件,展示商品分類樹,
<v-flex xs9 class="px-1">
:右側:內部是規格引數展示
2.1.2.右側規格
當我們點選一個分類時,最終要達到的效果:
可以看到右側分為上下兩部分:
- 上部:麵包屑,顯示當前選中的分類
- 下部:table,顯示規格引數資訊
頁面實現:
可以看到右側並不是我們熟悉的 v-data-table
,而是一個spec-group
元件(規格組)和spec-param
元件(規格引數),這是我們定義的獨立元件:
在SpecGroup中定義了表格:
2.2.規格組的查詢
2.2.1.樹節點的點選事件
當我們點選樹節點時,要將v-dialog
開啟,因此必須繫結一個點選事件:
我們來看下handleClick
方法:
點選事件發生時,發生了兩件事:
- 記錄當前選中的節點,選中的就是商品分類
showGroup
被置為true,則規格組就會顯示了。
同時,我們把被選中的節點(商品分類)的id傳遞給了SpecGroup
元件:
2.2.2.頁面查詢規格組
來看下SpecGroup.vue
中的實現:
我們檢視頁面控制檯,可以看到請求已經發出:
2.2.3.後端程式碼
實體類
在leyou-item-interface
中新增實體類:
內容:
@Table(name = "tb_spec_group")
public class SpecGroup {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long cid;
private String name;
@Transient
private List<SpecParam> params;
// getter和setter省略
}
@Table(name = "tb_spec_param")
public class SpecParam {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long cid;
private Long groupId;
private String name;
@Column(name = "`numeric`")
private Boolean numeric;
private String unit;
private Boolean generic;
private Boolean searching;
private String segments;
// getter和setter ...
}
在leyou-item-service
中編寫業務:
mapper
public interface SpecGroupMapper extends Mapper<SpecGroup> {
}
controller
先分析下需要的東西,在頁面的ajax請求中可以看出:
-
請求方式:查詢,肯定是get
-
請求路徑:/spec/groups/{cid} ,這裡通過路徑佔位符傳遞商品分類的id
-
請求引數:商品分類id
-
返回結果:頁面是直接把
resp.data
賦值給了groups:那麼我們返回的應該是規格組SpecGroup的集合
程式碼:
@RestController
@RequestMapping("spec")
public class SpecificationController {
@Autowired
private SpecificationService specificationService;
@GetMapping("groups/{cid}")
public ResponseEntity<List<SpecGroup>> querySpecGroups(@PathVariable("cid") Long cid){
List<SpecGroup> list = this.specificationService.querySpecGroups(cid);
if(list == null || list.size() == 0){
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return ResponseEntity.ok(list);
}
}
service
@Service
public class SpecificationService {
@Autowired
private SpecGroupMapper specGroupMapper;
public List<SpecGroup> querySpecGroups(Long cid) {
SpecGroup t = new SpecGroup();
t.setCid(cid);
return this.specGroupMapper.select(t);
}
}
頁面訪問測試:
目前,我們資料庫只為手機分類(76)提供了規格組:
我們訪問:http://api.leyou.com/api/item/spec/groups/76
然後在後臺系統中測試:
2.3.規格引數查詢
2.3.1.表格切換
當我們點選規格組,會切換到規格引數顯示,肯定是在規格組中綁定了點選事件:
我們看下事件處理:
可以看到這裡是使用了父子通訊,子元件觸發了select事件:
再來看下父元件的事件繫結:
事件處理:
這裡我們記錄了選中的分組,並且把標記設定為false,這樣規格組就不顯示了,而是顯示:SpecParam
並且,我們把group也傳遞到spec-param
元件:
2.3.2.頁面查詢規格引數
我們來看SpecParam.vue
的實現:
檢視頁面控制檯,發現請求已經發出:
報404,因為我們還沒有實現後臺邏輯,接下來就去實現。
2.3.3.後臺實現
controller
分析:
- 請求方式:GET
- 請求路徑:/spec/params
- 請求引數:gid,分組id
- 返回結果:該分組下的規格引數集合
List<SpecParam>
程式碼:
@GetMapping("/params")
public ResponseEntity<List<SpecParam>> querySpecParam(
@RequestParam(value="gid", required = false) Long gid
){
List<SpecParam> list =
this.specificationService.querySpecParams(gid);
if(list == null || list.size() == 0){
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return ResponseEntity.ok(list);
}
service
@Autowired
private SpecParamMapper specParamMapper;
public List<SpecParam> querySpecParams(Long gid){
SpecParam t = new SpecParam();
t.setGroupId(gid);
return this.specParamMapper.select(t);
}
mapper
public interface SpecParamMapper extends Mapper<SpecParam> {
}
測試:
2.4.增、刪、改(作業)
增刪改的作業就留給大家去完成了。頁面中介面都已定義,你要做的就是實現後臺介面。
3.SPU和SKU資料結構
規格確定以後,就可以新增商品了,先看下資料庫表
3.1.SPU表
SPU表:
CREATE TABLE `tb_spu` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'spu id',
`title` varchar(255) NOT NULL DEFAULT '' COMMENT '標題',
`sub_title` varchar(255) DEFAULT '' COMMENT '子標題',
`cid1` bigint(20) NOT NULL COMMENT '1級類目id',
`cid2` bigint(20) NOT NULL COMMENT '2級類目id',
`cid3` bigint(20) NOT NULL COMMENT '3級類目id',
`brand_id` bigint(20) NOT NULL COMMENT '商品所屬品牌id',
`saleable` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否上架,0下架,1上架',
`valid` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否有效,0已刪除,1有效',
`create_time` datetime DEFAULT NULL COMMENT '新增時間',
`last_update_time` datetime DEFAULT NULL COMMENT '最後修改時間',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=208 DEFAULT CHARSET=utf8 COMMENT='spu表,該表描述的是一個抽象的商品,比如 iphone8';
與我們前面分析的基本類似,但是似乎少了一些欄位,比如商品描述。
我們做了表的垂直拆分,將SPU的詳情放到了另一張表:tb_spu_detail
CREATE TABLE `tb_spu_detail` (
`spu_id` bigint(20) NOT NULL,
`description` text COMMENT '商品描述資訊',
`generic_spec` varchar(10000) NOT NULL DEFAULT '' COMMENT '通用規格引數資料',
`special_spec` varchar(1000) NOT NULL COMMENT '特有規格引數及可選值資訊,json格式',
`packing_list` varchar(3000) DEFAULT '' COMMENT '包裝清單',
`after_service` varchar(3000) DEFAULT '' COMMENT '售後服務',
PRIMARY KEY (`spu_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
這張表中的資料都比較大,為了不影響主表的查詢效率我們拆分出這張表。
需要注意的是這兩個欄位:generic_spec和special_spec。
前面講過規格引數與商品分類繫結,一個分類下的所有SPU具有類似的規格引數。SPU下的SKU可能會有不同的規格引數資訊,因此我們計劃是這樣:
- SPUDetail中儲存通用的規格引數資訊。
- SKU中儲存特有規格引數。
來看下我們的表如何儲存這些資訊。
3.1.1.generic_spec欄位
首先是generic_spec
,其中儲存通用規格引數資訊的值,這裡為了方便查詢,使用了json格式:
整體來看:
json結構,其中都是鍵值對:
- key:對應的規格引數的
spec_param
的id - value:對應規格引數的值
3.1.2.special_spec欄位
我們說spu中只儲存通用規格引數,那麼為什麼有多出了一個special_spec
欄位呢?
以手機為例,品牌、作業系統等肯定是全域性通用屬性,記憶體、顏色等肯定是特有屬性。
當你確定了一個SPU,比如小米的:紅米4X
全域性屬性值都是固定的了:
品牌:小米
型號:紅米4X
特有屬性舉例:
顏色:[香檳金, 櫻花粉, 磨砂黑]
記憶體:[2G, 3G]
機身儲存:[16GB, 32GB]
顏色、記憶體、機身儲存,作為SKU特有屬性,key雖然一樣,但是SPU下的每一個SKU,其值都不一樣,所以值會有很多,形成陣列。
我們在SPU中,會把特有屬性的所有值都記錄下來,形成一個數組:
裡面又有哪些內容呢?
來看資料格式:
也是json結構:
- key:規格引數id
- value:spu屬性的陣列
那麼問題來:特有規格引數應該在sku中記錄才對,為什麼在spu中也要記錄一份?
因為我們有時候需要把所有規格引數都查詢出來,而不是隻查詢1個sku的屬性。比如,商品詳情頁展示可選的規格引數時:
剛好符合我們的結構,這樣頁面渲染就非常方便了。
3.2.SKU表
CREATE TABLE `tb_sku` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'sku id',
`spu_id` bigint(20) NOT NULL COMMENT 'spu id',
`title` varchar(255) NOT NULL COMMENT '商品標題',
`images` varchar(1000) DEFAULT '' COMMENT '商品的圖片,多個圖片以‘,’分割',
`price` bigint(15) NOT NULL DEFAULT '0' COMMENT '銷售價格,單位為分',
`indexes` varchar(100) COMMENT '特有規格屬性在spu屬性模板中的對應下標組合',
`own_spec` varchar(1000) COMMENT 'sku的特有規格引數,json格式',
`enable` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否有效,0無效,1有效',
`create_time` datetime NOT NULL COMMENT '新增時間',
`last_update_time` datetime NOT NULL COMMENT '最後修改時間',
PRIMARY KEY (`id`),
KEY `key_spu_id` (`spu_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='sku表,該表表示具體的商品實體,如黑色的64GB的iphone 8';
還有一張表,代表庫存:
CREATE TABLE `tb_stock` (
`sku_id` bigint(20) NOT NULL COMMENT '庫存對應的商品sku id',
`seckill_stock` int(9) DEFAULT '0' COMMENT '可秒殺庫存',
`seckill_total` int(9) DEFAULT '0' COMMENT '秒殺總數量',
`stock` int(9) NOT NULL COMMENT '庫存數量',
PRIMARY KEY (`sku_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='庫存表,代表庫存,秒殺庫存等資訊';
問題:為什麼要將庫存獨立一張表?
因為庫存欄位寫頻率較高,而SKU的其它欄位以讀為主,因此我們將兩張表分離,讀寫不會干擾。
特別需要注意的是sku表中的indexes
欄位和own_spec
欄位。sku中應該儲存特有規格引數的值,就在這兩個欄位中。
3.2.1.indexes欄位
在SPU表中,已經對特有規格引數及可選項進行了儲存,結構如下:
{
"4": [
"香檳金",
"櫻花粉",
"磨砂黑"
],
"12": [
"2GB",
"3GB"
],
"13": [
"16GB",
"32GB"
]
}
這些特有屬性如果排列組合,會產生12個不同的SKU,而不同的SKU,其屬性就是上面備選項中的一個。
比如:
- 紅米4X,香檳金,2GB記憶體,16GB儲存
- 紅米4X,磨砂黑,2GB記憶體,32GB儲存
你會發現,每一個屬性值,對應於SPUoptions陣列的一個選項,如果我們記錄下角標,就是這樣:
- 紅米4X,0,0,0
- 紅米4X,2,0,1
既然如此,我們是不是可以將不同角標串聯起來,作為SPU下不同SKU的標示。這就是我們的indexes欄位。
這個設計在商品詳情頁會特別有用:
當用戶點選選中一個特有屬性,你就能根據 角標快速定位到sku。
3.2.2.own_spec欄位
看結構:
{"4":"香檳金","12":"2GB","13":"16GB"}
儲存的是特有屬性的鍵值對。
SPU中儲存的是可選項,但不確定具體的值,而SKU中的儲存的就是具體的值。
3.3.匯入圖片資訊
現在商品表中雖然有資料,但是所有的圖片資訊都是無法訪問的,我們需要把圖片匯入到虛擬機器:
首先,把課前資料提供的資料上傳到虛擬機器下:/leyou/static
目錄:
然後,使用命令解壓縮:
unzip images.zip
修改Nginx配置,使nginx反向代理這些圖片地址:
vim /opt/nginx/config/nginx.conf
修改成如下配置:
server {
listen 80;
server_name image.leyou.com;
# 監聽域名中帶有group的,交給FastDFS模組處理
location ~/group([0-9])/ {
ngx_fastdfs_module;
}
# 將其它圖片代理指向本地的/leyou/static目錄
location / {
root /leyou/static/;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
不要忘記重新載入nginx配置
nginx -s reload
4.商品查詢
4.1.效果預覽
接下來,我們實現商品管理的頁面,先看下我們要實現的效果:
可以看出整體是一個table,然後有新增按鈕。是不是跟昨天寫品牌管理很像?
4.2.頁面請求
先看整體頁面結構:
並且在Vue例項掛載後就會發起查詢:
我們重新整理頁面,可以看到瀏覽器發起已經發起了查詢商品資料的請求:
因此接下來,我們編寫介面即可。
4.3.後臺提供介面
頁面已經準備好,接下來在後臺提供分頁查詢SPU的功能。
4.3.1.實體類
SPU
@Table(name = "tb_spu")
public class Spu {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long brandId;
private Long cid1;// 1級類目
private Long cid2;// 2級類目
private Long cid3;// 3級類目
private String title;// 標題
private String subTitle;// 子標題
private Boolean saleable;// 是否上架
private Boolean valid;// 是否有效,邏輯刪除用
private Date createTime;// 建立時間
private Date lastUpdateTime;// 最後修改時間
// 省略getter和setter
}
SPU詳情
@Table(name="tb_spu_detail")
public class SpuDetail {
@Id
private Long spuId;// 對應的SPU的id
private String description;// 商品描述
private String specialSpec;// 商品特殊規格的名稱及可選值模板
private String genericSpec;// 商品的全域性規格屬性
private String packingList;// 包裝清單
private String afterService;// 售後服務
// 省略getter和setter
}
4.4.2.mapper
public interface SpuMapper extends Mapper<Spu> {
}
4.3.3.controller
先分析:
-
請求方式:GET
-
請求路徑:/spu/page
-
請求引數:
- page:當前頁
- rows:每頁大小
- key:過濾條件
- saleable:上架或下架
-
返回結果:商品SPU的分頁資訊。
-
要注意,頁面展示的是商品分類和品牌名稱,而資料庫中儲存的是id,怎麼辦?
我們可以新建一個類,繼承SPU,並且拓展cname和bname屬性,寫到
leyou-item-interface
public class SpuBo extends Spu { String cname;// 商品分類名稱 String bname;// 品牌名稱 // 略 。。 }
-
編寫controller程式碼:
我們把與商品相關的一切業務介面都放到一起,起名為GoodsController,業務層也是這樣
@RestController
public class GoodsController {
@Autowired
private GoodsService goodsService;
@GetMapping("spu/page")
public ResponseEntity<PageResult<SpuBo>> querySpuByPage(
@RequestParam(value = "page", defaultValue = "1") Integer page,
@RequestParam(value = "rows", defaultValue = "5") Integer rows,
@RequestParam(value = "saleable", required = false) Boolean saleable,
@RequestParam(value = "key", required = false) String key) {
PageResult<SpuBo> result = this.goodsService.querySpuPage(page, rows, saleable, key);
if (result == null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
}
return ResponseEntity.ok(result);
}
}
4.4.4.service
所有商品相關的業務(包括SPU和SKU)放到一個業務下:GoodsService。
@Service
public class GoodsService {
@Autowired
private SpuMapper spuMapper;
@Autowired
private CategoryService categoryService;
@Autowired
private BrandMapper brandMapper;
public PageResult<SpuBo> querySpuByPageAndSort(Integer page, Integer rows, Boolean saleable, String key) {
// 1、查詢SPU
// 分頁,最多允許查100條
PageHelper.startPage(page, Math.min(rows, 100));
// 建立查詢條件
Example example = new Example(Spu.class);
Example.Criteria criteria = example.createCriteria();
// 是否過濾上下架
if (saleable != null) {
criteria.orEqualTo("saleable", saleable);
}
// 是否模糊查詢
if (StringUtils.isNotBlank(key)) {
criteria.andLike("title", "%" + key + "%");
}
Page<Spu> pageInfo = (Page<Spu>) this.spuMapper.selectByExample(example);
List<SpuBo> list = pageInfo.getResult().stream().map(spu -> {
// 把spu變為 spuBo
SpuBo spuBo = new SpuBo();
// 屬性拷貝
BeanUtils.copyProperties(spu, spuBo);
// 2、查詢spu的商品分類名稱,要查三級分類
List<String> names = this.categoryService.queryNameByIds(
Arrays.asList(spu.getCid1(), spu.getCid2(), spu.getCid3()));
// 將分類名稱拼接後存入
spuBo.setCname(StringUtils.join(names, "/"));
// 3、查詢spu的品牌名稱
Brand brand = this.brandMapper.selectByPrimaryKey(spu.getBrandId());
spuBo.setBname(brand.getName());
return spuBo;
}).collect(Collectors.toList());
return new PageResult<>(pageInfo.getTotal(), list);
}
}
4.4.5.Category中拓展查詢名稱的功能
頁面需要商品的分類名稱需要在這裡查詢,因此要額外提供查詢分類名稱的