Javaee's Dark Horse Leyou Mall 2

Briefly analyze the structure of the commodity classification table

First, let’s talk about the relationship between the classification table and the brand table

 Let’s talk about the relationship between the classification table and the brand table and the commodity table

 Now we have to create sql statements at the beginning, here we analyze the fields

The database used is the table heima->tb_category

Now go to the database and create this table

 

 Next, before we go to write an entity class, let's take a look at the request method, request path, request parameters, and return data of this class.

Next, write the entity class

Entities are placed in 

There was a small episode. At the beginning, some modules on the right side of my maven project were grayed out. After I imported dependencies, all annotations could not be used. The solution is as follows

Then import the dependencies again

Let's first complete an entity class of our commodity classification table

 Category.java

package com.leyou.item.pojo;


import lombok.Data;
import tk.mybatis.mapper.annotation.KeySql;

import javax.persistence.Id;
import javax.persistence.Table;

/**
 * Created by Administrator on 2023/8/28.
 */
@Table(name="tb_category")
@Data
public class Category {
    @Id
    @KeySql(useGeneratedKeys = true)
    private Long id;
    private String name;
    private Long parentId;
    private Boolean isParent;
    private Integer sort;
}

Then go to ly-item-service to write specific business logic, such as mapper, service, and web are all in it

Here is a dependency problem

 Introduced the dependency of spring-boot-starter-web, which also includes the core dependency of spring

Let me tell you what our path is when writing this controller class. The path is the url passed by us when we access each interface.

What is the ResponseEntity class for?

 There are two format usages

CollectionUtils tool class

 This is a tool class provided by Spring

We can do the following tests

 Let's paste the code of this CategoryController

package com.leyou.item.web;

import com.leyou.item.pojo.Category;
import com.leyou.item.service.CategoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * Created by Administrator on 2023/8/29.
 */
@RestController
@RequestMapping("/category")
public class CategoryController {

    @Autowired
    private CategoryService categoryService;

    /**
     * 根据父节点的id查询商品分类
     * @param pid
     * @return
     */
    @GetMapping("/list")
    public ResponseEntity<List<Category>> queryCategoryListByPid(@RequestParam("pid")Long pid) {

        try {
            if(pid == null || pid.longValue() < 0) {
                //会返回带着状态码的对象400 参数不合法
               // return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
                //可以做一格优化,下面的类似
                return ResponseEntity.badRequest().build();
            }
            //开始利用service执行查询操作
            List<Category> categoryList = categoryService.queryCategoryListByParentId(pid);
            if(CollectionUtils.isEmpty(categoryList)) {
                //如果结果集为空,响应404
                return ResponseEntity.notFound().build();
            }
            //查询成功,响应200
            return ResponseEntity.ok(categoryList);//这里才真正放了数据
        } catch (Exception e) {
            e.printStackTrace();
        }
        //自定义状态码,然后返回
        //500返回一个服务器内部的错误
        //这里也可以不返回,程序出错,本身就会返回500
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build()
    }


}

Next, we go to Service to create queryCategoryListByParentId method

Take a look at the full code

package com.leyou.item.service;

import com.leyou.item.mapper.CategoryMapper;
import com.leyou.item.pojo.Category;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * Created by Administrator on 2023/8/29.
 */
@Service
public class CategoryService {

    @Autowired
    private CategoryMapper categoryMapper;

    /**
     * 根据父节点的id来查询子结点
     * @param pid
     * @return
     */
    public List<Category> queryCategoryListByParentId(Long pid) {
        Category category = new Category();
        category.setParentId(pid);
        return categoryMapper.select(category);
    }

}

The above is done, now go to the database to operate and insert the data in the classification, similar to the following data

 The following is the data that exists in the data

 Below I start to start:

Our data must go through the gateway

The gateway obviously we can see the data

 But when you click in the project, you can’t get out

 The above is obviously a cross-domain problem

Cross-domain we just do a configuration on the server side

To put it simply, the server will configure the following information for us

We can configure this information with a class on the server here.

Here we use a cors cross-domain filter written by SpringMVC for us: CrosFilter

The specific code is as follows

package com.leyou.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

/**
 * Created by Administrator on 2023/8/31.
 */
@Configuration
public class LeyouCorsConfiguration {
    @Bean
    public CorsFilter corsFilter() {
        //1.添加CORS配置信息
        CorsConfiguration config = new CorsConfiguration();
        //1) 允许的域,不要写*,否则cookie就无法使用了
        config.addAllowedOrigin("http://manage.leyou.com");
        config.addAllowedOrigin("http://www.leyou.com");
        //2) 是否发送Cookie信息
        config.setAllowCredentials(true);
        //3) 允许的请求方式
        config.addAllowedMethod("OPTIONS");
        config.addAllowedMethod("HEAD");
        config.addAllowedMethod("GET");
        config.addAllowedMethod("PUT");
        config.addAllowedMethod("POST");
        config.addAllowedMethod("DELETE");
        config.addAllowedMethod("PATCH");
        // 4)允许的头信息
        config.addAllowedHeader("*");

        //2.添加映射路径,我们拦截一切请求
        UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
        configSource.registerCorsConfiguration("/**", config);

        //3.返回新的CorsFilter.
        return new CorsFilter(configSource);
    }
}

Restart the gateway server

Let's talk about brand query 

 

What we have to do now is to find out the above brands

 We must figure out the request method, request path, request parameters, and response data to determine the return value

Generally speaking, if the page wants to display a list, it must return a List collection object or return a pagination object

We must define a pagination object

Everyone needs to use the paging object later, so we put it in the common

Let’s make this paging object first

package com.leyou.common.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

/**
 * Created by Administrator on 2023/9/2.
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PageResult<T> {
    private Long total;//总条数
    private Integer totalPage;//总页数
    private List<T> items;//当前页面数据对象

    public PageResult(Long total,List<T> items) {
        this.total = total;
        this.items = items;
    }
}

Let's do the front-end page

 Go to this page first, and check where the path of the product is in menu.js

The above is the path of the brand /item/brand, let’s see where the components are

go to this location below

Go to our routing page at this location

 

 

All page components are placed in pages

Let's write the components ourselves here

We define a MyBrand1.vue component ourselves

 

Our page is mainly to make a paging table

You can find it in Vuetify

Here we should find the data that has been paged and sorted from the server

Below we can see the template code inside

 The above is the template code we want to use

The data script is of course you are in the script, you can check it out

<script>
  const desserts = [
    {
      name: 'Frozen Yogurt',
      calories: 159,
      fat: 6.0,
      carbs: 24,
      protein: 4.0,
      iron: '1',
    },
    {
      name: 'Jelly bean',
      calories: 375,
      fat: 0.0,
      carbs: 94,
      protein: 0.0,
      iron: '0',
    },
    {
      name: 'KitKat',
      calories: 518,
      fat: 26.0,
      carbs: 65,
      protein: 7,
      iron: '6',
    },
    {
      name: 'Eclair',
      calories: 262,
      fat: 16.0,
      carbs: 23,
      protein: 6.0,
      iron: '7',
    },
    {
      name: 'Gingerbread',
      calories: 356,
      fat: 16.0,
      carbs: 49,
      protein: 3.9,
      iron: '16',
    },
    {
      name: 'Ice cream sandwich',
      calories: 237,
      fat: 9.0,
      carbs: 37,
      protein: 4.3,
      iron: '1',
    },
    {
      name: 'Lollipop',
      calories: 392,
      fat: 0.2,
      carbs: 98,
      protein: 0,
      iron: '2',
    },
    {
      name: 'Cupcake',
      calories: 305,
      fat: 3.7,
      carbs: 67,
      protein: 4.3,
      iron: '8',
    },
    {
      name: 'Honeycomb',
      calories: 408,
      fat: 3.2,
      carbs: 87,
      protein: 6.5,
      iron: '45',
    },
    {
      name: 'Donut',
      calories: 452,
      fat: 25.0,
      carbs: 51,
      protein: 4.9,
      iron: '22',
    },
  ]

  const FakeAPI = {
    async fetch ({ page, itemsPerPage, sortBy }) {
      return new Promise(resolve => {
        setTimeout(() => {
          const start = (page - 1) * itemsPerPage
          const end = start + itemsPerPage
          const items = desserts.slice()

          if (sortBy.length) {
            const sortKey = sortBy[0].key
            const sortOrder = sortBy[0].order
            items.sort((a, b) => {
              const aValue = a[sortKey]
              const bValue = b[sortKey]
              return sortOrder === 'desc' ? bValue - aValue : aValue - bValue
            })
          }

          const paginated = items.slice(start, end)

          resolve({ items: paginated, total: items.length })
        }, 500)
      })
    },
  }

  export default {
    data: () => ({
      itemsPerPage: 5,
      headers: [
        {
          title: 'Dessert (100g serving)',
          align: 'start',
          sortable: false,
          key: 'name',
        },
        { title: 'Calories', key: 'calories', align: 'end' },
        { title: 'Fat (g)', key: 'fat', align: 'end' },
        { title: 'Carbs (g)', key: 'carbs', align: 'end' },
        { title: 'Protein (g)', key: 'protein', align: 'end' },
        { title: 'Iron (%)', key: 'iron', align: 'end' },
      ],
      serverItems: [],
      loading: true,
      totalItems: 0,
    }),
    methods: {
      loadItems ({ page, itemsPerPage, sortBy }) {
        this.loading = true
        FakeAPI.fetch({ page, itemsPerPage, sortBy }).then(({ items, total }) => {
          this.serverItems = items
          this.totalItems = total
          this.loading = false
        })
      },
    },
  }
</script>

Next, let's take a look at what the brand table shows. Let's take a look at the fields in the database. First, create a product table, and then insert the data into it

Next, we insert the data, similar to inserting the following data

Take a look, it is obvious that the data in this table already exists

We can directly modify the header from the following position

The following directly shows all the codes on the front end of the brand page

<template>
  <v-card>
    <v-card-title>
      <v-btn color="primary" @click="addBrand">新增品牌</v-btn>
      <!--搜索框,与search属性关联-->
      <v-spacer/>
      <v-flex xs3>
        <v-text-field label="输入关键字搜索" v-model.lazy="search" append-icon="search" hide-details/>
      </v-flex>
    </v-card-title>
    <v-divider/>
    <v-data-table
      :headers="headers"
      :items="brands"
      :pagination.sync="pagination"
      :total-items="totalBrands"
      :loading="loading"
      class="elevation-1"
    >
      <template slot="items" slot-scope="props">
        <td class="text-xs-center">{
   
   { props.item.id }}</td>
        <td class="text-xs-center">{
   
   { props.item.name }}</td>
        <td class="text-xs-center">
          <img v-if="props.item.image" :src="props.item.image" width="130" height="40">
          <span v-else>无</span>
        </td>
        <td class="text-xs-center">{
   
   { props.item.letter }}</td>
        <td class="justify-center layout px-0">
          <v-btn flat icon @click="editBrand(props.item)" color="info">
            <i class="el-icon-edit"/>
          </v-btn>
          <v-btn flat icon @click="deleteBrand(props.item)" color="purple">
            <i class="el-icon-delete"/>
          </v-btn>
        </td>
      </template>
    </v-data-table>
    <!--弹出的对话框-->
    <v-dialog max-width="500" v-model="show" persistent scrollable>
      <v-card>
        <!--对话框的标题-->
        <v-toolbar dense dark color="primary">
          <v-toolbar-title>{
   
   {isEdit ? '修改' : '新增'}}品牌</v-toolbar-title>
          <v-spacer/>
          <!--关闭窗口的按钮-->
          <v-btn icon @click="closeWindow"><v-icon>close</v-icon></v-btn>
        </v-toolbar>
        <!--对话框的内容,表单 这里是要把获得的brand 数据传递给子组件,使用自定义标签::oldBrand 而父组件值为oldBrand-->
        <v-card-text class="px-5" style="height:400px">
          <brand-form @close="closeWindow" :oldBrand="oldBrand" :isEdit="isEdit"/>
        </v-card-text>
      </v-card>
    </v-dialog>
  </v-card>
</template>

<script>
  // 导入自定义的表单组件,引入子组件 brandform
  import BrandForm from './BrandForm'

  export default {
    name: "brand",
    data() {
      return {
        search: '', // 搜索过滤字段
        totalBrands: 0, // 总条数
        brands: [], // 当前页品牌数据
        loading: true, // 是否在加载中
        pagination: {}, // 分页信息
        headers: [
          {text: 'id', align: 'center', value: 'id'},
          {text: '名称', align: 'center', sortable: false, value: 'name'},
          {text: 'LOGO', align: 'center', sortable: false, value: 'image'},
          {text: '首字母', align: 'center', value: 'letter', sortable: true,},
          {text: '操作', align: 'center', value: 'id', sortable: false}
        ],
        show: false,// 控制对话框的显示
        oldBrand: {}, // 即将被编辑的品牌数据
        isEdit: false, // 是否是编辑
      }
    },
    mounted() { // 渲染后执行
      // 查询数据--搜索后页面还处在第几页,只要搜索,页面渲染后重新查询
      this.getDataFromServer();
    },
    watch: {
      pagination: { // 监视pagination属性的变化
        deep: true, // deep为true,会监视pagination的属性及属性中的对象属性变化
        handler() {
          // 变化后的回调函数,这里我们再次调用getDataFromServer即可
          this.getDataFromServer();
        }
      },
      search: { // 监视搜索字段
        handler() {
          this.pagination.page =1;
          this.getDataFromServer();
        }
      }
    },
    methods: {
      getDataFromServer() { // 从服务的加载数的方法。
        // 发起请求
        this.$http.get("/item/brand/page", {
          params: {
            key: this.search, // 搜索条件
            page: this.pagination.page,// 当前页
            rows: this.pagination.rowsPerPage,// 每页大小
            sortBy: this.pagination.sortBy,// 排序字段
            desc: this.pagination.descending// 是否降序
          }
        }).then(resp => { // 这里使用箭头函数
          this.brands = resp.data.items;
          this.totalBrands = resp.data.total;
          // 完成赋值后,把加载状态赋值为false
          this.loading = false;
          //
        })
      },
      addBrand() {
        // 修改标记,新增前修改为false
        this.isEdit = false;
        // 控制弹窗可见:
        this.show = true;
        // 把oldBrand变为null,因为之前打开过修改窗口,oldBrand数据被带过来了,导致新增
        this.oldBrand = null;
      },
      editBrand(oldBrand){
        //test 使用
        //this.show = true;
        //获取要编辑的brand
        //this.oldBrand = oldBrand;
        //requestParam,相当于把http,url ?name=zhangsan&age=21 传给方法
        //pathvarable 相当与把url www.emporium.com/1/2 传给方法
        //如果不需要url上的参数controller不需要绑定数据
        // 根据品牌信息查询商品分类, 因为前台页面请求是拼接的, data 类似于jquery 里面回显的数据
        this.$http.get("/item/category/bid/" + oldBrand.id)
          .then(({data}) => {
            // 修改标记
            this.isEdit = true;
            // 控制弹窗可见:
            this.show = true;
            // 获取要编辑的brand
            this.oldBrand = oldBrand
            // 回显商品分类
            this.oldBrand.categories = data;
          })
      },
      closeWindow(){
        // 重新加载数据
        this.getDataFromServer();
        // 关闭窗口
        this.show = false;
      }
    },
    components:{
        BrandForm
    }
  }
</script>

<style scoped>

</style>

 

Let's start writing the background logic

 

Start writing the background, first write a product category

 Brand.java

package com.leyou.item.pojo;

import lombok.Data;

import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

/**
 * Created by Administrator on 2023/9/2.
 */
@Data
@Table(name="tb_brand")
public class Brand {
    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private Long id;
    private String name;//品牌名称
    private String image;//品牌图片
    private Character letter;
}

Next we write our general Mapper class

Let's write the service interface

 Let's write the Controller class

analyze

What is returned: the data of the current page (list collection) and the total number of items 

That is, the above returns the following paging object. In the ly-common module, if we need to use the object of this module, then we need to import this module as a dependency into another module.

Here is the module ly-item-service under ly-item that needs to use the PageResult object

The following is the code in the Controller

Let's complete the method in Service

package com.leyou.item.service;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.leyou.common.pojo.PageResult;
import com.leyou.item.mapper.BrandMapper;
import com.leyou.item.pojo.Brand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import tk.mybatis.mapper.entity.Example;

/**
 * Created by Administrator on 2023/9/2.
 */
@Service
public class BrandService {

    //内部需要一个mapper调用
    @Autowired
    private BrandMapper brandMapper;

    /**
     *
     * @param page 当前页
     * @param rows 每页大小
     * @param sortBy 排序字段
     * @param desc 是否降序
     * @param key 搜索关键字
     * @return
     */
    public PageResult<Brand> queryBrandByPageAndSort(Integer page,Integer rows, String sortBy, Boolean desc, String key) {
        //开启分页
        //这个会自动拼接到后面的sql语句上面
        PageHelper.startPage(page,rows);//传进来一个页码和展示多少行的数据,
        //过滤
        Example example = new Example(Brand.class);
        if(key != null && !"".equals(key)) {
            //进来有一模糊查询
            //把这个语句拼接上
            example.createCriteria().andLike("name","%" + key + "%").orEqualTo("letter",key);
        }
        if(sortBy != null && !"".equals(sortBy)) {
            //根据sortBy字段进行排序
            String orderByClause = sortBy + (desc ? " DESC " : " ASC ");
            example.setOrderByClause(orderByClause);
        }
        //利用通用mapper进行查询
        Page<Brand> pageInfo = (Page<Brand>) brandMapper.selectByExample(example);
        //返回结果
        //这里面传递总条数和页面信息
        return new PageResult<>(pageInfo.getTotal(),pageInfo);
    }
}

Let me tell you, when using Autowired to inject Mapper, it prompts that it cannot be injected, and it becomes popular

 

Guess you like

Origin blog.csdn.net/Pxx520Tangtian/article/details/132537421