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"); }