5.(人脸签到)疫情下的在线办公签到系统-进阶篇


签到业务流程说明

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

一、获取定位地址

 uni.showLoading({
 title: '签到中请稍后'
 });
 setTimeout(function() {
 uni.hideLoading();
 }, 30000);
 //获取地理定位
 uni.getLocation({
 type: 'wgs84',
 success: function(resp) {
 let latitude = resp.latitude;
 let longitude = resp.longitude;
 }
})
var QQMapWX = require('../../lib/qqmap-wx-jssdk.min.js');
var qqmapsdk;
 onLoad: function() {
 qqmapsdk = new QQMapWX({
 key: 'KSFBZ-####-####-####-37KUE-W3FLZ'
 });
 },
qqmapsdk.reverseGeocoder({
   location: {
   latitude: latitude;
   longitude: longitude
},
   success: function(resp){
   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中安装人脸识别镜像

三、运行人脸识别程序

四、实现人脸签到(持久层)

<select id="searchFaceModel" parameterType="int" resultType="String">
  SELECT face_model FROM tb_face_model
  WHERE user_id = #{userId}
</select>
<insert id="insert" resultType="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>

五、实现人脸签到(业务层)

1.判断签到用户是否存在人脸模型

emos:
    face:
         createFaceModelUrl: http://192.168.0.101/create_face_model
         checkinUrl:http://192.168.0.101/checkin 

创建表单,接受小程序提交的签到数据

@Date
@ApiModel
public class CheckinForm{
  private String address;
  private String country;
  private String province;
  private String city;
  private String district;
}
public void checkin(HashMap param){
     Date d1 = DateUtil.date();
     Date d2 = DateUtil.parse(DateUtil.today()+""+constants.attendanceTime);
     Date d2 = DateUtil.parse(DateUtil.today()+""+attendanceEndTime);
     int status = 1;
     if(d1.compareTo(d2) <= 0){
      status = 1;
}     
     if(d1.compareTo(d2)>0 && d1.compareTo(d3)<0){
      status = 2;
      
}
  int userId = (Integer)param.get("userId");
  String faceModel = faceModelDao.searchFaceModel(userId);
  if(faceMode == null){
   throw new EmosException("不存在人脸模型");
}else{
   HttpRequest request = HttpUtil.createPost(checkinUrl);
   request.form("photo", FileUtil.file(path), "targetModel", faceModel);
   HttpResponse response = request.execute();
   if(response.getStatus() != 200){
   log.error("人脸识别服务异常");
   throw new EmosException("人脸识别服务异常");
}
      String body = response.body();
      if("无法识别出人脸".equals(body) || "照片中存在多张人脸".equals(body)){
       hrow new EmosException(body);
       }else if("False".equals(body)){
throw new EmosException("签到无效,非本人签到");
}else if ("True".equals(body)) {
//TODO 这里要获取签到地区新冠疫情风险等级
 //TODO 保存签到记录
}
}

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

1.利用本地宝查询地区风险等级

在浏览器输入URL连接:
http://m.code.bendibao.com/news/yqdengji/?qu=()
URL地址要传入两个参数: 城市编码 和 区县 。
城市编码可以从 tb_city 表中查询到,其中的code字段就是城市对应的编号

<select id="searchCode" parameter="String" resultType="String">
  SELECT code FROM tb_city WHERE city = #{city}
</select>
//查询疫情风险等级
   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();
    if(result.equals("高风险")){
    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)

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

1.为什么要采用异步发送邮件?

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

2.导入Email邮件库

org.springframework.boot spring-boot-starter-mail ## 3.设置SMTP服务器信息 ## 4.实现异步发送 在主类开启@EnableAsync 声明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;
}
}
@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);
     javaMailSender.send(message);
}
}
<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>

编写发送警告邮件的代码

HashMap<String,String> map = userDao.searchNameAndDept(userId);
String name = map.get("name");
String deptName = map.get("dept_name");
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)

1.设置图片上传路径

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

emos:
    image-folder:G:/emos/image

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

@Value("${emos.image-folder}")
private String imageFolder;
@PostConstruct
public void init(){
....
new File(imageFolder).mkdirs();
}
@RequestMapping("/checkin")
@RestController
@Api("签到模块Web接口")
@Slf4j
public class CheckinController{
   @Value("${emos.image-folder}")
   private String imageFolder;
   @PostMapping("/checkin")
   public R checkin(@Valid CheckinForm form ,@ReqeusetParam("photo") MutipartFile file @RequestHeader("token") String token ){
    if(file == null){
     return R.error("没有上传文件");
}
    String fileName = file.getOriginalFilename().toLowerCase();
    String path = imageFolder+"/"+fileName;
    if(!fileName.endsWith(".jpg")){
      FileUtil.del(path);
      return R.error("必须提交JPG格式图片");
}else{
    try{
     file.transferTo(Paths.get(path));
     HashMap param = new HashMap();
     param.put("userId",userId);
     param.put("path",path);
     param.put("district",district);
     param.put("address",form.getAddress);
     param.put("country",country);
     param.put("province",province);
     checkinService.checkin(param);
     return R.ok("签到成功");
}catch(IOException e){
   log.error(e.getMessage());
   throw new EmosException("保存图片错误");
}finally{
   FileUtil.del(path);
}
}
}}

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

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

public void createFaceModel(int userId,String path){
   HttpRequest request = HttpUtil.createPost(createFaceModelUrl);
   request.form("photo",FileUtil.file(path));
   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层)

@PostMapping("/createFaceModel")
public R createFaceModel(@RequestParam("photo") MultipartFile file,@RequestHeader("token") String token){
   int userId = jwtUtil.getUserId(token);
   if(file == null){
    return R.error("没有上传文件");
}
   String fileName = file.getOriginalFilename().toLowerCase();
   String path = imageFolder+"/"+fileName;
   if(!fileNmae.endWith(".jpg")){
    return R.error("必须提交jpg格图片");
}else {
   try{
    file.transferTo(Paths.get(path));
    checkService.createFaceModel(userId,path);
    return R.ok("人脸建模成功");
}catch(IOException e){
  log.error(e.getMessage());
  throw new EmosException("保存图片错误");
}finally{
  FileUtil.del(path);
}
}

}

十一、实现人脸签到(移动端)

1.封装Ajax请求路径

 Vue.prototype.url = {
 ……
 checkin: baseUrl + "/checkin/checkin",
 createFaceModel: baseUrl + "/checkin/createFaceModel",
 validCanCheckIn: baseUrl + "/checkin/validCanCheckIn",
}

2.上传照片执行签到

uni.uploadFile({
   url: that.url.checkin,
   filePath: that.photoPath,
   name: "photo",
   formDate:{
    address:address,
    country: nation,
    province: province,
   city: city,
   district: district
},
 success.function(resp){
   if(resp.statusCode = 500 && resp.data == "不存在人脸模型"){
   uni.hideLoading();
   uni.showModal({
   title:'提示信息',
   content:'EMOS系统中不存在你的人脸识别模型,是否用当前这张照片作为人脸识别模
型?',
   success:function(res){
   if(res.confirm){
    uni.uploadFile({
     url:that.url.createFaceModel,
     filePath:that.photoPath,
     name:'photo',
     header:{
     token:uni.getStorageSync('token')
},
   success: function(resp){
     if(resp.statusCode == 500){
    uni.showToast({
    title:resp.data,
    icon:'none'
});
}
else if(resp.statusCode == 200){
  uni.showToast({
    title:'人脸建模成功',
    icon:'none'
});
}
}
});
}
}
   });
}
else if(resp.statusCode == 200){
  let data = JSON.parse(reps.data);
  let code = data.code;
  let msg = data.msg;
  if(code == 200){
  uni.hideLoading();
  uni.showToast({
  title:'签到成功',
  complete:function(){
   //跳转签到结果统计页面
}
})
}
}
 else if (resp.statusCode == 500) {
 uni.showToast({
 title: resp.data,
 icon: 'none'
 });
 } 
}
  
})

3.检查当天是否可以签到

1. onShow: function() {
2. let that = this;
3. that.ajax(that.url.validCanCheckIn, 'GET', null, function(resp) {
4. let msg = resp.data.msg;
5. if (msg != '可以考勤') {
6. setTimeout(function() {
7. uni.showToast({
8. title: msg,
9. icon: 'none'
10. });
11. }, 1000);
12. that.canCheckin = false;
13. }
14. });
15. }
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值