基于 Spring Boot 的小区人脸识别与出入记录管理系统实现
在智慧社区建设中,人脸识别技术的应用极大提升了小区管理效率和安全性。本文将介绍如何使用 Spring Boot 框架结合百度 AI 人脸识别 API,实现小区人员出入自动识别与记录管理功能。
系统功能概述
本系统主要包含两大核心功能:
- 人脸识别出入管理:通过摄像头采集人脸图像,自动识别人员身份并记录出入时间
- 出入记录查询:支持按时间范围、人员姓名等条件查询出入记录,方便管理人员统计分析
技术栈选择
- 后端框架:Spring Boot 2.7.4
- 持久层框架:MyBatis-Plus 3.5.1
- 数据库:MySQL
- 人脸识别:百度 AI 开放平台
- 工具类:Hutool、Lombok
- 前端交互:RESTful API
核心依赖配置
首先在pom.xml中添加核心依赖:
<!-- 百度AI SDK -->
<dependency><groupId>com.baidu.aip</groupId><artifactId>java-sdk</artifactId><version>4.16.19</version><exclusions><exclusion><groupId>org.slf4j</groupId><artifactId>slf4j-simple</artifactId></exclusion></exclusions>
</dependency><!-- Spring Boot核心依赖 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency><!-- 数据访问 -->
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.1</version>
</dependency>
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope>
</dependency><!-- 工具类 -->
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional>
</dependency>
<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.2.4</version>
</dependency><!-- 文件处理 -->
<dependency><groupId>commons-fileupload</groupId><artifactId>commons-fileupload</artifactId><version>1.3.1</version>
</dependency>
数据模型设计
出入记录实体类
@Data
@TableName("in_out_record")
public class InOutRecordEntity implements Serializable {private static final long serialVersionUID = 1L;@TableId(value = "in_out_record_id", type = IdType.AUTO)private Integer inOutRecordId;@TableField("person_id")private Integer personId;@TableField("community_id")private Integer communityId;@TableField("in_time")private LocalDateTime inTime;@TableField("out_time")private LocalDateTime outTime;@TableField("in_pic")private String inPic;@TableField("out_pic")private String outPic;
}
出入记录查询表单
@Data
public class InOutForm {private Long page;private Long limit;private String userName;@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")private LocalDateTime startDate;@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")private LocalDateTime endDate;
}
出入记录 VO 类(用于前端展示)
@Data
public class InOutRecordVO {@TableId(value = "in_out_record_id", type = IdType.AUTO)private Integer inOutRecordId;private Integer personId;private Integer communityId;private LocalDateTime inTime;private LocalDateTime outTime;private String inPic;private String outPic;// 扩展字段,用于展示private String userName;private String communityName;private String termName;private String houseNo;
}
百度 AI 工具类实现
封装百度 AI 人脸识别相关操作:
@Component
@Slf4j
public class BaiduAiUtils {@Value("${baidu.face.appId}")private String APP_ID;@Value("${baidu.face.apiKey}")private String API_KEY;@Value("${baidu.face.secretKey}")private String SECRET_KEY;@Value("${baidu.face.imageType}")private String IMAGE_TYPE;@Value("${baidu.face.groupId}")private String groupId;private AipFace client;private HashMap<String, Object> options = new HashMap<>();public BaiduAiUtils() {// 设置图像质量控制options.put("quality_control", "NORMAL");// 设置活体检测控制级别options.put("liveness_control", "LOW");}@PostConstructpublic void init() {// 初始化百度AI客户端client = new AipFace(APP_ID, API_KEY, SECRET_KEY);}/*** 人脸检测(检查是否有且仅有一张人脸)*/public Boolean faceCheck(String image) {JSONObject res = client.detect(image, IMAGE_TYPE, options);log.info("detect result :{}", res);if (res.has("error_code") && res.getInt("error_code") == 0) {JSONObject resultObject = res.getJSONObject("result");Integer faceNum = resultObject.getInt("face_num");return faceNum == 1;}return false;}/*** 人脸搜索(匹配用户)*/public String faceSearch(String image) {JSONObject res = client.search(image, IMAGE_TYPE, groupId, options);log.info("search result :{}", res);if (res.has("error_code") && res.getInt("error_code") == 0) {JSONObject result = res.getJSONObject("result");JSONArray userList = result.getJSONArray("user_list");if (userList.length() > 0) {JSONObject user = userList.getJSONObject(0);double score = user.getDouble("score");// 置信度大于80分认为匹配成功if (score > 80) {return user.getString("user_id");}}}return null;}
}
业务逻辑实现
出入记录服务实现类
@Service
public class InOutRecordServiceImpl extends ServiceImpl<InOutRecordMapper, InOutRecordEntity> implements InOutRecordService {@Autowiredprivate InOutRecordMapper inOutRecordMapper;@Autowiredprivate PersonMapper personMapper;@Overridepublic InOutPageListVO getInOutList(InOutForm form) {Page<InOutRecordEntity> page = new Page<>(form.getPage(), form.getLimit());QueryWrapper<InOutRecordEntity> queryWrapper = new QueryWrapper<>();// 时间范围查询if (form.getStartDate() != null && form.getEndDate() != null) {queryWrapper.between("in_time", form.getStartDate(), form.getEndDate());}// 如果需要按用户名查询,可以在这里添加关联查询条件Page<InOutRecordEntity> pages = inOutRecordMapper.selectPage(page, queryWrapper);List<InOutRecordVO> inOutRecordVOList = new ArrayList<>();// 转换为VO对象并补充关联信息for(InOutRecordEntity entity : pages.getRecords()){InOutRecordVO vo = new InOutRecordVO();BeanUtils.copyProperties(entity, vo);PersonEntity person = personMapper.selectById(entity.getPersonId());if (person != null) {vo.setUserName(person.getUserName());vo.setHouseNo(person.getHouseNo());}// 获取小区名称vo.setCommunityName(personMapper.selectCommunityNameByID(entity.getCommunityId()));inOutRecordVOList.add(vo);}// 封装分页结果InOutPageListVO result = new InOutPageListVO();result.setRecords(inOutRecordVOList);result.setTotalCount(pages.getTotal());result.setPageSize(pages.getSize());result.setTotalPage(pages.getPages());result.setCurrPage(pages.getCurrent());return result;}@Overridepublic InOutRecordEntity findLatestRecord(Integer personId) {QueryWrapper<InOutRecordEntity> queryWrapper = new QueryWrapper<>();queryWrapper.eq("person_id", personId).orderByDesc("in_time").last("LIMIT 1");return this.getOne(queryWrapper);}
}
控制器实现
人脸识别与出入记录控制器
@RestController
@RequestMapping("/sys/inOut")
public class InOutRecordController {@Autowiredprivate BaiduAiUtils baiduAiUtils;@Autowiredprivate PersonService personService;@Autowiredprivate InOutRecordService recordService;@Value("${file.upload-dir}")private String uploadDir;/*** 人脸识别接口*/@PostMapping("/add")public Result add(@RequestBody FaceForm faceForm) {// 提取Base64图像数据String fileBase64 = faceForm.getFileBase64();if (fileBase64.contains(",")) {fileBase64 = fileBase64.split(",")[1];}// 1. 检测人脸boolean hasValidFace = baiduAiUtils.faceCheck(fileBase64);if (!hasValidFace) {return Result.error("人脸检测失败");}// 2. 人脸搜索匹配用户String userId = baiduAiUtils.faceSearch(fileBase64);if (userId == null) {return Result.ok().put("data", "人员信息不存在").put("status", "fail");}// 3. 查询用户信息int personId;try {personId = Integer.parseInt(userId);} catch (NumberFormatException e) {return Result.error("用户ID格式错误");}PersonEntity person = personService.getById(personId);if (person == null) {return Result.ok().put("data", "人员信息不存在").put("status", "fail");}try {// 4. 保存图片到本地String fileName = System.currentTimeMillis() + ".png";String filePath = Paths.get(uploadDir, fileName).toString();// 确保目录存在File dir = new File(uploadDir);if (!dir.exists()) {dir.mkdirs();}// 解码并保存图片byte[] imageBytes = Base64.getDecoder().decode(fileBase64);Files.write(Paths.get(filePath), imageBytes);// 构建图片URLString fullUrl = "http://localhost:8080/photos/" + fileName;// 5. 查找最近记录判断是入场还是出场InOutRecordEntity latestRecord = recordService.findLatestRecord(personId);if (latestRecord == null || latestRecord.getOutTime() != null) {// 入场记录InOutRecordEntity newRecord = new InOutRecordEntity();newRecord.setCommunityId(faceForm.getCommunityId());newRecord.setPersonId(personId);newRecord.setInTime(LocalDateTime.now());newRecord.setInPic(fullUrl);recordService.save(newRecord);return Result.ok().put("data", person.getUserName() + "进入小区").put("status", "success");} else {// 出场记录latestRecord.setOutTime(LocalDateTime.now());latestRecord.setOutPic(fullUrl);recordService.updateById(latestRecord);return Result.ok().put("data", person.getUserName() + "离开小区").put("status", "success");}} catch (Exception e) {e.printStackTrace();return Result.error("操作失败: " + e.getMessage());}}/*** 出入记录查询接口*/@GetMapping("/list")public Result list(InOutForm form) {// 获取分页数据InOutPageListVO pageListVO = inOutRecordService.getInOutList(form);// 构建返回结构Map<String, Object> pageListMap = new HashMap<>();pageListMap.put("totalCount", pageListVO.getTotalCount());pageListMap.put("pageSize", pageListVO.getPageSize());pageListMap.put("totalPage", pageListVO.getTotalPage());pageListMap.put("currPage", pageListVO.getCurrPage());pageListMap.put("list", pageListVO.getRecords());Map<String, Object> dataMap = new HashMap<>();dataMap.put("pageList", pageListMap);return Result.ok().put("data", dataMap);}
}
接口使用说明
人脸识别接口
请求地址:POST /sys/inOut/add
请求参数:
{"communityId": 2,"extName": "png","fileBase64": "iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAYAAAA10dzkAAAAAXNSR0IArs4c6QAAIABJREFUeF7sXe2OHblu3BljTX1lQUgyxEvv9HMQ5yIZq239/8B8gtdpbgl/6cAAAAASUVORK5CYII="
}
成功响应(人员进入):
{"msg": "操作成功","code": 200,"data": "张三进入小区","status": "success"
}
失败响应(人员不存在):
{"msg": "操作成功","code": 200,"data": "人员信息不存在","status": "fail"
}
出入记录查询接口
请求地址:GET /sys/inOut/list
请求参数:
{"page": 1,"limit": 10,"userName": "张三","startDate": "2023-07-20 12:59:54","endDate": "2023-07-20 23:00:00"
}
响应结果:
{"msg": "操作成功","code": 200,"data": {"pageList": {"totalCount": 1,"pageSize": 10,"totalPage": 1,"currPage": 1,"list": [{"inOutRecordId": 44,"inTime": "2023-07-19 16:51:55","outTime": "2023-07-19 16:52:07","inPic": "http://localhost:8181/villegePic/face/47b49187-a5e9-486a-b8ac-4409710b3323.png","outPic": "http://localhost:8181/villegePic/face/4cbfb2b9-a691-4d0a-a4d4-4bf602cb33ac.png","communityName": "栖海澐颂","termName": "8栋","houseNo": "802","userName": "丽丽"}]}}
}
系统优化建议
性能优化:
- 对人脸图片进行压缩处理,减少传输和存储开销
- 对查询接口添加缓存,提高高频查询效率
安全性增强:
- 提高活体检测级别,防止照片、视频等欺骗手段
- 对敏感接口添加权限控制
- 对 Base64 图片传输进行加密
功能扩展:
- 添加异常出入提醒功能
- 实现批量导出记录报表功能
- 增加访客临时授权功能
通过以上实现,我们构建了一个完整的小区人脸识别出入管理系统,该系统能够自动识别人员身份并记录出入信息,同时提供灵活的查询功能,为小区管理提供了便捷高效的解决方案。