人脸考勤签到进阶篇

这篇具有很好参考价值的文章主要介绍了人脸考勤签到进阶篇。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

签到业务流程说明

一、需求介绍

二、如何获取地理信息?

三、如何判定某地区新冠疫情的风险等级?

开通腾讯位置服务

二、腾讯位置服务SDK

把定位坐标转换成真实地址

一、获取定位坐标

uni.authorize(OBJECT)

二、编辑签到页面

在Docker中安装人脸识别镜像

安装Docker程序

导入人脸识别镜像

运行人脸识别程序

一、创建Docker容器

二、运行人脸识别程序

三、接口调用

实现人脸签到(持久层) 

一、维护员工人脸模型数据

二、保存签到记录

实现人脸签到(业务层)

一、判断签到用户是否存在人脸模型

查询签到所在地区新冠疫情风险等级

一、利用本地宝查询地区风险等级

二、编写持久层代码

三、补充签到业务层代码

发送疫情高风险地区告警邮件

一、为什么要采用异步发送邮件?

二、导入Email邮件库

三、设置SMTP服务器信息

二、实现异步发送邮件

实现人脸签到(Web层)

一、设置上传图片存储的路径

二、编辑Controller类 

创建新员工人脸模型数据(业务层) 

一、编写抽象方法

二、编写创建人脸模型方法

创建新员工人脸模型数据(Web层)

实现人脸签到(移动端)


签到业务流程说明

一、需求介绍

        Emos系统的人脸签到模块包含的功能非常丰富,不仅仅只有人脸识别的签到功能,而且还可以根据用户签到时候的地理定位,计算出该地区是 新冠疫情 的 高风险 还是 低风险 地区。如果员工是在疫情高风险地区签到的,Emos系统会立即向公司人事部门发送告警邮件。

人脸考勤签到进阶篇

二、如何获取地理信息?

        微信小程序提供了获取地理定位的接口方法,我们调用该方法就能获取到地理坐标。但是我们得到的仅仅是坐标而已,我们还需要把地理坐标转换成地址信息,例如什么省份、什么城市、什么街道等等。

        腾讯位置服务提供了把地理坐标转换成地址这个功能,只需要我们注册之后就可以免费使用了。并且还提供了JS调用接口,我们在小程序中可以很简单的把地理坐标转换成地址信息。

三、如何判定某地区新冠疫情的风险等级?

        本地宝这个网站提供了新冠疫情地区风险等级的查询,我们输入自己的地址,就能看到具体的风险等级。

        既然我们已经把地理坐标转换成了地址信息,那么就可以根据地址信息去查询风险等级了。但是本地宝并没有提供Web接口让我们调用,所以我们只能URL地址传参的方式获取本地宝返回的响应。而且响应的内容是HTML,我们还要从HTML中解析出我们想要的风险等级信息。

开通腾讯位置服务

一、开通腾讯位置服务步骤

        因为Emos签到流程中要获取用户当前所在地址的信息,所以需要把定位坐标缓存成地址,恰好腾讯位置服务提供了这个功能。所以我们按照提示开通这个服务即可,该服务对开发者来说是免费的,所以我们可以放心使用。 

        首先我们用浏览器访问 腾讯位置服务 官网,然后在页面的右上角点击注册按钮,并且填写注册信息。 

        在 应用管理  〉我的应用 栏目中,可以看到已经创建的密钥。如果是新注册的用户,这里没有任何密钥,需要你自己创建一个新的密钥。

        根据提示填写密钥的信息。密钥创建成功之后,你要把密钥字符串记录下来,在小程序开发当中会用到。

        把该密钥和咱们的小程序关联在一起,在界面中填写小程序的授权ID。

二、腾讯位置服务SDK

        腾讯位置服务提供了多种SDK程序包,其中的JavaScript版本的SDK适用于微信小程序,所以我们下载这个SDK包。 

        登陆微信公众平台里面,在“开发管理” -> “开发设置”中设置request合法域名,添加https://apis.map.qq.com 。

        在小程序项目中,创建 lib 目录,把SDK文件放入其中。

把定位坐标转换成真实地址

一、获取定位坐标

可以通过用户授权API来判断用户是否给应用授予定位权限。

uni.authorize(OBJECT)

uni.authorize({
    scope: 'scope.userLocation',
    success() {
        uni.getLocation()
    }
})

注意:scope.userLocation 权限需要在 manifest.json 配置 permission 

        微信小程序提供了定位接口,只需要我们调用方法即可。uni-app框架的uni对象里面也封装了地理定位的方法,我们来看一下。

uni.getLocation(OBJECT)     

        获取当前的地理位置和速度。 在微信小程序中,当用户离开应用后,此接口无法调用,除非申请后台持续定位权限;当用户点击“显示在聊天顶部”时,此接口可继续调用。 

// 示例
uni.getLocation({
    type: 'wgs84',
    success: function (res) {
        console.log('当前位置的经度:' + res.longitude);
        console.log('当前位置的纬度:' + res.latitude);
    }
});

二、编辑签到页面

我们首先要获取用户签到时的地理定位

uni.showLoading({
    title: '签到中请稍后' 3. });
    setTimeout(function() { 5. uni.hideLoading();
}, 30000);
//获取地理定位
uni.getLocation({
    type: 'wgs84',
    success: function(resp) {
        let latitude = resp.latitude;
        let longitude = resp.longitude;
    }
})

接下来我们根据定位坐标,换算成真实地址,先引用腾讯位置SDK文件 

var QQMapWX = require('../../lib/qqmap-wx-jssdk.min.js');
var qqmapsdk; 

然后在 onLoad() 生命周期函数中,初始化 qqmapsdk 对象 

onLoad: function() { 
    qqmapsdk = new QQMapWX({
        key: 'KSFBZ-####-####-####-37KUE-W3FLZ'
    });
},

编写JS代码把GPS坐标转换成地址 

qqmapsdk.reverseGeocoder({
    location: { 
        latitude: latitude, 
        longitude: longitude
    },
    success: function(resp) { 
        // console.log(resp.result);
        let address = resp.result.address; 
        let addressComponent = resp.result.address_component;
        let nation = addressComponent.nation;
        let province = addressComponent.province;
        let city = addressComponent.city;
        let district = addressComponent.district;
    }
})

在Docker中安装人脸识别镜像

安装Docker程序

执行下面的指令,稍等片刻,Docker程序就安装好了 

yum install docker -y 

管理Docker程序的命令也非常简单,如下:

service docker start

service docker stop

service docker restart

导入人脸识别镜像

把 face.tar.gz 文件上传到CentOS系统 

把镜像导入Docker环境

#导入镜像文件

docker load < face.tar.gz

#查看安装的镜像

docker images

#删除镜像

docker rmi face

运行人脸识别程序

一、创建Docker容器

        上节课我们在Docker中安装了人脸识别镜像,因为人脸识别程序是用Python写的,而且需要很多依赖库,安装起来非常麻烦,所以我就把依赖环境和人脸识别程序封装成Docker镜像,只要你在本地Docker上面导入镜像,创建出容器,就能运行Python人脸程序了。 

把 demo.tar 文件上传到Linux根目录,然后解压缩 

tar -xvf demo.tar

        解压缩之后,demo文件夹中就包含了人脸识别Python程序,我们只需要把demo文件夹挂载到Docker容器,那么在容器中就能访问Linux主机的demo文件夹了。下面开始创建容器,映射端口号,挂载目录。 

#创建容器,把容器3000端口映射到宿主机3000端口,把/demo映射到宿主机的/demo

docker run -d -it -p 3000:3000 -v /demo:/demo --name node face

#查看容器运行状态

docker ps -a 

#进入到node容器

docker exec -it node bash

二、运行人脸识别程序

进入到node容器之后,然后进入 /demo 目录,运行人脸识别程序

cd /demo

#把Python程序挂起到后台运行

nohup python3 -c "from app import app;" > log.out 2>&1 &

ps -aux

kill -9 进程ID

三、接口调用

人脸识别程序程序结合了Flask框架,提供Web接口,具体如下 

1. 创建人脸模型数据 

        当Emos系统的MySQL数据库中不存在签到员工的人脸模型数据,这时候应该调用人脸识别程序的Web接口,上传照片文件,然后由Python程序识别照片中的人脸,返回人脸模型数据。Java系统接收到人脸模型数据之后,把数据保存在MySQL数据表里面。 

接口名称:/create_face_model 

请求类型:POST 

传入参数:icode

返回结果:人脸模型数据 

2. 执行人脸签到识别 

接口名称:/checkin 

请求类型:POST 

传入参数:icode 

返回结果:人脸识别结果

实现人脸签到(持久层) 

一、维护员工人脸模型数据

在 TbFaceModelDao.xml 文件中添加SQL语句 

    <select id="searchFaceModel" parameterType="int" resultType="String">
        SELECT face_model FROM tb_face_model
        WHERE user_id=#{userId}
    </select>
    <insert id="insert" parameterType="com.example.emos.wx.db.pojo.TbFaceModel">
        INSERT INTO tb_face_model
        SET user_id=#{userId},
            face_model=#{faceModel}
    </insert>
    <delete id="deleteFaceModel" parameterType="int">
        DELETE FROM tb_face_model
        WHERE user_id=#{userId}
    </delete>

在 TbFaceModelDao.java 接口中添加DAO方法  

@Mapper
public interface TbFaceModelDao {
    public String searchFaceModel(int userId);
    public void insert(TbFaceModel faceModel);
    public int deleteFaceModel(int userId);
}

二、保存签到记录

在 TbCheckinDao.xml 文件中添加INSERT语句

  <insert id="insert" parameterType="com.example.emos.wx.db.pojo.TbCheckin">
    INSERT INTO tb_checkin
    SET user_id=#{userId},
    <if test="address!=null">
      address=#{address},
    </if>
    <if test="country!=null">
      country=#{country},
    </if>
    <if test="province!=null">
      province=#{province},
    </if>
    <if test="city!=null">
      city=#{city},
    </if>
    <if test="district!=null">
      district=#{district},
    </if>
    status=#{status},
    <if test="risk!=null">
      risk=#{risk},
    </if>
    date=#{date},
    create_time=#{createTime}
  </insert>

在 TbCheckinDao.java 中添加抽象方法 

@Mapper
public interface TbCheckinDao {
    ……
    public void insert(TbCheckin entity);
}

实现人脸签到(业务层)

一、判断签到用户是否存在人脸模型

在 application.yml 文件中,添加值注入信息 

emos:
  ……
  face:
    createFaceModelUrl: http://CentOS的IP地址:3000/create_face_model
    checkinUrl: http://CentOS的IP地址:3000/checkin
  code: HelloWorld

创建 CheckinForm.java 表单类,接收小程序提交的签到数据 

@Data
@ApiModel
public class CheckinForm {
    private String address;
    private String country;
    private String province;
    private String city;
    private String district;
}

在 CheckinService.java 接口中添加抽象的签到方法 

public interface CheckinService {
    ……
    public void checkin(HashMap param);
}

在 CheckinServiceImpl.java 中实现抽象方法

@Service
@Scope("prototype")
@Slf4j
public class CheckinServiceImpl implements CheckinService {
    @Autowired
    private TbFaceModelDao faceModelDao;

    @Value("${emos.face.checkinUrl}")
    private String checkinUrl;

    @Autowired
    private SystemConstants constants;

    @Value("${emos.code}")
    private String code;

    @Override
    public void checkin(HashMap param) {
        Date d1=DateUtil.date();
        Date d2=DateUtil.parse(DateUtil.today()+" "+constants.attendanceTime);
        Date d3=DateUtil.parse(DateUtil.today()+" "+constants.attendanceEndTime);
        int status=1;
        if(d1.compareTo(d2)<=0){
            status=1;
        }
        else if(d1.compareTo(d2)>0&&d1.compareTo(d3)<0){
            status=2;
        }
        else{
            throw new EmosException("超出考勤时间段,无法考勤");
        }
        int userId= (Integer) param.get("userId");
        String faceModel=faceModelDao.searchFaceModel(userId);
        if(faceModel==null){
            throw new EmosException("不存在人脸模型");
        }
        else{
            String path=(String)param.get("path");
            HttpRequest request= HttpUtil.createPost(checkinUrl);
            request.form("photo", FileUtil.file(path),"targetModel",faceModel);
            request.form("code",code);
            HttpResponse response=request.execute();
            if(response.getStatus()!=200){
                log.error("人脸识别服务异常");
                throw new EmosException("人脸识别服务异常");
            }
            String body=response.body();
            if("无法识别出人脸".equals(body)||"照片中存在多张人脸".equals(body)){
                throw new EmosException(body);
            }
            else if("False".equals(body)){
                throw new EmosException("签到无效,非本人签到");
            }
            else if("True".equals(body)){
                //TODO 查询疫情风险等级
                //TODO 保存签到记录
            }
        }
    }
}

查询签到所在地区新冠疫情风险等级

@Data
public class TbCheckin implements Serializable {

    private String date;

    private Date createTime;
}

延伸:date字段是日期类型,createTime字段是Datetime类型。Java中没有Datetime类型,所以映射时用了日期类型-Date类。数据表中date类型就是date类型,保存的数据就是日期不包含时间。如果映射成Java中的日期类型,Java中日期类型还会有小时分钟秒毫秒,这些信息不应该存在。所以一个正确的ORM映射,就是把数据表中date类型字段映射到Java的string变量上。这样就只保存了日期数据,并不包含小时分钟秒毫秒之类的。

一、利用本地宝查询地区风险等级

        本地宝H5网页提供了新冠疫情风险等级查询,在网页上面直接输入地区,就能查询到疫情的风险等级。 

        Java程序想要查询用户签到地区的风险等级,不能到页面里面点来点去的,所以我们要用URL传参的方式,把地址信息传入本地宝的H5页面。 

        你可以在浏览器地址栏填写下方的URL连接,就能查询到北京市西城区当前的新冠疫情风险等级。

        http://m.bj.bendibao.com/news/yqdengji/?qu=西城区

从上面的案例推断,URL地址要传入两个参数: 城市编码 和 区县 。 

城市编码可以从 tb_city 表中查询到,其中的code字段就是城市对应的编号。

人脸考勤签到进阶篇

        我们可以用小程序提交过来的签到城市,然后到 tb_city 表中根据城市名称查询到城市编号。接下来,就可以把参数添加到URL上面。 

        我们想要提取查询到的风险等级结果应该怎么办呢?这个很简单,用Java程序解析本地宝HTML页面的标签,提取我们想要的结果信息即可。在Java领域中 jsoup 提供了解析HTML标签的功能,所以我们要在Java项目中引入 jsoup 库。 

在 pom.xml 文件中添加 jsoup 依赖,然后重新reload项目

<dependency>
    <groupId>org.jsoup</groupId>
    <artifactId>jsoup</artifactId>
    <version>1.13.1</version>
</dependency>

二、编写持久层代码

在 TbCityDao.xml 文件中添加查询语句 

<select id="searchCode" parameterType="String" resultType="String"> 
    SELECT code
    FROM tb_city
    WHERE city = #{city}
</select>

在 TbCityDao.java 接口中添加抽象方法 

@Mapper
public interface TbCityDao { 
    public String searchCode(String city);
}

三、补充签到业务层代码

在 CheckinServiceImpl.java 文件中继续补充查询疫情风险等级的代码 

@Autowired
private TbCityDao cityDao;

@Override
public void checkin(HashMap param) {
    ……
    String faceModel=faceModelDao.searchFaceModel(userId);
        if(faceModel==null){
            throw new EmosException("不存在人脸模型");
        }
        else{
            ……
            if("无法识别出人脸".equals(body)||"照片中存在多张人脸".equals(body)){
                throw new EmosException(body);
            }
            else if("False".equals(body)){
                throw new EmosException("签到无效,非本人签到");
            }
            else if("True".equals(body)){
                //查询疫情风险等级
                int risk=1;
                String city= (String) param.get("city");
                String district= (String) param.get("district");
                String address= (String) param.get("address");
                String country= (String) param.get("country");
                String province= (String) param.get("province");
                if(!StrUtil.isBlank(city)&&!StrUtil.isBlank(district)){
                    String code=cityDao.searchCode(city);
                    try{
                        String url = "http://m." + code + ".bendibao.com/news/yqdengji/?qu=" + district;
                        Document document=Jsoup.connect(url).get();
                        Elements elements=document.getElementsByClass("list-content");
                        if(elements.size()>0){
                            Element element=elements.get(0);
                            String result=element.select("p:last-child").text();
                            // result="高风险";
                            if("高风险".equals(result)){
                                risk=3;
                                //发送告警邮件
                            }
                            else if("中风险".equals(result)){
                                risk=2;
                            }
                        }
                    }catch (Exception e){
                        log.error("执行异常",e);
                        throw new EmosException("获取风险等级失败");
                    }
                }
                //保存签到记录
                TbCheckin entity=new TbCheckin();
                entity.setUserId(userId);
                entity.setAddress(address);
                entity.setCountry(country);
                entity.setProvince(province);
                entity.setCity(city);
                entity.setDistrict(district);
                entity.setStatus((byte) status);
                entity.setRisk(risk);
                entity.setDate(DateUtil.today());
                entity.setCreateTime(d1);
                checkinDao.insert(entity);
            }
        }
    }
}

发送疫情高风险地区告警邮件

一、为什么要采用异步发送邮件?

        因为在签到过程中,执行人脸识别和查询疫情风险等级,都比较消耗时间。如果发送邮件再做成同步执行的,势必导致签到执行时间过长,影响用户体验。由于要把签到结果保存到签到表,所以人脸识别和疫情风险等级查询必须是同步执行的。发送邮件跟保存签到数据没有直接关联,所以做成异步并行执行的程序更好一些,这样也能缩短用户签到时候等待的时间。

人脸考勤签到进阶篇

二、导入Email邮件库

编辑 pom.xml 文件,添加依赖库 

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
</dependency>

三、设置SMTP服务器信息

        发送邮件是通过SMTP服务器来完成的,所以我们要配置一下SMTP服务器的连接信息。这里我以163的SMTP服务器为例,并且提前已经开启了163邮箱的SMTP功能。 

spring:
    ……
    mail:
        default-encoding: UTF-8
        host: smtp.163.com
        username: *************@163.com
        password: 此处是密码

        接下来我们把系统内的常用邮箱声明一下,以后会用到这些邮箱往外发送邮件,或者给这些邮箱发送内部邮件。例如,员工签到地点是疫情高风险地区,那么就应该向HR邮箱发送邮件,告知人事总监有员工需要隔离。 

emos:
    ……
    email:
        system: *********@163.com
        hr: **********@qq.com

二、实现异步发送邮件

在SpringBoot项目中开启异步多线程非常简单,只需要下面几个步骤即可。 

在主类上面开启 @EnableAsync 注解 

……
@EnableAsync
public class EmosWxApiApplication { 
    ……
} 

在 com.example.emos.wx.config 中创建 ThreadPoolConfig 类,声明Java线程池 

@Configuration
public class ThreadPoolConfig {
    @Bean("AsyncTaskExecutor")
    public AsyncTaskExecutor taskExecutor(){
        ThreadPoolTaskExecutor executor=new ThreadPoolTaskExecutor();
        // 设置核心线程数
        executor.setCorePoolSize(8);
        // 设置最大线程数
        executor.setMaxPoolSize(16);
        // 设置队列容量
        executor.setQueueCapacity(32);
        // 设置线程活跃时间(秒)
        executor.setKeepAliveSeconds(60);
        // 设置默认线程名称
        executor.setThreadNamePrefix("task-");
        // 设置拒绝策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;

    }
}

// 线程池对象自动注册给Spring项目了。

在 com.example.emos.wx.task 中创建 EmailTask 类,定义线程任务

@Component
@Scope("prototype")
public class EmailTask implements Serializable {
    @Autowired
    private JavaMailSender javaMailSender;

    @Value("${emos.email.system}")
    private String mailbox;

    @Async
    public void sendAsync(SimpleMailMessage message){
        message.setFrom(mailbox);
        // message.setCc(mailbox); // 抄送给自己
        javaMailSender.send(message);
    }
}

// @Component
// @Scope("prototype")
// Serializable
// @Async
// 都是必须的

查询员工的姓名和部门名称,在 TbUserDao.xml 文件中声明查询语句 

<select id="searchNameAndDept" parameterType="int" resultType="HashMap"> 
    SELECT u.name, d.dept_name
    FROM tb_user u LEFT JOIN tb_dept d ON u.dept_id=d.id
    WHERE u.id = #{userId} AND u.status = 1
</select>

在 TbUserDao 接口中定义抽象方法 

public HashMap searchNameAndDept(int userId);

定义值注入变量,用来接收人员隔离告警邮件 

@Value("${emos.email.hr}") 
private String hrEmail; 

@Autowired
private EmailTask emailTask; 

@Autowired
private TbUserDao userDao;

编写发送告警邮件的代码 

HashMap<String,String> map=userDao.searchNameAndDept(userId);
String name = map.get("name");
String deptName = map.get("dept_name");
deptName = deptName != null ? deptName : "";
SimpleMailMessage message=new SimpleMailMessage();
message.setTo(hrEmail);
message.setSubject("员工" + name + "身处高风险疫情地区警告");
message.setText(deptName + "员工" + name + "," + DateUtil.format(new Date(), "yyyy年MM月dd日") + "处于" + address + ",属于新冠疫情高风险地区,请及时与该员工联系,核实情况!");
emailTask.sendAsync(message);

实现人脸签到(Web层)

一、设置上传图片存储的路径

        因为签到自拍照是临时使用,所以不需要存储在腾讯云对象存储中,我们只需要在本地找个文件夹存放这些签到照片,签到业务执行完,就立即删除该文件即可。 

在 application.yml 文件中,设置图片存放路径

emos:
    ……
    image-folder: D:/emos/image

在主类中添加初始化代码,项目启动时候自动创建图片文件夹 

……
public class EmosWxApiApplication { 
    ……
    @Value("${emos.image-folder}") 
    private String imageFolder; 

    ……
    @PostConstruct
    public void init(){
        ……
        new File(imageFolder).mkdirs();
    }

}

二、编辑Controller类 

编辑 CheckinController.java 类,定义 checkin() 方法

@RequestMapping("/checkin")
@RestController
@Api("签到模块Web接口")
@Slf4j
public class CheckinController {

    @Value("${emos.image-folder}")
    private String imageFolder;

    @PostMapping("/checkin")
    @ApiOperation("签到")
    public R checkin(@Valid CheckinForm form,@RequestParam("photo") MultipartFile file,@RequestHeader("token") String token){
        if(file==null){
            return R.error("没有上传文件");
        }
        int userId=jwtUtil.getUserId(token);
        String fileName=file.getOriginalFilename().toLowerCase();
        if(!fileName.endsWith(".jpg")){
            return R.error("必须提交JPG格式图片");
        }
        else{
            String path=imageFolder+"/"+fileName;
            try{
                file.transferTo(Paths.get(path));
                HashMap param=new HashMap();
                param.put("userId",userId);
                param.put("path",path);
                param.put("city",form.getCity());
                param.put("district",form.getDistrict());
                param.put("address",form.getAddress());
                param.put("country",form.getCountry());
                param.put("province",form.getProvince());
                checkinService.checkin(param);
                return R.ok("签到成功");
            }catch (IOException e){
                log.error(e.getMessage(),e);
                throw new EmosException("图片保存错误");
            }
            finally {
                FileUtil.del(path);
            }

        }
    }
}
// 防止照片重名,加上时间戳
if (file != null) {
    //获取上传文件名
    fileName = file1.getOriginalFilename();
    //获取后缀名
    String sname = fileName.substring(fileName.lastIndexOf("."));
    //时间格式化格式
    SimpleDateFormat simpleDateFormat =new SimpleDateFormat("yyyyMMddHHmmssSSS");
    //获取当前时间并作为时间戳
    String timeStamp=simpleDateFormat.format(new Date());
    //拼接新的文件名
    String newName ="人脸识别"+timeStamp+sname;
    //指定上传文件的路径
    String path = "F:\\" + newName;
    //上传保存
    file.transferTo(new File(path));
    //保存当前文件路径
    request.getSession().setAttribute("currFilePath", path);
}

创建新员工人脸模型数据(业务层) 

一、编写抽象方法

        如果用户是第一次签到,checkin方法检测到数据库中没有该员工的人脸模型数据,移动端会收到异常消息,所以要重新发送HTTP请求,让后端项目用签到照片创建人脸模型数据。所以我们先来把创建人脸模型的业务层抽象方法声明一下。 

在 CheckinService 接口中,声明抽象方法 

public interface CheckinService {
    ……
    public void createFaceModel(int userId, String path);
}

二、编写创建人脸模型方法

在 CheckinServiceImpl 类中,实现抽象方法 

……
public class CheckinServiceImpl implements CheckinService {
    ……
    @Value("${emos.face.createFaceModelUrl}")
    private String createFaceModelUrl;
    ……
    @Override
    public void createFaceModel(int userId, String path) {
        HttpRequest request=HttpUtil.createPost(createFaceModelUrl);
        request.form("photo",FileUtil.file(path));
        request.form("code",code);
        HttpResponse response=request.execute();
        String body=response.body();
        if("无法识别出人脸".equals(body)||"照片中存在多张人脸".equals(body)){
            throw new EmosException(body);
        }
        else{
            TbFaceModel entity=new TbFaceModel();
            entity.setUserId(userId);
            entity.setFaceModel(body);
            faceModelDao.insert(entity);
        }
    }
}

创建新员工人脸模型数据(Web层)

在 CheckinController 类中创建 createFaceModel() 方法

@RequestMapping("/checkin")
@RestController
@Api("签到模块Web接口")
@Slf4j
public class CheckinController {
    ……
    @PostMapping("/createFaceModel")
    @ApiOperation("创建人脸模型")
    public R createFaceModel(@RequestParam("photo") MultipartFile file,@RequestHeader("token") String token){
        if(file==null){
            return R.error("没有上传文件");
        }
        int userId=jwtUtil.getUserId(token);
        String fileName=file.getOriginalFilename().toLowerCase();
        if(!fileName.endsWith(".jpg")){
            return R.error("必须提交JPG格式图片");
        }
        else{
            String path=imageFolder+"/"+fileName;
            try{
                file.transferTo(Paths.get(path));
                checkinService.createFaceModel(userId,path);
                return R.ok("人脸建模成功");
            }catch (IOException e){
                log.error(e.getMessage(),e);
                throw new EmosException("图片保存错误");
            }
            finally {
                FileUtil.del(path);
            }

        }
    }
}

实现人脸签到(移动端)

        每人每天只可签到一次,调试时要删掉数据表数据。

        163邮箱反垃圾邮件级别提升,会拦截咱们项目发送邮件,推荐使用阿里邮箱个人版。

        application.yml 中修改 spring.mail 和 emos.email 项文章来源地址https://www.toymoban.com/news/detail-505199.html

到了这里,关于人脸考勤签到进阶篇的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 基于java和百度智能AI的人脸识别考勤签到系统设计与实现

     博主介绍 :黄菊华老师《Vue.js入门与商城开发实战》《微信小程序商城开发》图书作者,CSDN博客专家,在线教育专家,CSDN钻石讲师;专注大学生毕业设计教育和辅导。 所有项目都配有从入门到精通的基础知识视频课程,免费 项目配有对应开发文档、开题报告、任务书、

    2024年02月05日
    浏览(34)
  • 安全策略与业务需求不匹配:安全规则与业务流程的优先级不一致

    随着网络攻击手段层出不穷、黑客技术的日益升级和网络安全法规的日益严格化,企业在保障信息安全的同时也面临着越来越大的压力和挑战。其中一个突出的问题是**安全策略与业务需求的不匹配问题**。这主要表现为安全规则的制定与企业日常的业务流程存在很大的差异和

    2024年01月21日
    浏览(35)
  • 自定义Dynamics 365实施和发布业务解决方案 - 4. 自动化业务流程

    本章的主要重点是研究拟议应用程序的关键业务流程的自动化。每个组织每天都有自己独特的业务操作,这些操作是业务的关键部分。有些自动化的业务流程不需要用户交互,有些流程需要用户交互。此外,在某些业务流程中,某些用户操作完成,然后触发自动化流程来完成

    2024年02月09日
    浏览(36)
  • JAVA基于百度AI接口的人脸识别考勤签到系统设计与实现(Springboot框架)

     博主介绍 :黄菊华老师《Vue.js入门与商城开发实战》《微信小程序商城开发》图书作者,CSDN博客专家,在线教育专家,CSDN钻石讲师;专注大学生毕业设计教育和辅导。 所有项目都配有从入门到精通的基础知识视频课程,免费 项目配有对应开发文档、开题报告、任务书、

    2024年02月04日
    浏览(40)
  • 业务流程自动化:ThinkAutomation Professional Crack

    ThinkAutomation 助力您的业务流程自动化。自动执行本地和基于云的业务流程,以降低成本并节省时间。 自动化传入的通信渠道,监控数据库,对传入的Webhook,Web表单和聊天机器人做出反应。处理文档、附件、本地文件和其他邮件源。 从传入消息中解析和提取数据并执行业务流

    2024年02月09日
    浏览(28)
  • AI智能语音机器人的基本业务流程

    先画个图,了解下AI语音机器人的基本业务流程。 上图是一个AI语音机器人的业务流程,简单来说就是首先要配置话术,就是告诉机器人在遇到问题该怎么回答,这个不同公司不同行业的差别比较大,所以一般每个客户都会配置其个性化的话术。 话术配置完成后,需要给账号

    2024年02月12日
    浏览(39)
  • 业务流程编排系统设计中的API安全与鉴权

    在当今的数字时代,API(应用程序接口)已经成为企业和组织中不可或缺的组件。它们为不同系统之间的通信和数据共享提供了一个标准化的方式。然而,随着API的普及和使用,API安全和鉴权问题也变得越来越重要。业务流程编排系统中的API安全与鉴权涉及到保护API免受未经授

    2024年02月19日
    浏览(26)
  • 秒杀系统的业务流程以及优化方案(实现异步秒杀)

    先看基本的业务流程  那么我们可以看到整个流程都是一个线程来完成的,这样的话耗时还是很长的,那么可不可以采用多线程去实现呢? 首先我们要思考怎么对业务进行拆分,可以想象一个我们去饭店点餐,会有前台接待,询问订单,之后将小票传给后厨去做饭,这样就会

    2024年02月11日
    浏览(31)
  • 尚上优选社区团购业务流程及微服务技术实现

    尚上优选是一家社区电商项目,采用“当日下单+次日送达+门店自提”的模式,围绕社区居民日常生活所需,满足不同用户的差异化需求,通过完善的 仓储配送体系,以便捷的方式和舒心的服务提升了每一个普通家庭的消费体验。 掌握社区团购业务流程及实现方式 掌握Spri

    2024年02月10日
    浏览(41)
  • 【vue2】使用vue常见的业务流程与实现思路

     🥳博       主: 初映CY的前说(前端领域) 🌞个人信条: 想要变成得到,中间还有做到! 🤘 本文核心 :vue的业务处理思路。前台数据渲染与后台的增删改查操作 【前言】 当大家会点开这一篇文章,大家可能会对vue全家桶与vue基础知识有了一个整体的认识。比如我要实

    2024年02月03日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包