项目介绍
帝可得是什么?
帝可得是一个基于物联网概念下的智能售货机运营管理系统

售货机术语
售货机业务中有一些专业名词

角色与功能
一个完整的售货机系统由五端五角色组成:

产品原型
帝可得项目点击链接立即查看 https://codesign.qq.com/s/426304924036117

库表设计
系统后台基础数据表关系说明:

认识AI
AIGC
AI(ArtificialIntelligence): 即人工智能,是指通过计算机系统模拟人类思维和行为一种技术, 它通过机器学习、深度学习等算法,使计算机具备对数据分析、理解、推理和决策的能力
AIGC(AIGeneratedContent):是AI领域的一个应用分支,专注于利用AI技术自动生成内容

AI大模型: 是一种机器学习模型,经过大量数据训练,具备复杂计算能力的人工智能,其中包括生成内容

国内常见的通用大模型(AGI)产品:

提示工程
提示(Prompt)是我们对大模型提出的问题



- 角色: 给AI定义一个最匹配任务的角色,比如:「你是一位专业的博客作者」
- 指示: 对任务进行描述,比如:「撰写一篇关于最新AI技术发展的文章」
- 上下文: 给出与任务相关的其它背景信息
- 例子: 必要时给出举例,[实践证明其对输出正确性有帮助]
- 输入: 任务的输入信息; 在提示词中明确的标识出输入
- 输出: 输出的格式描述,以便后继模块自动解析模型的输出结果,比如(JSON、Java)
角色: 你是一位专业的博客作者。
指示: 撰写一篇关于最新AI技术发展的文章。
上下文: 文章应该涵盖AI技术的当前状态和未来趋势。
例子: 可以引用最近的AI技术突破和行业专家的见解。
输入: 当前AI技术的相关信息和数据。
输出: 一篇结构清晰、观点鲜明的文章草稿。
你是一个软件工程师,帮我生成MysQL的表结构
需求如下:
1,课程管理表,表名tb_course,字段有主键id、课程编码、课程学科、课程名称、课程价格、适用人群、课程介绍
其他要求:
1,每个表中都有创建时间(create_time)、修改时间(date_time)、创建人(create_by)、修改人(update_by)、备注(remark)这些字段
2,每个表的主键都是自增的
3,课程价格是整型、课程编码是字符串
4,请为每个字段都添加上comment
5,帮我给生成的表中插入一些IT课程示例数据
课程学科: Java、人工智能、大数据
适用人群: 小白学员、中级程序员
你是一个软件工程师,现在要根据数据库的sq1脚本,编写数据库说明文档,sql脚本如下:
CREATE TABLE `tb_course` (
`id` INT AUTO_INCREMENT COMMENT '主键ID',
`course_code` VARCHAR(255) NOT NULL COMMENT '课程编码',
`course_subject` VARCHAR(100) NOT NULL COMMENT '课程学科',
`course_name` VARCHAR(255) NOT NULL COMMENT '课程名称',
`course_price` INT COMMENT '课程价格',
`target_audience` VARCHAR(100) COMMENT '适用人群',
`course_introduction` TEXT COMMENT '课程介绍',
`create_time` DATETIME COMMENT '创建时间',
`update_time` DATETIME COMMENT '修改时间',
`create_by` VARCHAR(64) COMMENT '创建人',
`update_by` VARCHAR(64) COMMENT '修改人',
`remark` VARCHAR(255) COMMENT '备注',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='课程管理表';
输出要求是:
1,每个表以及每个表的字段都要详细说明,包括,字段名称、类型、作用
2,使用markdown的输出格式,字段的描述需要使用表格展示
3,如果表之间有关系,需要描述清楚表之间的关系
你是一个软件工程师,为了方便理解代码执行流程,需要给出代码执行的流程图,代码如下:
****代码****
输出: 请使用mermaid语言来描述这个方法的流程
graph TD
A[查询售货机] –> B{售货机存在?}
B — no –> C[抛出错误:设备不存在]
B — yes –> D[校验售货机状态与工单类型]
D –> E{校验通过?}
E — no –> F[抛出错误:状态或类型不符]
E — yes –> G[查询是否存在未完成同类型工单]
G –> H{存在未完成工单?}
H — yes –> I[抛出错误:存在未完成同类型工单]
H — no –> J[查询员工]
J –> K{员工存在?}
K — no –> L[抛出错误:员工不存在]
K — yes –> M[校验员工区域与售货机区域是否一致]
M –> N{区域不一致?}
N — yes –> O[抛出错误:非同区域员工不能接受工单]
N — no –> P[构造工单信息]
P –> Q[保存工单基本信息至数据库]
Q –> R{工单类型是否为补货?}
R — no –> S[结束流程]
R — yes –> T[检查补货工单明细]
T –> U{明细为空?}
U — yes –> V[抛出错误:补货工单明细不能为空]
mermaid看起来不直观,可以把graph TD交给ai识别或者mackdown识别

生成代码
代码生成算是比较常规的方案,用的也比较多,分为了几种情况
- 给出表结构的ddI,可以输出这个表的增删改查的所有代码。
- 给出表结构的dlIl,可以输出增删改查的接口文档
- 例1-给出实体类,帮助编写getter、setter、toString、构造方法等等
- 例2-给出一个controller,帮助编写swagger注解等
- 例1-根据接口文档提取dto类或者vo类
项目搭建
搭建后端项目
- 后端仓库地址: https://gitee.com/yudian1991/dkd-parent.git
- 资料中提供了离线版项目可以使用





搭建前端工程
- 前端仓库地址: https://gitee.com/yudian1991/dkd-vue.git
- 资料中提供了离线版的工程可以使用


点位管理
需求说明
业务场景:
公司计划在北京的高流量商业和居民区与潜在合作商洽谈,确定点位部署智能售货机,提供便捷的购买服务

库表设计
参考页面原型和具体需求完成库表设计




使用AI生成建表语句
你是一位软件工程师,帮我生成MySQL的表结构
需求如下:
1,区域表,表名tb_region,字段有主键id、区域名称
2,合作商表,表名tb_partner,字段有主键id、合作商名称、联系人、联系电话、分成比例(int类型)、账号、密码
3,点位表,表名tb_node,字段有主键id、点位名称、详细地址、商圈类型(int类型)
其他要求:
1,每张表中都有创建时间(create_time)、修改时间(update_time)、创建人(create_by)、修改人(update_by)、备注(remark)这些字段
2,每张表的主键都是自增的
3,区域与点位是一对多的关系,合作商与点位是一对多的关系,请用字段表示出来,并建立外键约束
4,请为每个字段都添加上comment
5,帮我给生成的表中插入一些北京城市相关区域、点位、合作商的测试数据
— 1. 创建区域表 tb_region
CREATE TABLE tb_region (
id INT AUTO_INCREMENT PRIMARY KEY COMMENT '区域主键ID',
region_name VARCHAR(255) NOT NULL COMMENT '区域名称',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
create_by VARCHAR(64) DEFAULT '' COMMENT '创建人',
update_by VARCHAR(64) DEFAULT '' COMMENT '修改人',
remark TEXT COMMENT '备注'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='区域表';
— 1. 插入北京区域测试数据
INSERT INTO tb_region (region_name) VALUES ('北京市朝阳区'),('北京市海淀区'),('北京市东城区');
— 2. 创建合作商表 tb_partner
CREATE TABLE tb_partner (
id INT AUTO_INCREMENT PRIMARY KEY COMMENT '合作商主键ID',
partner_name VARCHAR(255) NOT NULL COMMENT '合作商名称',
contact_person VARCHAR(64) DEFAULT '' COMMENT '联系人',
contact_phone VARCHAR(15) DEFAULT '' COMMENT '联系电话',
profit_ratio INT DEFAULT 0 COMMENT '分成比例(%)',
account VARCHAR(64) NOT NULL COMMENT '账号',
password VARCHAR(64) NOT NULL COMMENT '密码',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
create_by VARCHAR(64) DEFAULT '' COMMENT '创建人',
update_by VARCHAR(64) DEFAULT '' COMMENT '修改人',
remark TEXT COMMENT '备注'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='合作商表';
— 2. 插入合作商测试数据
INSERT INTO tb_partner (partner_name, contact_person, contact_phone, profit_ratio, account, password) VALUES
('合作商A', '张三', '13800138000', 30, 'a001', 'pwdA'),
('合作商B', '李四', '13900139000', 25, 'a002', 'pwdB');
— 3. 创建点位表 tb_node(含外键关联区域/合作商)
CREATE TABLE tb_node (
id INT AUTO_INCREMENT PRIMARY KEY COMMENT '点位主键ID',
node_name VARCHAR(255) NOT NULL COMMENT '点位名称',
address VARCHAR(255) DEFAULT '' COMMENT '详细地址',
business_type INT DEFAULT 0 COMMENT '商圈类型',
region_id INT COMMENT '关联区域ID',
partner_id INT COMMENT '关联合作商ID',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
create_by VARCHAR(50) DEFAULT '' COMMENT '创建人',
update_by VARCHAR(50) DEFAULT '' COMMENT '修改人',
remark TEXT COMMENT '备注',
— 外键约束:关联区域表
FOREIGN KEY (region_id) REFERENCES tb_region(id) ON DELETE CASCADE ON UPDATE CASCADE,
— 外键约束:关联合作商表
FOREIGN KEY (partner_id) REFERENCES tb_partner(id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='点位表';
— 3. 插入点位测试数据(关联上述区域和合作商)
INSERT INTO tb_node (node_name, address, business_type, region_id, partner_id) VALUES
('三里屯站点', '北京市朝阳区三里屯路', 1, 1, 1),
('五道口站点', '北京市海淀区五道口', 2, 2, 2);

对于点位管理数据模型,下面是示意图:


生成基础代码
使用若依代码生成器,生成区域管理、合作商管理、点位管理前后端基础代码,并导入到项目中:




- 导入数据表信息

- 配置区域表



- 配置合作商表



- 配置点位表



- 生成代码

说明:.gitkeep是占位的空文件,没有实际意义,因为git不允许空文件夹所以占用用




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

在查询区域列表时,同时显示每个区域的点位数
方案选型

代码实现
# 传统模式
# 1.先聚合统计每个区域下的点位数
# 确定查询表 tb_node-
# 确定分组字段 region_id
select region_id,count(*) as node_count from tb_node group by region_id;
# 2.然后与区域表进行关联查询
select r.id,r.region_name,r.remark, ifnull(n.node_count, 0) as node_count from tb_region r
left join (select region_id,count(*) as node_count from tb_node group by region_id) n on r.id=n.region_id;
# AI生成sql
# 查询区域表(tb_region)所有的信息,需要显示每个区域的点位数(tb_node)
SELECT
r.*,
COUNT(n.id) AS node_count
FROM
tb_region r
LEFT JOIN
tb_node n ON r.id = n.region_id
GROUP BY
r.id;



package com.dkd.manage.domain.vo;
import com.dkd.manage.domain.Region;
import lombok.Data;
@Data
public class RegionVo extends Region {
// 点位数量
private Integer nodeCount;
}
package com.dkd.manage.mapper;
import java.util.List;
import com.dkd.manage.domain.Region;
import com.dkd.manage.domain.vo.RegionVo;
/**
* 区域管理Mapper接口
*
* @author itheima
* @date 2026-02-19
*/
public interface RegionMapper {
/**
* 查询区域列表
*
* @param region
* @return RegionVo集合
*/
public List<RegionVo> selectRegionVoList(Region region);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dkd.manage.mapper.RegionMapper">
<select id="selectRegionVoList" resultType="com.dkd.manage.domain.vo.RegionVo"
parameterType="com.dkd.manage.domain.Region">
SELECT r.*, COUNT(n.id) AS node_count FROM tb_region r LEFT JOIN tb_node n ON r.id = n.region_id
<where>
<if test="regionName != null and regionName != ''">and r.region_name like concat('%', #{regionName}, '%')
</if>
</where>
GROUP BY r.id
</select>
</mapper>

package com.dkd.manage.service;
import java.util.List;
import com.dkd.manage.domain.Region;
import com.dkd.manage.domain.vo.RegionVo;
/**
* 区域管理Service接口
*
* @author itheima
* @date 2026-02-19
*/
public interface IRegionService {
/**
* 查询区域管理
*/
public List<RegionVo> selectRegionVoList(Region region);
}
package com.dkd.manage.service.impl;
import java.util.List;
import com.dkd.common.utils.DateUtils;
import com.dkd.manage.domain.vo.RegionVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.dkd.manage.mapper.RegionMapper;
import com.dkd.manage.domain.Region;
import com.dkd.manage.service.IRegionService;
/**
* 区域管理Service业务层处理
*
* @author itheima
* @date 2026-02-19
*/
@Service
public class RegionServiceImpl implements IRegionService {
/**
* 查询区域列表
*
* @param region
* @return RegionVo集合
*/
@Override
public List<RegionVo> selectRegionVoList(Region region) {
return regionMapper.selectRegionVoList(region);
}
}
package com.dkd.manage.controller;
/**
* 区域管理Controller
*
* @author itheima
* @date 2026-02-19
*/
@RestController
@RequestMapping("/manage/region")
public class RegionController extends BaseController
{
@Autowired
private IRegionService regionService;
/**
* 查询区域管理列表
*/
@PreAuthorize("@ss.hasPermi('manage:region:list')")
@GetMapping("/list")
public TableDataInfo list(Region region)
{
startPage();
// List<Region> list = regionService.selectRegionList(region);
List<RegionVo> list = regionService.selectRegionVoList(region);
return getDataTable(list);
}
}



查看详情,需要显示当前区域下所有点位列表
合作商管理改造
参考页面原型,完成基础布局展示改造

避免密码明文存储
package com.dkd.manage.service.impl;
import java.util.List;
import com.dkd.common.utils.DateUtils;
import com.dkd.common.utils.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import com.dkd.manage.mapper.PartnerMapper;
import com.dkd.manage.domain.Partner;
import com.dkd.manage.service.IPartnerService;
/**
* 合作商管理Service业务层处理
*
* @author itheima
* @date 2026-02-19
*/
@Service
public class PartnerServiceImpl implements IPartnerService
{
/**
* 新增合作商管理
*
* @param partner 合作商管理
* @return 结果
*/
@Override
public int insertPartner(Partner partner)
{
// 使用springSecurity工具类,对密码加密处理
partner.setPassword(SecurityUtils.encryptPassword(partner.getPassword()));
partner.setCreateTime(DateUtils.getNowDate());
return partnerMapper.insertPartner(partner);
}
}


查看详情,需要显示合作商名称、联系人、联系电话、分成比例






在查询合作商列表时,同时显示每个合作商的点位数。
# AI生成sql
# 查询合作商表(tb_partner)的所有信息,同时查询每个合作商的点位数(node_count),生成sql语句
SELECT
p.*,
COUNT(n.id) AS node_count
FROM
tb_partner p
LEFT JOIN
tb_node n ON p.id = n.partner_id
GROUP BY
p.id

package com.dkd.manage.mapper;
import java.util.List;
import com.dkd.manage.domain.Partner;
import com.dkd.manage.domain.vo.PartnerVo;
/**
* 合作商管理Mapper接口
*
* @author itheima
* @date 2026-02-19
*/
public interface PartnerMapper
{
/**
* 查询合作商管理
*/
public List<PartnerVo> selectPartnerVoList(Partner partner);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dkd.manage.mapper.PartnerMapper">
<select id="selectPartnerVoList" resultType="com.dkd.manage.domain.vo.PartnerVo"
parameterType="com.dkd.manage.domain.Partner">
SELECT p.*,COUNT(n.id) AS node_count FROM tb_partner p LEFT JOIN tb_node n ON p.id = n.partner_id
<where>
<if test="partnerName != null and partnerName != ''">and p.partner_name like concat('%', #{partnerName},
'%')
</if>
</where>
GROUP BY p.id
</select>
</mapper>
package com.dkd.manage.service;
import java.util.List;
import com.dkd.manage.domain.Partner;
import com.dkd.manage.domain.vo.PartnerVo;
/**
* 合作商管理Service接口
*
* @author itheima
* @date 2026-02-19
*/
public interface IPartnerService
{
/**
* 查询合作商管理
*/
public List<PartnerVo> selectPartnerVoList(Partner partner);
}
package com.dkd.manage.service.impl;
/**
* 合作商管理Service业务层处理
*
* @author itheima
* @date 2026-02-19
*/
@Service
public class PartnerServiceImpl implements IPartnerService {
@Autowired
private PartnerMapper partnerMapper;
/**
* 查询合作商列表
*
* @param partner
* @return
*/
@Override
public List<PartnerVo> selectPartnerVoList(Partner partner) {
return partnerMapper.selectPartnerVoList(partner);
}
}
package com.dkd.manage.controller;
/**
* 合作商管理Controller
*
* @author itheima
* @date 2026-02-19
*/
@RestController
@RequestMapping("/manage/partner")
public class PartnerController extends BaseController
{
@Autowired
private IPartnerService partnerService;
/**
* 查询合作商管理列表
*/
@PreAuthorize("@ss.hasPermi('manage:partner:list')")
@GetMapping("/list")
public TableDataInfo list(Partner partner)
{
startPage();
// List<Partner> list = partnerService.selectPartnerList(partner);
List<PartnerVo> list = partnerService.selectPartnerVoList(partner);
return getDataTable(list);
}
}
ctrl + f9热更新服务


重置密码,初始密码为123456

package com.dkd.manage.controller;
/**
* 合作商管理Controller
*
* @author itheima
* @date 2026-02-19
*/
@RestController
@RequestMapping("/manage/partner")
public class PartnerController extends BaseController
{
@Autowired
private IPartnerService partnerService;
/**
* 重置合作商密码
*/
@PreAuthorize("@ss.hasPermi('manage:partner:edit')")
@Log(title = "重置合作商密码", businessType = BusinessType.UPDATE)
@PutMapping("resetPwd/{id}")
public AjaxResult resetPwd(@PathVariable Long id)
{
Partner partner = new Partner();
partner.setId(id);
partner.setPassword(SecurityUtils.encryptPassword("123456"));
return toAjax(partnerService.updatePartner(partner));
}
}



点位管理改造
在点位列表查询中,关联显示区域、合作商等信息

- 关联查询: 对于设备数量的统计,我们需要执行关联查询,在mapper层封装。
- 关联实体: 对于区域和合作商的数据,我们会采用Mybatis提供的嵌套查询功能。
- MyBatis嵌套查询就是将原来多表查询中的联合查询语句拆成单个表的查询,再使用mybatis的语法嵌套在一起,通过定义resultMap和sql语句中的association 或collection 元素来实现嵌套查询。




SELECT
n.id,
n.node_name,
n.address,
n.business_type,
n.region_id,
n.partner_id,
n.create_time,
n.update_time,
n.create_by,
n.update_by,
n.remark,
COUNT(vm.id) AS vm_count
FROM tb_node n
LEFT JOIN tb_vending_machine vm ON n.id = vm.node_id
GROUP BY
n.id;
# 再根据点位的区域外键region_id查询区域的信息
select * from tb_region where id=1;
# 再根据点位的合作商外键partner_id查询合作商信息
select * from tb_partner where id=1;

package com.dkd.manage.mapper;
import java.util.List;
import com.dkd.manage.domain.Node;
import com.dkd.manage.domain.vo.NodeVo;
/**
* 点位管理Mapper接口
*
* @author itheima
* @date 2026-02-19
*/
public interface NodeMapper
{
/**
* 查询点位管理列表
* @param node
* @return
*/
public List<NodeVo> selectNodeVoList(Node node);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dkd.manage.mapper.NodeMapper">
<resultMap type="NodeVo" id="NodeVoResult">
<result property="id" column="id" />
<result property="nodeName" column="node_name" />
<result property="address" column="address" />
<result property="businessType" column="business_type" />
<result property="regionId" column="region_id" />
<result property="partnerId" column="partner_id" />
<result property="createTime" column="create_time" />
<result property="updateTime" column="update_time" />
<result property="createBy" column="create_by" />
<result property="updateBy" column="update_by" />
<result property="remark" column="remark" />
<result property="vmCount" column="vm_count" />
<association property="region" javaType="Region" column="region_id" select="com.dkd.manage.mapper.RegionMapper.selectRegionById" />
<association property="partner" javaType="Partner" column="partner_id" select="com.dkd.manage.mapper.PartnerMapper.selectPartnerById" />
</resultMap>
<select id="selectNodeVoList" resultMap="NodeVoResult"
parameterType="com.dkd.manage.domain.Node">
SELECT
n.id,
n.node_name,
n.address,
n.business_type,
n.region_id,
n.partner_id,
n.create_time,
n.update_time,
n.create_by,
n.update_by,
n.remark,
COUNT(vm.id) AS vm_count
FROM tb_node n
LEFT JOIN tb_vending_machine vm ON n.id = vm.node_id
<where>
<if test="nodeName != null and nodeName != ''"> and n.node_name like concat('%', #{nodeName}, '%')</if>
<if test="regionId != null "> and n.region_id = #{regionId}</if>
<if test="partnerId != null "> and n.partner_id = #{partnerId}</if>
</where>
GROUP BY
n.id
</select>
</mapper>
package com.dkd.manage.service;
import java.util.List;
import com.dkd.manage.domain.Node;
import com.dkd.manage.domain.vo.NodeVo;
/**
* 点位管理Service接口
*
* @author itheima
* @date 2026-02-19
*/
public interface INodeService
{
/**
* 查询点位管理列表
* @param node
* @return
*/
public List<NodeVo> selectNodeVoList(Node node);
}
package com.dkd.manage.service.impl;
/**
* 点位管理Service业务层处理
*
* @author itheima
* @date 2026-02-19
*/
@Service
public class NodeServiceImpl implements INodeService
{
@Autowired
private NodeMapper nodeMapper;
@Override
public List<NodeVo> selectNodeVoList(Node node) {
return nodeMapper.selectNodeVoList(node);
}
}
package com.dkd.manage.controller;
/**
* 点位管理Controller
*
* @author itheima
* @date 2026-02-19
*/
@RestController
@RequestMapping("/manage/node")
public class NodeController extends BaseController {
@Autowired
private INodeService nodeService;
/**
* 查询点位管理列表
*/
@PreAuthorize("@ss.hasPermi('manage:node:list')")
@GetMapping("/list")
public TableDataInfo list(Node node) {
startPage();
// List<Node> list = nodeService.selectNodeList(node);
List<NodeVo> list = nodeService.selectNodeVoList(node);
return getDataTable(list);
}
}


查看详情,需要显示当前点位下所有设备列表






数据完整性
在删除区域或合作商数据时,关联的点位数据该如何处理?




- 将点位表级联操作改为限制删除
- 在全局异常处理器中捕获异常友情提示




- 把数据库外键的约束由联级操作改为拒绝操作之后,再删除区域数据,由于区域关键了点位数据,所以数据库拒绝操作,这符合数据一致性要求,但是这个报错信息可读性太差

- SQLIntegrityConstraintViolationException: 这个类通常用于表示SQL数据库操作中的完整性约束违反异常
- 例如: 外键约束、唯一约束等。当数据库操作违反了这些约束时,就会抛出这个异常。
- DatalntegrityViolationException: 在使用SpringBoot框架时,如果违反了数据完整性抛出这个异常。
- RuoYi框架已经提供了全局异常处理器,我们只需要添加一个自定义异常就能处理了


人员管理
前置工作
需求说明
业务场景: 想象一下,在我们确定了区域、合作商、点位之后,接下来的步骤是什么?

库表设计
对于人员管理数据模型,下面是示意图:

生成基础代码
使用若依代码生成器,生成人员列表前后端基础代码,并导入到项目中:














人员列表改造
把添加和修改弹窗中的所属区域和角色修改为下拉框









信息补全
添加员工后,由于前端只传递角色和所属区域的id, 所以角色和所属区域的名称并没有入库


package com.dkd.manage.service.impl;
/**
* 人员列表Service业务层处理
*
* @author itheima
* @date 2026-02-21
*/
@Service
public class EmpServiceImpl implements IEmpService
{
@Autowired
private EmpMapper empMapper;
@Autowired
private RegionMapper regionMapper;
@Autowired
private RoleMapper roleMapper;
/**
* 新增人员列表
*
* @param emp 人员列表
* @return 结果
*/
@Override
public int insertEmp(Emp emp)
{
// 补充区域名称
emp.setRegionName(regionMapper.selectRegionById(emp.getRegionId()).getRegionName());
// 补充角色信息
Role role = roleMapper.selectRoleByRoleId(emp.getRoleId());
emp.setRoleCode(role.getRoleCode());
emp.setRoleName(role.getRoleName());
// 创建时间
emp.setCreateTime(DateUtils.getNowDate());
return empMapper.insertEmp(emp);
}
/**
* 修改人员列表
*
* @param emp 人员列表
* @return 结果
*/
@Override
public int updateEmp(Emp emp)
{
// 补充区域名称
emp.setRegionName(regionMapper.selectRegionById(emp.getRegionId()).getRegionName());
// 补充角色信息
Role role = roleMapper.selectRoleByRoleId(emp.getRoleId());
emp.setRoleCode(role.getRoleCode());
emp.setRoleName(role.getRoleName());
// 修改时间
emp.setUpdateTime(DateUtils.getNowDate());
return empMapper.updateEmp(emp);
}
}
问题复现
修改区域信息后,人员列表中的区域信息未同步


数据一致性维护
同步存储: 在员工表中有区域名称的冗余字段,在更新区域表的同时,同步更新员工表中区域名称。

package com.dkd.manage.mapper;
import java.util.List;
import com.dkd.manage.domain.Emp;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;
/**
* 人员列表Mapper接口
*
* @author itheima
* @date 2026-02-21
*/
public interface EmpMapper
{
/**
* 根据区域id修改区域名称
* @return
*/
@Update("update tb_emp set region_name = #{regionName} where region_id = #{regionId}")
public int updateByRegionId(@Param("regionName") String regionName, @Param("regionId") Long regionId);
}
package com.dkd.manage.service.impl;
/**
* 区域管理Service业务层处理
*
* @author itheima
* @date 2026-02-19
*/
@Service
public class RegionServiceImpl implements IRegionService {
@Autowired
private RegionMapper regionMapper;
@Autowired
private EmpMapper empMapper;
/**
* 修改区域管理
*
* @param region 区域管理
* @return 结果
*/
@Transactional(rollbackFor = Exception.class)
@Override
public int updateRegion(Region region) {
// 先更新区域信息
region.setUpdateTime(DateUtils.getNowDate());
int result = regionMapper.updateRegion(region);
// 同步更新员工表的区域名称
empMapper.updateByRegionId(region.getRegionName(), region.getId());
return result;
}
}


