第18天-商城业务(商品检索服务,基于Elastic Search完成商品检索)

news2024/11/25 7:34:17

1.构建商品检索页面

在这里插入图片描述



1.1.引入依赖

 <!--  thymeleaf模板引擎   -->
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-thymeleaf</artifactId>
 </dependency>
 <!--   热更新     -->
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-devtools</artifactId>
     <optional>true</optional>
 </dependency>


1.2.模板页面

  • index.html 拷贝到 templates 目录
  • 修改 index.htmllist.html


1.3.静态资源

1)在nginx\html\static\ 文件下创建 search 文件夹,并将所有静态资源文件上传到 search\ 文件夹

在这里插入图片描述

2)修改list.html模板,将所有静态资源链接URL加上前置路径 /static/search/



1.4.域名访问配置

本地映射

192.168.139.10 search.gmall.com

Nginx配置 gmall.conf

server {
	listen 80;
	server_name *.gmall.com gmall.com;
	
	location /static/ {
		root /usr/share/nginx/html;
	}
	
	location / {
		proxy_pass http://gmall;
		proxy_set_header Host $host;
	}
}

网关路由配置

- id: gmall_host_route
  uri: lb://gmall-product
  predicates:
  - Host=gmall.com

- id: gmall_search_route
  uri: lb://gmall-search
  predicates:
  - Host=search.gmall.com


1.5.页面跳转后台接口

SearchController

package com.atguigu.gmall.search.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
/**
 * 商品检索 {@link SearchController}
 *
 * @author zhangwen
 * @email: 1466787185@qq.com
 */
@Controller
public class SearchController {

	/**
	 * 商品检索页面
	 * @return
	 */
	@GetMapping("/list.html")
	public String listPage() {
		return "list";
	}
}


2.商品检索业务分析



2.1.检索业务分析


2.1.1.商品检索三个入口

输入检索关键字展示检索页

在这里插入图片描述

选择分类进入商品检索

在这里插入图片描述

选择筛选条件进入(复杂)

根据检索关键字进入检索页面

在这里插入图片描述

点击三级分类进入检索页面

在这里插入图片描述

2.1.2.检索条件分析

  • 全文检索:skuTitle(keyword)
  • 排序:saleCount(销量)、hotScore(热度评分/综合排序)、skuPrice(价格)
  • 过滤:hasStock(仅显示有货)、skuPrice区间、brandId、catalogId、attrs
  • 聚合:attrs

完整查询参数

search.gmall.com/list.html?keyword=华为&sort=saleCount_desc
&hasStock=1&skuPrice=1000_5000&brandId=1&catalog3Id=1&attrs=1
_3G:4G:5G&attrs=2_骁龙845&attrs=3_高清屏


2.2.检索条件和检索结果封装


2.2.1.SearchParamVO

package com.atguigu.gmall.search.vo;

import lombok.Data;
import java.util.List;

/**
 * 商品检索条件 {@link SearchParamVO}
 *  ?keyword=华为&sort=saleCount_desc&hasStock=1&skuPrice=1000_5000&brandId=1&catalog3Id=225
 *  &attrs=1_3G:4G:5G&attrs=2_骁龙845&attrs=3_高清屏
 * @author zhangwen
 * @email: 1466787185@qq.com
 */
@Data
public class SearchParamVO {
    {
        // 页码默认值
        pageNum = 1;
    }
    /**
     * 检索输入框传递过来的检索关键字
     */
    private String keyword;
    /**
     * 三级分类id
     */
    private Long catalog3Id;
    /**
     * 排序条件,三选一
     *  销量排序:sort=saleCount_desc/asc
     *  综合排序:sort=hasStock_desc/asc
     *  价格排序:sort=skuPrice_desc/asc
     */
    private String sort;
    /**
     * 过滤条件
     * hasStock(仅显示有货)、skuPrice区间(价格区间)、brandId(品牌id)、catalogId(分类id)、attrs(商品属性)
     *  hasStock=0/1
     *  skuPrice=100_500/_500/100_
     *  brandId=1&brandId=2
     *  attrs=1_3G:4G:5G&attrs=2_骁龙845&attrs=3_高清屏
     */
    private Integer hasStock;
    private String skuPrice;
    private List<Long> brandId;
    private List<String> attrs;

    /**
     * 当前页码
     */
    private Integer pageNum;

    /**
     * 所有查询条件
     */
    private String queryString;
}

2.2.2.SearchResponseVO

package com.atguigua.gmall.search.vo;

import com.atguigua.common.to.es.SkuEsModel;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;

/**
 * 商品检索结果 {@link SearchResponseVO}
 *
 * @author zhangwen
 * @email: 1466787185@qq.com
 */
@Data
public class SearchResponseVO {
    {
        navs = new ArrayList<>();
        attrIds = new ArrayList<>();
    }
    /**
     * 检索到的所有商品信息
     */
    private List<SkuEsModel> products;

    /**
     * 当前页面
     */
    private Integer pageNum;
    /**
     * 总记录数
     */
    private Long totalCount;
    /**
     * 总页码
     */
    private Integer totalPage;
    /**
     * 导航页
     */
    private List<Integer> pageNavs;

    /**
     * 检索到的结果所涉及的所有品牌
     */
    private List<BrandVO> brands;
    /**
     * 检索结果涉及的所有分类
     */
    private List<CatalogVO> catalogs;
    /**
     * 检索结果涉及的商品属性
     */
    private List<AttrVO> attrs;

    /**
     * 面包屑导航
     */
    private List<NavVO> navs;
    private List<Long> attrIds;

    @Data
    public static class NavVO {
        private String navName;
        private String navValue;
        private String link;
    }

    @Data
    public static class BrandVO {
        private Long brandId;
        private String brandName;
        private String brandImg;
    }

    @Data
    public static class CatalogVO {
        private Long catalogId;
        private String catalogName;
    }

    @Data
    public static class AttrVO {
        private Long attrId;
        private String attrName;
        private List<String> attrValues;
    }
}


2.3.ElasticSearch数据迁移


2.3.1.创建新的索引及映射

PUT gmall_product
{
	"mappings": {
		"properties": {
			"attrs": {
				"type": "nested",
				"properties": {
					"attrId": {
						"type": "long"
					},
					"attrName": {
					"type": "keyword",
					"index": false,
					"doc_values": true
					},
					"attrValue": {
					"type": "keyword"
					}
				}
			},
			"brandId": {
				"type": "long"
			},
			"brandImg": {
				"type": "keyword",
				"index": false,
				"doc_values": true
			},
			"brandName": {
				"type": "keyword",
				"index": false,
				"doc_values": true
			},
			"catalogId": {
				"type": "long"
			},
			"catalogName": {
				"type": "keyword",
				"index": false,
				"doc_values": true
			},
			"hasScore": {
				"type": "long"
			},
			"hasStock": {
				"type": "boolean"
			},
			"hotScore": {
				"type": "long"
			},
			"saleCount": {
				"type": "long"
			},
			"skuId": {
				"type": "long"
			},
			"skuImg": {
				"type": "keyword",
				"index": false,
				"doc_values": true
			},
			"skuPrice": {
				"type": "keyword"
			},
			"skuTitle": {
				"type": "text",
				"analyzer": "ik_smart"
			},
			"spuId": {
				"type": "keyword"
			}
		}
	}
}

2.3.2.迁移数据

POST _reindex
{
	"source": {
		"index": "product"
	},
	"dest": {
		"index": "gmall_product"
	}
}

2.3.3.修改检索服务索引名

package com.atguigu.gmall.search.constant;
/**
 * ES常量类 {@link EsConstant}
 *
 * @author zhangwen
 * @email: 1466787185@qq.com
 */
public class EsConstant {
	/**
	 * sku数据在es中的索引
	 */
	public static final String PRODUCT_INDEX = "gmall_product";
}


2.4.分析ES检索DSL

#模糊匹配 must
#过滤 filter
#排序 sort
#分页 from size
#高亮 highlight
#聚合分析 aggs
#如果是嵌入式属性,查询、聚合分析都需要用嵌入式

GET gmall_product/_search
{
	"query": {
		"bool": {
			"must": [
				{
					"match": {
						"skuTitle": "华为"
					}
				}
			],
			"filter": [
				{
					"term": {
						"catalogId": "225"
					}
				},
				{
					"terms": {
						"brandId": [
							"4"
						]
					}
				},
				{
					"nested": {
						"path": "attrs",
						"query": {
							"bool": {
								"must": [
									{
										"term": {
											"attrs.attrId": {
												"value": "8"
											}
										}
									},
									{
										"terms": {
											"attrs.attrValue": [
												"LIO-AN00",
												"158.1"
											]
										}
									}
								]
							}
						}
					}
				},
				{
					"term": {
						"hasStock": true
					}
				},
				{
					"range": {
						"skuPrice": {
							"gte": 0,
							"lte": 6000
						}
					}
				}
			]
		}
	},
	"sort": [
		{
			"skuPrice": {
				"order": "desc"
			}
		}
	],
	"from": 0,
	"size": 5,
	"highlight": {
		"fields": {
			"skuTitle": {}
		},
		"pre_tags": "<b style='color:red'>",
		"post_tags": "</b>"
	},
	"aggs": {
		"brand_agg": {
			"terms": {
				"field": "brandId",
				"size": 10
			},
			"aggs": {
				"brand_name_agg": {
					"terms": {
						"field": "brandName",
						"size": 10
					}
				},
				"brand_img_agg": {
					"terms": {
						"field": "brandImg",
						"size": 10
					}
				}
			}
		},
		"catalog_agg": {
			"terms": {
				"field": "catalogId",
				"size": 10
			},
			"aggs": {
				"catalog_name_agg": {
					"terms": {
						"field": "catalogName",
						"size": 10
					}
				}
			}
		},
		"attr_agg": {
			"nested": {
				"path": "attrs"
			},
			"aggs": {
				"attr_id_agg": {
					"terms": {
						"field": "attrs.attrId",
						"size": 10
					},
					"aggs": {
						"attr_name_agg": {
							"terms": {
								"field": "attrs.attrName",
								"size": 10
							}
						},
						"attr_value_agg": {
							"terms": {
								"field": "attrs.attrValue",
								"size": 10
							}
						}
					}
				}
			}
		}
	}
}


3.商品检索业务实现



3.1.检索接口实现


3.1.1.检索接口

/**
 * 商品检索
 * @param searchParamVO 检索的所有参数
 * @return
 */
@Override
public SearchResponseVO search(SearchParamVO searchParamVO) {
	SearchResponseVO searchResponseVO = null;
	
	//1.准备检索请求
	SearchRequest searchRequest = buildSearchRequest(searchParamVO);
	
	try {
		//2.执行检索请求
		SearchResponse searchResponse = restHighLevelClient.search(searchRequest,ElasticSearchConfig.COMMON_OPTIONS);
		//3.分析检索结果,封装成SearchResponseVO
		searchResponseVO = buildSearchResponse(searchResponse, searchParamVO);
	} catch (IOException e) {
		e.printStackTrace();
	}
	
	return searchResponseVO;
}

3.1.1.构建检索查询SearchRequest

/**
 * 准备检索请求
 * 模糊匹配 must
 * 过滤 filter
 * 排序 sort
 * 分页 from size
 * 高亮 highlight
 * 聚合分析 aggs
 * @return
 */
private SearchRequest buildSearchRequest(SearchParamVO param) {
    // 动态构建检索的DSL语句
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

    // 查询:模糊匹配、过滤(品牌、分类、属性、价格区间、库存)
    BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

    // 按照商品名称模糊查询
    if (!StringUtils.isEmpty(param.getKeyword())) {
        boolQuery.must(QueryBuilders.matchQuery("skuTitle", param.getKeyword()));
    }
    // 按照三级分类id查询
    if (param.getCatalog3Id() != null) {
        boolQuery.filter(QueryBuilders.termQuery("catalogId", param.getCatalog3Id()));
    }
    // 按照品牌id查询
    if (param.getBrandId() != null && param.getBrandId().size() > 0) {
        boolQuery.filter(QueryBuilders.termsQuery("brandId", param.getBrandId()));
    }
    // 按照所有指定的属性进行查询
    // attr=1_3G:4G:5G&attr=2_高通骁龙845
    if (param.getAttrs() != null && param.getAttrs().size() > 0) {
        for (String attr : param.getAttrs()) {
            String[] s = attr.split("_");
            String attrId = s[0];
            String[] attrValues = s[1].split(":");

            BoolQueryBuilder query = QueryBuilders.boolQuery();
            query.must(QueryBuilders.termQuery("attrs.attrId", attrId));
            query.must(QueryBuilders.termsQuery("attrs.attrValue", attrValues));
            // 每一个属性都必须生成一个 NestedQuery
            NestedQueryBuilder nestedQuery = QueryBuilders.nestedQuery("attrs", query, ScoreMode.None);
            boolQuery.filter(nestedQuery);
        }
    }
    // 按照是否有库存进行查询
    if (param.getHasStock() != null) {
        boolQuery.filter(QueryBuilders.termQuery("hasStock", param.getHasStock() == 1));
    }
    // 按照价格区间进行查询:1_100/_100/100_
    if (!StringUtils.isEmpty(param.getSkuPrice())) {
        RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("skuPrice");
        String[] price = param.getSkuPrice().split("_", 2);
        if (StringUtils.isEmpty(price[0])) {
            rangeQuery.lte(price[1]);
        } else if (StringUtils.isEmpty(price[1])) {
            rangeQuery.gte(price[0]);
        } else {
            rangeQuery.gte(price[0]).lte(price[1]);
        }
        boolQuery.filter(rangeQuery);
    }

    searchSourceBuilder.query(boolQuery);

    // 排序
    // sort=saleCount_desc
    if (!StringUtils.isEmpty(param.getSort())) {
        String[] s = param.getSort().split("_");
        SortOrder sortOrder = s[1].equalsIgnoreCase("asc") ? SortOrder.ASC : SortOrder.DESC;
        if ("price".equals(s[0])) {
            searchSourceBuilder.sort("skuPrice", sortOrder);
        } else {
            searchSourceBuilder.sort(s[0], sortOrder);
        }
    }

    // 分页
    // from = (pageNum - 1) * pageSize
    searchSourceBuilder.from((param.getPageNum() - 1) * EsConstant.PRODUCT_PAGE_SIZE);
    searchSourceBuilder.size(EsConstant.PRODUCT_PAGE_SIZE);

    // 高亮
    if (!StringUtils.isEmpty(param.getKeyword())) {
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("skuTitle");
        highlightBuilder.preTags("<b style='color:red'>");
        highlightBuilder.postTags("</b>");
        searchSourceBuilder.highlighter(highlightBuilder);
    }

    // 聚合分析
    // 品牌聚合
    TermsAggregationBuilder brand_agg = AggregationBuilders.terms("brand_agg").field("brandId").size(50);
    // 品牌子聚合
    brand_agg.subAggregation(AggregationBuilders.terms("brand_name_agg").field("brandName").size(1));
    brand_agg.subAggregation(AggregationBuilders.terms("brand_img_agg").field("brandImg").size(1));
    searchSourceBuilder.aggregation(brand_agg);
    // 分类聚合
    TermsAggregationBuilder catalog_agg = AggregationBuilders.terms("catalog_agg").field("catalogId").size(20);
    catalog_agg.subAggregation(AggregationBuilders.terms("catalog_name_agg").field("catalogName").size(1));
    searchSourceBuilder.aggregation(catalog_agg);
    // 属性聚合
    NestedAggregationBuilder attr_agg = AggregationBuilders.nested("attr_agg", "attrs");
    TermsAggregationBuilder attr_id_agg = AggregationBuilders.terms("attr_id_agg").field("attrs.attrId").size(1);
    attr_id_agg.subAggregation(AggregationBuilders.terms("attr_name_agg").field("attrs.attrName").size(1));
    attr_id_agg.subAggregation(AggregationBuilders.terms("attr_value_agg").field("attrs.attrValue").size(50));
    attr_agg.subAggregation(attr_id_agg);
    searchSourceBuilder.aggregation(attr_agg);

    System.out.println("构建DSL:" + searchSourceBuilder.toString());

    SearchRequest searchRequest = new SearchRequest(new String[]{EsConstant.PRODUCT_INDEX}, searchSourceBuilder);
    return searchRequest;
}

3.1.2.分析检索结果SearchResponse

/**
 * 返回检索结果数据
 * @param searchResponse
 * @return
 */
private SearchResponseVO buildSearchResponse(SearchResponse searchResponse, SearchParamVO param) {
    SearchResponseVO searchResponseVO = new SearchResponseVO();

    // 返回所有查询到的商品
    SearchHits hits = searchResponse.getHits();
    List<SkuEsModel> skuEsModels = new ArrayList<>();
    if (hits.getHits() != null && hits.getHits().length > 0) {
        for (SearchHit hit : hits.getHits()) {
            String sourceAsString = hit.getSourceAsString();
            SkuEsModel skuEsModel = JsonUtils.jsonToPojo(sourceAsString, SkuEsModel.class);
            // 设置关键字高亮
            if (!StringUtils.isEmpty(param.getKeyword())) {
                String skuTitle = hit.getHighlightFields().get("skuTitle").getFragments()[0].string();
                skuEsModel.setSkuTitle(skuTitle);
            }
            skuEsModels.add(skuEsModel);
        }
    }
    searchResponseVO.setProducts(skuEsModels);

    // 聚合分析:分类信息、品牌信息、属性信息
    // 分类信息
    List<SearchResponseVO.CatalogVO> catalogVOS = new ArrayList<>();
    ParsedLongTerms catalog_agg = searchResponse.getAggregations().get("catalog_agg");
    for (Terms.Bucket bucket : catalog_agg.getBuckets()) {
        SearchResponseVO.CatalogVO catalogVO = new SearchResponseVO.CatalogVO();
        // 分类ID
        catalogVO.setCatalogId(Long.parseLong(bucket.getKeyAsString()));
        // 分类名
        ParsedStringTerms catalog_name_agg = bucket.getAggregations().get("catalog_name_agg");
        String catalogName = catalog_name_agg.getBuckets().get(0).getKeyAsString();
        catalogVO.setCatalogName(catalogName);

        catalogVOS.add(catalogVO);
    }
    searchResponseVO.setCatalogs(catalogVOS);
    // 品牌信息
    List<SearchResponseVO.BrandVO> brandVOS = new ArrayList<>();
    ParsedLongTerms brand_agg = searchResponse.getAggregations().get("brand_agg");
    for (Terms.Bucket bucket : brand_agg.getBuckets()) {
        SearchResponseVO.BrandVO brandVO = new SearchResponseVO.BrandVO();
        // 品牌ID
        brandVO.setBrandId(bucket.getKeyAsNumber().longValue());
        // 品牌名称
        ParsedStringTerms brand_name_agg = bucket.getAggregations().get("brand_name_agg");
        String brandName = brand_name_agg.getBuckets().get(0).getKeyAsString();
        brandVO.setBrandName(brandName);
        // 品牌图片
        ParsedStringTerms brand_img_agg = bucket.getAggregations().get("brand_img_agg");
        String brandImg = brand_img_agg.getBuckets().get(0).getKeyAsString();
        brandVO.setBrandImg(brandImg);

        brandVOS.add(brandVO);
    }
    searchResponseVO.setBrands(brandVOS);
    // 属性
    List<SearchResponseVO.AttrVO> attrVOS = new ArrayList<>();
    ParsedNested attr_agg = searchResponse.getAggregations().get("attr_agg");
    ParsedLongTerms attr_id_agg = attr_agg.getAggregations().get("attr_id_agg");
    for (Terms.Bucket bucket : attr_id_agg.getBuckets()) {
        SearchResponseVO.AttrVO attrVO = new SearchResponseVO.AttrVO();
        // 属性ID
        long attrId = bucket.getKeyAsNumber().longValue();
        attrVO.setAttrId(attrId);
        // 属性名
        String attrName = ((ParsedStringTerms) bucket.getAggregations().get("attr_name_agg"))
                .getBuckets().get(0).getKeyAsString();
        attrVO.setAttrName(attrName);
        // 属性所有值
        List<String> attrValues = ((ParsedStringTerms) bucket.getAggregations().get("attr_value_agg"))
                .getBuckets().stream().map(item -> {
                    String keyAsString = item.getKeyAsString();
                    return keyAsString;
                }).collect(Collectors.toList());
        attrVO.setAttrValues(attrValues);
        attrVOS.add(attrVO);
    }
    searchResponseVO.setAttrs(attrVOS);

    // 分页信息
    // 当前页码
    searchResponseVO.setPageNum(param.getPageNum());
    // 总记录数
    long totalCount = hits.getTotalHits().value;
    searchResponseVO.setTotalCount(totalCount);
    // 总页数
    int totalPage = (int)totalCount % EsConstant.PRODUCT_PAGE_SIZE == 0
            ? (int)totalCount / EsConstant.PRODUCT_PAGE_SIZE
            : (int)totalCount / EsConstant.PRODUCT_PAGE_SIZE + 1;
    searchResponseVO.setTotalPage(totalPage);
    // 页码导航数
    List<Integer> pageNavs = new ArrayList<>();
    for (int i = 1; i <= totalPage; i++) {
        pageNavs.add(i);
    }
    searchResponseVO.setPageNavs(pageNavs);

    // 构建面包屑导航
    // 面包屑-属性
    if (param.getAttrs() != null && param.getAttrs().size() > 0) {
        List<SearchResponseVO.NavVO> navs = param.getAttrs().stream().map(attr -> {
            SearchResponseVO.NavVO navVO = new SearchResponseVO.NavVO();
            String[] s = attr.split("_");
            searchResponseVO.getAttrIds().add(Long.parseLong(s[0]));
            navVO.setNavValue(s[1]);

            // 远程调用耗时,远程查询接口结果加入缓存
            R r = productFeignService.getAttrInfo(Long.parseLong(s[0]));
            if (r.getCode() == 0) {
                AttrResponseVO data = r.getData("attr", new TypeReference<AttrResponseVO>() {});
                navVO.setNavName(data.getAttrName());
            } else {
                log.error("调用远程服务 gmall-product 查询属性信息失败");
                navVO.setNavName("");
            }

            // 取消面包屑以后,需要将请求url里面的当前属性值置空
            String replace = replaceQueryString(param.getQueryString(), "attrs", attr);
            navVO.setLink("http://search.gmall.com/list.html?" + replace);

            return navVO;
        }).collect(Collectors.toList());
        searchResponseVO.setNavs(navs);
    }

    // 面包屑-品牌
    if (param.getBrandId() != null && param.getBrandId().size() > 0) {
        List<SearchResponseVO.NavVO> navs = searchResponseVO.getNavs();
        SearchResponseVO.NavVO navVO = new SearchResponseVO.NavVO();
        navVO.setNavName("品牌");

        // 远程调用耗时,远程查询接口结果加入缓存
        R r = productFeignService.getBrandInfos(param.getBrandId());
        if (r.getCode() == 0) {
            List<BrandResponseVO> brands = r.getData("brands", new TypeReference<List<BrandResponseVO>>() {});
            String replace = null;
            StringBuffer buffer = new StringBuffer();
            for (int i = 0; i < brands.size(); i++) {
                BrandResponseVO vo = brands.get(i);
                if (i == 0) {
                    buffer.append(vo.getName());
                } else {
                    buffer.append("、" + vo.getName());
                }
                replace = replaceQueryString(param.getQueryString(), "brandId", vo.getBrandId()+"");
            }
            navVO.setNavValue(buffer.toString());
            navVO.setLink("http://search.gmall.com/list.html?" + replace);
        } else {
            log.error("调用远程服务 gmall-product 查询品牌信息失败");
        }
        navs.add(navVO);
    }

    // TODO:面包屑-分类

    return searchResponseVO;
}


3.2.检索模板页面实现


3.2.1.模板页面数据渲染

  • 商品列表渲染
  • 商品筛选条件渲染
  • 商品列表分页数据渲染

3.2.2.商品检索页面功能

  • 商品筛选条件过滤

  • 关键字检索

  • 商品价格区间过滤

  • 商品排序

    1、综合排序
    2、按销量排序
    3、 按价格排序

  • 分页跳转处理

  • 面包屑导航

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/401744.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Revit开洞问题:结构专业开洞口剖面显示及一键开洞

一、Revit中关于结构专业开洞口剖面显示问题 Revit作业的时候&#xff0c;我们不仅只为了一个最后的三维立体模型,我们需要的是一个符合国家以及本院制图标准的一个出图样式,这时候就会出现各种各样的显示问题&#xff0c;本期就一个结构专业开洞显示问题&#xff0c;跟大家一起…

【CSS】CSS 背景设置 ③ ( 背景位置-长度值设置 | 背景位置-长度值方位值同时设置 )

文章目录一、背景位置-长度值设置二、背景位置-长度值方位值同时设置三、完整代码示例一、背景位置-长度值设置 长度值设置 效果展示 : 设置背景位置为具体值 10px 50px : 粉色区域是盒子的区域 , 图片背景位于盒子位置 x 轴方向 10 像素 , y 轴方向 50 像素 ; 在水平方向上 ,…

数组和指针面试题的补充(细的抠jio)

生命是一条艰险的峡谷&#xff0c;只有勇敢的人才能通过。 ——米歇潘 说明&#xff1a;用的vs都是x86的环境&#xff0c;也就是32位平台。 建议&#xff1a;对于难题来说&#xff0c;一定要配合画图来解决问题。 第一题&#xff1a; #include<stdio.h> int…

脉脉疯传!2023年程序员生存指南;多款prompt效率加倍工具;提示工程师最全秘籍;AI裁员正在发生 | ShowMeAI日报

&#x1f440;日报合辑 | &#x1f3a1;生产力工具与行业应用大全 | &#x1f9e1; 点赞关注评论拜托啦&#xff01; &#x1f916; 2023年程序员生存指南 不要在互联网公司做低代码项目 保证一定强度的刷题&#xff0c;不要用go刷题&#xff0c;用cpp或者java&#xff0c;性价…

DPDK系列之八虚拟化virtio

一、virtio的介绍 在一篇文章中对virtio进行了简单的说明。在早期的虚拟化的过程中&#xff0c;无论是KVM还是Vmware亦或是Xen&#xff0c;每个平台想当然的是自己搞自己的IO接口。这就和现在国内的互联各个平台都是大而全一样&#xff0c;怎么可能我用你的支付接口呢&#xf…

Python高频面试题——装饰器(带大家理解装饰器的本质)

装饰器概念装饰器本质上是一个python函数&#xff0c;它可以让其他函数在不需要做任何代码变动的前提下增加额外功能&#xff0c;装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景&#xff0c;比如&#xff1a;插入日志、性能测试、事务处理、缓存、权限验证等场景…

【C++】通过stack、queue、deque理解适配器模式

破镜不能重圆&#xff0c;枯木可以逢春。 文章目录一、stack1.stack的介绍2.stack相关OJ题&#xff08;巧妙利用stack数据结构的特征&#xff09;3.stack的模拟实现二、queue1.queue的介绍2.queue的相关OJ题&#xff08;巧妙利用queue数据结构的特征&#xff09;3.queue的模拟实…

多图片怎么转换成PDF?这招教你轻松转换

多图片怎么转换成PDF&#xff1f;我们经常会传输图片文件给同事或者朋友&#xff0c;但是多张图片的传输比较麻烦&#xff0c;有的时候传输比较慢&#xff0c;而且也不便于查看&#xff0c;所以我们就可以将需要传输的多张图片转换成一个PDF文件&#xff0c;这样查看文件时就可…

mxnet版本与numpy,requests等都不兼容问题

简介 跟着李沐学AI时遇到的mxnet环境问题。 问题 使用pip install mxnet时会重新安装相匹配的numpy和requests&#xff0c;而这新安装的这两个版本不满足d2l所需的版本。 然后报错&#xff1a; ERROR: pips dependency resolver does not currently take into account all …

常用docker命令

帮助启动类命令 镜像命令 docker images options -a :列出本地所有的镜像&#xff08;含历史映像层) -q :只显示镜像ID。docker search 某个XXX镜像名字 网站 Docker命令 docker search [OPTIONS] 镜像名字 案例 docker search redis option…

【AI绘图学习笔记】奇异值分解(SVD)、主成分分析(PCA)

这节的内容需要一些线性代数基础知识&#xff0c;如果你没听懂本文在讲什么&#xff0c;强烈建议你学习【官方双语/合集】线性代数的本质 - 系列合集 文章目录奇异值分解线性变换特征值和特征向量的几何意义什么是奇异值分解&#xff1f;公式推导SVD推广到任意大小矩阵如何求SV…

【C++的OpenCV】第十二课-OpenCV图像常用操作(九):找到图像的边界(轮廓)findContours()和drawContours()

&#x1f389;&#x1f389;&#x1f389;欢迎各位来到小白piao的学习空间&#xff01;\color{red}{欢迎各位来到小白piao的学习空间&#xff01;}欢迎各位来到小白piao的学习空间&#xff01;&#x1f389;&#x1f389;&#x1f389; &#x1f496;&#x1f496;&#x1f496…

PMP项目管理项目整合管理

目录1 项目整合管理概述2 制定项目章程3 制定项目管理计划4 指导与管理项目工作5 管理项目知识6 监控项目工作7 实施整体变更控制8 结束项目或阶段1 项目整合管理概述 项目整合管理包括对隶属于项目管理过程组的各种过程和项目管理活动进行识别、定义、组合、统一和协调的各个…

Linux服务器还有漏洞?建议使用 OpenVAS 日常检查!

几乎每天都会有新的系统漏洞产生&#xff0c;系统管理员经常忙于管理服务器&#xff0c;有时候会忽略一些很明显的安全问题。扫描 Linux 服务器以查找安全问题并不是很简单的事情&#xff0c;所以有时候需要借助于一些专门的工具。 OpenVAS 就是这样一种开源工具&#xff0c;它…

Easy Deep Learning——池化层

池化是什么&#xff1f;它有什么作用&#xff1f; 还是草地的场景&#xff0c;把草地分成一块块的网格&#xff0c;数量还是太多了&#xff0c;如何继续简化输入数据呢? 这时候可以只取一块网格中所有的小草的大小形状的平均值或者最大值作为一个输入数据&#xff0c;这样就大…

Tik Tok新手秘籍,做好五点可轻松起号

新手做TikTok需要有一个具体的规划布局&#xff0c;如果没有深思熟虑就上手开始的话&#xff0c;很有可能会导致功亏一篑&#xff0c;甚至是浪费时间。因此&#xff0c;想要做好 TikTok&#xff0c;就必须从最基本的运营细节开始&#xff0c;一步一步来&#xff0c;下面为大家分…

近红外荧光染料修饰氨基IR 825 NH2,IR 825-Amine,IR-825 NH2

IR 825 NH2&#xff0c;IR 825-NH2&#xff0c;IR825 Amine&#xff0c;IR825-Amine&#xff0c;新吲哚菁绿-氨基&#xff0c;荧光染料修饰氨基产品规格&#xff1a;1.CAS号&#xff1a;N/A2.包装规格&#xff1a;10mg&#xff0c;25mg&#xff0c;50mg&#xff0c;包装灵活&am…

数据结构(3)— 线性表之顺序存储详解介绍(含代码)

&#xff08;1&#xff09;博客代码在数据结构代码---GitHub仓库&#xff1b;线性表介绍线性表的基础概念&#xff08;1&#xff09;甲骨文表示&#xff1a;线性表是零个或多个数据元素的有限序列。&#xff08;2&#xff09;线性表&#xff0c;顾名思义&#xff0c;就是说这个…

git | git 2023 详细版

文章目录一、Git命令1.2 设计用户签名1.3 初始化本地库1.4 查看本地库状态1.5 添加至暂存区1.6 从暂存区删除1.7 将暂存区的文件提交到本地库1.8 查看版本信息二、Git分支2.1 查看分支2.2 创建分支2.3 切换分支2.4 合并分支三、GitHub3.1 代码克隆clone3.2 给库取别名3.3 推送本…

【服务器管理】Wordpress服务器内存占用太高(优化方案详解)

简述 在刚刚配置完服务器之后&#xff0c;想着试一试wordpress这个功能&#xff0c;结果打开服务器后台&#xff0c;发现本来就不多的内存被占用了一大半。 我真的服了&#xff0c;我还啥都没干呢&#xff0c;就这么多的内存占用&#xff0c;那之后我开始弄了还得了。因此有必…