乐优商城项目---day12-基本搜索

5.6.聚合

5.6.1.聚合为桶

桶就是分组,比如这里我们按照品牌brand进行分组:这个聚合查询还是比较复杂的,相对于前面的排序和分页查询,有很多子接。

聚合查询:

 @Test
    public void testJuhe(){
        NativeSearchQueryBuilder queryBuilder=new NativeSearchQueryBuilder();
        //不查询结果集合
        queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{""},null));
        //设置查询类型为词条查询,查询名称为,brands,查询字段为brand
        queryBuilder.addAggregation(AggregationBuilders.terms("brands").field("brand"));
        //执行查询,执行查询并转换类型为AggregatedPage
        AggregatedPage<Item> page= (AggregatedPage<Item>) this.repository.search(queryBuilder.build());
        //获取查询结果中brads的结果集合并转换为字符串的词条
        StringTerms brand = (StringTerms) page.getAggregation("brands");
        //获取桶,之后遍历得到相应的key和value
        List<StringTerms.Bucket> buckets = brand.getBuckets();
        for (StringTerms.Bucket bucket : buckets) {
            System.out.println(bucket.getKeyAsString());
            System.out.println(bucket.getDocCount());
        }
    }

完成测试:

嵌套子查询加求平均值

  @Test
    public void avgAndTest(){
        NativeSearchQueryBuilder queryBuilder=new NativeSearchQueryBuilder();
        queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{""},null));
        queryBuilder.addAggregation(AggregationBuilders.terms("brands").field("brand").subAggregation(AggregationBuilders.avg("price_avg").field("price")));
       AggregatedPage<Item> aggregatedPage= (AggregatedPage<Item>) this.repository.search(queryBuilder.build());
        StringTerms brands = (StringTerms) aggregatedPage.getAggregation("brands");
        List<StringTerms.Bucket> buckets = brands.getBuckets();
        for (StringTerms.Bucket bucket : buckets) {
            System.out.println(bucket.getKeyAsString());
            System.out.println(bucket.getDocCount());
            InternalAvg price_avg = (InternalAvg) bucket.getAggregations().asMap().get("price_avg");
            System.out.println("平均价格为:"+price_avg.getValue());
        }
    }

完成测试:

 

-------------------------------------------------------------------------------------------------------

1.索引库数据导入

昨天我们学习了Elasticsearch的基本应用。今天就学以致用,搭建搜索微服务,实现搜索功能。

1.1.创建搜索服务

创建模块,之后添加依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>leyou</artifactId>
        <groupId>com.leyou.parent</groupId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.leyou.search</groupId>
    <artifactId>leyou-search</artifactId>
    <dependencies>
        <!-- web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- elasticsearch -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </dependency>
        <!-- eureka -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!-- feign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>com.leyou.item</groupId>
            <artifactId>leyou-item-interface</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.leyou.common</groupId>
            <artifactId>leyou-common</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

    </dependencies>

</project>

添加对应的配置文件

server:
  port: 8083
spring:
  application:
    name: search-service
  data:
    elasticsearch:
      cluster-name: elasticsearch
      cluster-nodes: 192.168.1.99:9300
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
  instance:
    lease-renewal-interval-in-seconds: 5 # 每隔5秒发送一次心跳
    lease-expiration-duration-in-seconds: 10 # 10秒不发送就过期

添加引导类

@SpringBootApplication
@EnableFeignClients
@EnableDiscoveryClient
public class LeyouSearchAppliction {

    public static void main(String[] args) {
        SpringApplication.run(LeyouSearchAppliction.class);

    }
}

1.2.索引库数据格式分析

接下来,我们需要商品数据导入索引库,便于用户搜索。

那么问题来了,我们有SPU和SKU,到底如何保存到索引库?

1.2.1.以结果为导向

因为要用搜索引擎去搜索

这些过滤条件也都需要存储到索引库中,包括:

商品分类、品牌、可用来搜索的规格参数等

综上所述,我们需要的数据格式有:

spuId、SkuId、商品分类id、品牌id、图片、价格、商品的创建时间、sku信息集、可搜索的规格参数

1.2.3.最终的数据结构

我们创建一个类,封装要保存到索引库的数据,并设置映射属性:

@Document(indexName = "goods", type = "docs", shards = 1, replicas = 0)
public class Goods {
    @Id
    private Long id; // spuId
    @Field(type = FieldType.Text, analyzer = "ik_max_word")
    private String all; // 所有需要被搜索的信息,包含标题,分类,甚至品牌
    @Field(type = FieldType.Keyword, index = false)
    private String subTitle;// 卖点
    private Long brandId;// 品牌id
    private Long cid1;// 1级分类id
    private Long cid2;// 2级分类id
    private Long cid3;// 3级分类id
    private Date createTime;// 创建时间
    private List<Long> price;// 价格
    @Field(type = FieldType.Keyword, index = false)
    private String skus;// List<sku>信息的json结构
    private Map<String, Object> specs;// 可搜索的规格参数,key是参数名,值是参数值
}

一些特殊字段解释:

  • all:用来进行全文检索的字段,里面包含标题、商品分类信息

  • price:价格数组,是所有sku的价格集合。方便根据价格进行筛选过滤

  • skus:用于页面展示的sku信息,不索引,不搜索。包含skuId、image、price、title字段

  • specs:所有规格参数的集合。key是参数名,值是参数值。

    例如:我们在specs中存储 内存:4G,6G,颜色为红色,转为json就是:

    {
        "specs":{
            "内存":[4G,6G],
            "颜色":"红色"
        }
    }

    当存储到索引库时,elasticsearch会处理为两个字段:

    • specs.内存:[4G,6G]

    • specs.颜色:红色

    另外, 对于字符串类型,还会额外存储一个字段,这个字段不会分词,用作聚合。

    • specs.颜色.keyword:红色

接下来就是创建微服务接口:创建微服务接口的时候要创建远程的feign客户端进行远程调用,首先创建API接口

BrandApi

@RequestMapping("brand")
public interface BrandApi {
    /**
     * 根据bid查询品牌名称
     */
    @GetMapping("{id}")
    public String queryNameById(@PathVariable("id")Long id);

}

CategoryApi

@RequestMapping("category")
public interface CategoryApi {

    /**
     * 根据ids查询分类名称
     */
    @GetMapping
    public List<String> queryNameByids(@RequestParam("ids")List<Long> ids);

}

GoodsApi

@RequestMapping("/goods")
public interface GoodsApi {

    @RequestMapping("spu/page")
    public PageResult<SpuBo> queryGoods(
            @RequestParam(value = "key",required = false) String key,
            @RequestParam(value = "saleable",required = false) String saleable,
            @RequestParam(value = "page",defaultValue = "1") Integer page,
            @RequestParam(value = "rows",defaultValue = "5") Integer rows
    );


    @GetMapping("spu/detail/{spuId}")
    public SpuDetail queryBySpuId(@PathVariable("spuId")Long spuId);

    @GetMapping("sku/list")
    public List<Sku> querBySpu(@RequestParam("id")Long spuId);

}

SpecificationApi

@RequestMapping("spec")
public interface SpecificationApi {


    @GetMapping("groups/{cid}")
    public List<SpecGroup> querySpecGroyo(@PathVariable("cid") Long cid);

    @GetMapping("params")
    public List<SpecParam> querySpecParam(@RequestParam(value = "gid",required = false) Long gid,
                                                          @RequestParam(value = "cid",required = false)Long cid,
                                                          @RequestParam(value = "generic",required = false)Boolean generic,
                                                          @RequestParam(value = "searching",required = false)Boolean searching);


}

接下来就是在对应的Search微服务中创建对应的FeignClinet进行远程的调用,直接继承该接口就可以了,

BrandClient

@FeignClient(value = "item-service")
public interface BrandClient  extends BrandApi {

}

CategoryClient

@FeignClient(value = "item-service")
public interface CategoryClient extends CategoryApi {


}

SpecificationClient

@FeignClient(value = "item-service")
public interface SpecificationClient extends SpecificationApi {



}

GoodsClient

@FeignClient(value = "item-service")
public interface GoodsClient  extends GoodsApi {


}

接下来就是进行远程接口调用的测试,进行一个Test类的编写测试

测试成功:这个测试的时候要把相应的微服务全部启动起来才可以

接下来这个从Spu构建为Goods的模型比较难,业务逻辑比较复杂,但是拆开来看的话还是容易拆的

1,首先要把Spu构建成goods,因为传过来的参数是Spu,固可以得到spu里面的一些属性和字段,这个里面比较难处理的就是Sku和规格参数,其中这个地方处理Sku的地方是很巧妙的,因为一个Spu下面可以有多个sku,而多个Sku又有多个价格,这个就涉及到一个Spu下也存在多个价格的可能,解决的办法:

①针对一个Spu有多个Sku的解决方案,首先定义一个prices的集合,用来存放多个sku的价格,在其次,可以根据spu的Id查询出Sku的集合,将该单个Sku转换为map,统一存放到一个skuListMap中,这样在相应goods的时候,在把这个map的key,value结构在转换成json字符串响应出去,前台就可以解析处理了。

②规格参数的的特殊参数和通用参数,这个处理起来也是比较绕的,因为规格参数在SpuDetail这个 表里,而根据spuid查询出来的规格参数,只有能获取规格参数名字,而不能获取到规格参数的值,这个处理的办法,首先将SpuDetail进行Map反序列化处理,这样就可以得到kv结构,可以根据paramId,拿到对应的规格参数值,在定义一个paramMap,因为规格参数通过SpuId可以查询处理,得到一个集合,在遍历集合,将规格参数名,通过paramId,可以获取到parm的名字,而spuDetail里面的规格参数值,也可以通过paramId获取到,这样就可以用paramID作为中介,将规格参数名,和规格参数值,结合到一起了:


    public Goods goodsBuilds(Spu spu) throws IOException {
        //创建要返回的Goods对象 第一步必须要创建Goods对象这个后面是为了给Goods对象里面设置属性及其相应的值
        Goods goods=new Goods();
        //查询品牌      根据Spu的ID去查询出来对应的品牌
        Brand brand = this.brandClient.queryNameById(spu.getBrandId());
        //查询分类         根据Spu的ID去查询出来对应的分类名称
        List<String> categoryList = this.categoryClient.queryNameByids(Arrays.asList(spu.getCid1(), spu.getCid2(), spu.getCid3()));
        //查询SPu下的所有 Sku     根据SpuID去查询出来对应的下属的Sku的集合
        List<Sku> skus = this.goodsClient.querBySpu(spu.getId());
        //因为一个Spu下面可能有多个Sku,所以多个Sku,就可能有多个价格,定义价格price
        List<Long> prices=new ArrayList<>();
        //定义一个SkuMpa的集合,目的是把sku转成对应的固定的四个属性,之后在同一放到一起
        List<Map<String,Object>> skuMapList=new ArrayList<>();
        //遍历每个skus
        skus.forEach(sku -> {
            //把每个sku的价格放进去
            prices.add(sku.getPrice());
            //开始把每个Sku转换成Map
            Map<String,Object> skuMap=new HashMap<>();
            skuMap.put("id",sku.getId());
            skuMap.put("title",sku.getTitle());
            skuMap.put("price",sku.getPrice());
            skuMap.put("image", StringUtils.isNotBlank(sku.getImages())?StringUtils.split(sku.getImages(),",")[0]:"");
            skuMapList.add(skuMap);
        });
        //查询出所有的规格参数
        List<SpecParam> specParamList = this.specificationClient.querySpecParam(null, spu.getCid3(), null, true);
        //查询出所有的SpuDetail,获取规格参数值
        SpuDetail spuDetail = this.goodsClient.queryBySpuId(spu.getId());
        //获取通用的规格参数反序列化为Map对象  将获取的通用规格参数,反序为Map对象
        Map<Long,Object> genericScap=MAPPER.readValue(spuDetail.getGenericSpec(),new TypeReference<Map<Long,Object>>(){});
       //获取特殊规格参数反序列化为Map对象
        Map<Long,Object> specialSpecMap=MAPPER.readValue(spuDetail.getSpecialSpec(),new TypeReference<Map<Long,List<Object>>>(){});
        //定义map接收{规格参数名,规格参数值}
        Map<String,Object> paramap=new HashMap<>();
        specParamList.forEach(specParam -> {
            //检验是否是通用的规格参数
            if (specParam.getGeneric()){
                String value = genericScap.get(specParam.getId()).toString();
                //判断是否是数值类型
                if (specParam.getNumeric()){
                    //如果是数值类型,判断落在那个区间
                    value=chooseSegment(value,specParam);
                }
                paramap.put(specParam.getName(),value);
            }else{
                paramap.put(specParam.getName(),specialSpecMap.get(specParam.getId()));
            }
        });
        //设置参数
        goods.setId(spu.getId());
        goods.setCid1(spu.getCid1());
        goods.setCid2(spu.getCid2());
        goods.setCid3(spu.getCid3());
        goods.setBrandId(spu.getBrandId());
        goods.setCreateTime(spu.getCreateTime());
        goods.setSubTitle(spu.getSubTitle());
        goods.setAll(spu.getTitle()+brand.getName()+StringUtils.join(categoryList," "));
        goods.setPrice(prices);
        goods.setSkus(MAPPER.writeValueAsString(skuMapList));
        goods.setSpecs(paramap);
        return goods;
    }
    private String chooseSegment(String value, SpecParam p) {
        double val = NumberUtils.toDouble(value);
        String result = "其它";
        // 保存数值段
        for (String segment : p.getSegments().split(",")) {
            String[] segs = segment.split("-");
            // 获取数值范围
            double begin = NumberUtils.toDouble(segs[0]);
            double end = Double.MAX_VALUE;
            if(segs.length == 2){
                end = NumberUtils.toDouble(segs[1]);
            }
            // 判断是否在范围内
            if(val >= begin && val < end){
                if(segs.length == 1){
                    result = segs[0] + p.getUnit() + "以上";
                }else if(begin == 0){
                    result = segs[1] + p.getUnit() + "以下";
                }else{
                    result = segment + p.getUnit();
                }
                break;
            }
        }
        return result;
    }

开始构建模型:

   @Test
    public void TestContext(){
        this.elasticsearchTemplate.createIndex(Goods.class);
        this.elasticsearchTemplate.putMapping(Goods.class);
        Integer page=1;
        Integer rows=100;
        do {
            PageResult<SpuBo> pageResult = this.goodsClient.queryGoods(null, true, page, rows);
            List<Goods> goodsList = pageResult.getItems().stream().map(spuBo -> {
                try {
                    return this.searchService.goodsBuilds(spuBo);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return null;
            }).collect(Collectors.toList());
            this.goodsRepository.saveAll(goodsList);
            rows=pageResult.getItems().size();
            page++;

        }while (rows==100);

    }

构建模型成功:

2.实现基本搜索

2.1.页面分析

2.1.1.页面跳转

在首页的顶部,有一个输入框:

前台页面的AJAX发生的请求:

 var vm = new Vue({
        el: "#searchApp",
        data: {
            search:{
                key:"", // 搜索页面的关键字
            },
            goodList:[]
        }, created() {
            // 判断是否有请求参数
            if (!location.search) {
                return;
            }
            // 将请求参数转为对象
            const search = ly.parse(location.search.substring(1));
            // 记录在data的search对象中
            this.search = search;

            // 发起请求,根据条件搜索
            this.loadData();
        },
        methods: {
            loadData(){
                // ly.http.post("/search/page", ly.stringify(this.search)).then(resp=>{
                ly.http.post("/search/page", this.search).then(resp=>{
                    console.log(resp);
            });
            }
        },
        components:{
            lyTop: () => import("./js/pages/top.js")
        }
    });

 检查:

开始编写后台接口:

Controller:

@RestController
@RequestMapping
public class SearchController {

    @Autowired
    private SearchService searchService;

    @PostMapping("page")
    public ResponseEntity<PageResult<Goods>> queryByPage(@RequestBody SearchRequest searchRequest){
        PageResult<Goods> goodsList= this.searchService.queryByPage(searchRequest);
        if (goodsList.getItems().size()==0|| CollectionUtils.isEmpty(goodsList.getItems())){
            return ResponseEntity.badRequest().build();
        }
        return ResponseEntity.ok(goodsList);
    }

}

Service:

    public PageResult<Goods> queryByPage(SearchRequest searchRequest) {
        // 判断是否有搜索条件,如果没有,直接返回null。不允许搜索全部商品
        String key = searchRequest.getKey();
        if (StringUtils.isBlank(key)){
            return null;
        }
        NativeSearchQueryBuilder queryBuilder=new NativeSearchQueryBuilder();
        // 1、对key进行全文检索查询
        queryBuilder.withQuery(QueryBuilders.matchQuery("all",key).operator(Operator.AND));
        // 2、通过sourceFilter设置返回的结果字段,我们只需要id、skus、subTitle
        queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{"id","skus","subTitle"},null));
        // 3、分页准备分页参数
        Integer page = searchRequest.getPage();
        Integer size = searchRequest.getSize();
        queryBuilder.withPageable(PageRequest.of(page-1,size));
        // 4、查询,获取结果
        Page<Goods> goods = this.goodsRepository.search(queryBuilder.build());
        // 5.封装结果并返回
        return new PageResult<>(goods.getTotalElements(),goods.getTotalPages(),goods.getContent());

    }
}

完成测试:

今日总结:

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
自己做的乐优商城的XMIND文件,学习分享下。乐优商城 搭建父工程 pom.xml 添加依赖 springCloud mybatis启动器 通用Mapper启动器 mysql驱动 分页助手启动器 FastDFS客户端 其他配置 构建设置 环境设置 EurekaServer注册中心 添加的依赖 启动类 application.yml 创建Zuul网关 依赖 启动类 application.yml 创建商品微服务 ly-item-interface:主要是对外暴露的API接口及相关实体类 ly-item-service:所有业务逻辑及内部使用接口 创建父工程ly-item ly-item-interface ly-item-service 依赖 启动器 application.yml 添加商品微服务的路由规则 通用工具模块Common utils CookieUtils IdWorker JsonUtils NumberUtils 依赖 通用异常处理 测试结构 pojo service @Service web @RestController @RequestMapping @Autowired @PostMapping 引入Common依赖 Common advice 拦截异常、 CommonExceptionHandler ResponseEntity @ControllerAdvice @ExceptionHandler enums 异常的枚举 、ExceptionEnum exception 自定义异常、LyException 接口RuntimeException @Getter @NoArgsConstructor @AllArgsConstructor vo 异常结果处理对象、ExceptionResult @Data 构造方法ExceptionResult ly-item-service CategoryQuery 分类查询 实体类 @Table(name="tb_category") 声明此对象映射到数据库的数据表,通过它可以为实体指定表(talbe) @Data 注解在类上, 为类提供读写属性, 此外还提供了 equals()、hashCode()、toString() 方法 @Id & @GeneratedValue(strategy= GenerationType.IDENTITY) 自动增长,适用于支持自增字段的数据库 mapper Mapper IdListMapper 根据id操作数据库 @RequestMapping("category") Controller @RestController @Controller 处理HTTP请求 @ResponseBody 返回 json 数据 @GetMapping("list") ResponseEntity @ResponseBody可以直接返回Json结果 不仅可以返回json结果,还可以定义返回的HttpHeaders和HttpStatus service @Service 自动注册到Spring容器,不需要再在applicationContext.xml文件定义bean了 @Autowired 它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。 select select * from category c where c.pid = #{pid} CollectionUtils.isnotblank 判断集合是否为空 测试 可以利用url直接查询数据库,能否访问得到数据 报错 启动类 没有扫描到 @MapperScan("com.leyou.item.mapper") ,目录结构关系 访问网页报错 CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. 跨域问题 浏览器对于ajax请求的一种安全限制:一个页面发起的ajax请求,只能是于当前页同域名的路径,这能有效的阻止跨站攻击。因此:跨域问题 是针对ajax的一种限制。 解决跨域问题的方案 CORS 规范化的跨域请求解决方案,安全可靠 什么是cors 它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。 原理 简单请求 当浏览器发现发现的ajax请求是简单请求时,会在请求头中携带一个字段:Origin 如果服务器允许跨域,需要在返回的响应头中携带下面信息 Access-Control-Allow-Origin:可接受的域,是一个具体域名或者*,代表任意 Access-Control-Allow-Credentials:是否允许携带cookie,默认情况下,cors不会携带cookie,除非这个值是true 实现非常简单 gateway网关中编写一个配置类 GlobalCorsConfig 添加CORS配置信息 允许的域,不要写*,否则cookie就无法使用了 是否发送Cookie信息 允许的请求方式 允许的头信息 有效时长 添加映射路径,我们拦截一切请求 返回新的CorsFilter 提交方式 GET 用来获取资源,POST 用来新建资源,PUT 用来更新资源,DELETE 用来删除资源 BUG 分类不能打开,当添加后却能打开。 修改一天的BUG 最后发现是实体类里属性大小写的问题引起。 注意 Bule_bird 就必须写成 BlueBird Brand 查询 实体类 PageResult 响应结果 分页结果一般至少需要两个数据 总条数 total 当前页数据 items 有些还需要总页数 总页数 totalPage Controller @RequestParam(value = "page",defaultValue = "1") Integer page GET和POST请求传的参数会自动转换赋值到@RequestParam 所注解的变量上 defaultValue 默认值 required 默认值为true , 当为false时 这个注解可以不传这个参数 null || .size()==0 ResponseEntity(HttpStatus.NOT_FOUND) 返回404没找到 ResponseEntity.ok 返回ok状态 service 开始分页 通用分页拦截器 PageHelper.startPage(page,row); 过滤 Example查询 Example example = new Example(Brand.class); mybatis的逆向工程中会生成实例及实例对应的example,example用于添加条件,相当where后面的部分 xxxExample example = new xxxExample(); Criteria criteria = new Example().createCriteria(); StringUtils.isNotBlank isNotBlank(str) 等价于 str != null && str.length > 0 && str.trim().length > 0 str.trim() 去掉字符串头尾的空格 测试 报错500 com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'idASC' in 'order clause' 错误:(desc ? "DESC" : "ASC"); 正确:(desc ? " DESC" : " ASC"); 字符串空格问题 新增 Controller (Brand brand,@RequestParam("cids") List cids) ResponseEntity 无返回值 new ResponseEntity(HttpStatus.CREATED); 201成功 service @Transactional 自动纳入 Spring 的事务管理 使用默认配置,抛出异常之后,事务会自动回滚,数据不会插入到数据库。 setId(null) insert(brand) 新增中间表 mapper @Insert (#{cid},#{bid}) @Param 表示给参数命名,名称就是括号中的内容 name 命名为aa,然后sql语句....where s_name= #{aa} 中就可以根据aa得到参数值 修改 回显 Controller @PathVariable("bid") 通过 @PathVariable 可以将 URL 中占位符参数绑定到控制器处理方法的入参中:URL 中的 {xxx} 占位符可以通过@PathVariable(“xxx“) 绑定到操作方法的入参中。 select * from tb_category where id in (select category_id from tb_category_brand where brand_id = #{bid}) 测试 报错500 空指针异常 调用Service时候 忘记@Autowired 保存 VO视图对象 @NoArgsConstructor 生成一个无参数的构造方法 @AllArgsConstructor 会生成一个包含所有变量 Controller @PutMapping 添加信息,倾向于用@PostMapping,如果是更新信息,倾向于用@PutMapping。两者差别不是很明显 return ResponseEntity.ok().build(); 无返回值 service 根据id修改 先删除后新增 删除(前端有问题,待完善) spec Group 品牌分类id查询 实体类 @Transient 指定该属性或字段不是永久的。 它用于注释实体类,映射超类或可嵌入类的属性或字段。 @Column(name = "'numeric'") 用来标识实体类中属性与数据表中字段的对应关系 name 定义了被标注字段在数据库表中所对应字段的名称; mapper service Controller 测试 报错500 实体类@table路径写错 新增 Controller @RequestBody 常用其来处理application/json类型 子主题 2 将请求体中的JSON字符串绑定到相应的bean上 修改 Controller @PutMapping service updateByPrimaryKey 删除 Controller @DeleteMapping @PathVariable Param 规格组id查询规格 url:params?gid=14 @GetMapping("params") Controller @RequestParam 新增 @PostMapping("param") @RequestBody ResponseEntity.status(HttpStatus.CREATED).build(); 修改 @RequestBody 删除 @PathVariable 分支主题 3 遇到的问题 pom.xml 文件未下载完整,删掉后重新下载 能存在重复文件,IDEA不能确定文件路径,需要搜索删掉多余的 Param 删除 小问题:数据库删除后页面没有立即显示 Brand 删除(前端有问题,待完善)
《淘淘商城-day08.docx》是一份关于淘淘商城第八天工作的文档。在这一天,淘淘商城的团队采取了一系列措施来进一步提升用户体验和销售业绩。 首先,团队进行了网站内容的优化。他们对商品详情页进行了修复和更新,确保信息的准确性和完整性。同时,他们也对页面的布局进行了调整,使得用户更容易浏览和购买商品。这些改进措施能够提升用户的满意度和购买转化率。 其次,团队还加强了与供应商的合作关系。他们与一些热门品牌建立起了合作伙伴关系,以获得独家销售权或更优惠的价格。这不仅能够吸引更多的用户,还能提高淘淘商城的竞争力。 另外,团队还对物流配送进行了优化。他们与一家可靠的物流公司合作,以确保商品能够及时、安全地送达给用户。他们还推出了更快速、更便捷的配送选项,如次日达和晚间配送,以满足用户的各种需求。 为了增加用户的复购率,团队还积极进行了促销活动。他们推出了限时限量的特价商品,以及购买一定金额就能获得赠品或折扣的优惠活动。这些促销措施能够吸引用户重复购买,并提升销售额。 总之,《淘淘商城-day08.docx》记录了淘淘商城团队在第八天所做的一系列工作。通过网站内容优化、供应商合作、物流配送优化和促销活动,团队致力于提升用户体验和销售业绩,从而使淘淘商城更加竞争和成功。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值