8. 实现业务功能--用户注册
目录
1. 顺序图
2. 参数要求
3. 接口规范
4. 创建扩展 Mapper.xml
5. 修改 DAO
6. 创建 Service 接口
7. 实现接口
8. 测试接口
9. 实现 Controller
9.1 密码加密处理
10. 实现前端界面
业务实现过程中主要的包和目录及主要功能:
- model 包:实体对象
- dao 包:数据库访问
- services 包:业务处理相关的接口与实现,所有业务都在 Services 中实现
- controller 包:提供 URL 映射,用来接收参数并做校验, 调用 Service 中的业务代码 ,返回执行结果
- src/main/resources/mapper 目录:Mybaits 映射文件,配置数据库实体与类之间的映射关系
- src/main/resources/static 目录:前端资源
那么我们再来看一下各个包之间的调用关系:

- Controller 包:主要用来接收用户的参数,并封装 Service 层需要使用的对象,最终为客户端响应结果
- Service 包:处理业务逻辑,调用 dao 包完成数据的持久化。如果要执行多条数据库更新操作,那么就需要用事务管理
1. 顺序图

注意, 5 应该为根据用户名查询用户信息,即 selectByName(name)。
2. 参数要求
注册时,需要用户提交的参数列表:
| 参数名 | 描述 | 参数 | 默认值 | 条件 |
|---|---|---|---|---|
| username | 用户名 | String | 必须 | |
| nickname | 昵称 | String | 与用户名相同 | 必须 |
| password | 密码 | String | 必须 | |
| passwordRepeat | 确认密码 | String | 必须,与密码相同 |
必须 即 需要进行非空校验,还需要进行两次密码输入校验。
3. 接口规范
// 请求
POST /user/register HTTP/1.1Content-Type: application/x-www-form-urlencoded
username=user222&nickname=user222&password=123456&passwordRepeat=123456
// 响应
HTTP/1.1 200
Content-Type: application/json
{"code":0,"message":"成功","data":null}
接下来,就是实现 Service 层,具体通过以下步骤:
1. 在 Mapper.xml 中编写 SQL 语句
2. 在 Mapper.java 中定义方法
3. 定义 Service 接口
4. 实现 Service 接口
5. 单元测试
6. Controller 实现方法并对外提供的 API 接口
7. 测试 API 接口
8. 实现前端逻辑,完成前后端交互
4. 创建扩展 Mapper.xml
写入操作:已经存在(自动生成的)不需要手动编写


根据用户名查询信息:
为了避免在自动生成时重复生成相同的语句,我们在 mapper 包下,新建一个 extension 包,将 UserMapper.xml 文件重新复制一份,并重命名为:UserExtMapper

注意:

因为,命名相同的 xml 文件最终会被解析成一个大文件,里面定义的所有标签都可以相互调用。
接下来,我们先来查看一下此时数据库中的数据:

<?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.example.demo.dao.UserMapper"><!--1. 注意 namespace 表⽰命名空间,要与 UserMapper.xml 中的 namespace 相同2. 统⼀⽤ com.example.forum.dao.UserMapper, 也就是UserMapper的完全限定名(包名+类名)3. 不同的映射⽂件指定了相同的namespace后,定义的所有⽤id或name标识的结果集映射都可以在不同的⽂件中共享
--><!-- 根据用户名查询用户信息 --><select id="selectByName" parameterType="java.lang.String" resultMap="BaseResultMap">SELECT<include refid="Base_Column_List" />from t_user where username = #{username,jdbcType=VARCHAR}</select>
</mapper>
可以看到我们用来代替 * 的语句中,使用的标签已经进行了全列的定义:

5. 修改 DAO

/**
* 根据用户名查询用户信息
* @param username 用户名
* @return
*/
User selectByName(String username);

6. 创建 Service 接口
在 demo 包下创建 services 包,在 services 包下创建 IUserService 接口。
接口的命名规则: I + 实现类的名字 + Service

public interface IUserService {/*** 根据用户名查询用户信息* @param username 用户名* @return*/User selectByName(String username);/*** 创建普通用户* @param user 用户名* @return*/User createNormalUser(User user);
}
7. 实现接口
在 services 包下新建 impl 包,在 impl 包下新建类:UserServiceimpl 类,并实现 IUserService 接口:

命名规则:实体类名 + Service + Impl
接下来 Alt + 回车 生成实现方法,并加入注解:
@Slf4j // 日志
@Service // 交给 Spring 管理
public class UserServiceImpl implements IUserService {@Overridepublic User selectByName(String username) {return null;}@Overridepublic User createNormalUser(User user) {return null;}
}
在实现的过程中需要对每一个必传参数做非空校验,从而保证程序的健壮性。

可以看到我们需要多次使用到非空校验的判断,因此我们可以将此处的语句抽取出来,放在一个公共的工具类中。在 demo 包下新建包 utils,在 utils 包下新建类 StringUtils:

public class StringUtils {/*** 校验指定的字符串是否为空* @param value 待校验的字符串* @return true 空 <br/> false 非空*/public static boolean isEmpty(String value){if(value == null || value.isEmpty()){return true;}return false;}
}
接下来继续完善我们的实现接口部分:
@Slf4j // 日志
@Service // 交给 Spring 管理
public class UserServiceImpl implements IUserService {@Resourceprivate UserMapper userMapper;@Overridepublic User selectByName(String username) {// 非空校验if(StringUtils.isEmpty(username)){// 打印日志log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());// 抛出异常throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));}// 根据用户名查询用户信息User user = userMapper.selectByName(username);// 返回结果return user;}@Overridepublic User createNormalUser(User user) {return null;}
}
接下来我们实现 createNormalUser 方法:
@Slf4j // 日志
@Service // 交给 Spring 管理
public class UserServiceImpl implements IUserService {@Resourceprivate UserMapper userMapper;@Overridepublic User selectByName(String username) {// 非空校验if(StringUtils.isEmpty(username)){// 打印日志log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());// 抛出异常throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));}// 根据用户名查询用户信息User user = userMapper.selectByName(username);// 返回结果return user;}@Overridepublic void createNormalUser(User user) {// 非空校验if(user == null || StringUtils.isEmpty(user.getUsername())|| StringUtils.isEmpty(user.getNickname())|| StringUtils.isEmpty(user.getPassword())|| StringUtils.isEmpty(user.getSalt())){// 打印日志log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());// 抛出异常throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));}// 校验用户是否存在User existUser = selectByName(user.getUsername());if(existUser != null){// 打印日志log.warn(ResultCode.FAILED_USER_EXISTS.toString() + "username = " + user.getUsername());// 抛出异常,用户已存在throw new ApplicationException(AppResult.failed(ResultCode.FAILED_USER_EXISTS));}// 为属性设置默认值// 性别 0女 1男 2保密if(user.getGender() != null){if(user.getGender() < 0 || user.getGender() > 2){user.setGender((byte)2);}}else{user.setGender((byte)2);}// 发帖数user.setArticleCount(0);// 是否管理员user.setIsAdmin((byte)0);// 状态user.setState((byte)0);// 时间Date date = new Date();user.setCreateTime(date); // 创建时间user.setUpdateTime(date); // 更新时间// 写入数据库int row = userMapper.insertSelective(user);if(row != 1){// 打印日志log.warn(ResultCode.FAILED_CREATE.toString() + "注册用户失败,username = " + user.getUsername());// 抛出异常,用户已存在throw new ApplicationException(AppResult.failed(ResultCode.FAILED_CREATE));}}
}
8. 测试接口
在 UserServiceImpl 类中右键生成测试方法:

加上 @SpringBootTest 注解:
@SpringBootTest
class UserServiceImplTest {@Resourceprivate IUserService userService;@Resourceprivate ObjectMapper objectMapper; // JSON 对象@Testvoid selectByName() throws JsonProcessingException {// 调用 Service 查询用户信息User user = userService.selectByName("bitboy");System.out.println(objectMapper.writeValueAsString(user));System.out.println("========================================");user= userService.selectByName("bitboy111");System.out.println(objectMapper.writeValueAsString(user));System.out.println("========================================");user = userService.selectByName(null);System.out.println(objectMapper.writeValueAsString(user));}@Testvoid createNormalUser() {}
}
查询结果如下:

与数据库中的数据相符合,因此测试成功。
接下来添加 createNormalUser 的测试:
@SpringBootTest
class UserServiceImplTest {@Resourceprivate IUserService userService;@Resourceprivate ObjectMapper objectMapper; // JSON 对象@Testvoid selectByName() throws JsonProcessingException {// 调用 Service 查询用户信息User user = userService.selectByName("bitboy");System.out.println(objectMapper.writeValueAsString(user));System.out.println("========================================");user= userService.selectByName("bitboy111");System.out.println(objectMapper.writeValueAsString(user));System.out.println("========================================");
// user = userService.selectByName(null);
// System.out.println(objectMapper.writeValueAsString(user));}@Testvoid createNormalUser() {// 构造用户User user = new User();user.setUsername("TestUser1");user.setNickname("单元测试用户1");user.setPassword("123456");user.setSalt("123456");// 调用 ServiceuserService.createNormalUser((user));System.out.println("注册成功");System.out.println("========================");user.setUsername("bitboy");// 调用 ServiceuserService.createNormalUser((user));System.out.println("注册成功");}
}
运行结果如下图所示:

测试成功。
9. 实现 Controller
在 controller 包下新建 UserController 类:


9.1 密码加密处理
由于在存储用户的密码时,不能直接存储明文,否则会泄露账户信息,因此需要通过盐进行加密处理,最终将盐和密文(明文密码+ 扰动字符串(盐))存储在数据库中。
盐 -> 随机字符
MD5(MD5(明文密码) + SALT)= 密码对应的密文
新建 UUIDUtils 类:
public class UUIDUtils {/*** 生成一个标准的36字符的UUID** @return*/public static String UUID_36() {return UUID.randomUUID().toString();}/*** 生成一个32字符的UUID** @return*/public static String UUID_32() {return UUID.randomUUID().toString().replace("-", "");}
}
在 ForumApplicationTests 类中添加测试方法:
@Testvoid testUUID(){System.out.println(UUIDUtils.UUID_36());System.out.println(UUIDUtils.UUID_32());}
结果如下图所示:

在 pom.xml 中添加依赖:
<!-- 编码解码加密⼯具包-->
<dependency><groupId>commons-codec</groupId><artifactId>commons-codec</artifactId>
</dependency>
在 utils 包下新建 MD5Utils 类:
import org.apache.commons.codec.digest.DigestUtils;;public class MD5Utils {/*** 普通MD5加密* @param str 原始字符串* @return ⼀次MD5加密后的密⽂*/public static String md5 (String str) {return DigestUtils.md5Hex(str);}/*** 原始字符串与Key组合进⾏⼀次MD5加密* @param str 原始字符串* @param key* @return 组合字符串⼀次MD5加密后的密⽂*/public static String md5 (String str, String key) {return DigestUtils.md5Hex(str + key);}/*** 原始字符串加密后与扰动字符串组合再进⾏⼀次MD5加密* @param str 原始字符串* @param salt 扰动字符串* @return 加密后的密⽂*/public static String md5Salt (String str, String salt) {return DigestUtils.md5Hex(DigestUtils.md5Hex(str) + salt);}/*** 校验原⽂与盐加密后是否与传⼊的密⽂相同* @param original 原字符串* @param salt 扰动字符串* @param ciphertext 密⽂* @return true 相同, false 不同*/public static boolean verifyOriginalAndCiphertext (String original, String salt, String ciphertext) {String md5text = md5Salt(original, salt);if (md5text.equalsIgnoreCase(ciphertext)) {return true;}return false;}
}
继续在 UserController 类中编写代码:
@Api(tags = "用户接口")
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {@Resourceprivate IUserService userService;@ApiOperation("用户注册")@PostMapping("/register")public AppResult register(@ApiParam("用户名") @RequestParam("username") @NonNull String username,@ApiParam("昵称") @RequestParam("nickname") @NonNull String nickname,@ApiParam("密码") @RequestParam("password") @NonNull String password,@ApiParam("确认密码") @RequestParam("passwordRepeat") @NonNull String passwordRepeat){// 校验密码是否一致if (!password.equals(passwordRepeat)) {// 返回错误信息return AppResult.failed(ResultCode.FAILED_TWO_PWD_NOT_SAME);}// 构造对象User user = new User();user.setUsername(username); // 用户名user.setNickname(nickname); // 昵称// 处理密码// 1.生成盐String salt = UUIDUtils.UUID_32();// 2.生成密码的密文String encryptPassword = MD5Utils.md5Salt(password, salt);// 3 设置密码和盐user.setPassword(encryptPassword);user.setSalt(salt);// 3. 调⽤ServiceuserService.createNormalUser(user);// 4. 返回结果return AppResult.success("注册成功");}
}
运行启动类 ForumApplication:
打开:http://127.0.0.1:58080/swagger-ui/index.html

接下来进行密码测试:

测试成功:

继续测试用户名:

测试成功:

继续测试正确的输入:

测试成功:

查看数据库中的数据:

可以看到数据库中显示的密码已经进行了加密处理。
10. 实现前端界面
完成表单校验工作后,需要构造要提交的数据:
- 通过选择器找到需要提交的标签
- 获取标签中的值,并封装成JS对象
- 前后端交互时定义的参数列表:参数名=对象的属性 参数值=标签中的值(用户输入的值)

完整的前端代码可以参考:forum: 论坛项目 - Gitee.com
接下来我们打开注册界面,输入已有的用户名,可以看到提示框弹出:

接下来,我们在 html 文件中,自定义弹出的提示框的样式。
$.toast({heading: '警告',text: respData.message,icon: 'Warning'});

停止运行启动类,重新进行注册,可以看到:

接下来,我们正确的进行注册:

可以看到通过前端界面进行注册后,数据库中正确增加了一条数据。
并且注册成功后直接跳转至了登录界面:

用户注册的基本功能已经实现,在下篇文章中,我们将实现用户登录功能。
相关文章:
8. 实现业务功能--用户注册
目录 1. 顺序图 2. 参数要求 3. 接口规范 4. 创建扩展 Mapper.xml 5. 修改 DAO 6. 创建 Service 接口 7. 实现接口 8. 测试接口 9. 实现 Controller 9.1 密码加密处理 10. 实现前端界面 业务实现过程中主要的包和目录及主要功能: model 包:实体对象 d…...
深入浅出Pytorch函数——torch.nn.init.eye_
分类目录:《深入浅出Pytorch函数》总目录 相关文章: 深入浅出Pytorch函数——torch.nn.init.calculate_gain 深入浅出Pytorch函数——torch.nn.init.uniform_ 深入浅出Pytorch函数——torch.nn.init.normal_ 深入浅出Pytorch函数——torch.nn.init.c…...
版本控制工具Git集成IDEA的学习笔记(第一篇Gitee)
目录 一、Gitee的使用 1、注册网站会员 2、用户中心 3、创建远程仓库 4、配置SSH免密登录 二、集成IDEA,Git项目搭建 1、本地仓库搭建 1)创建一个新项目 2)打开终端,在当前目录新建一个Git代码库 3)忽略文件 …...
【链表】 61. 旋转链表
61. 旋转链表 解题思路 首先计算出链表长度将链表长度进行取余遍历链表 对链表进行分割 得到两个子链表重新连接两个链表比如1 2 3 4 5 k 2 进行分割得到 1 2 3 和 4 5两个子链表 /*** Definition for singly-linked list.* public class ListNode {* int val;* Lis…...
深入浅出Pytorch函数——torch.nn.init.kaiming_uniform_
分类目录:《深入浅出Pytorch函数》总目录 相关文章: 深入浅出Pytorch函数——torch.nn.init.calculate_gain 深入浅出Pytorch函数——torch.nn.init.uniform_ 深入浅出Pytorch函数——torch.nn.init.normal_ 深入浅出Pytorch函数——torch.nn.init.c…...
查询Oracle和MySQL数据库中当前所有连接信息
查询Oracle当前所有连接信息: SELECTs.sid AS 会话ID,s.serial# AS 序列号,s.username AS 用户名,s.osuser AS 操作系统用户,s.machine AS 客户端机器,s.program AS 客户端程序,s.status AS 会话状态,s.sql_id AS 正在执行的SQL_ID,t.sql_text AS 正在执行的SQL文本…...
Android glide框架及框架涉及到的设计模式
目录 原文链接Android glide框架 简单使用介绍Glide 框架整体结构设计Glide 框架的优点基本使用:Glide占位符 Android glide框架涉及到的设计模式 原文链接 Android glide框架 简单使用介绍 Glide:快速高效的Android图片加载库,可以自动加载…...
使用yolov5进行安全帽检测填坑指南
参考项目 cGitHub - PeterH0323/Smart_Construction: Base on YOLOv5 Head Person Helmet Detection on Construction Sites,基于目标检测工地安全帽和禁入危险区域识别系统,🚀😆附 YOLOv5 训练自己的…...
【BASH】回顾与知识点梳理(三十二)
【BASH】回顾与知识点梳理 三十二 三十二. SELinux 初探32.1 什么是 SELinux当初设计的目标:避免资源的误用传统的文件权限与账号关系:自主式访问控制, DAC以政策规则订定特定进程读取特定文件:委任式访问控制, MAC 32.2 SELinux 的运作模式安…...
vscode远程调试PHP代码
目录 一、准备工作 二、ssh连接和xdebug配置 1.ssh连接 2.xdebug配置 三、xdebug调试,访问 一、准备工作 1.安装vscode里面的两个扩展 2.安装对应PHP版本的xdebug 去xdebug官方,复制自己的phpinfo源码到方框里,再点击Analyse Xdebug: …...
flink1.17 实现 udf scalarFunctoin get_json_object 支持 非标准化json
特色 相比官方的json_value,该函数支持非标准化json,比如v是个object,但是非标准json会外套一层引号,内部有反引号. eg: {"kkkk2": "{\"kkkk1\":\"vvvvvvv\"}" } 支持value为 100L 这种java格式的bigint. {"k":999L…...
基于VUE3+Layui从头搭建通用后台管理系统(前端篇)九:自定义组件封装下
一、本章内容 续上一张,本章实现一些自定义组件的封装,包括文件上传组件封装、级联选择组件封装、富文本组件封装等。 1. 详细课程地址: 待发布 2. 源码下载地址: 待发布 二、界面预览 三、开发视频 基于VUE3+Layui从头搭建通用后台管...
设计模式详解-装饰器模式
类型:结构型模式 实现原理:装饰器模式通过将对象包装在装饰器类中,并在保持类方法签名完整性的前提下,提供额外功能 作用:动态地给一个对象添加一些额外的职责。增加功能方面,装饰器模式比生成子类更灵活…...
Android5:活动生命周期
创建项目Stopwatch activity_main.xml <?xml version"1.0" encoding"utf-8"?> <LinearLayoutxmlns:android"http://schemas.android.com/apk/res/android"xmlns:tools"http://schemas.android.com/tools"android:layout_w…...
第2章 数据结构和算法概述
2.3线性结构和非线性结构 数据结构包括: 线性结构和非线性结构 2.3.1线性结构 线性结构作为最常用的数据结构,其特点是数据元素之间存在一对一的线性关系线性结构有两种不同的存储结构,即顺序存储结构(数组)和链式存储结构(链表)。顺序存储的线性表称…...
WPF国际化的实现方法(WpfExtensions.Xaml)
https://blog.csdn.net/eyupaopao/article/details/120090431 resx资源文件实现 resx资源文件,实现的过程比第一种复杂,但resx文件本身编辑比较简单,维护起来比较方便。需要用到的框架:WpfExtensions.Xaml 为每种语言添加.resx资…...
【Linux】—— 进程程序替换
目录 序言 (一)替换原理 1、进程角度——见见猪跑 1️⃣ 认识 execl 函数 2、程序角度——看图理解 (二)替换函数 1、命名理解 2、函数理解 1️⃣execlp 2️⃣execv 3️⃣execvp 4️⃣execle 5️⃣execve 6️⃣execve…...
idea创建javaweb项目,jboss下没有web application
看看下图这个地方有没有web application...
广东灯具3D扫描抄数建模服务3D测绘出图纸三维逆向设计-CASAIM
灯具三维逆向建模是一种将实际物体转换为数字模型的过程。通过逆向工程技术,可以将现有的灯具进行3D扫描,然后利用专业的逆向设计软件将其转换为准确的三维模型。 以下是CASAIM实施灯具三维逆向建模的一般步骤图: 1. 扫描:三维扫…...
Nginx反向代理-负载均衡、webshell实践
目录 1.nginx反向代理-负载均衡 1)搭建web项目 2)修改 nginx.conf的配置 2.webshell 实践 1)异或操作绕过 2)取反绕过 3)php语法绕过 1.nginx反向代理-负载均衡 1)搭建web项目 首先通过SpringBoo…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...
【第二十一章 SDIO接口(SDIO)】
第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...
1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...
视频字幕质量评估的大规模细粒度基准
大家读完觉得有帮助记得关注和点赞!!! 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用,因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型(VLMs)在字幕生成方面…...
涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战
“🤖手搓TuyaAI语音指令 😍秒变表情包大师,让萌系Otto机器人🔥玩出智能新花样!开整!” 🤖 Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制(TuyaAI…...
自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...
uniapp 开发ios, xcode 提交app store connect 和 testflight内测
uniapp 中配置 配置manifest 文档:manifest.json 应用配置 | uni-app官网 hbuilderx中本地打包 下载IOS最新SDK 开发环境 | uni小程序SDK hbulderx 版本号:4.66 对应的sdk版本 4.66 两者必须一致 本地打包的资源导入到SDK 导入资源 | uni小程序SDK …...
BLEU评分:机器翻译质量评估的黄金标准
BLEU评分:机器翻译质量评估的黄金标准 1. 引言 在自然语言处理(NLP)领域,衡量一个机器翻译模型的性能至关重要。BLEU (Bilingual Evaluation Understudy) 作为一种自动化评估指标,自2002年由IBM的Kishore Papineni等人提出以来,…...
探索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 数据…...
破解路内监管盲区:免布线低位视频桩重塑停车管理新标准
城市路内停车管理常因行道树遮挡、高位设备盲区等问题,导致车牌识别率低、逃费率高,传统模式在复杂路段束手无策。免布线低位视频桩凭借超低视角部署与智能算法,正成为破局关键。该设备安装于车位侧方0.5-0.7米高度,直接规避树枝遮…...
