Java project practice - Ruiji takeout Day04

Ruiji takeout development note 4

The content of the notes is the video content of the dark horse programmer

Dish management business development

File upload and download

Introduction to file upload

File upload, also known as upload, refers to the process of uploading local pictures, videos, audio and other files to the server for other users to browse or download. File upload is widely used in the project. We often send microblogs and wechat, and the file upload function is used in our circle of friends.

When uploading files, the form of the page has the following requirements:

  • method="post" submit data by post
  • Enctype = "multipart / form data" upload files in multipart format
  • type="file" upload using the file control of input

At present, some front-end component libraries also provide corresponding upload components, but the underlying principle is file upload based on form. For example, the upload component provided in ElementUI:

To receive the files uploaded from the client page, the server usually uses two components of Apache:

  • commons-fileupload
  • commons-io

The Spring framework encapsulates the file upload in the Spring web package, which greatly simplifies the server code. We only need to declare a MultipartFile type parameter in the Controller method to receive the uploaded file.

File download introduction

File download, also known as download, refers to the process of transferring files from the server to the local computer.
There are usually two forms of file downloading through the browser:

  • Download as an attachment, and a save dialog box will pop up to save the file to the specified disk directory
  • Open directly in browser

Downloading files through the browser is essentially the process that the server writes files back to the browser in the form of stream.

File upload code implementation

For file upload, the page side can use the upload component provided by ElementuI.
You can directly use the upload page provided in the data. Location: data / file upload and download page / upload html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>File upload</title>
  <!-- Import style -->
  <link rel="stylesheet" href="../../plugins/element-ui/index.css" />
  <link rel="stylesheet" href="../../styles/common.css" />
  <link rel="stylesheet" href="../../styles/page.css" />
    <link rel="shortcut icon" href="../../favicon.ico">
</head>
<body>
   <div class="addBrand-container" id="food-add-app">
    <div class="container">
        <el-upload class="avatar-uploader"
                action="/common/upload"
                :show-file-list="false"
                :on-success="handleAvatarSuccess"
                :before-upload="beforeUpload"
                ref="upload">
            <img v-if="imageUrl" :src="imageUrl" class="avatar"></img>
            <i v-else class="el-icon-plus avatar-uploader-icon"></i>
        </el-upload>
    </div>
  </div>
    <!-- Development environment version with helpful command line warnings -->
    <script src="../../plugins/vue/vue.js"></script>
    <!-- Import component library -->
    <script src="../../plugins/element-ui/index.js"></script>
    <!-- introduce axios -->
    <script src="../../plugins/axios/axios.min.js"></script>
    <script src="../../js/index.js"></script>
    <script>
      new Vue({
        el: '#food-add-app',
        data() {
          return {
            imageUrl: ''
          }
        },
        methods: {
          handleAvatarSuccess (response, file, fileList) {
              this.imageUrl = `/common/download?name=${response.data}`
          },
          beforeUpload (file) {
            if(file){
              const suffix = file.name.split('.')[1]
              const size = file.size / 1024 / 1024 < 2
              if(['png','jpeg','jpg'].indexOf(suffix) < 0){
                this.$message.error('Uploading pictures only supports png,jpeg,jpg Format!')
                this.$refs.upload.clearFiles()
                return false
              }
              if(!size){
                this.$message.error('Upload file size cannot exceed 2 MB!')
                return false
              }
              return file
            }
          }
        }
      })
    </script>
</body>
</html>

Add CommonController to upload and download files

@Slf4j
@RestController
@RequestMapping("/common")
public class CommonController {

    //File upload
    @PostMapping("/upload")
    public R<String> upload(MultipartFile file){
        //File is a temporary file and needs to be transferred to the specified location. Otherwise, the temporary file will be deleted after the request is completed
        log.info("file:{}",file.toString());
        return null;
    }
}

The file variable defined by MultipartFile must be consistent with name

Complete code

@Slf4j
@RestController
@RequestMapping("/common")
public class CommonController {
    @Value("${reggie.path}")
    private String basePath;

    //File upload
    @PostMapping("/upload")
    public R<String> upload(MultipartFile file){
        //File is a temporary file and needs to be transferred to the specified location. Otherwise, the temporary file will be deleted after the request is completed
        //log.info("file:{}",file.toString());

        //original filename
        String originalFilename = file.getOriginalFilename();
        String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
        //Use UUID to randomly generate file names to prevent file overwriting due to the same file names
        String fileName = UUID.randomUUID().toString()+suffix;

        //Create a directory object
        File dir = new File(basePath);
        //Judge whether the current directory exists
        if(!dir.exists()){
            //directory does not exist
            dir.mkdirs();
        }

        try {
            //Transfer temporary files to the specified location
            file.transferTo(new File(basePath+fileName));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return R.success(fileName);
    }
}

File download code implementation

For file download, the label can be used at the page end to display the downloaded pictures

//File download
@GetMapping("/download")
public void download(String name, HttpServletResponse response){
    try {
        //Input stream, which reads the contents of the file
        FileInputStream fileInputStream=new FileInputStream(new File(basePath+name));
        //Output stream, write the file back to the browser through the output stream, and display the picture in the browser
        ServletOutputStream outputStream = response.getOutputStream();

        int len=0;
        byte[] bytes = new byte[1024];
        while ((len=fileInputStream.read(bytes))!=-1){
            outputStream.write(bytes,0,len);
            outputStream.flush();
        }
        outputStream.close();
        fileInputStream.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

New dishes

requirement analysis

The background system can manage the dish information and add a new dish through new functions. When adding dishes, you need to select the dish classification of the current dish and upload the dish picture. The mobile terminal will display the corresponding dish information according to the dish classification.

data model

Adding a new dish is actually inserting the dish information entered on the new page into the dish table. If you add a taste method, you need to add it to the dish table_ Insert data into the flavor table. Therefore, when adding dishes, two tables are involved:

  • Dish (menu)

  • dish_ Flavor (table of dishes)

Code development - Preparation

Before developing business functions, create the basic structure of classes and interfaces to be used:

  • Entity class dishflavor (you can import it directly from the course materials. Dish entity has been imported in the previous courses)
@Data
public class DishFlavor implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;


    //Dish id
    private Long dishId;


    //Taste name
    private String name;


    //Taste data list
    private String value;


    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;


    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;


    @TableField(fill = FieldFill.INSERT)
    private Long createUser;


    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long updateUser;


    //Delete
    private Integer isDeleted;
}
  • Mapper interface DishFlavorMapper
@Mapper
public interface DishFlavorMapper extends BaseMapper<DishFlavor> {
}
  • Business layer interface DishFlavorService
public interface DishFlavorService extends IService<DishFlavor> {
}
  • Business layer implementation class DishFlavorServicelmpl
@Service
public class DishFlavorServiceImpl extends ServiceImpl<DishFlavorMapper, DishFlavor>implements DishFlavorService {
}
  • Control layer DishController
@RestController
@RequestMapping("/dish")
public class DishController {
    @Autowired
    private DishService dishService;
    @Autowired
    private DishFlavorService dishFlavorService;
}

Code development - combing the interactive process

Before developing the code, you need to sort out the interaction process between the front-end page and the server when adding dishes:

1. The page (backend/page/food/add.html) sends an ajax request to request the server to obtain the dish classification data and display it in the drop-down box

2. The page sends a request to upload the picture, and requests the server to save the picture to the server

3. The page sends a request to download the picture and echo the uploaded picture

4. Click the Save button to send an ajax request and submit the dish related data to the server in json form

To develop the new dish function is to write code on the server to process the four requests sent by the front-end page.

Menu category drop-down box: add in CategoryController

//Query classified data according to criteria
@GetMapping("/list")
public R<List<Category>> list(Category category){
    //Conditional constructor
    LambdaQueryWrapper<Category> lambdaQueryWrapper=new LambdaQueryWrapper<>();
    //Add condition
    lambdaQueryWrapper.eq(category.getType()!=null,Category::getType,category.getType());
    //Add sort criteria
    lambdaQueryWrapper.orderByAsc(Category::getSort).orderByAsc(Category::getUpdateTime);
    List<Category> list = categoryService.list(lambdaQueryWrapper);
    return R.success(list);
}

Import DishDto (Location: data / dto) to encapsulate the data submitted by the page

@Data
public class DishDto extends Dish {

    private List<DishFlavor> flavors = new ArrayList<>();

    private String categoryName;

    private Integer copies;
}

Note: DTO, the full name of which is Data Transfer object, is generally used for data transmission between presentation layer and service layer.

When adding new dishes and inserting the taste data corresponding to the dishes, you need to operate two tables: dish and dishflavor

Add the method saveWithFlavor in the DishService interface and implement it in DishServiceImpl

@Service
public class DishServiceImpl extends ServiceImpl<DishMapper, Dish> implements DishService {
    @Autowired
    private DishFlavorService dishFlavorService;

    @Override
    @Transactional
    public void saveWithFlavor(DishDto dishDto) {
        //Save the basic information of dishes to dish table
        this.save(dishDto);

        Long dishid = dishDto.getId();
        //Dish taste
        List<DishFlavor> flavors = dishDto.getFlavors();
        flavors = flavors.stream().map((item) -> {
            item.setDishId(dishid);
            return item;
        }).collect(Collectors.toList());
        //dishFlavorService.saveBatch(dishDto.getFlavors());
        //Save the dish taste to dish data table dish_flavor
        dishFlavorService.saveBatch(flavors);
    }
}

Since the above code involves multi table operation, the @ EnableTransactionManagement annotation can be added when starting a transaction on the startup class. However, if I add this annotation, an error will be reported, the project startup will fail, and the annotation of springboot should be opened by default, so it is not added

New dishes

@PostMapping
public R<String> save(@RequestBody DishDto dishDto){
    dishService.saveWithFlavor(dishDto);
    return R.success("Added dishes successfully");
}

Pagination query of dish information

requirement analysis

When there are a lot of dish data in the system, if all of them are displayed on one page, it will appear messy and inconvenient to view, so the list data will be displayed in a paginated way in the general system.

Code development - combing interaction process

Before developing the code, you need to sort out the interaction process between the front-end page and the server when querying dishes by page:

1. The page (backend/page/food/list.html) sends an ajax request and submits the paging query parameters (page, pageSize, name) to the server to obtain paging data

2. Send a request to the page and request the server to download the image for page image display

To develop the paging query function of dish information is to write code on the server to process the two requests sent by the front-end page.

@GetMapping("/page")
public R<Page> page(int page, int pageSize, String name) {
  //Construct paging constructor
  Page<Dish> pageInfo = new Page<>(page, pageSize);

  Page<DishDto> dishDtoPage = new Page<>();

  //Construct conditional constructor
  LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>();

  //Add filter condition
  queryWrapper.like(!StringUtils.isEmpty(name), Dish::getName, name);

  //Add sort criteria
  queryWrapper.orderByDesc(Dish::getUpdateTime);

  //Paging query
  dishService.page(pageInfo, queryWrapper);

  //Object Copy 
  BeanUtils.copyProperties(pageInfo,dishDtoPage,"records");

  List<Dish> records = pageInfo.getRecords();
  List<DishDto> list=records.stream().map((item)->{
    DishDto dishDto=new DishDto();

    BeanUtils.copyProperties(item,dishDto);
    Long categoryId = item.getCategoryId();
    //Query classified objects by id
    Category category = categoryService.getById(categoryId);
    if(category!=null){
      String categoryName = category.getName();
      dishDto.setCategoryName(categoryName);
    }
    return dishDto;
  }).collect(Collectors.toList());

  dishDtoPage.setRecords(list);

  return R.success(dishDtoPage);
}

Modify dishes

requirement analysis

Click the Modify button on the menu management page to modify the menu information, and finally click the Modify button to go back to the menu management page

Code development - combing the interactive process

Before developing the code, you need to sort out the interaction process between the front-end page (add.html) and the server when modifying the dishes:

1. The page sends an ajax request to request the server to obtain the classification data for the data display in the dish classification drop-down box

2. The page sends an ajax request to the server to query the current dish information according to the id for the echo of the dish information

  • DishController handles Get requests
//Query the dish information and the corresponding taste information according to the Id
@GetMapping("/{id}")
public R<DishDto> getById(@PathVariable Long id){
    DishDto dishDto = dishService.getByIdWithFlavor(id);
    return R.success(dishDto);
}
  • Add the getByIdWithFlavor method in DishServiceImpl
@Override
@Transactional
public DishDto getByIdWithFlavor(Long id) {
    //Query basic information of dishes
    Dish dish = this.getById(id);

    DishDto dishDto=new DishDto();
    BeanUtils.copyProperties(dish,dishDto);

    //Query dish taste information
    LambdaQueryWrapper<DishFlavor> queryWrapper=new LambdaQueryWrapper<>();
    queryWrapper.eq(DishFlavor::getDishId,dish.getId());
    List<DishFlavor> list = dishFlavorService.list(queryWrapper);

    dishDto.setFlavors(list);

    return dishDto;
}

3. Send a request to the page and request the server to download the picture for the page picture echo

4. Click the Save button, and the page sends an ajax request to submit the modified dish related data to the server in the form of json

  • Add put method in DishController
//Modify dishes
@PutMapping
public R<String> update(@RequestBody DishDto dishDto){
    dishService.updateWithFlavor(dishDto);
    return R.success("Dishes modified successfully");
}
  • Add updateWithFlavor method in DishServiceImpl
@Override
public void updateWithFlavor(DishDto dishDto) {
    //Update basic information of dish table
    this.updateById(dishDto);

    //Update dish_ delete operation of flavor table information
    LambdaQueryWrapper<DishFlavor> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.eq(DishFlavor::getDishId, dishDto.getId());
    dishFlavorService.remove(queryWrapper);

    //Update dish_ insert operation of flavor table information
    List<DishFlavor> flavors = dishDto.getFlavors();

    flavors = flavors.stream().map((item) -> {
        item.setDishId(dishDto.getId());
        return item;
    }).collect(Collectors.toList());

    dishFlavorService.saveBatch(flavors);
}

The function of developing and modifying dishes is actually to write code on the server to process the four requests sent by the front-end page.

Stop selling / start selling dishes, delete dishes

requirement analysis

In the process of commodity sale, the goods stop selling, and the start-up sale can make it more convenient for users to know what kind of goods the store is selling. The deletion method is also more convenient to manage dishes

code implementation

Add the sale method and delete method in DishController, save ids through the array, and batch start, stop and delete can take effect

//Stop selling and start selling dishes
@PostMapping("/status/{status}")
public R<String> sale(@PathVariable int status,
                      String[] ids){
    for(String id: ids){
        Dish dish = dishService.getById(id);
        dish.setStatus(status);
        dishService.updateById(dish);
    }
    return R.success("Modified successfully");
}
//Delete dishes
@DeleteMapping
public R<String> delete(String[] ids){
    for (String id:ids) {
        dishService.removeById(id);
    }
    return R.success("Delete succeeded");
}

Posted by bernouli on Fri, 13 May 2022 14:39:52 +0300