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,6 +81,14 @@
81 <artifactId>mybatis-plus-boot-starter</artifactId> 81 <artifactId>mybatis-plus-boot-starter</artifactId>
82 <version>${baomidou.version}</version> 82 <version>${baomidou.version}</version>
83 </dependency> 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 <dependency> 92 <dependency>
85 <groupId>cn.hutool</groupId> 93 <groupId>cn.hutool</groupId>
86 <artifactId>hutool-crypto</artifactId> 94 <artifactId>hutool-crypto</artifactId>
shop/pom.xml
@@ -38,6 +38,11 @@ @@ -38,6 +38,11 @@
38 </dependency> 38 </dependency>
39 <dependency> 39 <dependency>
40 <groupId>org.springframework.boot</groupId> 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 <artifactId>spring-boot-starter-aop</artifactId> 46 <artifactId>spring-boot-starter-aop</artifactId>
42 </dependency> 47 </dependency>
43 <dependency> 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,26 +2,29 @@ package com.canrd.shop.controller;
2 2
3 import com.canrd.shop.common.constant.ServerResult; 3 import com.canrd.shop.common.constant.ServerResult;
4 import com.canrd.shop.common.jsr303.OperateGroup; 4 import com.canrd.shop.common.jsr303.OperateGroup;
  5 +import com.canrd.shop.config.StaticHtmlConfig;
  6 +import com.canrd.shop.module.dto.ProductDO;
5 import com.canrd.shop.module.vo.ProductCategoryQueryVO; 7 import com.canrd.shop.module.vo.ProductCategoryQueryVO;
6 import com.canrd.shop.module.vo.ProductQueryVO; 8 import com.canrd.shop.module.vo.ProductQueryVO;
7 import com.canrd.shop.module.vo.ProductVO; 9 import com.canrd.shop.module.vo.ProductVO;
8 import com.canrd.shop.service.ProductCategoryService; 10 import com.canrd.shop.service.ProductCategoryService;
9 import com.canrd.shop.service.ProductService; 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 import org.springframework.beans.factory.annotation.Autowired; 16 import org.springframework.beans.factory.annotation.Autowired;
  17 +import org.springframework.beans.factory.annotation.Value;
  18 +import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
17 import org.springframework.validation.annotation.Validated; 19 import org.springframework.validation.annotation.Validated;
18 import org.springframework.web.bind.annotation.*; 20 import org.springframework.web.bind.annotation.*;
19 21
20 import javax.annotation.Resource; 22 import javax.annotation.Resource;
21 import java.io.*; 23 import java.io.*;
22 import java.lang.reflect.Field; 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 * (Product)表控制层 30 * (Product)表控制层
@@ -32,6 +35,13 @@ import java.util.Map; @@ -32,6 +35,13 @@ import java.util.Map;
32 @RestController 35 @RestController
33 @RequestMapping("/shop/product") 36 @RequestMapping("/shop/product")
34 public class ProductController { 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,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 * @param productQueryVO 查询条件 125 * @param productQueryVO 查询条件
shop/src/main/java/com/canrd/shop/module/dto/ProductDO.java
@@ -48,6 +48,7 @@ public class ProductDO implements Serializable { @@ -48,6 +48,7 @@ public class ProductDO implements Serializable {
48 48
49 private Double marketprice; 49 private Double marketprice;
50 50
  51 +
51 private String metadescription; 52 private String metadescription;
52 53
53 private String metakeywords; 54 private String metakeywords;
@@ -130,6 +131,8 @@ public class ProductDO implements Serializable { @@ -130,6 +131,8 @@ public class ProductDO implements Serializable {
130 @TableField(value = "productType_id") 131 @TableField(value = "productType_id")
131 private String productTypeId; 132 private String productTypeId;
132 133
  134 + private String imageFileKey;
  135 +
133 @TableField(exist = false) 136 @TableField(exist = false)
134 private Integer similar; 137 private Integer similar;
135 @TableField(exist = false) 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,6 +2,7 @@ package com.canrd.shop.service.impl;
2 2
3 import cn.hutool.core.bean.BeanUtil; 3 import cn.hutool.core.bean.BeanUtil;
4 import cn.hutool.core.collection.CollUtil; 4 import cn.hutool.core.collection.CollUtil;
  5 +import com.alibaba.fastjson2.JSON;
5 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 6 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
6 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 7 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
7 import com.baomidou.mybatisplus.core.metadata.IPage; 8 import com.baomidou.mybatisplus.core.metadata.IPage;
@@ -180,15 +181,24 @@ public class ProductServiceImpl extends ServiceImpl&lt;ProductMapper, ProductDO&gt; im @@ -180,15 +181,24 @@ public class ProductServiceImpl extends ServiceImpl&lt;ProductMapper, ProductDO&gt; im
180 } 181 }
181 Set<String> productIds = null; 182 Set<String> productIds = null;
182 Map<String,List<TicketTypeDO>> pId2ttDOsMap = null; 183 Map<String,List<TicketTypeDO>> pId2ttDOsMap = null;
  184 + Map<String,BigDecimal> pId2ttMinPriceMap = null;
183 boolean needLikeMatch = false; 185 boolean needLikeMatch = false;
184 if(StringUtils.isNotBlank(productQueryVO.getKeyword())){ 186 if(StringUtils.isNotBlank(productQueryVO.getKeyword())){
185 List<TicketTypeDO> tickeyTypeDOList = ticketTypeService.lambdaQuery().like(TicketTypeDO::getRank, productQueryVO.getKeyword()).list(); 187 List<TicketTypeDO> tickeyTypeDOList = ticketTypeService.lambdaQuery().like(TicketTypeDO::getRank, productQueryVO.getKeyword()).list();
186 productIds = tickeyTypeDOList.stream().map(TicketTypeDO::getProductId).collect(Collectors.toSet()); 188 productIds = tickeyTypeDOList.stream().map(TicketTypeDO::getProductId).collect(Collectors.toSet());
187 needLikeMatch = true; 189 needLikeMatch = true;
188 pId2ttDOsMap = new HashMap<>(); 190 pId2ttDOsMap = new HashMap<>();
  191 + pId2ttMinPriceMap = new HashMap<>();
189 for (TicketTypeDO ticketTypeDO : tickeyTypeDOList) { 192 for (TicketTypeDO ticketTypeDO : tickeyTypeDOList) {
190 pId2ttDOsMap.computeIfAbsent(ticketTypeDO.getProductId(), k -> new ArrayList<>()); 193 pId2ttDOsMap.computeIfAbsent(ticketTypeDO.getProductId(), k -> new ArrayList<>());
191 pId2ttDOsMap.get(ticketTypeDO.getProductId()).add(ticketTypeDO); 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 Set<String> finalProductIds = productIds; 203 Set<String> finalProductIds = productIds;
194 queryWapper.and(subQueryWapper -> { 204 queryWapper.and(subQueryWapper -> {
@@ -206,7 +216,13 @@ public class ProductServiceImpl extends ServiceImpl&lt;ProductMapper, ProductDO&gt; im @@ -206,7 +216,13 @@ public class ProductServiceImpl extends ServiceImpl&lt;ProductMapper, ProductDO&gt; im
206 } 216 }
207 }); 217 });
208 List<ProductDO> similarProductList = Lists.newArrayList(); 218 List<ProductDO> similarProductList = Lists.newArrayList();
  219 + Boolean productPriceShow = switchControlService.getEnabledByName(SwitchControlConstants.PRODUCT_PRICE_SHOW);
209 for(ProductDO product:productDOS){ 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 if(accurateProductIdSet.contains(product.getId())){ 226 if(accurateProductIdSet.contains(product.getId())){
211 continue; 227 continue;
212 } 228 }
@@ -235,7 +251,12 @@ public class ProductServiceImpl extends ServiceImpl&lt;ProductMapper, ProductDO&gt; im @@ -235,7 +251,12 @@ public class ProductServiceImpl extends ServiceImpl&lt;ProductMapper, ProductDO&gt; im
235 List<ProductDO> allProductList = Lists.newArrayList(); 251 List<ProductDO> allProductList = Lists.newArrayList();
236 allProductList.addAll(accurateProducts); 252 allProductList.addAll(accurateProducts);
237 allProductList.addAll(similarProductList); 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 return pager(productQueryVO,allProductList); 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,9 +4,9 @@ import com.canrd.shop.module.dto.SwitchControl;
4 import com.canrd.shop.mapper.SwitchControlMapper; 4 import com.canrd.shop.mapper.SwitchControlMapper;
5 import com.canrd.shop.service.ISwitchControlService; 5 import com.canrd.shop.service.ISwitchControlService;
6 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 6 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
7 -import org.apache.commons.collections.CollectionUtils;  
8 import org.apache.commons.lang3.BooleanUtils; 7 import org.apache.commons.lang3.BooleanUtils;
9 import org.springframework.stereotype.Service; 8 import org.springframework.stereotype.Service;
  9 +import org.springframework.util.CollectionUtils;
10 10
11 import java.util.List; 11 import java.util.List;
12 12
shop/src/main/resources/application-prod.yml
@@ -65,3 +65,7 @@ spring: @@ -65,3 +65,7 @@ spring:
65 65
66 logging: 66 logging:
67 config: classpath:log4j2-prod.xml 67 config: classpath:log4j2-prod.xml
  68 +
  69 +static-html:
  70 + product-list:
  71 + url: /etc/nginx/canrud/products_html
68 \ No newline at end of file 72 \ No newline at end of file
shop/src/main/resources/application-test.yml
@@ -64,4 +64,9 @@ spring: @@ -64,4 +64,9 @@ spring:
64 64
65 65
66 logging: 66 logging:
67 - config: classpath:log4j2-dev.xml  
68 \ No newline at end of file 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 \ No newline at end of file 74 \ No newline at end of file
shop/src/main/resources/application.yml
@@ -3,8 +3,13 @@ server: @@ -3,8 +3,13 @@ server:
3 3
4 spring: 4 spring:
5 profiles: 5 profiles:
6 - active: prod 6 + active: test
7 mvc: 7 mvc:
8 throw-exception-if-no-handler-found: true 8 throw-exception-if-no-handler-found: true
9 resources: 9 resources:
10 add-mappings: false 10 add-mappings: false
  11 +static-html:
  12 + product-list:
  13 + urls:
  14 + test: E:\productList
  15 + prod: /etc/nginx/canrud/products_html
11 \ No newline at end of file 16 \ No newline at end of file