Эх сурвалжийг харах

企业微信 同步和统计功能

fancy 5 жил өмнө
parent
commit
3f8346ab93
15 өөрчлөгдсөн 1571 нэмэгдсэн , 52 устгасан
  1. 64 7
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/QywxAttendanceSyncQueue.java
  2. 165 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/QywxPersonStatisticQueue.java
  3. 180 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/QywxUnitStatisticQueue.java
  4. 16 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/ThisApplication.java
  5. 225 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/factory/DingdingAttendanceFactory.java
  6. 0 5
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/dingding/ActionListDDAttendanceDetail.java
  7. 207 33
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/qywx/ActionListQywxAttendanceDetail.java
  8. 6 3
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/qywx/QywxAttendanceAction.java
  9. 48 3
      o2server/x_attendance_core_entity/src/main/java/com/x/attendance/entity/AttendanceQywxDetail.java
  10. 9 0
      o2server/x_attendance_core_entity/src/main/java/com/x/attendance/entity/PersistenceProperties.java
  11. 213 0
      o2server/x_attendance_core_entity/src/main/java/com/x/attendance/entity/StatisticQywxPersonForMonth.java
  12. 214 0
      o2server/x_attendance_core_entity/src/main/java/com/x/attendance/entity/StatisticQywxUnitForDay.java
  13. 201 0
      o2server/x_attendance_core_entity/src/main/java/com/x/attendance/entity/StatisticQywxUnitForMonth.java
  14. 20 0
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/tools/DateTools.java
  15. 3 1
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/x_attendance_assemble_control.java

+ 64 - 7
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/QywxAttendanceSyncQueue.java

@@ -1,7 +1,9 @@
 package com.x.attendance.assemble.control;
 
 import com.x.attendance.assemble.control.exception.DingDingRequestException;
-import com.x.attendance.entity.*;
+import com.x.attendance.entity.AttendanceQywxDetail;
+import com.x.attendance.entity.AttendanceQywxDetail_;
+import com.x.attendance.entity.DingdingQywxSyncRecord;
 import com.x.base.core.container.EntityManagerContainer;
 import com.x.base.core.container.factory.EntityManagerContainerFactory;
 import com.x.base.core.entity.JpaObject;
@@ -14,11 +16,11 @@ import com.x.base.core.project.gson.GsonPropertyObject;
 import com.x.base.core.project.logger.Logger;
 import com.x.base.core.project.logger.LoggerFactory;
 import com.x.base.core.project.organization.Person;
+import com.x.base.core.project.organization.Unit;
 import com.x.base.core.project.queue.AbstractQueue;
+import com.x.base.core.project.tools.DateTools;
 import com.x.base.core.project.tools.ListTools;
-import com.x.base.core.project.tools.StringTools;
 import com.x.base.core.project.x_organization_assemble_control;
-import org.apache.commons.lang3.BooleanUtils;
 import org.apache.commons.lang3.StringUtils;
 
 import javax.persistence.EntityManager;
@@ -26,8 +28,10 @@ import javax.persistence.criteria.CriteriaBuilder;
 import javax.persistence.criteria.CriteriaQuery;
 import javax.persistence.criteria.Predicate;
 import javax.persistence.criteria.Root;
+import java.util.Calendar;
 import java.util.Date;
 import java.util.List;
+import java.util.Optional;
 import java.util.stream.Collectors;
 
 public class QywxAttendanceSyncQueue  extends AbstractQueue<DingdingQywxSyncRecord> {
@@ -76,8 +80,8 @@ public class QywxAttendanceSyncQueue  extends AbstractQueue<DingdingQywxSyncReco
 
                 if(ListTools.isNotEmpty(qywxUsers)){
                     QywxPost post = new QywxPost();
-                    post.setStarttime(fromDate.getTime());
-                    post.setEndtime(toDate.getTime());
+                    post.setStarttime(DateTools.toUnixTimeStamp(fromDate.getTime()));
+                    post.setEndtime(DateTools.toUnixTimeStamp(toDate.getTime()));
                     post.setUseridlist(qywxUsers);
                     post.setOpencheckindatatype(3);//全部打卡信息
                     logger.info("企业微信 post :" + post.toString());
@@ -85,7 +89,7 @@ public class QywxAttendanceSyncQueue  extends AbstractQueue<DingdingQywxSyncReco
                     logger.info("返回结果:"+result.toString());
                     if (result.errcode == 0) {
                         List<QywxResultItem> resultList = result.getCheckindata();
-                        saveQywxAttendance(resultList);
+                        saveQywxAttendance(resultList, list);
                         saveNumber += resultList.size();
                     } else {
                         //请求结果异常 结束
@@ -112,16 +116,50 @@ public class QywxAttendanceSyncQueue  extends AbstractQueue<DingdingQywxSyncReco
         }
         logger.info("结束 插入:"+saveNumber+" 条");
 
+        boolean hasNextDate = true;
+        Date statisticDate = fromDate;
+        while (hasNextDate) {
+            logger.info("发起企业微信考勤数据统计, date:"+ DateTools.format(statisticDate));
+            ThisApplication.personQywxStatisticQueue.send(statisticDate);
+            ThisApplication.unitQywxStatisticQueue.send(statisticDate);
+            if (!isSameDay(statisticDate, toDate)) {
+                statisticDate = DateTools.addDay(statisticDate, 1);
+            }else {
+                hasNextDate = false;
+            }
+        }
+        logger.info("发起数据统计程序 完成。。。。。。。。。。");
+
+    }
 
+    private boolean isSameDay(Date startDate, Date endDate) {
+        Calendar start = Calendar.getInstance();
+        start.setTime( startDate );
+        Calendar end = Calendar.getInstance();
+        end.setTime(endDate);
+        return (start.get(Calendar.YEAR) == end.get(Calendar.YEAR) && start.get(Calendar.DAY_OF_YEAR) == end.get(Calendar.DAY_OF_YEAR));
     }
 
-    private void saveQywxAttendance(List<QywxResultItem> resultList) throws Exception {
+    private void saveQywxAttendance(List<QywxResultItem> resultList, List<Person> personList) throws Exception {
         if (resultList != null && !resultList.isEmpty()) {
             try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
+                Business business = new Business(emc);
                 emc.beginTransaction(AttendanceQywxDetail.class);
                 for (int i = 0; i < resultList.size(); i++) {
                     QywxResultItem item = resultList.get(i);
                     AttendanceQywxDetail detail = QywxResultItem.copier.copy(item);
+                    if (detail.getCheckin_time() > 0) {
+                        long timestamp = DateTools.toTimestamp(detail.getCheckin_time());
+                        detail.setCheckin_time_date(new Date(timestamp));
+                    }
+                    //添加o2组织和用户
+                    Optional<Person> first = personList.stream().filter(p -> item.userid.equals(p.getQiyeweixinId())).findFirst();
+                    if (first.isPresent()) {
+                        Person person = first.get();
+                        String unit = getUnitWithPerson(person.getDistinguishedName(), business);
+                        detail.setO2Unit(unit);
+                        detail.setO2User(person.getDistinguishedName());
+                    }
                     emc.persist(detail);
                 }
                 emc.commit();
@@ -129,6 +167,25 @@ public class QywxAttendanceSyncQueue  extends AbstractQueue<DingdingQywxSyncReco
         }
     }
 
+    private String getUnitWithPerson(String person, Business business) throws Exception {
+        String result = null;
+        Integer level = 0;
+        Unit unit = null;
+        List<String> unitNames = business.organization().unit().listWithPerson( person );
+        if( ListTools.isNotEmpty( unitNames ) ) {
+            for( String unitName : unitNames ) {
+                if( StringUtils.isNotEmpty( unitName ) && !"null".equals( unitName ) ) {
+                    unit = business.organization().unit().getObject( unitName );
+                    if( level < unit.getLevel() ) {
+                        level = unit.getLevel();
+                        result = unitName;
+                    }
+                }
+            }
+        }
+        return result;
+    }
+
     private void deleteQywxAttendance(Date fromDate, Date toDate) throws Exception {
         try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
             //先删除 再同步

+ 165 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/QywxPersonStatisticQueue.java

@@ -0,0 +1,165 @@
+package com.x.attendance.assemble.control;
+
+import com.x.attendance.entity.AttendanceDingtalkDetail;
+import com.x.attendance.entity.AttendanceQywxDetail;
+import com.x.attendance.entity.StatisticQywxPersonForMonth;
+import com.x.base.core.container.EntityManagerContainer;
+import com.x.base.core.container.factory.EntityManagerContainerFactory;
+import com.x.base.core.project.Application;
+import com.x.base.core.project.logger.Logger;
+import com.x.base.core.project.logger.LoggerFactory;
+import com.x.base.core.project.organization.Person;
+import com.x.base.core.project.organization.Unit;
+import com.x.base.core.project.queue.AbstractQueue;
+import com.x.base.core.project.tools.DateTools;
+import com.x.base.core.project.tools.ListTools;
+import com.x.base.core.project.x_organization_assemble_control;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ * Created by fancyLou on 2020-04-05.
+ * Copyright © 2020 O2. All rights reserved.
+ */
+public class QywxPersonStatisticQueue extends AbstractQueue<Date> {
+    private static final Logger logger = LoggerFactory.getLogger(QywxPersonStatisticQueue.class);
+
+    @Override
+    protected void execute(Date date) throws Exception {
+        logger.info("开始执行人员企业微信考勤统计。。。time:"+DateTools.format(date));
+        try ( EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
+            Business business = new Business(emc);
+            saveStatisticPersonForMonth(business, emc, date);
+        }
+    }
+
+
+    private void saveStatisticPersonForMonth(Business business, EntityManagerContainer emc, Date date) throws Exception {
+        String dateString = DateTools.format(date, DateTools.format_yyyyMMdd);
+        String year = dateString.substring(0, 4);
+        String month = dateString.substring(5, 7);
+
+        Application app = ThisApplication.context().applications().randomWithWeight(x_organization_assemble_control.class.getName());
+        //开始分页查询人员
+        boolean hasNextPerson = true;
+        int personPageSize = 50;
+        //人员查询地址
+        String uri = "person/list/(0)/next/50";
+        while (hasNextPerson) {
+            List<Person> list = ThisApplication.context().applications()
+                    .getQuery(false, app, uri).getDataAsList(Person.class);
+            if (list != null && list.size() > 0) {
+                for (Person person : list) {
+                    List<String> ids = business.dingdingAttendanceFactory()
+                            .getQywxStatPersonForMonthIds(year, month, person.getDistinguishedName());
+                    emc.beginTransaction(StatisticQywxPersonForMonth.class);
+                    if (ids != null && ids.size() > 0) {
+                        for (String item : ids) {
+                            StatisticQywxPersonForMonth personForMonth_temp = emc.find(item, StatisticQywxPersonForMonth.class);
+                            emc.remove(personForMonth_temp);
+                        }
+                    }
+                    StatisticQywxPersonForMonth personForMonth = new StatisticQywxPersonForMonth();
+                    personForMonth.setStatisticYear(year);
+                    personForMonth.setStatisticMonth(month);
+                    personForMonth.setO2Unit(getUnitWithPerson(person.getDistinguishedName(), business));
+                    personForMonth.setO2User(person.getDistinguishedName());
+                    List<AttendanceQywxDetail> details = business.dingdingAttendanceFactory().qywxPersonForMonthList(year, month, person.getDistinguishedName());
+                    long workDayCount = 0L;
+                    long onDutyTimes = 0L;
+                    long offDutyTimes = 0L;
+                    long outsideDutyTimes = 0L;
+                    long resultNormal = 0L;
+                    long lateTimes = 0L;
+                    long leaveEarlyTimes = 0L;
+                    long absenteeismTimes = 0L;
+                    long notSignedCount = 0L;
+                    if ( details!=null && !details.isEmpty()) {
+                        workDayCount = (long) (details.size() / 2);
+                        onDutyTimes = details.stream().filter(d -> d.getCheckin_type().equals(AttendanceQywxDetail.CHECKIN_TYPE_ON)).count();
+                        offDutyTimes = details.stream().filter(d -> d.getCheckin_type().equals(AttendanceQywxDetail.CHECKIN_TYPE_OFF)).count();
+                        outsideDutyTimes = details.stream().filter(d -> d.getCheckin_type().equals(AttendanceQywxDetail.CHECKIN_TYPE_OUTSIDE)).count();
+                        resultNormal = details.stream().filter(d -> d.getException_type().equals(AttendanceQywxDetail.EXCEPTION_TYPE_NORMAL)).count();
+                        lateTimes = details.stream().filter(d ->
+                                (d.getException_type().contains(AttendanceQywxDetail.EXCEPTION_TYPE_TIME) &&
+                                        d.getCheckin_type().equals(AttendanceQywxDetail.CHECKIN_TYPE_ON))).count();
+                        leaveEarlyTimes = details.stream().filter(d ->
+                                (d.getException_type().contains(AttendanceQywxDetail.EXCEPTION_TYPE_TIME) &&
+                                        d.getCheckin_type().equals(AttendanceQywxDetail.CHECKIN_TYPE_OFF))).count();
+                        List<AttendanceQywxDetail> ondutys = details.stream().filter(d->
+                                d.getCheckin_type().equals(AttendanceQywxDetail.CHECKIN_TYPE_ON)).collect(Collectors.toList());
+                        List<AttendanceQywxDetail> offdutys = details.stream().filter(d->
+                                d.getCheckin_type().equals(AttendanceQywxDetail.CHECKIN_TYPE_OFF)).collect(Collectors.toList());
+                        for (int i = 0; i < ondutys.size(); i++) {
+                            AttendanceQywxDetail detail = ondutys.get(i);
+                            if (detail.getException_type().contains(AttendanceQywxDetail.EXCEPTION_TYPE_NOSIGN)) {
+                                notSignedCount++;
+                                String checkDate = DateTools.format(detail.getCheckin_time_date(), DateTools.format_yyyyMMdd);
+                                Optional<AttendanceQywxDetail> op = offdutys.stream().filter(d->
+                                        checkDate.equals(DateTools.format(d.getCheckin_time_date(), DateTools.format_yyyyMMdd))).findFirst();
+                                if (op.isPresent()) {
+                                    AttendanceQywxDetail detail1 = op.get();
+                                    if(detail1.getException_type().contains(AttendanceQywxDetail.EXCEPTION_TYPE_NOSIGN)) {
+                                        notSignedCount++;
+                                        absenteeismTimes++;
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    personForMonth.setOnDutyTimes(onDutyTimes);
+                    personForMonth.setOffDutyTimes(offDutyTimes);
+                    personForMonth.setOutsideDutyTimes(outsideDutyTimes);
+                    personForMonth.setWorkDayCount(workDayCount);
+                    personForMonth.setResultNormal(resultNormal);
+                    personForMonth.setLateTimes(lateTimes);
+                    personForMonth.setLeaveEarlyTimes(leaveEarlyTimes);
+                    personForMonth.setAbsenteeismTimes(absenteeismTimes);
+                    personForMonth.setNotSignedCount(notSignedCount);
+                    emc.persist(personForMonth);
+                    emc.commit();
+                }
+
+                //是否还有更多用户
+                if (list.size() < personPageSize) {
+                    logger.info("统计企业微信考勤个人数据 结束。。。。。。。。。。。。。。。");
+                    hasNextPerson = false;
+                } else {
+                    //还有更多用户继续查询
+                    uri = "person/list/" + list.get(list.size() - 1).getDistinguishedName() + "/next/50";
+                }
+            }else {
+                //没有用户查询到结束
+                logger.info("统计企业微信考勤个人数据 结束。。。。。。。。。。。。。。。");
+                hasNextPerson = false;
+            }
+        }
+    }
+
+    private String getUnitWithPerson(String person, Business business) throws Exception {
+        String result = null;
+        Integer level = 0;
+        Unit unit = null;
+        List<String> unitNames = business.organization().unit().listWithPerson( person );
+        if( ListTools.isNotEmpty( unitNames ) ) {
+            for( String unitName : unitNames ) {
+                if( StringUtils.isNotEmpty( unitName ) && !"null".equals( unitName ) ) {
+                    unit = business.organization().unit().getObject( unitName );
+                    if( level < unit.getLevel() ) {
+                        level = unit.getLevel();
+                        result = unitName;
+                    }
+                }
+            }
+        }
+        return result;
+    }
+
+
+
+
+}

+ 180 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/QywxUnitStatisticQueue.java

@@ -0,0 +1,180 @@
+package com.x.attendance.assemble.control;
+
+import com.x.attendance.entity.*;
+import com.x.base.core.container.EntityManagerContainer;
+import com.x.base.core.container.factory.EntityManagerContainerFactory;
+import com.x.base.core.project.logger.Logger;
+import com.x.base.core.project.logger.LoggerFactory;
+import com.x.base.core.project.queue.AbstractQueue;
+import com.x.base.core.project.tools.DateTools;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ * Created by fancyLou on 2020-04-05.
+ * Copyright © 2020 O2. All rights reserved.
+ */
+public class QywxUnitStatisticQueue extends AbstractQueue<Date> {
+    private static final Logger logger = LoggerFactory.getLogger(QywxUnitStatisticQueue.class);
+
+    @Override
+    protected void execute(Date date) throws Exception {
+        logger.info("开始执行组织企业微信考勤统计。。。time:"+DateTools.format(date));
+        try ( EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
+            Business business = new Business(emc);
+            saveStatisticUnitForDay(business, emc, date);
+        }
+    }
+
+
+
+    /**
+     * 单位 日统计
+     * @param business
+     * @param emc
+     * @param date
+     * @throws Exception
+     */
+    private void saveStatisticUnitForDay(Business business, EntityManagerContainer emc, Date date) throws Exception{
+        String dateString = DateTools.format(date, DateTools.format_yyyyMMdd);
+        String year = dateString.substring(0, 4);
+        String month = dateString.substring(5, 7);
+        String day = dateString.substring(8, 10);
+        List<String> units = business.dingdingAttendanceFactory().qywxUnitDistinct(dateString);
+        if (units != null && !units.isEmpty()) {
+            for (String unit : units) {
+                if (StringUtils.isEmpty(unit)){
+                    continue;
+                }
+                List<String> ids = business.dingdingAttendanceFactory().getQywxStatUnitForDayIds(year, month, day, unit);
+                emc.beginTransaction(StatisticQywxUnitForDay.class);
+                if (ids != null && ids.size() > 0) {
+                    for (String item : ids) {
+                        StatisticQywxUnitForDay statisticTopUnitForDay_tmp = emc.find(item, StatisticQywxUnitForDay.class);
+                        emc.remove(statisticTopUnitForDay_tmp);
+                    }
+                }
+                //for day
+                StatisticQywxUnitForDay unitForDay = new StatisticQywxUnitForDay();
+                unitForDay.setO2Unit(unit);
+                unitForDay.setStatisticYear(year);
+                unitForDay.setStatisticMonth(month);
+                unitForDay.setStatisticDate(day);
+                List<AttendanceQywxDetail> details = business.dingdingAttendanceFactory().qywxUnitForDayList(year, month, day, unit);
+                long workDayCount = 0L;
+                long onDutyTimes = 0L;
+                long offDutyTimes = 0L;
+                long outsideDutyTimes = 0L;
+                long resultNormal = 0L;
+                long lateTimes = 0L;
+                long leaveEarlyTimes = 0L;
+                long absenteeismTimes = 0L;
+                long notSignedCount = 0L;
+                if ( details!=null && !details.isEmpty()) {
+                    workDayCount = (long) (details.size() / 2);
+                    onDutyTimes = details.stream().filter(d -> d.getCheckin_type().equals(AttendanceQywxDetail.CHECKIN_TYPE_ON)).count();
+                    offDutyTimes = details.stream().filter(d -> d.getCheckin_type().equals(AttendanceQywxDetail.CHECKIN_TYPE_OFF)).count();
+                    outsideDutyTimes = details.stream().filter(d -> d.getCheckin_type().equals(AttendanceQywxDetail.CHECKIN_TYPE_OUTSIDE)).count();
+                    resultNormal = details.stream().filter(d -> d.getException_type().equals(AttendanceQywxDetail.EXCEPTION_TYPE_NORMAL)).count();
+                    lateTimes = details.stream().filter(d ->
+                            (d.getException_type().contains(AttendanceQywxDetail.EXCEPTION_TYPE_TIME) &&
+                                    d.getCheckin_type().equals(AttendanceQywxDetail.CHECKIN_TYPE_ON))).count();
+                    leaveEarlyTimes = details.stream().filter(d ->
+                            (d.getException_type().contains(AttendanceQywxDetail.EXCEPTION_TYPE_TIME) &&
+                                    d.getCheckin_type().equals(AttendanceQywxDetail.CHECKIN_TYPE_OFF))).count();
+                    List<AttendanceQywxDetail> ondutys = details.stream().filter(d->
+                            d.getCheckin_type().equals(AttendanceQywxDetail.CHECKIN_TYPE_ON)).collect(Collectors.toList());
+                    List<AttendanceQywxDetail> offdutys = details.stream().filter(d->
+                            d.getCheckin_type().equals(AttendanceQywxDetail.CHECKIN_TYPE_OFF)).collect(Collectors.toList());
+                    for (int i = 0; i < ondutys.size(); i++) {
+                        AttendanceQywxDetail detail = ondutys.get(i);
+                        if (detail.getException_type().contains(AttendanceQywxDetail.EXCEPTION_TYPE_NOSIGN)) {
+                            notSignedCount++;
+                            String checkDate = DateTools.format(detail.getCheckin_time_date(), DateTools.format_yyyyMMdd);
+                            Optional<AttendanceQywxDetail> op = offdutys.stream().filter(d->
+                                    checkDate.equals(DateTools.format(d.getCheckin_time_date(), DateTools.format_yyyyMMdd))).findFirst();
+                            if (op.isPresent()) {
+                                AttendanceQywxDetail detail1 = op.get();
+                                if(detail1.getException_type().contains(AttendanceQywxDetail.EXCEPTION_TYPE_NOSIGN)) {
+                                    notSignedCount++;
+                                    absenteeismTimes++;
+                                }
+                            }
+                        }
+                    }
+                }
+                unitForDay.setOnDutyTimes(onDutyTimes);
+                unitForDay.setOffDutyTimes(offDutyTimes);
+                unitForDay.setOutsideDutyTimes(outsideDutyTimes);
+                unitForDay.setWorkDayCount(workDayCount);
+                unitForDay.setResultNormal(resultNormal);
+                unitForDay.setLateTimes(lateTimes);
+                unitForDay.setLeaveEarlyTimes(leaveEarlyTimes);
+                unitForDay.setAbsenteeismTimes(absenteeismTimes);
+                unitForDay.setNotSignedCount(notSignedCount);
+                emc.persist(unitForDay);
+                emc.commit();
+            }
+            saveStatisticUnitForMonth(business, emc, units, year, month);
+        }
+
+
+    }
+
+    /**
+     * 单位月统计
+     * @param business
+     * @param emc
+     * @param units
+     * @param year
+     * @param month
+     * @throws Exception
+     */
+    private void saveStatisticUnitForMonth(Business business, EntityManagerContainer emc, List<String> units,
+                                           String year, String month) throws Exception {
+
+        for (String unit : units) {
+            if (StringUtils.isEmpty(unit)){
+                continue;
+            }
+            Long workDay = business.dingdingAttendanceFactory().sumQywxWorkDayUnitForDayWithMonth(year, month, unit);
+            Long onduty = business.dingdingAttendanceFactory().sumQywxOndutyUnitForDayWithMonth(year, month, unit);
+            Long offDuty = business.dingdingAttendanceFactory().sumQywxOffDutyUnitForDayWithMonth(year, month, unit);
+            Long outside = business.dingdingAttendanceFactory().sumQywxOutsideUnitForDayWithMonth(year, month, unit);
+            Long normal = business.dingdingAttendanceFactory().sumQywxResultNormalUnitForDayWithMonth(year, month, unit);
+            Long late = business.dingdingAttendanceFactory().sumQywxLatetimeUnitForDayWithMonth(year, month, unit);
+            Long leaveearly = business.dingdingAttendanceFactory().sumQywxLeaveEarlyUnitForDayWithMonth(year, month, unit);
+            Long notSign = business.dingdingAttendanceFactory().sumQywxNotSignUnitForDayWithMonth(year, month, unit);
+            Long absenteeism = business.dingdingAttendanceFactory().sumQywxAbsenteeismUnitForDayWithMonth(year, month, unit);
+
+            List<String> list = business.dingdingAttendanceFactory().getQywxStatUnitForMonthIds(year, month, unit);
+            emc.beginTransaction(StatisticQywxUnitForMonth.class);
+            if (list != null && list.size() > 0) {
+                for (String item : list) {
+                    StatisticQywxUnitForMonth statisticTopUnitForMonth_tmp = emc.find(item, StatisticQywxUnitForMonth.class);
+                    emc.remove(statisticTopUnitForMonth_tmp);
+                }
+            }
+            StatisticQywxUnitForMonth unitForMonth = new StatisticQywxUnitForMonth();
+            unitForMonth.setO2Unit(unit);
+            unitForMonth.setStatisticYear(year);
+            unitForMonth.setStatisticMonth(month);
+            unitForMonth.setWorkDayCount(workDay);
+            unitForMonth.setOnDutyTimes(onduty);
+            unitForMonth.setOffDutyTimes(offDuty);
+            unitForMonth.setResultNormal(normal);
+            unitForMonth.setLateTimes(late);
+            unitForMonth.setLeaveEarlyTimes(leaveearly);
+            unitForMonth.setNotSignedCount(notSign);
+            unitForMonth.setAbsenteeismTimes(absenteeism);
+            unitForMonth.setOutsideDutyTimes(outside);
+            emc.persist(unitForMonth);
+            emc.commit();
+        }
+
+    }
+}

+ 16 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/ThisApplication.java

@@ -18,9 +18,13 @@ public class ThisApplication {
 
 	public static DingdingAttendanceQueue dingdingQueue = new DingdingAttendanceQueue();
 	public static QywxAttendanceSyncQueue qywxQueue = new QywxAttendanceSyncQueue();
+	public static QywxUnitStatisticQueue unitQywxStatisticQueue = new QywxUnitStatisticQueue();
+	public static QywxPersonStatisticQueue personQywxStatisticQueue = new QywxPersonStatisticQueue();
+
 	public static DingdingPersonStatisticQueue personStatisticQueue = new DingdingPersonStatisticQueue();
 	public static DingdingUnitStatisticQueue unitStatisticQueue = new DingdingUnitStatisticQueue();
 
+
 	public static void init() throws Exception {
 		try {
 			new AttendanceSettingService().initAllSystemConfig();
@@ -37,6 +41,8 @@ public class ThisApplication {
 			}
 			if (BooleanUtils.isTrue(Config.qiyeweixin().getAttendanceSyncEnable())) {
 				qywxQueue.start();
+				unitQywxStatisticQueue.start();
+				personQywxStatisticQueue.start();
 				context.schedule(QywxAttendanceSyncScheduleTask.class, "0 0 1 * * ?");
 			}
 		} catch (Exception e) {
@@ -75,5 +81,15 @@ public class ThisApplication {
 		} catch (Exception e) {
 			e.printStackTrace();
 		}
+		try {
+			unitQywxStatisticQueue.stop();
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		try {
+			personQywxStatisticQueue.stop();
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
 	}
 }

+ 225 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/factory/DingdingAttendanceFactory.java

@@ -464,8 +464,233 @@ public class DingdingAttendanceFactory extends AbstractFactory {
         return em.createQuery(query).getSingleResult();
     }
 
+    /*********************企业微信统计 ******************************/
 
+    /**
+     * 个人 月份 所有数据
+     * @param year
+     * @param month
+     * @param person
+     * @return
+     * @throws Exception
+     */
+    public List<AttendanceQywxDetail> qywxPersonForMonthList(String year, String month, String person)  throws Exception {
+        Date start = monthFirstDay(year, month);
+        Date end = monthLastDay(year, month);
+        EntityManager em = this.entityManagerContainer().get(AttendanceQywxDetail.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<AttendanceQywxDetail> query = cb.createQuery(AttendanceQywxDetail.class);
+        Root<AttendanceQywxDetail> root = query.from(AttendanceQywxDetail.class);
+        Predicate p = cb.between(root.get(AttendanceQywxDetail_.checkin_time), start.getTime(), end.getTime());
+        p = cb.and(p, cb.equal(root.get(AttendanceQywxDetail_.o2User), person));
+        query.select(root).where(p);
+        return em.createQuery(query).getResultList();
+    }
+    /**
+     * StatisticDingdingPersonForMonth ids
+     * @param year
+     * @param month
+     * @param person
+     * @return
+     * @throws Exception
+     */
+    public List<String> getQywxStatPersonForMonthIds(String year, String month, String person) throws Exception {
+        EntityManager em = this.entityManagerContainer().get(StatisticQywxPersonForMonth.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<String> query = cb.createQuery(String.class);
+        Root<StatisticQywxPersonForMonth> root = query.from(StatisticQywxPersonForMonth.class);
+        Predicate p = cb.equal(root.get(StatisticQywxPersonForMonth_.statisticYear), year);
+        p = cb.and(p, cb.equal(root.get(StatisticQywxPersonForMonth_.statisticMonth), month));
+        p = cb.and(p, cb.equal(root.get(StatisticQywxPersonForMonth_.o2User), person));
+        query.select(root.get(StatisticQywxPersonForMonth_.id)).where(p);
+        return em.createQuery(query).getResultList();
+    }
+
+
+    /**
+     * 单位 日期 所有数据
+     * @param year
+     * @param month
+     * @param day
+     * @param unit
+     * @return
+     * @throws Exception
+     */
+    public List<AttendanceQywxDetail> qywxUnitForDayList(String year, String month, String day,  String unit)  throws Exception {
+        Date startTime = DateTools.parse(year+"-"+month+"-"+day);
+        startTime = startOneDate(startTime);
+        Date endTime = endOneDate(startTime);
+        EntityManager em = this.entityManagerContainer().get(AttendanceQywxDetail.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<AttendanceQywxDetail> query = cb.createQuery(AttendanceQywxDetail.class);
+        Root<AttendanceQywxDetail> root = query.from(AttendanceQywxDetail.class);
+        Predicate p = cb.between(root.get(AttendanceQywxDetail_.checkin_time), startTime.getTime(), endTime.getTime());
+        p = cb.and(p, cb.equal(root.get(AttendanceQywxDetail_.o2Unit), unit));
+        query.select(root).where(p);
+        return em.createQuery(query).getResultList();
+    }
+
+    /**
+     * 查询所有有数据的组织 去重的
+     * @param date
+     * @return
+     * @throws Exception
+     */
+    public List<String> qywxUnitDistinct(String date) throws Exception {
+        Date startTime = DateTools.parse(date);
+        startTime = startOneDate(startTime);
+        Date endTime = endOneDate(startTime);
+        EntityManager em = this.entityManagerContainer().get(AttendanceQywxDetail.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<String> query = cb.createQuery(String.class);
+        Root<AttendanceQywxDetail> root = query.from(AttendanceQywxDetail.class);
+        Predicate p = cb.between(root.get(AttendanceQywxDetail_.checkin_time), startTime.getTime(), endTime.getTime());
+        query.select(root.get(AttendanceQywxDetail_.o2Unit)).where(p).distinct(true);
+        return em.createQuery(query).getResultList();
+    }
 
+    /**
+     * 查询单位 日期 统计数据的id 删除用的
+     * @param year
+     * @param month
+     * @param day
+     * @param unit
+     * @return
+     * @throws Exception
+     */
+    public List<String> getQywxStatUnitForDayIds(String year, String month, String day, String unit) throws Exception {
+        EntityManager em = this.entityManagerContainer().get(StatisticQywxUnitForDay.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<String> query = cb.createQuery(String.class);
+        Root<StatisticQywxUnitForDay> root = query.from(StatisticQywxUnitForDay.class);
+        Predicate p = cb.equal(root.get(StatisticQywxUnitForDay_.statisticYear), year);
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.statisticMonth), month));
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.statisticDate), day));
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.o2Unit), unit));
+        query.select(root.get(StatisticQywxUnitForDay_.id)).where(p);
+        return em.createQuery(query).getResultList();
+    }
+
+    /**
+     * 查询单位 月份统计数据的id 删除用的
+     * @param year
+     * @param month
+     * @param unit
+     * @return
+     * @throws Exception
+     */
+    public List<String> getQywxStatUnitForMonthIds(String year, String month, String unit) throws Exception {
+        EntityManager em = this.entityManagerContainer().get(StatisticQywxUnitForMonth.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<String> query = cb.createQuery(String.class);
+        Root<StatisticQywxUnitForMonth> root = query.from(StatisticQywxUnitForMonth.class);
+        Predicate p = cb.equal(root.get(StatisticQywxUnitForMonth_.statisticYear), year);
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForMonth_.statisticMonth), month));
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForMonth_.o2Unit), unit));
+        query.select(root.get(StatisticQywxUnitForMonth_.id)).where(p);
+        return em.createQuery(query).getResultList();
+    }
+
+    public Long sumQywxWorkDayUnitForDayWithMonth(String year, String month, String unit) throws Exception {
+        EntityManager em = this.entityManagerContainer().get(StatisticQywxUnitForDay.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<Long> query = cb.createQuery(Long.class);
+        Root<StatisticQywxUnitForDay> root = query.from(StatisticQywxUnitForDay.class);
+        Predicate p = cb.equal(root.get(StatisticQywxUnitForDay_.statisticYear), year);
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.statisticMonth), month));
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.o2Unit), unit));
+        query.select(cb.sum(root.get(StatisticQywxUnitForDay_.workDayCount))).where(p);
+        return em.createQuery(query).getSingleResult();
+    }
+
+    public Long sumQywxOndutyUnitForDayWithMonth(String year, String month, String unit) throws Exception {
+        EntityManager em = this.entityManagerContainer().get(StatisticQywxUnitForDay.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<Long> query = cb.createQuery(Long.class);
+        Root<StatisticQywxUnitForDay> root = query.from(StatisticQywxUnitForDay.class);
+        Predicate p = cb.equal(root.get(StatisticQywxUnitForDay_.statisticYear), year);
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.statisticMonth), month));
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.o2Unit), unit));
+        query.select(cb.sum(root.get(StatisticQywxUnitForDay_.onDutyTimes))).where(p);
+        return em.createQuery(query).getSingleResult();
+    }
+    public Long sumQywxOffDutyUnitForDayWithMonth(String year, String month, String unit) throws Exception {
+        EntityManager em = this.entityManagerContainer().get(StatisticQywxUnitForDay.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<Long> query = cb.createQuery(Long.class);
+        Root<StatisticQywxUnitForDay> root = query.from(StatisticQywxUnitForDay.class);
+        Predicate p = cb.equal(root.get(StatisticQywxUnitForDay_.statisticYear), year);
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.statisticMonth), month));
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.o2Unit), unit));
+        query.select(cb.sum(root.get(StatisticQywxUnitForDay_.offDutyTimes))).where(p);
+        return em.createQuery(query).getSingleResult();
+    }
+    public Long sumQywxOutsideUnitForDayWithMonth(String year, String month, String unit) throws Exception {
+        EntityManager em = this.entityManagerContainer().get(StatisticQywxUnitForDay.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<Long> query = cb.createQuery(Long.class);
+        Root<StatisticQywxUnitForDay> root = query.from(StatisticQywxUnitForDay.class);
+        Predicate p = cb.equal(root.get(StatisticQywxUnitForDay_.statisticYear), year);
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.statisticMonth), month));
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.o2Unit), unit));
+        query.select(cb.sum(root.get(StatisticQywxUnitForDay_.outsideDutyTimes))).where(p);
+        return em.createQuery(query).getSingleResult();
+    }
+    public Long sumQywxResultNormalUnitForDayWithMonth(String year, String month, String unit) throws Exception {
+        EntityManager em = this.entityManagerContainer().get(StatisticQywxUnitForDay.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<Long> query = cb.createQuery(Long.class);
+        Root<StatisticQywxUnitForDay> root = query.from(StatisticQywxUnitForDay.class);
+        Predicate p = cb.equal(root.get(StatisticQywxUnitForDay_.statisticYear), year);
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.statisticMonth), month));
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.o2Unit), unit));
+        query.select(cb.sum(root.get(StatisticQywxUnitForDay_.resultNormal))).where(p);
+        return em.createQuery(query).getSingleResult();
+    }
+    public Long sumQywxLatetimeUnitForDayWithMonth(String year, String month, String unit) throws Exception {
+        EntityManager em = this.entityManagerContainer().get(StatisticQywxUnitForDay.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<Long> query = cb.createQuery(Long.class);
+        Root<StatisticQywxUnitForDay> root = query.from(StatisticQywxUnitForDay.class);
+        Predicate p = cb.equal(root.get(StatisticQywxUnitForDay_.statisticYear), year);
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.statisticMonth), month));
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.o2Unit), unit));
+        query.select(cb.sum(root.get(StatisticQywxUnitForDay_.lateTimes))).where(p);
+        return em.createQuery(query).getSingleResult();
+    }
+    public Long sumQywxLeaveEarlyUnitForDayWithMonth(String year, String month, String unit) throws Exception {
+        EntityManager em = this.entityManagerContainer().get(StatisticQywxUnitForDay.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<Long> query = cb.createQuery(Long.class);
+        Root<StatisticQywxUnitForDay> root = query.from(StatisticQywxUnitForDay.class);
+        Predicate p = cb.equal(root.get(StatisticQywxUnitForDay_.statisticYear), year);
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.statisticMonth), month));
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.o2Unit), unit));
+        query.select(cb.sum(root.get(StatisticQywxUnitForDay_.leaveEarlyTimes))).where(p);
+        return em.createQuery(query).getSingleResult();
+    }
+    public Long sumQywxAbsenteeismUnitForDayWithMonth(String year, String month, String unit) throws Exception {
+        EntityManager em = this.entityManagerContainer().get(StatisticQywxUnitForDay.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<Long> query = cb.createQuery(Long.class);
+        Root<StatisticQywxUnitForDay> root = query.from(StatisticQywxUnitForDay.class);
+        Predicate p = cb.equal(root.get(StatisticQywxUnitForDay_.statisticYear), year);
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.statisticMonth), month));
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.o2Unit), unit));
+        query.select(cb.sum(root.get(StatisticQywxUnitForDay_.absenteeismTimes))).where(p);
+        return em.createQuery(query).getSingleResult();
+    }
+    public Long sumQywxNotSignUnitForDayWithMonth(String year, String month, String unit) throws Exception {
+        EntityManager em = this.entityManagerContainer().get(StatisticQywxUnitForDay.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<Long> query = cb.createQuery(Long.class);
+        Root<StatisticQywxUnitForDay> root = query.from(StatisticQywxUnitForDay.class);
+        Predicate p = cb.equal(root.get(StatisticQywxUnitForDay_.statisticYear), year);
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.statisticMonth), month));
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.o2Unit), unit));
+        query.select(cb.sum(root.get(StatisticQywxUnitForDay_.notSignedCount))).where(p);
+        return em.createQuery(query).getSingleResult();
+    }
 
     private Date monthLastDay(String year, String month) throws Exception {
         Calendar cal = Calendar.getInstance();

+ 0 - 5
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/dingding/ActionListDDAttendanceDetail.java

@@ -40,7 +40,6 @@ public class ActionListDDAttendanceDetail extends BaseAction {
         try ( EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
             Business business = new Business(emc);
             Wi wi = this.convertToWrapIn(jsonElement , Wi.class);
-            logger.info("传入参数:"+wi.toString());
             if (StringUtils.isEmpty(wi.getYear())) {
                 throw new TimeEmptyException();
             }
@@ -61,11 +60,8 @@ public class ActionListDDAttendanceDetail extends BaseAction {
                     endDay = getEndDay(wi.getYear(), wi.getMonth(), wi.getDay());
                 }
             }
-            logger.info("startDay:"+DateTools.format(startDay));
-            logger.info("endDay:"+DateTools.format(endDay));
             BetweenTerms betweenTerms = new BetweenTerms();
             betweenTerms.put("userCheckTime", ListTools.toList(startDay.getTime(), endDay.getTime()));
-            logger.info("between :"+betweenTerms.toString());
             String id = EMPTY_SYMBOL;
             /** 如果不是空位标志位 */
             if (!StringUtils.equals(EMPTY_SYMBOL, flag)) {
@@ -80,7 +76,6 @@ public class ActionListDDAttendanceDetail extends BaseAction {
                 if (isTimeResultEnable(wi.getTimeResult())) {
                     equals.put("timeResult", wi.getTimeResult());
                 }
-                logger.info("equals :"+equals.toString());
                 result = this.standardListNext(Wo.copier, id, count, JpaObject.sequence_FIELDNAME, equals, null,
                         null, null, null, null, null, betweenTerms, true, DESC);
             }

+ 207 - 33
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/qywx/ActionListQywxAttendanceDetail.java

@@ -2,7 +2,11 @@ package com.x.attendance.assemble.control.jaxrs.qywx;
 
 import com.google.gson.JsonElement;
 import com.x.attendance.assemble.control.Business;
+import com.x.attendance.assemble.control.jaxrs.dingding.ActionListDDAttendanceDetail;
 import com.x.attendance.assemble.control.jaxrs.dingding.BaseAction;
+import com.x.attendance.assemble.control.jaxrs.dingding.exception.SearchArgEmptyException;
+import com.x.attendance.assemble.control.jaxrs.dingding.exception.TimeEmptyException;
+import com.x.attendance.entity.AttendanceDingtalkDetail;
 import com.x.attendance.entity.AttendanceQywxDetail;
 import com.x.base.core.container.EntityManagerContainer;
 import com.x.base.core.container.factory.EntityManagerContainerFactory;
@@ -12,11 +16,18 @@ import com.x.base.core.project.bean.WrapCopier;
 import com.x.base.core.project.bean.WrapCopierFactory;
 import com.x.base.core.project.gson.GsonPropertyObject;
 import com.x.base.core.project.http.ActionResult;
+import com.x.base.core.project.jaxrs.BetweenTerms;
+import com.x.base.core.project.jaxrs.EqualsTerms;
+import com.x.base.core.project.jaxrs.InTerms;
 import com.x.base.core.project.logger.Logger;
 import com.x.base.core.project.logger.LoggerFactory;
 import com.x.base.core.project.organization.Person;
 import com.x.base.core.project.tools.DateTools;
+import com.x.base.core.project.tools.ListTools;
+import org.apache.commons.lang3.StringUtils;
 
+import java.util.ArrayList;
+import java.util.Calendar;
 import java.util.Date;
 import java.util.List;
 import java.util.stream.Collectors;
@@ -25,60 +36,126 @@ public class ActionListQywxAttendanceDetail extends BaseAction {
 
     private static final Logger logger = LoggerFactory.getLogger(ActionListQywxAttendanceDetail.class);
 
-    public ActionResult<List<Wo>> execute(JsonElement jsonElement) throws Exception {
+    public ActionResult<List<Wo>> execute(String flag, Integer count, JsonElement jsonElement) throws Exception {
         ActionResult<List<Wo>> result = new ActionResult<>();
         try ( EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
             Business business = new Business(emc);
             Wi wi = this.convertToWrapIn(jsonElement , Wi.class);
-            Date start = DateTools.parseDateTime(wi.getStartTime());
-            Date end = DateTools.parseDateTime(wi.getEndTime());
-            String qywxUser = null;
-            //转化成企业微信的id
-            if (wi.getPerson() != null && !wi.getPerson().isEmpty()) {
-                Person person = business.organization().person().getObject(wi.getPerson());
-                qywxUser = person.getQiyeweixinId();
+            if (StringUtils.isEmpty(wi.getYear())) {
+                throw new TimeEmptyException();
             }
-            List<AttendanceQywxDetail> list = business.dingdingAttendanceFactory().findQywxAttendanceDetail(start, end, qywxUser);
-            if (list != null && !list.isEmpty()) {
-                List<Wo> wos = list.stream().map(detail -> {
-                    Wo wo = new Wo();
-                    try {
-                        wo = Wo.copier.copy(detail, wo);
-                        wo.formatDateTime();
-                    }catch (Exception e) {
-                        logger.error(e);
-                    }
-                    return wo;
-                }).collect(Collectors.toList());
-                result.setData(wos);
+            if (StringUtils.isEmpty(wi.getPerson()) && StringUtils.isEmpty(wi.getUnit()) && StringUtils.isEmpty(wi.getTopUnit())) {
+                throw new SearchArgEmptyException();
             }
+            Date startDay  ;
+            Date endDay;
+            if (StringUtils.isEmpty(wi.getMonth())) {
+                startDay = getDay(wi.getYear(), "1", "1");
+                endDay = getDay(wi.getYear(), "12", "31");
+            }else {
+                if (StringUtils.isEmpty(wi.getDay())) {
+                    startDay = getDay(wi.getYear(), wi.getMonth(), "1");
+                    endDay = getMonthLastDay(wi.getYear(), wi.getMonth());
+                }else {
+                    startDay = getDay(wi.getYear(), wi.getMonth(), wi.getDay());
+                    endDay = getEndDay(wi.getYear(), wi.getMonth(), wi.getDay());
+                }
+            }
+            BetweenTerms betweenTerms = new BetweenTerms();
+            betweenTerms.put("checkin_time_date", ListTools.toList(startDay, endDay));
+            String id = EMPTY_SYMBOL;
+            /** 如果不是空位标志位 */
+            if (!StringUtils.equals(EMPTY_SYMBOL, flag)) {
+                id = flag;
+            }
+            if (StringUtils.isNotEmpty(wi.getPerson())) {
+                EqualsTerms equals = new EqualsTerms();
+                equals.put("o2User", wi.getPerson());
+                if (isCheckTypeEnable(wi.getCheckType())){
+                    equals.put("checkin_type", wi.getCheckType());
+                }
+                if (isExceptionTypeEnable(wi.getExceptionType())) {
+                    equals.put("exception_type", wi.getExceptionType());
+                }
+                result = this.standardListNext(Wo.copier, id, count, JpaObject.sequence_FIELDNAME, equals, null,
+                        null, null, null, null, null, betweenTerms, true, DESC);
+            }
+            if (StringUtils.isNotEmpty(wi.getUnit())) {
+                EqualsTerms equals = new EqualsTerms();
+                equals.put("o2Unit", wi.getUnit());
+                if (isCheckTypeEnable(wi.getCheckType())){
+                    equals.put("checkin_type", wi.getCheckType());
+                }
+                if (isExceptionTypeEnable(wi.getExceptionType())) {
+                    equals.put("exception_type", wi.getExceptionType());
+                }
+                result = this.standardListNext(Wo.copier, id, count, JpaObject.sequence_FIELDNAME, equals, null,
+                        null, null, null, null, null, betweenTerms, true, DESC);
+            }
+            if (StringUtils.isNotEmpty(wi.getTopUnit())) {
+                EqualsTerms equals = new EqualsTerms();
+                if (isCheckTypeEnable(wi.getCheckType())){
+                    equals.put("checkin_type", wi.getCheckType());
+                }
+                if (isExceptionTypeEnable(wi.getExceptionType())) {
+                    equals.put("exception_type", wi.getExceptionType());
+                }
+                InTerms ins = new InTerms();
+                List<String> subUnits = business.organization().unit().listWithUnitSubNested( wi.getTopUnit() );
+                if (subUnits == null || subUnits.isEmpty()) {
+                    subUnits = new ArrayList<>();
+                }
+                subUnits.add(wi.getTopUnit());
+                ins.put("o2Unit", subUnits);
+                result = this.standardListNext(Wo.copier, id, count, JpaObject.sequence_FIELDNAME, equals, null,
+                        null, ins, null, null, null, betweenTerms, true, DESC);
+            }
+
         }
         return result;
     }
 
     public static class Wi extends GsonPropertyObject {
-        @FieldDescribe("开始时间:yyyy-MM-dd HH:mm:ss")
-        private String startTime;
-        @FieldDescribe("结束时间:yyyy-MM-dd HH:mm:ss")
-        private String endTime;
+        @FieldDescribe("年份")
+        private String year;
+        @FieldDescribe("月份")
+        private String month;
+        @FieldDescribe("日期")
+        private String day;
         @FieldDescribe("人员")
         private String person;
+        @FieldDescribe("部门")
+        private String unit;
+        @FieldDescribe("顶级部门,会及联查询下级部门")
+        private String topUnit;
+        @FieldDescribe("打卡类型:上班打卡,下班打卡,外出打卡")
+        private String checkType;
+        @FieldDescribe("打卡结果:时间异常,地点异常,未打卡,wifi异常,非常用设备")
+        private String exceptionType;
+
 
+        public String getYear() {
+            return year;
+        }
+
+        public void setYear(String year) {
+            this.year = year;
+        }
 
-        public String getStartTime() {
-            return startTime;
+        public String getMonth() {
+            return month;
         }
 
-        public void setStartTime(String startTime) {
-            this.startTime = startTime;
+        public void setMonth(String month) {
+            this.month = month;
         }
 
-        public String getEndTime() {
-            return endTime;
+        public String getDay() {
+            return day;
         }
 
-        public void setEndTime(String endTime) {
-            this.endTime = endTime;
+        public void setDay(String day) {
+            this.day = day;
         }
 
         public String getPerson() {
@@ -88,6 +165,38 @@ public class ActionListQywxAttendanceDetail extends BaseAction {
         public void setPerson(String person) {
             this.person = person;
         }
+
+        public String getUnit() {
+            return unit;
+        }
+
+        public void setUnit(String unit) {
+            this.unit = unit;
+        }
+
+        public String getTopUnit() {
+            return topUnit;
+        }
+
+        public void setTopUnit(String topUnit) {
+            this.topUnit = topUnit;
+        }
+
+        public String getCheckType() {
+            return checkType;
+        }
+
+        public void setCheckType(String checkType) {
+            this.checkType = checkType;
+        }
+
+        public String getExceptionType() {
+            return exceptionType;
+        }
+
+        public void setExceptionType(String exceptionType) {
+            this.exceptionType = exceptionType;
+        }
     }
 
     public static class Wo extends AttendanceQywxDetail {
@@ -114,4 +223,69 @@ public class ActionListQywxAttendanceDetail extends BaseAction {
             this.checkTimeFormat = checkTimeFormat;
         }
     }
+
+
+    private boolean isCheckTypeEnable(String type) {
+        if (StringUtils.isEmpty(type) || (!AttendanceQywxDetail.CHECKIN_TYPE_OFF.equals(type) && !AttendanceQywxDetail.CHECKIN_TYPE_ON.equals(type) && !AttendanceQywxDetail.CHECKIN_TYPE_OUTSIDE.equals(type))) {
+            return false;
+        }
+        return true;
+    }
+
+    private boolean isExceptionTypeEnable(String result) {
+        if (StringUtils.isEmpty(result) ||
+                (!AttendanceQywxDetail.EXCEPTION_TYPE_NORMAL.equals(result)
+                        && !AttendanceQywxDetail.EXCEPTION_TYPE_ADDRESS.equals(result)
+                        && !AttendanceQywxDetail.EXCEPTION_TYPE_NOSIGN.equals(result)
+                        && !AttendanceQywxDetail.EXCEPTION_TYPE_TIME.equals(result)
+                        && !AttendanceQywxDetail.EXCEPTION_TYPE_UNKOWN_DEVICE.equals(result)
+                        && !AttendanceQywxDetail.EXCEPTION_TYPE_WIFI.equals(result))) {
+            return false;
+        }
+        return true;
+    }
+
+    private static Date getMonthLastDay(String year, String month) throws Exception {
+        Calendar cal = Calendar.getInstance();
+        int yearInt = Integer.parseInt(year);
+        cal.set(Calendar.YEAR, yearInt);
+        int monthInt = Integer.parseInt(month);
+        cal.set(Calendar.MONTH, monthInt);
+        cal.set(Calendar.DAY_OF_MONTH, 1);
+        cal.set(Calendar.HOUR_OF_DAY, 23);
+        cal.set(Calendar.MINUTE, 59);
+        cal.set(Calendar.SECOND, 59);
+        cal.set(Calendar.MILLISECOND, 0);
+        cal.add(Calendar.DAY_OF_MONTH, -1);
+        return cal.getTime();
+    }
+
+    private static Date getDay(String year, String month, String day) throws Exception {
+        Calendar cal = Calendar.getInstance();
+        int yearInt = Integer.parseInt(year);
+        cal.set(Calendar.YEAR, yearInt);
+        int monthInt = Integer.parseInt(month);
+        cal.set(Calendar.MONTH, monthInt-1);
+        int dayInt = Integer.parseInt(day);
+        cal.set(Calendar.DATE, dayInt);
+        cal.set(Calendar.HOUR_OF_DAY, 0);
+        cal.set(Calendar.MINUTE, 0);
+        cal.set(Calendar.SECOND, 0);
+        cal.set(Calendar.MILLISECOND, 0);
+        return cal.getTime();
+    }
+    private static Date getEndDay(String year, String month, String day) throws Exception {
+        Calendar cal = Calendar.getInstance();
+        int yearInt = Integer.parseInt(year);
+        cal.set(Calendar.YEAR, yearInt);
+        int monthInt = Integer.parseInt(month);
+        cal.set(Calendar.MONTH, monthInt-1);
+        int dayInt = Integer.parseInt(day);
+        cal.set(Calendar.DATE, dayInt);
+        cal.set(Calendar.HOUR_OF_DAY, 23);
+        cal.set(Calendar.MINUTE, 59);
+        cal.set(Calendar.SECOND, 59);
+        cal.set(Calendar.MILLISECOND, 0);
+        return cal.getTime();
+    }
 }

+ 6 - 3
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/qywx/QywxAttendanceAction.java

@@ -86,14 +86,17 @@ public class QywxAttendanceAction  extends StandardJaxrsAction {
 
     @JaxrsMethodDescribe(value = "查询企业微信打卡结果", action = ActionListQywxAttendanceDetail.class)
     @PUT
-    @Path("attendance/list")
+    @Path("attendance/list/{id}/next/{count}")
     @Produces(HttpMediaType.APPLICATION_JSON_UTF_8)
     @Consumes(MediaType.APPLICATION_JSON)
-    public void listDingdingAttendance(@Suspended final AsyncResponse asyncResponse, @Context HttpServletRequest request, JsonElement jsonElement) {
+    public void listDingdingAttendance(@Suspended final AsyncResponse asyncResponse, @Context HttpServletRequest request,
+                                       @JaxrsParameterDescribe("最后一条数据ID") @PathParam("id") String id,
+                                       @JaxrsParameterDescribe("每页显示的条目数量") @PathParam("count") Integer count,
+                                       JsonElement jsonElement) {
         ActionResult<List<ActionListQywxAttendanceDetail.Wo>> result = new ActionResult<>();
         EffectivePerson effectivePerson = this.effectivePerson(request);
         try {
-            result = new ActionListQywxAttendanceDetail().execute(jsonElement);
+            result = new ActionListQywxAttendanceDetail().execute(id, count, jsonElement);
         }catch (Exception e) {
             logger.error(e, effectivePerson, request, null);
             result.error(e);

+ 48 - 3
o2server/x_attendance_core_entity/src/main/java/com/x/attendance/entity/AttendanceQywxDetail.java

@@ -7,6 +7,7 @@ import com.x.base.core.entity.annotation.ContainerEntity;
 import com.x.base.core.project.annotation.FieldDescribe;
 
 import javax.persistence.*;
+import java.util.Date;
 
 @ContainerEntity
 @Entity
@@ -44,6 +45,14 @@ public class AttendanceQywxDetail extends SliceJpaObject  {
      */
 
 
+    @FieldDescribe("O2用户")
+    @Column(name = ColumnNamePrefix + "o2User", length = length_128B)
+    private String o2User;
+
+    @FieldDescribe("O2用户所在的组织")
+    @Column(name = ColumnNamePrefix + "o2Unit", length = length_128B)
+    private String o2Unit;
+
     @FieldDescribe("用户id")
     @Column(name = ColumnNamePrefix + "userid", length = length_96B)
     private String userid;
@@ -52,6 +61,11 @@ public class AttendanceQywxDetail extends SliceJpaObject  {
     @Column(name = ColumnNamePrefix + "groupname", length = length_128B)
     private String groupname;
 
+    //打卡类型
+    public static final String CHECKIN_TYPE_ON = "上班打卡";
+    public static final String CHECKIN_TYPE_OFF = "下班打卡";
+    public static final String CHECKIN_TYPE_OUTSIDE = "外出打卡";
+
     @FieldDescribe("打卡类型。字符串,目前有:上班打卡,下班打卡,外出打卡")
     @Column(name = ColumnNamePrefix + "checkin_type", length = length_128B)
     private String checkin_type;
@@ -60,6 +74,17 @@ public class AttendanceQywxDetail extends SliceJpaObject  {
     @Column(name = ColumnNamePrefix + "checkin_time")
     private long checkin_time;
 
+    @FieldDescribe("实际打卡时间,  用Date格式存储")
+    @Column(name = ColumnNamePrefix + "checkin_time_date")
+    private Date checkin_time_date;
+
+    //异常类型
+    public static final String EXCEPTION_TYPE_TIME = "时间异常";
+    public static final String EXCEPTION_TYPE_ADDRESS = "地点异常";
+    public static final String EXCEPTION_TYPE_WIFI = "wifi异常";
+    public static final String EXCEPTION_TYPE_UNKOWN_DEVICE = "非常用设备";
+    public static final String EXCEPTION_TYPE_NOSIGN = "未打卡";
+    public static final String EXCEPTION_TYPE_NORMAL = "正常";
     @FieldDescribe("异常类型,字符串,包括:时间异常,地点异常,未打卡,wifi异常,非常用设备。如果有多个异常,以分号间隔")
     @Column(name = ColumnNamePrefix + "exception_type", length = length_255B)
     private String exception_type;
@@ -80,10 +105,30 @@ public class AttendanceQywxDetail extends SliceJpaObject  {
     @Column(name = ColumnNamePrefix + "notes", length = length_255B)
     private String notes;
 
-//    @FieldDescribe("用户id")
-//    @Column(name = ColumnNamePrefix + "userid", length = length_96B)
-//    private String wifimac;
 
+    public String getO2User() {
+        return o2User;
+    }
+
+    public void setO2User(String o2User) {
+        this.o2User = o2User;
+    }
+
+    public String getO2Unit() {
+        return o2Unit;
+    }
+
+    public void setO2Unit(String o2Unit) {
+        this.o2Unit = o2Unit;
+    }
+
+    public Date getCheckin_time_date() {
+        return checkin_time_date;
+    }
+
+    public void setCheckin_time_date(Date checkin_time_date) {
+        this.checkin_time_date = checkin_time_date;
+    }
 
     public String getUserid() {
         return userid;

+ 9 - 0
o2server/x_attendance_core_entity/src/main/java/com/x/attendance/entity/PersistenceProperties.java

@@ -81,12 +81,21 @@ public final class PersistenceProperties extends AbstractPersistenceProperties {
 	public static class StatisticDingdingPersonForMonth {
 		public static final String table = "ATDC_STATISTIC_DD_PERSON_FORMONTH";
 	}
+	public static class StatisticQywxPersonForMonth {
+		public static final String table = "ATDC_STATISTIC_QYWX_PERSON_FORMONTH";
+	}
 	public static class StatisticDingdingUnitForMonth {
 		public static final String table = "ATDC_STATISTIC_DD_UNIT_FORMONTH";
 	}
+	public static class StatisticQywxUnitForMonth {
+		public static final String table = "ATDC_STATISTIC_QYWX_UNIT_FORMONTH";
+	}
 	public static class StatisticDingdingUnitForDay {
 		public static final String table = "ATDC_STATISTIC_DD_UNIT_FORDAY";
 	}
+	public static class StatisticQywxUnitForDay {
+		public static final String table = "ATDC_STATISTIC_QYWX_UNIT_FORDAY";
+	}
 	
 	public static class AttendanceWorkPlace {
 		public static final String table = "ATDC_WORK_PLACE";

+ 213 - 0
o2server/x_attendance_core_entity/src/main/java/com/x/attendance/entity/StatisticQywxPersonForMonth.java

@@ -0,0 +1,213 @@
+package com.x.attendance.entity;
+
+import com.x.base.core.entity.JpaObject;
+import com.x.base.core.entity.SliceJpaObject;
+import com.x.base.core.entity.annotation.CheckPersist;
+import com.x.base.core.entity.annotation.ContainerEntity;
+import com.x.base.core.project.annotation.FieldDescribe;
+
+import javax.persistence.*;
+
+@ContainerEntity
+@Entity
+@Table(name = PersistenceProperties.StatisticQywxPersonForMonth.table, uniqueConstraints = {
+		@UniqueConstraint(name = PersistenceProperties.StatisticQywxPersonForMonth.table + JpaObject.IndexNameMiddle
+				+ JpaObject.DefaultUniqueConstraintSuffix, columnNames = { JpaObject.IDCOLUMN,
+						JpaObject.CREATETIMECOLUMN, JpaObject.UPDATETIMECOLUMN, JpaObject.SEQUENCECOLUMN }) })
+@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
+public class StatisticQywxPersonForMonth extends SliceJpaObject {
+
+
+
+	private static final String TABLE = PersistenceProperties.StatisticQywxPersonForMonth.table;
+	private static final long serialVersionUID = 2006440116216111693L;
+
+	public String getId() {
+		return id;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+
+	@FieldDescribe("数据库主键,自动生成.")
+	@Id
+	@Column(length = length_id, name = ColumnNamePrefix + id_FIELDNAME)
+	private String id = createId();
+
+	public void onPersist() throws Exception {
+	}
+	/*
+	 * =============================================================================
+	 * ===== 以上为 JpaObject 默认字段
+	 * =============================================================================
+	 * =====
+	 */
+
+	/*
+	 * =============================================================================
+	 * ===== 以下为具体不同的业务及数据表字段要求
+	 * =============================================================================
+	 * =====
+	 */
+	@FieldDescribe("O2用户")
+	@Column(name = ColumnNamePrefix + "o2User", length = length_128B)
+	@CheckPersist(allowEmpty = false)
+	private String o2User;
+
+	@FieldDescribe("O2用户所在的组织")
+	@Column(name = ColumnNamePrefix + "o2Unit", length = length_128B)
+	@CheckPersist(allowEmpty = false)
+	private String o2Unit;
+
+
+	@FieldDescribe("统计年份")
+	@Column(name = "xstatisticYear", length = JpaObject.length_16B)
+	@CheckPersist(allowEmpty = false)
+	private String statisticYear;
+
+	@FieldDescribe("统计月份")
+	@Column(name = "xstatisticMonth", length = JpaObject.length_16B)
+	@CheckPersist(allowEmpty = false)
+	private String statisticMonth;
+
+	@FieldDescribe("出勤天数")
+	@Column(name = "xworkDayCount")
+	private Long workDayCount;
+
+	@FieldDescribe("上班签到次数")
+	@Column(name = "xonDutyTimes")
+	private Long onDutyTimes;
+
+	@FieldDescribe("下班签到次数")
+	@Column(name = "xoffDutyTimes")
+	private Long offDutyTimes;
+
+	@FieldDescribe("外出签到次数")
+	@Column(name = "xoutsideDutyTimes")
+	private Long outsideDutyTimes;
+
+	@FieldDescribe("正常签到次数")
+	@Column(name = "xresultNormal")
+	private Long resultNormal;
+
+	@FieldDescribe("迟到次数")
+	@Column(name = "xlateTimes")
+	private Long lateTimes;
+
+	@FieldDescribe("早退次数")
+	@Column(name = "xleaveEarlyTimes")
+	private Long leaveEarlyTimes;
+
+	@FieldDescribe("旷工次数")
+	@Column(name = "xAbsenteeismTimes")
+	private Long absenteeismTimes;
+
+	@FieldDescribe("未打卡次数")
+	@Column(name = "xNotSignedCount")
+	private Long notSignedCount;
+
+	public Long getOutsideDutyTimes() {
+		return outsideDutyTimes;
+	}
+
+	public void setOutsideDutyTimes(Long outsideDutyTimes) {
+		this.outsideDutyTimes = outsideDutyTimes;
+	}
+
+	public Long getResultNormal() {
+		return resultNormal;
+	}
+
+	public void setResultNormal(Long resultNormal) {
+		this.resultNormal = resultNormal;
+	}
+
+	public String getO2User() {
+		return o2User;
+	}
+
+	public void setO2User(String o2User) {
+		this.o2User = o2User;
+	}
+
+	public String getO2Unit() {
+		return o2Unit;
+	}
+
+	public void setO2Unit(String o2Unit) {
+		this.o2Unit = o2Unit;
+	}
+
+	public String getStatisticYear() {
+		return statisticYear;
+	}
+
+	public void setStatisticYear(String statisticYear) {
+		this.statisticYear = statisticYear;
+	}
+
+	public String getStatisticMonth() {
+		return statisticMonth;
+	}
+
+	public void setStatisticMonth(String statisticMonth) {
+		this.statisticMonth = statisticMonth;
+	}
+
+	public Long getWorkDayCount() {
+		return workDayCount;
+	}
+
+	public void setWorkDayCount(Long workDayCount) {
+		this.workDayCount = workDayCount;
+	}
+
+	public Long getOnDutyTimes() {
+		return onDutyTimes;
+	}
+
+	public void setOnDutyTimes(Long onDutyTimes) {
+		this.onDutyTimes = onDutyTimes;
+	}
+
+	public Long getOffDutyTimes() {
+		return offDutyTimes;
+	}
+
+	public void setOffDutyTimes(Long offDutyTimes) {
+		this.offDutyTimes = offDutyTimes;
+	}
+
+	public Long getLateTimes() {
+		return lateTimes;
+	}
+
+	public void setLateTimes(Long lateTimes) {
+		this.lateTimes = lateTimes;
+	}
+	
+	public Long getLeaveEarlyTimes() {
+		return leaveEarlyTimes;
+	}
+
+	public void setLeaveEarlyTimes(Long leaveEarlyTimes) {
+		this.leaveEarlyTimes = leaveEarlyTimes;
+	}
+
+	public Long getAbsenteeismTimes() {
+		return absenteeismTimes;
+	}
+
+	public void setAbsenteeismTimes(Long absenteeismTimes) {
+		this.absenteeismTimes = absenteeismTimes;
+	}
+
+	public Long getNotSignedCount() {
+		return notSignedCount;
+	}
+
+	public void setNotSignedCount(Long notSignedCount) {
+		this.notSignedCount = notSignedCount;
+	}
+}

+ 214 - 0
o2server/x_attendance_core_entity/src/main/java/com/x/attendance/entity/StatisticQywxUnitForDay.java

@@ -0,0 +1,214 @@
+package com.x.attendance.entity;
+
+import com.x.base.core.entity.JpaObject;
+import com.x.base.core.entity.SliceJpaObject;
+import com.x.base.core.entity.annotation.CheckPersist;
+import com.x.base.core.entity.annotation.ContainerEntity;
+import com.x.base.core.project.annotation.FieldDescribe;
+
+import javax.persistence.*;
+
+@ContainerEntity
+@Entity
+@Table(name = PersistenceProperties.StatisticQywxUnitForDay.table, uniqueConstraints = {
+		@UniqueConstraint(name = PersistenceProperties.StatisticQywxUnitForDay.table + JpaObject.IndexNameMiddle
+				+ JpaObject.DefaultUniqueConstraintSuffix, columnNames = { JpaObject.IDCOLUMN,
+						JpaObject.CREATETIMECOLUMN, JpaObject.UPDATETIMECOLUMN, JpaObject.SEQUENCECOLUMN }) })
+@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
+public class StatisticQywxUnitForDay extends SliceJpaObject {
+
+
+
+	private static final String TABLE = PersistenceProperties.StatisticQywxUnitForDay.table;
+	private static final long serialVersionUID = 2090817422412907325L;
+
+	public String getId() {
+		return id;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+
+	@FieldDescribe("数据库主键,自动生成.")
+	@Id
+	@Column(length = length_id, name = ColumnNamePrefix + id_FIELDNAME)
+	private String id = createId();
+
+	public void onPersist() throws Exception {
+	}
+	/*
+	 * =============================================================================
+	 * ===== 以上为 JpaObject 默认字段
+	 * =============================================================================
+	 * =====
+	 */
+
+	/*
+	 * =============================================================================
+	 * ===== 以下为具体不同的业务及数据表字段要求
+	 * =============================================================================
+	 * =====
+	 */
+
+	@FieldDescribe("O2用户所在的组织")
+	@Column(name = ColumnNamePrefix + "o2Unit", length = length_128B)
+	@CheckPersist(allowEmpty = false)
+	private String o2Unit;
+
+	@FieldDescribe("统计年份")
+	@Column(name = "xstatisticYear", length = JpaObject.length_16B)
+	@CheckPersist(allowEmpty = false)
+	private String statisticYear;
+
+	@FieldDescribe("统计月份")
+	@Column(name = "xstatisticMonth", length = JpaObject.length_16B)
+	@CheckPersist(allowEmpty = false)
+	private String statisticMonth;
+
+	@FieldDescribe("统计日期")
+	@Column(name = "xstatisticDate", length = JpaObject.length_16B)
+	@CheckPersist(allowEmpty = false)
+	private String statisticDate;
+
+	@FieldDescribe("出勤天数")
+	@Column(name = "xworkDayCount")
+	private Long workDayCount;
+
+	@FieldDescribe("上班签到次数")
+	@Column(name = "xonDutyTimes")
+	private Long onDutyTimes;
+
+	@FieldDescribe("下班签到次数")
+	@Column(name = "xoffDutyTimes")
+	private Long offDutyTimes;
+
+	@FieldDescribe("外出签到次数")
+	@Column(name = "xoutsideDutyTimes")
+	private Long outsideDutyTimes;
+
+	@FieldDescribe("正常签到次数")
+	@Column(name = "xresultNormal")
+	private Long resultNormal;
+
+	@FieldDescribe("迟到次数")
+	@Column(name = "xlateTimes")
+	private Long lateTimes;
+
+	@FieldDescribe("早退次数")
+	@Column(name = "xleaveEarlyTimes")
+	private Long leaveEarlyTimes;
+
+	@FieldDescribe("旷工次数")
+	@Column(name = "xAbsenteeismTimes")
+	private Long absenteeismTimes;
+
+	@FieldDescribe("未打卡次数")
+	@Column(name = "xNotSignedCount")
+	private Long notSignedCount;
+
+
+	public Long getResultNormal() {
+		return resultNormal;
+	}
+
+	public void setResultNormal(Long resultNormal) {
+		this.resultNormal = resultNormal;
+	}
+
+	public String getStatisticDate() {
+		return statisticDate;
+	}
+
+	public void setStatisticDate(String statisticDate) {
+		this.statisticDate = statisticDate;
+	}
+
+	public String getO2Unit() {
+		return o2Unit;
+	}
+
+	public void setO2Unit(String o2Unit) {
+		this.o2Unit = o2Unit;
+	}
+
+	public String getStatisticYear() {
+		return statisticYear;
+	}
+
+	public void setStatisticYear(String statisticYear) {
+		this.statisticYear = statisticYear;
+	}
+
+	public String getStatisticMonth() {
+		return statisticMonth;
+	}
+
+	public void setStatisticMonth(String statisticMonth) {
+		this.statisticMonth = statisticMonth;
+	}
+
+	public Long getWorkDayCount() {
+		return workDayCount;
+	}
+
+	public void setWorkDayCount(Long workDayCount) {
+		this.workDayCount = workDayCount;
+	}
+
+	public Long getOnDutyTimes() {
+		return onDutyTimes;
+	}
+
+	public void setOnDutyTimes(Long onDutyTimes) {
+		this.onDutyTimes = onDutyTimes;
+	}
+
+	public Long getOffDutyTimes() {
+		return offDutyTimes;
+	}
+
+	public void setOffDutyTimes(Long offDutyTimes) {
+		this.offDutyTimes = offDutyTimes;
+	}
+
+	public Long getLateTimes() {
+		return lateTimes;
+	}
+
+	public void setLateTimes(Long lateTimes) {
+		this.lateTimes = lateTimes;
+	}
+
+	public Long getOutsideDutyTimes() {
+		return outsideDutyTimes;
+	}
+
+	public void setOutsideDutyTimes(Long outsideDutyTimes) {
+		this.outsideDutyTimes = outsideDutyTimes;
+	}
+
+	public Long getLeaveEarlyTimes() {
+		return leaveEarlyTimes;
+	}
+
+	public void setLeaveEarlyTimes(Long leaveEarlyTimes) {
+		this.leaveEarlyTimes = leaveEarlyTimes;
+	}
+
+	public Long getAbsenteeismTimes() {
+		return absenteeismTimes;
+	}
+
+	public void setAbsenteeismTimes(Long absenteeismTimes) {
+		this.absenteeismTimes = absenteeismTimes;
+	}
+
+	public Long getNotSignedCount() {
+		return notSignedCount;
+	}
+
+	public void setNotSignedCount(Long notSignedCount) {
+		this.notSignedCount = notSignedCount;
+	}
+}

+ 201 - 0
o2server/x_attendance_core_entity/src/main/java/com/x/attendance/entity/StatisticQywxUnitForMonth.java

@@ -0,0 +1,201 @@
+package com.x.attendance.entity;
+
+import com.x.base.core.entity.JpaObject;
+import com.x.base.core.entity.SliceJpaObject;
+import com.x.base.core.entity.annotation.CheckPersist;
+import com.x.base.core.entity.annotation.ContainerEntity;
+import com.x.base.core.project.annotation.FieldDescribe;
+
+import javax.persistence.*;
+
+@ContainerEntity
+@Entity
+@Table(name = PersistenceProperties.StatisticQywxUnitForMonth.table, uniqueConstraints = {
+		@UniqueConstraint(name = PersistenceProperties.StatisticQywxUnitForMonth.table + JpaObject.IndexNameMiddle
+				+ JpaObject.DefaultUniqueConstraintSuffix, columnNames = { JpaObject.IDCOLUMN,
+						JpaObject.CREATETIMECOLUMN, JpaObject.UPDATETIMECOLUMN, JpaObject.SEQUENCECOLUMN }) })
+@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
+public class StatisticQywxUnitForMonth extends SliceJpaObject {
+
+
+
+	private static final String TABLE = PersistenceProperties.StatisticQywxUnitForMonth.table;
+	private static final long serialVersionUID = 2831416127767736230L;
+
+	public String getId() {
+		return id;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+
+	@FieldDescribe("数据库主键,自动生成.")
+	@Id
+	@Column(length = length_id, name = ColumnNamePrefix + id_FIELDNAME)
+	private String id = createId();
+
+	public void onPersist() throws Exception {
+	}
+	/*
+	 * =============================================================================
+	 * ===== 以上为 JpaObject 默认字段
+	 * =============================================================================
+	 * =====
+	 */
+
+	/*
+	 * =============================================================================
+	 * ===== 以下为具体不同的业务及数据表字段要求
+	 * =============================================================================
+	 * =====
+	 */
+
+	@FieldDescribe("O2用户所在的组织")
+	@Column(name = ColumnNamePrefix + "o2Unit", length = length_128B)
+	@CheckPersist(allowEmpty = false)
+	private String o2Unit;
+
+
+	@FieldDescribe("统计年份")
+	@Column(name = "xstatisticYear", length = JpaObject.length_16B)
+	@CheckPersist(allowEmpty = false)
+	private String statisticYear;
+
+	@FieldDescribe("统计月份")
+	@Column(name = "xstatisticMonth", length = JpaObject.length_16B)
+	@CheckPersist(allowEmpty = false)
+	private String statisticMonth;
+
+	@FieldDescribe("出勤天数")
+	@Column(name = "xworkDayCount")
+	private Long workDayCount;
+
+	@FieldDescribe("上班签到次数")
+	@Column(name = "xonDutyTimes")
+	private Long onDutyTimes;
+
+	@FieldDescribe("下班签到次数")
+	@Column(name = "xoffDutyTimes")
+	private Long offDutyTimes;
+
+	@FieldDescribe("外出签到次数")
+	@Column(name = "xoutsideDutyTimes")
+	private Long outsideDutyTimes;
+
+	@FieldDescribe("正常签到次数")
+	@Column(name = "xresultNormal")
+	private Long resultNormal;
+
+	@FieldDescribe("迟到次数")
+	@Column(name = "xlateTimes")
+	private Long lateTimes;
+
+	@FieldDescribe("早退次数")
+	@Column(name = "xleaveEarlyTimes")
+	private Long leaveEarlyTimes;
+
+	@FieldDescribe("旷工次数")
+	@Column(name = "xAbsenteeismTimes")
+	private Long absenteeismTimes;
+
+	@FieldDescribe("未打卡次数")
+	@Column(name = "xNotSignedCount")
+	private Long notSignedCount;
+
+	public Long getResultNormal() {
+		return resultNormal;
+	}
+
+	public void setResultNormal(Long resultNormal) {
+		this.resultNormal = resultNormal;
+	}
+
+	public String getO2Unit() {
+		return o2Unit;
+	}
+
+	public void setO2Unit(String o2Unit) {
+		this.o2Unit = o2Unit;
+	}
+
+	public String getStatisticYear() {
+		return statisticYear;
+	}
+
+	public void setStatisticYear(String statisticYear) {
+		this.statisticYear = statisticYear;
+	}
+
+	public String getStatisticMonth() {
+		return statisticMonth;
+	}
+
+	public void setStatisticMonth(String statisticMonth) {
+		this.statisticMonth = statisticMonth;
+	}
+
+	public Long getWorkDayCount() {
+		return workDayCount;
+	}
+
+	public void setWorkDayCount(Long workDayCount) {
+		this.workDayCount = workDayCount;
+	}
+
+	public Long getOnDutyTimes() {
+		return onDutyTimes;
+	}
+
+	public void setOnDutyTimes(Long onDutyTimes) {
+		this.onDutyTimes = onDutyTimes;
+	}
+
+	public Long getOffDutyTimes() {
+		return offDutyTimes;
+	}
+
+	public void setOffDutyTimes(Long offDutyTimes) {
+		this.offDutyTimes = offDutyTimes;
+	}
+
+	public Long getLateTimes() {
+		return lateTimes;
+	}
+
+	public void setLateTimes(Long lateTimes) {
+		this.lateTimes = lateTimes;
+	}
+
+	public Long getOutsideDutyTimes() {
+		return outsideDutyTimes;
+	}
+
+	public void setOutsideDutyTimes(Long outsideDutyTimes) {
+		this.outsideDutyTimes = outsideDutyTimes;
+	}
+
+	public Long getLeaveEarlyTimes() {
+		return leaveEarlyTimes;
+	}
+
+	public void setLeaveEarlyTimes(Long leaveEarlyTimes) {
+		this.leaveEarlyTimes = leaveEarlyTimes;
+	}
+
+	public Long getAbsenteeismTimes() {
+		return absenteeismTimes;
+	}
+
+	public void setAbsenteeismTimes(Long absenteeismTimes) {
+		this.absenteeismTimes = absenteeismTimes;
+	}
+
+	public Long getNotSignedCount() {
+		return notSignedCount;
+	}
+
+	public void setNotSignedCount(Long notSignedCount) {
+		this.notSignedCount = notSignedCount;
+	}
+}

+ 20 - 0
o2server/x_base_core_project/src/main/java/com/x/base/core/project/tools/DateTools.java

@@ -625,6 +625,26 @@ public class DateTools {
 		return date;
 	}
 
+
+	/**
+	 * 时间戳转Unix时间戳
+	 * @param timestamp
+	 * @return
+	 */
+	public static long toUnixTimeStamp(long timestamp){
+		return timestamp/1000;
+	}
+
+	/**
+	 * Unix时间戳转时间戳
+	 * @param unixTimeStamp
+	 * @return
+	 */
+	public static long toTimestamp(long unixTimeStamp){
+		return unixTimeStamp*1000;
+	}
+
+
 	public static void main(String[] args) {
 		try {
 			Date today = new Date();

+ 3 - 1
o2server/x_base_core_project/src/main/java/com/x/base/core/project/x_attendance_assemble_control.java

@@ -16,7 +16,9 @@ import com.x.base.core.project.annotation.ModuleType;
 		"com.x.attendance.entity.StatisticUnitForDay", "com.x.attendance.entity.StatisticUnitForMonth",
 		"com.x.attendance.entity.AttendanceDingtalkDetail", "com.x.attendance.entity.AttendanceQywxDetail",
 		"com.x.attendance.entity.DingdingQywxSyncRecord", "com.x.attendance.entity.StatisticDingdingPersonForMonth",
-		"com.x.attendance.entity.StatisticDingdingUnitForDay", "com.x.attendance.entity.StatisticDingdingUnitForMonth"}, storeJars = {
+		"com.x.attendance.entity.StatisticDingdingUnitForDay", "com.x.attendance.entity.StatisticDingdingUnitForMonth",
+		"com.x.attendance.entity.StatisticQywxPersonForMonth", "com.x.attendance.entity.StatisticQywxUnitForDay",
+		"com.x.attendance.entity.StatisticQywxUnitForMonth"}, storeJars = {
 				"x_attendance_core_entity", "x_organization_core_express", "x_organization_core_entity" })
 public class x_attendance_assemble_control extends Deployable {
 }