Summary of basic distributed of grain mall

Posted by mausie on Sat, 11 Sep 2021 08:46:18 +0200

@TOC

1.SPU and SKU

SPU (standardized product unit): similar to classes in java, that is, abstract

SKU (inventory unit): similar to an object, it has more specific values

Basic attribute (specification parameter): common attributes such as length.

Sales attribute: specific attributes

1. Shared specification parameters under classification (such as mobile phones and computers) are basic attributes. spu (such as Xiaomi mobile phones) is a type with many basic attributes. sku corresponds to sales attributes. If there is an attribute table, spu accounts for some basic attributes and sku accounts for some sales attributes. sku will be associated with spu, If it is the same spu, it will have the basic attributes owned by the spu. If classification is a large class, spu is a small class in the large class, and sku is a specific class.

2. Attribute grouping (attribute type, such as basic information (xx,xx,xx), main chip (yy,yy,yy)), in which the basic information contains many attributes, such as model, size, etc. the main chip's attributes may be performance, memory size, etc.

  • Attributes and attribute groups are organized by three levels of classification
  • Specification parameters are also basic attributes, and they have their own grouping
  • Specification parameters can be retrieved
  • The attribute name is determined, but the attribute is determined by the product

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-kv4roczo-1631265738661) (C: \ users \ 11914 \ appdata \ roaming \ typora \ user images \ image-20210906183807825. PNG)]

There are two types of tables. The first is three-level classification, attribute grouping and attribute. The three-level classification contains multiple attribute groups, and there are also many attributes under the attribute group. There is an association table between attribute groups and attributes, which is used to associate the attributes.

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-svpmhvil-1631265738663) (C: \ users \ 11914 \ appdata \ roaming \ typora \ typora user images \ image-20210906183617859. PNG)]

2. Attribute grouping

1. Introduce classification tree and attribute grouping components.

  • Create category component tree
  • Create attrgroup, and then introduce category component and add and update component. Show table columns
  • Split through layout.

attrgroup.vue

<!--  -->
<template>
  <div>
    <el-row :gutter="20">
      <el-col :span="6"
        ><div class="grid-content bg-purple">
          <category></category></div
      ></el-col>

      <el-col :span="18">
        <div class="grid-content bg-purple">
          <div class="mod-config">
            <el-form
              :inline="true"
              :model="dataForm"
              @keyup.enter.native="getDataList()"
            >
              <el-form-item>
                <el-input
                  v-model="dataForm.key"
                  placeholder="Parameter name"
                  clearable
                ></el-input>
              </el-form-item>
              <el-form-item>
                <el-button @click="getDataList()">query</el-button>
                <el-button
                  v-if="isAuth('product:attrgroup:save')"
                  type="primary"
                  @click="addOrUpdateHandle()"
                  >newly added</el-button
                >
                <el-button
                  v-if="isAuth('product:attrgroup:delete')"
                  type="danger"
                  @click="deleteHandle()"
                  :disabled="dataListSelections.length <= 0"
                  >Batch delete</el-button
                >
              </el-form-item>
            </el-form>
            <el-table
              :data="dataList"
              border
              v-loading="dataListLoading"
              @selection-change="selectionChangeHandle"
              style="width: 100%"
            >
              <el-table-column
                type="selection"
                header-align="center"
                align="center"
                width="50"
              >
              </el-table-column>
              <el-table-column
                prop="attrGroupId"
                header-align="center"
                align="center"
                label="grouping id"
              >
              </el-table-column>
              <el-table-column
                prop="attrGroupName"
                header-align="center"
                align="center"
                label="Group name"
              >
              </el-table-column>
              <el-table-column
                prop="sort"
                header-align="center"
                align="center"
                label="sort"
              >
              </el-table-column>
              <el-table-column
                prop="descript"
                header-align="center"
                align="center"
                label="describe"
              >
              </el-table-column>
              <el-table-column
                prop="icon"
                header-align="center"
                align="center"
                label="Group icon"
              >
              </el-table-column>
              <el-table-column
                prop="catelogId"
                header-align="center"
                align="center"
                label="Classification id"
              >
              </el-table-column>
              <el-table-column
                fixed="right"
                header-align="center"
                align="center"
                width="150"
                label="operation"
              >
                <template slot-scope="scope">
                  <el-button
                    type="text"
                    size="small"
                    @click="addOrUpdateHandle(scope.row.attrGroupId)"
                    >modify</el-button
                  >
                  <el-button
                    type="text"
                    size="small"
                    @click="deleteHandle(scope.row.attrGroupId)"
                    >delete</el-button
                  >
                </template>
              </el-table-column>
            </el-table>
            <el-pagination
              @size-change="sizeChangeHandle"
              @current-change="currentChangeHandle"
              :current-page="pageIndex"
              :page-sizes="[10, 20, 50, 100]"
              :page-size="pageSize"
              :total="totalPage"
              layout="total, sizes, prev, pager, next, jumper"
            >
            </el-pagination>
            <!-- Popup, newly added / modify -->
            <add-or-update
              v-if="addOrUpdateVisible"
              ref="addOrUpdate"
              @refreshDataList="getDataList"
            ></add-or-update>
          </div>
        </div>
      </el-col>
    </el-row>
  </div>
</template>

<script>
//Other files can be imported here (such as components, tools js, third-party plug-ins js, json files, picture files, etc.)
//For example: import component name from 'component path';
import Category from "../common/category.vue";
import AddOrUpdate from "./attrgroup-add-or-update.vue"
export default {
  //Introduction component
  //The components introduced by import need to be injected into the object before they can be used
  components: { Category ,AddOrUpdate},
  data() {
    return {
      dataForm: {
        key: "",
      },
      dataList: [],
      pageIndex: 1,
      pageSize: 10,
      totalPage: 0,
      dataListLoading: false,
      dataListSelections: [],
      addOrUpdateVisible: false,
    };
  },
  //Listening properties are similar to the data concept
  computed: {},
  //Monitor data changes in data
  watch: {},
  //Method set
  methods: {
    // Get data list
    getDataList() {
      this.dataListLoading = true;
      this.$http({
        url: this.$http.adornUrl("/product/attrgroup/list"),
        method: "get",
        params: this.$http.adornParams({
          page: this.pageIndex,
          limit: this.pageSize,
          key: this.dataForm.key,
        }),
      }).then(({ data }) => {
        if (data && data.code === 0) {
          this.dataList = data.page.list;
          this.totalPage = data.page.totalCount;
        } else {
          this.dataList = [];
          this.totalPage = 0;
        }
        this.dataListLoading = false;
      });
    },
    // Number of pages per page
    sizeChangeHandle(val) {
      this.pageSize = val;
      this.pageIndex = 1;
      this.getDataList();
    },
    // Current page
    currentChangeHandle(val) {
      this.pageIndex = val;
      this.getDataList();
    },
    // Multiple choice
    selectionChangeHandle(val) {
      this.dataListSelections = val;
    },
    // Add / modify
    addOrUpdateHandle(id) {
      this.addOrUpdateVisible = true;
      this.$nextTick(() => {
        this.$refs.addOrUpdate.init(id);
      });
    },
    // delete
    deleteHandle(id) {
      var ids = id
        ? [id]
        : this.dataListSelections.map((item) => {
            return item.attrGroupId;
          });
      this.$confirm(
        `OK, yes[id=${ids.join(",")}]conduct[${id ? "delete" : "Batch delete"}]operation?`,
        "Tips",
        {
          confirmButtonText: "determine",
          cancelButtonText: "cancel",
          type: "warning",
        }
      ).then(() => {
        this.$http({
          url: this.$http.adornUrl("/product/attrgroup/delete"),
          method: "post",
          data: this.$http.adornData(ids, false),
        }).then(({ data }) => {
          if (data && data.code === 0) {
            this.$message({
              message: "Operation succeeded",
              type: "success",
              duration: 1500,
              onClose: () => {
                this.getDataList();
              },
            });
          } else {
            this.$message.error(data.msg);
          }
        });
      });
    },
  },
  //Lifecycle - creation complete (you can access the current instance of this)
  created() {},
  //Lifecycle - Mount complete (DOM elements can be accessed)
  mounted() {},
  beforeCreate() {}, //Lifecycle - before creating
  beforeMount() {}, //Lifecycle - before mounting
  beforeUpdate() {}, //Lifecycle - before updating
  updated() {}, //Lifecycle - after update
  beforeDestroy() {}, //Life cycle - before destruction
  destroyed() {}, //Life cycle - destruction complete
  activated() {
    this.getDataList();
  }, //If the page has a keep alive cache function, this function will be triggered
};
</script>
<style>
</style>

Key points: review the process of classification tree formation. And extract it. Then the table vue is generated by reverse engineering.

2. After clicking the node, the table can be displayed through the node information.

Idea:

① The tree component can bind a @ node click method, which can transfer node information, and then bind the tree node click method by clicking, and use this.$emit("parent component method binding name", parameter...);

② Once the tree node click method of the parent component bound to the child component in methods is triggered, these parameters will be sensed and obtained through the parent component's own method (embodied in code)

category.vue

<!--  -->
<template>
    <el-tree :data="menus" :props="defaultProps" ref="menuTree" @node-click="nodeclick"> </el-tree>
</template>

<script>
//Other files can be imported here (such as components, tools js, third-party plug-ins js, json files, picture files, etc.)
//For example: import component name from 'component path';

export default {
  //The components introduced by import need to be injected into the object before they can be used
  components: {},
  data() {
    //Data is stored here
    return {
      menus: [],
      defaultProps: {
        children: "children",
        label: "name",
      },
    };
  },
  //Listening properties are similar to the data concept
  computed: {},
  //Monitor data changes in data
  watch: {},
  //Method set
  methods: {
      //Click event
      nodeclick(data,node,component){
          console.log(data,node,component);
          //Send to parent component
          this.$emit("tree-node-click",data,node,component);
      }
    ,
    getMenus() {
      this.$http({
        url: this.$http.adornUrl("/product/category/list"),
        method: "get",
      }).then(({ data }) => {
        this.menus = data.data;
        console.log("Output corresponding data");
        console.log(data);
      });
    },
  },
  //Lifecycle - creation complete (you can access the current instance of this)
  created() {
      this.getMenus();
  },
  //Lifecycle - Mount complete (DOM elements can be accessed)
  mounted() {},
  beforeCreate() {}, //Lifecycle - before creating
  beforeMount() {}, //Lifecycle - before mounting
  beforeUpdate() {}, //Lifecycle - before updating
  updated() {}, //Lifecycle - after update
  beforeDestroy() {}, //Life cycle - before destruction
  destroyed() {}, //Life cycle - destruction complete
  activated() {}, //If the page has a keep alive cache function, this function will be triggered
};
</script>
<style>
</style>

attrgroup.vue section

Main part
<el-col :span="6"
        ><div class="grid-content bg-purple">
          <category @tree-node-click="treenodeclick"></category></div
      ></el-col>
      
method part
 //Receive parameters of sub components
     treenodeclick(data,node,component){
         console.log("data",data,"node",node,"component",component);
     }

3. Obtain the corresponding attribute group according to the directory id

thinking

① Write the catelog parameter passed in the list interface of AttrGroupController.

② Judge catelog_ Whether the ID is 0. If it is 0, query all directly. If not, splice the query string, including catalog_ id,group_ ID (EQ) and group_ Name (fuzzy query)

AttrGroupService

@Override
    public PageUtils queryPage(Map<String, Object> params, Long catelogId) {
        //1. Judge whether the category is 0
        if(catelogId==0){
            IPage<AttrGroupEntity> page = this.page(
                    new Query<AttrGroupEntity>().getPage(params),
                    new QueryWrapper<AttrGroupEntity>()
            );
            return new PageUtils(page);
        }else{
            //1.getKey
            String key = (String) params.get("key");
            QueryWrapper<AttrGroupEntity> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("catelog_id",catelogId);
            //2. Air judgment
            if(!StringUtils.isEmpty(key)) {
                //3. If not empty
                queryWrapper.and((item) -> {
                    item.eq("attr_group_id", key).or().like("attr_group_name", key);
                });
            }
            IPage<AttrGroupEntity> page = this.page(
                    new Query<AttrGroupEntity>().getPage(params),
                    queryWrapper
            );
            return new PageUtils(page);
        }
    }

supplement

By supplementing yml, sql statements can be displayed through logs

logging:
  level:
    com.atguigu.gulimall.product: debug

4. Process the display table of the front-end page

thinking

① Add catalog to the url of getDataList_ ID transmission

② If the node with level equal to 3 is clicked, the table will be regenerated. That is, access the list to get data

methods: {
      //Receive parameters of sub components
     treenodeclick(data,node,component){
         console.log("data",data,"node",node,"component",component);
         if(node.level==3){
             //Assign a value to the query id
             this.catId=node.data.catId;
             //Regenerate menu
             this.getDataList();
         }
     }
    ,
    // Get data list
    getDataList() {
      this.dataListLoading = true;
      this.$http({
        url: this.$http.adornUrl(`/product/attrgroup/list/${this.catId}`),
        method: "get",
        params: this.$http.adornParams({
          page: this.pageIndex,
          limit: this.pageSize,
          key: this.dataForm.key,
        }),
      }).then(({ data }) => {
        if (data && data.code === 0) {
          this.dataList = data.page.list;
          this.totalPage = data.page.totalCount;
        } else {
          this.dataList = [];
          this.totalPage = 0;
        }
        this.dataListLoading = false;
      });
    },

3. Group addition

1. Category display under group pop-up box

Idea:

① Replacement of cascade components

② Query menu

③ Replace the props of the cascade. (just imitate category)

④ To solve the problem that chidren is empty, add the jsoninclude attribute to the chidren attribute on the categoryEntity. If it is not empty, it can be assigned

@JsonInclude(JsonInclude.Include.NON_EMPTY)
	@TableField(exist = false)
	private List<CategoryEntity> children;
<el-cascader
          v-model="dataForm.catelogId"
          :options="category"
          :props="props"
        ></el-cascader>

Details: there are three levels of catalog id acquisition, that is, three ids. First obtain it with an ids, and then submit the last one when submitting.

2. Classification path (to be displayed in attribute classification) query

thinking

① Introducing catalogservice in attrGroupController

② After querying the group object, obtain the catalogid and give it to the catalogservice to query the Path

③ Query path through a recursion, if parentId is found= 0, then continue recursion. If not, add your id to the list.

catelogService

  @Override
    public Long[] findCatelogPath(Long catelogId) {
        List<Long> path=new ArrayList<>();
        //1. Query path
        List<Long> paths = findPath(catelogId, path);
        //2. Reverse path
        Collections.reverse(paths);
        return (Long[])paths.toArray(new Long[paths.size()]);
    }

    private List<Long> findPath(Long id,List<Long> path){
        //1. Put it in first
        path.add(id);
        CategoryEntity byId = this.getById(id);
        //2. If the query finds that the parentId is not 0, continue the query
        if(byId.getParentCid()!=0){
            findPath(byId.getParentCid(),path);
        }
        return path;
    }

3. Update catalogpath after dialog is closed. By adding a @ closed method. Finally, add a filterable to castor, that is, search in the category bar.

4. Brand management

1. The paging plug-in is imported and the transaction is started

@Configuration
@EnableTransactionManagement
@MapperScan("com.atguigu.gulimall.product.dao")
public class MybatisConfig {
    @Bean
    public PaginationInterceptor paginationInterceptor(){
        PaginationInterceptor paginationInterceptor=new PaginationInterceptor();

        paginationInterceptor.setOverflow(true);

        paginationInterceptor.setLimit(500);

        return paginationInterceptor;
    }
}

2. Query of brand management conditions

thinking

① The brandService determines that the key is empty. If it is not empty, the connection conditions will be queried

brandService

 @Override
    public PageUtils queryPage(Map<String, Object> params) {

        //1. Get the key to judge

        String key = (String) params.get("key");

        QueryWrapper<BrandEntity> queryWrapper = new QueryWrapper<>();

        //2. Air judgment
        if(!StringUtils.isEmpty(key)){
            queryWrapper.eq("brand_id",key).or().like("name",key);
        }

        IPage<BrandEntity> page = this.page(
                new Query<BrandEntity>().getPage(params),
                queryWrapper
        );

        return new PageUtils(page);
    }

3. Association classification

thinking

① The list of CategoryBrandController is rewritten to obtain data according to brandId

② Save the relationship between brand and category, and obtain the name through their respective id, so as not to associate many tables.

CategoryBrandService

   @Override
    public void saveDeatail(CategoryBrandRelationEntity categoryBrandRelation) {

        //1. Get two IDS
        Long brandId = categoryBrandRelation.getBrandId();
        Long catelogId = categoryBrandRelation.getCatelogId();
        //2. Search two entities according to dao
        BrandEntity brandEntity = brandDao.selectById(brandId);
        CategoryEntity categoryEntity = categoryDao.selectById(catelogId);

        //3.setname
        categoryBrandRelation.setBrandName(brandEntity.getName());
        categoryBrandRelation.setCatelogName(categoryEntity.getName());

        //4. Save

        this.save(categoryBrandRelation);

    }

CategoryBrandController

@RequestMapping(value = "catelog/list",method = RequestMethod.GET)
    //@RequiresPermissions("product:categorybrandrelation:list")
    public R cateloglist(@RequestParam("brandId")Long brandId){
        //Get all objects corresponding to brandId.
        List<CategoryBrandRelationEntity> page = categoryBrandRelationService.list(
                new QueryWrapper<CategoryBrandRelationEntity>().eq("brand_id", brandId)
        );

        return R.ok().put("data", page);
    }

4. Modify the brand name - > brand classification relationship table

Idea:

① The classification controller executes updateDetail, and the name is null. If it is not null, the relationship controller is called to updateBrand

② Then, the relationship controller is used to complete the corresponding update operation - > service. Create an object, set the attribute, and then update updates according to the wrapper

CategoryBrandService

@RequestMapping(value = "catelog/list",method = RequestMethod.GET)
    //@RequiresPermissions("product:categorybrandrelation:list")
    public R cateloglist(@RequestParam("brandId")Long brandId){
        //Get all objects corresponding to brandId.
        List<CategoryBrandRelationEntity> page = categoryBrandRelationService.list(
                new QueryWrapper<CategoryBrandRelationEntity>().eq("brand_id", brandId)
        );

        return R.ok().put("data", page);
    }

BrandService

@Override
    public void updateDetail(BrandEntity brand) {
       String name = brand.getName();
       Long brandId = brand.getBrandId();
        //1. If so, it is not empty
       if(!StringUtils.isEmpty(name)){
           //2. Update the brand name in the relationship
           categoryBrandRelationService.updateBrand(brandId,name);
       }

       this.updateById(brand);
    }

BrandController

 @RequestMapping("/update")
    //@RequiresPermissions("product:brand:update")
    public R update(@Validated(value = UpdateGroup.class)@RequestBody BrandEntity brand){

		brandService.updateDetail(brand);

        return R.ok();
    }

5. Modify the classification name and the relationship table at the same time

thinking

① Similarly, when modifying the category name, call the service of the relationship to modify it at the same time

② The difference this time is that cascade deletion is adopted, and baseMapper is used for modification.

@Override
public void updateDetail(BrandEntity brand) {
String name = brand.getName();
Long brandId = brand.getBrandId();
//1. If so, it is not empty
if(!StringUtils.isEmpty(name)){
//2. Update the brand name in the relationship
categoryBrandRelationService.updateBrand(brandId,name);
}

   this.updateById(brand);
}
BrandController

```java
 @RequestMapping("/update")
    //@RequiresPermissions("product:brand:update")
    public R update(@Validated(value = UpdateGroup.class)@RequestBody BrandEntity brand){

		brandService.updateDetail(brand);

        return R.ok();
    }

5. Modify the classification name and the relationship table at the same time

thinking

① Similarly, when modifying the category name, call the service of the relationship to modify it at the same time

② The difference this time is that cascade deletion is adopted, and baseMapper is used for modification.

Topics: Java JavaEE Maven Spring Tomcat