|
@@ -0,0 +1,301 @@
|
|
|
+package com.yx.face.service.mq;
|
|
|
+
|
|
|
+import cn.hutool.core.bean.BeanUtil;
|
|
|
+import cn.hutool.core.collection.CollUtil;
|
|
|
+import cn.hutool.core.date.DateTime;
|
|
|
+import cn.hutool.core.date.DateUnit;
|
|
|
+import cn.hutool.core.date.DateUtil;
|
|
|
+import cn.hutool.core.util.IdcardUtil;
|
|
|
+import cn.hutool.core.util.ObjectUtil;
|
|
|
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|
|
+import com.yx.face.boot.core.Constants;
|
|
|
+import com.yx.face.boot.uitls.RedisUtil;
|
|
|
+import com.yx.face.dao.*;
|
|
|
+import com.yx.face.model.entity.*;
|
|
|
+import com.yx.face.model.enums.PassEnum;
|
|
|
+import com.yx.face.model.enums.StatAgeTypeEnum;
|
|
|
+import com.yx.face.model.enums.StatDataStatusEnum;
|
|
|
+import com.yx.face.model.mq.StatDataMq;
|
|
|
+import lombok.RequiredArgsConstructor;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
+
|
|
|
+import java.time.Duration;
|
|
|
+import java.time.LocalDate;
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.time.ZoneId;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.Date;
|
|
|
+import java.util.List;
|
|
|
+
|
|
|
+@Component
|
|
|
+@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
|
|
+@Slf4j
|
|
|
+public class StatServer {
|
|
|
+ private final StatAgeDao statAgeDao;
|
|
|
+ private final StatDataDao statDataDao;
|
|
|
+ private final StatDataItemDao statDataItemDao;
|
|
|
+ private final StatPeopleDao statPeopleDao;
|
|
|
+ private final StatSexDao statSexDao;
|
|
|
+ private final StatSourceDao statSourceDao;
|
|
|
+ private final RedisUtil redisUtil;
|
|
|
+
|
|
|
+ private static List<Integer> list;
|
|
|
+
|
|
|
+ static {
|
|
|
+ list = new ArrayList<>(10);
|
|
|
+ for (int i = 0; i < 10; i++) {
|
|
|
+ list.add(i);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @RabbitListener(queues = Constants.STAT_DATA_QUEUE_NAME)
|
|
|
+ public void statDataQueue(StatDataMq mq) {
|
|
|
+ try {
|
|
|
+ method(mq);
|
|
|
+ }catch (Exception e){
|
|
|
+ log.error("消费报错",e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void method(StatDataMq mq) {
|
|
|
+ Integer adminId = mq.getAdminId();
|
|
|
+ int lock = adminId % 10;
|
|
|
+ synchronized (list.get(lock)) {
|
|
|
+ if (DateUtil.compare(mq.getPassTime(), DateUtil.beginOfDay(new Date())) < 0) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ //TODO 处理统计数据
|
|
|
+ log.info("处理统计数据:{}", mq);
|
|
|
+
|
|
|
+ String today = DateUtil.formatDate(mq.getPassTime());
|
|
|
+ String key = Constants.STAT_ALL_NUM + adminId + "::" + today;
|
|
|
+ this.keyIncr(key);//进出人次
|
|
|
+
|
|
|
+ if (mq.getPass() == null || mq.getPass() == 0) {//通用的设备进出不统计
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (!IdcardUtil.isValidCard(mq.getIdNumber())) {//只统计有身份证的
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ PassEnum passEnum = PassEnum.getEnumByCode(mq.getPass());
|
|
|
+ if (passEnum == null) return;
|
|
|
+ switch (passEnum) {
|
|
|
+ case IN:
|
|
|
+ this.inStat(mq);
|
|
|
+ break;
|
|
|
+ case OUT:
|
|
|
+ this.outStat(mq);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void outStat(StatDataMq mq) {
|
|
|
+ LocalDateTime dateTime1 = mq.getPassTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
|
|
|
+ List<StatData> statDataList = statDataDao.selectList(
|
|
|
+ Wrappers.<StatData>lambdaQuery()
|
|
|
+ .eq(StatData::getAdminId, mq.getAdminId())
|
|
|
+ .eq(StatData::getIdNumber, mq.getIdNumber())
|
|
|
+ .eq(StatData::getStatus, StatDataStatusEnum.IN.getCode())
|
|
|
+ .orderByDesc(StatData::getId)
|
|
|
+ );
|
|
|
+ if (CollUtil.isNotEmpty(statDataList)) {
|
|
|
+ for (StatData statData : statDataList) {
|
|
|
+ StatDataItem statDataItem = statDataItemDao.selectOne(
|
|
|
+ Wrappers.<StatDataItem>lambdaQuery()
|
|
|
+ .eq(StatDataItem::getStatDataId, statData.getId())
|
|
|
+ .orderByDesc(StatDataItem::getPassTime)
|
|
|
+ .last("limit 1")
|
|
|
+ );
|
|
|
+ LocalDateTime dateTime2 = statDataItem.getPassTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
|
|
|
+ Duration duration = Duration.between(dateTime1, dateTime2);
|
|
|
+ // 获取相差的小时
|
|
|
+ long hours = Math.abs(duration.toHours());
|
|
|
+ // 获取相差的分钟
|
|
|
+ long minutes = Math.abs(duration.toMinutes() % 60);
|
|
|
+ // 获取相差的秒
|
|
|
+ long seconds = Math.abs(duration.getSeconds() % 60);
|
|
|
+ statData.setBetweenTime(String.format("%02d",hours) + ":" + String.format("%02d",minutes) + ":" + String.format("%02d",seconds));
|
|
|
+ statData.setStatus(StatDataStatusEnum.OUT.getCode());
|
|
|
+ statDataDao.updateById(statData);
|
|
|
+ StatDataItem item = BeanUtil.copyProperties(mq, StatDataItem.class);
|
|
|
+ item.setStatDataId(statData.getId());
|
|
|
+ item.setStatus(true);
|
|
|
+ statDataItemDao.insert(item);
|
|
|
+ }
|
|
|
+ }else {
|
|
|
+ StatData statData = statDataDao.selectOne(
|
|
|
+ Wrappers.<StatData>lambdaQuery()
|
|
|
+ .eq(StatData::getAdminId, mq.getAdminId())
|
|
|
+ .eq(StatData::getIdNumber, mq.getIdNumber())
|
|
|
+ .eq(StatData::getStatus, StatDataStatusEnum.OUT.getCode())
|
|
|
+ .between(StatData::getCreateTime, DateUtil.formatDateTime(DateUtil.beginOfDay(mq.getPassTime())), DateUtil.formatDateTime(DateUtil.endOfDay(mq.getPassTime())))
|
|
|
+ .orderByDesc(StatData::getId)
|
|
|
+ .last("limit 1")
|
|
|
+ );
|
|
|
+ if(ObjectUtil.isNull(statData)){
|
|
|
+ statData = BeanUtil.copyProperties(mq, StatData.class);
|
|
|
+ statData.setStatus(StatDataStatusEnum.OUT.getCode());
|
|
|
+ statDataDao.insert(statData);
|
|
|
+ StatDataItem item = BeanUtil.copyProperties(mq, StatDataItem.class);
|
|
|
+ item.setStatDataId(statData.getId());
|
|
|
+ item.setStatus(false);
|
|
|
+ statDataItemDao.insert(item);
|
|
|
+ }else {
|
|
|
+ StatDataItem item = BeanUtil.copyProperties(mq, StatDataItem.class);
|
|
|
+ item.setStatDataId(statData.getId());
|
|
|
+ item.setStatus(false);
|
|
|
+ statDataItemDao.insert(item);
|
|
|
+ StatDataItem statDataItem = new StatDataItem();
|
|
|
+ statDataItem.setStatus(false);
|
|
|
+ statDataItemDao.update(statDataItem, Wrappers.<StatDataItem>lambdaUpdate()
|
|
|
+ .eq(StatDataItem::getStatDataId, statData.getId())
|
|
|
+ .eq(StatDataItem::getPass,StatDataStatusEnum.OUT.getCode())
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private void inStat(StatDataMq mq) {
|
|
|
+ //查询当天
|
|
|
+ StatData statData = statDataDao.selectOne(
|
|
|
+ Wrappers.<StatData>lambdaQuery()
|
|
|
+ .eq(StatData::getAdminId, mq.getAdminId())
|
|
|
+ .eq(StatData::getIdNumber, mq.getIdNumber())
|
|
|
+ .eq(StatData::getStatus, StatDataStatusEnum.IN.getCode())
|
|
|
+ .between(StatData::getCreateTime, DateUtil.formatDateTime(DateUtil.beginOfDay(mq.getPassTime())), DateUtil.formatDateTime(DateUtil.endOfDay(mq.getPassTime())))
|
|
|
+ .orderByDesc(StatData::getId)
|
|
|
+ .last("limit 1")
|
|
|
+ );
|
|
|
+ if (ObjectUtil.isNull(statData)) {
|
|
|
+ statData = BeanUtil.copyProperties(mq, StatData.class);
|
|
|
+ statData.setStatus(StatDataStatusEnum.IN.getCode());
|
|
|
+ statDataDao.insert(statData);
|
|
|
+ StatDataItem item = BeanUtil.copyProperties(mq, StatDataItem.class);
|
|
|
+ item.setStatDataId(statData.getId());
|
|
|
+ item.setStatus(true);
|
|
|
+ statDataItemDao.insert(item);
|
|
|
+ //数据统计
|
|
|
+ this.stat(mq);
|
|
|
+ } else {
|
|
|
+ StatDataItem item = BeanUtil.copyProperties(mq, StatDataItem.class);
|
|
|
+ item.setStatDataId(statData.getId());
|
|
|
+ item.setStatus(false);
|
|
|
+ statDataItemDao.insert(item);
|
|
|
+ StatDataItem statDataItem = new StatDataItem();
|
|
|
+ statDataItem.setStatus(false);
|
|
|
+ statDataItemDao.update(statDataItem, Wrappers.<StatDataItem>lambdaUpdate()
|
|
|
+ .eq(StatDataItem::getStatDataId, statData.getId())
|
|
|
+ .eq(StatDataItem::getPass,StatDataStatusEnum.IN.getCode()));
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private void stat(StatDataMq mq) {
|
|
|
+ LocalDate localDate = mq.getPassTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
|
|
|
+ //1.进场人数
|
|
|
+ String key = Constants.STAT_IN_NUM + mq.getAdminId() + "::" + DateUtil.formatDate(mq.getPassTime());
|
|
|
+ this.keyIncr(key);
|
|
|
+ //2.人员年龄
|
|
|
+ int age = IdcardUtil.getAgeByIdCard(mq.getIdNumber());
|
|
|
+ int ageType = this.getAgeType(age);
|
|
|
+ StatAge statAge = statAgeDao.selectOne(
|
|
|
+ Wrappers.<StatAge>lambdaQuery()
|
|
|
+ .eq(StatAge::getAdminId, mq.getAdminId())
|
|
|
+ .eq(StatAge::getStatDate, localDate)
|
|
|
+ .eq(StatAge::getAgeType, ageType)
|
|
|
+ );
|
|
|
+ if (ObjectUtil.isNull(statAge)) {
|
|
|
+ statAge = new StatAge();
|
|
|
+ statAge.setAdminId(mq.getAdminId());
|
|
|
+ statAge.setStatDate(localDate);
|
|
|
+ statAge.setAgeType(ageType);
|
|
|
+ statAge.setStatNum(1);
|
|
|
+ statAgeDao.insert(statAge);
|
|
|
+ } else {
|
|
|
+ statAge.setStatNum(statAge.getStatNum() + 1);
|
|
|
+ statAgeDao.updateById(statAge);
|
|
|
+ }
|
|
|
+ //3.人员性别
|
|
|
+ int sexType = IdcardUtil.getGenderByIdCard(mq.getIdNumber());
|
|
|
+ StatSex statSex = statSexDao.selectOne(
|
|
|
+ Wrappers.<StatSex>lambdaQuery()
|
|
|
+ .eq(StatSex::getAdminId, mq.getAdminId())
|
|
|
+ .eq(StatSex::getStatDate, localDate)
|
|
|
+ .eq(StatSex::getSexType, sexType)
|
|
|
+ );
|
|
|
+ if (ObjectUtil.isNull(statSex)) {
|
|
|
+ statSex = new StatSex();
|
|
|
+ statSex.setAdminId(mq.getAdminId());
|
|
|
+ statSex.setStatDate(localDate);
|
|
|
+ statSex.setSexType(sexType);
|
|
|
+ statSex.setStatNum(1);
|
|
|
+ statSexDao.insert(statSex);
|
|
|
+ } else {
|
|
|
+ statSex.setStatNum(statSex.getStatNum() + 1);
|
|
|
+ statSexDao.updateById(statSex);
|
|
|
+ }
|
|
|
+ //4.人员来源地
|
|
|
+ String statSourceCode = IdcardUtil.getProvinceCodeByIdCard(mq.getIdNumber());
|
|
|
+ StatSource statSource = statSourceDao.selectOne(
|
|
|
+ Wrappers.<StatSource>lambdaQuery()
|
|
|
+ .eq(StatSource::getAdminId, mq.getAdminId())
|
|
|
+ .eq(StatSource::getStatDate, localDate)
|
|
|
+ .eq(StatSource::getStatSourceCode, statSourceCode)
|
|
|
+ );
|
|
|
+ if (ObjectUtil.isNull(statSource)) {
|
|
|
+ statSource = new StatSource();
|
|
|
+ statSource.setAdminId(mq.getAdminId());
|
|
|
+ statSource.setStatDate(localDate);
|
|
|
+ statSource.setStatSourceCode(statSourceCode+"00");//因为我们area_code表中,省级账号有四位 3300
|
|
|
+ statSource.setStatNum(1);
|
|
|
+ statSourceDao.insert(statSource);
|
|
|
+ } else {
|
|
|
+ statSource.setStatNum(statSource.getStatNum() + 1);
|
|
|
+ statSourceDao.updateById(statSource);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private int getAgeType(int age) {
|
|
|
+ if (age < 20) {
|
|
|
+ return StatAgeTypeEnum.AGE_20.getCode();
|
|
|
+ } else if (age <= 35) {
|
|
|
+ return StatAgeTypeEnum.AGE_35.getCode();
|
|
|
+ } else if (age <= 50) {
|
|
|
+ return StatAgeTypeEnum.AGE_50.getCode();
|
|
|
+ } else if (age <= 65) {
|
|
|
+ return StatAgeTypeEnum.AGE_65.getCode();
|
|
|
+ } else {
|
|
|
+ return StatAgeTypeEnum.AGE_OTHER.getCode();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void keyIncr(String key) {
|
|
|
+ long incr = redisUtil.incr(key, 1);
|
|
|
+ if (incr == 1) {
|
|
|
+ redisUtil.expire(key, 86400L * 8);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public static void main(String[] args) {
|
|
|
+ DateTime date = DateUtil.date();
|
|
|
+ DateTime dateTime = DateUtil.offsetDay(date, 1);
|
|
|
+ System.out.println(DateUtil.between(date, dateTime, DateUnit.SECOND));
|
|
|
+ LocalDateTime dateTime1 = LocalDateTime.now();
|
|
|
+ LocalDateTime dateTime2 = dateTime1.plusHours(1).plusMinutes(2).plusSeconds(5);
|
|
|
+ Duration duration = Duration.between(dateTime2, dateTime1);
|
|
|
+ // 获取相差的小时
|
|
|
+ long hours = Math.abs(duration.toHours());
|
|
|
+ // 获取相差的分钟
|
|
|
+ long minutes = Math.abs(duration.toMinutes() % 60);
|
|
|
+ // 获取相差的秒
|
|
|
+ long seconds = Math.abs(duration.getSeconds() % 60);
|
|
|
+ System.out.println(hours + ":" + minutes + ":" + String.format("%02d",seconds));
|
|
|
+ }
|
|
|
+}
|