java实现分类下拉树,点击时对应搜索---后端逻辑
一直想做分类下拉,然后选择后搜索的页面,正好做项目有了明确的需求,查找后发现el-tree的构件可满足需求,数据要求为:{ id:1, label:name, childer:[……] }形式的,于是乎,开搞!
一、效果预览
点击后
二、实现步骤
1、数据库设计层面:
在设计数据库的分类表时,需要设置为层级结构的样式,指明父分类id和祖分类id,如此才可满足分类需求。
具体如下,比如我的书签分类表:markClass(以下只写出主要三列)
属性 | 数据类型 | 字段说明 | 举例 |
markClassId | int | 书签分类的id | 1001 |
parentid | int | 该分类的上级分类id(父id) | 1000 |
ancestors | string | 该分类所有上属分类id(祖id) | 0,10,100,1000 |
2、后端基础的构建
依据表格完成对后端基础增删改的设置,可以用Ruoyi一键生成。以下主要说明插入时,ancestors的设置:
插入时需要依据父分类id来完成对ancestors的设置。(注意设置ancestors的getter、setter方法)
private MarkclassMapper markclassMapper;
//这个是在你写的Impl层中的那个类里,你的selectById方法在哪里就引用哪个public int insertMarkclass(Markclass markclass){Markclass mar=markclassMapper.selectMarkclassByMarkClassId(markclass.getParentid());//依据传入的markclass对象的父分类id查找具体的分类markclass.setAncestors(mar.getAncestors()+","+markclass.getParentid());//调用set方法。按照字符串拼接的方法,获取父分类所包含的祖分类id,并加上父分类id。return markclassMapper.insertMarkclass(markclass);}
3、主代码——构建树相关
(1)创建适配el-tree数据类型的类
el-tree的构件数据要求为:{ id:1, label:name, childer:[……] }形式,于是可定义一个“TreeSelect”类(类名称随意)
package com.blhq.wjs.domain;
import java.io.Serializable;
import java.util.List;
import java.util.stream.Collectors;import com.blhq.common.core.domain.entity.SysMenu;
import com.fasterxml.jackson.annotation.JsonInclude;
//引入需要的你定义到的实体类/*** Treeselect树结构实体类* 数据类型依据el-tree数据需求进行定义,即为:id、label、children,* @author blhq*/
public class TreeSelect implements Serializable
{private static final long serialVersionUID = 1L;/** 节点ID */private Long id;/** 节点名称 */private String label;/** 子节点 */// @JsonInclude(JsonInclude.Include.NON_EMPTY)private List<TreeSelect> children;public TreeSelect(Class aclass) {this.id = aclass.getClassid();this.label = aclass.getClassname();// 检查此处是否正确设置了 class_children 属性this.children = aclass.getClass_children().stream().map(TreeSelect::new).collect(Collectors.toList());}//写了多个TreeSelect构造类,以满足后续其它类型对象的调用,public TreeSelect(Markclass markclass){this.id=markclass.getMarkClassId();this.label=markclass.getClassname();this.children=markclass.getMarkChildren().stream().map(TreeSelect::new).collect(Collectors.toList());}public TreeSelect(SysMenu menu){this.id = menu.getMenuId();this.label = menu.getMenuName();this.children = menu.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList());}public Long getId(){return id;}public void setId(Long id){this.id = id;}public String getLabel(){return label;}public void setLabel(String label){this.label = label;}public List<TreeSelect> getChildren(){return children;}public void setChildren(List<TreeSelect> children){this.children = children;}
}
补充:Markclass类
private List<Markclass> markChildren=new ArrayList<Markclass>();
//必须实例化,不然会报错java.lang.NullPointerException: null
//同时设置getter、setter方法
public List<Markclass> getMarkChildren() {return markChildren;
}public void setMarkChildren(List<Markclass> markChildren) {this.markChildren = markChildren;}
(2)service层:
在你的service类中创建方法:(如果没有service层,则无视,直接看实现层操作即可)
注意:部分返回值类型为刚刚定义的TreeSelect的列表。
public List<TreeSelect> selectMarkClassTreeList(Markclass markclass);//接收用户传入的分类,即为当前操作的分类public List<Markclass> buildMarkClassTree(List<Markclass> markclassList);//构建树的核心方法public List<TreeSelect> buildMarkClassTreeSelect(List<Markclass> classes);//注意返回值类型为刚刚定义的TreeSelect的列表。
(3)Impl实现层:
核心代码:
@Overridepublic List<TreeSelect> selectMarkClassTreeList(Markclass markclass) {//传入用户操作的分类,获取筛选后的书签分类列表List<Markclass> markClasses = this.selectMarkclassList(markclass);//selectMarkclassList()为查找分类列表用的方法,返回值为List<Markclass>类型return buildMarkClassTreeSelect(markClasses);//构建树}@Overridepublic List<TreeSelect> buildMarkClassTreeSelect(List<Markclass> classes) {List<Markclass> markClasses = buildMarkClassTree(classes);return markClasses.stream().map(TreeSelect::new).collect(Collectors.toList());}/*** 构建前端所需要树结构*/@Overridepublic List<Markclass> buildMarkClassTree(List<Markclass> markclassList) {List<Markclass> returnList=new ArrayList<Markclass>();存储当前分类出现的节点List<Long> tempList = markclassList.stream().map(Markclass::getMarkClassId).collect(Collectors.toList());for (Markclass markclass : markclassList) {if(!tempList.contains(markclass.getParentid())){// 如果是顶级节点, 遍历该父节点的所有子节点recursionFn(markclassList, markclass);returnList.add(markclass);//不存在的加入进去,从而完成一整个树的遍历}}if (returnList.isEmpty()) {returnList = markclassList;}return returnList;}private void recursionFn(List<Markclass> list, Markclass t) {// 递归。得到子节点列表List<Markclass> childList = getChildList(list, t);t.setMarkChildren(childList);for (Markclass tChild : childList) {if (hasChild(list, tChild)) {recursionFn(list, tChild);}}}/*** 得到子节点列表*/private List<Markclass> getChildList(List<Markclass> list, Markclass t) {List<Markclass> tlist = new ArrayList<Markclass>();for (Markclass n : list) {if (StringUtils.isNotNull(n.getParentid()) && n.getParentid().longValue() == t.getMarkClassId().longValue()) {tlist.add(n);}}return tlist;}private boolean hasChild(List<Markclass> list, Markclass t) {return getChildList(list, t).size() > 0;}
完整Impl层代码:
package com.blhq.wjs.service.impl;import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;import com.blhq.common.utils.StringUtils;
import com.blhq.wjs.domain.TreeSelect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.blhq.wjs.mapper.MarkclassMapper;
import com.blhq.wjs.domain.Markclass;
import com.blhq.wjs.service.IMarkclassService;/*** 书签分类Service业务层处理* * @author blhq* @date 2024-06-19*/
@Service
public class MarkclassServiceImpl implements IMarkclassService
{@Autowiredprivate MarkclassMapper markclassMapper;/*** 查询书签分类* * @param markClassId 书签分类主键* @return 书签分类*/@Overridepublic Markclass selectMarkclassByMarkClassId(Long markClassId){return markclassMapper.selectMarkclassByMarkClassId(markClassId);}/*** 查询书签分类列表* * @param markclass 书签分类* @return 书签分类*/@Overridepublic List<Markclass> selectMarkclassList(Markclass markclass){return markclassMapper.selectMarkclassList(markclass);}/*** 新增书签分类* * @param markclass 书签分类* @return 结果*/@Overridepublic int insertMarkclass(Markclass markclass){if (markclass.getMarkClassId() == null) {return markclassMapper.insertMarkclass(markclass);}else {Markclass mar=markclassMapper.selectMarkclassByMarkClassId(markclass.getParentid());markclass.setAncestors(mar.getAncestors()+","+markclass.getParentid());return markclassMapper.insertMarkclass(markclass);}}/*** 修改书签分类* * @param markclass 书签分类* @return 结果*/@Overridepublic int updateMarkclass(Markclass markclass){return markclassMapper.updateMarkclass(markclass);}/*** 批量删除书签分类* * @param markClassIds 需要删除的书签分类主键* @return 结果*/@Overridepublic int deleteMarkclassByMarkClassIds(Long[] markClassIds){return markclassMapper.deleteMarkclassByMarkClassIds(markClassIds);}/*** 删除书签分类信息* * @param markClassId 书签分类主键* @return 结果*/@Overridepublic int deleteMarkclassByMarkClassId(Long markClassId){return markclassMapper.deleteMarkclassByMarkClassId(markClassId);}@Overridepublic List<TreeSelect> selectMarkClassTreeList(Markclass markclass) {//传入用户操作的分类,获取筛选后的书签分类列表List<Markclass> markClasses = this.selectMarkclassList(markclass);//selectMarkclassList()为查找分类列表用的方法,返回值为List<Markclass>类型return buildMarkClassTreeSelect(markClasses);//构建树}@Overridepublic List<TreeSelect> buildMarkClassTreeSelect(List<Markclass> classes) {List<Markclass> markClasses = buildMarkClassTree(classes);return markClasses.stream().map(TreeSelect::new).collect(Collectors.toList());}/*** 构建前端所需要树结构*/@Overridepublic List<Markclass> buildMarkClassTree(List<Markclass> markclassList) {List<Markclass> returnList=new ArrayList<Markclass>();存储当前分类出现的节点List<Long> tempList = markclassList.stream().map(Markclass::getMarkClassId).collect(Collectors.toList());for (Markclass markclass : markclassList) {if(!tempList.contains(markclass.getParentid())){// 如果是顶级节点, 遍历该父节点的所有子节点recursionFn(markclassList, markclass);returnList.add(markclass);//不存在的加入进去,从而完成一整个树的遍历}}if (returnList.isEmpty()) {returnList = markclassList;}return returnList;}private void recursionFn(List<Markclass> list, Markclass t) {// 递归。得到子节点列表List<Markclass> childList = getChildList(list, t);t.setMarkChildren(childList);for (Markclass tChild : childList) {if (hasChild(list, tChild)) {recursionFn(list, tChild);}}}/*** 得到子节点列表*/private List<Markclass> getChildList(List<Markclass> list, Markclass t) {List<Markclass> tlist = new ArrayList<Markclass>();for (Markclass n : list) {if (StringUtils.isNotNull(n.getParentid()) && n.getParentid().longValue() == t.getMarkClassId().longValue()) {tlist.add(n);}}return tlist;}private boolean hasChild(List<Markclass> list, Markclass t) {return getChildList(list, t).size() > 0;}}
(4)controller层:
这时候,我们直接添加即可:推荐把它放到对应需要分类显示的文件中,比如我的是在书签的controller中。
@GetMapping("/deptTree")public AjaxResult deptTree(Markclass markclass){List<TreeSelect> list = markclassService.selectMarkClassTreeList(markclass);return success(list);}
(5)xml文件:
修改查找逻辑的语句:将分类的查询改为,从markclas表的ancestors查:
<select id="selectBookmarkList" parameterType="Bookmark" resultMap="BookmarkResult">select b.markId, b.markName, b.markClassId, b.website, b.`desc`, b.createTime, b.editTime, b.icon, b.statue, b.commonGrade, b.allGrade, b.markPlot, b.likes, b.markExtend, c.classname, c.ancestors from bookmark bleft join markclass c on b.markClassId = c.markClassIdwhere 1=1<if test="markId != null "> and markId = #{markId}</if><if test="markName != null and markName != ''"> and markName like concat('%', #{markName}, '%')</if><if test="markClassId != null ">AND (b.markClassId = #{markClassId} OR b.markClassId IN ( SELECT c.markClassId FROM class c WHERE find_in_set(#{markClassId}, ancestors) ))</if><if test="website != null and website != ''"> and website like concat('%', #{website}, '%')</if><if test="desc != null and desc != ''"> and b.`desc` like concat('%', #{desc}, '%')</if><if test="createTime != null "> and b.createTime >= #{createTime}</if><if test="editTime != null "> and b.editTime <= #{editTime}</if><if test="icon != null and icon != ''"> and icon like concat('%', #{icon}, '%')</if><if test="statue != null "> and statue = #{statue}</if><if test="commonGrade != null "> and commonGrade = #{commonGrade}</if><if test="allGrade != null "> and allGrade like concat('%', #{allGrade}, '%')</if><if test="markPlot != null and markPlot != ''"> and markPlot = #{markPlot}</if><if test="likes != null "> and likes like concat('%', #{likes}, '%')</if><if test="markExtend != null "> and markExtend like concat('%', #{markExtend}, '%')</if></select>
4、前端应用:
整体思路就是引用它,把它调过来使用就行。下面按我的思路来演示:
(1)引入api:
// 查询书签分类列表--tree
export function markClassTreeSelect(query) {return request({url: '/markclass/markclass/deptTree',method: 'get',params: query})
}
(2)页面导入
注意将v-model中的值,改为你自己的。
<template><el-row :gutter="20"><el-col :span="4" :xs="24"><div class="head-container"><el-inputv-model="markName"placeholder="请输入书签分类名称"clearablesize="small"prefix-icon="el-icon-search"style="margin-bottom: 20px"/></div><el-tree:data="markOptions":props="defaultProps":expand-on-click-node="false":filter-node-method="filterNode"ref="tree"node-key="id"default-expand-allhighlight-current@node-click="handleNodeClick"/><!-- </div>--></el-col><el-col :span="20" :xs="24">…………</el-col></el-row>…………//你的其它页面布局代码,上文gutter表示左侧分类栏占比大小。</template>
<script></script>内需要添加的:
data() {return {//data内需要添加的数据:// 书签分类名称markName:undefined,// 书签分类选项markOptions: undefined,//设置分类数据样式:defaultProps: {children: "children",//children:就是告诉el-tree,//它需要的children,在你这里的数据,叫啥名,比如我的为children//就是TreeSelect类中定义的childrenlabel: "label"},
},
},watch: {//在watch内,添加如下内容,没watch就自己加,在data(){},后面。// 根据名称筛选树markName(val) {this.$refs.tree.filter(val);}},created() {//调用方法this.getMarkTree();},methods: {// 获取书签分类树getMarkTree() {markClassTreeSelect().then(response => {this.markOptions = response.data;console.log(response.data);// console.log(response);});},// 筛选节点filterNode(value, data) {if (!value) return true;return data.label.indexOf(value) !== -1;},// 节点单击事件handleNodeClick(data) {this.queryParams.markClassId = data.id;this.handleQuery();},},
5、成果展示:
点击游戏类后:
三、我遇到的问题
(1):无法自动装配。找不到'MarkclassMapper’类型的 Bean。
方案:在MarkclassMapper中,最前面加入@Mapper注解即可
(2)Failed to instantiate: Factory method 'sqlSessionFactory' threw exception; TypeException: The alias 'TreeSelect' is already mapped to the value 'com.blhq.wjs.domain.TreeSelect'.
方案:如果提示这个,请把我们刚才定义的TreeSelect类,重构一下,改个名就行。
更详细说明请看下文:
分类树实现时遇见的bug:如XXXController $$EnhancerBySpringCGLIB$$c8ca0a15 cannot be cast to XXX……-CSDN博客文章浏览阅读2次。实现分类树实现时遇见的bughttps://blog.csdn.net/qq_64595427/article/details/139843770?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22139843770%22%2C%22source%22%3A%22qq_64595427%22%7D
如有问题,欢迎留言讨论哦~
相关文章:
java实现分类下拉树,点击时对应搜索---后端逻辑
一直想做分类下拉,然后选择后搜索的页面,正好做项目有了明确的需求,查找后发现el-tree的构件可满足需求,数据要求为:{ id:1, label:name, childer:[……] }形式的,于是乎,开搞! 一…...

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] 披萨大作战(100分) - 三语言AC题解(Python/Java/Cpp)
🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 …...

探索Facebook对世界各地文化的影响
随着数字化时代的到来,社交媒体已成为连接世界各地人们的重要平台之一。而在这个领域的巨头之一,Facebook不仅是人们沟通交流的场所,更是一座桥梁,将不同地域、文化的人们联系在一起。本文将探索Facebook对世界各地文化的影响&…...
导出requirements.txt
文章目录 requirements.txt导出环境中所有包导出当前项目的包可能遇到的问题 requirements.txt 在Python项目中,通常使用requirements.txt文件来列出所有需要的第三方库和模块。这个文件通常位于项目的根目录下,并且在安装Python项目时,可以…...

我主编的电子技术实验手册(09)——并联电路
本专栏是笔者主编教材(图0所示)的电子版,依托简易的元器件和仪表安排了30多个实验,主要面向经费不太充足的中高职院校。每个实验都安排了必不可少的【预习知识】,精心设计的【实验步骤】,全面丰富的【思考习…...

数据结构_二叉树
目录 一、树型结构 二、二叉树 2.1 概念 2.2 特殊的二叉树 2.3 二叉树的性质 2.4 二叉树的存储 2.5 遍历二叉树 2.6 操作二叉树 总结 一、树型结构 树是一种非线性的数据结构,它是由 n(n>0) 个有限结点组成一个具有层次关系的集合,一棵 n 个…...

Java线程池七个参数详解
ThreadPoolExecutor 是JDK中的线程池实现,这个类实现了一个线程池需要的各个方法,它提供了任务提交、线程管理、监控等方法 下面是 ThreadPoolExecutor 类的构造方法源码,其他创建线程池的方法最终都会导向这个构造方法,共有7个参…...

产品Web3D交互展示有什么优势?如何快速制作?
智能互联网时代,传统的图片、文字、视频等产品展示方式,因为缺少互动性,很难引起用户的兴趣,已经逐渐失去了宣传优势。 Web3D交互展示技术的出现,让众多品牌和企业找到了新的方向,线上产品展示不在枯燥无趣…...

Python | Leetcode Python题解之第171题Excel列表序号
题目: 题解: class Solution:def titleToNumber(self, columnTitle: str) -> int:number, multiple 0, 1for i in range(len(columnTitle) - 1, -1, -1):k ord(columnTitle[i]) - ord("A") 1number k * multiplemultiple * 26return n…...

【银河麒麟】高可用触发服务器异常重启,处理机制详解
1.服务器环境以及配置 【机型】物理机 处理器: Intel 内存: 126G 【内核版本】 4.19.90-25.16.v2101.ky10.x86_64 【银河麒麟操作系统镜像版本】 Kylin-Server-10-SP2-Release-Shenzhen-Metro-x86-Build01-20220619 Kylin-HA-10-SP2-Release-S…...

性能工具之 JMeter 常用组件介绍(七)
文章目录 一、后置处理器1、Regular Expression Extractor(正则表达式提取器)2、JSON Extractor(JSON表达式提取器)3、Regular Expression Extractor(正则表达式提取器) 二、小结 本文主要介绍JMeter主流后置处理器的功能 一、后置处理器 从上面可以看出后置处理可以插件挺多&a…...

Python学习笔记15:进阶篇(四)文件的读写。
文件操作 学习编程操作中,我觉得文件操作是必不可少的一部分。不管是读书的时候学习的c,c,工作的前学的java,现在学的Python,没学过的php和go,都有文件操作的模块以及库的支持,重要性毫无疑问。…...

角度调制与解调电路
music! (黄佳庆老师可爱捏) 调角 角度调制有较好的抗噪性能。 调相 相位变化的频率变化的微分,频率变化是相位变化的积分 相位的变化率就是频率 调频 调相与调频的关系 大F是输入信号的频率,大Ω是输入信号的角频率 …...

数据分析:微生物组差异丰度方法汇总
欢迎大家关注全网生信学习者系列: WX公zhong号:生信学习者Xiao hong书:生信学习者知hu:生信学习者CDSN:生信学习者2 介绍 微生物数据具有一下的特点,这使得在做差异分析的时候需要考虑到更多的问题&…...

Linux驱动开发(二)--字符设备驱动开发提升 LED驱动开发实验
1、地址映射 在编写驱动之前,需要知道MMU,也就是内存管理单元,在老版本的 Linux 中要求处理器必须有 MMU,但是现在Linux 内核已经支持无 MMU 的处理器了。 MMU的功能如下: 完成虚拟空间到物理空间的映射 内存保护&…...

钡铼BL101网关助力智慧城市路灯远程智能管控
在迈向智慧城市的征途中,基础设施的智能化改造是关键一环,而路灯作为城市脉络的照明灯塔,其智能化升级对于节能减排、提升城市管理效率具有重要意义。钡铼BL101网关,作为Modbus转MQTT的专业桥梁,正以其卓越的性能和广泛…...

如何优雅的使用Github Action服务来将Hexo部署到Github Pages
文章目录 参考文章前提条件1. 初始化Hexo2. 初始化仓库3. 创建Token4. 修改_config.yml5. 配置Github Action工作流6. 推送验证7. 配置Github Pages8. 修改Hexo主题样式10. 添加文章遇到了一些问题和方案1. 网站没有样式问题2. 图片不显示 参考文章 Bilibili视频教程-9分钟零成…...

After Effects 2024 mac/win版:创意视效,梦想起航
After Effects 2024是一款引领视效革命的专业软件,汇聚了创意与技术的精华。作为Adobe推出的全新版本,它以其强大的视频处理和动画创作能力,成为从事设计和视频特技的机构,如电视台、动画制作公司、个人后期制作工作室以及多媒体工…...

信息打点web篇----web后端源码专项收集
前言 欢迎来到我的博客 个人主页:北岭敲键盘的荒漠猫-CSDN博客 专栏描述:因为第一遍过信息收集的时候,没怎么把收集做回事 导致后来在实战中,遭遇资产获取少,可渗透点少的痛苦,如今决定 从头来过,全面全方位…...

ArcGIS批量投影转换的妙用(地理坐标系转换为平面坐标系)
点击下方全系列课程学习 点击学习—>ArcGIS全系列实战视频教程——9个单一课程组合系列直播回放 这次文章我们来介绍一下,如何巧妙用要素数据集来实现要素的批量投影。不需要ArcGIS的模型构建器与解决。 例如,有多个要素要将CGCS_2000地理坐标系投…...

网络六边形受到攻击
大家读完觉得有帮助记得关注和点赞!!! 抽象 现代智能交通系统 (ITS) 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 (…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
渲染学进阶内容——模型
最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...

【单片机期末】单片机系统设计
主要内容:系统状态机,系统时基,系统需求分析,系统构建,系统状态流图 一、题目要求 二、绘制系统状态流图 题目:根据上述描述绘制系统状态流图,注明状态转移条件及方向。 三、利用定时器产生时…...
rnn判断string中第一次出现a的下标
# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...
第7篇:中间件全链路监控与 SQL 性能分析实践
7.1 章节导读 在构建数据库中间件的过程中,可观测性 和 性能分析 是保障系统稳定性与可维护性的核心能力。 特别是在复杂分布式场景中,必须做到: 🔍 追踪每一条 SQL 的生命周期(从入口到数据库执行)&#…...
探索Selenium:自动化测试的神奇钥匙
目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...
Spring Security 认证流程——补充
一、认证流程概述 Spring Security 的认证流程基于 过滤器链(Filter Chain),核心组件包括 UsernamePasswordAuthenticationFilter、AuthenticationManager、UserDetailsService 等。整个流程可分为以下步骤: 用户提交登录请求拦…...

篇章二 论坛系统——系统设计
目录 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 1. 数据库设计 1.1 数据库名: forum db 1.2 表的设计 1.3 编写SQL 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 通过需求分析获得概念类并结合业务实现过程中的技术需要&#x…...
React从基础入门到高级实战:React 实战项目 - 项目五:微前端与模块化架构
React 实战项目:微前端与模块化架构 欢迎来到 React 开发教程专栏 的第 30 篇!在前 29 篇文章中,我们从 React 的基础概念逐步深入到高级技巧,涵盖了组件设计、状态管理、路由配置、性能优化和企业级应用等核心内容。这一次&…...