当前位置: 首页 > news >正文

若依——基于AI+若依框架的实战项目(实战篇(下))

目录

前言​

6. 设备管理

6.1 需求说明

6.2 生成基础代码

6.2.1 需求

6.2.2 步骤

①创建目录菜单

②添加数据字典

③配置代码生成信息

④下载代码并导入项目

6.3 设备类型改造

6.3.1 基础页面

需求

代码实现

6.4 设备管理改造

6.4.1 基础页面

需求

代码实现

6.4.2 新增设备

需求

VendingMachineServiceImpl

ChannelMapper接口和xml

IChannelService

ChannelServiceImpl

6.4.3 修改设备

需求

VendingMachineServiceImpl

6.5 设备状态改造

6.5.1 创建视图组件

6.5.2 创建二级菜单

6.5.3 改造视图组件

6.6 点位查看详情

7.策略管理

7.1 需求说明

7.2 生成基础代码

7.2.1 需求

7.2.2 步骤

①创建目录菜单

②配置代码生成信息

③下载代码并导入项目

7.3 策略管理改造

7.3.1 基础页面

需求

7.3.2 查看详情

需求

代码实现

7.3 设备策略分配

7.3.1 需求

7.3.2 代码实现

8.商品管理

8.1 需求说明

8.2 生成基础代码

8.2.1 需求

8.2.2 步骤

①创建目录菜单

②配置代码生成信息

③下载代码并导入项目

8.3 商品类型改造

8.3.1 基础页面

需求

8.3.2 代码实现

8.4 商品管理改造

8.4.1 基础页面

需求

代码实现

8.4.2 商品删除

需求

SkuServiceImpl

IChannelService

ChannelServiceImpl

ChannelMapper接口和xml

8.5 批量导入

8.5.1 需求

8.5.2 前端

8.5.3 后端

SkuController

SKuMapper

ISkuService

8.6 EasyExcel

8.6.1 介绍

8.6.2 项目集成

8.7 货道关联商品

8.7.1 需求

8.7.2 货道对话框

8.7.3 查询货道列表

ChannelVo

ChannelMapper和xml

IChannelService接口和实现

ChannelController

8.7.4 货道关联商品

ChannelSkuDto

ChannelConfigDto

ChannelMapper和xml

application-druid.yml

IChannelService接口和实现

ChannelController

9.工单管理

9.1 需求说明

9.2 基础代码生成

9.2.1 需求

9.2.2 步骤

①创建目录菜单

②添加数据字典

③配置代码生成信息

④下载代码并导入项目

⑤配置工单前端代码

9.2.3 修复bug

9.3 查询工单列表

9.3.1 需求

9.3.2 TaskVo 

9.3.3 TaskMapper

9.3.4 ITaskService

9.3.5 TaskController

9.4 获取运营人员列表

9.4.1 需求

9.4.2 VendingMachineMapper

9.4.3 IVendingMachineService

9.4.4 DkdContants

9.4.5 EmpController

9.5 获取运维人员列表

9.5.1 需求

9.5.2 EmpController

10.运营管理App

10.1 Android模拟器

10.2 Java后端

10.3 功能测试

10.3.1 投放工单

10.3.2 补货工单

10.4 源码介绍

11.设备屏幕端

11.1 设备屏幕

11.2 Java后端

11.3 功能测试

11.4 支付出货流程

11.5 源码介绍

12.订单管理(作业)

12.1 业务说明

结尾​


前言​

在当今数字化浪潮汹涌的时代,人工智能(AI)与高效开发框架的融合已成为推动软件开发领域不断前进的强大动力。若依框架,作为 Java 语言生态下备受瞩目的后台管理系统快速开发框架,以其模块化设计、前后端分离架构、强大的代码生成器以及全面的安全机制等显著优势,在众多项目中发挥着关键作用。而 AI 技术的蓬勃发展,为解决复杂业务问题、提升系统智能化水平提供了无限可能。本博客聚焦于 AI 与若依框架的实战结合,上篇已初步探索了项目的基础搭建与部分功能实现,实战篇(下)将进一步深入挖掘,带你领略如何利用二者合力,打造出功能更为强大、智能的应用系统,在实际项目开发中披荆斩棘,实现效率与创新的双赢。

6. 设备管理

6.1 需求说明

点位管理主要涉及到三个功能模块,业务流程如下:

  1. 新增设备类型: 允许管理员定义新的售货机型号,包括其规格和容量。

  2. 新增设备: 在新的设备类型定义后,系统应允许添加新的售货机实例,并将它们分配到特定的点位。

  3. 新增货道: 对于每个新添加的设备,系统应支持定义新的货道,后期用于关联相应的商品SKU。

对于设备和其他管理数据,下面是示意图:

  • 关系字段:vm_type_id、node_id、vm_id

  • 数据字典:vm_status(0未投放、1运营、3撤机)

  • 冗余字段:addr、business_type、region_id、partner_id(简化查询接口、提高查询效率)

 

6.2 生成基础代码

6.2.1 需求

使用若依代码生成器,生成设备类型、设备、货道前后端基础代码,并导入到项目中:

6.2.2 步骤

①创建目录菜单

创建设备管理目录菜单

②添加数据字典

先创建设备状态的字典类型

再创建设备状态的字典数据

③配置代码生成信息

导入三张表

④下载代码并导入项目

选中三张表生成下载

解压ruoyi.zip得到前后端代码和动态菜单sql

注意:货道动态菜单sql和前端不需要导入

调整二级菜单显示顺序(设备状态稍后完成)

6.3 设备类型改造

6.3.1 基础页面

需求

参考页面原型,完成基础布局展示改造

代码实现

在vmType/index.vue视图组件中修改

<!-- 查询条件 -->
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px"><el-form-item label="型号名称" prop="name"><el-input  v-model="queryParams.name" placeholder="请输入型号名称" clearable  @keyup.enter="handleQuery"/></el-form-item><el-form-item><el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button><el-button icon="Refresh" @click="resetQuery">重置</el-button></el-form-item></el-form><!-- 列表展示 -->
<el-table v-loading="loading" :data="vmTypeList" @selection-change="handleSelectionChange"><el-table-column type="selection" width="55" align="center" /><el-table-column label="型号名称" align="center" prop="name" /><el-table-column label="型号编码" align="center" prop="model" /><el-table-column label="设备图片" align="center" prop="image" width="100"><template #default="scope">
<image-preview :src="scope.row.image" :width="50" :height="50"/></template></el-table-column><el-table-column label="货道行" align="center" prop="vmRow" /><el-table-column label="货道列" align="center" prop="vmCol" /><el-table-column label="设备容量" align="center" prop="channelMaxCapacity" /><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:vmType:edit']">修改</el-button><el-button link type="primary" @click="handleDelete(scope.row)" v-hasPermi="['manage:vmType:remove']">删除</el-button></template></el-table-column></el-table><!-- 添加或修改设备类型管理对话框 -->
<el-dialog :title="title" v-model="open" width="500px" append-to-body><el-form ref="vmTypeRef" :model="form" :rules="rules" label-width="80px"><el-form-item label="型号名称" prop="name"><el-input v-model="form.name" placeholder="请输入型号名称" /></el-form-item><el-form-item label="型号编码" prop="model"><el-input v-model="form.model" placeholder="请输入型号编码" /></el-form-item><el-form-item label="货道数" prop="vmRow"><el-input-number v-model="form.vmRow" placeholder="请输入"  :min="1" :max="10"/>行&nbsp;&nbsp;<el-input-number v-model="form.vmCol" placeholder="请输入"  :min="1" :max="10"/>列</el-form-item><el-form-item label="货道容量" prop="channelMaxCapacity"><el-input-number v-model="form.channelMaxCapacity" placeholder="请输入"  :min="1" :max="10"/>个</el-form-item><el-form-item label="设备图片" prop="image"><image-upload v-model="form.image"/></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>

6.4 设备管理改造

6.4.1 基础页面

需求

参考页面原型,完成基础布局展示改造

代码实现

刷新设备表数据

update tb_vending_machine set partner_id=2 where id=80;
update tb_vending_machine set addr=(select address from tb_node where id = 1) where node_id=1;
update tb_vending_machine set addr=(select address from tb_node where id = 2) where node_id=2;
<!-- 查询条件 -->
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px"><el-form-item label="设备编号" prop="innerCode"><el-input v-model="queryParams.innerCode" placeholder="请输入设备编号" clearable @keyup.enter="handleQuery" /></el-form-item><el-form-item><el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button><el-button icon="Refresh" @click="resetQuery">重置</el-button></el-form-item></el-form><!-- 列表展示 -->
<el-table v-loading="loading" :data="vmList" @selection-change="handleSelectionChange"><el-table-column type="selection" width="55" align="center" /><el-table-column label="设备编号" align="center" prop="innerCode" /><el-table-column label="设备型号" align="center" prop="vmTypeId" ><template #default="scope"><div v-for="item in vmTypeList" :key="item.id"><span v-if="item.id==scope.row.vmTypeId">{{ item.name }}</span></div></template></el-table-column><el-table-column label="详细地址" align="center" prop="addr" /><el-table-column label="合作商" align="center" prop="partnerId" ><template #default="scope"><div v-for="item in partnerList" :key="item.id"><span v-if="item.id==scope.row.partnerId">{{ item.partnerName }}</span></div></template></el-table-column><el-table-column label="设备状态" align="center" prop="vmStatus"><template #default="scope"><dict-tag :options="vm_status" :value="scope.row.vmStatus" /></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:vm:edit']">修改</el-button></template></el-table-column></el-table><!-- 添加或修改设备管理对话框 -->
<el-dialog :title="title" v-model="open" width="500px" append-to-body><el-form ref="vmRef" :model="form" :rules="rules" label-width="80px"><el-form-item label="设备编号"><span>{{ form.innerCode == null ? '系统自动生成' : form.innerCode }}</span></el-form-item><el-form-item label="供货时间" v-if="form.innerCode != null"><span>{{ parseTime(form.lastSupplyTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span></el-form-item><el-form-item label="设备类型" v-if="form.innerCode != null"><div v-for="item in vmTypeList" :key="item.id"><span v-if="form.vmTypeId == item.id">{{ item.name }}</span></div></el-form-item><el-form-item label="设备容量" v-if="form.innerCode != null"><span>{{ form.channelMaxCapacity }}</span></el-form-item><el-form-item label="选择型号" prop="vmTypeId" v-if="form.innerCode == null"><!-- <el-input v-model="form.vmTypeId" placeholder="请输入设备型号" /> --><el-select v-model="form.vmTypeId" placeholder="请选择设备型号" style="width: 100%"><el-option v-for="item in vmTypeList" :key="item.id" :label="item.name" :value="item.id" /></el-select></el-form-item><el-form-item label="选择点位" prop="nodeId"><!-- <el-input v-model="form.nodeId" placeholder="请输入点位Id" /> --><el-select v-model="form.nodeId" placeholder="请选择点位" style="width: 100%"><el-option v-for="item in nodeList" :key="item.id" :label="item.nodeName" :value="item.id" /></el-select></el-form-item><el-form-item label="合作商" v-if="form.innerCode != null"><div v-for="item in partnerList" :key="item.id"><span v-if="form.partnerId == item.id">{{ item.partnerName }}</span></div></el-form-item><el-form-item label="所属区域" v-if="form.innerCode != null"><div v-for="item in regionList" :key="item.id"><span v-if="form.regionId == item.id">{{ item.regionName }}</span></div></el-form-item><el-form-item label="设备地址" v-if="form.innerCode != null"><span>{{ form.addr }}</span></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><script setup name="Vm">
import { listVmType } from "@/api/manage/vmType";
import { listPartner } from "@/api/manage/partner";
import { loadAllParams } from '@/api/page';
import { listNode } from '@/api/manage/node';
import { listRegion } from "@/api/manage/region";/* 查询设备类型列表 */
const vmTypeList = ref([]);
function getVmTypeList() {listVmType(loadAllParams).then((response) => {vmTypeList.value = response.rows;});
}
/* 查询合作商列表 */
const partnerList = ref([]);
function getPartnerList() {listPartner(loadAllParams).then((response) => {partnerList.value = response.rows;});
}
/* 查询点位列表 */
const nodeList = ref([]);
function getNodeList() {listNode(loadAllParams).then((response) => {nodeList.value = response.rows;});
}
/* 查询区域列表 */
const regionList = ref([]);
function getRegionList() {listRegion(loadAllParams).then((response) => {regionList.value = response.rows;});
}
getRegionList();
getPartnerList();
getNodeList();
getVmTypeList();
</script>

6.4.2 新增设备

需求

新增设备时,补充设备表其他字段信息,还需要根据售货机类型创建所属货道

我们了解到在新增设备时,添加设备和货道表,还包含点位和设备类型的查询,共涉及到四张表的操作。 这个过程需要我们仔细处理每个字段,确保数据的一致性和完整性

VendingMachineServiceImpl
@Autowired
private INodeService nodeService;
@Autowired
private IVmTypeService vmTypeService;
@Autowired
private IChannelService channelService;/*** 新增设备管理** @param vendingMachine 设备管理* @return 结果*/
@Transactional
@Override
public int insertVendingMachine(VendingMachine vendingMachine) {//1. 新增设备//1-1 生成8位编号,补充货道编号String innerCode = UUIDUtils.getUUID();vendingMachine.setInnerCode(innerCode); // 售货机编号//1-2 查询售货机类型表,补充设备容量VmType vmType = vmTypeService.selectVmTypeById(vendingMachine.getVmTypeId());vendingMachine.setChannelMaxCapacity(vmType.getChannelMaxCapacity());//1-3 查询点位表,补充 区域、点位、合作商等信息Node node = nodeService.selectNodeById(vendingMachine.getNodeId());BeanUtil.copyProperties(node, vendingMachine, "id");vendingMachine.setAddr(node.getAddress());//1-4 设备状态vendingMachine.setVmStatus(DkdContants.VM_STATUS_NODEPLOY);// 0-未投放(数据库有默认值,这个不写也不影响)vendingMachine.setCreateTime(DateUtils.getNowDate());// 创建时间vendingMachine.setUpdateTime(DateUtils.getNowDate());// 更新时间//1-5 保存int result = vendingMachineMapper.insertVendingMachine(vendingMachine);//2. 新增货道//2-1 声明货道集合List<Channel> channelList = new ArrayList<>();//2-2 双层for循环for (int i = 1; i <= vmType.getVmRow(); i++) { // 外层行for (int j = 1; j <= vmType.getVmCol(); j++) {// 内层列//2-3 封装channelChannel channel = new Channel();channel.setChannelCode(i + "-" + j);// 货道编号channel.setVmId(vendingMachine.getId());// 售货机idchannel.setInnerCode(vendingMachine.getInnerCode());// 售货机编号channel.setMaxCapacity(vmType.getChannelMaxCapacity());// 货道最大容量channel.setCreateTime(DateUtils.getNowDate());// 创建时间channel.setUpdateTime(DateUtils.getNowDate());// 更新时间channelList.add(channel);}}//2-4 批量新增channelService.batchInsertChannel(channelList);return result;
}
ChannelMapper接口和xml
/*** 批量新增售货机货道* @param channelList* @return 结果*/
public int batchInsertChannel(List<Channel> channelList);

<insert id="batchInsertChannel" parameterType="java.util.List">INSERT INTO tb_channel (channel_code, vm_id, inner_code, max_capacity, last_supply_time, create_time, update_time) VALUES<foreach collection="list" item="channel" separator=",">(#{channel.channelCode},#{channel.vmId},#{channel.innerCode},#{channel.maxCapacity},#{channel.lastSupplyTime},#{channel.createTime},#{channel.updateTime})</foreach></insert>
IChannelService
/*** 批量新增售货机货道* @param channelList* @return 结果*/
public int batchInsertChannel(List<Channel> channelList);
ChannelServiceImpl
/*** 批量新增售货机货道* @param channelList* @return 结果*/
@Override
public int batchInsertChannel(List<Channel> channelList) {return channelMapper.batchInsertChannel(channelList);
}

6.4.3 修改设备

需求

修改设备时,根据点位同步更新冗余字段信息

根据前端提交的点位ID,后端需要查询点位表,来获取点位的详细信息,包括详细地址、商圈类型、区域ID和合作商ID,获取到点位信息后,我们需要更新设备表中的相关冗余字段。

VendingMachineServiceImpl
/*** 修改设备管理** @param vendingMachine 设备管理* @return 结果*/
@Override
public int updateVendingMachine(VendingMachine vendingMachine) {//查询点位表,补充 区域、点位、合作商等信息Node node = nodeService.selectNodeById(vendingMachine.getNodeId());BeanUtil.copyProperties(node, vendingMachine, "id");// 商圈类型、区域、合作商vendingMachine.setAddr(node.getAddress());// 设备地址vendingMachine.setUpdateTime(DateUtils.getNowDate());// 更新时间return vendingMachineMapper.updateVendingMachine(vendingMachine);
}

6.5 设备状态改造

6.5.1 创建视图组件

创建vmStatus/index.vue视图组件

6.5.2 创建二级菜单

6.5.3 改造视图组件

<template><div class="app-container"><el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px"><el-form-item label="设备编号" prop="innerCode"><el-inputv-model="queryParams.innerCode"placeholder="请输入设备编号"clearable@keyup.enter="handleQuery"/></el-form-item><el-form-item><el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button><el-button icon="Refresh" @click="resetQuery">重置</el-button></el-form-item></el-form><!-- 列表展示 --><el-table v-loading="loading" :data="vmList" @selection-change="handleSelectionChange"><el-table-column label="序号" type="index" width="55" align="center" /><el-table-column label="设备编号" align="center" prop="innerCode" /><el-table-column label="设备型号" align="center" prop="vmTypeId" ><template #default="scope"><div v-for="item in vmTypeList" :key="item.id"><span v-if="item.id==scope.row.vmTypeId">{{ item.name }}</span></div></template></el-table-column><el-table-column label="详细地址" align="left" prop="addr" show-overflow-tooltip="true"/><el-table-column label="运营状态" align="center" prop="vmStatus"><template #default="scope"><dict-tag :options="vm_status" :value="scope.row.vmStatus"/></template></el-table-column><el-table-column label="设备状态" align="center" prop="vmStatus"><template #default="scope">{{ scope.row.runningStatus!=null? JSON.parse(scope.row.runningStatus).status==true?'正常':'异常' :'异常'}}</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="getVmInfo(scope.row)" v-hasPermi="['manage:vm:query']">查看详情</el-button></template></el-table-column></el-table><paginationv-show="total>0":total="total"v-model:page="queryParams.pageNum"v-model:limit="queryParams.pageSize"@pagination="getList"/><!-- 添加或修改设备管理对话框 --><el-dialog :title="title" v-model="open" width="500px" append-to-body></el-dialog></div></template><script setup name="Vm">
import { listVm, getVm, delVm, addVm, updateVm } from "@/api/manage/vm";
import{listVmType} from "@/api/manage/vmType";
import{listPartner} from "@/api/manage/partner";
import{loadAllParams} from "@/api/page";
import{listNode} from "@/api/manage/node";
import{listRegion} from "@/api/manage/region";
import { ref } from "vue";const { proxy } = getCurrentInstance();
const { vm_status } = proxy.useDict('vm_status');const vmList = ref([]);
const open = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const title = ref("");const data = reactive({form: {},queryParams: {pageNum: 1,pageSize: 10,innerCode: null,nodeId: null,businessType: null,regionId: null,partnerId: null,vmTypeId: null,vmStatus: null,runningStatus: null,policyId: null,},rules: {nodeId: [{ required: true, message: "点位Id不能为空", trigger: "blur" }],vmTypeId: [{ required: true, message: "设备型号不能为空", trigger: "blur" }],}
});const { queryParams, form, rules } = toRefs(data);/** 查询设备管理列表 */
function getList() {loading.value = true;listVm(queryParams.value).then(response => {vmList.value = response.rows;total.value = response.total;loading.value = false;});
}// 取消按钮
function cancel() {open.value = false;reset();
}// 表单重置
function reset() {form.value = {id: null,innerCode: null,channelMaxCapacity: null,nodeId: null,addr: null,lastSupplyTime: null,businessType: null,regionId: null,partnerId: null,vmTypeId: null,vmStatus: null,runningStatus: null,longitudes: null,latitude: null,clientId: null,policyId: null,createTime: null,updateTime: null};proxy.resetForm("vmRef");
}/** 搜索按钮操作 */
function handleQuery() {queryParams.value.pageNum = 1;getList();
}/** 重置按钮操作 */
function resetQuery() {proxy.resetForm("queryRef");handleQuery();
}// 多选框选中数据
function handleSelectionChange(selection) {ids.value = selection.map(item => item.id);single.value = selection.length != 1;multiple.value = !selection.length;
}/** 新增按钮操作 */
function handleAdd() {reset();open.value = true;title.value = "添加设备管理";
}/** 修改按钮操作 */
function getVmInfo(row) {reset();const _id = row.id || ids.valuegetVm(_id).then(response => {form.value = response.data;open.value = true;title.value = "设备详情";});
}/** 提交按钮 */
function submitForm() {proxy.$refs["vmRef"].validate(valid => {if (valid) {if (form.value.id != null) {updateVm(form.value).then(response => {proxy.$modal.msgSuccess("修改成功");open.value = false;getList();});} else {addVm(form.value).then(response => {proxy.$modal.msgSuccess("新增成功");open.value = false;getList();});}}});
}/** 删除按钮操作 */
function handleDelete(row) {const _ids = row.id || ids.value;proxy.$modal.confirm('是否确认删除设备管理编号为"' + _ids + '"的数据项?').then(function() {return delVm(_ids);}).then(() => {getList();proxy.$modal.msgSuccess("删除成功");}).catch(() => {});
}/** 导出按钮操作 */
function handleExport() {proxy.download('manage/vm/export', {...queryParams.value}, `vm_${new Date().getTime()}.xlsx`)
}/* 查询设备类型列表 */
const vmTypeList=ref([]);
function getVmTypeList() {listVmType(loadAllParams).then(response => {vmTypeList.value = response.rows;});
}/* 查询合作商列表 */
const partnerList=ref([]);
function getPartnerList() {listPartner(loadAllParams).then(response => {partnerList.value = response.rows;});
}/* 查询点位列表 */
const nodeList=ref([]);
function getNodeList() {listNode(loadAllParams).then(response => {nodeList.value = response.rows;});
}/* 查询区域列表 */
const regionList=ref([]);
function getRegionList() {listRegion(loadAllParams).then(response => {regionList.value = response.rows;});
}getRegionList();
getNodeList();
getPartnerList();
getVmTypeList();
getList();
</script>

6.6 点位查看详情

在node/index.vue视图组件中修改

<el-button link type="primary" @click="getNodeInfo(scope.row)" v-hasPermi="['manage:vm:list']">查看详情</el-button><!-- 点位详情对话框 -->
<el-dialog title="点位详情" v-model="nodeOpen" width="600px" append-to-body><el-table :data="vmList"><el-table-column label="序号" type="index" width="80" align="center" prop="id" /><el-table-column label="设备编号" align="center" prop="innerCode" /><el-table-column label="设备状态" align="center" prop="vmStatus"><template #default="scope"><dict-tag :options="vm_status" :value="scope.row.vmStatus" /></template></el-table-column><el-table-column label="最后一次供货时间" align="center" prop="lastSupplyTime" width="180"><template #default="scope"><span>{{ parseTime(scope.row.lastSupplyTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span></template></el-table-column></el-table></el-dialog><script setup name="Node">
import { listVm } from "@/api/manage/vm";
/* 引入设备状态数据字典 */
const { vm_status } = proxy.useDict('vm_status');/* 查看详情 */
const nodeOpen = ref(false);
const vmList = ref([]);
function getNodeInfo(row) {// 根据点位id,查询设备列表loadAllParams.nodeId = row.id;listVm(loadAllParams).then(response => {vmList.value = response.rows;nodeOpen.value = true;});
}
</script>

7.策略管理

7.1 需求说明

策略管理主要涉及到二个功能模块,业务流程如下:

  1. 新增策略: 允许管理员定义新的策略,包括策略的具体内容和参数(如折扣率)

  2. 策略分配: 将策略分配给一个或多个售货机。

对于策略和其他管理数据,下面是示意图:

  • 关系字段:policy_id

7.2 生成基础代码

7.2.1 需求

使用若依代码生成器,生成策略管理前后端基础代码,并导入到项目中:

7.2.2 步骤

①创建目录菜单

创建策略管理目录菜单

②配置代码生成信息

导入策略表

配置策略表(参考原型)

③下载代码并导入项目

选中策略表生成下载

解压ruoyi.zip得到前后端代码和动态菜单sql

7.3 策略管理改造

7.3.1 基础页面

需求

参考页面原型,完成基础布局展示改造

<!-- 列表展示 -->
<el-table v-loading="loading" :data="policyList" @selection-change="handleSelectionChange"><el-table-column type="selection" width="55" align="center" /><el-table-column label="序号" type="index" width="50" align="center" prop="policyId" /><el-table-column label="策略名称" align="center" prop="policyName" /><el-table-column label="策略方案" align="center" prop="discount" /><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:policy:edit']">修改</el-button><el-button link type="primary"  @click="handleDelete(scope.row)" v-hasPermi="['manage:policy:remove']">删除</el-button></template></el-table-column></el-table><!-- 添加或修改策略管理对话框 -->
<el-dialog :title="title" v-model="open" width="500px" append-to-body><el-form ref="policyRef" :model="form" :rules="rules" label-width="80px"><el-form-item label="策略名称" prop="policyName"><el-input v-model="form.policyName" placeholder="请输入策略名称" /></el-form-item><el-form-item label="策略方案" prop="discount"><el-input-number :min="1" :max="100" :precision="0" v-model="form.discount" placeholder="请输入策略" /></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>

7.3.2 查看详情

需求

点击查看详情,展示策略名称和该策略下的设备列表

代码实现

在policy/index.vue视图组件中修改

<el-button link type="primary"  @click="getPolicyInfo(scope.row)" v-hasPermi="['manage:vm:list']">查看详情</el-button><!-- 策略详情对话框 -->
<el-dialog v-model="policyOpen" title="策略详情" width="500px"><el-form-item label="策略名称" prop="policyName"><el-input v-model="form.policyName" placeholder="请输入策略名称" disabled /></el-form-item><label>包含设备:</label><el-table :data="vmList"><el-table-column label="序号" type="index" width="80" align="center" prop="id" /><el-table-column label="点位地址" align="left" prop="addr" show-overflow-tooltip /><el-table-column label="设备编号" align="center" prop="innerCode" /></el-table></el-dialog><script setup name="Policy">
import { listVm } from "@/api/manage/vm";
import { loadAllParams } from "@/api/page";/* 查看策略详情 */
const policyOpen = ref(false);
const vmList = ref([]);
function getPolicyInfo(row) {//1. 获取策略信息form.value = row;//2. 根据策略id,查询设备列表loadAllParams.policyId = row.policyId;listVm(loadAllParams).then(response => {vmList.value = response.rows;policyOpen.value = true;});
}
</script>

7.3 设备策略分配

7.3.1 需求

在设备管理页面中点击策略,对设备设置一个固定折扣,用于营销作用

7.3.2 代码实现

在vm/index.vue视图组件中修改

<el-button link type="primary" @click="handleUpdatePolicy(scope.row)" v-hasPermi="['manage:vm:edit']">策略</el-button><!-- 策略管理对话框 -->
<el-dialog title="策略管理" v-model="policyOpen" width="500px" append-to-body><el-form ref="vmRef" :model="form" label-width="80px"><el-form-item label="策略" prop="policyId"><el-select v-model="form.policyId" placeholder="请选择策略"><el-option v-for="item in policyList" :key="item.policyId" :label="item.policyName":value="item.policyId"></el-option></el-select></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><script setup name="Vm">
import { listPolicy } from '@/api/manage/policy';// 取消按钮
function cancel() {open.value = false;policyOpen.value=false; // 关闭策略对话框reset();
}/** 提交按钮 */
function submitForm() {proxy.$refs["vmRef"].validate(valid => {if (valid) {if (form.value.id != null) {updateVm(form.value).then(response => {proxy.$modal.msgSuccess("修改成功");open.value = false;getList();});} else {addVm(form.value).then(response => {proxy.$modal.msgSuccess("新增成功");open.value = false;getList();});}}});
}/* 设备策略分配 */
const policyList = ref([]);
const policyOpen = ref(false);
function handleUpdatePolicy(row) {//1. 为表单赋值设备id和策略idform.value.id = row.id;form.value.policyId = row.policyId;//2. 查询策略列表listPolicy(loadAllParams).then((response) => {policyList.value = response.rows;policyOpen.value = true;});
}
</script>

在VendingMachineServiceImpl中修改

/*** 修改设备管理** @param vendingMachine 设备管理* @return 结果*/
@Override
public int updateVendingMachine(VendingMachine vendingMachine)
{if (vendingMachine.getNodeId()!=null) {// 查询点位表,补充:区域、点位、合作商等信息Node node = nodeService.selectNodeById(vendingMachine.getNodeId());BeanUtil.copyProperties(node,vendingMachine,"id");// 商圈类型、区域、合作商vendingMachine.setAddr(node.getAddress());// 设备地址}vendingMachine.setUpdateTime(DateUtils.getNowDate());// 更新时间return vendingMachineMapper.updateVendingMachine(vendingMachine);
}

8.商品管理

8.1 需求说明

点位管理主要涉及到三个功能模块,业务流程如下:

  1. 新增商品类型: 定义商品的不同分类,如饮料、零食、日用品等。

  2. 新增商品: 添加新的商品信息,包括名称、规格、价格、类型等。

  3. 设备货道管理: 将商品与售货机的货道关联,管理每个货道的商品信息。

对于商品和其他管理数据,下面是示意图:

  • 关系字段:class_id、sku_id、vm_id

8.2 生成基础代码

8.2.1 需求

使用若依代码生成器,生成商品类型、商品管理前后端基础代码,并导入到项目中:

8.2.2 步骤

①创建目录菜单

创建商品管理目录菜单

②配置代码生成信息

导入二张表

配置商品类型表(参考原型)

③下载代码并导入项目

选中商品表和商品类型表生成下载

解压ruoyi.zip得到前后端代码和动态菜单sql

代码导入

8.3 商品类型改造

8.3.1 基础页面

需求

参考页面原型,完成基础布局展示改造

8.3.2 代码实现

在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="序号" type="index" width="50" align="center" prop="classId" /><el-table-column label="商品类型" align="center" prop="className" /><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>

修改全局异常处理器,添加以下内容

/*** 数据完整性异常*/
@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("数据完整性异常,请联系管理员");
}

8.4 商品管理改造

8.4.1 基础页面

需求

参考页面原型,完成基础布局展示改造

代码实现

在sku/index.vue视图组件中修改

<!-- 查询条件 -->
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px"><el-form-item label="商品名称" prop="skuName"><el-input v-model="queryParams.skuName" placeholder="请输入商品名称" clearable @keyup.enter="handleQuery" /></el-form-item><el-form-item><el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button><el-button icon="Refresh" @click="resetQuery">重置</el-button></el-form-item></el-form><!-- 列表展示 -->
<el-table v-loading="loading" :data="skuList" @selection-change="handleSelectionChange"><el-table-column type="selection" width="55" align="center" /><el-table-column label="序号" type="index" width="50" 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" ><template #default="scope"><el-tag>{{ scope.row.price / 100 }}元</el-tag></template></el-table-column> <el-table-column label="商品类型" align="center" prop="classId"><template #default="scope"><div v-for="item in skuClassList" :key="item.classI"><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><!-- 添加或修改商品管理对话框 -->
<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="请输入" />元</el-form-item><el-form-item label="商品类型" prop="classId"><!-- <el-input v-model="form.classId" placeholder="请输入商品类型Id" /> --><el-select v-model="form.classId" placeholder="请选择商品类型"><el-optionv-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="请输入规格" /></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><script setup name="Sku">
import { listSkuClass } from "@/api/manage/skuClass";
import { loadAllParams } from "@/api/page";
/** 修改按钮操作 */
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 = "修改商品管理";});
}
/** 提交按钮 */
function submitForm() {proxy.$refs["skuRef"].validate(valid => {if (valid) {// 将价格单位从元转换回分form.value.price*=100;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();});}}});
}
/* 查询商品类型列表 */
const skuClassList = ref([]);
function getSkuClassList() {listSkuClass(loadAllParams).then(response => {skuClassList.value = response.rows;});
}
getSkuClassList();
</script>

8.4.2 商品删除

需求

在删除商品时,需要判断此商品是否被售货机的货道关联,如果关联则无法删除

  • 物理外键约束:通过在子表中添加一个外键列和约束,该列与父表的主键列相关联,由数据库维护数据的一致性和完整性

  • 逻辑外键约束:在不使用数据库外键约束的情况下,通常在应用程序中通过代码来检查和维护数据的一致性和完整性

使用逻辑外键约束的原因:我们在新增售货机货道记录时暂不指定商品,货道表中的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>

8.5 批量导入

8.5.1 需求

点击导入数据弹出导入数据弹窗,实现商品的批量导入

8.5.2 前端

在sku/index.vue视图组件中修改

<!--  导入按钮-->
<el-col :span="1.5"><el-button type="warning" plain icon="Upload" @click="handleExcelImport" v-hasPermi="['manage:sku:add']">导入</el-button></el-col><!-- 数据导入对话框 -->
<el-dialog title="数据导入" v-model="excelOpen" width="400px" append-to-body><el-upload ref="uploadRef" class="upload-demo":action="uploadExcelUrl":headers="headers":on-success="handleUploadSuccess":on-error="handleUploadError":before-upload="handleBeforeUpload"         :limit="1":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><script setup name="Sku">
import { getToken } from "@/utils/auth";/* 打开数据导入对话框 */
const excelOpen = ref(false);
function handleExcelImport() {excelOpen.value = true;
}/* 上传地址 */
const uploadExcelUrl = ref(import.meta.env.VITE_APP_BASE_API + "/manage/sku/import"); // 上传excel文件地址
/* 上传请求头 */
const headers = ref({ Authorization: "Bearer " + getToken() });/* 上传excel */
const uploadRef = ref({});
function submitUpload() {uploadRef.value.submit()
}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,请稍候...");
}// 上传失败
function handleUploadError() {proxy.$modal.msgError("上传excel失败");uploadRef.value.clearFiles();proxy.$modal.closeLoading();
}// 上传成功回调
function handleUploadSuccess(res, file) {if (res.code === 200) {proxy.$modal.msgSuccess("上传excel成功");excelOpen.value = false;getList();}else{proxy.$modal.msgError("res.msg);} uploadRef.value.clearFiles();proxy.$modal.closeLoading();
}    
</script>

8.5.3 后端

SkuController
/*** 导入商品管理列表*/
@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.importExcel(file.getInputStream());return toAjax(skuService.insertSkus(skuList));
}
SKuMapper
/*** 批量新增商品管理* @param skuList* @return 结果*/
public int insertSkus(List<Sku> skuList);
<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>
ISkuService
/*** 批量新增商品管理* @param skuList* @return 结果*/
public int insertSkus(List<Sku> skuList);
/*** 批量新增商品管理* @param skuList* @return 结果*/
@Override
public int insertSkus(List<Sku> skuList) {return skuMapper.insertSkus(skuList);
}

8.6 EasyExcel

8.6.1 介绍

官方地址:EasyExcel官方文档 - 基于Java的Excel处理工具 | Easy Excel 官网 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模式,在上层做了模型转换的封装,让使用者更加简单方便

8.6.2 项目集成

文档地址 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改为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改为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, "商品管理数据");
}

8.7 货道关联商品

8.7.1 需求

对智能售货机内部的货道进行商品摆放的管理

8.7.2 货道对话框

① 从资料中复制货道api请求js文件到api/manage目录下

② 从资料中复制货道的视图组件到views/manage/vm目录下

③ 修改设备index.vue

<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>

8.7.3 查询货道列表

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

8.7.4 货道关联商品

ChannelSkuDto
// 某个货道对应的sku信息
@Data
public class ChannelSkuDto {private String innerCode;private String channelCode;private Long skuId;
}
ChannelConfigDto
// 售货机货道配置
@Data
public class ChannelConfigDto {// 售货机编号private String innerCode;// 货道配置private List<ChannelSkuDto> channelList;
}
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>
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));
}

9.工单管理

9.1 需求说明

工单是一种专业名词,是指用于记录、处理、跟踪一项工作的完成情况。

  • 管理人员登录后台系统选择创建工单,在工单类型里选择合适的工单类型,在设备编号里输入正确的设备编号。

  • 工作人员在运营管理App可以看到分配给自己的工单,根据实际情况选择接收工单并完成,或者拒绝/取消工单。

立可得工单分为两大类 :

  • 运营工单:运营人员来维护售货机商品,即补货工单。

  • 运维工单:运维人员来维护售货机设备,即投放工单、撤机工单、维修工单。

工单有四种状态: 1 待处理 2 进行中 3 已取消 4 已完成

对于工单和其他管理数据,下面是示意图:

  • 关系字段:task_id、 product_type_id、inner_code、user_id、assignor_id、region_id

  • 数据字典:task_status(1待办、2进行、3取消、4完成)

  • 数据字典:create_type(0自动、1手动)

  • PS:运营的工单包含补货信息,运维工单没有,所以运营工单需要单独创建补货工单

创建所有工单,都会在工单表和工单明细表插入记录吗?

创建运维类工单只会在工单表插入数据。

创建运营类工单(补货工单)会在工单表和工单明细表插入数据。

task_code和task_id有什么区别?

task_code是工单编号,具有业务规则 ,格式为年月日+当日序号。

task_id 为工单表数据唯一标识。

工单表的user_id和assignor_id分别是做什么的?

user_id是工单执行人的id(运维或运营)

assignor_id是工单指派人的id(创建工单的人)

9.2 基础代码生成

9.2.1 需求

使用若依代码生成器,生成工管理前后端基础代码,并导入到项目中:

9.2.2 步骤

①创建目录菜单

创建点位管理目录菜单

②添加数据字典

先创建工单状态的字典类型

再创建工单状态的字典数据

先创建工单创建类型的字典类型

再创建工单创建类型的字典数据

③配置代码生成信息

导入四张表

配置工单表(运维、运营)

创建自动补货任务表(工单原型)

④下载代码并导入项目

选中四张表生成下载

解压ruoyi.zip得到前后端代码和动态菜单sql

注意:工单管理只需要后端代码,前端使用资料中的

代码导入

⑤配置工单前端代码

1)从资料中复制工单api请求js文件到api/manage目录下

4)从资料中复制货道的视图组件到views/manage目录下

3)创建运营工单二级菜单

4)创建运维工单二级菜单

9.2.3 修复bug

在工单表中,有一个desc备注字段,这个desc是一个数据库关键字,所以我们在执行查询时,报了语法错误所以要给所有的desc增加反引号表示一个普通的sql字段

<sql id="selectTaskVo">select task_id, task_code, task_status, create_type, inner_code, user_id, user_name, region_id, `desc`, product_type_id, assignor_id, addr, create_time, update_time from tb_task
</sql>

9.3 查询工单列表

9.3.1 需求

运营和运营工单共享一套后端接口,通过特定的查询条件区分工单类型,并在返回结果中包含工单类型的详细信息

9.3.2 TaskVo 

@Data
public class TaskVo extends Task {// 工单类型private TaskType taskType;
}

9.3.3 TaskMapper

/*** 查询运维工单列表** @param task 运维工单* @return TaskVo集合*/
List<TaskVo> selectTaskVoList(Task task);
<resultMap type="taskVo" id="TaskVoResult"><result property="taskId" column="task_id"/><result property="taskCode" column="task_code"/><result property="taskStatus" column="task_status"/><result property="createType" column="create_type"/><result property="innerCode" column="inner_code"/><result property="userId" column="user_id"/><result property="userName" column="user_name"/><result property="regionId" column="region_id"/><result property="desc" column="desc"/><result property="productTypeId" column="product_type_id"/><result property="assignorId" column="assignor_id"/><result property="addr" column="addr"/><result property="createTime" column="create_time"/><result property="updateTime" column="update_time"/><association property="taskType" javaType="TaskType" column="product_type_id" select="com.dkd.manage.mapper.TaskTypeMapper.selectTaskTypeByTypeId"/>
</resultMap><select id="selectTaskVoList" resultMap="TaskVoResult"><include refid="selectTaskVo"/><where><if test="taskCode != null  and taskCode != ''">and task_code = #{taskCode}</if><if test="taskStatus != null ">and task_status = #{taskStatus}</if><if test="createType != null ">and create_type = #{createType}</if><if test="innerCode != null  and innerCode != ''">and inner_code = #{innerCode}</if><if test="userId != null ">and user_id = #{userId}</if><if test="userName != null  and userName != ''">and user_name like concat('%', #{userName}, '%')</if><if test="regionId != null ">and region_id = #{regionId}</if><if test="desc != null  and desc != ''">and `desc` = #{desc}</if><if test="productTypeId != null ">and product_type_id = #{productTypeId}</if><if test="assignorId != null ">and assignor_id = #{assignorId}</if><if test="addr != null  and addr != ''">and addr = #{addr}</if><if test="params.isRepair != null and params.isRepair=='true'">and product_type_id in (1,3,4)</if><if test="params.isRepair != null and params.isRepair=='false'">and product_type_id =2</if>order by create_time desc</where></select>

9.3.4 ITaskService

/*** 查询运维工单列表* @param task* @return TaskVo集合*/
List<TaskVo> selectTaskVoList(Task task);
/*** 查询运维工单列表* @param task* @return TaskVo集合*/
@Override
public List<TaskVo> selectTaskVoList(Task task) {return taskMapper.selectTaskVoList(task);
}

9.3.5 TaskController

/*** 查询工单列表*/
@PreAuthorize("@ss.hasPermi('manage:task:list')")
@GetMapping("/list")
public TableDataInfo list(Task task)
{startPage();List<TaskVo> voList = taskService.selectTaskVoList(task);return getDataTable(voList);
}

9.4 获取运营人员列表

9.4.1 需求

根据售货机编号获取负责当前区域下的运营人员列表

9.4.2 VendingMachineMapper

/*** 根据设备编号查询设备信息** @param innerCode* @return VendingMachine*/
@Select("select * from tb_vending_machine where inner_code=#{innerCode}")
VendingMachine selectVendingMachineByInnerCode(String innerCode);

9.4.3 IVendingMachineService

/*** 根据设备编号查询设备信息** @param innerCode* @return VendingMachine*/
VendingMachine selectVendingMachineByInnerCode(String innerCode);
/*** 根据设备编号查询设备信息** @param innerCode* @return VendingMachine*/
@Override
public VendingMachine selectVendingMachineByInnerCode(String innerCode) {return vendingMachineMapper.selectVendingMachineByInnerCode(innerCode);
}

9.4.4 DkdContants

/*** 员工启用*/
public static final Long EMP_STATUS_NORMAL = 1L;/*** 员工禁用*/
public static final Long EMP_STATUS_DISABLE = 0L;

9.4.5 EmpController

@Autowired
private IVendingMachineService vendingMachineService;/*** 根据售货机获取运营人员列表*/
@PreAuthorize("@ss.hasPermi('manage:emp:list')")
@GetMapping("/businessList/{innerCode}")
public AjaxResult businessList(@PathVariable("innerCode") String innerCode) {// 1.查询售货机信息VendingMachine vm = vendingMachineService.selectVendingMachineByInnerCode(innerCode);if (vm == null) {return error();}// 2.根据区域id、角色编号、员工状态查询运营人员列表Emp empParam = new Emp();empParam.setRegionId(vm.getRegionId());// 设备所属区域empParam.setStatus(DkdContants.EMP_STATUS_NORMAL);// 员工启用empParam.setRoleCode(DkdContants.ROLE_CODE_BUSINESS);// 角色编码:运营员return success(empService.selectEmpList(empParam));
}

9.5 获取运维人员列表

9.5.1 需求

根据售货机编号获取负责当前区域下的运维人员列表

9.5.2 EmpController

/*** 根据售货机获取运维人员列表*/
@PreAuthorize("@ss.hasPermi('manage:emp:list')")
@GetMapping("/operationList/{innerCode}")
public AjaxResult getOperationList(@PathVariable("innerCode") String innerCode) {// 1.查询售货机信息VendingMachine vm = vendingMachineService.selectVendingMachineByInnerCode(innerCode);if (vm == null) {return error("售货机不存在");        }// 2.根据区域id、角色编号、状态查询运维人员列表Emp empParam = new Emp();empParam.setRegionId(vm.getRegionId());// 设备所属区域empParam.setStatus(DkdContants.EMP_STATUS_NORMAL);// 员工启用empParam.setRoleCode(DkdContants.ROLE_CODE_OPERATOR);// 角色编码:维修员return success(empService.selectEmpList(empParam));
}
  1. 10.运营管理App

10.1 Android模拟器

本项目的App客户端部分已经由前端团队进行开发完成,并且以apk的方式提供出来,供我们测试使用,如果要运行apk,需要先安装安卓的模拟器。 可以选择国内的安卓模拟器产品,比如:网易mumu、雷电、夜神等。课程中使用网易mumu模拟器,官网地址:http://mumu.172.com/。 资料中提供了mumu安装包,大家安装到非中文路径即可。

10.2 Java后端

本项目运营管理App的java后端已开发完成,在资料中已提供源码,导入idea中即可

本项目连接的也是dkd数据库,如果密码不是root可以进行修改

10.3 功能测试

10.3.1 投放工单

帝可得管理端,创建新设备

帝可得管理端,创建投放工单

运营管理App端登录负责此工单员工,即可查看待办工单,可以选择拒绝、接受

如果点击接受,帝可得管理端工单状态改为进行

在进行工单界面,可以点击查看详情,选择取消、完成

10.3.2 补货工单

帝可得管理端,货道关联商品(大家练习时,可以全部关联)

帝可得管理端,创建补货工单

运营管理App端登录负责此工单员工,即可查看待办工单,可以选择拒绝、接受

如果点击接受,帝可得管理端工单状态改为进行

在进行工单界面,可以点击查看详情,选择取消、完成

如果点击完成工单,帝可得管理端工单状态改为完成

10.4 源码介绍

运营管理App的java后端技术栈:SpringBoot+MybatisPlus+阿里云短信

11.设备屏幕端

商品列表--选择支付方式--显示支付二维码--用户扫码完成支付

11.1 设备屏幕

本项目的设备屏幕客户端部分已经由前端团队进行开发完成,在资料中已提供源码,双击打开index.html即可

11.2 Java后端

本项目设备屏幕端的java后端已开发完成,在资料中已提供源码,导入idea中即可

11.3 功能测试

在设备屏幕端加上innerCode=设备编号,即可显示当前设备货道信息

11.4 支付出货流程

我们能够从屏幕上看到支付二维码,其实是经历了“长途跋涉” ,屏幕端实际上是一个H5页面,向后端发起支付请求,订单服务首先会创建订单,然后调用第三方支付来获得用于生成支付二维码的链接。 然后订单微服务将二维码链接返回给屏幕端,屏幕端生成二维码图片展示。

用户看到二维码后,拿出手机扫码支付,此时第三方支付平台确认用户支付成功后会回调订单服务。订单服务收到回调信息后修改订单状态,并通知设备发货

11.5 源码介绍

设备屏幕端的java后端技术栈:SpringBoot+MybatisPlus

12.订单管理(作业)

12.1 业务说明

用户在设备屏幕端下单后,系统将自动创建订单数据。后台管理人员可以查看这些订单信息,进行后续的管理操作。

我将跟大家分享一下实现订单管理功能的基本思路:

首先,根据原型和数据库表结构来生成订单管理的基础代码。

在生成代码的过程中,考虑是否需要引入数据字典来优化数据管理。

接下来,完成基础页面的改造,以适应订单管理的特定需求。

前端页面包含了日期选择范围的查询功能,这部分知识我们尚未讲解。

不过没关系,小伙们可以参考代码生成页面的日期搜索框,以及后端代码来实现此功能。

结尾​

通过这一系列关于 AI + 若依框架的实战探索,我们深入体验到了两者融合所释放出的巨大能量。从项目的一步步搭建,到各个功能模块借助 AI 技术实现智能化升级,每一个环节都凝聚着创新与实践的成果。在这个过程中,我们不仅掌握了若依框架高效开发的精髓,更学会了如何巧妙运用 AI 工具,为项目注入智能的灵魂。展望未来,随着技术的不断进步,AI 与若依框架的结合必将拥有更广阔的应用前景。无论是在优化现有系统的用户体验,还是拓展全新的业务场景方面,都大有可为。希望读者们能从这篇博客中汲取灵感,在自己的项目实践中充分发挥 AI + 若依框架的优势,创造出更多优秀的软件作品 。

相关文章:

若依——基于AI+若依框架的实战项目(实战篇(下))

目录 前言​ 6. 设备管理 6.1 需求说明 6.2 生成基础代码 6.2.1 需求 6.2.2 步骤 ①创建目录菜单 ②添加数据字典 ③配置代码生成信息 ④下载代码并导入项目 6.3 设备类型改造 6.3.1 基础页面 需求 代码实现 6.4 设备管理改造 6.4.1 基础页面 需求 代码实现 …...

SpringBoot项目瘦身指南:从臃肿到高效的优化实践

精心整理了最新的面试资料和简历模板&#xff0c;有需要的可以自行获取 点击前往百度网盘获取 点击前往夸克网盘获取 一、问题背景 SpringBoot的"约定优于配置"特性极大提升了开发效率&#xff0c;但默认配置可能导致项目逐渐臃肿。典型的症状包括&#xff1a; 打…...

运筹帷幄:制胜软件开发

运筹学在软件开发项目中的作用主要体现在复杂系统建模、资源优化和决策支持中。通过数学建模、算法设计和数据分析&#xff0c;运筹学能够帮助开发团队更高效地实现软件需求&#xff0c;尤其是在涉及资源分配、路径规划、调度优化等场景时。 案例&#xff1a;电商物流配送系统的…...

代码随想录|动态规划|18完全背包理论基础

leetcode:52. 携带研究材料&#xff08;第七期模拟笔试&#xff09; 题目 有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i]&#xff0c;得到的价值是value[i] 。每件物品都有无限个&#xff08;也就是可以放入背包多次&#xff09;&#xff0c;求解将哪些…...

52.个人健康管理系统小程序(基于springbootvue)

目录 1.系统的受众说明 2.开发环境与技术 2.1 MYSQL数据库 2.2 Java语言 2.3 微信小程序技术 2.4 SpringBoot框架 2.5 B/S架构 2.6 Tomcat 介绍 2.7 HTML简介 2.8 MyEclipse开发工具 3.系统分析 3.1 可行性分析 3.1.1 技术可行性 3.1.2 经济可行性 3.1.3 操作…...

大语言模型中的嵌入模型

本教程将拆解什么是嵌入模型、为什么它们在NLP中如此重要,并提供一个简单的Python实战示例。 分词器将原始文本转换为token和ID,而嵌入模型则将这些ID映射为密集向量表示。二者合力为LLMs的语义理解提供动力。图片来源:[https://tzamtzis.gr/2024/coding/tokenization-by-an…...

运维之 Centos7 防火墙(CentOS 7 Firewall for Operations and Maintenance)

运维之 Centos7 防火墙 1.介绍 Linux CentOS 7 防火墙/端口设置&#xff1a; 基础概念&#xff1a; 防火墙是一种网络安全设备&#xff0c;用于监控和控制网络流量&#xff0c;以保护计算机系统免受未经授权的访问和恶意攻击。Linux CentOS 7操作系统自带了一个名为iptables的…...

Ubuntu 20.04 出现问号图标且无法联网 修复

在 Ubuntu 中遇到网络连接问题&#xff08;如出现问号图标且无法联网&#xff09;&#xff0c;可以通过以下命令尝试重启网络服务&#xff1a; 1. 推荐先修改DNS 编辑 -> 虚拟机网络编辑器-> VMnet8 ->NAT 设置 -> DNS 设置 -> 设置DNS 服务器 DNS填什么 取决…...

联想M7400打印机怎么清零

一&#xff08;粉盒加粉后清零&#xff09;&#xff1a; 开机&#xff0c;打开前盖&#xff1b; 按下 “清除返回” 键&#xff0c;屏幕显示 “更换硒鼓&#xff1f;是否”&#xff1b; 按 “开始” 键&#xff0c;屏幕无显示&#xff1b; 按下 “” 号键 11 次&#xff0c…...

AIGC7——AIGC驱动的视听内容定制化革命:从Sora到商业化落地

引言&#xff1a;个性化视听时代的到来 2024年&#xff0c;OpenAI发布视频生成模型Sora&#xff0c;可生成60秒高清视频&#xff1b;中国团队推出的Vidu模型实现16秒镜头连贯生成。这些突破标志着AIGC正式进入高质量视听内容定制化阶段。据Gartner预测&#xff0c;到2027年&am…...

Git Restore 命令详解与实用示例

文章目录 Git Restore 命令详解与实用示例1. 恢复工作区文件到最后一次提交的状态基本命令示例恢复所有更改 2. 恢复某个文件到特定提交的状态基本命令示例 3. 恢复暂存区的文件基本命令示例恢复所有暂存的文件 git restore 的常见选项git restore 与 git checkout 比较总结 Gi…...

Sentinel全面解析与实战教程

Sentinel全面解析与实战教程 一、引言 在现代分布式系统中&#xff0c;随着业务的不断发展和流量的日益增长&#xff0c;保障系统的稳定性成为了重中之重。Sentinel作为一款优秀的高可用流量防护组件&#xff0c;为解决系统中的流量控制、熔断降级等问题提供了有效方案。本文…...

考研总结(初试篇)--双非勇闯985

考研终于告一段落了&#xff0c;也是被湖南大学拟录取了&#xff0c;写下这篇总结&#xff0c;回顾一下我的经历吧。 我是2024年3月开学的时候&#xff0c;正式确定考研的。当时纠结于定学校&#xff0c;长理和湖大。最后还是选择湖大&#xff0c;因为反正都是要好好准备的&am…...

【电路笔记】-触发器的转换

触发器的转换 文章目录 触发器的转换1、概述2、置位-复位SR触发器3、门控置位-复位(SR)触发器4、数据(D型)触发器5、JK触发器6、使用主从触发器的触发器转换7、(切换)T型触发器8、总结触发器是时序电路的基本构建模块,可以从一种形式转换到另一种形式,能够存储单个数据…...

QT 中的元对象系统(五):QMetaObject::invokeMethod的使用和实现原理

目录 1.简介 2.原理概述 3.实现分析 3.1.通过方法名调用方法的实现分析 3.2.通过可调用对象调用方法的实现分析 4.使用场景 5.总结 1.简介 QMetaObject::invokeMethod 是 Qt 框架中的一个静态方法&#xff0c;用于在运行时调用对象的成员函数。这个方法提供了一种动态调…...

基数排序算法解析与TypeScript实现

基数排序&#xff08;Radix Sort&#xff09;是一种高效的非比较型整数排序算法&#xff0c;通过逐位分配与收集的方式实现排序。本文将深入解析其工作原理&#xff0c;并给出完整的TypeScript实现。 一、算法原理 1. 核心思想 多关键字排序&#xff1a;将整数按位数切割成不同…...

oracle asm 相关命令和查询视图

有关asm磁盘的命令 添加磁盘 alter diskgroup data1 add disk /devices/diska*;---runs with a rebalance power of 5 , and dose not return until the rebalance operation is completealter diskgroup data1 add disk /devices/diskd* rebalance power 5 wait;查询 select …...

THUNLP_Multimodal_Excercise

背景 多模态大模型&#xff08;Multimodal Large Language Models, MLLM&#xff09;的构建过程中&#xff0c;模型结构、模型预测、指令微调以及偏好对齐训练是其中重要的组成部分。本次任务中&#xff0c;将提供一个不完整的多模态大模型结构及微调代码&#xff0c;请根据要求…...

AIGC6——AI的哲学困境:主体性、认知边界与“天人智一“的再思考

引言&#xff1a;当机器开始"思考" 2023年&#xff0c;Google工程师Blake Lemoine声称对话AI LaMDA具有"自我意识"&#xff0c;引发轩然大波。这一事件将古老的哲学问题重新抛回公众视野&#xff1a;​**机器能否拥有主体性&#xff1f;**从东方"天人…...

主题(topic)中使用键(key)来区分同一主题下的多个数据实例

在Fast DDS中&#xff0c;通过在主题&#xff08;topic&#xff09;中使用键&#xff08;key&#xff09;来区分同一主题下的多个数据实例&#xff0c;具体含义如下&#xff1a; 1. **主题&#xff08;Topic&#xff09;**&#xff1a;在DDS中&#xff0c;主题是数据的类型或类…...

[王阳明代数讲义]琴语言类型系统工程特性

琴语言类型系统工程特性 层展物理学组织实务与艺术与琴生生.物机.械科.技工.业研究.所软凝聚态物理开发工具包社会科学气质砥砺学人生意气场社群成员魅力场与心气微积分社会关系力学 意气实体过程图论信息编码&#xff0c;如来码导引 注意力机制道装Transformer架构的发展标度律…...

蜜蜡是什么?蜜蜡与琥珀的区别以及蜜蜡的收藏价值一览

蜜蜡是琥珀的一种&#xff0c;在物理成分和化学成分上都和琥珀没有区别&#xff0c;只是因其“色如蜜&#xff0c;光如蜡”而得名。蜜蜡形成于白垩纪时期&#xff0c;因形成时间较长&#xff0c;形成过程比较复杂等原因&#xff0c;种类较其他产地要多。 蜜蜡是有机矿物&#x…...

第五课:高清修复和放大算法

文章目录 Part.01 高清修复(Hi-Res Fix)Part.02 SD放大(SD Upscale)Part.03 附加功能放大Part.01 高清修复(Hi-Res Fix) 文生图中的高清修复/高分辨率修复/超分辨率修复先低分辨率抽卡,再高分辨率修复。不能突破显存限制放大重绘幅度安全范围是0.3-0.5,如果想让AI更有想象力0…...

SpringSecurity6.0 通过JWTtoken进行认证授权

之前写过一个文章&#xff0c;从SpringSecurity 5.x升级到6.0&#xff0c;当时是为了配合公司的大版本升级做的&#xff0c;里面的各项配置都是前人留下来的&#xff0c;其实没有花时间进行研究SpringSecurity的工作机制。现在新东家有一个简单的系统要搭建&#xff0c;用户的认…...

TypeScript工程集成

以下是关于 TypeScript 工程集成 的系统梳理,涵盖基础配置、进阶优化、开发规范及实际场景的注意事项,帮助我们构建高效可靠的企业级 TypeScript 项目: 一、基础知识点 1. 项目初始化与配置 tsconfig.json 核心配置:{"compilerOptions": {"target": &…...

网络空间安全(51)邮件函数漏洞

前言 邮件函数漏洞&#xff0c;特别是在PHP环境中使用mail()函数时&#xff0c;是一个重要的安全问题。 一、概述 在PHP中&#xff0c;mail()函数是一个用于发送电子邮件的内置函数。其函数原型为&#xff1a; bool mail ( string $to , string $subject , string $message [, …...

怎么让一台云IPPBX实现多家酒店相同分机号码一起使用

下面用到的IPPBX是我们二次开发后的成品&#xff0c;支持各种云服务器一键安装&#xff0c;已经写好了一键安装包&#xff0c;自动识别系统环境&#xff0c;安装教程这里就不再陈述了&#xff01; 前言需求 今天又遇到了一个客户咨询&#xff0c;关于部署一台云IPPBX&#xf…...

qt tcpsocket编程遇到的并发问题

1. 单个socket中接收消息的方法要使用局部变量而非全局&#xff0c;避免消息频发时产生脏数据 优化后的关键代码 recieveInfo() 方法通过返回内部处理后的 msg 进行传递if (data.indexOf("0103") -1) { 这里增加了判断, 对数据&#xff08;非注册和心跳&#xff0…...

U盘实现——BOT 常用命令

文章目录 U盘实现——BOT 常用命令命令格式CBWCSW数据传输条件命令传输数据传输状态传输命令汇总INQUIRY Command:12h数据格式抓包READ FORMAT CAPACITIES Command: 23h数据格式抓包READ CAPACITY Command: 25h数据格式抓包TEST UNIT READY Command: 00h数据格式抓包WRITE(10) …...

JavaScript DOM 节点操作

目录 一、DOM 节点 节点类型&#xff08;Node Types&#xff09; 二、查找节点 1.查找父节点 1. parentNode 2. parentElement 2.查找子节点 1. childNodes 2. children 3. firstChild / lastChild 4. firstElementChild / lastElementChild 3.查找兄弟节点 1. pre…...

JDBC常用的接口

一、什么是JDBC JDBC是Java语言连接数据库的接口规范。 二、JDBC的体系 1、Java官方提供一个操作数据库的抽象接口 抽象接口有很多的接口和抽象类。 例如&#xff1a;Driver、Connection、Statement。 2、各个数据库厂商提供各自的Java实现类 需要各自实现具体的细节。 例如&am…...

【DLI】Generative AI with Diffusion Models通关秘籍

Generative AI with Diffusion Models&#xff0c;加载时间在20分钟左右&#xff0c;耐心等待。 6.2TODO 这里是在设置扩散模型的参数&#xff0c;代码里的FIXME部分需要根据上下文进行替换。以下是各个FIXME的替换说明&#xff1a; 1.a_bar 是 a 的累积乘积&#xff0c;在 …...

TP6图片操作 Image::open 调用->save()方法时候报错Type is not supported

错误提示&#xff1a; { "code": 0, "msg": "Type is not supported", "data": { "code": 0, "line": 50, "file": "/www/wwwroot/ytems/vendor/topthink/framework/src/think…...

11_常用函数

文章目录 一、概述二、字符函数2.1、获取字符串所占字节数2.2、获取字符个数2.3、拼接字符串2.4、大小写转换2.5、获取子串2.6、获取子串第一次出现的索引2.7、去除字符串前后子字符串2.7.1、去掉左侧空格2.7.2、去掉右侧空格 2.8、左右填充2.9、字符串替换 三、数学函数3.1、四…...

《inZOI(云族裔)》50+MOD整合包

载具 RebelCore - 年龄和时间 mod启动器 优化补丁 去除雾气 坦克模型 菜单 前置 跳过启动 更好性能 等 共计50MOD整合 在游戏的世界里&#xff0c;追求更丰富、更优质的体验是玩家们永恒的主题。RebelCore 这款游戏通过精心打造的 50MOD 整合&#xff0c;为玩家带来了前所未有的…...

【技术报告】GPT-4o 原生图像生成的应用与分析

【技术报告】GPT-4o 原生图像生成的应用与分析 1. GPT-4o 原生图像生成简介1.1 文本渲染能力1.2 多轮对话迭代1.3 指令遵循能力1.4 上下文学习能力1.5 跨模态知识调用1.6 逼真画质与多元风格1.7 局限性与安全性 2. GPT-4o 技术报告2.1 引言2.2 安全挑战、评估与缓解措施2.2.1 安…...

拼多多延迟发货解答2

三、延迟发货处理标准 延迟发货极大地影响了消费者的购物体验&#xff0c;平台对延迟发货行为也有相应的处理标准&#xff0c;因此各位商家一定不要以为延迟发货是小事儿。延迟发货处理标准具体可查看《拼多多发货规则》第3条。 商家发生延迟发货的&#xff0c;拼多多平台有权…...

RTOS基础 -- NXP M4小核的RPMsg-lite与端点机制回顾

一、RPMsg-lite与端点机制回顾 在RPMsg协议框架中&#xff1a; Endpoint&#xff08;端点&#xff09; 是一个逻辑通信端口&#xff0c;由本地地址&#xff08;local addr&#xff09;、远程地址&#xff08;remote addr&#xff09;和回调函数组成。每个消息都会发送到特定的…...

25.4.3学习总结【Java】

又是一道错题&#xff1a; 1. 班级活动https://www.lanqiao.cn/problems/17153/learning/?page1&first_category_id1&sortdifficulty&asc1&second_category_id3 问题描述 小明的老师准备组织一次班级活动。班上一共有 n 名 (n 为偶数) 同学&#xff0c;老师…...

市场交易策略优化与波动管理

市场交易策略优化与波动管理 在市场交易中&#xff0c;策略的优化和波动的管理至关重要。市场价格的变化受多种因素影响&#xff0c;交易者需要根据市场环境动态调整策略&#xff0c;以提高交易的稳定性&#xff0c;并有效规避市场风险。 一、市场交易策略的优化方法 趋势交易策…...

TypeScript 元数据操作 API 及示例

TypeScript 元数据操作 API 及示例 1. 配置环境 安装依赖 npm install reflect-metadatatsconfig.json 配置 {"compilerOptions": {"experimentalDecorators": true,"emitDecoratorMetadata": true,"target": "ES6"} }2…...

蓝桥杯刷题记录【并查集001】(2024)

主要内容&#xff1a;并查集 并查集 并查集的题目感觉大部分都是模板题&#xff0c;上板子&#xff01;&#xff01; class UnionFind:def __init__(self, n):self.pa list(range(n))self.size [1]*n self.cnt ndef find(self, x):if self.pa[x] ! x:self.pa[x] self.fi…...

搜广推校招面经六十六

高德推荐算法 一、介绍Transformer中的位置编码&#xff08;Positional Encoding&#xff09; 在 Transformer 结构中&#xff0c;由于模型没有内置的序列信息&#xff08;不像 RNN 那样有时间步的顺序依赖&#xff09;&#xff0c;需要通过**位置编码&#xff08;Positional…...

Java高频面试题2:集合框架

一、集合框架概述 1. 常见的集合框架有哪些&#xff1f; Collection&#xff1a;存储单个元素的集合。 List&#xff08;有序、可重复&#xff09;&#xff1a;ArrayList&#xff08;动态数组&#xff09;、LinkedList&#xff08;双向链表&#xff09;。Set&#xff08;无序…...

06-公寓租赁项目-后台管理-公寓管理篇

尚庭公寓项目/公寓管理模块 https://www.yuque.com/pkqzyh/qg2yge/5ba67653b51379d18df61b9c14c3e946 一、属性管理 属性管理页面包含公寓和房间各种可选的属性信息&#xff0c;其中包括房间的可选支付方式、房间的可选租期、房间的配套、公寓的配套等等。其所需接口如下 1.1…...

目前主流OCR/语义理解/ASR

OCR 基于多篇专业评测的结果&#xff0c;以下是目前免费开源OCR工具的推荐排名&#xff08;侧重中文场景&#xff09;&#xff1a; 1. RapidOCR 优势&#xff1a;基于PaddleOCR优化&#xff0c;在印刷中文、自然场景文字识别中综合评分第一&#xff0c;支持180度旋转和低对比…...

Selenium 元素定位方法详解

Selenium 提供了多种元素定位方式&#xff0c;掌握这些方法是进行 Web 自动化测试的基础。以下是主要的元素定位方法及其使用示例&#xff1a; 1. 基本定位方法 1.1 通过 ID 定位 element driver.find_element(By.ID, "element_id") 1.2 通过 Name 定位 element …...

fastGPT—前端开发获取api密钥调用机器人对话接口(HTML实现)

官网文档链接&#xff1a;OpenAPI 介绍 | FastGPT 首先按照文档说明创建api密钥 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-sca…...

c语言数据结构--------拓扑排序和逆拓扑排序(Kahn算法和DFS算法实现)

#include <stdio.h> #include <string.h> #include <stdbool.h> #include <stdlib.h>//使用卡恩算法(Kahn)和深度优先算法(DFS)实现//拓扑排序和逆拓扑排序//拓扑排序和逆拓扑排序顶点顺序相反//图&#xff0c;邻接矩阵存储 #define MaxVertexNum 100 …...

日期类的实现

本文运用c类和对象中的构造函数&#xff0c; 析构函数 &#xff0c;拷贝构造函数 &#xff0c; 赋值运算符重载等为大家模拟实现日期类的操作 #define _CRT_SECURE_NO_WARNINGS 1 #include"date.h" void Date:: showinfo() {cout << _year << "年&…...