1. 项目概述

本文将介绍如何基于Spring Boot集成腾讯云人员库实现人脸识别门禁系统。系统功能包括:

  • 前端拍照或上传人脸照片
  • 调用腾讯云AI接口进行人脸识别
  • 验证人员身份及小区归属
  • 生成出入记录并保存人脸图片
  • 数据库实时更新出入状态

2. 系统架构设计

前端页面 → Spring Boot后端 → 腾讯云AI接口 → 本地数据库
       ↑        ↑
   人脸图片    出入记录

3. 前端页面实现

前端页面需要实现以下功能:

  • 摄像头实时拍照功能
  • 人脸图片上传功能
  • 小区选择下拉菜单
  • 识别结果展示区域
  • 出入记录显示面板

4. 后端接口实现

4.1 出入记录控制器(InOurRecordController)

@PostMapping("/add")
public Result add(@RequestBody InOutForm inOutFaceForm) {
    // 调用腾讯AI接口进行人脸识别
    FaceApi faceApi = new FaceApi();
    RootResp resp = faceApi.searchPersonsReturnsByGroup(
        apiConfiguration, 
        inOutFaceForm.getFileBase64()
    );
    
    if (resp.getRet() != 0) {
        return Result.error("人脸识别失败: " + resp.getMsg());
    }
    
    // 解析腾讯云返回数据
    JSONObject result = parseTencentResponse(resp);
    if (result == null) {
        return Result.error("未识别到有效人脸信息");
    }
    
    // 验证人员身份和小区归属
    Person person = validatePerson(result, inOutFaceForm.getCommunityId());
    if (person == null) {
        return Result.error("身份验证失败");
    }
    
    // 生成出入记录
    return generateInOutRecord(inOutFaceForm, person);
}

// 解析腾讯云返回数据
private JSONObject parseTencentResponse(RootResp resp) {
    JSONObject object = JSONObject.parseObject(resp.getData().toString());
    JSONArray resultsReturnsByGroup = object.getJSONArray("ResultsReturnsByGroup");
    JSONObject returnsByGroup = resultsReturnsByGroup.getJSONObject(0);
    JSONArray groupCandidates = returnsByGroup.getJSONArray("GroupCandidates");
    JSONObject groupCandidate = groupCandidates.getJSONObject(0);
    JSONArray candidates = groupCandidate.getJSONArray("Candidates");
    
    // 获取相似度最高的候选人
    JSONObject candidate = candidates.getJSONObject(0);
    if (candidate.getInteger("Score") < 80) {
        return null; // 相似度过低
    }
    return candidate;
}

// 验证人员信息
private Person validatePerson(JSONObject candidate, Integer communityId) {
    String personId = candidate.getString("PersonId").substring(4);
    Person person = personService.getById(Long.parseLong(personId));
    
    if (person == null) {
        return null;
    }
    
    if (!communityId.equals(person.getCommunityId())) {
        throw new BusinessException("非本小区居民");
    }
    return person;
}

// 生成出入记录
private Result generateInOutRecord(InOutForm form, Person person) {
    InOutRecord record = new InOutRecord();
    record.setCommunityId(person.getCommunityId());
    record.setPersonId(person.getPersonId());
    
    try {
        // 保存人脸图片
        String fileName = saveFaceImage(form.getFileBase64(), form.getExtName());
        String imagePath = urlPrefix + "community/upload/face/" + fileName;
        
        // 检查现有记录
        InOutRecord existingRecord = inOutRecordMapper.getInOutRecord(record);
        
        if (existingRecord == null) {
            // 进入记录
            record.setInTime(LocalDateTime.now());
            record.setInPic(imagePath);
            inOutRecordMapper.insert(record);
            return Result.ok("【" + person.getUserName() + "】进入小区");
        } else {
            // 离开记录
            existingRecord.setOutTime(LocalDateTime.now());
            existingRecord.setOutPic(imagePath);
            inOutRecordMapper.updateById(existingRecord);
            return Result.ok("【" + person.getUserName() + "】离开小区");
        }
    } catch (Exception e) {
        logger.error("记录生成失败", e);
        return Result.error("系统异常");
    }
}

// 保存图片到本地
private String saveFaceImage(String base64Data, String extName) {
    String fileName = UUID.randomUUID() + "." + extName;
    Base64Util.decoderBase64File(base64Data, faceImagePath + fileName);
    return fileName;
}

4.2 出入记录表单(InOutForm)

@Data
public class InOutForm {
    private Integer communityId;  // 小区ID
    private String extName;       // 图片扩展名
    private String fileBase64;    // Base64编码的图片数据
}

5. 腾讯云接口封装(FaceApi)

5.1 关键方法:人员搜索

public RootResp searchPersonsReturnsByGroup(ApiConfiguration config, String image) {
    RootResp result = new RootResp();
    try {
        Credential cred = new Credential(config.getSecretId(), config.getSecretKey());
        HttpProfile httpProfile = new HttpProfile();
        httpProfile.setEndpoint(config.getServerIp());
        
        ClientProfile clientProfile = new ClientProfile();
        clientProfile.setHttpProfile(httpProfile);
        
        IaiClient client = new IaiClient(cred, config.getArea(), clientProfile);
        
        JSONObject paramObj = new JSONObject();
        paramObj.put("GroupIds", new String[]{config.getGroupId()});
        paramObj.put("Image", image);
        paramObj.put("MaxPersonNumPerGroup", 5); // 返回最相似的5个候选人
        paramObj.put("NeedPersonInfo", 1);       // 返回人员详细信息
        paramObj.put("MaxFaceNum", 1);           // 最多识别1张人脸
        
        SearchFacesReturnsByGroupRequest req = SearchFacesReturnsByGroupRequest.fromJsonString(
            paramObj.toJSONString(), 
            SearchFacesReturnsByGroupRequest.class
        );
        
        SearchFacesReturnsByGroupResponse resp = client.SearchFacesReturnsByGroup(req);
        result.setData(SearchFacesReturnsByGroupResponse.toJsonString(resp));
    } catch (TencentCloudSDKException e) {
        result.setRet(-1);
        result.setMsg(e.toString());
        logger.error("腾讯云接口调用失败", e);
    }
    return result;
}

6. 数据库设计与关键SQL

6.1 出入记录表(in_out_record)

字段 类型 描述
id BIGINT 主键ID
community_id INT 小区ID
person_id BIGINT 人员ID
in_time DATETIME 进入时间
out_time DATETIME 离开时间
in_pic VARCHAR(255) 进入时照片路径
out_pic VARCHAR(255) 离开时照片路径

6.2 关键SQL:判断进出状态

<select id="getInOutRecord" resultType="com.qcby.AICommunity.entity.InOutRecord">
    SELECT * FROM in_out_record 
    WHERE community_id = #{communityId} 
      AND person_id = #{personId} 
      AND out_time IS NULL
</select>
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐