StudentStatisticsController.java 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. package com.xjrsoft.module.attendance.controller;
  2. import cn.dev33.satoken.annotation.SaCheckPermission;
  3. import cn.hutool.core.util.ObjectUtil;
  4. import cn.hutool.core.util.StrUtil;
  5. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  6. import com.baomidou.mybatisplus.core.metadata.IPage;
  7. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  8. import com.github.yulichang.wrapper.MPJLambdaWrapper;
  9. import com.xjrsoft.common.enums.ArchivesStatusEnum;
  10. import com.xjrsoft.common.model.result.RT;
  11. import com.xjrsoft.common.page.ConventPage;
  12. import com.xjrsoft.common.page.PageOutput;
  13. import com.xjrsoft.module.attendance.dto.AttendanceStatisticDto;
  14. import com.xjrsoft.module.attendance.dto.StudentDetailsDto;
  15. import com.xjrsoft.module.attendance.vo.ClassStatisticsVo;
  16. import com.xjrsoft.module.attendance.vo.StudentStatisticsPageVo;
  17. import com.xjrsoft.module.base.entity.BaseClass;
  18. import com.xjrsoft.module.base.service.IBaseClassService;
  19. import com.xjrsoft.module.holiday.entity.HolidayDate;
  20. import com.xjrsoft.module.holiday.service.IHolidayDateService;
  21. import com.xjrsoft.module.organization.entity.User;
  22. import com.xjrsoft.module.organization.service.IUserService;
  23. import com.xjrsoft.module.outint.entity.StudentOutInRecord;
  24. import com.xjrsoft.module.outint.service.IStudentOutInRecordService;
  25. import com.xjrsoft.module.outint.vo.StudentOutInRecordVo;
  26. import com.xjrsoft.module.student.entity.BaseStudentFamily;
  27. import com.xjrsoft.module.student.entity.BaseStudentSchoolRoll;
  28. import com.xjrsoft.module.student.entity.StudentLeave;
  29. import com.xjrsoft.module.student.service.IStudentLeaveService;
  30. import com.xjrsoft.module.system.entity.DictionaryDetail;
  31. import io.swagger.annotations.Api;
  32. import io.swagger.annotations.ApiOperation;
  33. import lombok.AllArgsConstructor;
  34. import org.springframework.web.bind.annotation.GetMapping;
  35. import org.springframework.web.bind.annotation.RequestMapping;
  36. import org.springframework.web.bind.annotation.RestController;
  37. import javax.validation.Valid;
  38. import java.math.BigDecimal;
  39. import java.math.RoundingMode;
  40. import java.time.DayOfWeek;
  41. import java.time.LocalDate;
  42. import java.time.LocalDateTime;
  43. import java.time.format.DateTimeFormatter;
  44. import java.time.temporal.ChronoUnit;
  45. import java.time.temporal.TemporalAdjusters;
  46. import java.util.ArrayList;
  47. import java.util.HashMap;
  48. import java.util.List;
  49. import java.util.Map;
  50. /**
  51. * @title: 考勤消息设置
  52. * @Author dzx
  53. * @Date: 2024-05-21
  54. * @Version 1.0
  55. */
  56. @RestController
  57. @RequestMapping("/studentStatistics")
  58. @Api(value = "/studentStatistics" ,tags = "学生考勤统计")
  59. @AllArgsConstructor
  60. public class StudentStatisticsController {
  61. private final IUserService xjrUserService;
  62. private final IStudentOutInRecordService studentOutInRecordService;
  63. private final IStudentLeaveService studentLeaveService;
  64. private final IBaseClassService classService;
  65. private final IHolidayDateService holidayDateService;
  66. @GetMapping(value = "/class-statistics")
  67. @ApiOperation(value="班级考勤统计")
  68. @SaCheckPermission("statistics:detail")
  69. public RT<PageOutput<ClassStatisticsVo>> classStatistics(@Valid AttendanceStatisticDto dto){
  70. Page<ClassStatisticsVo> attendancePage = classService.getAttendancePage(new Page<>(dto.getLimit(), dto.getSize()), dto);
  71. List<Long> classIds = new ArrayList<>();
  72. for (ClassStatisticsVo record : attendancePage.getRecords()) {
  73. classIds.add(record.getId());
  74. }
  75. if(dto.getDate() != null && !"".equals(dto.getDate())){
  76. DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE;
  77. LocalDate queryDate = LocalDate.parse(dto.getDate(), formatter);
  78. HolidayDate holidayDate = holidayDateService.getOne(
  79. new QueryWrapper<HolidayDate>().lambda()
  80. .eq(HolidayDate::getDate, queryDate)
  81. );
  82. if(holidayDate != null && holidayDate.getWay() != null && holidayDate.getWay() != 0){
  83. return RT.ok(ConventPage.getPageOutput(attendancePage, ClassStatisticsVo.class));
  84. }
  85. LocalDateTime startTime, endTime;
  86. if(dto.getTimePeriod() != null && dto.getTimePeriod() == 1){
  87. startTime = queryDate.atTime(5, 0, 0);
  88. endTime = queryDate.atTime(12, 0, 0);
  89. }else if(dto.getTimePeriod() != null && dto.getTimePeriod() == 2){
  90. startTime = queryDate.atTime(12, 0, 0);
  91. endTime = queryDate.atTime(18, 0, 0);
  92. }else if(dto.getTimePeriod() != null && dto.getTimePeriod() == 3){
  93. startTime = queryDate.atTime(18, 0, 0);
  94. endTime = queryDate.atTime(23, 59, 59);
  95. }else{
  96. startTime = queryDate.atTime(0, 0, 0);
  97. endTime = queryDate.atTime(23, 59, 59);
  98. }
  99. //查询每个班的走读生实到人数
  100. Map<Long, List<StudentOutInRecordVo>> notStayMap = studentOutInRecordService.getList(startTime, endTime, classIds);
  101. //查询各班的请假人数
  102. Map<Long, Integer> classLeaveCount = studentLeaveService.getClassLeaveCount(startTime, endTime);
  103. for (ClassStatisticsVo record: attendancePage.getRecords()) {
  104. Integer leaveCount = 0;
  105. if(classLeaveCount.get(record.getId()) != null){
  106. leaveCount = classLeaveCount.get(record.getId());
  107. }
  108. record.setLeaveCount(leaveCount);
  109. int actualCount = 0;
  110. for (StudentOutInRecordVo outInRecordVo : notStayMap.get(record.getId())) {
  111. if(outInRecordVo.getStatus() == 1){
  112. actualCount ++;
  113. }
  114. }
  115. record.setActualCount(actualCount);
  116. Integer lateCount = 0, playTruantCount = 0;
  117. for (StudentOutInRecordVo outInRecord : notStayMap.get(record.getId())) {
  118. if(outInRecord.getStatus() == 0){
  119. continue;
  120. }
  121. if("迟到".equals(outInRecord.getAttendanceStatus())){
  122. lateCount ++;
  123. }else if("旷课".equals(outInRecord.getAttendanceStatus())){
  124. playTruantCount ++;
  125. }
  126. }
  127. record.setPlayTruantCount(playTruantCount);
  128. record.setLateCount(lateCount);
  129. //最后通过总人数-实到人数-请假人数计算出缺勤人数
  130. record.setAbsenteeismCount(record.getStudentCount() - record.getLeaveCount() - record.getActualCount());
  131. //计算出勤率
  132. BigDecimal divide = BigDecimal.ZERO;
  133. if(record.getStudentCount() != null && record.getStudentCount() != 0){
  134. divide = BigDecimal.valueOf(record.getActualCount()).divide(BigDecimal.valueOf(record.getStudentCount()), 4, RoundingMode.HALF_UP);
  135. }
  136. record.setAttendanceRate(divide.doubleValue());
  137. }
  138. }
  139. PageOutput<ClassStatisticsVo> pageOutput = ConventPage.getPageOutput(attendancePage, ClassStatisticsVo.class);
  140. return RT.ok(pageOutput);
  141. }
  142. @GetMapping(value = "/student-details")
  143. @ApiOperation(value="学生考勤")
  144. @SaCheckPermission("statistics:detail")
  145. public RT<PageOutput<StudentStatisticsPageVo>> studentDetails(@Valid StudentDetailsDto dto){
  146. MPJLambdaWrapper<User> queryUser = new MPJLambdaWrapper<>();
  147. queryUser.disableSubLogicDel().distinct()
  148. .like(StrUtil.isNotEmpty(dto.getName()), User::getName, dto.getName())
  149. .like(StrUtil.isNotEmpty(dto.getCredentialNumber()), User::getCredentialNumber, dto.getCredentialNumber())
  150. .eq(StrUtil.isNotEmpty(dto.getStduyStatus()), BaseStudentSchoolRoll::getStduyStatus, dto.getStduyStatus())
  151. .eq(ObjectUtil.isNotNull(dto.getClassId()), BaseStudentSchoolRoll::getClassId, dto.getClassId())
  152. .eq(BaseStudentSchoolRoll::getArchivesStatus, ArchivesStatusEnum.FB2901.getCode())
  153. .selectAs(BaseClass::getName, StudentStatisticsPageVo::getClassName)
  154. .select("t3.name", StudentStatisticsPageVo::getTeacherName)
  155. .selectAs(User::getName, StudentStatisticsPageVo::getStudentName)
  156. .selectAs(User::getMobile, StudentStatisticsPageVo::getMobile)
  157. .selectAs(User::getId, StudentStatisticsPageVo::getUserId)
  158. .selectAs(BaseStudentFamily::getTelephone, StudentStatisticsPageVo::getGuardianPhone)
  159. .selectAs(DictionaryDetail::getName, StudentStatisticsPageVo::getStduyStatusCn)
  160. .selectAs(User::getCredentialNumber, StudentStatisticsPageVo::getCredentialNumber)
  161. .innerJoin(BaseStudentSchoolRoll.class, BaseStudentSchoolRoll::getUserId, User::getId)
  162. .innerJoin(BaseClass.class, BaseClass::getId, BaseStudentSchoolRoll::getClassId)
  163. .leftJoin(User.class, User::getId, BaseClass::getTeacherId)
  164. .leftJoin(BaseStudentFamily.class, BaseStudentFamily::getUserId, User::getId)
  165. .leftJoin(DictionaryDetail.class, DictionaryDetail::getCode, BaseStudentSchoolRoll::getStduyStatus);
  166. IPage<StudentStatisticsPageVo> voIPage = xjrUserService.selectJoinListPage(ConventPage.getPage(dto), StudentStatisticsPageVo.class, queryUser);
  167. if(dto.getDate() != null && !"".equals(dto.getDate())){
  168. DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE;
  169. LocalDate queryDate = LocalDate.parse(dto.getDate(), formatter);
  170. LocalDateTime startTime, endTime;
  171. if(dto.getTimePeriod() != null && dto.getTimePeriod() == 1){
  172. startTime = queryDate.atTime(5, 0, 0);
  173. endTime = queryDate.atTime(12, 0, 0);
  174. }else if(dto.getTimePeriod() != null && dto.getTimePeriod() == 2){
  175. startTime = queryDate.atTime(12, 0, 0);
  176. endTime = queryDate.atTime(18, 0, 0);
  177. }else if(dto.getTimePeriod() != null && dto.getTimePeriod() == 3){
  178. startTime = queryDate.atTime(18, 0, 0);
  179. endTime = queryDate.atTime(23, 59, 59);
  180. }else{
  181. startTime = queryDate.atTime(0, 0, 0);
  182. endTime = queryDate.atTime(23, 59, 59);
  183. }
  184. //查询当前时间段存在请假的学生
  185. Map<Long, StudentLeave> leaveList = studentLeaveService.getLeaveList(startTime, endTime);
  186. //查询进入记录
  187. List<StudentOutInRecord> outInRecords = studentOutInRecordService.list(
  188. new QueryWrapper<StudentOutInRecord>().lambda()
  189. .between(StudentOutInRecord::getRecordTime, startTime, endTime)
  190. .eq(StudentOutInRecord::getStatus, 1)
  191. );
  192. Map<Long, StudentOutInRecord> outInMap = new HashMap<>();
  193. for (StudentOutInRecord inRecord : outInRecords) {
  194. outInMap.put(inRecord.getUserId(), inRecord);
  195. }
  196. for (StudentStatisticsPageVo record : voIPage.getRecords()) {
  197. StudentLeave studentLeave = leaveList.get(record.getUserId());
  198. if(studentLeave != null){
  199. record.setStatus(studentLeave.getLeaveType());
  200. }else{
  201. StudentOutInRecord outInRecord = outInMap.get(record.getUserId());
  202. if(outInRecord != null){
  203. record.setRecordTime(outInRecord.getRecordTime());
  204. record.setStatus(outInRecord.getAttendanceStatus());
  205. }else{
  206. record.setStatus("缺勤");
  207. }
  208. }
  209. }
  210. }
  211. PageOutput<StudentStatisticsPageVo> pageOutput = ConventPage.getPageOutput(voIPage, StudentStatisticsPageVo.class);
  212. return RT.ok(pageOutput);
  213. }
  214. @GetMapping(value = "/class-history")
  215. @ApiOperation(value="历史考勤")
  216. @SaCheckPermission("statistics:detail")
  217. public RT<PageOutput<ClassStatisticsVo>> classHistory(@Valid AttendanceStatisticDto dto){
  218. Page<ClassStatisticsVo> attendancePage = classService.getAttendancePage(new Page<>(dto.getLimit(), dto.getSize()), dto);
  219. List<Long> classIds = new ArrayList<>();
  220. for (ClassStatisticsVo record : attendancePage.getRecords()) {
  221. classIds.add(record.getId());
  222. }
  223. if(dto.getStartTime() != null && !"".equals(dto.getStartTime()) && dto.getEndTime() != null && !"".equals(dto.getEndTime())){
  224. DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE;
  225. LocalDateTime startTime = LocalDate.parse(dto.getStartTime(), formatter).atTime(0, 0, 0);
  226. LocalDateTime endTime = LocalDate.parse(dto.getEndTime(), formatter).atTime(23, 59, 59);
  227. //查询每个班的走读生实到人数
  228. Map<Long, List<StudentOutInRecordVo>> notStayMap = studentOutInRecordService.getNotStayList(startTime, endTime, classIds);
  229. //查询住校生的实到情况
  230. Map<Long, List<StudentOutInRecordVo>> stayMap = studentOutInRecordService.getStayList(startTime, endTime, classIds);
  231. //查询各班的请假人数
  232. Map<Long, Integer> classLeaveCount = studentLeaveService.getClassLeaveCount(startTime, endTime);
  233. //计算2个时间相差的天数
  234. long days = ChronoUnit.DAYS.between(startTime.toLocalDate(), endTime.toLocalDate());
  235. List<String> dayOfWeeks = new ArrayList<>();
  236. for (int i = 0; i <= days; i ++){
  237. dayOfWeeks.add(startTime.plusDays(i).getDayOfWeek().name());
  238. }
  239. for (ClassStatisticsVo record: attendancePage.getRecords()) {
  240. record.setLeaveCount(classLeaveCount.get(record.getId()) == null ? 0:classLeaveCount.get(record.getId()));
  241. record.setActualCount(notStayMap.get(record.getId()).size() + stayMap.get(record.getId()).size());
  242. record.setStudentCount(record.getStudentCount() * dayOfWeeks.size() * 3);
  243. Integer lateCount = 0;
  244. for (String dayOfWeek : dayOfWeeks) {
  245. for (StudentOutInRecordVo outInRecord : notStayMap.get(record.getId())) {
  246. if("迟到".equals(outInRecord.getAttendanceStatus())){
  247. lateCount ++;
  248. }
  249. }
  250. for (StudentOutInRecordVo outInRecord : stayMap.get(record.getId())) {
  251. if("迟到".equals(outInRecord.getAttendanceStatus())){
  252. lateCount ++;
  253. }
  254. }
  255. }
  256. record.setLateCount(lateCount);
  257. //最后通过总人数-实到人数-请假人数计算出缺勤人数
  258. record.setAbsenteeismCount(record.getStudentCount() - record.getLeaveCount() - record.getActualCount());
  259. //计算出勤率
  260. BigDecimal divide = BigDecimal.ZERO;
  261. if(record.getStudentCount() != null && record.getStudentCount() != 0){
  262. divide = BigDecimal.valueOf(record.getActualCount()).divide(BigDecimal.valueOf(record.getStudentCount()), 4, RoundingMode.HALF_UP);
  263. }
  264. record.setAttendanceRate(divide.doubleValue());
  265. }
  266. }
  267. PageOutput<ClassStatisticsVo> pageOutput = ConventPage.getPageOutput(attendancePage, ClassStatisticsVo.class);
  268. return RT.ok(pageOutput);
  269. }
  270. @GetMapping(value = "/leaving-school")
  271. @ApiOperation(value="离校统计")
  272. @SaCheckPermission("statistics:detail")
  273. public RT<PageOutput<ClassStatisticsVo>> leavingSchool(@Valid AttendanceStatisticDto dto){
  274. Page<ClassStatisticsVo> attendancePage = classService.getAttendancePage(new Page<>(dto.getLimit(), dto.getSize()), dto);
  275. List<Long> classIds = new ArrayList<>();
  276. for (ClassStatisticsVo record : attendancePage.getRecords()) {
  277. classIds.add(record.getId());
  278. }
  279. if(dto.getDate() != null && !"".equals(dto.getDate())){
  280. DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE;
  281. LocalDate queryDate = LocalDate.parse(dto.getDate(), formatter);
  282. LocalDateTime startTime, endTime;
  283. if(dto.getTimePeriod() != null && dto.getTimePeriod() == 1){
  284. startTime = queryDate.atTime(5, 0, 0);
  285. endTime = queryDate.atTime(12, 0, 0);
  286. }else if(dto.getTimePeriod() != null && dto.getTimePeriod() == 2){
  287. startTime = queryDate.atTime(12, 0, 0);
  288. endTime = queryDate.atTime(18, 0, 0);
  289. }else if(dto.getTimePeriod() != null && dto.getTimePeriod() == 3){
  290. startTime = queryDate.atTime(18, 0, 0);
  291. endTime = queryDate.atTime(23, 59, 59);
  292. }else{
  293. startTime = queryDate.atTime(0, 0, 0);
  294. endTime = queryDate.atTime(23, 59, 59);
  295. }
  296. LocalDateTime lastSundayStart = startTime.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)).plusDays(-1);
  297. LocalDateTime lastSundayEnd = endTime.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)).plusDays(-1);
  298. //查询每个班的走读生实到人数
  299. Map<Long, List<StudentOutInRecordVo>> notStayMap = studentOutInRecordService.getNotStayList(startTime, endTime, classIds);
  300. //查询住校生的实到情况
  301. Map<Long, List<StudentOutInRecordVo>> stayMap = studentOutInRecordService.getStayList(lastSundayStart, lastSundayEnd, classIds);
  302. //查询各班的请假人数
  303. Map<Long, Integer> classLeaveCount = studentLeaveService.getClassLeaveCount(startTime, endTime);
  304. for (ClassStatisticsVo record: attendancePage.getRecords()) {
  305. record.setLeaveCount(classLeaveCount.get(record.getId()));
  306. record.setActualCount(notStayMap.get(record.getId()).size() + stayMap.get(record.getId()).size());
  307. Integer lateCount = 0;
  308. for (StudentOutInRecordVo outInRecord : notStayMap.get(record.getId())) {
  309. if(outInRecord.getStatus() == 1){
  310. continue;
  311. }
  312. if("迟到".equals(outInRecord.getAttendanceStatus())){
  313. lateCount ++;
  314. }
  315. }
  316. for (StudentOutInRecordVo outInRecord : stayMap.get(record.getId())) {
  317. if(outInRecord.getStatus() == 1){
  318. continue;
  319. }
  320. if("迟到".equals(outInRecord.getAttendanceStatus())){
  321. lateCount ++;
  322. }
  323. }
  324. record.setLateCount(lateCount);
  325. //最后通过总人数-实到人数-请假人数计算出缺勤人数
  326. record.setAbsenteeismCount(record.getStudentCount() - record.getLeaveCount() - record.getActualCount());
  327. //计算出勤率
  328. BigDecimal divide = BigDecimal.valueOf(record.getActualCount()).divide(BigDecimal.valueOf(record.getStudentCount()), 4, RoundingMode.HALF_UP);
  329. record.setAttendanceRate(divide.doubleValue());
  330. }
  331. }
  332. PageOutput<ClassStatisticsVo> pageOutput = ConventPage.getPageOutput(attendancePage, ClassStatisticsVo.class);
  333. return RT.ok(pageOutput);
  334. }
  335. }