用户中心项目(登录 + 用户管理功能后端)
文章目录
- 1.登录功能-后端
- 1.思路分析
- 2.完成对用户名和密码的校验
- 1.com/sun/usercenter/service/UserService.java 添加方法
- 2.com/sun/usercenter/service/impl/UserServiceImpl.java 添加方法
- 3.com/sun/usercenter/service/impl/UserServiceImpl.java 新增属性
- 3.记录用户的登录态并配置MyBatisPlus的逻辑删除
- 1.com/sun/usercenter/service/UserService.java 修改doLogin,增加参数 request
- 2.com/sun/usercenter/service/impl/UserServiceImpl.java 新增属性
- 3.com/sun/usercenter/service/impl/UserServiceImpl.java 添加代码
- doLogin 方法添加代码,记录用户登录态
- 4.配置MyBatisPlus的逻辑删除
- 1.application.yml 配置
- 2.实体类添加注解
- 2.接口开发及测试
- 1.com/sun/usercenter/controller/UserController.java 注册和登录接口
- 2.封装登录和注册请求信息的对象
- 1.文件目录
- 2.com/sun/usercenter/model/request/UserLoginRequest.java 用户登录请求体
- 3.com/sun/usercenter/model/request/UserRegisterRequest.java 用户注册请求体
- 3.单元测试
- 1.用户注册接口测试
- 1.debug模式启动IDEA
- 2.postman测试
- 2.用户登录接口测试
- 1.com/sun/usercenter/service/impl/UserServiceImpl.java的doLogin最后下断点
- 2.postman测试
- 3.查看session是否有用户登录状态的信息
- 4.放行,查看postman的返回结果
- 3.逻辑删除测试
- 1.在数据库中把刚才进行登录的用户id改成1
- 2.重新登录一下
- 3.用户管理接口
- 1.com/sun/usercenter/controller/UserController.java 添加方法
- 2.user表新增字段role表示用户权限
- 1.新增字段role
- 2.com/sun/usercenter/model/domain/User.java 实体类新增权限字段
- 3.Mapper.xml不用修改,因为这个字段本来就与表的字段对应
- 4.com/sun/usercenter/service/impl/UserServiceImpl.java 用户脱敏里面把role字段加进去
- 3.com/sun/usercenter/contant/UserConstant.java 存放用户常量的接口
- 4.com/sun/usercenter/controller/UserController.java 对两个接口进行权限验证
- 1.添加方法 isAdmin 判断是否为管理员
- 2.修改两个接口的方法
- 5.application.yml 设置session超时时间
- 6.单元测试
- 1.测试 searchUsers接口
- 1.登录
- 2.表中增加两条测试记录
- 3.进行查询,成功查询!
- 2.测试deleteUser接口
- 1.删除id为1的用户
- 2.查看数据库,成功进行逻辑删除
- 3.解决 searchUsers接口返回的用户信息没有脱敏的问题
- 1.com/sun/usercenter/service/UserService.java 添加方法
- 2.com/sun/usercenter/service/impl/UserServiceImpl.java 实现方法并修改逻辑
- 3.com/sun/usercenter/controller/UserController.java 修改searchUsers方法对查询到的用户列表进行脱敏
- 4.再次测试,敏感信息变成null了
1.登录功能-后端
1.思路分析
2.完成对用户名和密码的校验
1.com/sun/usercenter/service/UserService.java 添加方法
public Long userRegister(String userAccount, String userPassword, String checkPassword);/*** 用户登录接口* @param userAccount 账号* @param userPassword 密码* @return 成功返回脱敏后的User对象,失败返回null*/public User doLogin(String userAccount, String userPassword);
2.com/sun/usercenter/service/impl/UserServiceImpl.java 添加方法
@Overridepublic User doLogin(String userAccount, String userPassword) {// 1 检验用户名和密码是否合法,如果不合法就没必要去数据库查询// 验证非空if (StringUtils.isAnyBlank(userAccount, userPassword)) {return null;}// 账户4到16位,不能包含特殊字符if (!userAccount.matches("^[a-zA-Z0-9]{4,16}$")) {return null;}// 密码不小于8位if (userPassword.length() < 8) {return null;}// 2 密码加密进行查询String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());// 编写查询条件QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();userQueryWrapper.eq("userPassword", encryptPassword);userQueryWrapper.eq("userAccount", userAccount);// 执行查询User user = userMapper.selectOne(userQueryWrapper);// 判断用户是否存在if (user == null) {// 输出日志log.info("user login faild, userAccount can not match password");return null;}// 3 记录用户的登录态return user;}
3.com/sun/usercenter/service/impl/UserServiceImpl.java 新增属性
3.记录用户的登录态并配置MyBatisPlus的逻辑删除
1.com/sun/usercenter/service/UserService.java 修改doLogin,增加参数 request
/*** 用户登录接口* @param userAccount 账号* @param userPassword 密码* @return 成功返回脱敏后的User对象,失败返回null*/public User doLogin(String userAccount, String userPassword, HttpServletRequest request);
2.com/sun/usercenter/service/impl/UserServiceImpl.java 新增属性
3.com/sun/usercenter/service/impl/UserServiceImpl.java 添加代码
doLogin 方法添加代码,记录用户登录态
// 3 如果登录成功,记录用户的登录态// 首先进行脱敏User cleanUser = new User();cleanUser.setId(user.getId());cleanUser.setUsername(user.getUsername());cleanUser.setUserAccount(user.getUserAccount());cleanUser.setAvatarUrl(user.getAvatarUrl());cleanUser.setGender(user.getGender());cleanUser.setPhone(user.getPhone());cleanUser.setEmail(user.getEmail());cleanUser.setUserStatus(user.getUserStatus());cleanUser.setCreateTime(user.getCreateTime());HttpSession session = request.getSession();// 然后将脱敏后的用户信息放入session中session.setAttribute(USER_LOGIN_SAVE, cleanUser);return cleanUser;
4.配置MyBatisPlus的逻辑删除
1.application.yml 配置
global-config:db-config:logic-delete-field: isDelete # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)logic-delete-value: 1 # 逻辑已删除值(默认为 1)logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
2.实体类添加注解
2.接口开发及测试
1.com/sun/usercenter/controller/UserController.java 注册和登录接口
package com.sun.usercenter.controller;import com.sun.usercenter.model.domain.User;
import com.sun.usercenter.model.request.UserLoginRequest;
import com.sun.usercenter.model.request.UserRegisterRequest;
import com.sun.usercenter.service.UserService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;/*** Description:** @Author sun* @Create 2024/3/21 13:04* @Version 1.0*/
@RestController // 作为一个Controller注入容器,并将返回结果转换为json
@RequestMapping("/user") // restful风格的请求
public class UserController {@Resourceprivate UserService userService; // 注入针对Service接口的bean对象,可以调用接口的方法@PostMapping("/register")public Long userRegister(@RequestBody UserRegisterRequest userRegisterRequest) {// 首先判断数据是否成功封装if (userRegisterRequest == null) {return null;}// 对封装的数据进行校验,如果有一个是空直接返回nullString userAccount = userRegisterRequest.getUserAccount();String userPassword = userRegisterRequest.getUserPassword();String checkPassword = userRegisterRequest.getCheckPassword();if (StringUtils.isAnyBlank(userAccount, userPassword, checkPassword)) {return null;}// 调用Service层的注册方法,如果成功注册,则返回注册成功的用户id, 否则返回-1return userService.userRegister(userAccount, userPassword, checkPassword);}@PostMapping("/login")public User userRegister(@RequestBody UserLoginRequest userLoginRequest, HttpServletRequest request) {// 首先判断数据是否成功封装if (userLoginRequest == null) {return null;}// 对封装的数据进行校验,如果有一个是空直接返回nullString userAccount = userLoginRequest.getUserAccount();String userPassword = userLoginRequest.getUserPassword();if (StringUtils.isAnyBlank(userAccount, userPassword)) {return null;}return userService.doLogin(userAccount, userPassword, request);}}
2.封装登录和注册请求信息的对象
1.文件目录
2.com/sun/usercenter/model/request/UserLoginRequest.java 用户登录请求体
package com.sun.usercenter.model.request;import lombok.Data;/*** Description: 用户登录请求体** @Author sun* @Create 2024/3/21 13:39* @Version 1.0*/
@Data
public class UserLoginRequest {private String userAccount;private String userPassword;
}
3.com/sun/usercenter/model/request/UserRegisterRequest.java 用户注册请求体
package com.sun.usercenter.model.request;import lombok.Data;/*** Description: 用户注册请求体** @Author sun* @Create 2024/3/21 13:26* @Version 1.0*/
@Data
public class UserRegisterRequest {private String userAccount;private String userPassword;private String checkPassword;
}
3.单元测试
1.用户注册接口测试
1.debug模式启动IDEA
2.postman测试
2.用户登录接口测试
1.com/sun/usercenter/service/impl/UserServiceImpl.java的doLogin最后下断点
2.postman测试
3.查看session是否有用户登录状态的信息
4.放行,查看postman的返回结果
3.逻辑删除测试
1.在数据库中把刚才进行登录的用户id改成1
2.重新登录一下
3.用户管理接口
1.com/sun/usercenter/controller/UserController.java 添加方法
/*** 根据用户名进行模糊查询** @param username* @return 成功返回信息,失败返回null*/@GetMapping("/search")public List<User> searchUsers(String username) {QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();if (StringUtils.isNotBlank(username)) {userQueryWrapper.like("username", username);}return userService.list(userQueryWrapper);}/*** 根据id进行逻辑删除(只要配置了MyBatisPlus的逻辑删除即可进行自动逻辑删除)** @param id* @return 成功返回true,失败返回false*/@PostMapping("/delete")public boolean deleteUser(@RequestBody Long id) {if (id <= 0) {return false;}return userService.removeById(id); // 这里会自动逻辑删除}
2.user表新增字段role表示用户权限
1.新增字段role
2.com/sun/usercenter/model/domain/User.java 实体类新增权限字段
3.Mapper.xml不用修改,因为这个字段本来就与表的字段对应
4.com/sun/usercenter/service/impl/UserServiceImpl.java 用户脱敏里面把role字段加进去
3.com/sun/usercenter/contant/UserConstant.java 存放用户常量的接口
package com.sun.usercenter.contant;/*** Description: 用户常量** @Author sun* @Create 2024/3/21 15:14* @Version 1.0*/
public interface UserConstant {/*用户登录状态*/String USER_LOGIN_SAVE = "userLoginState";// 用户权限/*默认权限*/Integer DEFAULT_ROLE = 0;/*管理员权限*/Integer ADMIN_ROLE = 1;
}
4.com/sun/usercenter/controller/UserController.java 对两个接口进行权限验证
1.添加方法 isAdmin 判断是否为管理员
/*** 判断是否是管理员* @param request* @return 布尔*/public boolean isAdmin(HttpServletRequest request) {User user = (User) request.getSession().getAttribute(USER_LOGIN_SAVE);return user != null && user.getRole() == ADMIN_ROLE;}
2.修改两个接口的方法
/*** 根据用户名进行模糊查询** @param username* @return 成功返回信息,失败返回null*/@GetMapping("/search")public List<User> searchUsers(String username, HttpServletRequest request) {// 仅管理员可查询if (!isAdmin(request)) {return new ArrayList<>();}QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();if (StringUtils.isNotBlank(username)) {userQueryWrapper.like("username", username);}return userService.list(userQueryWrapper);}/*** 根据id进行逻辑删除(只要配置了MyBatisPlus的逻辑删除即可进行自动逻辑删除)** @param id* @return 成功返回true,失败返回false*/@PostMapping("/delete")public boolean deleteUser(long id, HttpServletRequest request) {// 仅管理员可删除if (!isAdmin(request)) {return false;}if (id <= 0) {return false;}return userService.removeById(id); // 这里会自动逻辑删除}
5.application.yml 设置session超时时间
6.单元测试
1.测试 searchUsers接口
1.登录
2.表中增加两条测试记录
3.进行查询,成功查询!
2.测试deleteUser接口
1.删除id为1的用户
2.查看数据库,成功进行逻辑删除
3.解决 searchUsers接口返回的用户信息没有脱敏的问题
1.com/sun/usercenter/service/UserService.java 添加方法
/*** 进行用户信息脱敏* @param user* @return*/User getCleanUser(User user);
2.com/sun/usercenter/service/impl/UserServiceImpl.java 实现方法并修改逻辑
/*** 对得到的user对象,进行用户信息脱敏* @param user* @return*/@Overridepublic User getCleanUser(User user) {User cleanUser = new User();cleanUser.setId(user.getId());cleanUser.setUsername(user.getUsername());cleanUser.setUserAccount(user.getUserAccount());cleanUser.setAvatarUrl(user.getAvatarUrl());cleanUser.setGender(user.getGender());cleanUser.setPhone(user.getPhone());cleanUser.setEmail(user.getEmail());cleanUser.setUserStatus(user.getUserStatus());cleanUser.setCreateTime(user.getCreateTime());cleanUser.setRole(user.getRole());return cleanUser;}
3.com/sun/usercenter/controller/UserController.java 修改searchUsers方法对查询到的用户列表进行脱敏
/*** 根据用户名进行模糊查询** @param username* @return 成功返回信息,失败返回null*/
@GetMapping("/search")
public List<User> searchUsers(String username, HttpServletRequest request) {// 仅管理员可查询if (!isAdmin(request)) {return new ArrayList<>();}QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();if (StringUtils.isNotBlank(username)) {userQueryWrapper.like("username", username);}List<User> userList = userService.list(userQueryWrapper);// 将查询到的用户数据进行脱敏// 这里的逻辑就是把查询到User对象列表使用user来遍历然后对每个user对象进行脱敏最后组合成一个新的listreturn userList.stream().map(user -> {return userService.getCleanUser(user);}).collect(Collectors.toList());
}
4.再次测试,敏感信息变成null了
相关文章:

用户中心项目(登录 + 用户管理功能后端)
文章目录 1.登录功能-后端1.思路分析2.完成对用户名和密码的校验1.com/sun/usercenter/service/UserService.java 添加方法2.com/sun/usercenter/service/impl/UserServiceImpl.java 添加方法3.com/sun/usercenter/service/impl/UserServiceImpl.java 新增属性 3.记录用户的登录…...
嵌入式相机WEB,用C直接处理?
以前用HTTP连接相机的时候,以为是相机内部有一个类似tomcat之类的WEB服务器。收到相机命令后,通过链接库执行动作。 昨天想给相机增加一个时间显示,增加的项目一点就跳转到登录。 于是问了之前负责的,说是要后端改。再问嵌入式相…...
LeetCode_31_中等_下一个排列
文章目录 1. 题目2. 思路及代码实现详解(Python)2.1 两遍扫描 1. 题目 整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。 例如, a r r [ 1 , 2 , 3 ] arr [1,2,3] arr[1,2,3] ,以下这些都可以视作 a r r arr arr…...

huggingface的transformers训练gpt
目录 1.原理 2.安装 3.运行 编辑 4.数据集 编辑 4.代码 4.1 model init编辑 forward: 总结: 关于loss和因果语言模型: 编辑 交叉熵:编辑 记录一下transformers库训练gpt的过程。 transformers/examples/…...

第六十一回 放冷箭燕青救主 劫法场石秀跳楼-编译安装飞桨paddlepaddle@openKylin+RISCV
卢俊义在水里被张顺抓住,用轿子抬到了梁山。宋江等人下马跪在地上迎接,请他坐第一把交椅。卢俊义宁死不从,大家只好说留他在山寨几天,先让李固带着马车货物回去。吴用对李固说,你的主人已经答应坐第二把交椅了…...

白话讲人工智能、机器学习、深度学习
人工智能(Artificial Intelligence,AI) 定义: 想象一个聪明的机器人,它能思考、决策和学习,就像电影里的智能角色那样。人工智能就是努力打造这样的智能实体的学科,它试图模仿、扩展乃至超越人…...

ssm项目(tomcat项目),定时任务(每天运行一次)相同时间多次重复运行job 的bug
目录标题 一、原因 一、原因 debug本地调试没有出现定时任务多次运行的bug,上传到服务器就出现多次运行的bug。(war的方式部署到tomcat) 一开始我以为是代码原因,或者是linux和win环境不同运行定时任务的方式不一样。 但是自己…...

vue3 + ts +element-plus + vue-router + scss + axios搭建项目
本地环境: node版本:20.10.0 目录 一、搭建环境 二、创建项目 三、修改页面 四、封装路由vue-router 五、element-plus 六、安装scss 七、封装axios 一、搭建环境 1、安装vue脚手架 npm i -g vue/cli 2、查看脚手架版本 vue -V3、切换路径到需…...

二叉树试题解析
一、单项选择题 01.下列关于二叉树的说法中,正确的是( C ). A.度为2的有序树就是二叉树 B.含有n个结点的二叉树的高度为 C.在完全二叉树中,若一个结点没有左孩子,则它必是叶结点 D.含有n个结点的完全二叉树的高度为解析:A 二叉树…...

计算机服务器中了faust勒索病毒怎么办,faust勒索病毒解密工具流程
网络是一把利剑,可以方便企业开展各项工作业务,为企业提供极大的便利,但随着网络技术的不断发展与应用,网络数据安全威胁也在不断增加,给企业的正常生产运营带来了极大困扰,近日,云天数据恢复中…...
初次部署麒麟V10系统需要的配置,快速完成测试环境的搭建
配置麒麟V10 设置“root”登录密码 sudo su -passwd # 设置登录密码允许“root”远程登录 sudo vim /etc/ssh/sshd_configsshd_config # ↓↓↓↓修改的内容↓↓↓↓ PermitRootLogin yes # ↑↑↑↑修改的内容↑↑↑↑重启服务 sudo systemctl restart sshd允许通过图像界…...
DOcker in Docker 原理与实战代码详解
Docker in Docker(DinD)指的是在Docker容器内部运行另一个Docker守护进程和客户端。这种技术可以用于创建嵌套的Docker环境,例如在持续集成/持续部署(CI/CD)管道中构建和测试Docker镜像。然而,需要注意的是…...

公司系统中了.rmallox勒索病毒如何恢复数据?
早晨上班时刻: 当阳光逐渐洒满大地,城市的喧嚣开始涌动,某公司的员工们纷纷踏入办公大楼,准备开始新的一天的工作。他们像往常一样打开电脑,准备接收邮件、查看日程、浏览项目进展。 病毒悄然发作: 就在员…...

论文阅读:Forget-Me-Not: Learning to Forget in Text-to-Image Diffusion Models
Forget-Me-Not: Learning to Forget in Text-to-Image Diffusion Models 论文链接 代码链接 这篇文章提出了Forget-Me-Not (FMN),用来消除文生图扩散模型中的特定内容。FMN的流程图如下: 可以看到,FMN的损失函数是最小化要消除的概念对应的…...

html5cssjs代码 036 CSS默认值
html5&css&js代码 036 CSS默认值 一、代码二、解释 CSS默认值(也称为浏览器默认样式)是指当HTML元素没有应用任何外部CSS样式时,浏览器自动为这些元素赋予的一组基本样式。这些样式是由浏览器的默认样式表(User Agent sty…...
小米路由器4A千兆版刷回官方固件
原文链接:小米路由器4A千兆版刷回官方固件及修改SN绑定APP-小米无线路由器及小米网络设备-恩山无线论坛 (right.com.cn) 进入breed 由于openwrt工作不稳定,决定重新刷回官方固件。 由于当前路由器已经刷过breed,不再重新刷入。 如何刷入b…...

【Leetcode每日一题】 递归 - 两两交换链表中的节点(难度⭐)(38)
1. 题目解析 题目链接:24. 两两交换链表中的节点 这个问题的理解其实相当简单,只需看一下示例,基本就能明白其含义了。 2.算法原理 一、理解递归函数的含义 首先,我们需要明确递归函数的任务:给定一个链表…...

如何部署GPT模型至自有服务器:从零开始搭建你的智能聊天机器人
引言 GPT模型是自然语言处理领域的重要突破,它能够通过生成式的文本生成方式,实现与用户的智能交互。本文将详细介绍如何将GPT模型部署到自有服务器上,并编写一个基本的API接口来实现与聊天机器人的交互。 目录 引言 一、准备工作 首先&am…...
uniapp 之 一些常用方法的封装(页面跳转,页面传参等)
util.js 提示:permission.js是uniapp插件市场由官方DCloud_heavensoft提供的App权限判断和提示插件。 import permision from "/js_sdk/wa-permission/permission.js"/*** uni.toast 封装* param {String} msg toast 提示内容* param {Number} duration …...
flutter 单列选择器
引入 flutter_pickers: ^2.1.9 import package:flutter_pickers/pickers.dart; import package:flutter_pickers/style/default_style.dart; import package:flutter_pickers/style/picker_style.dart;List<String> _numberList [99,98,97,96,95,94,93,92,91,90,89,88,…...

手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘
美国西海岸的夏天,再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至,这不仅是开发者的盛宴,更是全球数亿苹果用户翘首以盼的科技春晚。今年,苹果依旧为我们带来了全家桶式的系统更新,包括 iOS 26、iPadOS 26…...
树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频
使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...

Xshell远程连接Kali(默认 | 私钥)Note版
前言:xshell远程连接,私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...

【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
【HTML-16】深入理解HTML中的块元素与行内元素
HTML元素根据其显示特性可以分为两大类:块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战
“🤖手搓TuyaAI语音指令 😍秒变表情包大师,让萌系Otto机器人🔥玩出智能新花样!开整!” 🤖 Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制(TuyaAI…...