【商城实战(20)】商品管理功能深化实战
【商城实战】专栏重磅来袭!这是一份专为开发者与电商从业者打造的超详细指南。从项目基础搭建,运用 uniapp、Element Plus、SpringBoot 搭建商城框架,到用户、商品、订单等核心模块开发,再到性能优化、安全加固、多端适配,乃至运营推广策略,102 章内容层层递进。无论是想深入钻研技术细节,还是探寻商城运营之道,本专栏都能提供从 0 到 1 的系统讲解,助力你打造独具竞争力的电商平台,开启电商实战之旅。
目录
- 一、商品批量上架、下架功能实现
- 1.1 前端交互设计
- 1.2 后端接口开发(SpringBoot)
- 1.3 数据库操作(Mybatis-plus)
- 1.4 完整源码展示与分析
- 二、商品库存管理页面开发
- 2.1 库存管理页面布局设计
- 2.2 库存预警功能实现
- 2.3 库存盘点功能实现
- 2.4 源码实现与解读
- 三、完善商品信息编辑功能
- 3.1 前端编辑页面优化
- 3.2 后端编辑接口升级
- 3.3 数据库关联更新(Mybatis-plus)
- 3.4 完整代码示例与解析
- 四、总结与展望
一、商品批量上架、下架功能实现
在电商系统中,商品的批量上架与下架操作是提升商品管理效率的关键环节。当商家需要快速调整一批商品的展示状态时,逐个操作不仅耗时费力,还容易出错。批量操作功能能够一次性处理多个商品,大大节省了时间和人力成本,使得商家能够更高效地应对市场变化和业务需求。接下来,我们将详细介绍如何使用 uniapp、Element Plus、SpringBoot 和 Mybatis-plus 实现这一功能。
1.1 前端交互设计
- uniapp 实现:在 uniapp 中,使用checkbox组件实现商品选择,checkbox-group组件用于收集选中的商品。通过v-model指令绑定选中的商品列表,然后添加 “批量上架” 和 “批量下架” 按钮,并绑定对应的点击事件。示例代码如下:
<template>
<view>
<checkbox-group v-model="selectedIds">
<view v-for="(item, index) in productList" :key="index">
<checkbox :value="item.id">{{ item.productName }}</checkbox>
</view>
</checkbox-group>
<button @click="batchOperate('上架')">批量上架</button>
<button @click="batchOperate('下架')">批量下架</button>
</view>
</template>
<script>
export default {
data() {
return {
selectedIds: [],
productList: []
};
},
methods: {
async batchOperate(status) {
if (this.selectedIds.length === 0) {
uni.showToast({ title: '请选择商品', icon: 'none' });
return;
}
const res = await uni.request({
url: 'http://your-backend-url/batchProduct',
method: 'POST',
data: {
status,
productIds: this.selectedIds
}
});
if (res.data.success) {
uni.showToast({ title: `批量${status}成功`, icon:'success' });
// 刷新商品列表
await this.getProductList();
} else {
uni.showToast({ title: `批量${status}失败`, icon: 'none' });
}
},
async getProductList() {
const res = await uni.request({
url: 'http://your-backend-url/products',
method: 'GET'
});
this.productList = res.data.data;
}
},
onLoad() {
this.getProductList();
}
};
</script>
- Element Plus 实现:Element Plus 中,使用el-checkbox和el-checkbox-group组件实现商品选择,同样通过v-model绑定选中的商品 ID 数组。按钮部分使用el-button组件,并绑定点击事件。示例代码如下:
<template>
<el-container>
<el-main>
<el-checkbox-group v-model="selectedIds">
<el-checkbox v-for="(item, index) in productList" :key="index" :label="item.id">{{ item.productName }}</el-checkbox>
</el-checkbox-group>
<el-button @click="batchOperate('上架')">批量上架</el-button>
<el-button @click="batchOperate('下架')">批量下架</el-button>
</el-main>
</el-container>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import axios from 'axios';
const selectedIds = ref([]);
const productList = ref([]);
const batchOperate = async (status) => {
if (selectedIds.value.length === 0) {
alert('请选择商品');
return;
}
try {
const res = await axios.post('http://your-backend-url/batchProduct', {
status,
productIds: selectedIds.value
});
if (res.data.success) {
alert(`批量${status}成功`);
await getProductList();
} else {
alert(`批量${status}失败`);
}
} catch (error) {
console.error('操作失败', error);
alert(`批量${status}失败`);
}
};
const getProductList = async () => {
const res = await axios.get('http://your-backend-url/products');
productList.value = res.data.data;
};
onMounted(() => {
getProductList();
});
</script>
1.2 后端接口开发(SpringBoot)
在 SpringBoot 中,创建一个 Controller 来接收前端传递的批量操作请求。通过@RequestBody注解获取前端发送的参数,包括操作状态(上架或下架)和商品 ID 数组。然后调用 Service 层方法来处理业务逻辑。示例代码如下:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class ProductController {
@Autowired
private ProductService productService;
@PostMapping("/batchProduct")
public Result batchProduct(@RequestBody BatchProductDTO batchProductDTO) {
try {
productService.batchUpdateProductStatus(batchProductDTO.getStatus(), batchProductDTO.getProductIds());
return Result.success("操作成功");
} catch (Exception e) {
e.printStackTrace();
return Result.error("操作失败");
}
}
}
class BatchProductDTO {
private String status;
private List<Long> productIds;
// 省略getter和setter方法
}
class Result {
private boolean success;
private String message;
public static Result success(String message) {
Result result = new Result();
result.success = true;
result.message = message;
return result;
}
public static Result error(String message) {
Result result = new Result();
result.success = false;
result.message = message;
return result;
}
// 省略getter和setter方法
}
1.3 数据库操作(Mybatis-plus)
Mybatis-plus 提供了强大的 CRUD 操作,无需手写 mapper 文件。在 Service 层中,通过LambdaUpdateWrapper来构建更新条件,实现批量更新商品的上下架状态。示例代码如下:
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.List;
@Service
public class ProductService {
@Resource
private ProductMapper productMapper;
@Transactional
public void batchUpdateProductStatus(String status, List<Long> productIds) {
LambdaUpdateWrapper<Product> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.in(Product::getId, productIds);
updateWrapper.set(Product::getStatus, status);
productMapper.update(null, updateWrapper);
}
}
1.4 完整源码展示与分析
上述代码分别展示了 uniapp、Element Plus 前端以及 SpringBoot 后端和 Mybatis-plus 数据库操作实现商品批量上架、下架功能的关键部分。前端部分通过checkbox组件实现商品选择,点击按钮触发请求,将选中的商品 ID 和操作状态发送到后端。后端 Controller 接收请求后,调用 Service 层方法,Service 层利用 Mybatis-plus 的LambdaUpdateWrapper构建更新条件,实现对数据库中商品状态的批量更新。这样,通过前后端的协同工作,高效地实现了商品批量上架、下架功能,提升了商品管理的效率。
二、商品库存管理页面开发
商品库存管理是商城运营的核心环节之一,它直接关系到商品的供应与销售。准确的库存管理能够避免缺货现象,提升客户满意度,同时防止库存积压,减少资金占用。开发支持库存预警和库存盘点功能的页面,有助于商家及时掌握库存动态,做出合理的采购与销售决策,保障商城的稳定运营。
2.1 库存管理页面布局设计
- uniapp 实现:使用view组件构建页面布局,u-table组件展示库存数据表格,每列展示商品 ID、名称、库存数量、预警阈值等信息。在表格列中添加操作按钮,如盘点按钮,绑定点击事件。示例代码如下:
<template>
<view>
<u-table :columns="columns" :data="stockList">
<template #default="{ row }">
<view :key="row.id" class="u-table-cell">
{{ row.productId }}
</view>
<view :key="row.id + 'name'" class="u-table-cell">
{{ row.productName }}
</view>
<view :key="row.id + 'quantity'" class="u-table-cell">
{{ row.stockQuantity }}
</view>
<view :key="row.id + 'threshold'" class="u-table-cell">
{{ row.threshold }}
</view>
<view :key="row.id + 'operation'" class="u-table-cell">
<button @click="startStockTaking(row.id)">盘点</button>
</view>
</template>
</u-table>
</view>
</template>
<script>
export default {
data() {
return {
columns: [
{ title: '商品ID' },
{ title: '商品名称' },
{ title: '库存数量' },
{ title: '预警阈值' },
{ title: '操作' }
],
stockList: []
};
},
methods: {
async getStockList() {
const res = await uni.request({
url: 'http://your-backend-url/stockList',
method: 'GET'
});
this.stockList = res.data.data;
},
async startStockTaking(productId) {
// 发起盘点请求逻辑
}
},
onLoad() {
this.getStockList();
}
};
</script>
- Element Plus 实现:通过el-table组件展示库存数据,el-table-column定义每列内容。操作按钮使用el-button,并绑定点击事件。示例代码如下:
<template>
<el-container>
<el-main>
<el-table :data="stockList" border>
<el-table-column prop="productId" label="商品ID"></el-table-column>
<el-table-column prop="productName" label="商品名称"></el-table-column>
<el-table-column prop="stockQuantity" label="库存数量"></el-table-column>
<el-table-column prop="threshold" label="预警阈值"></el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-button @click="startStockTaking(scope.row.productId)">盘点</el-button>
</template>
</el-table-column>
</el-table>
</el-main>
</el-container>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import axios from 'axios';
const stockList = ref([]);
const getStockList = async () => {
const res = await axios.get('http://your-backend-url/stockList');
stockList.value = res.data.data;
};
const startStockTaking = async (productId) => {
// 发起盘点请求逻辑
};
onMounted(() => {
getStockList();
});
</script>
2.2 库存预警功能实现
- 后端实现:在 SpringBoot 的 Service 层中,通过LambdaQueryWrapper查询库存数量小于预警阈值的商品。当查询到符合条件的商品时,将预警信息封装成对象返回给前端。示例代码如下:
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
@Service
public class StockService {
@Resource
private StockMapper stockMapper;
public List<Stock> getLowStockProducts() {
LambdaQueryWrapper<Stock> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.lt(Stock::getStockQuantity, Stock::getThreshold);
return stockMapper.selectList(queryWrapper);
}
}
- 前端实现:在 uniapp 或 Element Plus 前端页面,通过定时器定时调用后端接口获取预警信息。当接收到预警信息后,在页面上以红色字体或特殊图标标识预警商品。在 uniapp 中,可以在u-table组件的对应列中根据数据判断添加样式;在 Element Plus 中,在el-table-column的default插槽中根据数据判断添加样式。示例代码(以 Element Plus 为例):
<template>
<el-container>
<el-main>
<el-table :data="stockList" border>
<el-table-column prop="productId" label="商品ID"></el-table-column>
<el-table-column prop="productName" label="商品名称">
<template #default="scope">
<span :class="scope.row.stockQuantity < scope.row.threshold? 'warning-text' : ''">
{{ scope.row.productName }}
</span>
</template>
</el-table-column>
<el-table-column prop="stockQuantity" label="库存数量"></el-table-column>
<el-table-column prop="threshold" label="预警阈值"></el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-button @click="startStockTaking(scope.row.productId)">盘点</el-button>
</template>
</el-table-column>
</el-table>
</el-main>
</el-container>
</template>
<script setup>
import { ref, onMounted, setInterval } from 'vue';
import axios from 'axios';
const stockList = ref([]);
const getStockList = async () => {
const res = await axios.get('http://your-backend-url/stockList');
stockList.value = res.data.data;
};
const startStockTaking = async (productId) => {
// 发起盘点请求逻辑
};
onMounted(() => {
getStockList();
setInterval(getStockList, 60 * 1000); // 每分钟获取一次预警信息
});
</script>
<style scoped>
.warning-text {
color: red;
}
</style>
2.3 库存盘点功能实现
- 后端实现:在 SpringBoot 的 Controller 中接收前端传递的盘点数据,包括商品 ID 和实际盘点数量。调用 Service 层方法更新数据库中的库存数据。在 Service 层中,通过LambdaUpdateWrapper构建更新条件,实现库存数据的更新。示例代码如下:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class StockController {
@Autowired
private StockService stockService;
@PostMapping("/stockTaking")
public Result stockTaking(@RequestBody StockTakingDTO stockTakingDTO) {
try {
stockService.updateStock(stockTakingDTO.getProductId(), stockTakingDTO.getActualQuantity());
return Result.success("盘点成功");
} catch (Exception e) {
e.printStackTrace();
return Result.error("盘点失败");
}
}
}
class StockTakingDTO {
private Long productId;
private Integer actualQuantity;
// 省略getter和setter方法
}
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
@Service
public class StockService {
@Resource
private StockMapper stockMapper;
@Transactional
public void updateStock(Long productId, Integer actualQuantity) {
LambdaUpdateWrapper<Stock> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(Stock::getProductId, productId);
updateWrapper.set(Stock::getStockQuantity, actualQuantity);
stockMapper.update(null, updateWrapper);
}
}
- 前端实现:当用户点击盘点按钮时,弹出盘点弹窗。在弹窗中,用户输入实际盘点数量,点击确认按钮后,将盘点数据发送到后端。在 uniapp 中,使用uni.showModal弹出确认框,获取用户输入后发送请求;在 Element Plus 中,使用el-dialog组件作为弹窗,通过表单获取用户输入,点击按钮时发送请求。示例代码(以 Element Plus 为例):
<template>
<el-container>
<el-main>
<el-table :data="stockList" border>
<el-table-column prop="productId" label="商品ID"></el-table-column>
<el-table-column prop="productName" label="商品名称"></el-table-column>
<el-table-column prop="stockQuantity" label="库存数量"></el-table-column>
<el-table-column prop="threshold" label="预警阈值"></el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-button @click="showStockTakingDialog(scope.row.productId)">盘点</el-button>
</template>
</el-table-column>
</el-table>
<el-dialog title="库存盘点" :visible.sync="stockTakingDialogVisible" width="30%">
<el-form :model="stockTakingForm" label-width="80px">
<el-form-item label="实际数量">
<el-input v-model="stockTakingForm.actualQuantity" type="number"></el-input>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="stockTakingDialogVisible = false">取消</el-button>
<el-button type="primary" @click="confirmStockTaking">确认</el-button>
</template>
</el-dialog>
</el-main>
</el-container>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import axios from 'axios';
const stockList = ref([]);
const stockTakingDialogVisible = ref(false);
const stockTakingForm = ref({
productId: null,
actualQuantity: null
});
const getStockList = async () => {
const res = await axios.get('http://your-backend-url/stockList');
stockList.value = res.data.data;
};
const showStockTakingDialog = (productId) => {
stockTakingForm.value.productId = productId;
stockTakingDialogVisible.value = true;
};
const confirmStockTaking = async () => {
try {
const res = await axios.post('http://your-backend-url/stockTaking', stockTakingForm.value);
if (res.data.success) {
stockTakingDialogVisible.value = false;
await getStockList();
} else {
alert('盘点失败');
}
} catch (error) {
console.error('盘点失败', error);
alert('盘点失败');
}
};
onMounted(() => {
getStockList();
});
</script>
2.4 源码实现与解读
上述代码展示了库存管理页面前后端实现的关键部分。前端通过表格展示库存数据,实现了库存预警标识和库存盘点操作的交互。后端通过 SpringBoot 和 Mybatis-plus 实现了数据的查询、更新等业务逻辑。在整个实现过程中,前后端通过 HTTP 请求进行数据交互,确保了库存管理功能的正常运行。通过对这些源码的解读,可以清晰地了解库存管理功能的实现思路和技术细节,为进一步优化和扩展功能提供了基础。
三、完善商品信息编辑功能
在商城系统中,完善商品信息编辑功能是实现精细化商品管理的重要一环。随着业务的发展,商品的属性和关联信息可能会发生变化,例如商品的分类调整、品牌更换等。一个完善的商品信息编辑功能能够满足这些业务需求,确保商品数据的准确性和完整性,提升用户体验和商城的运营效率。接下来,我们将详细介绍如何使用 uniapp、Element Plus、SpringBoot 和 Mybatis-plus 来完善这一功能。
3.1 前端编辑页面优化
- uniapp 实现:在 uniapp 中,使用u-form组件搭建商品信息编辑表单。对于商品分类和品牌选择,使用u-picker组件实现下拉选择功能。通过v-model指令绑定表单数据,实现数据的双向绑定。示例代码如下:
<template>
<view>
<u-form :model="productForm" ref="productFormRef">
<u-form-item label="商品名称">
<u-input v-model="productForm.productName"></u-input>
</u-form-item>
<u-form-item label="商品分类">
<u-picker :columns="categoryOptions" @change="handleCategoryChange" v-model="productForm.categoryId"></u-picker>
</u-form-item>
<u-form-item label="商品品牌">
<u-picker :columns="brandOptions" @change="handleBrandChange" v-model="productForm.brandId"></u-picker>
</u-form-item>
<!-- 其他商品信息字段 -->
<button @click="submitEdit">提交编辑</button>
</u-form>
</view>
</template>
<script>
export default {
data() {
return {
productForm: {
productName: '',
categoryId: '',
brandId: ''
},
categoryOptions: [],
brandOptions: []
};
},
methods: {
async getCategoryOptions() {
const res = await uni.request({
url: 'http://your-backend-url/categories',
method: 'GET'
});
this.categoryOptions = res.data.data.map(item => ({ label: item.categoryName, value: item.id }));
},
async getBrandOptions() {
const res = await uni.request({
url: 'http://your-backend-url/brands',
method: 'GET'
});
this.brandOptions = res.data.data.map(item => ({ label: item.brandName, value: item.id }));
},
handleCategoryChange(e) {
this.productForm.categoryId = e.detail.value;
},
handleBrandChange(e) {
this.productForm.brandId = e.detail.value;
},
async submitEdit() {
await this.$refs.productFormRef.validate();
const res = await uni.request({
url: 'http://your-backend-url/products/' + this.productForm.id,
method: 'PUT',
data: this.productForm
});
if (res.data.success) {
uni.showToast({ title: '编辑成功', icon:'success' });
// 刷新商品列表或返回上一页
} else {
uni.showToast({ title: '编辑失败', icon: 'none' });
}
}
},
onLoad() {
this.getCategoryOptions();
this.getBrandOptions();
// 从路由参数获取商品ID,查询商品信息并填充表单
const productId = this.$route.query.productId;
if (productId) {
this.getProductDetail(productId);
}
},
async getProductDetail(productId) {
const res = await uni.request({
url: 'http://your-backend-url/products/' + productId,
method: 'GET'
});
this.productForm = res.data.data;
}
};
</script>
- Element Plus 实现:利用el-form组件创建商品信息编辑表单,el-select组件实现商品分类和品牌的下拉选择。同样通过v-model绑定数据,实现数据的实时更新。示例代码如下:
<template>
<el-container>
<el-main>
<el-form :model="productForm" ref="productFormRef" label-width="120px">
<el-form-item label="商品名称">
<el-input v-model="productForm.productName"></el-input>
</el-form-item>
<el-form-item label="商品分类">
<el-select v-model="productForm.categoryId" placeholder="请选择分类">
<el-option v-for="item in categoryOptions" :key="item.id" :label="item.categoryName" :value="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="商品品牌">
<el-select v-model="productForm.brandId" placeholder="请选择品牌">
<el-option v-for="item in brandOptions" :key="item.id" :label="item.brandName" :value="item.id"></el-option>
</el-select>
</el-form-item>
<!-- 其他商品信息字段 -->
<el-form-item>
<el-button type="primary" @click="submitEdit">提交编辑</el-button>
</el-form-item>
</el-form>
</el-main>
</el-container>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import axios from 'axios';
const productForm = ref({
productName: '',
categoryId: '',
brandId: ''
});
const categoryOptions = ref([]);
const brandOptions = ref([]);
const getCategoryOptions = async () => {
const res = await axios.get('http://your-backend-url/categories');
categoryOptions.value = res.data.data;
};
const getBrandOptions = async () => {
const res = await axios.get('http://your-backend-url/brands');
brandOptions.value = res.data.data;
};
const submitEdit = async () => {
await productFormRef.value.validate();
try {
const res = await axios.put('http://your-backend-url/products/' + productForm.value.id, productForm.value);
if (res.data.success) {
alert('编辑成功');
// 刷新商品列表或返回上一页
} else {
alert('编辑失败');
}
} catch (error) {
console.error('编辑失败', error);
alert('编辑失败');
}
};
onMounted(() => {
getCategoryOptions();
getBrandOptions();
// 从路由参数获取商品ID,查询商品信息并填充表单
const productId = new URLSearchParams(window.location.search).get('productId');
if (productId) {
getProductDetail(productId);
}
});
const getProductDetail = async (productId) => {
const res = await axios.get('http://your-backend-url/products/' + productId);
productForm.value = res.data.data;
};
</script>
3.2 后端编辑接口升级
在 SpringBoot 后端,创建一个 Controller 来接收前端传递的商品编辑请求。通过@PathVariable获取商品 ID,@RequestBody获取编辑后的商品数据。在 Service 层中,根据商品 ID 查询原有商品信息,更新需要修改的字段,如商品分类和品牌关联信息,然后调用 Mybatis-plus 的更新方法将数据持久化到数据库。示例代码如下:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/products")
public class ProductController {
@Autowired
private ProductService productService;
@PutMapping("/{id}")
public Result updateProduct(@PathVariable Long id, @RequestBody Product product) {
try {
product.setId(id);
productService.updateProduct(product);
return Result.success("编辑成功");
} catch (Exception e) {
e.printStackTrace();
return Result.error("编辑失败");
}
}
}
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
@Service
public class ProductService {
@Resource
private ProductMapper productMapper;
@Transactional
public void updateProduct(Product product) {
// 根据ID查询原有商品信息
Product originalProduct = productMapper.selectById(product.getId());
// 更新需要修改的字段
originalProduct.setProductName(product.getProductName());
originalProduct.setCategoryId(product.getCategoryId());
originalProduct.setBrandId(product.getBrandId());
// 其他字段更新
// 更新数据库
productMapper.updateById(originalProduct);
}
}
3.3 数据库关联更新(Mybatis-plus)
Mybatis-plus 提供了便捷的更新操作,无需编写复杂的 SQL 语句。在上述 Service 层代码中,通过productMapper.updateById(originalProduct)方法即可实现商品信息的更新。如果商品与分类、品牌存在关联关系,并且在数据库设计中通过外键关联,当更新商品的分类 ID 和品牌 ID 时,数据库会自动维护关联关系的一致性。例如,假设商品表product中有category_id和brand_id字段分别关联分类表category和品牌表brand的主键:
<!-- 假设Mybatis-plus的配置文件,虽然不需要手写mapper文件,但这里展示配置结构 -->
<configuration>
<settings>
<!-- 开启驼峰命名映射 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
</configuration>
在 Java 代码中,当执行productMapper.updateById(originalProduct)时,Mybatis-plus 会根据originalProduct对象中的属性值生成对应的 SQL 语句,实现商品信息及关联字段的更新。例如,生成的 SQL 语句可能类似于:
UPDATE product
SET product_name = #{productName}, category_id = #{categoryId}, brand_id = #{brandId}
WHERE id = #{id}
这样就完成了商品信息及其关联的分类、品牌数据在数据库中的更新操作。
3.4 完整代码示例与解析
- 前端完整代码(以 Element Plus 为例):
<template>
<el-container>
<el-main>
<el-form :model="productForm" ref="productFormRef" label-width="120px">
<el-form-item label="商品名称">
<el-input v-model="productForm.productName"></el-input>
</el-form-item>
<el-form-item label="商品分类">
<el-select v-model="productForm.categoryId" placeholder="请选择分类">
<el-option v-for="item in categoryOptions" :key="item.id" :label="item.categoryName" :value="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="商品品牌">
<el-select v-model="productForm.brandId" placeholder="请选择品牌">
<el-option v-for="item in brandOptions" :key="item.id" :label="item.brandName" :value="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="商品价格">
<el-input-number v-model="productForm.price" :precision="2"></el-input-number>
</el-form-item>
<el-form-item label="商品库存">
<el-input-number v-model="productForm.stock"></el-input-number>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitEdit">提交编辑</el-button>
</el-form-item>
</el-form>
</el-main>
</el-container>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import axios from 'axios';
const productForm = ref({
id: null,
productName: '',
categoryId: '',
brandId: '',
price: 0,
stock: 0
});
const categoryOptions = ref([]);
const brandOptions = ref([]);
const getCategoryOptions = async () => {
const res = await axios.get('http://your-backend-url/categories');
categoryOptions.value = res.data.data;
};
const getBrandOptions = async () => {
const res = await axios.get('http://your-backend-url/brands');
brandOptions.value = res.data.data;
};
const submitEdit = async () => {
await productFormRef.value.validate();
try {
if (productForm.value.id) {
const res = await axios.put('http://your-backend-url/products/' + productForm.value.id, productForm.value);
if (res.data.success) {
alert('编辑成功');
// 刷新商品列表或返回上一页
} else {
alert('编辑失败');
}
} else {
alert('商品ID为空,无法编辑');
}
} catch (error) {
console.error('编辑失败', error);
alert('编辑失败');
}
};
onMounted(() => {
getCategoryOptions();
getBrandOptions();
const productId = new URLSearchParams(window.location.search).get('productId');
if (productId) {
getProductDetail(productId);
}
});
const getProductDetail = async (productId) => {
const res = await axios.get('http://your-backend-url/products/' + productId);
productForm.value = res.data.data;
};
</script>
- 后端完整代码:
// ProductController.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/products")
public class ProductController {
@Autowired
private ProductService productService;
@PutMapping("/{id}")
public Result updateProduct(@PathVariable Long id, @RequestBody Product product) {
try {
product.setId(id);
productService.updateProduct(product);
return Result.success("编辑成功");
} catch (Exception e) {
e.printStackTrace();
return Result.error("编辑失败");
}
}
}
// ProductService.java
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
@Service
public class ProductService {
@Resource
private ProductMapper productMapper;
@Transactional
public void updateProduct(Product product) {
Product originalProduct = productMapper.selectById(product.getId());
originalProduct.setProductName(product.getProductName());
originalProduct.setCategoryId(product.getCategoryId());
originalProduct.setBrandId(product.getBrandId());
originalProduct.setPrice(product.getPrice());
originalProduct.setStock(product.getStock());
productMapper.updateById(originalProduct);
}
}
// ProductMapper.java 由Mybatis-plus自动生成,继承BaseMapper
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import com.example.demo.entity.Product;
@Mapper
public interface ProductMapper extends BaseMapper<Product> {
}
// Product.java 商品实体类
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
@Data
public class Product {
@TableId
private Long id;
private String productName;
private Long categoryId;
private Long brandId;
private Double price;
private Integer stock;
}
// Result.java 返回结果类
class Result {
private boolean success;
private String message;
public static Result success(String message) {
Result result = new Result();
result.success = true;
result.message = message;
return result;
}
public static Result error(String message) {
Result result = new Result();
result.success = false;
result.message = message;
return result;
}
// 省略getter和setter方法
}
- 代码解析:
-
- 前端部分通过el-form和el-select等组件构建了商品信息编辑表单,实现了用户与页面的交互。getCategoryOptions和getBrandOptions方法从后端获取商品分类和品牌数据,填充下拉选择框。submitEdit方法在用户点击提交按钮时,验证表单数据并将编辑后的商品信息发送到后端。
-
- 后端ProductController接收前端的编辑请求,调用ProductService的updateProduct方法处理业务逻辑。ProductService中,先根据商品 ID 查询原有商品信息,然后更新需要修改的字段,最后通过ProductMapper的updateById方法将更新后的数据保存到数据库。
-
- Mybatis-plus 的BaseMapper为ProductMapper提供了基本的 CRUD 操作,使得数据库操作更加便捷高效。通过上述前后端代码的协同工作,实现了完善的商品信息编辑功能,满足了商城对商品精细化管理的需求。
四、总结与展望
通过上述对商品批量上架、下架功能,商品库存管理页面,以及商品信息编辑功能的深化开发,我们显著提升了商城后台商品管理的效率和灵活性。商品批量操作功能极大地减少了商家调整商品展示状态的时间成本,使得商品能够更及时地响应市场需求;库存管理页面的开发实现了库存的可视化管理和预警机制,有效避免了缺货和库存积压问题;完善的商品信息编辑功能则确保了商品数据的准确性和完整性,为商城的精细化运营提供了有力支持。
展望未来,我们可以进一步优化和拓展商品管理功能。在技术层面,持续提升系统性能,引入更先进的缓存技术和数据库优化策略,以应对高并发场景下的商品管理需求;在功能拓展方面,增加商品数据分析功能,通过对商品销售数据、库存数据等的深入挖掘,为商家提供更具针对性的决策支持,如智能补货建议、商品推荐策略等;同时,随着移动端购物的普及,进一步优化 uniapp 前端的用户体验,实现更流畅的操作和更友好的界面设计。此外,考虑到多语言和多地区的业务拓展,商品管理系统应具备良好的国际化支持,以满足不同地区用户的需求。通过不断地优化和创新,我们将为商城的稳定发展和业务增长奠定坚实的基础。