1. Complete commodity classification business
1.1 page JS analysis
- Life cycle function
//Define initialization function
created() {
//Get commodity classification list data by default
this.findItemCatList()
},
- Get data function description
async findItemCatList() {
const {
data: result
} = await this.$http.get("/itemCat/findItemCatList/3")
if (result.status !== 200) return this.$message.error("Failed to get commodity classification list!!")
this.itemCatList = result.data
},
1.2 business interface documents
- Request path: / itemCat/findItemCatList/{level}
- Request type: get
- Request parameter: level
Parameter name | Parameter description | remarks |
---|
level | Query level | 1 query level 1 Classification 2 query level 1-2 commodity classification 3 query level 1-2-3 commodity classification |
- Business description: querying Level 3 Classification menu data requires a three-tier nested structure
- Return value: SysResult object
Parameter name | Parameter description | remarks |
---|
status | status information | 200 indicates that the server request is successful, and 201 indicates that the server is abnormal |
msg | Prompt information returned by the server | Can be null |
data | Business data returned by the server | Level 3 commodity classification information |
1.3 structure description of commodity classification table
-
Table structure
-
sql case exercise
/*All first level menus parent_id=0*/
SELECT * FROM item_cat WHERE parent_id = 0
/*Query the secondary menu of automobile users*/
SELECT * FROM item_cat WHERE parent_id = 249
/*Query the three-level menu of on-board electrical appliances*/
SELECT * FROM item_cat WHERE parent_id = 281
Summary: commodity classification table, through parent_id to specify the parent - child relationship
1.4 edit ItemCatController
@RestController
@CrossOrigin
@RequestMapping("/itemCat")
public class ItemCatController {
@Autowired
private ItemCatService itemCatService;
/**
* Demand: query commodity classification information
* Parameters: / {level} 1 level 1 2 level 1 2 Level 3 level 1 2 Level 3
* url: /itemCat/findItemCatList/{level} restFul
* Return value: sysresult (Level 3 list information)
*/
@GetMapping("findItemCatList/{level}")
public SysResult findItemCatList(@PathVariable Integer level){
List<ItemCat> itemCatList = itemCatService.findItemCatList(level);
return SysResult.success(itemCatList);
}
}
1.5 edit ItemCatService
package com.jt.service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.jt.mapper.ItemCatMapper;
import com.jt.pojo.ItemCat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class ItemCatServiceImpl implements ItemCatService{
@Autowired
private ItemCatMapper itemCatMapper;
/**
* Step 1. Query the first level menu list
* @param level
* @return
*/
@Override
public List<ItemCat> findItemCatList(Integer level) {
//1. Query level-1 menu
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.eq("parent_id",0);
List<ItemCat> oneList = itemCatMapper.selectList(queryWrapper);
//2. Query secondary menu secondary data is the sub level of primary data encapsulated in primary data
for(ItemCat oneItemCat : oneList){
int oneId = oneItemCat.getId(); //Primary object ID
//Empty original condition must have
queryWrapper.clear();
queryWrapper.eq("parent_id",oneId);
List<ItemCat> twoList = itemCatMapper.selectList(queryWrapper);
for(ItemCat twoItemCat : twoList){
//Get secondary classification ID
int twoId = twoItemCat.getId();
//Query Level 3 list information
queryWrapper.clear();
queryWrapper.eq("parent_id",twoId);
List<ItemCat> threeList = itemCatMapper.selectList(queryWrapper);
//Encapsulate a tertiary list into a secondary object
twoItemCat.setChildren(threeList);
}
//Encapsulate secondary data into primary objects
oneItemCat.setChildren(twoList);
}
return oneList;
}
}
1.6 above case analysis
- The above case adopts a multi-level cycle. In the future, it will consume 100 server resources, 100 inner layers and 10000 cycles in total. It is acceptable for the time being
- The above code frequently accesses the database, resulting in increased database pressure. In severe cases, it may lead to database server downtime. Unacceptable
Optimization strategy: reduce the number of database accesses
1.7 data structure optimization code
Idea:
- Users query all database information. (1-2-3 all data)
- The data structure map < K, V > key is unique, and value can be of any type
Idea: Map < parentid, list >
example: - Map < 0, list < first level ItemCat Object > >
- Map < 249, list < secondary ItemCat Object > >
- Map < 281, list < Level 3 ItemCat Object > >
map is used to encapsulate parent - child relationship
1.8 code implementation
@Service
public class ItemCatServiceImpl implements ItemCatService{
@Autowired
private ItemCatMapper itemCatMapper;
/**
* Encapsulate all database records with Map collection
* Encapsulated data:
* 1.Traverse all data information
* 2.Get the value of each parentId
* example:
* 1.{id=1,parentId=0,name="Zhang San "}
* 2.{id=2,parentId=0,name="Li Si "}
* 3.{id=3,parentId=1,name="Wang Wu "}
* Map= {
* key : value
* 0 : List[Zhang San object, Li Si object...],
* 1 : List[Wang Wu...]
* }
* @return
*/
public Map<Integer,List<ItemCat>> getMap(){
Map<Integer,List<ItemCat>> map = new HashMap<>();
//1. Query all database information
List<ItemCat> itemCatList = itemCatMapper.selectList(null);
//2. Encapsulate the data into the map set
for (ItemCat itemCat : itemCatList){
Integer key = itemCat.getParentId(); //Get parentId as key
//3. Judge whether there is a value in the map set
if(map.containsKey(key)){
//With value: get the List collection and append yourself to it
map.get(key).add(itemCat);
}else{
//No value: add data. Fill yourself as the first element
List<ItemCat> list = new ArrayList<>();
list.add(itemCat);
map.put(key,list);
}
}
return map;
}
@Override
public List<ItemCat> findItemCatList(Integer level) {
long startTime = System.currentTimeMillis();
Map<Integer,List<ItemCat>> map = getMap();
//Obtain child information according to level
if(level == 1){ //Get only the first level list information
return map.get(0);
}
if(level == 2){ //Obtain primary and secondary data
return getTwoList(map);
}
List<ItemCat> oneList = getThreeList(map);
long endTime = System.currentTimeMillis();
System.out.println("Time before optimization: 500ms,Time consuming after optimization:"+(endTime - startTime)+"ms");
return oneList;
}
//To obtain the three-level list information, first obtain the level 1 data, then obtain the level 2 data, and then obtain the level 3 data
private List<ItemCat> getThreeList(Map<Integer, List<ItemCat>> map) {
//1. Call the level 2 menu method
List<ItemCat> oneList = getTwoList(map);
//2. The implementation idea traverses the primary set, obtains secondary data, and encapsulates the tertiary menu
for(ItemCat oneItemCat : oneList){
//2.1 obtaining secondary data
List<ItemCat> twoList = oneItemCat.getChildren();
if(twoList == null || twoList.size()==0){
//Judge whether the secondary collection is null. If it is null, it means there is no secondary menu
continue;
}
for (ItemCat twoItemCat : twoList){
int twoId = twoItemCat.getId();
List<ItemCat> threeList = map.get(twoId);
twoItemCat.setChildren(threeList);
}
}
return oneList;
}
//Get the first and second level menu information through the map set
private List<ItemCat> getTwoList(Map<Integer, List<ItemCat>> map) {
List<ItemCat> oneList = map.get(0);
//To obtain secondary information, you should first traverse the primary collection
for (ItemCat oneItemCat : oneList){
int oneId = oneItemCat.getId();
//Get the secondary set according to the primary Id
List<ItemCat> twoList = map.get(oneId);
oneItemCat.setChildren(twoList);
}
return oneList;
}
/**
* Step 1. Query the first level menu list
* @param level
* @return
*/
/* @Override
public List<ItemCat> findItemCatList(Integer level) {
long startTime = System.currentTimeMillis();
//1.Query level 1 menu
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.eq("parent_id",0);
List<ItemCat> oneList = itemCatMapper.selectList(queryWrapper);
//2.Query Level 2 menu level 2 data is the sub level of level 1 data encapsulated in level 1 data
for(ItemCat oneItemCat : oneList){
int oneId = oneItemCat.getId(); //Primary object ID
//Empty original condition must have
queryWrapper.clear();
queryWrapper.eq("parent_id",oneId);
List<ItemCat> twoList = itemCatMapper.selectList(queryWrapper);
for(ItemCat twoItemCat : twoList){
//Get secondary classification ID
int twoId = twoItemCat.getId();
//Query Level 3 list information
queryWrapper.clear();
queryWrapper.eq("parent_id",twoId);
List<ItemCat> threeList = itemCatMapper.selectList(queryWrapper);
//Encapsulate a tertiary list into a secondary object
twoItemCat.setChildren(threeList);
}
//Encapsulate secondary data into primary objects
oneItemCat.setChildren(twoList);
}
long endTime = System.currentTimeMillis();
System.out.println("Time consuming: "+ (endTime - startTime)+"ms ");
return oneList;
}*/
}
2. Implementation of adding commodity classification
2.1 page JS analysis
- Page JS analysis
//Define new objects for commodity classification
itemCatForm: {
name: '', //Define commodity classification name
parentId: 0, //Default parent ID=0
level: 1 //The default is the first level menu
},
async addItemCatForm() {
//Verify the whole form first
this.$refs.itemCatFormRef.validate(async validate => {
if (!validate) return
const {
data: result
} = await this.$http.post("/itemCat/saveItemCat", this.itemCatForm)
if (result.status !== 200) return this.$message.error("Failed to add commodity classification")
this.$message.success("Successfully added commodity classification!!!")
//If the addition is successful, the classification list information will be refreshed
this.findItemCatList();
this.addItemCatDialogVisible = false
})
},
2.2 new interface document for commodity classification
- Request path: / itemCat/saveItemCat
- Request type: post
- Request parameter: form data
Parameter name | Parameter description | remarks |
---|
name | Commodity classification name | Cannot be null |
parentId | User parent ID | Cannot be null |
level | Classification level | 1 2 3 commodity classification level |
- Return value: SysResult object
Parameter name | Parameter description | remarks |
---|
status | status information | 200 indicates that the server request is successful, and 201 indicates that the server is abnormal |
msg | Prompt information returned by the server | Can be null |
data | Business data returned by the server | Can be null |
2.3 editing ItemCatController
/**
* Business requirements: add a new commodity classification
* URL: /itemCat/saveItemCat
* Type: post
* Parameters: {name ":" AAAAA "," parentid ": 0," level ": 1} JSON string
* Return value: SysResult object
*/
@PostMapping("/saveItemCat")
public SysResult saveItem(@RequestBody ItemCat itemCat){
itemCatService.saveItem(itemCat);
return SysResult.success();
}
2.4 edit ItemCatService
@Override
@Transactional //Transaction control
public void saveItem(ItemCat itemCat) {
Date date = new Date();
itemCat.setStatus(true).setCreated(date).setUpdated(date);//start-up
itemCatMapper.insert(itemCat);
}
3. Delete commodity classification
3.1 deleting a business interface
- Request path: / itemCat/deleteItemCat
- Request type: delete
- Business description: when the deleted node is the parent, it should delete itself and all child nodes
- Request parameters:
Parameter name | Parameter description | remarks |
---|
ID | User ID number | Cannot be null |
level | Commodity classification level: Level I, level II and level III | |
- Return value result SysResult object
Parameter name | Parameter description | remarks |
---|
status | status information | 200 indicates that the server request is successful, and 201 indicates that the server is abnormal |
msg | Prompt information returned by the server | Can be null |
data | Business data returned by the server | Can be null |
3.2 front end page JS
1. page JS
<el-button type="danger" icon="el-icon-delete" @click="deleteItemCatBtn(scope.row)">delete</el-button>
2. launch Ajax request
//Pass classification id
const {data: result} = await this.$http.delete("/itemCat/deleteItemCat",{params:{id:itemCat.id,level:itemCat.level}})
if(result.status !== 200) return this.$message.error("Failed to delete commodity classification")
this.$message.success("Data deleted successfully")
//After the deletion is successful, refresh the page data
this.findItemCatList()
3.3 editing ItemCatController
/**
* Finish deleting the commodity classification
* 1. Edit URL: /itemCat/deleteItemCat
* 2. Parameter: id/level
* 3. Return value: SysResult()
*/
@DeleteMapping("/deleteItemCat")
public SysResult deleteItemCat(Integer id,Integer level){
itemCatService.deleteItemCat(id,level);
return SysResult.success();
}
3.4 editing ItemCatService
//Delete commodity classification data
@Override
public void deleteItemCat(Integer id, Integer level) {
//Judge whether it is a level 3 menu
if(level == 3){
itemCatMapper.deleteById(id);
}
if(level == 2){
//If it is level 2, you should first obtain level 3 data, then delete it, and then delete yourself
//delete from item_cat where parent_id=#{id} or id = #{id}
QueryWrapper<ItemCat> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("parent_id",id)
.or()
.eq("id",id);
itemCatMapper.delete(queryWrapper);
}
/**
* How to delete the first level menu?
* 1.Get secondary ID
* Ultimate sql: delete from item_cat where parent_id in (twoIds)
* or id in (twoIds)
* or id = #{id}
*/
if(level == 1){
QueryWrapper<ItemCat> queryWrapper = new QueryWrapper();
queryWrapper.eq("parent_id",id);
List twoIds = itemCatMapper.selectObjs(queryWrapper);
//wipe data
queryWrapper.clear();
//Rule: if the level 2 menu has a value, level 2 and level 3 will be deleted
queryWrapper.in(twoIds.size()>0,"parent_id",twoIds)
.or()
.in(twoIds.size()>0,"id",twoIds)
.or()
.eq("id",id);
itemCatMapper.delete(queryWrapper);
}
}
4. Modify commodity classification
4.1 status modification
4.1.1 page JS analysis
1. page JS function
updateStatus(scope.row)
2. page JS
//Modify status information according to ID
async updateStatus(itemCat) {
const {
data: result
} = await this.$http.put(`/itemCat/status/${itemCat.id}/${itemCat.status}`)
if (result.status !== 200) return this.$message.error("Failed to modify status")
this.$message.success("Status modified successfully")
},
4.1.2 business interface documents
- Request path: / itemCat/status/{id}/{status}
- Request type: put
- Request parameters:
Parameter name | Parameter description | remarks |
---|
ID | User ID number | Cannot be null |
status | User status information | Cannot be null |
- Return value: SysResult object
Parameter name | Parameter description | remarks |
---|
status | status information | 200 indicates that the server request is successful, and 201 indicates that the server is abnormal |
msg | Prompt information returned by the server | Can be null |
data | Business data returned by the server | Can be null |
4.1.3 editing ItemCatController
/**
* Modify commodity classification status
* URL: /itemCat/status/{id}/{status}
* Request type: PUT/POST
* Request parameter: id/status
* Return value: SysResult object
*/
@PutMapping("/status/{id}/{status}")
public SysResult updateStatus(ItemCat itemCat){
itemCatService.updateStatus(itemCat);
return SysResult.success();
}
4.1.4 edit ItemCatService
@Override
@Transactional
public void updateStatus(ItemCat itemCat) {//id/status
itemCat.setUpdated(new Date());
itemCatMapper.updateById(itemCat);
}
4.2 modifying commodity classification
4.2.1 page JS analysis
1.Specifies the button to modify
<el-button type="success" icon="el-icon-edit" @click="updateItemCatBtn(scope.row)">edit</el-button>
2. Echo of data
//Due to the hierarchical relationship, all modifications can only modify the name
updateItemCatBtn(itemCat) {
this.updateItemCatForm = itemCat
this.updateItemCatDialogVisible = true
},
3. Modify page JS
<el-dialog title="Modify commodity classification" :visible.sync="updateItemCatDialogVisible" width="50%">
<!-- Define classification form -->
<el-form :model="updateItemCatForm" :rules="rules" ref="upDateItemCatForm" label-width="100px">
<el-form-item label="Classification name:" prop="name">
<el-input v-model="updateItemCatForm.name"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="updateItemCatDialogVisible = false">Cancel</el-button>
<el-button type="primary" @click="updateItemCat">determine</el-button>
</span>
</el-dialog>
4. Modify button JS
async updateItemCat() {
//Modify commodity classification information
const {data: result} =
await this.$http.put('/itemCat/updateItemCat', this.updateItemCatForm)
if (result.status !== 200) return this.$message.error("Failed to update product classification")
this.$message.success("Update commodity classification succeeded")
this.findItemCatList();
this.updateItemCatDialogVisible = false;
},
4.2.2 page interface document
- Request path: / itemCat/updateItemCat
- Request type: put
- Request parameter: form data ItemCat object
- Return value: SysResult object
Parameter name | Parameter description | remarks |
---|
status | status information | 200 indicates that the server request is successful, and 201 indicates that the server is abnormal |
msg | Prompt information returned by the server | Can be null |
data | Business data returned by the server | Can be null |
Data analysis:
4.2.3 editing ItemCatController
/**
* Modify commodity classification name
* URL: /itemCat/updateItemCat
* Parameter: JSON string of the whole form
* Return value: SysResult object
*/
@PutMapping("/updateItemCat")
public SysResult updateItemCat(@RequestBody ItemCat itemCat){
itemCatService.updateItemCat(itemCat);
return SysResult.success();
}
4.2.4 edit ItemCatService
//Since only the name of the page is modified, sql only modifies name/updated
@Override
@Transactional
public void updateItemCat(ItemCat itemCat) {
//The user only modifies name,updated by id
ItemCat temp = new ItemCat();
temp.setId(itemCat.getId())
.setName(itemCat.getName())
.setUpdated(new Date());
itemCatMapper.updateById(temp);
}
4.3 debug breakpoint debugging
5. Business realization of commodity module
5.1 product page Jump
- Edit the routing file of index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Login from '../components/Login.vue'
import ElementUI from '../components/ElementUI.vue'
import Home from '../components/Home.vue'
import User from '../components/user/user.vue'
import ItemCat from '../components/items/ItemCat.vue'
import Item from '../components/items/Item.vue'
//Using routing mechanism
Vue.use(VueRouter)
const routes = [
{path: '/', redirect: '/login'},
{path: '/login', component: Login},
{path: '/elementUI', component: ElementUI},
{path: '/home', component: Home, children: [
{path: '/user', component: User},
{path: '/itemCat', component: ItemCat},
{path: '/item', component: Item}
]}
]
- Page effect
5.2 automatic data filling
5.2.1 business analysis
Note: if you need to add the creation time / modification time every time you add / update, it is cumbersome. Can you automatically fill in the data through the framework
5.2.2 add auto fill annotation
//pojo base class, completing 2 tasks and 2 dates, realizing serialization
@Data
@Accessors(chain=true)
public class BasePojo implements Serializable{
@TableField(fill = FieldFill.INSERT) //Automatic filling is realized when adding an operation
private Date created; //Indicates that assignment is required during warehousing
@TableField(fill = FieldFill.INSERT_UPDATE) //Automatically fill in when adding / modifying
private Date updated; //Indicates the assignment during warehousing / updating
}
5.2.3 configure auto fill class
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
//Framework usage Description: MP automatically completes data injection according to the implementation class. MP framework calls automatically
//metaObject: Specifies the default rule
@Override
public void insertFill(MetaObject metaObject) {
Date date = new Date();
this.setFieldValByName("created", date, metaObject);
this.setFieldValByName("updated", date, metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("updated", new Date(), metaObject);
}
}
5.3 building commodity level codes
5.3.1 item table design
5.3.2 edit Item POJO
/**
* @author Liu Yujiang
* Time: April 7, 2021
*/
@Data
@Accessors(chain = true)
@TableName("item")
public class Item extends BasePojo{
@TableId(type= IdType.AUTO)
private Integer id; //Item Id No
private String title; //Item title information
private String sellPoint; //Selling point information
private Integer price; //commodity price
private Integer num; //Quantity of goods
private String images; //Product picture
private Integer itemCatId; //Commodity classification ID number
private Boolean status; //Status information 0 off shelf 1 on shelf
}
5.3.3 edit hierarchical code structure
5.4 complete the display of commodity list
5.4.1 page analysis
//1. Life cycle function
created() {
//1. Obtain commodity list data
this.getItemList()
},
//2. Call this.getItemList()
async getItemList() {
const {data: result} =
await this.$http.get("/item/getItemList", {
params: this.queryItemInfo
})
if (result.status !== 200) return this.$message.error("Commodity list query failed")
this.itemList = result.data.rows
this.total = result.data.total
},
5.4.2 interface document description
- Request path: / item / getitemlist? Query = & pagenum = 1 & PageSize = 10
- Request type: get
- Request parameters: receive using pageResult object
Parameter name | Parameter description | remarks |
---|
query | Data queried by user | Can be null |
pageNum | Number of pages for paged queries | A value must be assigned and cannot be null |
pageSize | Number of paged queries | A value must be assigned and cannot be null |
Parameter name | Parameter description | remarks |
---|
status | status information | 200 indicates that the server request is successful, and 201 indicates that the server is abnormal |
msg | Prompt information returned by the server | Can be null |
data | Business data returned by the server | Item paging object |
5.4.3 editing ItemController
@RestController
@CrossOrigin
@RequestMapping("/item")
public class ItemController {
@Autowired
private ItemService itemService;
/**
* Business: realize pagination query of goods
* URL: /item/getItemList?query=&pageNum=1&pageSize=10
* Parameters: query = & pagenum = 1 & PageSize = 10
* Return value: SysResult(PageResult)
*/
@GetMapping("/getItemList")
public SysResult getItemList(PageResult pageResult){//3
//3 + 2 (total records, paging results)
pageResult = itemService.getItemList(pageResult);
return SysResult.success(pageResult);//5
}
}
5.4.4 edit ItemService
@Service
public class ItemServiceImpl implements ItemService{
@Autowired
private ItemMapper itemMapper;
/**
* Requirement: 3 + 2 (total records, paging results)
* About selectpage (parameter description)
* Parameter 1: paging object provided by page mp
* Parameter 2: condition constructor
* @param pageResult
* @return
*/
@Override
public PageResult getItemList(PageResult pageResult) {
//1. Build paging object parameter 1: page number parameter 2: how many pages
Page<Item> page = new Page<>(pageResult.getPageNum(),pageResult.getPageSize());
//2. Prepare the condition constructor to build the fuzzy query
QueryWrapper queryWrapper = new QueryWrapper();
String query = pageResult.getQuery();
boolean flag = StringUtils.hasLength(query);
queryWrapper.like(flag,"title",query);
//3. Realize the automatic encapsulation of paging data according to MP query
page = itemMapper.selectPage(page,queryWrapper);
//4. Get the data and return the paging object
long total = page.getTotal();
//Get paging results
List<Item> rows = page.getRecords();
return pageResult.setTotal(total).setRows(rows);
}
}
5.4.5 editing paging configuration class
@Configuration //This is the configuration class
public class MybatisConfig {
//The database type needs to be specified through the configuration file
// the latest version
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MARIADB));
return interceptor;
}
}
5.4.6 page effect display
5.5 modification of commodity status
5.5.1 page JS analysis
<template slot-scope="scope">
<el-switch v-model="scope.row.status" active-color="#13ce66" inactive-color="#ff4949"
@change="updateStatus(scope.row)"></el-switch>
</template>
async updateStatus(item) {
const { data: result} =
await this.$http.put("/item/updateItemStatus", {
id: item.id,
status: item.status
})
if (result.status !== 200) return this.$message.error("Update status failed")
this.$message.success("Update status succeeded")
},
5.5.2 business interface documents
- Request path: / item/updateItemStatus
- Request type: put
- Request parameters: receive using object
Parameter name | Parameter description | remarks |
---|
id | Commodity id | Cannot be null |
status | status information | Cannot be null |
Parameter name | Parameter description | remarks |
---|
status | status information | 200 indicates that the server request is successful, and 201 indicates that the server is abnormal |
msg | Prompt information returned by the server | Can be null |
data | Business data returned by the server | Can be null |
5.5.3 editing ItemController
/**
* Modify the status information of goods
* URL: /item/updateItemStatus
* Parameters: JSON string {id:xx,status:xx}
* Return value: SysResult object
*/
@PutMapping("/updateItemStatus")
public SysResult updateItemStatus(@RequestBody Item item){
itemService.updateItemStatus(item);
return SysResult.success();
}
5.5.4 edit ItemService
@Override
@Transactional //Control transactions
public void updateItemStatus(Item item) {
itemMapper.updateById(item);
}
5.6 deleting goods
5.6.1 page analysis
//Delete data by id
const {data: result} = await this.$http.delete("/item/deleteItemById", {
params: {
id: item.id
}
})
if (result.status !== 200) return this.$message.error("Failed to delete product")
this.$message.success("Product deleted successfully")
//Retrieve product list information
this.getItemList()
5.6.2 business interface documents
- Request path: / item/deleteItemById
- Request type: delete
- Request parameters:
Parameter name | Parameter description | remarks |
---|
ID | Item ID number | Cannot be null |
Parameter name | Parameter description | remarks |
---|
status | status information | 200 indicates that the server request is successful, and 201 indicates that the server is abnormal |
msg | Prompt information returned by the server | Can be null |
data | Business data returned by the server | Can be null |
5.6.3 editing ItemController
/**
* Business requirement: delete data according to Id
* URL: /item/deleteItemById
* Parameter: id
* Return value: SysResult object
*/
@DeleteMapping("/deleteItemById")
public SysResult deleteItemById(Integer id){
itemService.deleteItemById(id);
return SysResult.success();
}
5.6.4 edit ItemService
@Override
@Transactional
public void deleteItemById(Integer id) {
itemMapper.deleteById(id);
}
5.7 adding goods
5.7.1 page Jump
const routes = [
{path: '/', redirect: '/login'},
{path: '/login', component: Login},
{path: '/elementUI', component: ElementUI},
{path: '/home', component: Home, children: [
{path: '/user', component: User},
{path: '/itemCat', component: ItemCat},
{path: '/item', component: Item},
{path: '/item/addItem', component: AddItem}
]}
]
Page effect:
5.7.2 add page JS
Table structure description:
- Save the basic information of goods to the item table
- Save item details to item_desc table
/* Add item button */
async addItemBtn(){
//console.log(this.addItemForm)
//1. Complete form verification
this.$refs.addItemFormRef.validate( valid => {
if(!valid) return this.$message.error("Please enter a required item")
})
//2. Complete the packaging of commodity parameters
//2.0 expand commodity prices by 100 times
this.addItemForm.price = this.addItemForm.price * 100
//2.1 convert the data of commodity picture into string
this.addItemForm.images = this.addItemForm.images.join(",")
//2.5 realize commodity data submission and wrap two small objects with one large object
let submitAddItem = {
item : this.addItemForm,
itemDesc: this.itemDesc
}
//console.log(submitAddItem)
let {data: result} = await this.$http.post("/item/saveItem",submitAddItem)
if(result.status !== 200) return this.$message.error("Failed to add product")
this.$message.success("Product added successfully")
//2.5 after adding, redirect the data to the product display page
this.$router.push("/item")
}
5.7.3 service interface description
-
Request path: http://localhost:8091/item/saveItem
- Request type: post
- Front end transfer parameter analysis
{
item: {
images: "/2021/05/20/da0c1d4781c1499399f090da8b60f359.jpg,/2021/05/20/2ac1c34776a7465887eb019655354c3c.jpg"
itemCatId: 560
num: "100"
price: 718800
sellPoint: "[Huawei official direct supply,Zhigao 12 interest free 0 down payment,[original] send Huawei original wireless charger+Sports Bluetooth headset+Bluetooth Speaker +Three in one multifunctional data line+Toughened film, etc!"
title: "Huawei P40 Pro 5G Mobile phone [12 interest free optional gifts] all Netcom smart phone"
},
itemDesc: {
itemDesc: "<ul><li>Brand: <a href=https://list.jd.com/list.html"....... "
}
}
- Request parameters: received using ItemVO object
Parameter name | Parameter description | remarks |
---|
item | Item | Commodity basic information object encapsulation |
itemDesc | ItemDesc | Product details |
itemParam | ItemParam | Commodity parameter information |
- ItemVO parameter details:
- Item object
Parameter name | Parameter type | Parameter description | remarks |
---|
title | String | Item title information | Cannot be null |
sellPoint | String | | Commodity selling point information |
price | Integer | Commodity price information | Cannot be null. You need to expand the data 100 times |
num | Integer | Commodity quantity information | Cannot be null |
images | String | Product picture address information | Cannot be null |
itemCatId | Integer | Product parent category ID | Cannot be null |
status | Boolean | Product status information | Cannot be null |
-
itemDesc object
In order to reduce the coupling of commodity submission code,Large field information details,use ItemDesc Object
Parameter name | Parameter type | Parameter description | remarks |
---|
id | Integer | Item Id information | Because Item and ItemDesc are one-to-one, they need to rely on the Id value of the Item object |
itemDesc | String | Product details | It contains a large number of html statements |
Parameter name | Parameter description | remarks |
---|
status | status information | 200 indicates that the server request is successful, and 201 indicates that the server is abnormal |
msg | Prompt information returned by the server | Can be null |
data | Business data returned by the server | Can be null |
5.7.4 editing ItemController
/**
* Finish adding goods
* 1.URL Address http://localhost:8091/item/saveItem
* 2.Parameter post itemvo JSON string
* 3.Return value SysResult object
*/
@PostMapping("/saveItem")
public SysResult saveItem(@RequestBody ItemVO itemVO){
itemService.saveItem(itemVO);
return SysResult.success();
}
5.7.5 edit ItemService
@Override
@Transactional
public void saveItem(ItemVO itemVO) {
Item item = itemVO.getItem();
//Setting state
item.setStatus(true);
itemMapper.insert(item);
}
6 product file upload
6.1 details of goods warehousing
6.1.1 rich text editor
Note: rich text can achieve the "WYSIWYG" effect on the page
6.1.2 introduction steps
- Introducing js
/* Import rich text editor */
import VueQuillEditor from 'vue-quill-editor'
/* Import the style corresponding to the rich text editor */
import 'quill/dist/quill.core.css' // import styles
import 'quill/dist/quill.snow.css' // for snow theme
import 'quill/dist/quill.bubble.css' // for bubble theme
/* Register the rich text editor as a globally available component */
Vue.use(VueQuillEditor)
- Using the rich text editor
<!-- Define rich text editor-->
<quill-editor ref="myQuillEditor" v-model="itemDesc.itemDesc">
</quill-editor>
6.1.3 description of ItemDesc
Note: since Item and ItemDesc are typical one-to-one, item.id = itemDesc.id is required
@Data
@Accessors(chain = true)
@TableName("item_desc")
public class ItemDesc extends BasePojo{
@TableId
private Integer id;
private String itemDesc;
}
6.1.4 edit ItemService
/**
* Problem: id is the primary key self increment. There is a primary key only after receipt, so
* The primary key should be echoed dynamically
* 1.Mybatis Dynamic echo
* <insert id="xxxx" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
* insertinto xxxx
* </insert>
* 2.MP It is an enhanced version of mybatis. So it can realize automatic primary key echo!!!
* @param itemVO
*/
@Override
@Transactional
public void saveItem(ItemVO itemVO) {
Item item = itemVO.getItem();
//Setting state
item.setStatus(true);
itemMapper.insert(item);
//Get product details
ItemDesc itemDesc = itemVO.getItemDesc();
itemDesc.setId(item.getId());
itemDescMapper.insert(itemDesc);
}
6.1.5
Description: to store large fields, modify the database type
6.2 upload commodity pictures
6.2.1 edit page
//1. Define page html
<!-- Dialog box for defining product modification -->
<!-- Dialog box for defining product modification -->
<el-dialog title="Commodity modification" :visible.sync="updateDialogVisible" width="60%">
<!-- Form ready for modification-->
<el-form :model="updateItem" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
<el-form-item label="Title Information" prop="title">
<el-input v-model="updateItem.title"></el-input>
</el-form-item>
<el-form-item label="Selling point information" prop="sellPoint">
<el-input v-model="updateItem.sellPoint"></el-input>
</el-form-item>
<el-form-item label="price information " prop="price">
<el-input v-model="updateItem.price"></el-input>
</el-form-item>
<el-form-item label="Quantity information" prop="num">
<el-input v-model="updateItem.num"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="updateDialogVisible = false">Cancel</el-button>
<el-button type="primary" @click="dialogVisible = false">determine</el-button>
</span>
</el-dialog>
//2. Define page JS
updateDialogVisible: false,
updateItem: {},
//Prepare a validation rule
rules: {
title: [
{ required: true, message: 'Please enter product title information', trigger: 'blur' },
{ min: 3, max: 50, message: 'The length is between 3 and 50 characters', trigger: 'blur' }
],
sellPoint: [
{ required: true, message: 'Please enter the selling point information', trigger: 'blur' },
{ min: 3, max: 50, message: 'The length is between 3 and 50 characters', trigger: 'blur' }
],
price: [
{ required: true, message: 'Please enter commodity price information', trigger: 'blur' },
{ min: 3, max: 50, message: 'The length is between 3 and 50 characters', trigger: 'blur' }
],
num: [
{ required: true, message: 'Please enter product quantity information', trigger: 'blur' },
{ min: 3, max: 50, message: 'The length is between 3 and 50 characters', trigger: 'blur' }
],
}
//Define JS button
updateItemBtn(item){
console.log("Extended case,You only need to modify the title/selling point/Price/quantity")
this.updateDialogVisible = true
this.updateItem = item
this.updateItem.price = (this.updateItem.price / 100).toFixed(2)
}
6.2.2 official website description
//1. Official website picture JS description
<!-- Picture upload JS
1. action: Represents the address of the picture upload url
2. file-list: Collection of picture list data[{name:"xx",url:"xxx"},{}]
3. Hook function: Triggered when certain conditions are met.
4. on-preview Triggered when the information of the uploaded list is clicked
5. on-remove Triggered when a picture is removed from the list
-->
<el-upload
class="upload-demo"
action="https://jsonplaceholder.typicode.com/posts/"
:on-preview="handlePreview"
:on-remove="handleRemove"
:file-list="fileList"
list-type="picture">
<el-button size="small" type="primary">Click upload</el-button>
<div slot="tip" class="el-upload__tip">Upload only jpg/png Documents, and no more than 500 kb</div>
</el-upload>
2. page JS Supplementary knowledge
handlePreview(){
console.log("Trigger view function!!!!")
},
handleRemove(){
console.log("Triggered on removal!!!!")
}
6.2.3 picture upload project description
<!--
one.File upload component description
1.action: Upload picture address http://localhost:8091/xxx/xxx
2.on-preview Triggered when a picture is clicked
3.on-remove Triggered when a picture is removed
4.on-success Triggered when the picture is uploaded successfully
5.multiple It can support uploading multiple pictures
6.drag Allow drag
two.Request type: Generally, when uploading byte information,be the first choice post request
three.Upload file key explain: When uploading files key=file.
When receiving data at the back end file receive.
-->
<el-upload class="upload-demo" :action="uploadUrl" :on-preview="handlePreview" :on-remove="handleRemove"
:on-success="handleSuccess" list-type="picture" multiple drag>
<el-button size="small" type="primary">Click upload</el-button>
<div slot="tip" class="el-upload__tip">Upload only jpg/png Documents, and no more than 500 kb</div>
</el-upload>
//Define file upload path address
uploadUrl: "http://localhost:8091/file/upload",
6.2.4 picture upload interface document description
- Request path: http://localhost:8091/file/upload
- Request type: post
- Request parameters:
Parameter name | Parameter description | remarks |
---|
file | Parameter name of file upload | file carries binary information |
Parameter name | Parameter description | remarks |
---|
status | status information | 200 indicates that the server request is successful, and 201 indicates that the server is abnormal |
msg | Prompt information returned by the server | Can be null |
data | Business data returned by the server | Return ImageVO object |
- ImageVO object description
Parameter name | Parameter type | Parameter description | remarks |
---|
virtualPath | String | The actual path of the picture does not contain disk information | For example, 2021/11/11/a.jpg does not need to write the disk address |
urlPath | String | Picture url access address | http://image.jt.com/2021/11/11/a.jpg Domain name address needs to be specified |
fileName | String | File name after file upload | UUID.type |
6.2.5 editing ImageVO
@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class ImageVO {
private String virtualPath; //Picture virtual path dynamic path
private String urlPath; //URL address of picture echo
private String fileName; //File name after file upload
}
6.2.6 edit ItemController
@RestController
@CrossOrigin
@RequestMapping("/file")
public class FileController {
/**
* Business description: upload pictures
* URL: http://localhost:8091/file/upload
* Type: post
* Parameter: file byte information
* Return value: SysResult.success()
* Extension:
* Normally:
* Generally, the front end sends byte information to the back end server. Data transmission is realized from outside to inside
* Adopt input stream information. InputStream file
* Disadvantages of using byte stream: 1. It must be closed manually; 2. The code operation is cumbersome
* The implementation of the underlying code
* SpringMVC The advanced API multipartfile specifically handles IO stream operations
* File upload steps:
* 1.Get file name
* 2.Directory for preparing file upload
* 3.Judge whether the directory exists: upload without Directory: create directory
* 4.Use tool API method to upload files
* Note: MultipartFile supports 1M data by default
*/
@PostMapping("/upload")
public SysResult upload(MultipartFile file) throws IOException {
//1. Get file name
String fileName = file.getOriginalFilename();
//2. Prepare the disk address
String dirPath = "E:/project3/images/";
//3. Encapsulate the File directory as a File object
File dirFile = new File(dirPath);
//4. Judge whether the object exists
if(!dirFile.exists()){
//If the file directory does not exist, create a directory
dirFile.mkdirs(); //Indicates multi-level directory upload
}
//5. Full path of package file E:xxx/xxx/a.jpg
String path = dirPath + fileName;
File allFile = new File(path);
//6. Realize file upload and output the IO stream according to the specified object format
file.transferTo(allFile);
return SysResult.success();
}
}
6.3 regular expressions (review)
6.3.1 regular expression description
Regular Expression, also known as Regular Expression. (English: Regular Expression, often abbreviated as regex, regexp or RE in code), a concept of computer science. Regular expressions are often used to retrieve and replace text that conforms to a pattern (rule).
Summary: regular expression is a special format string. It verifies the of text information
6.3.2 matching uncertainty times
6.3.3 fixed matching times
6.3.4 matching value range
6.3.5 group matching
6.3.6 regular case exercises
-
The matching phone number is required to start with 1 in 11 digits
Regular expression: 1 [3-9] [0-9] {9}
-
Require matching mailbox xxxx@qq.com
Regular expression:
^[a-zA-Z0-9-_]+@[a-zA-Z0-9-_]+\.[a-zA-Z0-9-_]+$
6.4 file upload implementation
6.4.1 editing FileController
@RestController
@CrossOrigin
@RequestMapping("/file")
public class FileController {
@Autowired
private FileService fileService;
/**
* Business description: upload pictures
* URL: http://localhost:8091/file/upload
* Type: post
* Parameter: multipartfile byte information
* Return value: SysResult.success()
* Problem thinking:
* 1.Complete picture type verification jpg|png|gif
* 2.Prevent malicious programs a.exe.jpg
* 3.Store pictures in different directories
* 3.1.According to the theory of classification, we can, but we have to allocate more
* 3.2.By time. yyyy/MM/dd
* 4.Custom file name. Use UUID as picture name
*/
@PostMapping("/upload")
public SysResult upload(MultipartFile file) throws IOException {
ImageVO imageVO = fileService.upload(file);
if(imageVO == null){
return SysResult.fail();
}
return SysResult.success(imageVO);
}
/**
* Business description: upload pictures
* URL: http://localhost:8091/file/upload
* Type: post
* Parameter: file byte information
* Return value: SysResult.success()
* Extension:
* Normally:
* Generally, the front end sends byte information to the back end server. Data transmission is realized from outside to inside
* Adopt input stream information. InputStream file
* Disadvantages of using byte stream: 1. It must be closed manually; 2. The code operation is cumbersome
* The implementation of the underlying code
* SpringMVC The advanced API multipartfile specifically handles IO stream operations
* File upload steps:
* 1.Get file name
* 2.Directory for preparing file upload
* 3.Judge whether the directory exists: upload without Directory: create directory
* 4.Use tool API method to upload files
* Note: MultipartFile supports 1M data by default
*/
/*@PostMapping("/upload")
public SysResult upload(MultipartFile file) throws IOException {
//1.Get file name
String fileName = file.getOriginalFilename();
//2.Prepare disk address
String dirPath = "E:/project3/images/";
//3.Encapsulate the File directory as a File object
File dirFile = new File(dirPath);
//4.Judge whether the object exists
if(!dirFile.exists()){
//If the file directory does not exist, create a directory
dirFile.mkdirs(); //Indicates multi-level directory upload
}
//5.Full path of package file E:xxx/xxx/a.jpg
String path = dirPath + fileName;
File allFile = new File(path);
//6.Realize file upload and output the IO stream according to the specified object format
file.transferTo(allFile);
return SysResult.success();
}*/
}
6.4.2 editing FileService
package com.jt.service;
import com.jt.vo.ImageVO;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.FileNameMap;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
@Service
public class FileServiceImpl implements FileService{
private String localDirPath = "E:/project3/images";
//1. Verify that the picture type is xxx.jpg and that the suffix is jpg
@Override
public ImageVO upload(MultipartFile file) {
//1.1 get the file name abc.jpg
String fileName = file.getOriginalFilename();
//1.2 convert all to lowercase letters
fileName = fileName.toLowerCase();
//1.3 regular check whether it is a picture type
if(!fileName.matches("^.+\\.(jpg|png|gif)$")){
//Picture type mismatch program should terminate
return null;
}
//2. Check whether it is a malicious program. How to judge is the height and width of a picture
//2.1 processing through picture objects
try {
BufferedImage bufferedImage = ImageIO.read(file.getInputStream());
int height = bufferedImage.getHeight();
int width = bufferedImage.getWidth();
if(height == 0 || width == 0){
return null;
}
//3. Store pictures in directory yyyy/MM/dd
String dateDir = new SimpleDateFormat("/yyyy/MM/dd/")
.format(new Date());
String dateDirPath = localDirPath + dateDir;
File dirFile = new File(dateDirPath);
if(!dirFile.exists()){
dirFile.mkdirs();
}
//4. Prevent duplicate file names from dynamically generating UUID types
//4.1 dynamically generate UUID
String uuid = UUID.randomUUID().toString()
.replace("-","");
//4.2 get picture type abc.jpg. Jpg
String fileType = fileName.substring(fileName.lastIndexOf("."));
// uuid.jpg
String newFileName = uuid + fileType;
//5. Realize file upload 1. Prepare the full file path 2. Encapsulate the object to realize file upload
String path = dateDirPath + newFileName;
file.transferTo(new File(path));
} catch (IOException e) {
e.printStackTrace();
return null;
}
return null;
}
}
6.5 file deletion
6.5.1 file deletion JS
6.5.2 file deletion service interface
- Request path: http://localhost:8091/file/deleteFile
- Request type: delete
- Request parameters:
Parameter name | Parameter description | remarks |
---|
virtualPath | Virtual path for file upload | When deleting, you need to delete it together with the disk path |
Parameter name | Parameter description | remarks |
---|
status | status information | 200 indicates that the server request is successful, and 201 indicates that the server is abnormal |
msg | Prompt information returned by the server | Can be null |
data | Business data returned by the server | Can be null |
6.5.3 editing FileController
/**
* Business description: file deletion
* URL Address: http://localhost:8091/file/deleteFile
* Request type: delete
* Parameter: virtualPath virtual path
* Return value: SysResult object
*/
@DeleteMapping("/deleteFile")
public SysResult deleteFile(String virtualPath){
fileService.deleteFile(virtualPath);
return SysResult.success();
}
6.5.4 editing FileService
@Override
public void deleteFile(String virtualPath) {
String filePath = localDirPath + virtualPath;
File file = new File(filePath);
if(file.exists()){ //If the file exists, delete the data
file.delete();
}
}
6.6 picture path encapsulation
6.6.1 path analysis
- Picture network address: https://img14.360buyimg.com/n0/jfs/t2/ac4a3f32ea776da3.jpg
Protocol: / / Domain Name: 80 / virtual address - Picture address encapsulation: http://image.jt.com:80/2021/11/11/uuid.jpg.
6.6.2 page URL address encapsulation
package com.jt.service;
import com.jt.vo.ImageVO;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.FileNameMap;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
@Service
public class FileServiceImpl implements FileService{
private String localDirPath = "E:/project3/images";
private String preUrl = "http://image.jt.com";
//1. Verify that the picture type is xxx.jpg and that the suffix is jpg
@Override
public ImageVO upload(MultipartFile file) {
//1.1 get the file name abc.jpg
String fileName = file.getOriginalFilename();
//1.2 convert all to lowercase letters
fileName = fileName.toLowerCase();
//1.3 regular check whether it is a picture type
if(!fileName.matches("^.+\\.(jpg|png|gif)$")){
//Picture type mismatch program should terminate
return null;
}
//2. Check whether it is a malicious program. How to judge is the height and width of a picture
//2.1 processing through picture objects
try {
BufferedImage bufferedImage = ImageIO.read(file.getInputStream());
int height = bufferedImage.getHeight();
int width = bufferedImage.getWidth();
if(height == 0 || width == 0){
return null;
}
//3. Store pictures in directory yyyy/MM/dd
String dateDir = new SimpleDateFormat("/yyyy/MM/dd/")
.format(new Date());
String dateDirPath = localDirPath + dateDir;
File dirFile = new File(dateDirPath);
if(!dirFile.exists()){
dirFile.mkdirs();
}
//4. Prevent duplicate file names from dynamically generating UUID types
//4.1 dynamically generate UUID
String uuid = UUID.randomUUID().toString()
.replace("-","");
//4.2 get picture type abc.jpg. Jpg
String fileType = fileName.substring(fileName.lastIndexOf("."));
// uuid.jpg
String newFileName = uuid + fileType;
//5. Realize file upload 1. Prepare the full file path 2. Encapsulate the object to realize file upload
String path = dateDirPath + newFileName;
file.transferTo(new File(path));
//6. Realize the return of ImageVO data
//6.1 prepare virtual path / 2021/11/11/uuid.jpg
String virtualPath = dateDir + newFileName;
//6.2 prepare URL address, domain name prefix + virtual path
String url = preUrl + virtualPath;
System.out.println(url);
return new ImageVO(virtualPath,url,newFileName);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
@Override
public void deleteFile(String virtualPath) {
String filePath = localDirPath + virtualPath;
File file = new File(filePath);
if(file.exists()){ //If the file exists, delete the data
file.delete();
}
}
}
6.7 dynamic attribute assignment
6.7.1 business requirements
Note: if the attribute is written to the java class, it will be inconvenient for later maintenance
Optimization: you can dynamically assign values through the @ value annotation
6.7.2 editing the properties configuration file
image.localDirPath=E:/project3/images
image.preUrl=http://image.jt.com
6.7.3 dynamic attribute assignment