Commit 4ee4435a55490e9e4ad0d62fbe54d1e7f7ac2870

Authored by 曾国涛
1 parent 15f913dc

feat(静态页面生成): 实现产品列表静态页面生成功能

- 新增静态页面配置和生成逻辑
- 添加 FreeMarker 模板支持
- 实现产品数据动态加载和渲染
- 优化产品价格显示逻辑
- 新增静态资源路径配置

Too many changes to show.

To preserve performance only 10 of 11 files are displayed.

... ... @@ -81,6 +81,14 @@
81 81 <artifactId>mybatis-plus-boot-starter</artifactId>
82 82 <version>${baomidou.version}</version>
83 83 </dependency>
  84 +
  85 + <dependency>
  86 + <groupId>org.springframework.boot</groupId>
  87 + <artifactId>spring-boot-starter-freemarker</artifactId>
  88 + <version>3.3.5</version>
  89 + </dependency>
  90 +
  91 +
84 92 <dependency>
85 93 <groupId>cn.hutool</groupId>
86 94 <artifactId>hutool-crypto</artifactId>
... ...
shop/pom.xml
... ... @@ -38,6 +38,11 @@
38 38 </dependency>
39 39 <dependency>
40 40 <groupId>org.springframework.boot</groupId>
  41 + <artifactId>spring-boot-starter-freemarker</artifactId>
  42 + <version>3.3.5</version>
  43 + </dependency>
  44 + <dependency>
  45 + <groupId>org.springframework.boot</groupId>
41 46 <artifactId>spring-boot-starter-aop</artifactId>
42 47 </dependency>
43 48 <dependency>
... ...
shop/src/main/java/com/canrd/shop/config/StaticHtmlConfig.java 0 → 100644
  1 +package com.canrd.shop.config;
  2 +
  3 +import lombok.Data;
  4 +import org.springframework.beans.factory.annotation.Value;
  5 +import org.springframework.boot.context.properties.ConfigurationProperties;
  6 +import org.springframework.stereotype.Component;
  7 +
  8 +import java.util.Map;
  9 +
  10 +@Component
  11 +@Data
  12 +@ConfigurationProperties(prefix = "static-html.product-list")
  13 +public class StaticHtmlConfig {
  14 +
  15 + private String url;
  16 +
  17 +}
... ...
shop/src/main/java/com/canrd/shop/controller/ProductController.java
... ... @@ -2,26 +2,29 @@ package com.canrd.shop.controller;
2 2  
3 3 import com.canrd.shop.common.constant.ServerResult;
4 4 import com.canrd.shop.common.jsr303.OperateGroup;
  5 +import com.canrd.shop.config.StaticHtmlConfig;
  6 +import com.canrd.shop.module.dto.ProductDO;
5 7 import com.canrd.shop.module.vo.ProductCategoryQueryVO;
6 8 import com.canrd.shop.module.vo.ProductQueryVO;
7 9 import com.canrd.shop.module.vo.ProductVO;
8 10 import com.canrd.shop.service.ProductCategoryService;
9 11 import com.canrd.shop.service.ProductService;
10   -import org.apache.commons.collections.Transformer;
11   -import org.apache.commons.collections.functors.ChainedTransformer;
12   -import org.apache.commons.collections.functors.ConstantTransformer;
13   -import org.apache.commons.collections.functors.InvokerTransformer;
14   -import org.apache.commons.collections.keyvalue.TiedMapEntry;
15   -import org.apache.commons.collections.map.LazyMap;
  12 +import freemarker.template.Configuration;
  13 +import freemarker.template.Template;
  14 +import freemarker.template.TemplateException;
  15 +import org.apache.commons.lang3.StringUtils;
16 16 import org.springframework.beans.factory.annotation.Autowired;
  17 +import org.springframework.beans.factory.annotation.Value;
  18 +import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
17 19 import org.springframework.validation.annotation.Validated;
18 20 import org.springframework.web.bind.annotation.*;
19 21  
20 22 import javax.annotation.Resource;
21 23 import java.io.*;
22 24 import java.lang.reflect.Field;
23   -import java.util.HashMap;
24   -import java.util.Map;
  25 +import java.net.URL;
  26 +import java.util.*;
  27 +import java.util.stream.Stream;
25 28  
26 29 /**
27 30 * (Product)表控制层
... ... @@ -32,6 +35,13 @@ import java.util.Map;
32 35 @RestController
33 36 @RequestMapping("/shop/product")
34 37 public class ProductController {
  38 + @Autowired
  39 + private Configuration freeMarkerConfig;
  40 + @Value("${spring.profiles.active}")
  41 + private String activeProfile;
  42 +
  43 + @Autowired
  44 + private StaticHtmlConfig staticHtmlConfig;
35 45 /**
36 46 * 服务对象
37 47 */
... ... @@ -69,6 +79,47 @@ public class ProductController {
69 79 }
70 80  
71 81 /**
  82 + * 分页查询
  83 + *
  84 + * @param productQueryVO 查询条件
  85 + * @return 查询结果
  86 + */
  87 + @GetMapping("/generateList")
  88 + public ServerResult generateList(@Validated({OperateGroup.List.class}) ProductQueryVO productQueryVO) throws IOException, TemplateException {
  89 + String path = staticHtmlConfig.getUrl();
  90 + String fileName = Optional.ofNullable(productQueryVO.getRootProductCategoryId()).orElse("-")+"_"
  91 + +Optional.ofNullable(productQueryVO.getProductCategoryId()).orElse("-")+"_"
  92 + +Optional.ofNullable(productQueryVO.getProductFunctionId()).orElse("-")+"_"
  93 + +Optional.ofNullable(productQueryVO.getKeyword()).orElse("-").trim();
  94 + Map res = (Map) productService.listBySimilar(productQueryVO).getData();
  95 + List<ProductDO> itemList = (List<ProductDO>) res.get("records");
  96 +
  97 + // 将数据模型放入 map
  98 + Map<String, Object> dataModel = new HashMap<>();
  99 + dataModel.put("items", itemList);
  100 +
  101 + // 获取 FreeMarker 模板
  102 + Template template = freeMarkerConfig.getTemplate("canrud.ftl");
  103 +
  104 + // 渲染模板并保存到文件
  105 + String renderedTemplate = FreeMarkerTemplateUtils.processTemplateIntoString(template, dataModel);
  106 +
  107 + //将renderedTemplate写入到path目录
  108 + String outputDir = path;
  109 +
  110 + // 指定文件路径
  111 + File outputFile = new File(outputDir, fileName+".html");
  112 +
  113 + // 将渲染后的内容写入文件
  114 + try (FileWriter writer = new FileWriter(outputFile)) {
  115 + writer.write(renderedTemplate);
  116 + }
  117 +
  118 + System.out.println("文件已成功保存到 " + outputFile.getAbsolutePath());
  119 + return null;
  120 + }
  121 +
  122 + /**
72 123 * 通过主键查询单条数据
73 124 *
74 125 * @param productQueryVO 查询条件
... ...
shop/src/main/java/com/canrd/shop/module/dto/ProductDO.java
... ... @@ -48,6 +48,7 @@ public class ProductDO implements Serializable {
48 48  
49 49 private Double marketprice;
50 50  
  51 +
51 52 private String metadescription;
52 53  
53 54 private String metakeywords;
... ... @@ -130,6 +131,8 @@ public class ProductDO implements Serializable {
130 131 @TableField(value = "productType_id")
131 132 private String productTypeId;
132 133  
  134 + private String imageFileKey;
  135 +
133 136 @TableField(exist = false)
134 137 private Integer similar;
135 138 @TableField(exist = false)
... ...
shop/src/main/java/com/canrd/shop/service/impl/ProductServiceImpl.java
... ... @@ -2,6 +2,7 @@ package com.canrd.shop.service.impl;
2 2  
3 3 import cn.hutool.core.bean.BeanUtil;
4 4 import cn.hutool.core.collection.CollUtil;
  5 +import com.alibaba.fastjson2.JSON;
5 6 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
6 7 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
7 8 import com.baomidou.mybatisplus.core.metadata.IPage;
... ... @@ -180,15 +181,24 @@ public class ProductServiceImpl extends ServiceImpl&lt;ProductMapper, ProductDO&gt; im
180 181 }
181 182 Set<String> productIds = null;
182 183 Map<String,List<TicketTypeDO>> pId2ttDOsMap = null;
  184 + Map<String,BigDecimal> pId2ttMinPriceMap = null;
183 185 boolean needLikeMatch = false;
184 186 if(StringUtils.isNotBlank(productQueryVO.getKeyword())){
185 187 List<TicketTypeDO> tickeyTypeDOList = ticketTypeService.lambdaQuery().like(TicketTypeDO::getRank, productQueryVO.getKeyword()).list();
186 188 productIds = tickeyTypeDOList.stream().map(TicketTypeDO::getProductId).collect(Collectors.toSet());
187 189 needLikeMatch = true;
188 190 pId2ttDOsMap = new HashMap<>();
  191 + pId2ttMinPriceMap = new HashMap<>();
189 192 for (TicketTypeDO ticketTypeDO : tickeyTypeDOList) {
190 193 pId2ttDOsMap.computeIfAbsent(ticketTypeDO.getProductId(), k -> new ArrayList<>());
191 194 pId2ttDOsMap.get(ticketTypeDO.getProductId()).add(ticketTypeDO);
  195 + if (Objects.isNull(pId2ttMinPriceMap.get(ticketTypeDO.getProductId()))){
  196 + pId2ttMinPriceMap.put(ticketTypeDO.getProductId(), ticketTypeDO.getPrice());
  197 + }else {
  198 + if (pId2ttMinPriceMap.get(ticketTypeDO.getProductId()).compareTo(ticketTypeDO.getPrice())>0){
  199 + pId2ttMinPriceMap.put(ticketTypeDO.getProductId(), ticketTypeDO.getPrice());
  200 + }
  201 + }
192 202 }
193 203 Set<String> finalProductIds = productIds;
194 204 queryWapper.and(subQueryWapper -> {
... ... @@ -206,7 +216,13 @@ public class ProductServiceImpl extends ServiceImpl&lt;ProductMapper, ProductDO&gt; im
206 216 }
207 217 });
208 218 List<ProductDO> similarProductList = Lists.newArrayList();
  219 + Boolean productPriceShow = switchControlService.getEnabledByName(SwitchControlConstants.PRODUCT_PRICE_SHOW);
209 220 for(ProductDO product:productDOS){
  221 + if (productPriceShow){
  222 + if (Objects.nonNull(pId2ttMinPriceMap.get(product.getId()))){
  223 + product.setPrice(pId2ttMinPriceMap.get(product.getId()));
  224 + }
  225 + }
210 226 if(accurateProductIdSet.contains(product.getId())){
211 227 continue;
212 228 }
... ... @@ -235,7 +251,12 @@ public class ProductServiceImpl extends ServiceImpl&lt;ProductMapper, ProductDO&gt; im
235 251 List<ProductDO> allProductList = Lists.newArrayList();
236 252 allProductList.addAll(accurateProducts);
237 253 allProductList.addAll(similarProductList);
238   -
  254 + allProductList.forEach(productDO -> {
  255 + //将productDO的productimageliststore解析为map
  256 + List<Map> maps = JSON.parseArray(productDO.getProductimageliststore(), Map.class);
  257 + Map map = maps.get(0);
  258 + productDO.setImageFileKey(map.get("fileKey").toString());
  259 + });
239 260 return pager(productQueryVO,allProductList);
240 261 }
241 262  
... ...
shop/src/main/java/com/canrd/shop/service/impl/SwitchControlServiceImpl.java
... ... @@ -4,9 +4,9 @@ import com.canrd.shop.module.dto.SwitchControl;
4 4 import com.canrd.shop.mapper.SwitchControlMapper;
5 5 import com.canrd.shop.service.ISwitchControlService;
6 6 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
7   -import org.apache.commons.collections.CollectionUtils;
8 7 import org.apache.commons.lang3.BooleanUtils;
9 8 import org.springframework.stereotype.Service;
  9 +import org.springframework.util.CollectionUtils;
10 10  
11 11 import java.util.List;
12 12  
... ...
shop/src/main/resources/application-prod.yml
... ... @@ -65,3 +65,7 @@ spring:
65 65  
66 66 logging:
67 67 config: classpath:log4j2-prod.xml
  68 +
  69 +static-html:
  70 + product-list:
  71 + url: /etc/nginx/canrud/products_html
68 72 \ No newline at end of file
... ...
shop/src/main/resources/application-test.yml
... ... @@ -64,4 +64,9 @@ spring:
64 64  
65 65  
66 66 logging:
67   - config: classpath:log4j2-dev.xml
68 67 \ No newline at end of file
  68 + config: classpath:log4j2-dev.xml
  69 +
  70 +
  71 +static-html:
  72 + product-list:
  73 + url: E:\productList
69 74 \ No newline at end of file
... ...
shop/src/main/resources/application.yml
... ... @@ -3,8 +3,13 @@ server:
3 3  
4 4 spring:
5 5 profiles:
6   - active: prod
  6 + active: test
7 7 mvc:
8 8 throw-exception-if-no-handler-found: true
9 9 resources:
10 10 add-mappings: false
  11 +static-html:
  12 + product-list:
  13 + urls:
  14 + test: E:\productList
  15 + prod: /etc/nginx/canrud/products_html
11 16 \ No newline at end of file
... ...