Table of contents
1.1. Configure Nginx and gateway
1.2. Static and dynamic resource allocation
1.3. Jump from the search page to the details page
2. Model class extraction and controller
2.1. Analyze the information that needs to be displayed on the homepage
2.3. Sales attribute combination
2.5. Create an ItemController to display the details of the current sku
3.0, business process, get homepage information according to sku_id
3.1. Obtain basic sku information
3.2. Get the picture information of sku
3.3. Obtain the sales attribute combination of spu
3.3.2,SkuSaleAttrValueServiceImpl
3.3.4, sql, query all sales attributes id,name,value under the specified spu_id
3.4. Get the introduction of spu
3.5. Obtain the specification parameter information of spu
4. Front-end, details page rendering
5. Switch between sku combinations, click on the sales attribute to jump to sku products
5.2, mapper, obtain sales attributes through sku_id
5.3. On the front end, modify the item.html file and re-render the sales attributes
6. Asynchronous orchestration optimization
6.1.1. Add thread pool attribute class
6.1.2. Import dependencies, spring metadata processor
6.1.3, yml configuration thread pool
6.1.4, custom thread pool configuration class
6.2. Asynchronous orchestration optimization details page query business
1. Build the page environment
1.1. Configure Nginx and gateway
Configure Nginx and Gateway
Modify the local hosts file vim /etc/hosts
# Gulimall Host Start 127.0.0.1 gulimall.cn 127.0.0.1 search.gulimall.cn 127.0.0.1 item.gulimall.cn # Gulimall Host End
Configure Nginx (forward requests under search to the gateway)
The mall business-retrieval service has already been configured, and there is no need to modify it here
If you are not sure, you can check it again:
vim /mydata/nginx/conf/conf.d/gulimall.conf
server_name gulimall.com *.gulimall.com
configure gateway
Modify the application.yml under the path of gulimall-gateway service /src/main/resources
- id: gulimall_host uri: lb://gulimall-product predicates: - Host=gulimall.com,item.gulimall.com #Settings can also be routed to the item module via "item.gulimall.com"
1.2. Static and dynamic resource allocation
Dynamic resources:
Copy shangpinxiangqing.html to the gulimall-product/src/main/resources/templates/ directory, and rename it to item.index.
Modify the static resource addresses of src and herf in the "item.index" file, and add "/static/item" to the prefix
Static resources:
Upload static resources to nginx
1.3. Jump from the search page to the details page
-
Modify the list.html file in the gulimall-search service
<p class="da"> <a th:href="|http://item.gulimall.cn/${product.skuId}.html|" > <img th:src="${product.skuImg}" class="dim"> </a> </p>
-
Write Controller to realize page jump
-
Add the "com.atguigu.gulimall.product.web.ItemController" class, the code is as follows:
@Controller public class ItemController { /** * Display the details of the current sku * @param skuId * @return */ @GetMapping("/{skuId}.html") public String skuItem(@PathVariable("skuId") Long skuId) { System.out.println("ready query:" + skuId + "details of"); return "item.html"; //return to item.html } }
-
Access test:
After searching, click on the product to enter the details page:
2. Model class extraction and controller
2.1. Analyze the information that needs to be displayed on the homepage
According to the expected display information extraction on the home page:
Note: It is necessary to distinguish which information belongs to spu and which information belongs to sku.
2.2, home page model class vo
package com.xx.gulimall.product.vo; @ToString @Data public class SkuItemVo { //1. Acquisition of sku basic information pms_sku_info private SkuInfoEntity info; private boolean hasStock = true; //2. sku image information pms_sku_images private List<SkuImagesEntity> images; //3. Obtain the sales attribute combination of spu private List<SkuItemSaleAttrVo> saleAttr; //4. Obtain the introduction of spu private SpuInfoDescEntity desc; //5. Obtain the specification parameter information of spu private List<SpuItemAttrGroupVo> groupAttrs; //6. Preferential information for flash sale products private SeckillSkuVo seckillSkuVo; }
2.3. Sales attribute combination
@Data public class SkuItemSaleAttrsVo { private Long attrId; private String attrName; private String attrValues; }
2.4. Specification parameters
@ToString @Data public class SpuItemAttrGroupVo { private String groupName; private List<Attr> attrs; }
@Data public class Attr { private Long attrId; private String attrName; private String attrValue; }
2.5. Create an ItemController to display the details of the current sku
package com.xxx.gulimall.product.web; @Controller public class ItemController { @Resource private SkuInfoService skuInfoService; /** * Display the details of the current sku */ @GetMapping("/{skuId}.html") public String skuItem(@PathVariable("skuId") Long skuId, Model model) throws ExecutionException, InterruptedException { System.out.println("ready query" + skuId + "details"); SkuItemVo vos = skuInfoService.item(skuId); model.addAttribute("item",vos); return "item"; } }
3. Business realization (do not use asynchrony)
3.0, business process, get homepage information according to sku_id
- 1. Acquisition of sku basic information pms_sku_info
- 2. sku image information pms_sku_images
- 3. Obtain the sales attribute combination of spu
- 4. Get the introduction of spu pms_spu_info_desc
- 5. Obtain the specification parameter information of spu
3.1. Obtain basic sku information
Query pms_sku_info
com.xxx.gulimall.product.service.impl.SkuInfoServiceImpl
@Override public SkuItemVo item(Long skuId) throws ExecutionException, InterruptedException{ // 1. sku basic information pms_sku_info SkuInfoEntity info = getById(skuId); Long spuId=info.getSpuId(); skuItemVo.setInfo(info); }
3.2. Get the picture information of sku
Table pms_sku_images
com.xxx.gulimall.product.service.impl.SkuInfoServiceImpl
// 2. sku image information pms_sku_images List<SkuImagesEntity> images = skuImagesService.getImagesBySkuId(skuId); skuItemVo.setImages(images);
Inject SkuImagesService and call the getImagesBySkuId(skuId) method of the implementation class to obtain the image information of the spu
Write the SkuImagesServiceImpl implementation class under the com.atguigu.gulimall.product.service.impl path:
@Override public List<SkuImagesEntity> getImagesBySkuId(Long skuId) { SkuImagesDao imagesDao = this.baseMapper; List<SkuImagesEntity> imagesEntities = imagesDao.selectList(new QueryWrapper<SkuImagesEntity>().eq("sku_id", skuId)); return imagesEntities; }
3.3. Obtain the sales attribute combination of spu
3.3.1,SkuInfoServiceImpl
com.xxx.gulimall.product.service.impl.SkuInfoServiceImpl
// 3. Obtain the sales attribute combination of spu List<SkuItemSaleAttrsVo> saleAttrVos = skuSaleAttrValueService.getSaleAttrsBySpuId(spuId); skuItemVo.setSaleAttr(saleAttrVos);
3.3.2,SkuSaleAttrValueServiceImpl
@Override public List<SkuItemSaleAttrsVo> getSaleAttrsBySpuId(Long spuId) { SkuSaleAttrValueDao dao = this.baseMapper; List<SkuItemSaleAttrsVo> saleAttrVos = dao.getSaleAttrsBySpuId(spuId); return saleAttrVos; }
3.3.3,dao
Use the getSaleAttrsBySpuId method of the SkuSaleAttrValueDao layer:
package com.atguigu.gulimall.product.dao; @Mapper public interface SkuSaleAttrValueDao extends BaseMapper<SkuSaleAttrValueEntity> { List<SkuItemSaleAttrsVo> getSaleAttrsBySpuId(@Param("spuId") Long spuId); }
3.3.4, sql, query all sales attributes id,name,value under the specified spu_id
sku table pms_sku_info:
sku sales attribute table pms_sku_sale_attr_value:
Left outer join query:
SELECT #Check the id,name, and value of the sales attribute ssav.attr_id attr_id, ssav.attr_name attr_name, GROUP_CONCAT( DISTINCT ssav.attr_value ) attr_values FROM #sku table left outer join sku sales attribute table pms_sku_info info LEFT JOIN pms_sku_sale_attr_value ssav ON ssav.sku_id = info.sku_id WHERE spu_id = #{spuId} GROUP BY ssav.attr_id, ssav.attr_name;
search result:
3.3.5,mapper
gulimall-product/src/main/resources/mapper/product/SkuSaleAttrValueDao.xml
<select id="getSaleAttrsBySpuId" resultType="com.atguigu.gulimall.product.vo.SkuItemSaleAttrsVo"> SELECT ssav.attr_id attr_id, ssav.attr_name attr_name, GROUP_CONCAT(DISTINCT ssav.attr_value) attr_values FROM pms_sku_info info LEFT JOIN pms_sku_sale_attr_value ssav ON ssav.sku_id = info.sku_id WHERE spu_id = #{spuId} GROUP BY ssav.attr_id,ssav.attr_name; </select>
Analyze how many skus the current spu has, and the attribute combinations involved in all skus
- Query the pms_sku_info table through spu_id to get the sku_id corresponding to the current spu
- Query the pms_sku_sale_attr_value table through sku_id to obtain the sales attributes of all skus corresponding to the current spu
- Encapsulate it into what we want by summarizing functions
3.4. Get the introduction of spu
Query pms_spu_info_desc
@Autowired SpuInfoDescService spuInfoDescService; // 4. Get the introduction of spu pms_spu_info_desc Long spuId = info.getSpuId(); SpuInfoDescEntity spuInfoDescEntity = spuInfoDescService.getById(spuId); skuItemVo.setDesp(spuInfoDescEntity);
3.5. Obtain the specification parameter information of spu
Query pms_spu_info_desc
@Autowired AttrGroupService attrGroupService; Long spuId = info.getSpuId(); Long catalogId = info.getCatalogId(); // 5. Obtain the specification parameter information of spu pms_spu_info_desc List<SpuItemAttrGroupVo> attrGroupVos = attrGroupService.getAttrGroupWithAttrsBySpuId(spuId,catalogId); skuItemVo.setGroupAttrs(attrGroupVos);
Inject AttrGroupService and call the getAttrGroupWithAttrsBySpuId(spuId, catalogId) method of the implementation class
/** * Investigate and deal with all attribute grouping information corresponding to the current spuId and the values corresponding to all attributes under the current grouping * @param spuId * @param catalogId * @return */ @Override public List<SpuItemAttrGroupVo> getAttrGroupWithAttrsBySpuId(Long spuId, Long catalogId) { AttrGroupDao baseMapper = this.getBaseMapper(); List<SpuItemAttrGroupVo> vos = baseMapper.getAttrGroupWithAttrsBySpuId(spuId,catalogId); return vos; }
Use the getAttrGroupWithAttrsBySpuId method of the AttrGroupDao layer:
package com.atguigu.gulimall.product.dao; @Mapper public interface AttrGroupDao extends BaseMapper<AttrGroupEntity> { List<SpuItemAttrGroupVo> getAttrGroupWithAttrsBySpuId(@Param("spuId") Long spuId, @Param("catalogId") Long catalogId); }
gulimall-product/src/main/resources/mapper/product/AttrGroupDao.xml :
<!--resultType Return the type of elements in the collection, as long as there are nested attributes, you need to customize the result set--> <resultMap id="spuItemAttrGroupVo" type="com.atguigu.gulimall.product.vo.SpuItemAttrGroupVo"> <result property="groupName" column="attr_group_name"></result> <collection property="attrs" ofType="com.atguigu.gulimall.product.vo.Attr"> <result property="attrName" column="attr_name"></result> <result property="attrValue" column="attr_value"></result> </collection> </resultMap> <select id="getAttrGroupWithAttrsBySpuId" resultMap="spuItemAttrGroupVo"> SELECT pav.spu_id, ag.attr_group_name, ag.attr_group_id, aar.attr_id, attr.attr_name, pav.attr_value FROM pms_attr_group ag LEFT JOIN pms_attr_attrgroup_relation aar ON ag.attr_group_id = aar.attr_group_id LEFT JOIN pms_attr attr ON attr.attr_id = aar.attr_id LEFT JOIN pms_product_attr_value pav on pav.attr_id = attr.attr_id WHERE ag.catelog_id=#{catalogId} AND pav.spu_id = #{spuId}; </select>
Join table query is used here:
- Query the information of the corresponding attribute group in the pms_attr_group table through category_id attr_group_id, attr_group_name
- Query the attribute id in the pms_attr_attrgroup_relation table through the attr_group_id join table attr_id
- Query the corresponding attr_name and attr_id in the pms_attr table through the attr_id join table
- Query the corresponding attribute value in the pms_product_attr_value table through the attr_id join table attr_value
4. Front-end, details page rendering
1. Add the namespace of thymeleaf
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org">
2. Title name setting
<div class="box-name" th:text="${item.info.skuTitle}"> Huawei HUAWEI Mate 10 6GB+128GB Bright black Mobile Unicom Telecom 4 G Mobile phone dual card dual standby </div> <div class="box-hide" th:text="${item.info.skuSubtitle}">Pre-order users are expected to ship around November 30! Kirin 970 chip! AI Smart camera! <a href="/static/item/"><u></u></a> </div>
3. Big picture display
<div class="imgbox"> <div class="probox"> <img class="img1" alt="" th:src="${item.info.skuDefaultImg}"> <div class="hoverbox"></div> </div> <div class="showbox"> <img class="img1" alt="" th:src="${item.info.skuDefaultImg}"> </div> </div>
4. Price setting
<div class="box-summary clear"> <ul> <li>Jingdong price</li> <li> <span>¥</span> <span th:text="${#numbers.formatDecimal(item.info.price,0,2)}">4499.00</span> </li> <li> Eligibility for reservation </li> <li> <a href="/static/item/"> Appointment instructions </a> </li> </ul> </div>
5. Is it in stock?
<li th:text="${item.hasStock?'In stock':'Sold Out'}"> <span>Sold Out</span>, This item is temporarily sold out </li>
6. Small picture display
<div class="box-lh-one"> <ul> <li th:each="img:${item.images}" th:if="${!#strings.isEmpty(img.imgUrl)}"><img th:src="${img.imgUrl}" /></li> </ul> </div>
7. Sales attribute
<div class="box-attr-3"> <div class="box-attr-2 clear" th:each="attr:${item.saleAttr}"> <dl> <dt>choose[[${attr.attrName}]]</dt> <dd th:each="val:${#strings.listSplit(attr.attrValues,',')}"> [[${val}]] </dd> </dl> </div> </div>
8. Product introduction
<img class="xiaoguo" th:src="${desccp}" th:each="desccp:${#strings.listSplit(item.desp.decript,',')}" />
9. Specification packaging
<li class="baozhuang actives" id="li2"> <div class="guiGebox" > <div class="guiGe" th:each="group:${item.groupAttrs}"> <h3 th:text="${group.groupName}">main body</h3> <dl> <div th:each="attr:${group.attrs}"> <dt th:text="${attr.attrName}">brand</dt> <dd th:text="${attr.attrValue}">Huawei(HUAWEI)</dd> </div> </dl> </div> <div class="package-list"> <h3>packing list</h3> <p>Mobile phone (with built-in battery) X 1,5A High current Huawei SuperCharge charger X 1,5A USB data line X 1,Semi in-ear remote control headset X 1,quick guide X 1,Three Guarantees Certificate X 1,Pick up needle X 1,protective case X 1</p> </div> </div> </li>
5. Switch between sku combinations, click on the sales attribute to jump to sku products
Requirement: Render sku products through different sales attributes
Obtain the sku corresponding to the sales attribute by selecting the sales attribute, and select the sku through the algorithm
5.1, encapsulation Vo class
-
Create the AttrValueWithSkuIdVo class under the com.atguigu.gulimall.product.vo path
@Data public class AttrValueWithSkuIdVo { private String attrValue; private String skuIds; }
-
Modify the SkuItemSaleAttrsVo class
@Data public class SkuItemSaleAttrsVo { private Long attrId; private String attrName; private List<AttrValueWithSkuIdVo> attrValues; }
5.2, mapper, obtain sales attributes through sku_id
Modify gulimall-product/src/main/resources/mapper/product/SkuSaleAttrValueDao.xml
<resultMap id="skuItemSaleAttrsVo" type="com.atguigu.gulimall.product.vo.SkuItemSaleAttrsVo"> <result property="attrId" column="attr_id"/> <result property="attrName" column="attr_name"/> <collection property="attrValues" ofType="com.atguigu.gulimall.product.vo.AttrValueWithSkuIdVo"> <result property="attrValue" column="attr_value"/> <result property="skuIds" column="sku_ids"/> </collection> </resultMap> <select id="getSaleAttrsBySpuId" resultMap="skuItemSaleAttrsVo"> SELECT ssav.attr_id attr_id, ssav.attr_name attr_name, ssav.attr_value attr_value, GROUP_CONCAT(DISTINCT info.sku_id) sku_ids FROM pms_sku_info info LEFT JOIN pms_sku_sale_attr_value ssav ON ssav.sku_id = info.sku_id WHERE info.spu_id = #{spuId} GROUP BY ssav.attr_id,ssav.attr_name,ssav.attr_value; </select>
5.3. On the front end, modify the item.html file and re-render the sales attributes
<div class="box-attr-3"> <div class="box-attr-2 clear" th:each="attr:${item.saleAttr}"> <dl> <dt>choose[[${attr.attrName}]]</dt> <dd th:each="vals:${attr.attrValues}"> <a th:attr=" class=${#lists.contains(#strings.listSplit(vals.skuIds,','),item.info.skuId.toString())}?'sku_attr_value checked':'sku_attr_value', skus=${vals.skuIds}"> [[${vals.attrValue}]] </a> </dd> </dl> </div> </div>
<script> $(".sku_attr_value").click(function () { // 1. Add custom attributes to the clicked element. To recognize that we were just clicked var skus = new Array(); $(this).addClass("ckicked"); // Look for kicked in the class attribute in this column attribute var curr = $(this).attr("skus").split(","); // Put all the currently clicked sku combination arrays into it skus.push(curr); // Remove all checked in the same line $(this).parent().parent().find(".sku_attr_value").removeClass("checked"); // Find that the class attribute in other attributes has checked $("a[class='sku_attr_value checked']").each(function () { skus.push($(this).attr("skus").split(",")); }); console.log(skus); // 2. Take out their intersection and get skuId var filterEle = skus[0]; for (var i = 1; i<skus.length; i++) { filterEle = $(filterEle).filter(skus[i]); } console.log(filterEle[0]); // 3. Jump location.href = "http://item.gulimall.cn/"+ filterEle[0] +".html"; }); $(function () { $(".sku_attr_value").parent().css({"border":"solid 1px #CCC"}); $("a[class='sku_attr_value checked']").parent().css({"border":"solid 1px red"}); }) </script>
6. Asynchronous orchestration optimization
6.1. Environment preparation
6.1.1. Add thread pool attribute class
Add a thread pool attribute configuration class and inject it into the container
package com.atguigu.gulimall.product.config; //Binding to configuration files related to gulimall.thread //This annotation sets the prefix of the yml configuration file, so that the yml data will be automatically injected into the Bean after configuration, no need to @Value @ConfigurationProperties(prefix = "gulimall.thread") @Component @Data public class ThreadPoolConfigProperties { private Integer coreSize; private Integer maxSize; private Integer keepAliveTime; }
6.1.2. Import dependencies, spring metadata processor
The function is to add prompts to the yml configuration, even without importing.
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>
6.1.3, yml configuration thread pool
Add the following configuration to the gulimall-product service:
# Configure thread pool gulimall: thread: core-size: 20 max-size: 200 keep-alive-time: 10
This is the custom configuration above, because the spring-boot-configuration-processor dependency is imported, so there is a hint when writing:
6.1.4, custom thread pool configuration class
Get the attribute value of the thread pool Here, directly call the attribute configuration class corresponding to the configuration file
package com.atguigu.gulimall.product.config; @Configuration public class MyThreadConfig { @Bean public ThreadPoolExecutor threadPoolExecutor(ThreadPoolConfigProperties pool) { return new ThreadPoolExecutor(pool.getCoreSize(), pool.getMaxSize(), pool.getKeepAliveTime(), TimeUnit.SECONDS, new LinkedBlockingDeque<>(100000), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy()); } }
6.2. Asynchronous orchestration optimization details page query business
Inject into the thread pool:
@Autowired ThreadPoolExecutor executor;
-
infoFuture
- saleAttrFuture
- descFuture
- baseAttrFuture (These three asynchronous tasks can only be executed after infoFuture is executed and its result is obtained)
-
imageFuture
Use supplyAsync instead of runAsync to get the results returned by the thread.
com.xxx.gulimall.product.service.impl.SkuInfoServiceImpl
@Override public SkuItemVo item(Long skuId) { SkuItemVo skuItemVo = new SkuItemVo(); // 1. sku basic information pms_sku_info //To create an asynchronous object, use supplyAsync instead of runAsync, so that the thread can return the result. CompletableFuture<SkuInfoEntity> infoFuture = CompletableFuture.supplyAsync(() -> { SkuInfoEntity info = getById(skuId); skuItemVo.setInfo(info); return info; }, executor); // 2. Obtain the sales attribute combination of spu //Thread serialization uses thenAcceptAsync to receive the result of the first step, that is, the sku entity class, and no result is returned after executing it by itself CompletableFuture<Void> saleAttrFuture = infoFuture.thenAcceptAsync(res -> { List<SkuItemSaleAttrsVo> saleAttrVos = saleAttrValueService.getSaleAttrsBySpuId(res.getSpuId()); skuItemVo.setSaleAttr(saleAttrVos); }, executor); // 3. Get the introduction of spu pms_spu_info_desc //The sku entity class of the first step is also needed here, so the thenAcceptAsync of the future is still the first step CompletableFuture<Void> descFuture = infoFuture.thenAcceptAsync(res -> { SpuInfoDescEntity spuInfoDescEntity = spuInfoDescService.getById(res.getSpuId()); skuItemVo.setDesp(spuInfoDescEntity); }, executor); // 4. Obtain the specification parameter information of spu pms_spu_info_desc CompletableFuture<Void> baseAttrFuture = infoFuture.thenAcceptAsync(res -> { List<SpuItemAttrGroupVo> attrGroupVos = attrGroupService.getAttrGroupWithAttrsBySpuId(res.getSpuId(), res.getCatalogId()); skuItemVo.setGroupAttrs(attrGroupVos); }, executor); // 5. sku image information pms_sku_images //This task has nothing to do with the previous tasks. //Create asynchronous objects here with runAsync instead of supplyAsync, because there is no need to get thread results CompletableFuture<Void> imageFuture = CompletableFuture.runAsync(() -> { List<SkuImagesEntity> images = imagesService.getImagesBySkuId(skuId); skuItemVo.setImages(images); }, executor); // Wait for all tasks to complete //Multi-task combination, allOf waits for all tasks to complete. There is no need to add infoFuture here, because the saleAttrFuture that depends on its result has been completed, and it must have been completed. CompletableFuture.allOf(saleAttrFuture,descFuture,baseAttrFuture,imageFuture).get(); return skuItemVo; }