因最近又被困在了OSGI技術POC,更新進度有點慢,但願你們不要怪罪哦。html
上節 咱們實現了登陸以後前端的展現,如:
前端
接着,咱們來實現左側分類欄目的功能。java
從上圖咱們能夠看出,商品的分類實際上是有層級關係的,並且這種關係通常都是無限層級。在咱們的實現中,爲了效果的展現,咱們僅僅是展現3級分類,在大多數的中小型電商系統中,三級分類徹底足夠應對SKU的分類。git
先來分析分類都包含哪些元素,以jd
爲例:
程序員
分類Id
跳轉到固定的分類商品列表展現頁面,可是在一些特殊的場景,好比咱們要作一個活動,但願能夠點擊某一個分類的主圖直接定位到活動頁面,這個url就可使用了。在上一小節,咱們簡單分析了一下要實現商品分類的一些points
,那麼咱們最好在每次拿到需求【開發以前】,對需求進行拆解,而後分解開發流程,這樣能夠保證咱們更好的理解需求,以及在開發以前發現一部分不合理的需求,而且若是需求設計不合理的話,開發人員徹底有權,也有責任告知PM。你們的終極目的都是爲了咱們作的產品更加合理,好用,受歡迎!github
1.在com.liferunner.service
中建立service 接口ICategoryService.java
, 編寫查詢全部一級分類的方法getAllRootCategorys
,以下:web
package com.liferunner.service; import com.liferunner.dto.CategoryResponseDTO; import com.liferunner.dto.SecondSubCategoryResponseDTO; import java.util.List; /** * ICategoryService for : 分類service * * @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang | 若初</a> * @since 2019/11/13 */ public interface ICategoryService { /** * 獲取全部有效的一級分類(根節點) * * @return */ List<CategoryResponseDTO> getAllRootCategorys(); }
2.編寫實現類com.liferunner.service.ICategoryService.java
spring
@Service @Slf4j public class CategorySericeImpl implements ICategoryService { @Autowired private CategoryMapper categoryMapper; @Override public List<CategoryResponseDTO> getAllRootCategorys() { Example example = new Example(Category.class); val conditions = example.createCriteria(); conditions.andEqualTo("type", CategoryTypeEnum.ROOT.type); val categoryList = this.categoryMapper.selectByExample(example); //聲明返回對象 List<CategoryResponseDTO> categoryResponseDTOS = new ArrayList<>(); if (!CollectionUtils.isEmpty(categoryList)) { //賦值 CategoryResponseDTO dto; for (Category category : categoryList) { dto = new CategoryResponseDTO(); BeanUtils.copyProperties(category, dto); categoryResponseDTOS.add(dto); } } return categoryResponseDTOS; } }
上述代碼很好理解,建立tk.mybatis.mapper.entity.Example
,將條件傳入,而後使用通用Mapper
查詢到type=1
的一級分類,接着將查到的對象列表轉換爲DTO對象列表。json
通常狀況下,此類查詢都會出如今網站的首頁,所以咱們來建立一個com.liferunner.api.controller.IndexController
,並對外暴露一個查詢一級分類的接口:segmentfault
package com.liferunner.api.controller; import com.liferunner.service.ICategoryService; import com.liferunner.service.IProductService; import com.liferunner.service.ISlideAdService; import com.liferunner.utils.JsonResponse; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import java.util.Collections; import lombok.extern.slf4j.Slf4j; import lombok.val; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.CollectionUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; /** * IndexController for : 首頁controller * * @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang | 若初</a> * @since 2019/11/12 */ @RestController @RequestMapping("/index") @Api(value = "首頁信息controller", tags = "首頁信息接口API") @Slf4j public class IndexController { @Autowired private ICategoryService categoryService; @GetMapping("/rootCategorys") @ApiOperation(value = "查詢一級分類", notes = "查詢一級分類") public JsonResponse findAllRootCategorys() { log.info("============查詢一級分類=============="); val categoryResponseDTOS = this.categoryService.getAllRootCategorys(); if (CollectionUtils.isEmpty(categoryResponseDTOS)) { log.info("============未查詢到任何分類=============="); return JsonResponse.ok(Collections.EMPTY_LIST); } log.info("============一級分類查詢result:{}==============", categoryResponseDTOS); return JsonResponse.ok(categoryResponseDTOS); } }
編寫完成以後,咱們須要對咱們的代碼進行測試驗證,仍是經過使用RestService
插件來實現,固然,你們也能夠經過Postman來測試。
{ "status": 200, "message": "OK", "data": [ { "id": 1, "name": "菸酒", "type": 1, "parentId": 0, "logo": "img/cake.png", "slogan": "吸菸受害健康", "catImage": "http://www.life-runner.com/shop/category/cake.png", "bgColor": "#fe7a65" }, { "id": 2, "name": "服裝", "type": 1, "parentId": 0, "logo": "img/cookies.png", "slogan": "我選擇我喜歡", "catImage": "http://www.life-runner.com/shop/category/cookies.png", "bgColor": "#f59cec" }, { "id": 3, "name": "鞋帽", "type": 1, "parentId": 0, "logo": "img/meat.png", "slogan": "飛通常的感受", "catImage": "http://www.life-runner.com/shop/category/meat.png", "bgColor": "#b474fe" } ], "ok": true }
由於根據一級id查詢子分類的時候,咱們是在同一張表中作自鏈接查詢,所以,通用mapper已經不適合咱們的使用,所以咱們須要自定義mapper來實現咱們的需求。
在以前的編碼中,咱們都是使用的插件幫咱們實現的通用Mapper
,可是這種查詢只能處理簡單的單表CRUD
,一旦咱們須要SQL 包含一部分邏輯處理的時候,那就必須得本身來編寫了,let's code.
1.在項目mscx-shop-mapper
中,建立一個新的custom package
,在該目錄下建立自定義mappercom.liferunner.custom.CategoryCustomMapper
。
public interface CategoryCustomMapper { List<SecondSubCategoryResponseDTO> getSubCategorys(Integer parentId); }
2.resources
目錄下建立目錄mapper.custom
,以及建立和上面的接口相同名稱的XML
文件mapper/custom/CategoryCustomMapper.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.liferunner.custom.CategoryCustomMapper"> <resultMap id="subCategoryDTO" type="com.liferunner.dto.SecondSubCategoryResponseDTO"> <id column="id" jdbcType="INTEGER" property="id"/> <result column="name" jdbcType="VARCHAR" property="name"/> <result column="type" jdbcType="INTEGER" property="type"/> <result column="parentId" jdbcType="INTEGER" property="parentId"/> <collection property="thirdSubCategoryResponseDTOList" ofType="com.liferunner.dto.ThirdSubCategoryResponseDTO"> <id column="subId" jdbcType="INTEGER" property="subId"/> <result column="subName" jdbcType="VARCHAR" property="subName"/> <result column="subType" jdbcType="INTEGER" property="subType"/> <result column="subParentId" jdbcType="INTEGER" property="subParentId"/> </collection> </resultMap> <select id="getSubCategorys" resultMap="subCategoryDTO" parameterType="INTEGER"> SELECT p.id as id,p.`name` as `name`,p.`type` as `type`,p.father_id as parentId, c.id as subId,c.`name` as subName,c.`type` as subType,c.parent_id as subParentId FROM category p LEFT JOIN category c ON p.id = c.parent_id WHERE p.parent_id = ${parentId}; </select> </mapper>
TIPS
上述建立的package,必定要在項目的啓動類com.liferunner.api.ApiApplication
中修改@MapperScan(basePackages = { "com.liferunner.mapper", "com.liferunner.custom"})
,若是不把咱們的custom
package加上,會形成掃描不到而報錯。
在上面的xml中,咱們定義了兩個DTO對象,分別用來處理二級和三級分類的DTO,實現以下:
@Data @ToString public class SecondSubCategoryResponseDTO { /** * 主鍵 */ private Integer id; /** * 分類名稱 */ private String name; /** * 分類類型 1:一級大分類 2:二級分類 3:三級小分類 */ private Integer type; /** * 父id */ private Integer parentId; List<ThirdSubCategoryResponseDTO> thirdSubCategoryResponseDTOList; } --- @Data @ToString public class ThirdSubCategoryResponseDTO { /** * 主鍵 */ private Integer subId; /** * 分類名稱 */ private String subName; /** * 分類類型 1:一級大分類 2:二級分類 3:三級小分類 */ private Integer subType; /** * 父id */ private Integer subParentId; }
編寫完自定義mapper以後,咱們就能夠繼續編寫service了,在com.liferunner.service.ICategoryService
中新增一個方法:getAllSubCategorys(parentId)
.以下:
public interface ICategoryService { ... /** * 根據一級分類獲取子分類 * * @param parentId 一級分類id * @return 子分類list */ List<SecondSubCategoryResponseDTO> getAllSubCategorys(Integer parentId); }
在com.liferunner.service.impl.CategorySericeImpl
實現上述方法:
@Service @Slf4j public class CategorySericeImpl implements ICategoryService { @Autowired private CategoryMapper categoryMapper; @Autowired private CategoryCustomMapper categoryCustomMapper; ... @Override @Transactional(propagation = Propagation.SUPPORTS) public List<SecondSubCategoryResponseDTO> getAllSubCategorys(Integer parentId) { return this.categoryCustomMapper.getSubCategorys(parentId); } }
@RestController @RequestMapping("/index") @Api(value = "首頁信息controller", tags = "首頁信息接口API") @Slf4j public class IndexController { @Autowired private ICategoryService categoryService; ... @GetMapping("/subCategorys/{parentId}") @ApiOperation(value = "查詢子分類", notes = "根據一級分類id查詢子分類") public JsonResponse findAllSubCategorys( @ApiParam(name = "parentId", value = "一級分類id", required = true) @PathVariable Integer parentId) { log.info("============查詢id = {}的子分類==============", parentId); val categoryResponseDTOS = this.categoryService.getAllSubCategorys(parentId); if (CollectionUtils.isEmpty(categoryResponseDTOS)) { log.info("============未查詢到任何分類=============="); return JsonResponse.ok(Collections.EMPTY_LIST); } log.info("============子分類查詢result:{}==============", categoryResponseDTOS); return JsonResponse.ok(categoryResponseDTOS); } }
{ "status": 200, "message": "OK", "data": [ { "id": 11, "name": "國產", "type": 2, "parentId": 1, "thirdSubCategoryResponseDTOList": [ { "subId": 37, "subName": "中華", "subType": 3, "subParentId": 11 }, { "subId": 38, "subName": "冬蟲夏草", "subType": 3, "subParentId": 11 }, { "subId": 39, "subName": "南京", "subType": 3, "subParentId": 11 }, { "subId": 40, "subName": "雲煙", "subType": 3, "subParentId": 11 } ] }, { "id": 12, "name": "外菸", "type": 2, "parentId": 1, "thirdSubCategoryResponseDTOList": [ { "subId": 44, "subName": "XXXXX", "subType": 3, "subParentId": 12 }, { "subId": 45, "subName": "RRRRR", "subType": 3, "subParentId": 12 } ] } ], "ok": true }
以上咱們就已經實現了和jd
相似的商品分類的功能實現。
這個就是jd
或者tb
首先的最頂部的廣告圖片是同樣的,每隔1秒自動切換圖片。接下來咱們分析一下輪播圖中都包含哪些信息:
直接查詢出全部的有效的
輪播圖片,而且進行排序
。
和商品分類實現同樣,在mscx-shop-service
中建立com.liferunner.service.ISlideAdService
並實現,代碼以下:
public interface ISlideAdService { /** * 查詢全部可用廣告並排序 * @param isShow * @return */ List<SlideAdResponseDTO> findAll(Integer isShow, String sortRanking); }
@Service @Slf4j public class SlideAdServiceImpl implements ISlideAdService { // 注入mapper private final SlideAdsMapper slideAdsMapper; @Autowired public SlideAdServiceImpl(SlideAdsMapper slideAdsMapper) { this.slideAdsMapper = slideAdsMapper; } @Override public List<SlideAdResponseDTO> findAll(Integer isShow, String sortRanking) { Example example = new Example(SlideAds.class); //設置排序 if (StringUtils.isBlank(sortRanking)) { example.orderBy("sort").asc(); } else { example.orderBy("sort").desc(); } val conditions = example.createCriteria(); conditions.andEqualTo("isShow", isShow); val slideAdsList = this.slideAdsMapper.selectByExample(example); //聲明返回對象 List<SlideAdResponseDTO> slideAdResponseDTOList = new ArrayList<>(); if (!CollectionUtils.isEmpty(slideAdsList)) { //賦值 SlideAdResponseDTO dto; for (SlideAds slideAds : slideAdsList) { dto = new SlideAdResponseDTO(); BeanUtils.copyProperties(slideAds, dto); slideAdResponseDTOList.add(dto); } } return slideAdResponseDTOList; } }
從上述能夠看到,這裏我使用的是構造函數注入SlideAdsMapper
,其他代碼單表查詢沒什麼特別的,根據條件查詢輪播圖,並返回結果,返回的對象是com.liferunner.dto.SlideAdResponseDTO
列表,代碼以下:
@Data @AllArgsConstructor @NoArgsConstructor @Builder @ApiModel(value = "輪播廣告返回DTO", description = "輪播廣告返回DTO") public class SlideAdResponseDTO{ /** * 主鍵 */ private String id; /** * 圖片地址 */ private String imageUrl; /** * 背景顏色 */ private String backgroundColor; /** * 商品id */ private String productId; /** * 商品分類id */ private String catId; /** * 圖片跳轉URL */ private String imageLinkUrl; /** * 輪播圖類型 用於判斷,能夠根據商品id或者分類進行頁面跳轉,1:商品 2:分類 3:連接url */ private Integer type; /** * 輪播圖展現順序 輪播圖展現順序,從小到大 */ private Integer sort; /** * 是否展現 是否展現,1:展現 0:不展現 */ private Integer isShow; /** * 建立時間 建立時間 */ private Date createTime; /** * 更新時間 更新 */ private Date updateTime; }
在com.liferunner.api.controller.IndexController
中,新添加一個查詢輪播圖API,代碼以下:
@Autowired private ISlideAdService slideAdService; @GetMapping("/slideAds") @ApiOperation(value = "查詢輪播廣告", notes = "查詢輪播廣告接口") public JsonResponse findAllSlideList() { log.info("============查詢全部輪播廣告,isShow={},sortRanking={}==============" , 1, "desc"); val slideAdsList = this.slideAdService.findAll(1, "desc"); if (CollectionUtils.isEmpty(slideAdsList)) { log.info("============未查詢到任何輪播廣告=============="); return JsonResponse.ok(Collections.EMPTY_LIST); } log.info("============輪播廣告查詢result:{}==============" , slideAdsList); return JsonResponse.ok(slideAdsList); }
{ "status": 200, "message": "OK", "data": [ { "id": "slide-100002", "imageUrl": "http://www.life-runner.com/2019/11/CpoxxF0ZmH6AeuRrAAEZviPhyQ0768.png", "backgroundColor": "#55be59", "productId": "", "catId": "133", "type": 2, "sort": 2, "isShow": 1, "createTime": "2019-10-11T21:33:01.000+0000", "updateTime": "2019-10-11T21:33:02.000+0000" }, { "id": "slide-100003", "imageUrl": "http://www.life-runner.com/2019/11/CpoxxF0ZmHuAPlXvAAFe-H5_-Nw961.png", "backgroundColor": "#ff9801", "productId": "y200008", "catId": "", "type": 1, "sort": 1, "isShow": 1, "createTime": "2019-10-11T21:33:01.000+0000", "updateTime": "2019-10-11T21:33:02.000+0000" } ], "ok": true }
在咱們的實現代碼中,有心的同窗能夠看到,我使用了3種不一樣的Bean注入方式:
@Autowired private ISlideAdService slideAdService;
// 注入mapper private final SlideAdsMapper slideAdsMapper; @Autowired public SlideAdServiceImpl(SlideAdsMapper slideAdsMapper) { this.slideAdsMapper = slideAdsMapper; }
@RequiredArgsConstructor(onConstructor = @__(@Autowired)) public class ProductServiceImpl implements IProductService { // RequiredArgsConstructor 構造器注入 private final ProductCustomMapper productCustomMapper; private final ProductsMapper productsMapper; ... }
那麼,這幾種注入都有什麼區別呢?首先咱們下了解一下Spring的注入是幹什麼的?
Spring提出了依賴注入的思想,即依賴類不禁程序員實例化,而是經過Spring容器幫咱們new指定實例而且將實例注入到須要該對象的類中。
依賴注入的另外一種說法是"控制反轉"。通俗的理解是:日常咱們new一個實例,這個實例的控制權是咱們程序員, 而控制反轉是指new實例工做不禁咱們程序員來作而是交給Spring容器來作。
在傳統的SpringMVC中,你們使用的都是XML注入
,好比:
<!--配置bean,配置後該類由spring管理--> <bean name="CategorySericeImpl" class="com.liferunner.service.impl.CategorySericeImpl"> <!--注入配置當前類中相應的屬性--> <property name="categoryMapper" ref="categoryMapper"></property> </bean> <bean name="categoryMapper" class="com.liferunner.mapper.CategoryMapper"></bean>
注入以後,使用@Autowired
,咱們能夠很方便的自動從IOC容器中查找屬性,並返回。
@Autowired的原理
在啓動spring IoC時,容器自動裝載了一個AutowiredAnnotationBeanPostProcessor
後置處理器,當容器掃描到@Autowied、@Resource或@Inject時,就會在IoC容器自動查找須要的bean,並裝配給該對象的屬性。
注意事項:
在使用@Autowired時,首先在容器中查詢對應類型的bean
若是查詢結果恰好爲一個,就將該bean裝配給@Autowired指定的數據
若是查詢的結果不止一個,那麼@Autowired會根據名稱來查找。
若是查詢的結果爲空,那麼會拋出異常。解決方法時,使用required=false
source傳送門
上述三種註解方式,其實本質上仍是注入的2種:set屬性注入
& 構造器注入
,使用方式均可以,根據我的喜愛來用,本人喜歡使用lombok插件注入
是由於,它將代碼整合在一塊兒,更加符合咱們Spring自動注入的規範。
下一節咱們將繼續開發咱們電商的核心部分-商品列表和詳情展現,在過程當中使用到的任何開發組件,我都會經過專門的一節來進行介紹的,兄弟們末慌!
gogogo!