商品管理
需求说明
商品管理主要涉及到三个功能模块,业务流程如下:
- 新增商品类型: 定义商品的不同分类,如饮料、零食、日用品等。
- 新增商品: 添加新的商品信息,包括名称、规格、价格、类型等。
- 设备货道管理: 将商品与售货机的货道关联,管理每个货道的商品信息。
对于商品和其他管理数据,下面是示意图:
- 关系字段:class_id、sku_id、vm_id
生成基础代码
需求
使用若依代码生成器,生成商品类型、商品管理前后端基础代码,并导入到项目中:
步骤
创建目录菜单
创建商品管理目录菜单
配置代码生成信息
导入二张表
配置商品类型表(参考原型)
配置商品表(参考原型)
下载代码并导入项目
选中商品表和商品类型表生成下载
解压ruoyi.zip
得到前后端代码和动态菜单sql
代码导入
商品类型改造
基础页面
需求
参考页面原型,完成基础布局展示改造
列表显示改造
在skuClass/index.vue视图组件中修改
<el-table v-loading="loading" :data="skuClassList" @selection-change="handleSelectionChange"><!-- <el-table-column type="selection" width="55" align="center" /> --><el-table-column label="序号" align="center" prop="classId" /><el-table-column label="商品类型" align="center" prop="className" /><!-- 创建日期 --><el-table-column label="创建日期" align="center" prop="createTime" width="180" /><el-table-column label="操作" align="center" class-name="small-padding fixed-width"><template #default="scope"><el-button link type="primary" @click="handleUpdate(scope.row)" v-hasPermi="['manage:skuClass:edit']">修改</el-button><el-button link type="primary" @click="handleDelete(scope.row)" v-hasPermi="['manage:skuClass:remove']">删除</el-button></template></el-table-column></el-table>
原来的商品类型表中没有create_time和update_time字段需要手动添加,并在xml中返回查询创建时间。
SkuClassMapper.xml
<sql id="selectSkuClassVo">select class_id, class_name, parent_id,create_time, update_time from tb_sku_class</sql><select id="selectSkuClassList" parameterType="SkuClass" resultMap="SkuClassResult"><include refid="selectSkuClassVo"/><where> <if test="className != null and className != ''"> and class_name like concat('%', #{className}, '%')</if></where></select>
对话框改造
商品管理中的商品与商品类型存在外键约束,不能删除有关联的商品类型,所以要设置一个全局异常处理器,拦截这些异常并返回前端能理解的提示。同时当添加已存在的数据时,返回的是违反数据完整性约束
,需要指定该异常信息为无法保存名称已存在
。
修改全局异常处理器,添加以下内容
/*** 数据完整性异常*/
@ExceptionHandler(DataIntegrityViolationException.class)
public AjaxResult handelDataIntegrityViolationException(DataIntegrityViolationException e) {if (e.getMessage().contains("foreign")) {return AjaxResult.error("无法删除,有其他数据引用");}if(e.getMessage().contains("Duplicate")){return AjaxResult.error("无法保存,名称已存在");}return AjaxResult.error("数据完整性异常,请联系管理员");
}
商品管理改造
基础页面
需求
参考页面原型,完成基础布局展示改造
搜索区域改造
代码实现
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px"><el-form-item label="商品搜索" prop="skuName"><el-inputv-model="queryParams.skuName"placeholder="请输入"clearable@keyup.enter="handleQuery"/></el-form-item><el-form-item><el-button type="primary" @click="handleQuery">查询</el-button><!-- <el-button icon="Refresh" @click="resetQuery">重置</el-button> --></el-form-item></el-form><el-row :gutter="10" class="mb8"><el-col :span="1.5"><el-buttontype="primary"@click="handleAdd"v-hasPermi="['manage:sku:add']">新增</el-button></el-col><!-- 后续修改实现方法 --><el-col :span="1.5"><el-buttontype="primary"@click="handleExport"v-hasPermi="['manage:sku:export']">导入</el-button></el-col><right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar></el-row>
列表显示改造
在sku/index.vue视图组件中修改
代码实现
商品类型展示的应该是商品的类型名,而不是商品类型id。
<el-table v-loading="loading" :data="skuList" @selection-change="handleSelectionChange"><el-table-column label="商品编号" align="center" prop="skuId" /><el-table-column label="商品名称" align="center" prop="skuName" /><el-table-column label="商品图片" align="center" prop="skuImage" width="100"><template #default="scope"><image-preview :src="scope.row.skuImage" :width="50" :height="50"/></template></el-table-column><el-table-column label="品牌" align="center" prop="brandName" /><el-table-column label="规格" align="center" prop="unit" /><el-table-column label="商品价格" align="center" prop="price" /></el-table-column><el-table-column label="商品类型" align="center" prop="classId" ><template #default="scope"><div v-for="item in skuClassList" :key="item.classId"><span v-if="item.classId == scope.row.classId">{{ item.className }}</span></div></template></el-table-column><el-table-column label="创建日期" align="center" prop="createTime" width="180"><template #default="scope"><span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span></template></el-table-column><el-table-column label="操作" align="center" class-name="small-padding fixed-width"><template #default="scope"><el-button link type="primary" @click="handleUpdate(scope.row)" v-hasPermi="['manage:sku:edit']">修改</el-button><el-button link type="primary" @click="handleDelete(scope.row)" v-hasPermi="['manage:sku:remove']">删除</el-button></template></el-table-column></el-table>import { listSkuClass } from "@/api/manage/skuClass";
import { loadAllParams } from "@/api/page";// 查询商品类型列表
const skuClassList = ref([]);
function getSkuClassList() {listSkuClass(loadAllParams).then(response => {skuClassList.value = response.rows;});
}
getSkuClassList();
将前端分改为元,为了突出这一列的可读性,可以通过增加tag标签对这一列进行高亮。
<el-table-column label="商品价格" align="center" prop="price"><template #default="scope"><el-tag >{{ scope.row.price/100 }} 元</el-tag></template></el-table-column>
添加或修改对话框改造
页面原型
代码实现
商品价格需要改为数字输入框,并且通过precision属性保留两位小数,并且通过step属性将增加减小的步径金额从1元改为0.5元。
<!-- 添加或修改商品管理对话框 --><el-dialog :title="title" v-model="open" width="500px" append-to-body><el-form ref="skuRef" :model="form" :rules="rules" label-width="80px"><el-form-item label="商品名称" prop="skuName"><el-input v-model="form.skuName" placeholder="请输入" /></el-form-item><el-form-item label="品牌" prop="brandName"><el-input v-model="form.brandName" placeholder="请输入" /></el-form-item> <el-form-item label="商品价格" prop="price"><el-input-number :min="0.01" :max="999.99" :precision="2" :step="0.5" v-model="form.price" placeholder="请输入" style="width: 140px" /> <span>{{ '元' }}</span></el-form-item><el-form-item label="商品类型" prop="classId"><el-select v-model="form.classId" placeholder="请选择" style="width: 140px"><el-option v-for="item in skuClassList" :key="item.classId" :label="item.className" :value="item.classId" /></el-select></el-form-item><el-form-item label="规格" prop="unit"><el-input v-model="form.unit" placeholder="请输入规格" style="width: 140px" /> </el-form-item><el-form-item label="商品图片" prop="skuImage"><image-upload v-model="form.skuImage"/></el-form-item> </el-form><template #footer><div class="dialog-footer"><el-button type="primary" @click="submitForm">确 定</el-button><el-button @click="cancel">取 消</el-button></div></template></el-dialog>
但是在修改对话框中回显的数据是分,原因是数据库中存储的数据是整形,也就是分。所以在回显之前将单位的分转换为元回显,同时提交修改的时候需要将元转换为分。
/** 修改按钮操作 */
function handleUpdate(row) {reset();const _skuId = row.skuId || ids.valuegetSku(_skuId).then(response => {form.value = response.data;// 回显价格时,需要将分转为元。form.value.price /= 100;open.value = true;title.value = "修改商品管理";});
}
但是在新增是商品后,价格除了100。原因就是在提交的时候提交的单位是元
,在数据库存储的应该是分
,所以在新增修改在提交数据前需要将元转换为分。
代码:
/** 提交按钮 */
function submitForm() {proxy.$refs["skuRef"].validate(valid => {// 将价格单位从元转换为分form.value.price *= 100;if (valid) {if (form.value.skuId != null) {updateSku(form.value).then(response => {proxy.$modal.msgSuccess("修改成功");open.value = false;getList();});} else {addSku(form.value).then(response => {proxy.$modal.msgSuccess("新增成功");open.value = false;getList();});}}});
}
再次添加数据:
商品删除
需求
在删除商品时,需要判断此商品是否被售货机的货道关联,如果关联则无法删除
-
物理外键约束:通过在子表中添加一个外键列和约束,该列与父表的主键列相关联,由数据库维护数据的一致性和完整性
-
逻辑外键约束:在不使用数据库外键约束的情况下,通常在应用程序中通过代码来检查和维护数据的一致性和完整性
使用逻辑外键约束的原因:我们在新增售货机货道记录时暂不指定商品,货道表中的SKU_ID
有默认值0,而这个值在商品表中并不存在,那么物理外键约束会阻止货道表的插入,因为0并不指向任何有效的商品记录
代码实现
SkuServiceImpl
@Autowired
private IChannelService channelService;/*** 批量删除商品管理** @param skuIds 需要删除的商品管理主键* @return 结果*/
@Override
public int deleteSkuBySkuIds(Long[] skuIds)
{ //1. 判断商品的id集合是否有关联货道int count = channelService.countChannelBySkuIds(skuIds);if(count>0){throw new ServiceException("此商品被货道关联,无法删除");}//2. 没有关联货道才能删除return skuMapper.deleteSkuBySkuIds(skuIds);
}
IChannelService
/*** 根据商品id集合统计货道数量* @param skuIds* @return 统计结果*/
int countChannelBySkuIds(Long[] skuIds);
ChannelServiceImpl
/*** 根据商品id集合统计货道数量* @param skuIds* @return 统计结果*/
@Override
public int countChannelBySkuIds(Long[] skuIds) {return channelMapper.countChannelBySkuIds(skuIds);
}
ChannelMapper接口和xml
/*** 根据商品id集合统计货道数量* @param skuIds* @return 统计结果*/
int countChannelBySkuIds(Long[] skuIds);
<select id="countChannelBySkuIds" resultType="java.lang.Integer">select count(1) from tb_channel where sku_id in<foreach item="id" collection="array" open="(" separator="," close=")">#{id}</foreach>
</select>
当删除被货道关联的商品时
导出功能详解
在若依中已经默认将商品的导出功能实现了,主要是基于后端实现的。
Excel工具类底层是若依封装的,底层是基于apache的poi,原生apache的poi代码写起来非常繁琐,若依就将poi的基础功能封装和抽取,实现了文件的上传和下载功能。
主要包括文件的导入解析和文件的导出操作。
代码生成器默认生成好了文件的导出功能
批量导入
接口文档分析
两个请求投,Content-Type的作用告诉后端是一个表单文件提交,Authorization是告诉后端前端的身份令牌信息,只有登录以后前端具有了令牌才能上传,非则就会被拦截器拦截。返回结果就是标准的ajax result,有提示信息和业务状态码。
前端
需求
点击导入数据弹出导入数据弹窗,实现商品的批量导入
代码实现
打开导入按钮的对话框:
<el-col :span="1.5"><el-buttontype="warning"icon="Upload"@click="handleImport"v-hasPermi="['manage:sku:add']">导入</el-button></el-col><!-- 新增导入对话框 --><el-dialog title="数据导入" v-model="openImport" width="500px" append-to-body></el-dialog>// 打开导入按钮
const openImport = ref(false);
function handleImport() {openImport.value = true;
}
完善上传对话框:
使用element-plus官网的上传按钮的方法
<!-- 新增导入对话框 --><el-dialogtitle="数据导入"v-model="openImport"width="500px"append-to-body><!-- 上传文件组件 --><el-uploadref="uploadRef"class="upload-demo"action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15":auto-upload="false"><template #trigger><el-button type="primary">上传文件</el-button></template><el-button class="ml-3" type="success" @click="submitUpload">上传</el-button><template #tip><div class="el-upload__tip">上传文件仅支持,xls/xlsx格式,文件大小不得超过1M</div></template></el-upload></el-dialog>// 上传excel文件
const uploadRef = ref({});
function submitUpload() {uploadRef.value.submit();
}
请求地址是element官网提供的,需要将请求地址改为帝可得商品的批量导入路径,可以参考图片上传的组件。
/* 上传地址 */
const uploadExcelUrl = ref(import.meta.env.VITE_APP_BASE_API + "/manage/sku/import"); // 上传excel文件地址
/* 上传请求头 */
const headers = ref({ Authorization: "Bearer " + getToken() });
上传失败提供失败信息,上传成功以后关闭对话框,并提示上传成功的信息。
// 上传成功回调
function handleUploadSuccess(res, file) {if (res.code === 200) {proxy.$modal.msgSuccess(`上传excel文件成功`);// 关闭导入对话框openImport.value = false;// 刷新列表getList();} else {proxy.$modal.msgError(res.msg);}// 清理上传文件uploadRef.value.clearFiles();}// 上传失败回调
function handleUploadError(err, file) {proxy.$modal.msgError("上传excel文件失败");// 清理上传文件uploadRef.value.clearFiles();
}
限制文件上传的大小,在上传前loading加载中添加相应的方法。
// 上传前的校验
const props = defineProps({modelValue: [String, Object, Array],// 大小限制(MB)fileSize: {type: Number,default: 1,},// 文件类型, 例如['xls', 'xlsx']fileType: {type: Array,default: () => ["xls", "xlsx"],},});
// 上传前loading加载
function handleBeforeUpload(file) {let isExcel = false;if (props.fileType.length) {let fileExtension = "";if (file.name.lastIndexOf(".") > -1) {fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1);}isExcel = props.fileType.some(type => {if (file.type.indexOf(type) > -1) return true;if (fileExtension && fileExtension.indexOf(type) > -1) return true;return false;});}if (!isExcel) {proxy.$modal.msgError(`文件格式不正确, 请上传${props.fileType.join("/")}格式文件!`);return false;}if (props.fileSize) {const isLt = file.size / 1024 / 1024 < props.fileSize;if (!isLt) {proxy.$modal.msgError(`上传excel文件大小不能超过 ${props.fileSize} MB!`);return false;}}proxy.$modal.loading("正在上传excel文件,请稍候...");
}
上传成功或者失败之后关闭loading加载状态。
// 上传成功回调
function handleUploadSuccess(res, file) {if (res.code === 200) {proxy.$modal.msgSuccess(`上传excel文件成功`);// 关闭导入对话框openImport.value = false;// 刷新列表getList();} else {proxy.$modal.msgError(res.msg);}// 清理上传文件uploadRef.value.clearFiles();// 关闭加载状态proxy.$modal.closeLoading();
}// 上传失败回调
function handleUploadError(err, file) {proxy.$modal.msgError("上传excel文件失败");// 清理上传文件uploadRef.value.clearFiles();// 关闭加载状态proxy.$modal.closeLoading();
}
上传前的loading方法已经取消了多个文件上传,所以需要添加支持单个文件上传的属性limit。
<!-- 上传文件组件 --><el-uploadref="uploadRef"class="upload-demo":action="uploadExcelUrl":headers="headers":auto-upload="false":before-upload="handleBeforeUpload":on-success="handleUploadSuccess":on-error="handleUploadError":limit="1"><template #trigger><el-button type="primary">上传文件</el-button></template><el-button class="ml-3" type="success" @click="submitUpload">上传</el-button><template #tip><div class="el-upload__tip">上传文件仅支持,xls/xlsx格式,文件大小不得超过1M</div></template></el-upload>
后端
需求
请求路径和接收参数
代码实现
SkuController:
/*** 导出商品管理列表*/@PreAuthorize("@ss.hasPermi('manage:sku:export')")@Log(title = "商品管理", businessType = BusinessType.EXPORT)@PostMapping("/export")// 接收前端的请求条件skupublic void export(HttpServletResponse response, Sku sku){// 1. 调用service实现商品列表的查询List<Sku> list = skuService.selectSkuList(sku);// 2. 将查询的结果list集合,交给一个excel工具类,实现文件的下载和导出功能。在创建excel工具类的同时需要指定商品类型的字节码对象,因为它底层是一道反射来解析实体属性获取数据。ExcelUtil<Sku> util = new ExcelUtil<Sku>(Sku.class);// 3. 创建好工具类之后,通过exportExcel方法,实现excel文件的生成和下载功能。util.exportExcel(response, list, "商品管理数据");}
如何将商品名称封装到对应的skuName属性中,图片封装到image中?
实体类中每个属性的上方都有一个Excel注解,这个注解的作用就是将当前的列标题与属性建立关系,导出数据每列的标题也是由Excel注解提供的。
ISkuService:
/*** 批量新增商品管理* @param skuList* @return 结果*/
public int insertSkus(List<Sku> skuList);
/*** 批量新增商品管理* @param skuList* @return 结果*/
@Override
public int insertSkus(List<Sku> skuList) {return skuMapper.insertSkus(skuList);
}
SKuMapper:
/*** 批量新增商品管理* @param skuList* @return 结果*/
public int insertSkus(List<Sku> skuList);
根据通义灵码将单个商品添加xml改造为批量添加:
假如你是一名软件开发工程师,请把下面的xml改为批量新增,不需要动态sql
<insert id="insertSkus" parameterType="java.util.List">insert into tb_sku (sku_name,sku_image,brand_Name,unit,price,class_id,is_discount,create_time,update_time)values<foreach collection="list" item="sku" separator=",">(#{sku.skuName},#{sku.skuImage},#{sku.brandName},#{sku.unit},#{sku.price},#{sku.classId},#{sku.isDiscount},#{sku.createTime},#{sku.updateTime})</foreach></insert>
发现添加的数据没有创建日期,因为传入的excel文件并没有创建时间和更新时间的数据,在插入的时候会添加null。创建时间和更新时间在插入数据的时候会自动添加当前系统时间。是否打折不应该由商品决定,而是由商品所关联的售货机的策略模式判断的。在xml中去掉这些字段
<insert id="insertSkus" parameterType="java.util.List" useGeneratedKeys="true" keyProperty="skuId">insert into tb_sku (sku_name, sku_image, brand_Name, unit, price, class_id)values<foreach item="item" index="index" collection="list" separator=",">(#{item.skuName}, #{item.skuImage}, #{item.brandName}, #{item.unit}, #{item.price}, #{item.classId})</foreach>
</insert>
添加成功:
EasyExcel
介绍
官方地址:https://easyexcel.alibaba.com/
Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。
easyexcel重写了poi对07版Excel的解析,一个3M的excel用POI sax解析依然需要100M左右内存,改用easyexcel可以降低到几M,并且再大的excel也不会出现内存溢出;03版依赖POI的sax模式,在上层做了模型转换的封装,让使用者更加简单方便
项目集成
文档地址
1、dkd-common\pom.xml
模块添加整合依赖
<!-- excel处理工具-->
<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>4.0.1</version>
</dependency>
2、在dkd-common\
模块的ExcelUtil.java
新增easyexcel
导出导入方法
/*** 对excel表单默认第一个索引名转换成list(EasyExcel)* * @param is 输入流* @return 转换后集合*/
public List<T> importEasyExcel(InputStream is) throws Exception
{return EasyExcel.read(is).head(clazz).sheet().doReadSync();
}/*** 对list数据源将其里面的数据导入到excel表单(EasyExcel)* * @param list 导出数据集合* @param sheetName 工作表的名称* @return 结果*/
public void exportEasyExcel(HttpServletResponse response, List<T> list, String sheetName)
{try{EasyExcel.write(response.getOutputStream(), clazz).sheet(sheetName).doWrite(list);}catch (IOException e){log.error("导出EasyExcel异常{}", e.getMessage());}
}
3、Sku.java修改为@ExcelProperty
注解
package com.dkd.manage.domain;import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.annotation.write.style.HeadFontStyle;
import com.alibaba.excel.annotation.write.style.HeadRowHeight;
import com.dkd.common.annotation.Excel;
import com.dkd.common.core.domain.BaseEntity;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;/*** 商品管理对象 tb_sku** @author itheima* @date 2024-07-15*/
@ExcelIgnoreUnannotated// 注解表示在导出Excel时,忽略没有被任何注解标记的字段
@ColumnWidth(16)// 注解用于设置列的宽度
@HeadRowHeight(14)// 注解用于设置表头行的高度
@HeadFontStyle(fontHeightInPoints = 11)// 注解用于设置表头的字体样式
public class Sku extends BaseEntity
{private static final long serialVersionUID = 1L;/** 主键 */private Long skuId;/** 商品名称 */@Excel(name = "商品名称")@ExcelProperty("商品名称")private String skuName;/** 商品图片 */@Excel(name = "商品图片")@ExcelProperty("商品图片")private String skuImage;/** 品牌 */@Excel(name = "品牌")@ExcelProperty("品牌")private String brandName;/** 规格(净含量) */@Excel(name = "规格(净含量)")@ExcelProperty("规格(净含量)")private String unit;/** 商品价格 */@Excel(name = "商品价格")@ExcelProperty("商品价格")private Long price;/** 商品类型Id */@Excel(name = "商品类型Id")@ExcelProperty("商品类型Id")private Long classId;/** 是否打折促销 */private Integer isDiscount;// 其他略...
}
4、SkuController.java中将importExcel
改为importEasyExcel
/*** 导入商品管理列表*/
@PreAuthorize("@ss.hasPermi('manage:sku:add')")
@Log(title = "商品管理", businessType = BusinessType.IMPORT)
@PostMapping("/import")
public AjaxResult excelImport(MultipartFile file) throws Exception {ExcelUtil<Sku> util = new ExcelUtil<Sku>(Sku.class);List<Sku> skuList = util.importEasyExcel(file.getInputStream());return toAjax(skuService.insertSkus(skuList));
}
5、SkuController.java中将exportExcel
改为exportEasyExcel
/*** 导出商品管理列表*/
@PreAuthorize("@ss.hasPermi('manage:sku:export')")
@Log(title = "商品管理", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, Sku sku) {List<Sku> list = skuService.selectSkuList(sku);ExcelUtil<Sku> util = new ExcelUtil<Sku>(Sku.class);util.exportEasyExcel(response, list, "商品管理数据");
}
会出现easyexcel
与poi
版本不匹配的情况,在parent得xml中修改poi的版本号为5.2.3
即可。
货道关联商品
需求
在设备管理的操作中点击货道,会弹出一个货道设置的对话框。点击添加按钮会弹出商品的对话框,可以选择关联的商品。点击智能排货,会弹出智能排货的对话框。
对智能售货机内部的货道进行商品摆放的管理
此功能涉及四个后端接口
- 查询设备类型(已完成)
- 查询货道列表(待完成)
- 查询商品列表(已完成)
- 货道关联商品(待完成)
货道对话框
从资料中复制货道api请求js文件到api/manage目录下
从资料中复制货道的视图组件components到views/manage/vm目录下
修改index.vue视图,并引入css样式
<el-button link type="primary" @click="handleGoods(scope.row)" v-hasPermi="['manage:vm:edit']">货道</el-button><!-- 货道组件对话框 -->
<ChannelDialog :goodVisible="goodVisible" :goodData="goodData" @handleCloseGood="handleCloseGood"></ChannelDialog>
<!-- end --><script setup name="Vm">// ********************货道********************// 货道组件import ChannelDialog from './components/ChannelDialog.vue';const goodVisible = ref(false); //货道弹层显示隐藏const goodData = ref({}); //货道信息用来拿取 vmTypeId和innerCode// 打开货道弹层const handleGoods = (row) => {goodVisible.value = true;goodData.value = row;};// 关闭货道弹层const handleCloseGood = () => {goodVisible.value = false;};// ********************货道end********************
</script>
<style lang="scss" scoped src="./index.scss"></style>
查询货道列表
前端向后端发送的请求是list/售货机编号,返回的信息不仅有货道的信息还有货道对应的商品信息。
需要先根据售货机编号inner_code
去货道表
中查询货道信息,再根据货道信息中的sku_id
去商品表
中嵌套查询
商品信息。将货道信息和商品信息封装到channelVo
中返回。
ChannelVo
@Data
public class ChannelVo extends Channel {// 商品信息private Sku sku;
}
ChannelMapper和xml
/*** 根据售货机编号查询货道列表** @param innerCode* @return ChannelVo集合*/
List<ChannelVo> selectChannelVoListByInnerCode(String innerCode);
<resultMap type="ChannelVo" id="ChannelVoResult"><result property="id" column="id" /><result property="channelCode" column="channel_code" /><result property="skuId" column="sku_id" /><result property="vmId" column="vm_id" /><result property="innerCode" column="inner_code" /><result property="maxCapacity" column="max_capacity" /><result property="currentCapacity" column="current_capacity" /><result property="lastSupplyTime" column="last_supply_time" /><result property="createTime" column="create_time" /><result property="updateTime" column="update_time" /><association property="sku" javaType="Sku" column="sku_id" select="com.dkd.manage.mapper.SkuMapper.selectSkuBySkuId"/>
</resultMap><select id="selectChannelVoListByInnerCode" resultMap="ChannelVoResult"><include refid="selectChannelVo"/>where inner_code = #{innerCode}
</select>
IChannelService接口和实现
/*** 根据售货机编号查询货道列表** @param innerCode* @return ChannelVo集合*/
List<ChannelVo> selectChannelVoListByInnerCode(String innerCode);
/*** 根据售货机编号查询货道列表** @param innerCode* @return ChannelVo集合*/
@Override
public List<ChannelVo> selectChannelVoListByInnerCode(String innerCode) {return channelMapper.selectChannelVoListByInnerCode(innerCode);
}
ChannelController
/*** 根据售货机编号查询货道列表*/
@PreAuthorize("@ss.hasPermi('manage:channel:list')")
@GetMapping("/list/{innerCode}")
public AjaxResult lisetByInnerCode(@PathVariable("innerCode") String innerCode) {List<ChannelVo> voList = channelService.selectChannelVoListByInnerCode(innerCode);return success(voList);
}
货道关联商品
完成货道的配置,实现货道关联商品,并通过点击按钮向后端发送请求更新数据库。根据channel_code和inner_code可以确定货道的唯一标识,然后可以更新货道中的sku_id。主要关注的是前端的请求参数,接口返回的数据就是一个标准的AjaxResult
DTO对象不能直接更新数据库货道表的,需要将DTO转换为与数据库对应的PO对象,也就是持久化对象,实体的属性名与货道的属性名做到一一对应。在转换的过程中需要对前端的数据进行校验,确保当前货道是存在的,所以需要根据售货机的编号和货道的编号来查询当前货道的信息。如果查到了就给当前的货道实体类设置skuId,这是其中一个货道的设置,需要遍历查询转换设置sku_id。
ChannelSkuDto
设计一个数据传输对象专门接收innerCode和channelCode,因为目前数据库对应的实体类没有这样的属性与前端的参数所对应,channelList用于接收每个货道的信息,也需要设计一个数据传输对象。
// 某个货道对应的sku信息
@Data
public class ChannelSkuDto {private String innerCode; // 售货机编号private String channelCode; // 货道编号private Long skuId; // 商品ID
}
ChannelConfigDto
// 售货机货道配置,接收前端传输的整体数据
@Data
public class ChannelConfigDto {// 售货机编号private String innerCode;// 货道配置private List<ChannelSkuDto> channelList;
}
根据innerCode和channelCode查询对应的货道信息
ChannelMapper和xml
/*** 根据售货机编号和货道编号查询货道信息* @param innerCode* @param channelCode* @return 售货机货道*/
@Select("select * from tb_channel where inner_code =#{innerCode} and channel_code=#{channelCode}")
Channel getChannelInfo(@Param("innerCode") String innerCode, @Param("channelCode") String channelCode);/*** 批量修改货道* @param list* @return 结果*/
int batchUpdateChannel(List<Channel> list);
<update id="batchUpdateChannel" parameterType="java.util.List"><foreach item="channel" collection="list" separator=";">UPDATE tb_channel<set><if test="channel.channelCode != null and channel.channelCode != ''">channel_code = #{channel.channelCode},</if><if test="channel.skuId != null">sku_id = #{channel.skuId},</if><if test="channel.vmId != null">vm_id = #{channel.vmId},</if><if test="channel.innerCode != null and channel.innerCode != ''">inner_code = #{channel.innerCode},</if><if test="channel.maxCapacity != null">max_capacity = #{channel.maxCapacity},</if><if test="channel.currentCapacity != null">current_capacity = #{channel.currentCapacity},</if><if test="channel.lastSupplyTime != null">last_supply_time = #{channel.lastSupplyTime},</if><if test="channel.createTime != null">create_time = #{channel.createTime},</if><if test="channel.updateTime != null">update_time = #{channel.updateTime},</if></set>WHERE id = #{channel.id}</foreach>
</update>
mybatis在连接数据库的时候,默认情况下一个请求只能执行一条sql语句。之前的批量新增是insert into values()();
而生成的动态更新是update xxx;update xxx;
这就包含多个更新语句了。
application-druid.yml
允许mybatis框架在单个请求中发送多个sql语句
IChannelService接口和实现
/*** 货道关联商品* @param channelConfigDto* @return 结果*/
int setChannel(ChannelConfigDto channelConfigDto);
/*** 货道关联商品* @param channelConfigDto* @return 结果*/
@Override
public int setChannel(ChannelConfigDto channelConfigDto) {//1. dto转poList<Channel> list = channelConfigDto.getChannelList().stream().map(c -> {// 根据售货机编号和货道编号查询货道Channel channel = channelMapper.getChannelInfo(c.getInnerCode(), c.getChannelCode());if (channel != null) {// 货道更新skuIdchannel.setSkuId(c.getSkuId());// 货道更新时间channel.setUpdateTime(DateUtils.getNowDate());}return channel;}).collect(Collectors.toList());//2. 批量修改货道return channelMapper.batchUpdateChannel(list);
}
ChannelController
/*** 货道关联商品*/
@PreAuthorize("@ss.hasPermi('manage:channel:edit')")
@Log(title = "售货机货道", businessType = BusinessType.UPDATE)
@PutMapping("/config")
public AjaxResult setChannel(@RequestBody ChannelConfigDto channelConfigDto) {return toAjax(channelService.setChannel(channelConfigDto));
}