• 谷粒商城 高级篇 (五) --------- 商品上架



    前言

    上架的商品才可以在网站展示。
    上架的商品需要可以被检索。


    一、商品 Mapping

    分析:商品上架在 es 中是存 sku 还是 spu?

    1、检索的时候输入名字,是需要按照 sku 的 title 进行全文检索

    2、检索使用商品规格,规格是 spu 的公共属性,每个 spu 是一样的

    3、按照分类 id 进去的都是直接列出 spu 的,还可以切换。

    4、我们如果将 sku 的全量信息保存到 es 中 (包括 spu 属性) 就太多量字段了。

    5、我们如果将 spu 以及他包含的 sku 信息保存到 es 中,也可以方便检索。但是 sku 属于spu 的级联对象,在 es 中需要 nested 模型,这种性能差点。

    6、但是存储与检索我们必须性能折中。

    7、如果我们分拆存储,spu 和 attr 一个索引,sku 单独一个索引可能涉及的问题。检索商品的名字,如“手机”,对应的 spu 有很多,我们要分析出这些spu 的所有关联属性,再做一次查询,就必须将所有 spu_id 都发出去。假设有 1 万个数据,数据传输一次就10000*4=4MB;并发情况下假设 1000 检索请求,那就是 4GB 的数据,,传输阻塞时间会很长,业务更加无法继续。

    所以,我们如下设计,这样才是文档区别于关系型数据库的地方,宽表设计,不能去考虑数据库范式。

    PUT product

    product 的 mapping

    { 
    	"mappings": { 
    		"properties": { 
    			"skuId": { 
    				"type": "long"
    			},
    			"spuId": { 
    				"type": "keyword"
    			},
    			"skuTitle": { 
    				"type": "text", 
    				"analyzer": "ik_smart"
    			},
    			"skuPrice": { 
    				"type": "keyword"
    			},
    			"skuImg": { 
    				"type": "keyword",
    				"index": false, 
    				"doc_values": false
    			},
    			"saleCount": { 
    				"type": "long"
    			},
    			"hasStock": { 
    				"type": "boolean"
    			},
    			"hotScore": { 
    				"type": "long"
    			},
    			"brandId": { 
    				"type": "long"
    			},
    			"catalogId": { 
    				"type": "long"
    			},
    			"brandName": { 
    				"type": "keyword", 
    				"index": false, 
    				"doc_values": false
    			},
    			"brandImg": { 
    				"type": "keyword", 
    				"index": false, 
    				"doc_values": false
    			},
    			"catalogName": { 
    				"type": "keyword", 
    				"index": false, 
    				"doc_values": false		
    			},
    			"attrs": { 
    				"type": "nested", 
    				"properties": { 
    					"attrId": { 
    						"type": "long"
    					},
    					"attrName": { 
    						"type": "keyword", 
    						"index": false, 
    						"doc_values": false
    					},
    					"attrValue": { 
    						"type": "keyword"
    					 }
    				}	
    			}
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70

    index:

    默认 true,如果为 false,表示该字段不会被索引,但是检索结果里面有,但字段本身不能当做检索条件。

    doc_values:

    默认 true,设置为 false,表示不可以做排序、聚合以及脚本操作,这样更节省磁盘空间。还可以通过设定 doc_values 为 true,index 为 false 来让字段不能被搜索但可以用于排序、聚合以及脚本操作。

    二、上架细节

    上架是将后台的商品放在 es 中可以提供检索和查询功能

    1、hasStock:代表是否有库存。默认上架的商品都有库存。如果库存无货的时候才需要更新一下 es

    2、库存补上以后,也需要重新更新一下 es

    3、hotScore 是热度值,我们只模拟使用点击率更新热度。点击率增加到一定程度才更新热度值。

    4、下架就是从 es 中移除检索项,以及修改 mysql 状态

    商品上架步骤:

    A、先在 es 中按照之前的 mapping 信息,建立 product 索引。

    B、点击上架,查询出所有 sku 的信息,保存到 es 中。

    C、es 保存成功返回,更新数据库的上架状态信息。

    数据一致性

    A、商品无库存的时候需要更新 es 的库存信息
    B、商品有库存也要更新 es 的信息

    三、具体实现

    首先实现 SpuInfioController 下的 spuUp 接口

    //商品上架
    ///product/spuinfo/{spuId}/up
    @PostMapping(value = "/{spuId}/up")
    public R spuUp(@PathVariable("spuId") Long spuId) {
    
        spuInfoService.up(spuId);
    
        return R.ok();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    对应的 spuInfoServiceImpl 中实现 up 方法

    @Override
    public void up(Long spuId) {
    
        //1、查出当前spuid对应的所有sku信息,品牌的名字。
        List<SkuInfoEntity> skus = skuInfoService.getSkusBySpuId(spuId);
        List<Long> skuIdList = skus.stream().map(SkuInfoEntity::getSkuId).collect(Collectors.toList());
    
        //TODO 4、查询当前sku的所有可以被用来检索的规格属性,
        List<ProductAttrValueEntity> baseAttrs = attrValueService.baseAttrlistforspu(spuId);
        List<Long> attrIds = baseAttrs.stream().map(attr -> {
            return attr.getAttrId();
        }).collect(Collectors.toList());
    
        List<Long> searchAttrIds = attrService.selectSearchAttrIds(attrIds);
    
        Set<Long> idSet = new HashSet<>(searchAttrIds);
    
        List<SkuEsModel.Attrs> attrsList = baseAttrs.stream().filter(item -> {
            return idSet.contains(item.getAttrId());
        }).map(item -> {
            SkuEsModel.Attrs attrs1 = new SkuEsModel.Attrs();
            BeanUtils.copyProperties(item, attrs1);
            return attrs1;
        }).collect(Collectors.toList());
    
        //TODO 1、发送远程调用,库存系统查询是否有库存
        Map<Long, Boolean> stockMap = null;
        try{
            R r = wareFeignService.getSkusHasStock(skuIdList);
            //
            TypeReference<List<SkuHasStockVo>> typeReference = new TypeReference<List<SkuHasStockVo>>() {
            };
            stockMap = r.getData(typeReference).stream().collect(Collectors.toMap(SkuHasStockVo::getSkuId, item -> item.getHasStock()));
        }catch (Exception e){
            log.error("库存服务查询异常:原因{}",e);
        }
    
    
        //2、封装每个sku的信息
        Map<Long, Boolean> finalStockMap = stockMap;
        List<SkuEsModel> upProducts = skus.stream().map(sku -> {
            //组装需要的数据
            SkuEsModel esModel = new SkuEsModel();
            BeanUtils.copyProperties(sku,esModel);
            //skuPrice,skuImg,
            esModel.setSkuPrice(sku.getPrice());
            esModel.setSkuImg(sku.getSkuDefaultImg());
            //hasStock,hotScore
            //设置库存信息
            if(finalStockMap == null){
                esModel.setHasStock(true);
            }else {
                esModel.setHasStock(finalStockMap.get(sku.getSkuId()));
            }
    
            //TODO 2、热度评分。0
            esModel.setHotScore(0L);
    
            //TODO 3、查询品牌和分类的名字信息
            BrandEntity brand = brandService.getById(esModel.getBrandId());
            esModel.setBrandName(brand.getName());
            esModel.setBrandImg(brand.getLogo());
    
            CategoryEntity category = categoryService.getById(esModel.getCatalogId());
            esModel.setCatalogName(category.getName());
    
            //设置检索属性
            esModel.setAttrs(attrsList);
    
            return esModel;
        }).collect(Collectors.toList());
    
        //TODO 5、将数据发送给es进行保存;gulimall-search;
        R r = searchFeignService.productStatusUp(upProducts);
        if(r.getCode() == 0){
            //远程调用成功
            //TODO 6、修改当前spu的状态
            baseMapper.updateSpuStatus(spuId, ProductConstant.StatusEnum.SPU_UP.getCode());
        }else {
            //远程调用失败
            //TODO 7、重复调用?接口幂等性;重试机制?xxx
            //Feign调用流程
            /**
             * 1、构造请求数据,将对象转为json;
             *      RequestTemplate template = buildTemplateFromArgs.create(argv);
             * 2、发送请求进行执行(执行成功会解码响应数据):
             *      executeAndDecode(template);
             * 3、执行请求会有重试机制
             *      while(true){
             *          try{
             *            executeAndDecode(template);
             *          }catch(){
             *              try{retryer.continueOrPropagate(e);}catch(){throw ex;}
             *              continue;
             *          }
             *
             *      }
             */
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100

    我们来看这个方法,首先查出 spuid 对应的 sku 信息,即 getSkusBySpuId() 方法实现

    @Override
    public List<SkuInfoEntity> getSkusBySpuId(Long spuId) {
        List<SkuInfoEntity> list = this.list(new QueryWrapper<SkuInfoEntity>().eq("spu_id",spuId));
        return list;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    然后 baseAttrlistforspu 的实现,查询可用来被检索的属性,这个我们之前已经实现过。

    构造 es 检索基本数据 SkuEsModel,我们将其放到 common 模块下

    package com.fancy.common.to.es;
    
    import lombok.Data;
    
    import java.math.BigDecimal;
    import java.util.List;
    
    @Data
    public class SkuEsModel {
    
        private Long skuId;
    
        private Long spuId;
    
        private String skuTitle;
    
        private BigDecimal skuPrice;
    
        private String skuImg;
    
        private Long saleCount;
    
        private Boolean hasStock;
    
        private Long hotScore;
    
        private Long brandId;
    
        private Long catalogId;
    
        private String brandName;
    
        private String brandImg;
    
        private String catalogName;
    
        private List<Attrs> attrs;
    
        @Data
        public static class Attrs {
    
            private Long attrId;
    
            private String attrName;
    
            private String attrValue;
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    远程调用 ware 模块相关接口的实现

    在这里插入图片描述

    WareSkuController 中:

    //查询sku是否有库存
    @PostMapping("/hasstock")
    public R getSkusHasStock(@RequestBody List<Long> skuIds){
    
        //sku_id,stock
        List<SkuHasStockVo> vos =  wareSkuService.getSkusHasStock(skuIds);
    
    
        return R.ok().setData(vos);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    R 工具类的相关调整:

    package com.fancy.common.utils;
    
    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.TypeReference;
    import org.apache.http.HttpStatus;
    
    import java.util.HashMap;
    import java.util.Map;
    
    import static jdk.management.resource.internal.SimpleResourceContext.get;
    
    public class R extends HashMap<String, Object> {
    	private static final long serialVersionUID = 1L;
    
    	//利用fastjson进行逆转
    	public <T> T getData(String key, TypeReference<T> typeReference){
    		Object data = get(key);//默认是map
    		String s = JSON.toJSONString(data);
    		T t = JSON.parseObject(s, typeReference);
    		return t;
    	}
    
    	//利用fastjson进行逆转
    	public <T> T getData(TypeReference<T> typeReference){
    		Object data = get("data");//默认是map
    		String s = JSON.toJSONString(data);
    		T t = JSON.parseObject(s, typeReference);
    		return t;
    	}
    	public R setData(Object data){
    		put("data",data);
    		return this;
    	}
    
    	public R() {
    		put("code", 0);
    		put("msg", "success");
    	}
    
    	public static R error() {
    		return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, "未知异常,请联系管理员");
    	}
    
    	public static R error(String msg) {
    		return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, msg);
    	}
    
    	public static R error(int code, String msg) {
    		R r = new R();
    		r.put("code", code);
    		r.put("msg", msg);
    		return r;
    	}
    
    	public static R ok(String msg) {
    		R r = new R();
    		r.put("msg", msg);
    		return r;
    	}
    
    	public static R ok(Map<String, Object> map) {
    		R r = new R();
    		r.putAll(map);
    		return r;
    	}
    
    	public static R ok() {
    		return new R();
    	}
    
    	public R put(String key, Object value) {
    		super.put(key, value);
    		return this;
    	}
    	public  Integer getCode() {
    
    		return (Integer) this.get("code");
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80

    SkuHasStockVo 的封装

    package com.fancy.gulimall.ware.vo;
    
    import lombok.Data;
    
    @Data
    public class SkuHasStockVo {
    
        private Long skuId;
        private Boolean hasStock;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    getSkusHasStock 的实现

    在这里插入图片描述

    getSkuStock 的 mapper 映射文件

    在这里插入图片描述

    将数据发送给 es 进行保存的相关实现

    在这里插入图片描述
    ElasticSaveController 的实现:

    package com.fancy.gulimall.search.controller;
    
    
    import com.fancy.common.exception.BizCodeEnum;
    import com.fancy.common.to.es.SkuEsModel;
    import com.fancy.common.utils.R;
    import com.fancy.gulimall.search.service.ProductSaveService;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.List;
    
    @Slf4j
    @RequestMapping("/search/save")
    @RestController
    public class ElasticSaveController {
    
        @Autowired
        ProductSaveService productSaveService;
    
        //上架商品
        @PostMapping("/product")
        public R productStatusUp(@RequestBody List<SkuEsModel> skuEsModels) {
            boolean b = false;
            try {
                b = productSaveService.productStatusUp(skuEsModels);
            } catch (Exception e) {
                log.error("ElasticSaveController商品上架错误:{}", e);
                return R.error(BizCodeEnum.PRODUCT_UP_EXCEPTION.getCode(), BizCodeEnum.PRODUCT_UP_EXCEPTION.getMsg());
            }
    
            if (!b) {
                return R.ok();
            } else {
                return R.error(BizCodeEnum.PRODUCT_UP_EXCEPTION.getCode(), BizCodeEnum.PRODUCT_UP_EXCEPTION.getMsg());
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    BizCodeEnum 的调整

    package com.fancy.common.exception;
    /***
     * 错误码和错误信息定义类
     * 1. 错误码定义规则为 5 为数字
     * 2. 前两位表示业务场景,最后三位表示错误码。例如:100001。10:通用001:系统未知异常
     * 3. 维护错误码后需要维护错误描述,将他们定义为枚举形式
     * 错误码列表:
     * 10: 通用
     * 001:参数格式校验
     * 11: 商品
     * 12: 订单
     * 13: 购物车
     * 14: 物流
     *
     *
     */
    public enum BizCodeEnum {
    
    
        UNKNOWN_EXCEPTION(10000, "系统未知异常"),
        VALID_EXCEPTION(10001, "参数格式校验失败"),
    
        TOO_MANY_REQUEST(10002,"请求流量过大"),
    
        SMS_CODE_EXCEPTION(10002,"验证码获取频率太高,稍后再试"),
        PRODUCT_UP_EXCEPTION(11000,"商品上架异常"),
    
        USER_EXIST_EXCEPTION(15001,"用户存在"),
    
        PHONE_EXIST_EXCEPTION(15002,"手机号存在"),
    
        NO_STOCK_EXCEPTION(21000,"商品库存不足"),
    
        LOGINACCT_PASSWORD_INVAILD_EXCEPTION(15003,"账号密码错误");
    
        private int code;
        private String msg;
    
        BizCodeEnum(int code, String msg) {
            this.code = code;
            this.msg = msg;
        }
    
        public int getCode() {
            return code;
        }
    
        public String getMsg() {
            return msg;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    productStatusUp 方法的实现:

    @Override
    public boolean productStatusUp(List<SkuEsModel> skuEsModels) throws IOException {
    
        //保存到es
        //1、给es中建立索引。product,建立好映射关系。
    
        //2、给es中保存这些数据
        //BulkRequest bulkRequest, RequestOptions options
        BulkRequest bulkRequest = new BulkRequest();
        for (SkuEsModel model : skuEsModels) {
            //1、构造保存请求
            IndexRequest indexRequest = new IndexRequest(EsConstant.PRODUCT_INDEX);
            indexRequest.id(model.getSkuId().toString());
            String s = JSON.toJSONString(model);
            indexRequest.source(s, XContentType.JSON);
    
            bulkRequest.add(indexRequest);
        }
    
        BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, ElasticSearchConfig.COMMON_OPTIONS);
    
        //TODO 1、如果批量错误
        boolean b = bulk.hasFailures();
        List<String> collect = Arrays.stream(bulk.getItems()).map(item -> {
            return item.getId();
        }).collect(Collectors.toList());
        log.info("商品上架完成:{},返回数据:{}",collect,bulk.toString());
    
    
        return b;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    ElasticSearchConfig 中:

    package com.fancy.gulimall.search.config;
    
    import org.apache.http.HttpHost;
    import org.elasticsearch.client.RequestOptions;
    import org.elasticsearch.client.RestClient;
    import org.elasticsearch.client.RestClientBuilder;
    import org.elasticsearch.client.RestHighLevelClient;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class ElasticSearchConfig {
    
        public static final RequestOptions COMMON_OPTIONS;
        static {
            RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder();
    //        builder.addHeader("Authorization", "Bearer " + TOKEN);
    //        builder.setHttpAsyncResponseConsumerFactory(
    //                new HttpAsyncResponseConsumerFactory
    //                        .HeapBufferedResponseConsumerFactory(30 * 1024 * 1024 * 1024));
            COMMON_OPTIONS = builder.build();
        }
    
        @Bean
        public RestHighLevelClient esRestClient(@Value("${spring.elasticsearch.jest.uris}")String esUrl){
    
            //TODO 修改为线上的地址
            RestClientBuilder builder = null;
            //final String hostname, final int port, final String scheme
    
    //        builder = RestClient.builder(new HttpHost("192.168.38.130", 9200, "http"));
            builder = RestClient.builder(HttpHost.create(esUrl));
            RestHighLevelClient client = new RestHighLevelClient(builder);
    //        RestHighLevelClient client = new RestHighLevelClient(
    //                RestClient.builder(
    //                        new HttpHost("192.168.38.130", 9200, "http")));
            return client;
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    在配置文件中,将spring.elasticsearch.jest.uris映射成 es 地址即可

  • 相关阅读:
    深度学习:cross-attention介绍以及与self-attention的区别
    古人道中秋 | 制作一个可拖动的月球
    AVM赛道研究:预计2024年渗透率突破50%!下一个破局点在哪儿?
    Mac M1安装Centos8
    Win11+RTX3060+Anconda+CUDA11.3+cuDNN8.2+Pytorch1.8一条龙服务
    modelscope适配昇腾NPU
    K-verse 小型活动来袭!
    tcl脚本学习-基础语法
    RTA-OS Port Guide学习(二)-基于S32K324 OS
    算法进修Day-33
  • 原文地址:https://blog.csdn.net/m0_51111980/article/details/126820848