From 4f3a1910bcc6426d827e43471b5e24b22d40f63a Mon Sep 17 00:00:00 2001 From: zhouhefeng Date: Mon, 2 Sep 2024 14:08:37 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mobile/MMeetingAuditController.java | 94 ++ .../mobile/MMeetingMineController.java | 222 +++++ .../pc/MeetingManageController.java | 158 +++ .../pc/MeetingOnlineController.java | 133 +++ .../controller/pc/MeetingTypeController.java | 148 +++ .../MeetingBasicStatisticsController.java | 126 +++ .../meeting/models/CustomAuditTypeEnum.java | 28 + .../meeting/models/CustomAuditTypeHandle.java | 83 ++ .../io/v/nutz/meeting/models/MeetingInfo.java | 90 ++ .../meeting/models/MeetingTimePeriod.java | 48 + .../meeting/models/MeetingTimePeriodUser.java | 83 ++ .../io/v/nutz/meeting/models/MeetingType.java | 38 + .../io/v/nutz/meeting/models/MeetingUser.java | 78 ++ .../meeting/service/MeetingAuditService.java | 40 + .../MeetingBasicStatisticsService.java | 29 + .../meeting/service/MeetingCommonService.java | 66 ++ .../meeting/service/MeetingManageService.java | 52 + .../meeting/service/MeetingMineService.java | 56 ++ .../meeting/service/MeetingOnlineService.java | 46 + .../meeting/service/MeetingTypeService.java | 24 + .../service/impl/MeetingAuditServiceImpl.java | 238 +++++ .../MeetingBasicStatisticsServiceImpl.java | 265 +++++ .../impl/MeetingCommonServiceImpl.java | 137 +++ .../impl/MeetingManageServiceImpl.java | 241 +++++ .../service/impl/MeetingMineServiceImpl.java | 141 +++ .../impl/MeetingOnlineServiceImpl.java | 219 +++++ .../service/impl/MeetingTypeServiceImpl.java | 103 ++ .../v/nutz/meeting/ws/MeetingWebsocket.java | 30 + .../v/nutz/meeting/ws/MeetingWsHandler.java | 58 ++ .../io/v/nutz/modules/models/AuditState.java | 203 ++++ .../QuestionAnswerRankingController.java | 103 +- .../QuestionAnswerStatisticsController.java | 156 --- .../QuestionAnswerWinnerController.java | 54 +- .../controller/QuestionCakeController.java | 17 + .../controller/QuestionH5Controller.java | 563 +++++++---- .../controller/QuestionH5CtguController.java | 273 ------ .../controller/QuestionMangeController.java | 124 ++- .../QuestionPollAnalysisController.java | 59 +- ...stionUnionIntentionalChoiceController.java | 23 +- .../io/v/nutz/question/model/Question.java | 72 +- .../model/QuestionAnswerUserScore.java | 4 +- .../question/model/QuestionRandomIssue.java | 52 + .../question/model/QuestionUserScore.java | 18 +- .../v/nutz/question/model/QuestionWinner.java | 5 + .../v/nutz/question/model/Question_issue.java | 4 +- .../v/nutz/question/model/Question_reply.java | 3 + .../service/QuestionAnswerRankingService.java | 23 + .../service/QuestionReplyService.java | 1 - .../question/service/QuestionService.java | 26 + .../QuestionAnswerRankingServiceImpl.java | 422 ++++++++ .../impl/QuestionReplyServiceImpl.java | 4 +- .../service/impl/QuestionServiceImpl.java | 186 +++- .../nutz/task/question/QuestionRaffleJob.java | 154 +++ .../mobile/icon/meeting/会议(会议审核).png | Bin 0 -> 87898 bytes .../mobile/icon/meeting/会议(我的会议).png | Bin 0 -> 69650 bytes .../platform/module/mixins/initTableMixins.js | 14 +- src/main/resources/views/mobile/home.html | 80 +- .../resources/views/mobile/meeting/audit.html | 504 ++++++++++ .../views/mobile/meeting/myMeeting.html | 532 ++++++++++ .../views/mobile/meeting/sign_in.html | 320 ++++++ .../views/mobile/question/answer.html | 924 +++++++++--------- .../views/mobile/question/answer_ctgu.html | 391 -------- .../question/ctgu_answer/answerSheet.html | 323 ------ .../mobile/question/ctgu_answer/score.html | 163 --- .../resources/views/mobile/question/list.html | 79 +- .../resources/views/mobile/question/poll.html | 15 +- .../resources/views/mobile/question/shop.html | 502 ++++++++++ .../resources/views/platform/meeting/add.html | 229 +++++ .../views/platform/meeting/basic/type.html | 422 ++++++++ .../platform/meeting/includeJs/meetingUtil.js | 40 + .../views/platform/meeting/index.html | 139 +++ .../views/platform/meeting/manage/add.html | 263 +++++ .../meeting/manage/add_time_period.html | 49 + .../platform/meeting/manage/add_users.html | 3 + .../views/platform/meeting/manage/index.html | 128 +++ .../views/platform/meeting/online.html | 377 +++++++ .../platform/meeting/statistics/basic.html | 321 ++++++ .../views/platform/question/pollAnalysis.html | 85 +- .../views/platform/question/question.html | 429 ++++---- .../views/platform/question/ranking.html | 37 +- .../views/platform/question/statistics.html | 222 ----- .../views/platform/question/winner.html | 16 +- 82 files changed, 9497 insertions(+), 2733 deletions(-) create mode 100644 src/main/java/io/v/nutz/meeting/controller/mobile/MMeetingAuditController.java create mode 100644 src/main/java/io/v/nutz/meeting/controller/mobile/MMeetingMineController.java create mode 100644 src/main/java/io/v/nutz/meeting/controller/pc/MeetingManageController.java create mode 100644 src/main/java/io/v/nutz/meeting/controller/pc/MeetingOnlineController.java create mode 100644 src/main/java/io/v/nutz/meeting/controller/pc/MeetingTypeController.java create mode 100644 src/main/java/io/v/nutz/meeting/controller/pc/statistics/MeetingBasicStatisticsController.java create mode 100644 src/main/java/io/v/nutz/meeting/models/CustomAuditTypeEnum.java create mode 100644 src/main/java/io/v/nutz/meeting/models/CustomAuditTypeHandle.java create mode 100644 src/main/java/io/v/nutz/meeting/models/MeetingInfo.java create mode 100644 src/main/java/io/v/nutz/meeting/models/MeetingTimePeriod.java create mode 100644 src/main/java/io/v/nutz/meeting/models/MeetingTimePeriodUser.java create mode 100644 src/main/java/io/v/nutz/meeting/models/MeetingType.java create mode 100644 src/main/java/io/v/nutz/meeting/models/MeetingUser.java create mode 100644 src/main/java/io/v/nutz/meeting/service/MeetingAuditService.java create mode 100644 src/main/java/io/v/nutz/meeting/service/MeetingBasicStatisticsService.java create mode 100644 src/main/java/io/v/nutz/meeting/service/MeetingCommonService.java create mode 100644 src/main/java/io/v/nutz/meeting/service/MeetingManageService.java create mode 100644 src/main/java/io/v/nutz/meeting/service/MeetingMineService.java create mode 100644 src/main/java/io/v/nutz/meeting/service/MeetingOnlineService.java create mode 100644 src/main/java/io/v/nutz/meeting/service/MeetingTypeService.java create mode 100644 src/main/java/io/v/nutz/meeting/service/impl/MeetingAuditServiceImpl.java create mode 100644 src/main/java/io/v/nutz/meeting/service/impl/MeetingBasicStatisticsServiceImpl.java create mode 100644 src/main/java/io/v/nutz/meeting/service/impl/MeetingCommonServiceImpl.java create mode 100644 src/main/java/io/v/nutz/meeting/service/impl/MeetingManageServiceImpl.java create mode 100644 src/main/java/io/v/nutz/meeting/service/impl/MeetingMineServiceImpl.java create mode 100644 src/main/java/io/v/nutz/meeting/service/impl/MeetingOnlineServiceImpl.java create mode 100644 src/main/java/io/v/nutz/meeting/service/impl/MeetingTypeServiceImpl.java create mode 100644 src/main/java/io/v/nutz/meeting/ws/MeetingWebsocket.java create mode 100644 src/main/java/io/v/nutz/meeting/ws/MeetingWsHandler.java create mode 100644 src/main/java/io/v/nutz/modules/models/AuditState.java delete mode 100644 src/main/java/io/v/nutz/question/controller/QuestionAnswerStatisticsController.java delete mode 100644 src/main/java/io/v/nutz/question/controller/QuestionH5CtguController.java create mode 100644 src/main/java/io/v/nutz/question/model/QuestionRandomIssue.java create mode 100644 src/main/java/io/v/nutz/question/service/QuestionAnswerRankingService.java create mode 100644 src/main/java/io/v/nutz/question/service/impl/QuestionAnswerRankingServiceImpl.java create mode 100644 src/main/java/io/v/nutz/task/question/QuestionRaffleJob.java create mode 100644 src/main/resources/static/assets/mobile/icon/meeting/会议(会议审核).png create mode 100644 src/main/resources/static/assets/mobile/icon/meeting/会议(我的会议).png create mode 100644 src/main/resources/views/mobile/meeting/audit.html create mode 100644 src/main/resources/views/mobile/meeting/myMeeting.html create mode 100644 src/main/resources/views/mobile/meeting/sign_in.html delete mode 100644 src/main/resources/views/mobile/question/answer_ctgu.html delete mode 100644 src/main/resources/views/mobile/question/ctgu_answer/answerSheet.html delete mode 100644 src/main/resources/views/mobile/question/ctgu_answer/score.html create mode 100644 src/main/resources/views/mobile/question/shop.html create mode 100644 src/main/resources/views/platform/meeting/add.html create mode 100644 src/main/resources/views/platform/meeting/basic/type.html create mode 100644 src/main/resources/views/platform/meeting/includeJs/meetingUtil.js create mode 100644 src/main/resources/views/platform/meeting/index.html create mode 100644 src/main/resources/views/platform/meeting/manage/add.html create mode 100644 src/main/resources/views/platform/meeting/manage/add_time_period.html create mode 100644 src/main/resources/views/platform/meeting/manage/add_users.html create mode 100644 src/main/resources/views/platform/meeting/manage/index.html create mode 100644 src/main/resources/views/platform/meeting/online.html create mode 100644 src/main/resources/views/platform/meeting/statistics/basic.html delete mode 100644 src/main/resources/views/platform/question/statistics.html diff --git a/src/main/java/io/v/nutz/meeting/controller/mobile/MMeetingAuditController.java b/src/main/java/io/v/nutz/meeting/controller/mobile/MMeetingAuditController.java new file mode 100644 index 0000000..b39b556 --- /dev/null +++ b/src/main/java/io/v/nutz/meeting/controller/mobile/MMeetingAuditController.java @@ -0,0 +1,94 @@ +package io.v.nutz.meeting.controller.mobile; + +import io.v.nutz.annontation.ViReturn; +import io.v.nutz.base.result.Result; +import io.v.nutz.meeting.service.MeetingAuditService; +import io.v.nutz.modules.models.Audit; +import io.v.nutz.modules.models.PageForm; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.shiro.authz.annotation.RequiresAuthentication; +import org.nutz.dao.Dao; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.mvc.annotation.At; +import org.nutz.mvc.annotation.Ok; +import org.nutz.mvc.annotation.Param; + +import javax.servlet.http.HttpServletRequest; + +/** + * 会议手机端审核 + */ +@IocBean +@Slf4j +@At("/mobile/meeting/audit") +public class MMeetingAuditController { + + @Inject + private MeetingAuditService meetingAuditService; + + @Inject + private Dao dao; + + /** + * 跳转审核页面 + * + * @param request + */ + @At("") + @Ok("beetl:/mobile/meeting/audit.html") + @RequiresAuthentication + public void index(HttpServletRequest request) { + + } + + @At("/pageData") + @Ok("json:full") + @ViReturn + @RequiresAuthentication + public Object pageData(PageForm pageForm, + @Param(value = "typeId", required = false) String typeId, + @Param(value = "meetingTime", required = false) Integer meetingTime) { + + return meetingAuditService.myMeetingPageData(pageForm.getPageNumber(), pageForm.getPageSize(), pageForm.getSearchKeyword(), typeId, meetingTime); + } + + /** + * 查询会议提交了请假的人员 + * 这里需要根据当前登录用户去过滤他能审核的 are you ok? + */ + @At("/getLeaveUser") + @Ok("json:full") + @ViReturn + @RequiresAuthentication + public Object getLeaveUser(@Param(value = "meetingId", required = false) String meetingId, + @Param(value = "moduleName", required = false) String moduleName, + @Param(value = "auditType", required = false) String auditType) { + if (StringUtils.isBlank(meetingId)) { + log.error("getUserByLoginUser()参数为空"); + return Result.error("参数为空"); + } + return meetingAuditService.getLeaveUser(meetingId, moduleName, auditType); + } + + /** + * 会议审核 + * + * @param tpuIds 审核记录Ids + * @param audit 审核 + * @param isPass 是否通过 + * @return + */ + @At("/audit") + @Ok("json:full") + @ViReturn + @RequiresAuthentication + public Object audit(@Param("tpuIds") String[] tpuIds, Audit audit, + @Param("isPass") boolean isPass) { + meetingAuditService.audit(tpuIds, audit, isPass); + return null; + } + + +} diff --git a/src/main/java/io/v/nutz/meeting/controller/mobile/MMeetingMineController.java b/src/main/java/io/v/nutz/meeting/controller/mobile/MMeetingMineController.java new file mode 100644 index 0000000..8192f5a --- /dev/null +++ b/src/main/java/io/v/nutz/meeting/controller/mobile/MMeetingMineController.java @@ -0,0 +1,222 @@ +package io.v.nutz.meeting.controller.mobile; + +import cn.hutool.core.util.StrUtil; +import cn.wizzer.framework.base.Result; +import io.v.nutz.annontation.ViReturn; +import io.v.nutz.base.utils.DateUtil; +import io.v.nutz.meeting.models.MeetingInfo; +import io.v.nutz.meeting.models.MeetingTimePeriodUser; +import io.v.nutz.meeting.service.MeetingMineService; +import io.v.nutz.modules.models.PageForm; +import io.v.nutz.web.commons.utils.ShiroUtil; +import org.apache.shiro.authz.annotation.RequiresAuthentication; +import org.nutz.dao.Chain; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.json.Json; +import org.nutz.lang.Lang; +import org.nutz.mvc.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import java.util.List; +import java.util.Map; + +/** + * 会议手机端签到 + */ +@IocBean +@At("/mobile/meeting/mine") +public class MMeetingMineController { + + @Inject + private MeetingMineService meetingMineService; + + @Inject + private Dao dao; + + /** + * 跳转签到页面 + * + * @param request + */ + @At("/go_sign") + @RequiresAuthentication + @Ok("beetl:/mobile/meeting/sign_in.html") + public void goSign(HttpServletRequest request) { + } + + @At("/myMeeting") + @RequiresAuthentication + @Ok("beetl:/mobile/meeting/myMeeting.html") + public void myMeeting(HttpServletRequest request) { + } + + /** + * 我的会议 + * + * @param pageForm 分页 + * @param tabNameIndex tab页下标 + * @return + */ + @At("/pageData") + @Ok("json:full") + @ViReturn + @RequiresAuthentication + public Object pageData(PageForm pageForm, + @Param(value = "tabNameIndex", required = false) Integer tabNameIndex, + @Param(value = "typeId", required = false) String typeId, + @Param(value = "meetingTime", required = false) Integer meetingTime) { + Cnd cnd = Cnd.NEW(); + if (StrUtil.isNotBlank(typeId)) { + cnd.andEX("info.typeId", "=", typeId); + } + if (meetingTime != null) { + if (meetingTime == 0) { + cnd.andEX("info.startTime", ">=", DateUtil.getDateTime()); + } else { + cnd.andEX("info.startTime", "<", DateUtil.getDateTime()); + } + } + switch (tabNameIndex) { + case 1 -> cnd.and("mu.isJoin", "is", null); + case 2 -> cnd.and("mu.isJoin", "=", 0); + default -> { + } + } + + return meetingMineService.myMeetingPageData(pageForm, cnd); + } + + /** + * 我的会议时间段信息 + * + * @param meetingId + * @return + */ + @At("/findMyMeetPeriod") + @Ok("json:full") + @ViReturn + @RequiresAuthentication + public Object findMyMeetPeriod(@Param("meetingId") String meetingId) { + if (StrUtil.isBlank(meetingId)) { + return Result.error("参数错误"); + } + return meetingMineService.findMyMeetPeriod(meetingId); + } + + /** + * 请假 + * + * @return + */ + @At("/askForLeave") + @POST + @Ok("json:full") + @ViReturn + @RequiresAuthentication + public Object askForLeave(@Param("data") String meetingTimePeriodUsers) { + //先清空,因为可能会涉及到修改 + /*org.nutz.dao.Chain clearChain = Chain.make("isJoin", null); + clearChain.add("stateCode", null); + clearChain.add("leaveReason", ""); + clearChain.add("auditIdList", null); + Cnd clearCnd = Cnd.where("meetingId", "=", meetingId) + .and("userId", "=", ShiroUtil.getPrincipalProperty("id")); + dao.update(MeetingTimePeriodUser.class, clearChain, clearCnd);*/ + + List meetingTimePeriod = Json.fromJsonAsList(MeetingTimePeriodUser.class, meetingTimePeriodUsers); + if (Lang.isEmpty(meetingTimePeriod)) { + return null; + } + meetingMineService.askForLeave(meetingTimePeriod); + return null; + } + + /** + * 确认参加会议 + * + * @return + */ + @At("/confirmMeeting") + @POST + @Ok("json:full") + @ViReturn + @RequiresAuthentication + public Object confirmMeeting(@Param("tpuIds") List tpuIds) { + meetingMineService.confirmMeeting(tpuIds); + return null; + } + + /** + * @param meetingId 会议id + * @param mtpId 时间段id + * @return + */ + @At("/getMeetingInfo") + @GET + @Ok("json:full") + @ViReturn + @RequiresAuthentication + public Object getMeetingInfo(@Param("meetingId") String meetingId, @Param("mtpId") String mtpId) { + MeetingInfo meetingInfo = dao.fetch(MeetingInfo.class, meetingId); + dao.fetchLinks(meetingInfo, "meetingTimePeriods", Cnd.NEW().asc("startTime")); + + Cnd cnd = Cnd.NEW(); + cnd.and("meetingTimePeriodId", "=", mtpId); + cnd.and("userId", "=", ShiroUtil.getPrincipalProperty("id")); + MeetingTimePeriodUser mtPu = dao.fetch(MeetingTimePeriodUser.class, cnd); + + if (Lang.isEmpty(meetingInfo)) { + return Result.error("参数错误"); + } + if (Lang.isEmpty(mtPu)) { + return Result.error("你不是当前会议人员"); + } + + return Map.of("meetingInfo", meetingInfo, "signInfo", mtPu); + } + + /** + * 签到 + * + * @return + */ + @At("/signIn") + @POST + @Ok("json:full") + @ViReturn + @RequiresAuthentication + public Object signIn(@Param("mtpId") String mtpId) { + if (StrUtil.isBlank(mtpId)) { + return Result.error("参数错误"); + } + Cnd cnd = Cnd.NEW(); + cnd.and("meetingTimePeriodId", "=", mtpId); + cnd.and("userId", "=", ShiroUtil.getPrincipalProperty("id")); + MeetingTimePeriodUser mtpu = dao.fetch(MeetingTimePeriodUser.class, cnd); + if (Lang.isEmpty(mtpu)) { + return Result.error("参数错误"); + } + if (Lang.isNotEmpty(mtpu.getIsJoin()) && !mtpu.getIsJoin()) { + return Result.error("您已请假,不能签到喔"); + } + if (Lang.isNotEmpty(mtpu.getIsSignIn()) && mtpu.getIsSignIn()) { + return Result.error("您已签到,不要重复签到喔"); + } + meetingMineService.signIn(mtpu); + return Result.success().addMsg("签到成功"); + } + + @At + @POST + @Ok("json:full") + @ViReturn + @RequiresAuthentication + public Object cancelLeave(String id) { + dao.update(MeetingTimePeriodUser.class, Chain.make("isJoin", null).add("leaveReason", null).add("stateCode", null) + , Cnd.where("id", "=", id)); + return null; + } +} diff --git a/src/main/java/io/v/nutz/meeting/controller/pc/MeetingManageController.java b/src/main/java/io/v/nutz/meeting/controller/pc/MeetingManageController.java new file mode 100644 index 0000000..ff78609 --- /dev/null +++ b/src/main/java/io/v/nutz/meeting/controller/pc/MeetingManageController.java @@ -0,0 +1,158 @@ +package io.v.nutz.meeting.controller.pc; + +import io.v.nutz.annontation.ViReturn; +import io.v.nutz.meeting.models.MeetingInfo; +import io.v.nutz.meeting.models.MeetingUser; +import io.v.nutz.meeting.service.MeetingManageService; +import io.v.nutz.modules.models.PageForm; +import io.v.nutz.msgNotify.models.MsgNotify; +import io.v.nutz.msgNotify.service.MsgNotifyService; +import org.apache.shiro.authz.annotation.Logical; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.lang.util.NutMap; +import org.nutz.mvc.annotation.At; +import org.nutz.mvc.annotation.Ok; +import org.nutz.mvc.annotation.POST; +import org.nutz.mvc.annotation.Param; + +import javax.servlet.http.HttpServletRequest; +import java.util.ArrayList; +import java.util.List; + +@IocBean +@At("/platform/meeting/manage") +public class MeetingManageController { + + @Inject + private Dao dao; + + @Inject + private MeetingManageService meetingManageService; + + @Inject + private MsgNotifyService msgNotifyService; + + @At("/createMeeting") + @Ok("beetl:/platform/meeting/manage/add.html") + @RequiresPermissions("meeting.manage.add") + public void createMeeting(HttpServletRequest request) { + + } + + @At("") + @Ok("beetl:/platform/meeting/index.html") + @RequiresPermissions("meeting.manage.search") + public void index(HttpServletRequest request) { + + } + + @At("/pageData") + @Ok("json:full") + @ViReturn + @RequiresPermissions("meeting.manage.search") + public Object pageData(PageForm pageForm, + @Param(value = "year", required = false) Integer year, + @Param(value = "typeId", required = false) String typeId) { + Cnd cnd = Cnd.NEW(); + cnd.andEX("YEAR(info.startTime)", "=", year); + cnd.andEX("info.typeId", "=", typeId); + return meetingManageService.pageData(pageForm, cnd); + } + + + /** + * 添加会议 + * + * @param meetingInfo 会议信息 + * @return + */ + @At("/doAdd") + @POST + @Ok("json:full") + @ViReturn + @RequiresPermissions("meeting.manage.add") + public Object doAdd(@Param("meetingInfo") MeetingInfo meetingInfo, + @Param("msgNotify") MsgNotify msgNotify, String[] users) { + + + NutMap nutMap = msgNotifyService.addMsgNotify(msgNotify, users); + meetingInfo.setNotifyId(nutMap.getString("id")); + List userIdList = nutMap.getList("userIdList", String.class); + List meetingUserList = new ArrayList<>(); + for (String id : userIdList) { + MeetingUser meetingUser = new MeetingUser(); + meetingUser.setUserId(id); + meetingUserList.add(meetingUser); + } + meetingInfo.setMeetingUserList(meetingUserList); + + meetingManageService.doAdd(meetingInfo); + return null; + } + + /** + * 编辑会议 + * + * @param meetingInfo 会议信息 + * @return + */ + @At("/doEdit") + @POST + @Ok("json:full") + @ViReturn + @RequiresPermissions("meeting.manage.add") + public Object doEdit(@Param("meetingInfo") MeetingInfo meetingInfo, + @Param("msgNotify") MsgNotify msgNotify, String[] users) { + + NutMap msgMap = msgNotifyService.editMsgNotify(msgNotify, users); + List userIdList = msgMap.getList("userIdList", String.class); + +// Cnd muCnd = Cnd.where("meetingId", "=", meetingInfo.getId()); +// List muList = dao.query(MeetingUser.class, muCnd); +// +// List meetingUserList = new ArrayList<>(); +// for (String userId : userIdList) { +// MeetingUser insertMu = new MeetingUser(); +// Optional optionalMeetingUser = muList.stream().filter(v -> v.getUserId().equals(userId)).findFirst(); +// MeetingUser mu = optionalMeetingUser.orElse(null); +// if (Lang.isNotEmpty(mu)) { +// insertMu.setId(mu.getId()); +// } +// insertMu.setMeetingId(meetingInfo.getId()); +// insertMu.setUserId(userId); +// meetingUserList.add(insertMu); +// } +// meetingInfo.setMeetingUserList(meetingUserList); + meetingManageService.doEdit(meetingInfo, userIdList); + return null; + } + + + @At + @Ok("json:full") + @ViReturn + @RequiresPermissions(value = {"meeting.manage.search", "meeting.manage.add"}, logical = Logical.OR) + public Object findOne(String id) { + return dao.fetchLinks(dao.fetch(MeetingInfo.class, id), "^meetingTimePeriods$", Cnd.NEW().asc("startTime")); + } + + /** + * 删除会议 + * + * @param id + * @return + */ + @At + @Ok("json:full") + @ViReturn + @RequiresPermissions("meeting.manage.search") + public Object doDelete(String id) { + meetingManageService.doDelete(id); + return null; + } + +} diff --git a/src/main/java/io/v/nutz/meeting/controller/pc/MeetingOnlineController.java b/src/main/java/io/v/nutz/meeting/controller/pc/MeetingOnlineController.java new file mode 100644 index 0000000..5c7a577 --- /dev/null +++ b/src/main/java/io/v/nutz/meeting/controller/pc/MeetingOnlineController.java @@ -0,0 +1,133 @@ +package io.v.nutz.meeting.controller.pc; + +import cn.afterturn.easypoi.excel.ExcelExportUtil; +import cn.afterturn.easypoi.excel.entity.ExportParams; +import cn.afterturn.easypoi.excel.entity.enmus.ExcelType; +import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity; +import io.v.nutz.annontation.ViReturn; +import io.v.nutz.meeting.models.MeetingInfo; +import io.v.nutz.meeting.models.MeetingTimePeriod; +import io.v.nutz.meeting.service.MeetingOnlineService; +import io.v.nutz.modules.models.PageForm; +import io.v.nutz.util.ViTool; +import io.v.nutz.web.commons.base.Globals; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.shiro.authz.annotation.RequiresAuthentication; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.lang.util.NutMap; +import org.nutz.mvc.annotation.At; +import org.nutz.mvc.annotation.Ok; +import org.nutz.mvc.annotation.Param; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@IocBean +@At("/platform/meeting/online") +public class MeetingOnlineController { + + @Inject + private Dao dao; + + @Inject + private MeetingOnlineService meetingOnlineService; + + @At("") + @Ok("beetl:/platform/meeting/online.html") + @RequiresPermissions("meeting.online") + public void index(HttpServletRequest request) { + + } + + /** + * 分页查询 + * + * @param pageForm + * @param year + * @param typeId + * @return + */ + @At("/pageData") + @Ok("json:full") + @ViReturn + @RequiresPermissions("meeting.online") + public Object pageData(PageForm pageForm, + @Param(value = "year", required = false) Integer year, + @Param(value = "typeId", required = false) String typeId) { + Cnd cnd = Cnd.NEW(); + cnd.andEX("YEAR(info.startTime)", "=", year); + cnd.andEX("info.typeId", "=", typeId); + return meetingOnlineService.pageData(pageForm, cnd); + } + + /** + * 导出签字表 + * + * @param timePeriodId + * @param response + */ + @At("/exportSignature/?") + @Ok("void") + @ViReturn + @RequiresAuthentication + public void exportSignature(@Param(value = "timePeriodId", required = false) String timePeriodId, HttpServletResponse response) { + MeetingTimePeriod timePeriod = dao.fetch(MeetingTimePeriod.class, timePeriodId); + MeetingInfo meetingInfo = dao.fetch(MeetingInfo.class, timePeriod.getMeetingId()); + String fileName = meetingInfo.getMeetingName() + "(" + timePeriod.getTimePeriodName() + ")签字表"; + + List entityArrayList = new ArrayList<>(); + entityArrayList.add(new ExcelExportEntity("姓名", "username", 20)); + entityArrayList.add(new ExcelExportEntity("工号", "loginname", 20)); + entityArrayList.add(new ExcelExportEntity("单位", "unitname", 20)); + entityArrayList.add(new ExcelExportEntity("分工会", "unionname", 20)); + entityArrayList.add(new ExcelExportEntity("角色", "roleName", 20)); + entityArrayList.add(new ExcelExportEntity("签字", "", 20)); + try { + ViTool.excelResponse(response, fileName + ".xlsx"); + ExportParams exportParams = new ExportParams(); + exportParams.setTitle(timePeriod.getTimePeriodName() + "签字表"); + exportParams.setType(ExcelType.XSSF); + Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entityArrayList, meetingOnlineService.exportSignature(timePeriodId)); + workbook.write(response.getOutputStream()); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @At("/generateQrCodeUrl") + @Ok("json:full") + @ViReturn + @RequiresAuthentication + public Object generateQrCodeUrl(@Param(value = "tpId", required = false) String tpId, + @Param(value = "meetingId", required = false) String meetingId, + HttpServletRequest request) { + StringBuffer redirect = new StringBuffer(); + redirect.append(Globals.AppDomain); + + redirect.append("/mobile/meeting/mine/go_sign?"); + redirect.append("mtpId=").append(tpId); + redirect.append("&"); + redirect.append("meetingId=").append(meetingId); + return redirect.toString(); + } + + @At("/getRealTimeData/?") + @Ok("json:full") + @ViReturn + @RequiresPermissions("meeting.online") + public Object getRealTimeData(String timePeriodId) { + List signInUser = meetingOnlineService.getSignInUser(timePeriodId); + List> countData = meetingOnlineService.getRealTimeData(timePeriodId); + return Map.of("countData", countData, "userData", signInUser); + } + + +} diff --git a/src/main/java/io/v/nutz/meeting/controller/pc/MeetingTypeController.java b/src/main/java/io/v/nutz/meeting/controller/pc/MeetingTypeController.java new file mode 100644 index 0000000..935a491 --- /dev/null +++ b/src/main/java/io/v/nutz/meeting/controller/pc/MeetingTypeController.java @@ -0,0 +1,148 @@ +package io.v.nutz.meeting.controller.pc; + +import cn.wizzer.framework.base.Result; +import io.v.nutz.annontation.ViReturn; +import io.v.nutz.meeting.models.MeetingType; +import io.v.nutz.meeting.service.MeetingCommonService; +import io.v.nutz.meeting.service.MeetingTypeService; +import io.v.nutz.modules.models.AuditState; +import org.apache.shiro.authz.annotation.RequiresAuthentication; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.dao.Sqls; +import org.nutz.dao.sql.Sql; +import org.nutz.dao.util.Daos; +import org.nutz.dao.util.cri.SqlExpressionGroup; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.json.Json; +import org.nutz.mvc.annotation.At; +import org.nutz.mvc.annotation.Ok; +import org.nutz.mvc.annotation.Param; + +import javax.servlet.http.HttpServletRequest; +import java.util.List; + +@IocBean +@At("/platform/meeting/basic/type") +public class MeetingTypeController { + + @Inject + private Dao dao; + + @Inject + private MeetingTypeService meetingTypeService; + + @Inject + private MeetingCommonService meetingCommonService; + + @At("") + @Ok("beetl:/platform/meeting/basic/type.html") + @RequiresPermissions("meeting.basic.type") + public void index(HttpServletRequest request) { + + } + + @At("/findAll") + @Ok("json:full") + @ViReturn + @RequiresAuthentication + public Object findAll() { + List meetingTypeList = dao.query(MeetingType.class, Cnd.NEW().asc("sortNum")); + dao.fetchLinks(meetingTypeList, "^auditStateList$"); + for (MeetingType meetingType : meetingTypeList) { + meetingType.setAuditStateList(dao.query(AuditState.class, Cnd.where("meetingTypeId", "=", meetingType.getId()).and("module", "=", meetingType.getModuleName()))); + List auditStateList = meetingType.getAuditStateList(); + dao.fetchLinks(auditStateList, "^auditStateUserList$"); + } + return meetingTypeList; + } + + @At("/doHandle") + @Ok("json:full") + @ViReturn + @RequiresPermissions("meeting.basic.type") + public Object doHandle(@Param("data") String data) { + MeetingType meetingType = Json.fromJson(MeetingType.class, data); + + if (null == meetingType.getId()) { + int typeCount = dao.count(MeetingType.class, Cnd.where("moduleName", "=", meetingType.getModuleName())); + int count = dao.count(AuditState.class, Cnd.where("module", "=", meetingType.getModuleName())); + if (typeCount > 0 || count > 0) { + return Result.error("模块名称已存在,换一个试试"); + } + meetingTypeService.add(meetingType); + } else { + meetingTypeService.edit(meetingType); + } + return null; + } + + @At("/delete/?") + @Ok("json:full") + @ViReturn + @RequiresPermissions("meeting.basic.type") + public Object delete(Integer id) { + MeetingType meetingType = dao.fetch(MeetingType.class, id); + dao.clear(AuditState.class, Cnd.where("module", "=", meetingType.getModuleName())); + dao.clear(MeetingType.class, Cnd.where("id", "=", id)); + return null; + } + + @At("/findUserList") + @Ok("json:full") + @ViReturn + @RequiresPermissions("meeting.basic.type") + public Object findUserList(@Param("keyWords") String[] keyWords) { + Sql sql = Sqls.create(""" + SELECT + id, + username, + loginname, + mobile, + sex, + unitname, + unionname, + unionid, + unitid + FROM + `user` + $condition + """); + Cnd cnd = Cnd.NEW(); + SqlExpressionGroup seg = new SqlExpressionGroup(); + for (String keyWord : keyWords) { + seg.andLike("loginname", keyWord); + seg.orLike("username", keyWord); + } + cnd.and(seg); + sql.setCondition(cnd); + return Daos.query(dao, sql.toString(), Sqls.callback.maps()); + } + + @At + @Ok("json:full") + @ViReturn + @RequiresPermissions("meeting.basic.type") + public Object findMaxStateId() { + return meetingTypeService.findMaxStateId(); + } + + @At + @Ok("json:full") + @ViReturn + @RequiresPermissions("meeting.basic.type") + public Object checkStateId(@Param("stateId") Integer stateId, @Param("stateIndex") Integer stateIndex) { + if (stateId == null) { + return Result.error("参数错误"); + } + int count = dao.count(AuditState.class, Cnd.where("stateId", "=", stateId)); + if (count == 0) { + return Result.success().addData(stateId); + } else { + return Result.success(meetingTypeService.findMaxStateId() + 10 * stateIndex); + } + } + +} diff --git a/src/main/java/io/v/nutz/meeting/controller/pc/statistics/MeetingBasicStatisticsController.java b/src/main/java/io/v/nutz/meeting/controller/pc/statistics/MeetingBasicStatisticsController.java new file mode 100644 index 0000000..8d4039e --- /dev/null +++ b/src/main/java/io/v/nutz/meeting/controller/pc/statistics/MeetingBasicStatisticsController.java @@ -0,0 +1,126 @@ +package io.v.nutz.meeting.controller.pc.statistics; + +import cn.afterturn.easypoi.excel.entity.ExportParams; +import cn.afterturn.easypoi.excel.entity.enmus.ExcelType; +import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity; +import cn.afterturn.easypoi.excel.export.ExcelExportService; +import io.v.nutz.annontation.ViReturn; +import io.v.nutz.meeting.models.MeetingInfo; +import io.v.nutz.meeting.models.MeetingType; +import io.v.nutz.meeting.service.MeetingBasicStatisticsService; +import io.v.nutz.modules.models.PageForm; +import io.v.nutz.util.ViTool; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.apache.shiro.authz.annotation.RequiresAuthentication; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.nutz.dao.Dao; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.lang.util.NutMap; +import org.nutz.mvc.annotation.At; +import org.nutz.mvc.annotation.Ok; +import org.nutz.mvc.annotation.Param; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.ArrayList; +import java.util.List; + +@IocBean +@Ok("json:full") +@At("/meeting/statistics/basic") +public class MeetingBasicStatisticsController { + + @Inject + private MeetingBasicStatisticsService meetingBasicStatisticsService; + + @Inject + private Dao dao; + + @At("") + @Ok("beetl:/platform/meeting/statistics/basic.html") + @RequiresPermissions("meeting.statistics.basic") + public void index(HttpServletRequest request) { + + } + + + /** + * 根据会议id查询会议时间段及对应人数 + * + * @param pageForm + * @return + */ + @At + @ViReturn + @RequiresPermissions("meeting.statistics.basic") + public Object pageData(PageForm pageForm, + @Param(value = "meetingId", required = false) String meetingId, + @Param(value = "meetingTime", required = false) String[] meetingTime, + @Param(value = "typeId", required = false) String typeId) { + return meetingBasicStatisticsService.pageData(pageForm, meetingId, meetingTime, typeId); + } + + @At + @ViReturn + @RequiresPermissions("meeting.statistics.basic") + public Object getMeetingList(String[] meetingTime) { + return meetingBasicStatisticsService.getMeetingList(meetingTime); + } + + @At + @ViReturn + @RequiresPermissions("meeting.statistics.basic") + public Object getUserData(PageForm pageForm, + @Param(value = "id", required = false) String id, + @Param(value = "meetingId", required = false) String meetingId, + @Param(value = "type", required = false) String type, + @Param(value = "unitId", required = false) String unitId, + @Param(value = "unionId", required = false) String unionId) { + return meetingBasicStatisticsService.getUserData(pageForm, id, meetingId, type, unitId, unionId); + } + + + @At + @Ok("void") + @RequiresAuthentication + public void exportUser(@Param(value = "id", required = false) String id, + @Param(value = "meetingId", required = false) String meetingId, + HttpServletResponse response) { + List entityList = new ArrayList<>(); + entityList.add(new ExcelExportEntity("姓名", "userName", 20)); + entityList.add(new ExcelExportEntity("工号", "loginName", 20)); + entityList.add(new ExcelExportEntity("单位", "unitName", 30)); + entityList.add(new ExcelExportEntity("分工会", "unionName", 35)); + + Workbook workbook = new XSSFWorkbook(); + + MeetingInfo meetingInfo = dao.fetch(MeetingInfo.class, meetingId); + MeetingType meetingType = dao.fetch(MeetingType.class, meetingInfo.getTypeId()); + String[] arr = null; + if(List.of("teacherMeeting","gdh").contains(meetingType.getModuleName())) { + arr = new String[]{"应到人数", "实到人数", "正式代表", "列席代表", "请假人数", "未到人数"}; + } else { + arr = new String[]{"应到人数", "实到人数", "请假人数", "未到人数"}; + } + for (String a : arr) { + List userData = meetingBasicStatisticsService.getUserData(id, meetingId, a); + ExcelExportService service = new ExcelExportService(); + ExportParams exportParams = new ExportParams(); + exportParams.setTitle(a); + exportParams.setSheetName(a); + exportParams.setType(ExcelType.XSSF); + service.createSheetForMap(workbook, exportParams, entityList, userData); + } + try { + ViTool.excelResponse(response, "人员列表.xlsx"); + ServletOutputStream outputStream = response.getOutputStream(); + workbook.write(outputStream); + outputStream.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/io/v/nutz/meeting/models/CustomAuditTypeEnum.java b/src/main/java/io/v/nutz/meeting/models/CustomAuditTypeEnum.java new file mode 100644 index 0000000..d2ee639 --- /dev/null +++ b/src/main/java/io/v/nutz/meeting/models/CustomAuditTypeEnum.java @@ -0,0 +1,28 @@ +package io.v.nutz.meeting.models; + +import io.v.nutz.annontation.SelectEnum; + +@SelectEnum +public enum CustomAuditTypeEnum { + + CLUB("CLUB", "审核本协会"), + DW("DW", "审核本单位"), + FGH("FGH", "审核本分工会"), + TZ("TZ", "审核本代表团"); + + public String value; + public String desc; + + public String getValue() { + return this.value; + } + + public String getDesc() { + return this.desc; + } + + private CustomAuditTypeEnum(String value, String desc) { + this.value = value; + this.desc = desc; + } +} diff --git a/src/main/java/io/v/nutz/meeting/models/CustomAuditTypeHandle.java b/src/main/java/io/v/nutz/meeting/models/CustomAuditTypeHandle.java new file mode 100644 index 0000000..4eb4c1d --- /dev/null +++ b/src/main/java/io/v/nutz/meeting/models/CustomAuditTypeHandle.java @@ -0,0 +1,83 @@ +package io.v.nutz.meeting.models; + +import io.v.nutz.annontation.SelectEnum; +import io.v.nutz.base.utils.Roles; +import io.v.nutz.base.utils.Vi; +import io.v.nutz.sys.models.Sys_club_user; +import io.v.nutz.sys.models.Sys_user; +import io.v.nutz.sys.models.Sys_user_role; +import io.v.nutz.util.ShiroUtil; +import io.v.nutz.util.ViResource; +import lombok.Getter; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.dao.Sqls; +import org.nutz.dao.util.cri.SqlExpressionGroup; +import org.nutz.lang.Lang; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * @Author JyuHsin + * @Date 2023/3/30 + * @Description + */ +@Getter +@SelectEnum +public enum CustomAuditTypeHandle { + + + DW(CustomAuditTypeEnum.DW.getValue()) { + @Override + public SqlExpressionGroup next(SqlExpressionGroup sqlExpressionGroup) { + Sys_user user = dao.fetch(Sys_user.class, Cnd.where("id", "=", ShiroUtil.getPrincipalProperty("id"))); + sqlExpressionGroup.or("u.unitid", "in", user.getUnitid()); + return sqlExpressionGroup; + } + }, + FGH(CustomAuditTypeEnum.FGH.getValue()) { + @Override + public SqlExpressionGroup next(SqlExpressionGroup sqlExpressionGroup) { + //查询当前登录用户管理的工会 + List userRoles = dao.query(Sys_user_role.class, Cnd.where("userid", "=", ShiroUtil.getPrincipalProperty("id")) + .and("roleid", "in", Lang.array(Roles.UNION_MANGER))); + List unionIds = userRoles.stream().map(Sys_user_role::getUnionid).collect(Collectors.toList()); + sqlExpressionGroup.or("u.unionid", "in", unionIds); + return sqlExpressionGroup; + } + }, + CLUB(CustomAuditTypeEnum.CLUB.getValue()) { + @Override + public SqlExpressionGroup next(SqlExpressionGroup sqlExpressionGroup) { + //查询当前登录用户管理的协会 + List userRoles = dao.query(Sys_user_role.class, Cnd.where("userid", "=", ShiroUtil.getPrincipalProperty("id")) + .and("roleid", "in", Lang.array(Roles.CLUB_FZR))); + List clubIds = userRoles.stream().map(Sys_user_role::getStid).collect(Collectors.toList()); + List clubUsers = dao.query(Sys_club_user.class, Cnd.where("stid", "in", clubIds)); + if (Lang.isNotEmpty(clubUsers)) { + sqlExpressionGroup.or("u.id", "in", clubUsers.stream().map(v -> v.getUserid()).collect(Collectors.toList())); + } + return sqlExpressionGroup; + } + }, + TZ(CustomAuditTypeEnum.TZ.getValue()) { + @Override + public SqlExpressionGroup next(SqlExpressionGroup sqlExpressionGroup) { + String dbtId = vi.getLastDelegationId((String) ShiroUtil.getPrincipalProperty("id")); + sqlExpressionGroup.or("u.id", "in", Sqls.createf("select DISTINCT dbid from jdh_db where dbtid = '%s'", dbtId)); + return sqlExpressionGroup; + } + }; + + private final String name; + + public Dao dao = ViResource.ioc.getByType(Dao.class); + public Vi vi = ViResource.ioc.getByType(Vi.class); + + CustomAuditTypeHandle(String name) { + this.name = name; + } + + public abstract SqlExpressionGroup next(SqlExpressionGroup sqlExpressionGroup); +} diff --git a/src/main/java/io/v/nutz/meeting/models/MeetingInfo.java b/src/main/java/io/v/nutz/meeting/models/MeetingInfo.java new file mode 100644 index 0000000..65b5b96 --- /dev/null +++ b/src/main/java/io/v/nutz/meeting/models/MeetingInfo.java @@ -0,0 +1,90 @@ +package io.v.nutz.meeting.models; + +import lombok.Data; +import lombok.experimental.Accessors; +import org.nutz.dao.entity.annotation.*; +import org.nutz.dao.interceptor.annotation.PrevInsert; + +import java.util.Date; +import java.util.List; + +@Table +@Data +@Accessors(chain = true) +public class MeetingInfo { + + @Name + @Comment("ID") + @ColDefine(type = ColType.VARCHAR, width = 32) + @PrevInsert(uu32 = true) + private String id; + + @Column + @Comment("名称") + @ColDefine(type = ColType.VARCHAR, width = 50) + private String meetingName; + + @Column + @Comment("地点") + @ColDefine(type = ColType.VARCHAR, width = 100) + private String location; + + @Column + @Comment("会议类型") + @ColDefine(type = ColType.INT) + private Integer typeId; + + @Column + @Comment("开始时间") + @ColDefine(type = ColType.DATETIME) + private Date startTime; + + @Column + @Comment("结束时间") + @ColDefine(type = ColType.DATETIME) + private Date endTime; + + @Column + @Comment("是否可以请假") + @ColDefine(type = ColType.BOOLEAN) + private Boolean isCanLeave; + + @Column + @Comment("是否多次签到") + @ColDefine(type = ColType.BOOLEAN) + private Boolean manySign; + + @Column + @Comment("请假截至时间") + @ColDefine(type = ColType.DATETIME) + private Date leaveDeadline; + + @Column + @Comment("会议描述") + @ColDefine(type = ColType.VARCHAR, width = 2000) + private String meetingDescription; + + @Column + @Comment("关联会议id") + @ColDefine(type = ColType.VARCHAR, width = 32) + private String notifyId; + + /** + * 会议时间段 + */ + @Many(field = "meetingId") + private List meetingTimePeriods; + + /** + * 会议参加人员 + */ + @Many(field = "meetingId") + private List meetingUserList; + + /** + * 消息对象 + */ +// @One(field = "") +// private MsgNotify msgNotify; + +} diff --git a/src/main/java/io/v/nutz/meeting/models/MeetingTimePeriod.java b/src/main/java/io/v/nutz/meeting/models/MeetingTimePeriod.java new file mode 100644 index 0000000..0dc9ebe --- /dev/null +++ b/src/main/java/io/v/nutz/meeting/models/MeetingTimePeriod.java @@ -0,0 +1,48 @@ +package io.v.nutz.meeting.models; + +import lombok.Data; +import org.nutz.dao.entity.annotation.*; +import org.nutz.dao.interceptor.annotation.PrevInsert; + +import java.util.Date; +import java.util.List; + +/** + * 会议时间段 + */ +@Table +@Data +@TableIndexes({ + @Index(name = "INDEX_MEETINGTIMEPERIOD_MEETING_ID", fields = {"meetingId"}, unique = false),}) +public class MeetingTimePeriod { + + @Name + @Comment("ID") + @ColDefine(type = ColType.VARCHAR, width = 32) + @PrevInsert(uu32 = true) + private String id; + + @Column + @Comment("会议id") + @ColDefine(type = ColType.VARCHAR, width = 32) + private String meetingId; + + @Column + @Comment("时间段会议名称") + @ColDefine(type = ColType.VARCHAR, width = 20) + private String timePeriodName; + + @Column + @Comment("开始时间") + @ColDefine(type = ColType.DATETIME) + private Date startTime; + + @Column + @Comment("结束时间") + @ColDefine(type = ColType.DATETIME) + private Date endTime; + + @Many(field = "meetingTimePeriodId") + private List meetingTimePeriodUserList; + +} diff --git a/src/main/java/io/v/nutz/meeting/models/MeetingTimePeriodUser.java b/src/main/java/io/v/nutz/meeting/models/MeetingTimePeriodUser.java new file mode 100644 index 0000000..143e75f --- /dev/null +++ b/src/main/java/io/v/nutz/meeting/models/MeetingTimePeriodUser.java @@ -0,0 +1,83 @@ +package io.v.nutz.meeting.models; + +import lombok.Data; +import org.nutz.dao.entity.annotation.*; +import org.nutz.dao.interceptor.annotation.PrevInsert; +import org.nutz.lang.util.NutMap; + +import java.util.Date; +import java.util.List; + +@Table +@Data +@TableIndexes({ + @Index(name = "INDEX_MEETINGTIMEPERIODUSER_MEETING_TIME_PERIOD_ID", fields = {"meetingTimePeriodId"}, unique = false), + @Index(name = "INDEX_MEETINGTIMEPERIODUSER_MEETING_ID", fields = {"meetingId"}, unique = false)}) +public class MeetingTimePeriodUser { + + @Name + @Comment("ID") + @ColDefine(type = ColType.VARCHAR, width = 32) + @PrevInsert(uu32 = true) + private String id; + + @Column + @Comment("会议id") + @ColDefine(type = ColType.VARCHAR, width = 32) + private String meetingId; + + @Column + @Comment("会议时间段id") + @ColDefine(type = ColType.VARCHAR, width = 32) + private String meetingTimePeriodId; + + @Column + @ColDefine(type = ColType.VARCHAR, width = 32) + @Comment("用户id") + private String userId; + + @Column + @ColDefine(type = ColType.BOOLEAN) + @Comment("是否参加") + private Boolean isJoin; + + @Column + @ColDefine(type = ColType.VARCHAR, width = 100) + @Comment("请假理由") + private String leaveReason; + + @Column + @Comment("用户请假开始时间") + @ColDefine(type = ColType.DATETIME) + private Date userLeaveStartTime; + + @Column + @Comment("用户请假结束时间") + @ColDefine(type = ColType.DATETIME) + private Date userLeaveEndTime; + + @Column + @ColDefine(type = ColType.INT) + @Comment("请假当前状态") + private Integer stateCode; + + @Column + @ColDefine(type = ColType.BOOLEAN) + @Comment("是否签到") + private Boolean isSignIn; + + @Column + @ColDefine(type = ColType.DATETIME) + @Comment("签到时间") + private Date signInDateTime; + + /** + * 审核记录 + */ + @Column + @ColDefine(type = ColType.MYSQL_JSON) + @Comment("审核记录ID") + private List auditIdList; + + +} diff --git a/src/main/java/io/v/nutz/meeting/models/MeetingType.java b/src/main/java/io/v/nutz/meeting/models/MeetingType.java new file mode 100644 index 0000000..bf3bf47 --- /dev/null +++ b/src/main/java/io/v/nutz/meeting/models/MeetingType.java @@ -0,0 +1,38 @@ +package io.v.nutz.meeting.models; + +import io.v.nutz.modules.models.AuditState; +import lombok.Data; +import lombok.experimental.Accessors; +import org.nutz.dao.entity.annotation.*; + +import java.util.List; + +@Table +@Data +@Accessors(chain = true) +public class MeetingType { + + @Id + @Comment("ID") + @ColDefine(type = ColType.INT) + private Integer id; + + @Column + @Comment("名称") + @ColDefine(type = ColType.VARCHAR, width = 50) + private String meetingTypeName; + + @Column + @Comment("模块名称") + @ColDefine(type = ColType.VARCHAR, width = 50) + private String moduleName; + + @Column + @Comment("排序") + @ColDefine(type = ColType.INT) + private int sortNum; + + @Many(field = "meetingTypeId") + private List auditStateList; + +} diff --git a/src/main/java/io/v/nutz/meeting/models/MeetingUser.java b/src/main/java/io/v/nutz/meeting/models/MeetingUser.java new file mode 100644 index 0000000..f9410e8 --- /dev/null +++ b/src/main/java/io/v/nutz/meeting/models/MeetingUser.java @@ -0,0 +1,78 @@ +package io.v.nutz.meeting.models; + +import lombok.Data; +import lombok.experimental.Accessors; +import org.nutz.dao.entity.annotation.*; +import org.nutz.dao.interceptor.annotation.PrevInsert; +import org.nutz.lang.util.NutMap; + +import java.util.Date; +import java.util.List; + +/** + * 参会人员表 + */ +@Table +@Data +@Accessors(chain = true) +public class MeetingUser { + + @Name + @ColDefine(type = ColType.VARCHAR, width = 32) + @PrevInsert(uu32 = true) + private String id; + + @Column + @ColDefine(type = ColType.VARCHAR, width = 32) + @Comment("会议ID") + private String meetingId; + + @Column + @ColDefine(type = ColType.VARCHAR, width = 32) + @Comment("用户id") + private String userId; + + @Column + @ColDefine(type = ColType.BOOLEAN) + @Comment("是否参加") + private Boolean isJoin; + + @Column + @ColDefine(type = ColType.VARCHAR, width = 100) + @Comment("请假理由") + private String leaveReason; + + @Column + @Comment("用户请假开始时间") + @ColDefine(type = ColType.DATETIME) + private Date userLeaveStartTime; + + @Column + @Comment("用户请假结束时间") + @ColDefine(type = ColType.DATETIME) + private Date userLeaveEndTime; + + @Column + @ColDefine(type = ColType.INT) + @Comment("请假当前状态") + private Integer stateCode; + + @Column + @ColDefine(type = ColType.BOOLEAN) + @Comment("是否签到") + private Boolean isSignIn; + + @Column + @ColDefine(type = ColType.DATETIME) + @Comment("签到时间") + private Date signInDateTime; + + /** + * 审核记录 + */ + @Column + @ColDefine(type = ColType.MYSQL_JSON) + @Comment("审核记录ID") + private List auditIdList; + +} diff --git a/src/main/java/io/v/nutz/meeting/service/MeetingAuditService.java b/src/main/java/io/v/nutz/meeting/service/MeetingAuditService.java new file mode 100644 index 0000000..9094957 --- /dev/null +++ b/src/main/java/io/v/nutz/meeting/service/MeetingAuditService.java @@ -0,0 +1,40 @@ +package io.v.nutz.meeting.service; + +import cn.wizzer.framework.page.Pagination; +import io.v.nutz.modules.models.Audit; +import io.v.nutz.service.ViService; +import org.nutz.lang.util.NutMap; + +import java.util.List; + +/** + * 会议审核接口 + */ +public interface MeetingAuditService extends ViService { + + /** + * 会议审核分页数据 + * + * @param pageNumber + * @param pageSize + * @return + */ + Pagination myMeetingPageData(int pageNumber, int pageSize, String keySearchWord, String typeId, Integer meetingTime); + + /** + * 查询会议提交了请假的人员 + * + * @param meetingId 会议id + */ + List getLeaveUser(String meetingId, String moduleName, String auditType); + + + /** + * 会议审核 + * + * @param tpuIds 记录id + * @param audit 审核对象 + * @param isPass isPass + */ + void audit(String[] tpuIds, Audit audit, boolean isPass); +} diff --git a/src/main/java/io/v/nutz/meeting/service/MeetingBasicStatisticsService.java b/src/main/java/io/v/nutz/meeting/service/MeetingBasicStatisticsService.java new file mode 100644 index 0000000..097bdf2 --- /dev/null +++ b/src/main/java/io/v/nutz/meeting/service/MeetingBasicStatisticsService.java @@ -0,0 +1,29 @@ +package io.v.nutz.meeting.service; + +import cn.wizzer.framework.page.Pagination; +import io.v.nutz.modules.models.PageForm; +import org.nutz.lang.util.NutMap; + +import java.util.List; + +/** + * 会议基本统计service + */ +public interface MeetingBasicStatisticsService { + + Pagination pageData(PageForm pageForm, String meetingId, String[] meetingTime, String typeId); + + + /** + * 根据时间段查询会议列表 + * + * @param meetingTime + * @return + */ + Object getMeetingList(String[] meetingTime); + + Object getUserData(PageForm pageForm, String id, String meetingId, String type, String unitId, String unionId); + + List getUserData(String id, String meetingId, String type); + +} diff --git a/src/main/java/io/v/nutz/meeting/service/MeetingCommonService.java b/src/main/java/io/v/nutz/meeting/service/MeetingCommonService.java new file mode 100644 index 0000000..d1de556 --- /dev/null +++ b/src/main/java/io/v/nutz/meeting/service/MeetingCommonService.java @@ -0,0 +1,66 @@ +package io.v.nutz.meeting.service; + +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.lang.util.NutMap; + +import java.util.List; + +/** + * 会议通用方法 + */ +@IocBean +public interface MeetingCommonService { + + /** + * 根据typeId查询所属模块名称 + * + * @param typeId + * @return + */ + String findModuleNameByTypeId(Integer typeId); + + /** + * 根据MeetId查询所属模块名称 + * + * @param meetingId + * @return + */ + String findModuleNameByMeetingId(String meetingId); + + /** + * 根据模块名称查找对应stateCode 最开始的那个 + * + * @param moduleName + * @return + */ + Integer findStartStateCodeByModuleName(String moduleName); + + Integer findAfterStateCode(Integer stateCode, boolean isPass); + + Integer findAfterStateCodeByModuleName(Integer stateCode, String moduleName); + + /** + * 根据会议id查询请假成功的code + * + * @param meetingId + * @return + */ + Integer findSuccessStateCode(String meetingId); + + /** + * 获取当前用户关联哪些会议 + * + * @return + */ + Object find(); + + /** + * 查询当前登录用户能审核的状态Id + * + * @return + */ + List findStateIdForMeCanAudit(); + + List getNextAuditUser(Integer stateCode, String moduleName); + +} diff --git a/src/main/java/io/v/nutz/meeting/service/MeetingManageService.java b/src/main/java/io/v/nutz/meeting/service/MeetingManageService.java new file mode 100644 index 0000000..9026d26 --- /dev/null +++ b/src/main/java/io/v/nutz/meeting/service/MeetingManageService.java @@ -0,0 +1,52 @@ +package io.v.nutz.meeting.service; + +import cn.wizzer.framework.page.Pagination; +import io.v.nutz.meeting.models.MeetingInfo; +import io.v.nutz.modules.models.PageForm; +import io.v.nutz.service.ViService; +import org.nutz.dao.Cnd; + +import java.util.List; + +/** + * 会议管理service + */ +public interface MeetingManageService extends ViService { + + /** + * 模块名字 + */ + String MODULE_NAME = ""; + + /** + * 分页数据 + * + * @param pageForm + * @param cnd + * @return + */ + Pagination pageData(PageForm pageForm, Cnd cnd); + + /** + * 添加会议及审核流程 + * + * @param meetingInfo 会议信息 + */ + void doAdd(MeetingInfo meetingInfo); + + /** + * 编辑会议及审核流程 + * + * @param meetingInfo 会议信息 + */ + void doEdit(MeetingInfo meetingInfo, List userIds); + + /** + * 删除会议 + * + * @param id 会议id + */ + void doDelete(String id); + + +} diff --git a/src/main/java/io/v/nutz/meeting/service/MeetingMineService.java b/src/main/java/io/v/nutz/meeting/service/MeetingMineService.java new file mode 100644 index 0000000..b73609e --- /dev/null +++ b/src/main/java/io/v/nutz/meeting/service/MeetingMineService.java @@ -0,0 +1,56 @@ +package io.v.nutz.meeting.service; + +import cn.wizzer.framework.page.Pagination; +import io.v.nutz.meeting.models.MeetingTimePeriodUser; +import io.v.nutz.modules.models.PageForm; +import io.v.nutz.service.ViService; +import org.nutz.dao.Cnd; + +import java.util.List; + +/** + * 会议通用service + */ +public interface MeetingMineService extends ViService { + + /** + * 我的会议分页数据 + * + * @param pageForm + * @param cnd + * @return + */ + Pagination myMeetingPageData(PageForm pageForm, Cnd cnd); + + /** + * 查询我的会议时间段 + * + * @return + */ + List findMyMeetPeriod(String meetingId); + + /** + * 请假 + * + * @param meetingId 会议id + * @param tpuIds tpuId 不解释 + * @param leaveReason 请假理由 + */ + void askForLeave(List meetingTimePeriodUsers); + + /** + * 参加会议确认 + * + * @param tpuIds + */ + void confirmMeeting(List tpuIds); + + /** + * 签到 + * + * @param timePeriodUser + */ + void signIn(MeetingTimePeriodUser timePeriodUser); + + +} diff --git a/src/main/java/io/v/nutz/meeting/service/MeetingOnlineService.java b/src/main/java/io/v/nutz/meeting/service/MeetingOnlineService.java new file mode 100644 index 0000000..c286fb8 --- /dev/null +++ b/src/main/java/io/v/nutz/meeting/service/MeetingOnlineService.java @@ -0,0 +1,46 @@ +package io.v.nutz.meeting.service; + +import cn.wizzer.framework.page.Pagination; +import io.v.nutz.modules.models.PageForm; +import io.v.nutz.service.ViService; +import org.nutz.dao.Cnd; +import org.nutz.lang.util.NutMap; + +import java.util.List; +import java.util.Map; + +public interface MeetingOnlineService extends ViService { + + /** + * 分页查询 + * + * @param pageForm + * @param cnd + * @return + */ + Pagination pageData(PageForm pageForm, Cnd cnd); + + /** + * 导出签字表 + * + * @param timePeriodId 时间段id + * @return + */ + List exportSignature(String timePeriodId); + + /** + * 在线数据 + * + * @param timePeriodId + * @return + */ + List> getRealTimeData(String timePeriodId); + + /** + * 获取签到人员 + * + * @param timePeriodId + * @return + */ + List getSignInUser(String timePeriodId); +} diff --git a/src/main/java/io/v/nutz/meeting/service/MeetingTypeService.java b/src/main/java/io/v/nutz/meeting/service/MeetingTypeService.java new file mode 100644 index 0000000..c1cb8ad --- /dev/null +++ b/src/main/java/io/v/nutz/meeting/service/MeetingTypeService.java @@ -0,0 +1,24 @@ +package io.v.nutz.meeting.service; + +import io.v.nutz.meeting.models.MeetingType; +import io.v.nutz.service.ViService; + +/** + * 会议类型 流程 service + */ +public interface MeetingTypeService extends ViService { + + /** + * 找出最大的那行stateId + * + * @return + */ + int findMaxStateId(); + + void add(MeetingType meetingType); + + void edit(MeetingType meetingType); + + void delete(MeetingType meetingTyp); + +} diff --git a/src/main/java/io/v/nutz/meeting/service/impl/MeetingAuditServiceImpl.java b/src/main/java/io/v/nutz/meeting/service/impl/MeetingAuditServiceImpl.java new file mode 100644 index 0000000..011c59a --- /dev/null +++ b/src/main/java/io/v/nutz/meeting/service/impl/MeetingAuditServiceImpl.java @@ -0,0 +1,238 @@ +package io.v.nutz.meeting.service.impl; + +import cn.hutool.core.util.StrUtil; +import cn.wizzer.framework.page.Pagination; +import io.v.nutz.base.utils.DateUtil; +import io.v.nutz.base.utils.Vi; +import io.v.nutz.constant.AuditTypeEnum; +import io.v.nutz.lang.Enum; +import io.v.nutz.meeting.models.CustomAuditTypeHandle; +import io.v.nutz.meeting.models.MeetingTimePeriodUser; +import io.v.nutz.meeting.service.MeetingAuditService; +import io.v.nutz.meeting.service.MeetingCommonService; +import io.v.nutz.modules.models.Audit; +import io.v.nutz.modules.models.AuditState; +import io.v.nutz.service.impl.ViServiceImpl; +import io.v.nutz.web.commons.utils.ShiroUtil; +import org.apache.commons.lang3.StringUtils; +import org.nutz.aop.interceptor.ioc.TransAop; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.dao.Sqls; +import org.nutz.dao.entity.Record; +import org.nutz.dao.sql.Sql; +import org.nutz.dao.util.cri.SqlExpressionGroup; +import org.nutz.dao.util.cri.Static; +import org.nutz.ioc.aop.Aop; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.json.Json; +import org.nutz.lang.Lang; +import org.nutz.lang.util.NutMap; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; + +@IocBean(args = {"refer:dao"}) +public class MeetingAuditServiceImpl extends ViServiceImpl implements MeetingAuditService { + + @Inject + private MeetingCommonService meetingCommonService; + @Inject + private Vi vi; + + public MeetingAuditServiceImpl(Dao dao) { + super(dao); + } + + @Override + public Pagination myMeetingPageData(int pageNumber, int pageSize, String keySearchWord, String typeId, Integer meetingTime) { + Sql sql = Sqls.create(""" + SELECT + info.*, + type.meetingTypeName, + type.moduleName, + (select count(*) from meeting_time_period_user where meetingId=info.id and JSON_CONTAINS(auditIdList, JSON_OBJECT('auditUser', @userid))) as audit + FROM + meeting_info info + LEFT JOIN meeting_type type ON type.id = info.typeId + LEFT JOIN audit_state state ON state.module = type.moduleName + $condition + """).setParam("userid", ShiroUtil.getPrincipalProperty("id")); + Cnd cnd = Cnd.NEW(); + cnd.andEX("info.isCanLeave", "=", 1); + List stateIdForMeCanAudit = meetingCommonService.findStateIdForMeCanAudit(); + cnd.and("state.stateId", "in", stateIdForMeCanAudit); + if (StringUtils.isNotBlank(keySearchWord)) { + SqlExpressionGroup seg = new SqlExpressionGroup(); + seg.orLike("mi.meetingName", keySearchWord); + seg.orLike("mi.location", keySearchWord); + cnd.and(seg); + } + if (StrUtil.isNotBlank(typeId)) { + cnd.andEX("info.typeId", "=", typeId); + } + if (meetingTime != null) { + if (meetingTime == 0) { + cnd.andEX("info.startTime", ">=", DateUtil.getDateTime()); + } else { + cnd.andEX("info.startTime", "<", DateUtil.getDateTime()); + } + } + cnd.groupBy("info.id"); + cnd.desc("startTime"); + + sql.setCondition(cnd); + Pagination pagination = listPage(pageNumber, pageSize, sql); + List list = pagination.getList(); + list.forEach(item -> { + Record map = (Record) item; + List canAuditUserByLoginUser = this.getCanAuditUserByLoginUser(map.getString("id"), stateIdForMeCanAudit); + map.put("no_audit", canAuditUserByLoginUser.size()); + }); + return pagination; + } + + @Override + public List getLeaveUser(String meetingId, String moduleName, String auditType) { + Sql sqlMeeting = Sqls.create(""" + select + id, + startTime, + endTime, + timePeriodName + from + meeting_time_period + $condition + """); + Cnd cndMeeting = Cnd.NEW(); + cndMeeting.and("meetingId", "=", meetingId); + sqlMeeting.setCondition(cndMeeting); + List meetingList = listMap(sqlMeeting); + + List list = "canAudit".equals(auditType) ? this.getCanAuditUserByLoginUser(meetingId, meetingCommonService.findStateIdForMeCanAudit()) : this.getHasAuditUserByLoginUser(meetingId); + + //将list分组 + LinkedHashMap> map = new LinkedHashMap<>(); + list.forEach(item -> { + String meetingTimePeriodId = item.getString("meetingTimePeriodId"); + if (map.containsKey(meetingTimePeriodId)) { + map.get(meetingTimePeriodId).add(item); + } else { + List listMap = new ArrayList<>(); + listMap.add(item); + map.put(meetingTimePeriodId, listMap); + } + }); + + //汇总数据 + meetingList.forEach(item -> { + String id = item.getString("id"); + item.put("users", map.get(id)); + }); + return meetingList; + } + + @Override + @Aop(TransAop.READ_COMMITTED) + public void audit(String[] tpuIds, Audit audit, boolean isPass) { + dao().insert(audit); + for (String tp_id : tpuIds) { + MeetingTimePeriodUser periodUser = dao().fetch(MeetingTimePeriodUser.class, tp_id); + Integer stateCode = periodUser.getStateCode(); + + Integer afterStateCode = meetingCommonService.findAfterStateCode(stateCode, isPass); + periodUser.setStateCode(afterStateCode); + + List auditIdList = periodUser.getAuditIdList(); + if (Lang.isEmpty(auditIdList)) { + auditIdList = new ArrayList<>(); + } + auditIdList.add(NutMap.NEW().addv("stateCode", stateCode).addv("auditId", audit.getId()).addv("auditUser", ShiroUtil.getPrincipalProperty("id")).addv("auditState", isPass)); + periodUser.setAuditIdList(auditIdList); + + if (!isPass) { + periodUser.setIsJoin(null); + periodUser.setLeaveReason(null); + } + dao().update(periodUser); + } + } + + /*根据当前登录用户获取可以审核的用户*/ + public List getCanAuditUserByLoginUser(String meetingId, List stateIdForMeCanAudit) { + Sql sql = Sqls.create(""" + SELECT + mu.*, + u.username, + u.loginname, + u.sex, + u.unionid, + u.unitid, + u.unitname, + state.auditAfterType + FROM + meeting_time_period_user mu + left join `user` u on mu.userId=u.id + LEFT JOIN audit_state state on state.stateId = mu.stateCode + $condition + """); + Cnd cnd = Cnd.NEW(); + cnd.andEX("mu.meetingId", "=", meetingId); + cnd.and("u.username", "is not", null); + cnd.and("u.username", "!=", ""); + cnd.and("mu.stateCode", "in", Sqls.createf("SELECT stateId FROM audit_state_user WHERE userid = '%s'", ShiroUtil.getPrincipalProperty("id"))); + cnd.and("state.stateAuditType", "in", Lang.list(AuditTypeEnum.AUDIT.getValue())); + + List audit = dao().query(AuditState.class, Cnd.where("stateId", "in", stateIdForMeCanAudit)); + SqlExpressionGroup sqlExpressionGroup = new SqlExpressionGroup(); + audit.forEach(v -> { + if (StrUtil.isNotBlank(v.getAuditAfterType())) { + CustomAuditTypeHandle instance = Enum.instance(CustomAuditTypeHandle.class, v.getAuditAfterType()); + if(instance != null) { + instance.next(sqlExpressionGroup); + } + } + }); + if (!sqlExpressionGroup.isEmpty()) { + cnd.and(sqlExpressionGroup); + } + sql.setCondition(cnd); + return listMap(sql); + } + + /*根据当前登录用户获取已经审核的用户*/ + public List getHasAuditUserByLoginUser(String meetingId) { + Sql sql = Sqls.create(""" + select + mu.*, + u.username, + u.loginname, + u.unitname + from + meeting_time_period_user mu + left join + `user` u on mu.userid=u.id + $condition + """); + Cnd cnd = Cnd.NEW(); + cnd.and("meetingId", "=", meetingId); + cnd.and(new Static("JSON_CONTAINS(auditIdList, JSON_OBJECT('auditUser', '" + ShiroUtil.getPrincipalProperty("id") + "'))")); + + sql.setCondition(cnd); + List list = listMap(sql); + list.forEach(item -> { + List listMap = Json.fromJsonAsList(NutMap.class, item.getString("auditIdList")); + Boolean auditState = false; + for (NutMap map : listMap) { + if (map.getString("auditUser").equals(ShiroUtil.getPrincipalProperty("id"))) { + auditState = map.get("auditState") == null ? null : map.getBoolean("auditState"); + break; + } + } + item.put("auditState", auditState); + }); + return list; + } +} diff --git a/src/main/java/io/v/nutz/meeting/service/impl/MeetingBasicStatisticsServiceImpl.java b/src/main/java/io/v/nutz/meeting/service/impl/MeetingBasicStatisticsServiceImpl.java new file mode 100644 index 0000000..ea46f2b --- /dev/null +++ b/src/main/java/io/v/nutz/meeting/service/impl/MeetingBasicStatisticsServiceImpl.java @@ -0,0 +1,265 @@ +package io.v.nutz.meeting.service.impl; + +import cn.wizzer.framework.page.Pagination; +import io.v.nutz.base.utils.Roles; +import io.v.nutz.jdh.model.cb.Jdh_jdhxx; +import io.v.nutz.meeting.models.MeetingInfo; +import io.v.nutz.meeting.models.MeetingType; +import io.v.nutz.meeting.service.MeetingBasicStatisticsService; +import io.v.nutz.meeting.service.MeetingCommonService; +import io.v.nutz.modules.models.PageForm; +import io.v.nutz.service.impl.ViServiceImpl; +import io.v.nutz.workersCongress.model.WorkersCongressSession; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.dao.Sqls; +import org.nutz.dao.sql.Sql; +import org.nutz.dao.util.cri.Static; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.lang.Strings; +import org.nutz.lang.util.NutMap; + +import java.util.List; + +@IocBean(args = {"refer:dao"}) +public class MeetingBasicStatisticsServiceImpl extends ViServiceImpl implements MeetingBasicStatisticsService { + + @Inject + private MeetingCommonService meetingCommonService; + + + public MeetingBasicStatisticsServiceImpl(Dao dao) { + super(dao); + } + + @Override + public Pagination pageData(PageForm pageForm, String meetingId, String[] meetingTime, String typeId) { + Cnd cnd = Cnd.NEW(); + Sql sql = Sqls.create(""" + SELECT + info.* , + type.moduleName, + type.meetingTypeName + FROM + meeting_info info + LEFT JOIN meeting_type type ON type.id = info.typeId + $condition + """); + if (meetingTime != null && meetingTime.length == 2) { + cnd.and("info.startTime", ">=", meetingTime[0]); + cnd.and("info.startTime", "<=", meetingTime[1]); + } + cnd.andEX("info.id", "=", meetingId); + cnd.andEX("type.id", "=", typeId); + cnd.desc("startTime"); + sql.setCondition(cnd); + Pagination list = list(pageForm, sql); + List listList = list.getList(); + //获取教代会最新届次 + Jdh_jdhxx jdh = dao().fetch(Jdh_jdhxx.class, Cnd.where("jdhkqzt", "=", 1).desc("jdhkqsj")); + //获取教代会最新届次 + WorkersCongressSession gdh = dao().fetch(WorkersCongressSession.class, Cnd.where("startState", "=", 1).desc("startTime")); + + listList.forEach(v -> { + MeetingType meetingType = dao().fetch(MeetingType.class, v.getInt("typeId")); + String successStateCode = meetingCommonService.findSuccessStateCode(v.getString("id")).toString(); + if ("teacherMeeting".equals(meetingType.getModuleName())) { + Cnd cndChild = Cnd.NEW(); + Sql sqlChild = Sqls.create(""" + SELECT + mtp.*, + info.meetingName, + type.meetingTypeName, + count( mtpu.id ) AS total, + sum( CASE WHEN mtpu.isJoin IS null OR (mtpu.isJoin is NOT null AND mtpu.stateCode != @successStateCode) THEN 1 ELSE 0 END ) AS joinNum, + sum( CASE WHEN mtpu.isJoin = 0 AND mtpu.stateCode = @successStateCode THEN 1 ELSE 0 END ) AS notJoinNum, + sum( CASE WHEN mtpu.isSignIn = 1 THEN 1 ELSE 0 END ) AS signJoinNum, + sum( CASE WHEN (mtpu.isJoin IS null AND mtpu.isSignIn is null) OR (mtpu.isJoin is NOT null AND mtpu.stateCode !=@successStateCode AND mtpu.isSignIn is null) THEN 1 ELSE 0 END ) AS notJoinNullNum, + sum( CASE WHEN (mtpu.isSignIn = 1) AND (d.roleid = @zsdb) AND d.jdhid = @jdhId THEN 1 ELSE 0 END) as zsdbNum, + sum( CASE WHEN (mtpu.isSignIn = 1) AND (d.roleid = @lxdb) AND d.jdhid = @jdhId THEN 1 ELSE 0 END) as lxdbNum + FROM + meeting_time_period mtp + LEFT JOIN meeting_info info ON mtp.meetingId = info.id + LEFT JOIN meeting_type type ON type.id = info.typeId + LEFT JOIN meeting_time_period_user mtpu ON mtpu.meetingTimePeriodId = mtp.id + LEFT JOIN jdh_db d on d.dbid = mtpu.userId and jdhid=@jdhId + $condition + """).setParam("successStateCode", successStateCode) + .setParam("zsdb", Roles.ZSDB) + .setParam("lxdb", Roles.LXDB) + .setParam("jdhId", jdh.getId()); + cndChild.and("mtp.meetingId", "=", v.getString("id")); + cndChild.desc("mtp.startTime"); + cndChild.groupBy("mtp.id"); + sqlChild.setCondition(cndChild); + List childList = listMap(sqlChild); + v.put("meetingTimeData", childList); + + }else if ("gdh".equals(meetingType.getModuleName())) { + Cnd cndChild = Cnd.NEW(); + Sql sqlChild = Sqls.create(""" + SELECT + mtp.*, + info.meetingName, + type.meetingTypeName, + count( mtpu.id ) AS total, + sum( CASE WHEN mtpu.isJoin IS null OR (mtpu.isJoin is NOT null AND mtpu.stateCode != @successStateCode) THEN 1 ELSE 0 END ) AS joinNum, + sum( CASE WHEN mtpu.isJoin = 0 AND mtpu.stateCode = @successStateCode THEN 1 ELSE 0 END ) AS notJoinNum, + sum( CASE WHEN mtpu.isSignIn = 1 THEN 1 ELSE 0 END ) AS signJoinNum, + sum( CASE WHEN (mtpu.isJoin IS null AND mtpu.isSignIn is null) OR (mtpu.isJoin is NOT null AND mtpu.stateCode !=@successStateCode AND mtpu.isSignIn is null) THEN 1 ELSE 0 END ) AS notJoinNullNum, + sum( CASE WHEN (mtpu.isSignIn = 1) AND (d.roleId = @zsdb) AND d.gdh_id = @gdhId THEN 1 ELSE 0 END) as zsdbNum, + sum( CASE WHEN (mtpu.isSignIn = 1) AND (d.roleId = @lxdb) AND d.gdh_id = @gdhId THEN 1 ELSE 0 END) as lxdbNum + FROM + meeting_time_period mtp + LEFT JOIN meeting_info info ON mtp.meetingId = info.id + LEFT JOIN meeting_type type ON type.id = info.typeId + LEFT JOIN meeting_time_period_user mtpu ON mtpu.meetingTimePeriodId = mtp.id + LEFT JOIN workers_congress_representative d on d.userId = mtpu.userId and gdh_id=@gdhId + $condition + """).setParam("successStateCode", successStateCode) + .setParam("zsdb", Roles.GDHDB) + .setParam("lxdb", Roles.GDHLX) + .setParam("gdhId", gdh.getId()); + cndChild.and("mtp.meetingId", "=", v.getString("id")); + cndChild.desc("mtp.startTime"); + cndChild.groupBy("mtp.id"); + sqlChild.setCondition(cndChild); + List childList = listMap(sqlChild); + v.put("meetingTimeData", childList); + + } + + + }); + return list; + } + + @Override + public Object getMeetingList(String[] meetingTime) { + Cnd cnd = Cnd.NEW(); + if (meetingTime != null && meetingTime.length == 2) { + cnd.and("startTime", ">=", meetingTime[0]); + cnd.and("startTime", "<=", meetingTime[1]); + + } + cnd.desc("startTime"); + return dao().query(MeetingInfo.class, cnd); + } + + @Override + public Object getUserData(PageForm pageForm, String id, String meetingId, String type, String unitId, String unionId) { + String successStateCode = meetingCommonService.findSuccessStateCode(meetingId).toString(); + MeetingInfo meetingInfo = dao().fetch(MeetingInfo.class, meetingId); + MeetingType meetingType = dao().fetch(MeetingType.class, meetingInfo.getTypeId()); + + Cnd cnd = Cnd.NEW(); + Sql sql = Sqls.create(""" + SELECT + mtp.*, + u.loginname AS loginName, + u.username AS userName, + u.unitname AS unitName, + u.unionname AS unionName + FROM + meeting_time_period_user mtp + LEFT JOIN `user` u ON mtp.userId = u.id + $condition + """); + if (type.equals("notJoinNum")) { + cnd.and("mtp.isJoin", "=", 0); + } else if (type.equals("signJoinNum")) { + cnd.and("mtp.isSignIn", "=", 1); + } else if (type.equals("notJoinNullNum")) { + cnd.and(new Static("(mtp.isJoin IS null AND mtp.isSignIn is null AND mtp.meetingTimePeriodId = '%s') OR (mtp.isJoin is NOT null AND mtp.stateCode !='%s' AND mtp.isSignIn is null AND mtp.meetingTimePeriodId = '%s')".formatted(id, successStateCode, id))); + } + if ("teacherMeeting".equals(meetingType.getModuleName())) { + //获取教代会最新届次 + Jdh_jdhxx jdh = dao().fetch(Jdh_jdhxx.class, Cnd.where("jdhkqzt", "=", 1).desc("jdhkqsj")); + if (type.equals("zsdbNum")) { + cnd.and("mtp.isSignIn", "=", 1); + cnd.and(new Static(" mtp.userId in (select dbid from jdh_db where jdhid='%s' and roleid='%s')".formatted(jdh.getId(), Roles.ZSDB))); + } else if (type.equals("lxdbNum")) { + cnd.and("mtp.isSignIn", "=", 1); + cnd.and(new Static(" mtp.userId in (select dbid from jdh_db where jdhid='%s' and roleid='%s')".formatted(jdh.getId(), Roles.LXDB))); + } + } else if ("gdh".equals(meetingType.getModuleName())) { + //获取教代会最新届次 + WorkersCongressSession jdh = dao().fetch(WorkersCongressSession.class, Cnd.where("startState", "=", 1).desc("startTime")); + if (type.equals("zsdbNum")) { + cnd.and("mtp.isSignIn", "=", 1); + cnd.and(new Static(" mtp.userId in (select userId from workers_congress_representative where gdh_id='%s' and roleId='%s')".formatted(jdh.getId(), Roles.GDHDB))); + } else if (type.equals("lxdbNum")) { + cnd.and("mtp.isSignIn", "=", 1); + cnd.and(new Static(" mtp.userId in (select userId from workers_congress_representative where gdh_id='%s' and roleId='%s')".formatted(jdh.getId(), Roles.GDHLX))); + } + } + + cnd.andEX("u.unitid", "=", unitId); + cnd.andEX("u.unionid", "=", unionId); + if (Strings.isNotBlank(pageForm.getSearchName()) && Strings.isNotBlank(pageForm.getSearchKeyword())) { + cnd.where().andLike(pageForm.getSearchName(), pageForm.getSearchKeyword()); + } + cnd.and("mtp.meetingTimePeriodId", "=", id); + cnd.desc("u.unitid"); + sql.setCondition(cnd); + + return list(pageForm, sql); + } + + @Override + public List getUserData(String id, String meetingId, String type) { + String successStateCode = meetingCommonService.findSuccessStateCode(meetingId).toString(); + MeetingInfo meetingInfo = dao().fetch(MeetingInfo.class, meetingId); + MeetingType meetingType = dao().fetch(MeetingType.class, meetingInfo.getTypeId()); + + Cnd cnd = Cnd.NEW(); + Sql sql = Sqls.create(""" + SELECT + mtp.*, + u.loginname AS loginName, + u.username AS userName, + u.unitname AS unitName, + u.unionname AS unionName + FROM + meeting_time_period_user mtp + LEFT JOIN `user` u ON mtp.userId = u.id + $condition + """); + cnd.and("mtp.meetingTimePeriodId", "=", id); + if (type.equals("请假人数")) { + cnd.and("mtp.isJoin", "=", 0).and("mtp.stateCode", "=", successStateCode); + } else if (type.equals("实到人数")) { + cnd.and("mtp.isSignIn", "=", 1); + } else if (type.equals("未到人数")) { + cnd.and(new Static("(mtp.isJoin IS null AND mtp.isSignIn is null and mtp.meetingTimePeriodId ='%s') OR (mtp.isJoin is NOT null AND mtp.stateCode !='%s' AND mtp.isSignIn is null and mtp.meetingTimePeriodId ='%s')".formatted(id, successStateCode, id))); + } + if ("teacherMeeting".equals(meetingType.getModuleName())) { + //获取教代会最新届次 + Jdh_jdhxx jdh = dao().fetch(Jdh_jdhxx.class, Cnd.where("jdhkqzt", "=", 1).desc("jdhkqsj")); + if (type.equals("正式代表")) { + cnd.and("mtp.isSignIn", "=", 1); + cnd.and(new Static(" mtp.userId in (select dbid from jdh_db where jdhid='%s' and roleid='%s')".formatted(jdh.getId(), Roles.ZSDB))); + } else if (type.equals("列席代表")) { + cnd.and("mtp.isSignIn", "=", 1); + cnd.and(new Static(" mtp.userId in (select dbid from jdh_db where jdhid='%s' and roleid='%s')".formatted(jdh.getId(), Roles.LXDB))); + } + } else if ("gdh".equals(meetingType.getModuleName())) { + //获取教代会最新届次 + WorkersCongressSession jdh = dao().fetch(WorkersCongressSession.class, Cnd.where("startState", "=", 1).desc("startTime")); + if (type.equals("正式代表")) { + cnd.and("mtp.isSignIn", "=", 1); + cnd.and(new Static(" mtp.userId in (select userId from workers_congress_representative where gdh_id='%s' and roleId='%s')".formatted(jdh.getId(), Roles.GDHDB))); + } else if (type.equals("列席代表")) { + cnd.and("mtp.isSignIn", "=", 1); + cnd.and(new Static(" mtp.userId in (select userId from workers_congress_representative where gdh_id='%s' and roleId='%s')".formatted(jdh.getId(), Roles.GDHLX))); + } + } + + + sql.setCondition(cnd); + return listMap(sql); + } + + +} diff --git a/src/main/java/io/v/nutz/meeting/service/impl/MeetingCommonServiceImpl.java b/src/main/java/io/v/nutz/meeting/service/impl/MeetingCommonServiceImpl.java new file mode 100644 index 0000000..47770d5 --- /dev/null +++ b/src/main/java/io/v/nutz/meeting/service/impl/MeetingCommonServiceImpl.java @@ -0,0 +1,137 @@ +package io.v.nutz.meeting.service.impl; + +import cn.hutool.core.util.StrUtil; +import io.v.nutz.base.utils.Roles; +import io.v.nutz.base.utils.Vi; +import io.v.nutz.constant.AuditTypeEnum; +import io.v.nutz.meeting.models.MeetingInfo; +import io.v.nutz.meeting.models.MeetingType; +import io.v.nutz.meeting.service.MeetingCommonService; +import io.v.nutz.modules.models.AuditState; +import io.v.nutz.service.impl.ViServiceImpl; +import io.v.nutz.util.ShiroUtil; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.dao.Sqls; +import org.nutz.dao.sql.Sql; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.lang.Lang; +import org.nutz.lang.util.NutMap; + +import java.util.Comparator; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +@IocBean(args = {"refer:dao"}) +public class MeetingCommonServiceImpl extends ViServiceImpl implements MeetingCommonService { + + @Inject + private Vi vi; + + public MeetingCommonServiceImpl(Dao dao) { + super(dao); + } + + @Override + public String findModuleNameByTypeId(Integer typeId) { + MeetingType meetingType = dao().fetch(MeetingType.class, Cnd.where("id", "=", typeId)); + if (Lang.isNotEmpty(meetingType)) { + return meetingType.getModuleName(); + } + return null; + } + + @Override + public String findModuleNameByMeetingId(String meetingId) { + MeetingInfo meetingInfo = dao().fetch(MeetingInfo.class, meetingId); + if (Lang.isNotEmpty(meetingInfo) && null != meetingInfo.getTypeId()) { + return findModuleNameByTypeId(meetingInfo.getTypeId()); + } + return null; + } + + @Override + public Integer findStartStateCodeByModuleName(String moduleName) { + List auditStateList = dao().query(AuditState.class, Cnd.where("module", "=", moduleName).asc("stateId")); + Optional minOptional = auditStateList.stream().map(v -> v.getStateId()).min(Comparator.comparingInt(a -> a)).stream().findFirst(); + return minOptional.orElse(null); + } + + @Override + public Integer findAfterStateCode(Integer stateCode, boolean isPass) { + AuditState auditState = dao().fetch(AuditState.class, stateCode); + return isPass ? auditState.getAfterPassStateId() : auditState.getAfterRejectStateId(); + } + + @Override + public Integer findSuccessStateCode(String meetingId) { + String moduleName = findModuleNameByMeetingId(meetingId); + Cnd cnd = Cnd.where("module", "=", moduleName).and("stateAuditType", "=", AuditTypeEnum.PROCESS_PASS.getValue()); + AuditState auditState = dao().fetch(AuditState.class, cnd); + return auditState.getStateId(); + } + + @Override + public Integer findAfterStateCodeByModuleName(Integer stateCode, String moduleName) { + AuditState auditState = dao().fetch(AuditState.class, Cnd.where("stateId", "=", stateCode)); + if (Lang.isNotEmpty(auditState)) { + return auditState.getAfterStateId(); + } + return null; + } + + @Override + public Object find() { + List typeList = dao().query(MeetingType.class, null); + List typeModuleNameList = typeList.stream().map(MeetingType::getModuleName).collect(Collectors.toList()); + + List auditStateList = dao().query(AuditState.class, Cnd.where("module", "in", typeModuleNameList)); + + for (AuditState state : auditStateList) { + dao().fetchLinks(state, "auditStateUserList"); + } + + return null; + } + + @Override + public List findStateIdForMeCanAudit() { + Cnd cnd = Cnd.NEW(); + cnd.and("stateId", "in", Sqls.create("select stateId from audit_state_user where userId = @userid").setParam("userid", ShiroUtil.getPrincipalProperty("id"))); + List auditStateList = dao().query(AuditState.class, cnd); + return auditStateList.stream().map(v -> v.getStateId()).collect(Collectors.toList()); + } + + /*根据状态码获取下一节点审核人*/ + @Override + public List getNextAuditUser(Integer stateCode, String moduleName) { + + Sql sql = Sqls.create(""" + select + au.userId as userId, + u.username as userName, + u.loginname as loginName, + u.unitname as unitName, + u.mobile as mobile + from + audit_state_user au + left join + `user` u on au.userid = u.id + $condition + """); + Cnd cnd = Cnd.NEW(); + + cnd.and("stateId", "=", stateCode); + + if (StrUtil.isNotBlank(moduleName) && "teacherMeeting".equals(moduleName) && stateCode == 4910) { + String jdhid = vi.getLastTeacherMeetingId(); + String dbtId = vi.getLastDelegationId(ShiroUtil.getPrincipalProperty("id").toString()); + cnd.and("userId", "in", Sqls.createf("select userid from sys_user_role where jdhid='%s' and roleid='%s' and dbtId='%s'", jdhid, Roles.DBT_TZ, dbtId)); + } + sql.setCondition(cnd); + + return listMap(sql); + } +} diff --git a/src/main/java/io/v/nutz/meeting/service/impl/MeetingManageServiceImpl.java b/src/main/java/io/v/nutz/meeting/service/impl/MeetingManageServiceImpl.java new file mode 100644 index 0000000..32a8d69 --- /dev/null +++ b/src/main/java/io/v/nutz/meeting/service/impl/MeetingManageServiceImpl.java @@ -0,0 +1,241 @@ +package io.v.nutz.meeting.service.impl; + +import cn.hutool.core.util.StrUtil; +import cn.wizzer.framework.page.Pagination; +import io.v.nutz.meeting.models.MeetingInfo; +import io.v.nutz.meeting.models.MeetingTimePeriod; +import io.v.nutz.meeting.models.MeetingTimePeriodUser; +import io.v.nutz.meeting.models.MeetingUser; +import io.v.nutz.meeting.service.MeetingManageService; +import io.v.nutz.modules.models.PageForm; +import io.v.nutz.msgNotify.models.MsgNotify; +import io.v.nutz.msgNotify.models.MsgNotifyUser; +import io.v.nutz.msgNotify.service.MsgNotifyService; +import io.v.nutz.service.impl.ViServiceImpl; +import org.nutz.aop.interceptor.ioc.TransAop; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.dao.Sqls; +import org.nutz.dao.sql.Sql; +import org.nutz.dao.util.cri.SqlExpressionGroup; +import org.nutz.ioc.aop.Aop; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.lang.Lang; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +@IocBean(args = {"refer:dao"}) +public class MeetingManageServiceImpl extends ViServiceImpl implements MeetingManageService { + + @Inject + private MsgNotifyService msgNotifyService; + + + public MeetingManageServiceImpl(Dao dao) { + super(dao); + } + + @Override + public Pagination pageData(PageForm pageForm, Cnd cnd) { + Sql sql = Sqls.create(""" + SELECT + info.*, + type.meetingTypeName + FROM + meeting_info info + LEFT JOIN meeting_type type ON type.id = info.typeId + $condition + """); + if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) { + SqlExpressionGroup seg = new SqlExpressionGroup(); + seg.orLike("info.meetingName", pageForm.getSearchKeyword()); + seg.orLike("info.location", pageForm.getSearchKeyword()); + cnd.and(seg); + } + cnd.desc("info.startTime"); + sql.setCondition(cnd); + return list(pageForm, sql); + } + + @Override + @Aop(TransAop.READ_COMMITTED) + public void doAdd(MeetingInfo meetingInfo) { + dao().insertWith(meetingInfo, "^meetingUserList|meetingTimePeriods$"); + + insertMeetingTimePeriodUser(meetingInfo.getId(), meetingInfo.getMeetingTimePeriods(), meetingInfo.getMeetingUserList()); + } + + /** + * 插入会议时间段 - 用户 数据 + * + * @param meetingId 会议id + * @param meetingTimePeriods 时间段 + * @param meetingUserList 人员 + */ + private void insertMeetingTimePeriodUser(String meetingId, List meetingTimePeriods, List meetingUserList) { + if (Lang.isNotEmpty(meetingTimePeriods) && Lang.isNotEmpty(meetingUserList)) { + ArrayList insertList = new ArrayList<>(); + for (MeetingTimePeriod timePeriod : meetingTimePeriods) { + for (MeetingUser meetingUser : meetingUserList) { + MeetingTimePeriodUser timePeriodUser = new MeetingTimePeriodUser(); + timePeriodUser.setMeetingId(meetingId); + timePeriodUser.setMeetingTimePeriodId(timePeriod.getId()); + timePeriodUser.setUserId(meetingUser.getUserId()); + insertList.add(timePeriodUser); + } + } + dao().insert(insertList); + } + } + + + @Override + @Aop(TransAop.READ_COMMITTED) + public void doEdit(MeetingInfo meetingInfo, List userIds) { + //原来的人 + List oldMus = dao().query(MeetingUser.class, Cnd.where("meetingId", "=", meetingInfo.getId())); + List oldMuIds = oldMus.stream().map(v -> v.getUserId()).collect(Collectors.toList()); + + //新增的人 + List newMuIds = userIds.stream().filter(v -> !oldMuIds.contains(v)).collect(Collectors.toList()); + + + //新增的人 +// List newMus = dao().query(MeetingUser.class, Cnd.where("userid", "not in", userIds).and("meetingId","=",meetingInfo.getId())); +// List newMuIds = newMus.stream().map(v -> v.getUserId()).collect(Collectors.toList()); + + //新增的时间段 + List newTps = meetingInfo.getMeetingTimePeriods().stream().filter(v -> StrUtil.isBlank(v.getId())).collect(Collectors.toList()); + + //原来的用户 +// List oldMus = dao().query(MeetingUser.class, Cnd.where("meetingId", "=", meetingInfo.getId())); +// List oldMuIds = oldMus.stream().map(v -> v.getUserId()).collect(Collectors.toList()); + + dao().update(meetingInfo); + + //删除改删得记录 + dao().clear(MeetingUser.class, Cnd.where("userid", "not in", userIds).and("meetingId", "=", meetingInfo.getId())); + dao().clear(MeetingTimePeriodUser.class, Cnd.where("userid", "not in", userIds).and("meetingId", "=", meetingInfo.getId())); + List meetingTimePeriods = meetingInfo.getMeetingTimePeriods(); + List meetingTimePeriodIds = meetingTimePeriods.stream().map(v -> v.getId()).collect(Collectors.toList()); + dao().clear(MeetingTimePeriod.class, Cnd.where("id", "not in", meetingTimePeriodIds).and("meetingId", "=", meetingInfo.getId())); +// dao().clear(MeetingTimePeriodUser.class, Cnd.where("meetingTimePeriodId", "not in", meetingTimePeriodIds)); + + dao().clear(MeetingUser.class, Cnd.where("meetingId", "=", meetingInfo.getId())); + ArrayList insertMuList = new ArrayList<>(); + userIds.forEach(v -> { + MeetingUser meetingUser = new MeetingUser(); + meetingUser.setMeetingId(meetingInfo.getId()); + meetingUser.setUserId(v); + insertMuList.add(meetingUser); + }); + dao().insert(insertMuList); + + List mTps = meetingInfo.getMeetingTimePeriods(); + for (MeetingTimePeriod mTp : mTps) { + if (StrUtil.isNotBlank(mTp.getId())) { + dao().update(mTp); + List mtpus = new ArrayList<>(); + if (Lang.isNotEmpty(newMuIds)) { + //旧时间段 并且 有新增的人员 + for (String newMuId : newMuIds) { + MeetingTimePeriodUser mtpu = new MeetingTimePeriodUser(); + mtpu.setMeetingId(meetingInfo.getId()); + mtpu.setUserId(newMuId); + mtpu.setMeetingTimePeriodId(mTp.getId()); + mtpus.add(mtpu); + } + dao().insert(mtpus); + } else { + //旧时间段 并且 没有新增的人员 不用管 + } + } else { + //有新增的时间段 + mTp.setMeetingId(meetingInfo.getId()); + dao().insert(mTp); + List mtpus = new ArrayList<>(); + for (String userId : userIds) { + MeetingTimePeriodUser mtpu = new MeetingTimePeriodUser(); + mtpu.setMeetingId(meetingInfo.getId()); + mtpu.setUserId(userId); + mtpu.setMeetingTimePeriodId(mTp.getId()); + mtpus.add(mtpu); + } + dao().insert(mtpus); + + + /*if (Lang.isEmpty(newMus)) { + //有新增的时间段 但是 没有新增的人员 + for (String oldMuUserId : oldMuIds) { + MeetingTimePeriodUser mtpu = new MeetingTimePeriodUser(); + mtpu.setMeetingId(meetingInfo.getId()); + mtpu.setUserId(oldMuUserId); + mtpu.setMeetingTimePeriodId(mTp.getId()); + mtpus.add(mtpu); + } + dao().insert(mtpus); + } else { + //有新增的时间段 并且 有新增的人员 + for (String userId : userIds) { + MeetingTimePeriodUser mtpu = new MeetingTimePeriodUser(); + mtpu.setMeetingId(meetingInfo.getId()); + mtpu.setUserId(userId); + mtpu.setMeetingTimePeriodId(mTp.getId()); + mtpus.add(mtpu); + } + dao().insert(mtpus); + }*/ + } + } + + +// //更新会议 +// dao().update(meetingInfo); +// +// //新会议人员 +// List meetingUserList = meetingInfo.getMeetingUserList(); +// List meetingUserIdList = meetingUserList.stream().map(MeetingUser::getId).collect(Collectors.toList()); +// //删除不在新会议人员里面的数据 +// dao().clear(MeetingUser.class, Cnd.where("id", "not in", meetingUserIdList)); +// //批量更新 效率更高 +// for (MeetingUser meetingUser : meetingUserList) { +// meetingUser.setMeetingId(meetingInfo.getId()); +// } +// dao().insertOrUpdate(meetingUserList); +// +// +// //会议时间段 +// List meetingTimePeriods = meetingInfo.getMeetingTimePeriods(); +// List meetingTimePeriodIds = meetingTimePeriods.stream().map(MeetingTimePeriod::getId).collect(Collectors.toList()); +// //删除会议时间段 - 用户 记录 +// dao().clear(MeetingTimePeriodUser.class, Cnd.where("meetingTimePeriodId", "not in", meetingTimePeriodIds)); +// dao().clear(MeetingTimePeriodUser.class, Cnd.where("userId", "not in", meetingUserList.stream().map(v -> v.getUserId()).collect(Collectors.toList()))); +// +// //新增的时间段 +// List newTimePeriod = meetingInfo.getMeetingTimePeriods().stream().filter(v -> StrUtil.isBlank(v.getId())).collect(Collectors.toList()); +// insertMeetingTimePeriodUser(meetingInfo.getId(), newTimePeriod, meetingInfo.getMeetingUserList()); +// +// //需要编辑的时间段 +// List editTimePeriod = meetingInfo.getMeetingTimePeriods().stream().filter(v -> StrUtil.isBlank(v.getId())).collect(Collectors.toList()); +// dao().update(editTimePeriod); + + + } + + + @Override + @Aop(TransAop.READ_COMMITTED) + public void doDelete(String id) { + MeetingInfo meetingInfo = dao().fetch(MeetingInfo.class, id); + dao().clear(MeetingUser.class, Cnd.where("meetingId", "=", id)); + dao().clear(MeetingTimePeriod.class, Cnd.where("meetingId", "=", id)); + dao().clear(MeetingTimePeriodUser.class, Cnd.where("meetingId", "=", id)); + dao().clear(MsgNotify.class, Cnd.where("id", "=", meetingInfo.getNotifyId())); + dao().clear(MsgNotifyUser.class, Cnd.where("titleId", "=", meetingInfo.getNotifyId())); + dao().delete(meetingInfo); + + } +} diff --git a/src/main/java/io/v/nutz/meeting/service/impl/MeetingMineServiceImpl.java b/src/main/java/io/v/nutz/meeting/service/impl/MeetingMineServiceImpl.java new file mode 100644 index 0000000..0b11dbe --- /dev/null +++ b/src/main/java/io/v/nutz/meeting/service/impl/MeetingMineServiceImpl.java @@ -0,0 +1,141 @@ +package io.v.nutz.meeting.service.impl; + +import cn.hutool.core.util.StrUtil; +import cn.wizzer.framework.page.Pagination; +import io.v.nutz.meeting.models.MeetingInfo; +import io.v.nutz.meeting.models.MeetingTimePeriodUser; +import io.v.nutz.meeting.service.MeetingCommonService; +import io.v.nutz.meeting.service.MeetingMineService; +import io.v.nutz.modules.models.PageForm; +import io.v.nutz.msgNotify.service.MsgNotifyService; +import io.v.nutz.service.impl.ViServiceImpl; +import io.v.nutz.web.commons.utils.ShiroUtil; +import org.nutz.dao.Chain; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.dao.Sqls; +import org.nutz.dao.sql.Sql; +import org.nutz.dao.util.cri.SqlExpressionGroup; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.lang.util.NutMap; + +import java.util.Date; +import java.util.List; + +@IocBean(args = {"refer:dao"}) +public class MeetingMineServiceImpl extends ViServiceImpl implements MeetingMineService { + + @Inject + private MeetingCommonService meetingCommonService; + + @Inject + private MsgNotifyService msgNotifyService; + + public MeetingMineServiceImpl(Dao dao) { + super(dao); + } + + @Override + public Pagination myMeetingPageData(PageForm pageForm, Cnd cnd) { + String userId = (String) ShiroUtil.getPrincipalProperty("id"); + Sql sql = Sqls.create(""" + SELECT + info.*, + type.meetingTypeName, + mu.isJoin, + state.stateName, + state.stateColor, + mu.stateCode, + (select stateId from audit_state where module = type.moduleName order by stateId asc limit 1) firstState + FROM + meeting_info info + LEFT JOIN meeting_type type ON type.id = info.typeId + LEFT JOIN meeting_time_period_user mu ON mu.meetingId = info.id + LEFT JOIN audit_state state on state.stateId = mu.stateCode + $condition + """); + Sql userSql = Sqls.create("select meetingId from meeting_time_period_user where userId = @userId").setParam("userId", userId); + cnd.and("info.id", "in", userSql); + + cnd.and("mu.userid", "=", userId); + + if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) { + SqlExpressionGroup seg = new SqlExpressionGroup(); + seg.orLike("meetingName", pageForm.getSearchKeyword()); + seg.orLike("location", pageForm.getSearchKeyword()); + cnd.and(seg); + } + + cnd.groupBy("info.id"); + cnd.desc("info.startTime"); + sql.setCondition(cnd); + Pagination list = list(pageForm, sql); + return list; + } + + @Override + public List findMyMeetPeriod(String meetingId) { + Sql sql = Sqls.create(""" + SELECT + tp.id AS tpid, + tp.startTime, + tp.endTime, + tp.timePeriodName, + tpu.*, + tpu.id as tpuId, + state.stateName, + state.stateAuditType + FROM + meeting_time_period tp + LEFT JOIN meeting_time_period_user tpu ON tpu.meetingTimePeriodId = tp.id + LEFT JOIN audit_state state on state.stateId = tpu.stateCode + $condition + """); + Cnd cnd = Cnd.NEW(); + cnd.and("tp.meetingId", "=", meetingId); + cnd.and("tpu.userId", "=", ShiroUtil.getPrincipalProperty("id")); + sql.setCondition(cnd); + return listMap(sql); + } + + @Override + public void askForLeave(List meetingTimePeriodUsers) { + String moduleName = meetingCommonService.findModuleNameByMeetingId(meetingTimePeriodUsers.get(0).getMeetingId()); + Integer stateCode = meetingCommonService.findStartStateCodeByModuleName(moduleName); + if (stateCode != null) { + + meetingTimePeriodUsers.forEach(item -> { + item.setIsJoin(false); + item.setStateCode(stateCode); + }); + dao().updateIgnoreNull(meetingTimePeriodUsers); + + List userIdMap = meetingCommonService.getNextAuditUser(stateCode, moduleName); + String userInfoStr = msgNotifyService.userData(userIdMap); + + String meetingId = meetingTimePeriodUsers.get(0).getMeetingId(); + MeetingInfo meetingInfo = dao().fetch(MeetingInfo.class, meetingId); + + String loginname = ShiroUtil.getPlatformUsername(); + String msgStr = String.format("%s针对%s提交了请假申请,请您审批!", loginname, meetingInfo.getMeetingName()); + //msgApi.sendSmsMsg(userInfoStr, msgStr); + } + } + + @Override + public void confirmMeeting(List tpuIds) { +// meetingUser.setIsJoin(true); +// dao().updateIgnoreNull(meetingUser); + Chain chain = Chain.make("isJoin", true); + Cnd cnd = Cnd.where("id", "in", tpuIds); + dao().update(MeetingTimePeriodUser.class, chain, cnd); + } + + @Override + public void signIn(MeetingTimePeriodUser timePeriodUser) { + timePeriodUser.setIsSignIn(true); + timePeriodUser.setSignInDateTime(new Date()); + dao().updateIgnoreNull(timePeriodUser); + } +} diff --git a/src/main/java/io/v/nutz/meeting/service/impl/MeetingOnlineServiceImpl.java b/src/main/java/io/v/nutz/meeting/service/impl/MeetingOnlineServiceImpl.java new file mode 100644 index 0000000..f3ebde4 --- /dev/null +++ b/src/main/java/io/v/nutz/meeting/service/impl/MeetingOnlineServiceImpl.java @@ -0,0 +1,219 @@ +package io.v.nutz.meeting.service.impl; + +import cn.hutool.core.util.StrUtil; +import cn.wizzer.framework.page.Pagination; +import io.v.nutz.base.utils.Roles; +import io.v.nutz.jdh.model.cb.Jdh_jdhxx; +import io.v.nutz.jdh.model.db.Jdh_db; +import io.v.nutz.meeting.models.MeetingInfo; +import io.v.nutz.meeting.models.MeetingTimePeriod; +import io.v.nutz.meeting.models.MeetingType; +import io.v.nutz.meeting.service.MeetingCommonService; +import io.v.nutz.meeting.service.MeetingOnlineService; +import io.v.nutz.modules.models.PageForm; +import io.v.nutz.msgNotify.models.MsgNotify; +import io.v.nutz.service.impl.ViServiceImpl; +import io.v.nutz.workersCongress.model.WorkersCongressRepresentative; +import io.v.nutz.workersCongress.model.WorkersCongressSession; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.dao.Sqls; +import org.nutz.dao.sql.Sql; +import org.nutz.dao.util.Daos; +import org.nutz.dao.util.cri.SqlExpressionGroup; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.lang.Lang; +import org.nutz.lang.util.NutMap; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@IocBean(args = {"refer:dao"}) +public class MeetingOnlineServiceImpl extends ViServiceImpl implements MeetingOnlineService { + + @Inject + private MeetingCommonService meetingCommonService; + + public MeetingOnlineServiceImpl(Dao dao) { + super(dao); + } + + @Override + public Pagination pageData(PageForm pageForm, Cnd cnd) { + Sql sql = Sqls.create(""" + SELECT + info.*, + type.meetingTypeName, + type.moduleName, + @qrCodeUrl as qrCodeUrl + FROM + meeting_info info + LEFT JOIN meeting_type type ON type.id = info.typeId + $condition + """); + sql.setParam("qrCodeUrl", ""); + if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) { + SqlExpressionGroup seg = new SqlExpressionGroup(); + seg.orLike("info.meetingName", pageForm.getSearchKeyword()); + seg.orLike("info.location", pageForm.getSearchKeyword()); + cnd.and(seg); + } + cnd.desc("info.startTime"); + sql.setCondition(cnd); + Pagination pagination = list(pageForm, sql); + List list = pagination.getList(); + for (NutMap map : list) { + List timePeriods = dao().query(MeetingTimePeriod.class, Cnd.where("meetingId", "=", map.getString("id", "")).asc("startTime")); + map.setv("timePeriods", timePeriods); + } + pagination.setList(list); + return pagination; + } + + @Override + public List exportSignature(String timePeriodId) { + Sql sql = Sqls.create(""" + SELECT + u.username, + u.loginname, + u.unitname, + u.unionname, + u.mobile + FROM + `meeting_time_period_user` tpu + RIGHT JOIN `user` u ON u.id = tpu.userId + $condition + """); + Cnd cnd = Cnd.NEW(); + cnd.and("tpu.meetingTimePeriodId", "=", timePeriodId); + sql.setCondition(cnd); + List map = listMap(sql); + + MeetingTimePeriod period = dao().fetch(MeetingTimePeriod.class, timePeriodId); + MeetingInfo meetingInfo = dao().fetch(MeetingInfo.class, period.getMeetingId()); + MeetingType meetingType = dao().fetch(MeetingType.class, meetingInfo.getTypeId()); + + + List userIds = map.stream().map(m -> m.getString("loginname")).collect(Collectors.toList()); + List roleNameList=new ArrayList<>(); + //如果是教代会会议 + if (meetingType.getModuleName().equals("teacherMeeting")) { + MsgNotify notify = dao().fetch(MsgNotify.class, meetingInfo.getNotifyId()); + Sql SqlRoleName = Sqls.create(""" + SELECT + us.loginname, + sr.`name` + FROM + `jdh_db` db + LEFT JOIN sys_user us ON us.id=db.dbid + LEFT JOIN sys_role sr ON sr.id = db.roleid + where db.jdhid=@jdhid and us.loginname in (@userIds) + """).setParam("jdhid", notify.getTeacherMeetingId()).setParam("userIds", userIds); + roleNameList = listMap(SqlRoleName); + + }else if (meetingType.getModuleName().equals("gdh")){ + MsgNotify notify = dao().fetch(MsgNotify.class, meetingInfo.getNotifyId()); + Sql SqlRoleName = Sqls.create(""" + SELECT + us.loginname, + sr.`name` + FROM + `workers_congress_representative` db + LEFT JOIN sys_user us ON us.id = db.userId + LEFT JOIN sys_role sr ON sr.id = db.roleId + WHERE + db.gdh_id = @jdhid and us.loginname in (@userIds) + """).setParam("jdhid", notify.getTeacherMeetingId()).setParam("userIds", userIds); + roleNameList = listMap(SqlRoleName); + } + + if (Lang.isNotEmpty(roleNameList)){ + List finalRoleNameList = roleNameList; + map.forEach(m -> { + NutMap roleNameMap = finalRoleNameList.stream().filter(r -> r.getString("loginname").equals(m.getString("loginname"))).findFirst().orElse(null); + if (Lang.isNotEmpty(roleNameMap)) { + m.setv("roleName", roleNameMap.getString("name")); + } else { + m.setv("roleName", null); + } + }); + } + return map; + } + + @Override + public List> getRealTimeData(String timePeriodId) { + MeetingTimePeriod timePeriod = dao().fetch(MeetingTimePeriod.class, timePeriodId); + Integer stateCode = meetingCommonService.findSuccessStateCode(timePeriod.getMeetingId()); + Sql sql = Sqls.create(""" + SELECT + (select count(1) from meeting_time_period_user where meetingTimePeriodId = @timePeriodId) as shouldComeNumber, + (select count(1) from meeting_time_period_user where meetingTimePeriodId = @timePeriodId and (isJoin is null or (isJoin is not null and stateCode != @stateCode)) and isSignIn is null) as actualComeNumber, + (select count(1) from meeting_time_period_user where meetingTimePeriodId = @timePeriodId and isSignIn = 1) as signInNumber, + (select count(1) from meeting_time_period_user where meetingTimePeriodId = @timePeriodId and isJoin = 0 and stateCode = @stateCode) as leaveNumber + """); + sql.setParam("stateCode", stateCode).setParam("timePeriodId", timePeriodId); + NutMap dataMap = (NutMap) Daos.query(dao(), sql.toString(), Sqls.callback.map()); + + List> list = new ArrayList<>(); + list.add(Map.of("label", "应到人数", "key", "shouldComeNumber", "value", dataMap.getInt("shouldComeNumber", 0))); + list.add(Map.of("label", "未到人数", "key", "actualComeNumber", "value", dataMap.getInt("actualComeNumber", 0))); + list.add(Map.of("label", "签到人数", "key", "signInNumber", "value", dataMap.getInt("signInNumber", 0))); + + //如果是教代会,就查询签到的正式代表和列席代表各多少人 + MeetingInfo meetingInfo = dao().fetch(MeetingInfo.class, timePeriod.getMeetingId()); + MeetingType meetingType = dao().fetch(MeetingType.class, meetingInfo.getTypeId()); + if ("teacherMeeting".equals(meetingType.getModuleName())) { + //获取教代会最新届次 + Jdh_jdhxx jdh = dao().fetch(Jdh_jdhxx.class, Cnd.where("jdhkqzt", "=", 1).desc("jdhkqsj")); + //查询签到人员 + List signInUser = getSignInUser(timePeriodId); + List userList = signInUser.stream().map(o -> o.getString("userId")).collect(Collectors.toList()); + //查询代表 + List dbList = dao().query(Jdh_db.class, Cnd.where("jdhid", "=", jdh.getId())); + long zsdb = dbList.stream().filter(o -> o.getRoleid().equals(Roles.ZSDB) && userList.contains(o.getDbid())).count(); + long lxdb = dbList.stream().filter(o -> o.getRoleid().equals(Roles.LXDB) && userList.contains(o.getDbid())).count(); + list.add(Map.of("label", "正式代表", "key", "zsdbNumber", "value", zsdb)); + list.add(Map.of("label", "列席代表", "key", "lxdbNumber", "value", lxdb)); + } else if ("gdh".equals(meetingType.getModuleName())) { + //获取教代会最新届次 + WorkersCongressSession jdh = dao().fetch(WorkersCongressSession.class, Cnd.where("startState", "=", 1).desc("startTime")); + //查询签到人员 + List signInUser = getSignInUser(timePeriodId); + List userList = signInUser.stream().map(o -> o.getString("userId")).collect(Collectors.toList()); + //查询代表 + List dbList = dao().query(WorkersCongressRepresentative.class, Cnd.where("gdh_id", "=", jdh.getId())); + long zsdb = dbList.stream().filter(o -> o.getRoleId().equals(Roles.GDHDB) && userList.contains(o.getUserId())).count(); + long lxdb = dbList.stream().filter(o -> o.getRoleId().equals(Roles.GDHLX) && userList.contains(o.getUserId())).count(); + list.add(Map.of("label", "正式代表", "key", "zsdbNumber", "value", zsdb)); + list.add(Map.of("label", "列席代表", "key", "lxdbNumber", "value", lxdb)); + } + list.add(Map.of("label", "请假人数", "key", "leaveNumber", "value", dataMap.getInt("leaveNumber", 0))); + return list; + } + + @Override + public List getSignInUser(String timePeriodId) { + Sql sql = Sqls.create(""" + SELECT + tpu.userId, + u.loginname, + u.username, + u.sex, + u.unitname, + tpu.signInDateTime + FROM + `meeting_time_period_user` tpu + LEFT JOIN `user` u ON u.id = tpu.userId + WHERE + tpu.meetingTimePeriodId = @timePeriodId + AND tpu.isSignIn = 1 + ORDER BY tpu.signInDateTime desc + """); + sql.setParam("timePeriodId", timePeriodId); + return listMap(sql); + } +} diff --git a/src/main/java/io/v/nutz/meeting/service/impl/MeetingTypeServiceImpl.java b/src/main/java/io/v/nutz/meeting/service/impl/MeetingTypeServiceImpl.java new file mode 100644 index 0000000..0f63156 --- /dev/null +++ b/src/main/java/io/v/nutz/meeting/service/impl/MeetingTypeServiceImpl.java @@ -0,0 +1,103 @@ +package io.v.nutz.meeting.service.impl; + +import io.v.nutz.meeting.models.MeetingType; +import io.v.nutz.meeting.service.MeetingCommonService; +import io.v.nutz.meeting.service.MeetingTypeService; +import io.v.nutz.modules.models.AuditState; +import io.v.nutz.modules.models.AuditStateUser; +import io.v.nutz.service.impl.ViServiceImpl; +import org.nutz.aop.interceptor.ioc.TransAop; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.dao.Sqls; +import org.nutz.dao.sql.Sql; +import org.nutz.dao.util.Daos; +import org.nutz.ioc.aop.Aop; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.lang.Lang; + +import java.util.List; + +@IocBean(args = {"refer:dao"}) +public class MeetingTypeServiceImpl extends ViServiceImpl implements MeetingTypeService { + + @Inject + private MeetingCommonService meetingCommonService; + + public MeetingTypeServiceImpl(Dao dao) { + super(dao); + } + + @Override + public int findMaxStateId() { + return dao().func(AuditState.class, "max", "stateId"); + } + + /** + * 根据模块名称查找stateId + * + * @param moduleName + * @return + */ + private String[] findStateIdArray(String moduleName) { + //查询该类型原来关联的stateId + Sql stateSql = Sqls.create("select stateId from audit_state where module = @moduleName").setParam("moduleName", moduleName); + return (String[]) Daos.query(dao(), stateSql.toString(), Sqls.callback.strs()); + } + + /** + * 添加流程信息 + * + * @param meetingType + */ + private void insertAuditInfo(MeetingType meetingType) { + List auditStateList = meetingType.getAuditStateList(); + if (Lang.isNotEmpty(auditStateList)) { + for (int i = 0; i < auditStateList.size(); i++) { + AuditState state = auditStateList.get(i); + state.setModule(meetingType.getModuleName()); + state.setMeetingTypeId(meetingType.getId()); + dao().insertWith(state, "^auditStateUserList$"); + } + } + } + + @Override + @Aop(TransAop.READ_COMMITTED) + public void add(MeetingType meetingType) { + dao().insert(meetingType); + insertAuditInfo(meetingType); + } + + @Override + @Aop(TransAop.READ_COMMITTED) + public void edit(MeetingType meetingType) { + dao().update(meetingType); + + //查询该类型原来关联的stateId + String[] stateIdStr = findStateIdArray(meetingType.getModuleName()); + + //删除原来审核状态下的审核人员 + dao().clear(AuditStateUser.class, Cnd.where("stateId", "in", stateIdStr)); + //删除原来的审核状态 + dao().clear(AuditState.class, Cnd.where("stateId", "in", stateIdStr)); + + //添加新的审核流程 + insertAuditInfo(meetingType); + } + + @Override + @Aop(TransAop.READ_COMMITTED) + public void delete(MeetingType meetingType) { + //查询该类型原来关联的stateId + String[] stateIdStr = findStateIdArray(meetingType.getModuleName()); + + //删除原来审核状态下的审核人员 + dao().clear(AuditStateUser.class, Cnd.where("stateId", "in", stateIdStr)); + //删除原来的审核状态 + dao().clear(AuditState.class, Cnd.where("stateId", "in", stateIdStr)); + + dao().delete(meetingType); + } +} diff --git a/src/main/java/io/v/nutz/meeting/ws/MeetingWebsocket.java b/src/main/java/io/v/nutz/meeting/ws/MeetingWebsocket.java new file mode 100644 index 0000000..b53a063 --- /dev/null +++ b/src/main/java/io/v/nutz/meeting/ws/MeetingWebsocket.java @@ -0,0 +1,30 @@ +package io.v.nutz.meeting.ws; + +import lombok.extern.slf4j.Slf4j; +import org.nutz.dao.Dao; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.plugins.mvc.websocket.AbstractWsEndpoint; +import org.nutz.plugins.mvc.websocket.NutWsConfigurator; +import org.nutz.plugins.mvc.websocket.WsHandler; + +import javax.websocket.EndpointConfig; +import javax.websocket.Session; +import javax.websocket.server.ServerEndpoint; + +@ServerEndpoint(value = "/websocket/meeting", configurator = NutWsConfigurator.class) +@IocBean +@Slf4j +public class MeetingWebsocket extends AbstractWsEndpoint { + + @Inject + private Dao dao; + + @Override + public WsHandler createHandler(Session session, EndpointConfig config) { + // 是的,返回你自己的实现类就可以了,需要每次新建哦 + return new MeetingWsHandler(dao); + } + + +} diff --git a/src/main/java/io/v/nutz/meeting/ws/MeetingWsHandler.java b/src/main/java/io/v/nutz/meeting/ws/MeetingWsHandler.java new file mode 100644 index 0000000..a9d5077 --- /dev/null +++ b/src/main/java/io/v/nutz/meeting/ws/MeetingWsHandler.java @@ -0,0 +1,58 @@ +package io.v.nutz.meeting.ws; + +import lombok.extern.slf4j.Slf4j; +import org.nutz.dao.Dao; +import org.nutz.lang.util.NutMap; +import org.nutz.mvc.Mvcs; +import org.nutz.plugins.mvc.websocket.handler.AbstractWsHandler; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + +@Slf4j +public class MeetingWsHandler extends AbstractWsHandler { + + private Dao dao; + + public MeetingWsHandler(Dao dao) { + // 覆盖默认前缀 + super(""); + this.dao = dao; + } + + @Override + public void init() { + // 必须调用超类的init,除非直接实现WsHandler接口 + super.init(); + if (this.httpSession != null) { + // 其他业务代码只需要从HttpSession取出wsid,即可调用AbstractWsEndpoint的api发送消息 + this.httpSession.setAttribute("wsid", session.getId()); + } + + HttpServletRequest req = Mvcs.getReq(); + log.debug("req={}", req); + } + + /** + * 对应js端的action名称,方法参数必须是NutMap哦 + * 通过endpoint可以发生给任何你想发生的对象, session就是当前WebSocket的会话. + * + * @param req + */ + public void getSignData(NutMap req) { + NutMap resp = NutMap.NEW(); + resp.setv("?????", "?????????"); + endpoint.sendJson(session.getId(), resp); + } + + /** + * 这里不知道为什么拿不到httpsession + * + * @param httpSession + */ + @Override + public void setHttpSession(HttpSession httpSession) { + log.debug("http session = " + httpSession); + } + +} diff --git a/src/main/java/io/v/nutz/modules/models/AuditState.java b/src/main/java/io/v/nutz/modules/models/AuditState.java new file mode 100644 index 0000000..5a623f7 --- /dev/null +++ b/src/main/java/io/v/nutz/modules/models/AuditState.java @@ -0,0 +1,203 @@ + + +package io.v.nutz.modules.models; + +import org.nutz.dao.entity.annotation.*; + +import java.util.List; + +@Table("audit_state") +public class AuditState { + @Column + @Id( + auto = false + ) + @Comment("ID") + @ColDefine( + type = ColType.INT, + width = 8 + ) + private Integer stateId; + @Column + @Comment("状态名") + @ColDefine( + type = ColType.VARCHAR, + width = 50 + ) + private String stateName; + @Column + @Comment("强调色") + @ColDefine( + type = ColType.VARCHAR, + width = 20 + ) + private String stateColor; + @Column + @Comment("隶属(***模块)") + @ColDefine( + type = ColType.VARCHAR, + width = 50 + ) + private String module; + @Column + @Comment("下个审核节点代码") + @ColDefine( + type = ColType.INT, + width = 8 + ) + private Integer afterStateId; + @Column + @Comment("通过下个审核节点代码") + @ColDefine( + type = ColType.INT, + width = 8 + ) + private Integer afterPassStateId; + @Column + @Comment("拒绝后下个节点代码") + @ColDefine( + type = ColType.INT, + width = 8 + ) + private Integer afterRejectStateId; + @Column + @Comment("会议类型id 属于那个会议类型的流程") + @ColDefine( + type = ColType.INT + ) + private Integer meetingTypeId; + @Column + @Comment("会议审核类型") + @ColDefine( + type = ColType.VARCHAR + ) + private String auditAfterType; + @Column + @Comment("审核节点类型") + @ColDefine( + type = ColType.INT + ) + private Integer stateAuditType; + @Column + @Comment("审核人员匹配条件") + @ColDefine( + type = ColType.VARCHAR + ) + private String matchCnd; + @Many( + field = "stateId" + ) + private List auditStateUserList; + + public AuditState() { + } + + public Integer getStateId() { + return this.stateId; + } + + public String getStateName() { + return this.stateName; + } + + public String getStateColor() { + return this.stateColor; + } + + public String getModule() { + return this.module; + } + + public Integer getAfterStateId() { + return this.afterStateId; + } + + public Integer getAfterPassStateId() { + return this.afterPassStateId; + } + + public Integer getAfterRejectStateId() { + return this.afterRejectStateId; + } + + public Integer getMeetingTypeId() { + return this.meetingTypeId; + } + + public Integer getStateAuditType() { + return this.stateAuditType; + } + + public String getMatchCnd() { + return this.matchCnd; + } + + public List getAuditStateUserList() { + return this.auditStateUserList; + } + + public AuditState setStateId(Integer stateId) { + this.stateId = stateId; + return this; + } + + public AuditState setStateName(String stateName) { + this.stateName = stateName; + return this; + } + + public AuditState setStateColor(String stateColor) { + this.stateColor = stateColor; + return this; + } + + public AuditState setModule(String module) { + this.module = module; + return this; + } + + public AuditState setAfterStateId(Integer afterStateId) { + this.afterStateId = afterStateId; + return this; + } + + public AuditState setAfterPassStateId(Integer afterPassStateId) { + this.afterPassStateId = afterPassStateId; + return this; + } + + public AuditState setAfterRejectStateId(Integer afterRejectStateId) { + this.afterRejectStateId = afterRejectStateId; + return this; + } + + public AuditState setMeetingTypeId(Integer meetingTypeId) { + this.meetingTypeId = meetingTypeId; + return this; + } + + public AuditState setStateAuditType(Integer stateAuditType) { + this.stateAuditType = stateAuditType; + return this; + } + + public String getAuditAfterType() { + return auditAfterType; + } + + public void setAuditAfterType(String auditAfterType) { + this.auditAfterType = auditAfterType; + } + + public AuditState setMatchCnd(String matchCnd) { + this.matchCnd = matchCnd; + return this; + } + + public AuditState setAuditStateUserList(List auditStateUserList) { + this.auditStateUserList = auditStateUserList; + return this; + } + + +} diff --git a/src/main/java/io/v/nutz/question/controller/QuestionAnswerRankingController.java b/src/main/java/io/v/nutz/question/controller/QuestionAnswerRankingController.java index d66f8a5..2ab25e1 100644 --- a/src/main/java/io/v/nutz/question/controller/QuestionAnswerRankingController.java +++ b/src/main/java/io/v/nutz/question/controller/QuestionAnswerRankingController.java @@ -4,12 +4,13 @@ import cn.afterturn.easypoi.excel.ExcelExportUtil; import cn.afterturn.easypoi.excel.entity.ExportParams; import cn.afterturn.easypoi.excel.entity.enmus.ExcelType; import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity; -import cn.hutool.core.util.StrUtil; import io.v.nutz.annontation.ViReturn; +import io.v.nutz.base.page.Pagination; import io.v.nutz.modules.models.PageForm; +import io.v.nutz.question.service.QuestionAnswerRankingService; import io.v.nutz.question.service.QuestionService; import io.v.nutz.sys.services.SysUnionService; -import io.v.nutz.util.ShiroUtil; +import io.v.nutz.web.commons.utils.ShiroUtil; import org.apache.poi.ss.usermodel.Workbook; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.nutz.dao.Cnd; @@ -40,6 +41,9 @@ public class QuestionAnswerRankingController { @Inject private SysUnionService sysUnionService; + @Inject + private QuestionAnswerRankingService questionAnswerRankingService; + @At("") @Ok("beetl:/platform/question/ranking.html") @RequiresPermissions("question.answer.ranking") @@ -49,96 +53,25 @@ public class QuestionAnswerRankingController { @At @ViReturn @RequiresPermissions("question.answer.ranking") - public Object pageData(PageForm pageForm, @Param(value = "questionId",required = false) String questionId, - @Param(value = "unionId",required = false) String unionId, - @Param(value = "unitId",required = false) String unitId, - @Param(value = "answerDate",required = false) String answerDate) { - Sql sql = Sqls.create(""" - SELECT - qus.userId, - DATE_FORMAT( qus.answerDate, '%Y-%m-%d' ) as answerDate, - MAX( score ) AS max_score, - qus.answerNum, - u.loginname, - u.username, - u.sex, - u.unitname, - u.unionname, - u.mobile - FROM - question_user_score qus - LEFT JOIN `user` u ON u.id = qus.userId - $condition - """); - Cnd cnd = Cnd.NEW(); - cnd.groupBy("qus.userId", "qus.answerDate"); - cnd.desc("qus.userId").desc("max_score"); - cnd.andEX("u.unionid", "=", unionId); - cnd.andEX("u.unitid", "=", unitId); - cnd.andEX("qus.answerDate", "=", answerDate); - cnd.and("qus.questionId", "=", questionId); - if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) { - cnd.and(Cnd.exps("u.loginname", "like", "%" + pageForm.getSearchKeyword() + "%").or("u.username", "like", "%" + pageForm.getSearchKeyword() + "%")); - } - cnd.asc("qus.answerDate").asc("u.unionid").asc("u.id"); - sql.setCondition(cnd); - return questionService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); + public Object pageData(PageForm pageForm, @Param(value = "questionId", required = false) String questionId, + @Param(value = "unionId", required = false) String unionId, + @Param(value = "unitId", required = false) String unitId, + @Param(value = "answerDate", required = false) String answerDate) { + Pagination pagination = questionAnswerRankingService.pageData(pageForm, questionId, unionId, unitId, answerDate); + return pagination; } @At @Ok("void") @RequiresPermissions("question.answer.ranking") - public void doExport(@Param(value = "questionId",required = false) String questionId, - @Param(value = "unionId",required = false) String unionId, - @Param(value = "unitId",required = false) String unitId, - @Param(value = "answerDate",required = false) String answerDate, HttpServletResponse response) { + public void doExport(@Param(value = "questionId", required = false) String questionId, + @Param(value = "unionId", required = false) String unionId, + @Param(value = "unitId", required = false) String unitId, + @Param(value = "answerDate", required = false) String answerDate, HttpServletResponse response) { try { - Sql sql = Sqls.create(""" - SELECT - qus.userId, - DATE_FORMAT( qus.answerDate, '%Y-%m-%d' ) as answerDate, - MAX( score ) AS max_score, - qus.answerNum, - u.loginname, - u.username, - u.sex, - u.unitname, - u.unionname, - u.mobile - FROM - question_user_score qus - LEFT JOIN `user` u ON u.id = qus.userId - $condition - """); - Cnd cnd = Cnd.NEW(); - cnd.groupBy("qus.userId", "qus.answerDate"); - cnd.desc("qus.userId").desc("max_score"); - cnd.andEX("u.unionid", "=", unionId); - cnd.andEX("u.unitid", "=", unitId); - cnd.andEX("qus.answerDate", "=", answerDate); - cnd.and("qus.questionId", "=", questionId); - cnd.asc("qus.answerDate").asc("u.unionid").asc("u.id"); - sql.setCondition(cnd); - List dataList = questionService.listMap(sql); - - List exportEntities = new ArrayList<>(); - exportEntities.add(new ExcelExportEntity("工号", "loginname", 20)); - exportEntities.add(new ExcelExportEntity("姓名", "username", 20)); - exportEntities.add(new ExcelExportEntity("性别", "sex", 20)); - exportEntities.add(new ExcelExportEntity("所属单位", "unitname", 20)); - exportEntities.add(new ExcelExportEntity("所属工会", "unionname", 20)); - exportEntities.add(new ExcelExportEntity("联系方式", "mobile", 20)); - exportEntities.add(new ExcelExportEntity("分数", "max_score", 20)); - exportEntities.add(new ExcelExportEntity("答题日期", "answerDate", 20)); - - response.setContentType("application/octet-stream"); response.setHeader("Content-Disposition", "attachment;filename=" + new String(("得分名单.xlsx").getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1)); - ExportParams exportParams = new ExportParams(); - exportParams.setType(ExcelType.XSSF); - Workbook workbook = ExcelExportUtil.exportExcel(exportParams, exportEntities, dataList); - workbook.write(response.getOutputStream()); - + questionAnswerRankingService.exportXlsx(questionId, response.getOutputStream()); } catch (Exception e) { e.printStackTrace(); } @@ -147,7 +80,7 @@ public class QuestionAnswerRankingController { @At @Ok("void") @RequiresPermissions("question.answer.ranking") - public void doExportAll(@Param(value = "questionId",required = false) String questionId, HttpServletResponse response) { + public void doExportAll(@Param(value = "questionId", required = false) String questionId, HttpServletResponse response) { try { Sql sql = Sqls.create(""" SELECT diff --git a/src/main/java/io/v/nutz/question/controller/QuestionAnswerStatisticsController.java b/src/main/java/io/v/nutz/question/controller/QuestionAnswerStatisticsController.java deleted file mode 100644 index 72c645c..0000000 --- a/src/main/java/io/v/nutz/question/controller/QuestionAnswerStatisticsController.java +++ /dev/null @@ -1,156 +0,0 @@ -package io.v.nutz.question.controller; - -import cn.afterturn.easypoi.excel.ExcelExportUtil; -import cn.afterturn.easypoi.excel.entity.ExportParams; -import cn.afterturn.easypoi.excel.entity.enmus.ExcelType; -import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity; -import io.v.nutz.annontation.ViReturn; -import io.v.nutz.modules.models.PageForm; -import io.v.nutz.question.model.Question; -import io.v.nutz.question.model.QuestionWinner; -import io.v.nutz.question.service.QuestionService; -import io.v.nutz.util.ViTool; -import org.apache.poi.ss.usermodel.Workbook; -import org.apache.shiro.authz.annotation.RequiresPermissions; -import org.nutz.dao.Cnd; -import org.nutz.dao.Sqls; -import org.nutz.dao.sql.Sql; -import org.nutz.ioc.loader.annotation.Inject; -import org.nutz.ioc.loader.annotation.IocBean; -import org.nutz.lang.util.NutMap; -import org.nutz.mvc.annotation.At; -import org.nutz.mvc.annotation.Ok; -import org.nutz.mvc.annotation.Param; - -import javax.servlet.ServletOutputStream; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -@At("/platform/question/answer/statistics") -@IocBean -@Ok("json:full") -public class QuestionAnswerStatisticsController { - - @Inject - private QuestionService questionService; - - @At("") - @Ok("beetl:/platform/question/statistics.html") - @RequiresPermissions("question.answer.statistics") - public void index() { - } - - @At - @ViReturn - @RequiresPermissions("question.answer.statistics") - public Object pageData(PageForm pageForm, @Param(value = "questionId", required = false) String questionId, - @Param(value = "unionId", required = false) String unionId, - @Param(value = "unitId", required = false) String unitId) { - - Sql sql = Sqls.create(""" - SELECT - qus.userId, - MAX( qus.score ) AS max_score, - u.username, - u.loginname, - u.unitname, - u.unionname, - u.sex, - u.mobile - FROM - question_user_score qus - LEFT JOIN `user` u ON u.id = qus.userId - $condition - """); - Cnd cnd = Cnd.NEW(); - cnd.groupBy("qus.userId"); - cnd.and("qus.questionId", "=", questionId); - cnd.andEX("u.unitid", "=", unitId); - cnd.andEX("u.unionid", "=", unionId); - sql.setCondition(cnd); - return questionService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); - } - - @At - @ViReturn - @RequiresPermissions("question.answer.statistics") - public Object doRaffle(String questionId) { - questionService.dao().clear(QuestionWinner.class, Cnd.where("questionId", "=", questionId)); - - Question question = questionService.fetch(questionId); - Integer raffleScore = question.getRaffleScore(); - Integer raffleWinnerUserNum = question.getRaffleWinnerUserNum(); - - Sql sql = Sqls.create("SELECT userId FROM `question_user_score` where score >= @score order by RAND() limit @num"); - sql.setParam("score", raffleScore); - sql.setParam("num", raffleWinnerUserNum); - List list = questionService.listMap(sql); - - List userIds = list.stream().map(v -> v.getString("userId")).collect(Collectors.toList()); - - List winners = new ArrayList<>(); - for (String userId : userIds) { - QuestionWinner questionWinner = new QuestionWinner(); - questionWinner.setQuestionId(questionId); - questionWinner.setUserId(userId); - winners.add(questionWinner); - } - questionService.dao().insert(winners); - return null; - } - - @At - @Ok("void") - @RequiresPermissions("question.answer.statistics") - public void export(@Param(value = "questionId", required = false) String questionId, - @Param(value = "unionId", required = false) String unionId, - @Param(value = "unitId", required = false) String unitId, - HttpServletResponse response) { - - Sql sql = Sqls.create(""" - SELECT - qus.userId, - MAX( qus.score ) AS max_score, - u.username, - u.loginname, - u.unitname, - u.unionname, - u.sex, - u.mobile - FROM - question_user_score qus - LEFT JOIN `user` u ON u.id = qus.userId - $condition - """); - Cnd cnd = Cnd.NEW(); - cnd.groupBy("qus.userId"); - cnd.and("qus.questionId", "=", questionId); - cnd.andEX("u.unitid", "=", unitId); - cnd.andEX("u.unionid", "=", unionId); - sql.setCondition(cnd); - List list = questionService.listMap(sql); - - List exportEntities = new ArrayList<>(); - exportEntities.add(new ExcelExportEntity("姓名", "username", 20)); - exportEntities.add(new ExcelExportEntity("工号", "loginname", 20)); - exportEntities.add(new ExcelExportEntity("单位", "unitname", 20)); - exportEntities.add(new ExcelExportEntity("分工会", "unionname", 20)); - exportEntities.add(new ExcelExportEntity("得分", "max_score", 20)); - - try (ServletOutputStream outputStream = response.getOutputStream()) { - ViTool.excelResponse(response, "参加人员.xlsx"); - ExportParams exportParams = new ExportParams(); - exportParams.setType(ExcelType.XSSF); - Workbook work = ExcelExportUtil.exportExcel(exportParams, exportEntities, list); - work.write(outputStream); - } catch (IOException e) { - e.printStackTrace(); - } - - - } - -} diff --git a/src/main/java/io/v/nutz/question/controller/QuestionAnswerWinnerController.java b/src/main/java/io/v/nutz/question/controller/QuestionAnswerWinnerController.java index b8a303b..2df92fa 100644 --- a/src/main/java/io/v/nutz/question/controller/QuestionAnswerWinnerController.java +++ b/src/main/java/io/v/nutz/question/controller/QuestionAnswerWinnerController.java @@ -51,10 +51,10 @@ public class QuestionAnswerWinnerController { @At @ViReturn @RequiresPermissions("question.answer.winners") - public Object pageData(PageForm pageForm, @Param(value = "questionId", required = false) String questionId, - @Param(value = "unionId", required = false) String unionId, - @Param(value = "unitId", required = false) String unitId, - @Param(value = "winDate", required = false) String winDate) { + public Object pageData(PageForm pageForm, @Param(value = "questionId",required = false) String questionId, + @Param(value = "unionId",required = false) String unionId, + @Param(value = "unitId",required = false) String unitId, + @Param(value = "winDate",required = false) String winDate) { Sql sql = Sqls.create(""" SELECT u.loginname, @@ -63,15 +63,13 @@ public class QuestionAnswerWinnerController { u.unitname, u.unionname, u.mobile, - qus.score + DATE_FORMAT( qw.winDate, '%Y-%m-%d' ) as winDate, + qw.score FROM question_winner qw LEFT JOIN `user` u ON u.id = qw.userId - LEFT JOIN question_user_score qus on qus.userId = qw.userId and qus.questionId = @questionId $condition """); - sql.setParam("questionId", questionId); - Cnd cnd = Cnd.NEW(); cnd.andEX("u.unionid", "=", unionId); cnd.andEX("u.unitid", "=", unitId); @@ -80,7 +78,6 @@ public class QuestionAnswerWinnerController { if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) { cnd.and(Cnd.exps("u.loginname", "like", "%" + pageForm.getSearchKeyword() + "%").or("u.username", "like", "%" + pageForm.getSearchKeyword() + "%")); } - cnd.groupBy("u.loginname"); cnd.desc("qw.winDate"); sql.setCondition(cnd); Pagination pagination = questionService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); @@ -98,33 +95,31 @@ public class QuestionAnswerWinnerController { @At @Ok("void") @RequiresPermissions("question.answer.winners") - public void exportWinners(@Param(value = "questionId", required = false) String questionId, - @Param(value = "unionId", required = false) String unionId, - @Param(value = "unitId", required = false) String unitId, - @Param(value = "winDate", required = false) String winDate, HttpServletResponse response) { + public void exportWinners(@Param(value = "questionId",required = false) String questionId, + @Param(value = "unionId",required = false) String unionId, + @Param(value = "unitId",required = false) String unitId, + @Param(value = "winDate",required = false) String winDate, HttpServletResponse response) { try { Sql sql = Sqls.create(""" - SELECT - u.loginname, - u.username, - u.sex, - u.unitname, - u.unionname, - qus.score + SELECT + u.loginname, + u.username, + u.sex, + u.unitname, + u.unionname, + u.mobile, + DATE_FORMAT( qw.winDate, '%Y-%m-%d' ) as winDate, + qw.score FROM question_winner qw LEFT JOIN `user` u ON u.id = qw.userId - LEFT JOIN question_user_score qus on qus.userId = qw.userId and qus.questionId = @questionId - $condition - """); - sql.setParam("questionId", questionId); - + $condition + """); Cnd cnd = Cnd.NEW(); cnd.andEX("u.unionid", "=", unionId); cnd.andEX("u.unitid", "=", unitId); cnd.andEX("qw.winDate", "=", winDate); cnd.and("qw.questionId", "=", questionId); - cnd.groupBy("u.loginname"); cnd.desc("qw.winDate"); sql.setCondition(cnd); List dataList = questionService.listMap(sql); @@ -136,9 +131,8 @@ public class QuestionAnswerWinnerController { exportEntities.add(new ExcelExportEntity("所属单位", "unitname", 20)); exportEntities.add(new ExcelExportEntity("所属工会", "unionname", 20)); exportEntities.add(new ExcelExportEntity("联系方式", "mobile", 20)); - exportEntities.add(new ExcelExportEntity("得分", "score", 20)); -// exportEntities.add(new ExcelExportEntity("中奖日期", "winDate", 20)); -// exportEntities.add(new ExcelExportEntity("抽奖分数(最高分)", "maxScore", 20)); + exportEntities.add(new ExcelExportEntity("中奖日期", "winDate", 20)); + exportEntities.add(new ExcelExportEntity("抽奖分数", "score", 20)); response.setContentType("application/octet-stream"); response.setHeader("Content-Disposition", "attachment;filename=" + new String(("中奖名单.xlsx").getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1)); @@ -154,7 +148,7 @@ public class QuestionAnswerWinnerController { @At @RequiresPermissions("question.answer.winners") public Object getQuestion(Integer year) { - List list = questionService.query(Cnd.NEW().andEX("YEAR(qctime)", "=", year).and("qlx", "=", 2).and("isRaffle", "=", 1)); + List list = questionService.query(Cnd.NEW().andEX("YEAR(qctime)", "=", year).and("qlx", "=", 2).and("isRaffle","=",1)); return Result.success(list); } diff --git a/src/main/java/io/v/nutz/question/controller/QuestionCakeController.java b/src/main/java/io/v/nutz/question/controller/QuestionCakeController.java index 70926dc..57c9a37 100644 --- a/src/main/java/io/v/nutz/question/controller/QuestionCakeController.java +++ b/src/main/java/io/v/nutz/question/controller/QuestionCakeController.java @@ -46,6 +46,23 @@ public class QuestionCakeController { un.unionName, (select count(1) from user where member = 1 and unionid = un.id) AS teacherSum, ( + select + count(1) + from + activity_user_scope aus + left join "user" u on u.id = aus.userId + where + groupId in ( + select + activityUserScopeId + from + question + where + qid = @questionId + ) + and u.unionId = un.id + ) as teacherSum, + ( SELECT count( DISTINCT r.userId ) FROM diff --git a/src/main/java/io/v/nutz/question/controller/QuestionH5Controller.java b/src/main/java/io/v/nutz/question/controller/QuestionH5Controller.java index a86d64e..447054e 100644 --- a/src/main/java/io/v/nutz/question/controller/QuestionH5Controller.java +++ b/src/main/java/io/v/nutz/question/controller/QuestionH5Controller.java @@ -1,5 +1,6 @@ package io.v.nutz.question.controller; +import cn.hutool.core.date.DateTime; import cn.hutool.core.date.DateUtil; import cn.wizzer.framework.base.Result; import cn.wizzer.framework.page.Pagination; @@ -8,7 +9,7 @@ import io.v.nutz.modules.models.PageForm; import io.v.nutz.question.model.*; import io.v.nutz.question.service.QuestionService; import io.v.nutz.web.commons.utils.ShiroUtil; -import org.nutz.dao.Chain; +import org.apache.shiro.authz.annotation.RequiresAuthentication; import org.nutz.dao.Cnd; import org.nutz.dao.Dao; import org.nutz.dao.Sqls; @@ -23,8 +24,10 @@ import org.nutz.mvc.annotation.Ok; import org.nutz.mvc.annotation.Param; import org.springframework.web.bind.annotation.PathVariable; -import java.util.*; -import java.util.concurrent.atomic.AtomicReference; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; /** @@ -46,6 +49,7 @@ public class QuestionH5Controller { @At("/list") @Ok("beetl:/mobile/question/list.html") + @RequiresAuthentication public void list() { } @@ -70,16 +74,11 @@ public class QuestionH5Controller { @At("/answer/index") @Ok("beetl:/mobile/question/answer.html") + @RequiresAuthentication public void answerIndex() { } - @At("/answer/index_ctgu") - @Ok("beetl:/mobile/question/answer_ctgu.html") - public void answerIndex2() { - - } - /** * 页面数据 @@ -93,13 +92,12 @@ public class QuestionH5Controller { public Object pageData(PageForm pageForm, int status, int type) { Sql sql = Sqls.create(""" select - q.*, - (select count(1) from question_issue where iqid = q.qid) as topicTotal - from + q.* + from question q $condition """); -// sql.setParam("today", DateUtil.today()); + sql.setParam("today", DateUtil.today()); Cnd cnd = Cnd.NEW(); long now = System.currentTimeMillis(); if (status == 1) { @@ -133,171 +131,203 @@ public class QuestionH5Controller { /** * 问卷信息 * - * @param id id + * @param id id + * @param currentDate 日期 + * @param randomCount 轮数 * @return {@link Object} */ @At("/questionInfo/{id}") - public Object questionInfo(@PathVariable String id) { + public Object questionInfo(@PathVariable String id, + @Param(required = false, value = "currentDate") String currentDate, + @Param(required = false, value = "randomCount") Integer randomCount) { Question question = questionService.fetch(id); - Cnd issueCnd = Cnd.NEW(); - //出题模式 - // mode==1 定时定题 直接查询 - // mode==2 先查询今天的随机题目有没有设置好 设置好了直接查询今天的题目 没有的话查询displayDate为null的题目然后取题目再设置时间进行查询 Integer ctMode = question.getCtMode(); - if (ctMode == 2) { - //单次出题的数量 - Integer ctNum = question.getCtNum(); - int todayIssueCount = dao.count(Question_issue.class, Cnd.where("displayDate", "=", DateUtil.format(new Date(), "yyyy-MM-dd")) - .and("iqid", "=", id)); - if (todayIssueCount == 0) { - Sql randIssueSql = Sqls.create("SELECT iid FROM question_issue WHERE iqid = @qid AND displayDate IS NULL ORDER BY RAND() LIMIT @ctNum"); - randIssueSql.setParam("qid", id); - randIssueSql.setParam("ctNum", ctNum); - String[] randIssueIds = (String[]) Daos.query(dao, randIssueSql.toString(), Sqls.callback.strs()); - dao.update(Question_issue.class, Chain.make("displayDate", new Date()), Cnd.where("iid", "in", randIssueIds)); + + if (ctMode == 1) { + //定时定题 + + //判断传入的日期是否已经加入到表里 + QuestionRandomIssue randomIssue = dao.fetch(QuestionRandomIssue.class, Cnd.where("questionId", "=", question.getQid()).and("userId", "=", ShiroUtil.getUserId()) + .and("displayDate", "=", currentDate)); + if (Lang.isEmpty(randomIssue)) { + Sql issueSql = Sqls.queryString(" SELECT iid FROM `question_issue` where iqid = @qid AND displayDate = @displayDate"); + issueSql.setParam("qid", id); + issueSql.setParam("displayDate", currentDate); + dao.execute(issueSql); + String[] randomIssueIds = (String[]) issueSql.getResult(); + QuestionRandomIssue questionRandomIssue = new QuestionRandomIssue(); + questionRandomIssue.setQuestionId(id); + questionRandomIssue.setUserId(ShiroUtil.getUserId()); + questionRandomIssue.setIssueIds(Arrays.asList(randomIssueIds)); + questionRandomIssue.setNum(null); + questionRandomIssue.setIsFinish(false); + questionRandomIssue.setDisplayDate(DateUtil.parseDate(currentDate)); + dao.insert(questionRandomIssue); + issueCnd.and("iid", "in", randomIssueIds); + } else { + issueCnd.and("iid", "in", randomIssue.getIssueIds()); + } + } else if (ctMode == 2) { + //随机模式 + + //判断是否为一天答题 + DateTime startTime = DateUtil.date(question.getStartTime()); + DateTime endTime = DateUtil.date(question.getEndTime()); + long betweenDay = DateUtil.betweenDay(startTime, endTime, true); + + //每次随机的题目数量 + Integer ranDomSingleIssueNum = question.getRanDomSingleIssueNum(); + //总共随机几次 + Integer ranDomSum = question.getRanDomSum(); + + if (betweenDay == 0) { + //同一天 一天随机多次 + + if (randomCount != null) { + QuestionRandomIssue randomIssues = dao.fetch(QuestionRandomIssue.class, Cnd.where("questionId", "=", question.getQid()).and("userId", "=", ShiroUtil.getUserId()).and("num", "=", randomCount)); + issueCnd.andEX("iid", "in", randomIssues.getIssueIds()); + } else { + //用户的随机题目 + List randomIssues = dao.query(QuestionRandomIssue.class, Cnd.where("questionId", "=", question.getQid()).and("userId", "=", ShiroUtil.getUserId())); + + //查询已经随机但是并没有答题完成的题目 + List notFinishIssueIds = randomIssues.stream().filter(v -> v.getIsFinish() != null && !v.getIsFinish()).findFirst().map(QuestionRandomIssue::getIssueIds).orElseGet(Collections::emptyList); + + if (Lang.isNotEmpty(notFinishIssueIds)) { + //就查询没答完的题目 + issueCnd.and("iid", "in", notFinishIssueIds); + } else { + //进行下一次随机 然后加入到随机题目表里 + //已经答了的题目 + List hasAnswerIssueIds = randomIssues.stream().flatMap(v -> v.getIssueIds().stream()).collect(Collectors.toList()); + //已经随机几次了 + int maxAnswerNum = randomIssues.stream().mapToInt(QuestionRandomIssue::getNum).max().orElse(0); + + Sql issueSql = null; + if (maxAnswerNum == ranDomSum) { + //随机完了 没有题目了 默认去最后一轮 +// issueCnd.andEX("iid", "in", hasAnswerIssueIds); + List lastRanDomIssueIds = randomIssues.stream().filter(v -> v.getNum() == maxAnswerNum).flatMap(v -> v.getIssueIds().stream()).collect(Collectors.toList()); + issueCnd.andEX("iid", "in", lastRanDomIssueIds); + } else { + if (maxAnswerNum == 0) { + //从没有随即过 + issueSql = Sqls.queryString(" SELECT iid FROM `question_issue` where iqid = @qid ORDER BY RAND() LIMIT @num"); + } else { + //之前随机过 + issueSql = Sqls.queryString(" SELECT iid FROM `question_issue` where iqid = @qid and iid not in (@issueIds) ORDER BY RAND() LIMIT @num"); + issueSql.setParam("issueIds", hasAnswerIssueIds); + } + issueSql.setParam("qid", id); + issueSql.setParam("num", ranDomSingleIssueNum); + dao.execute(issueSql); + String[] randomIssueIds = (String[]) issueSql.getResult(); + + if (Lang.isNotEmpty(randomIssueIds)) { + QuestionRandomIssue questionRandomIssue = new QuestionRandomIssue(); + questionRandomIssue.setQuestionId(id); + questionRandomIssue.setUserId(ShiroUtil.getUserId()); + questionRandomIssue.setIssueIds(Arrays.asList(randomIssueIds)); + questionRandomIssue.setNum(maxAnswerNum + 1); + questionRandomIssue.setIsFinish(false); + questionRandomIssue.setDisplayDate(DateUtil.parseDate(DateUtil.today())); + dao.insert(questionRandomIssue); + } + //获取本次的随机题目 + issueCnd.andEX("iid", "in", randomIssueIds); + } + } + } + } else { + //不是一天 那就一天随机一次 + + //用户的随机题目记录 + List randomIssues = dao.query(QuestionRandomIssue.class, Cnd.where("questionId", "=", question.getQid()).and("userId", "=", ShiroUtil.getUserId())); + //已经随机的题目 + List hasRanDomIssueIds = randomIssues.stream().flatMap(v -> v.getIssueIds().stream()).collect(Collectors.toList()); + + //判断传入的日期是否已经加入到表里 + QuestionRandomIssue currentDateRandomIssue = dao.fetch(QuestionRandomIssue.class, Cnd.where("questionId", "=", question.getQid()).and("userId", "=", ShiroUtil.getUserId()) + .and("displayDate", "=", currentDate)); + if (Lang.isEmpty(currentDateRandomIssue)) { + Sql issueSql = null; + if (Lang.isEmpty(hasRanDomIssueIds)) { + issueSql = Sqls.queryString(" SELECT iid FROM `question_issue` where iqid = @qid ORDER BY RAND() LIMIT @num"); + } else { + //之前随机过 + issueSql = Sqls.queryString(" SELECT iid FROM `question_issue` where iqid = @qid and iid not in (@issueIds) ORDER BY RAND() LIMIT @num"); + issueSql.setParam("issueIds", hasRanDomIssueIds); + } + issueSql.setParam("num", ranDomSingleIssueNum); + issueSql.setParam("qid", id); + dao.execute(issueSql); + String[] randomIssueIds = (String[]) issueSql.getResult(); + QuestionRandomIssue questionRandomIssue = new QuestionRandomIssue(); + questionRandomIssue.setQuestionId(id); + questionRandomIssue.setUserId(ShiroUtil.getUserId()); + questionRandomIssue.setIssueIds(Arrays.asList(randomIssueIds)); + questionRandomIssue.setNum(null); + questionRandomIssue.setIsFinish(false); + questionRandomIssue.setDisplayDate(DateUtil.parseDate(currentDate)); + dao.insert(questionRandomIssue); + issueCnd.and("iid", "in", randomIssueIds); + } else { + issueCnd.and("iid", "in", currentDateRandomIssue.getIssueIds()); + } + } + } else if (ctMode == 3) { + //常规模式 + //判断是否将题目加进去了 + QuestionRandomIssue randomIssue = dao.fetch(QuestionRandomIssue.class, Cnd.where("questionId", "=", question.getQid()).and("userId", "=", ShiroUtil.getUserId())); + if (Lang.isEmpty(randomIssue)) { + Sql issueSql = Sqls.create(" SELECT iid FROM `question_issue` where iqid = @qid"); + issueSql.setParam("qid", id); + issueSql.setCallback(Sqls.callback.strList()); + dao.execute(issueSql); + List randomIssueIds = (List) issueSql.getResult(); + Collections.shuffle(randomIssueIds); + QuestionRandomIssue questionRandomIssue = new QuestionRandomIssue(); + questionRandomIssue.setQuestionId(id); + questionRandomIssue.setUserId(ShiroUtil.getUserId()); + questionRandomIssue.setIssueIds(randomIssueIds); + questionRandomIssue.setNum(null); + questionRandomIssue.setIsFinish(false); + dao.insert(questionRandomIssue); + issueCnd.and("iid", "in", randomIssueIds); + } else { + issueCnd.and("iid", "in", randomIssue.getIssueIds()); } -// issueCnd.and("displayDate", "=", DateUtil.format(new Date(), "yyyy-MM-dd")); } - issueCnd.asc("ipxbh"); +// issueCnd.asc("ipxbh"); questionService.fetchLinks(question, "issues", issueCnd); for (Question_issue issue : question.getIssues()) { questionService.fetchLinks(issue, "wors", Cnd.NEW().asc("wpxbh")); } + + if (question.getDisruptingOrder()) { + Collections.shuffle(question.getIssues()); + } + return Result.success(question); } /** * 保存答案 * - * @param repliesArray 回复 + * @param repliesArray 答复记录 + * @param questionId 问卷ID + * @param isSave 是否保存 * @return {@link Object} */ @At("/saveAnswer") - public Object saveAnswer(@Param("repliesArray") Question_reply[] repliesArray) { - if (Lang.isNotEmpty(repliesArray)) { - List replies = Arrays.asList(repliesArray); - if (Lang.isNotEmpty(replies)) { - String qid = replies.get(0).getQid(); - questionService.dao().clear(Question_reply.class, Cnd.where("qid", "=", qid).and("userId", "=", ShiroUtil.getPrincipalProperty("id"))); - replies.forEach(v -> { - v.setUserId(String.valueOf(ShiroUtil.getPrincipalProperty("id"))); - v.setRtime(DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss")); - }); - questionService.insert(replies); - } - return Result.success(); - } + public Object saveAnswer(@Param("replies") Question_reply[] repliesArray, String questionId, Boolean isSave) { + questionService.saveAnswer(repliesArray, questionId, isSave); return Result.success(); } - /** - * 保存答案 只针对答题模式 - * - * @param replies 问题回复 - * @return {@link Object} - */ - @At("/saveAnswer2") - public Object saveAnswer2(Question_reply[] replies) { - if (Lang.isNotEmpty(replies)) { - Arrays.asList(replies).forEach(v -> { - v.setUserId(String.valueOf(ShiroUtil.getPrincipalProperty("id"))); - v.setRtime(DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss")); - }); - questionService.insert(replies); - insertQuestionAnswerScore(Arrays.asList(replies)); - insertQuestionAnswerScore2(Arrays.asList(replies)); - } - return Result.success(); - } - - public void insertQuestionAnswerScore(List replyList) { - //用户id - String userId = (String) ShiroUtil.getPrincipalProperty("id"); - //问卷id - String questionId = replyList.get(0).getQid(); - //题目id - String issueId = replyList.get(0).getIssueId(); - - Question_issue issue = dao.fetch(Question_issue.class, Cnd.where("iid", "=", issueId)); - Float fraction = issue.getFraction(); - - Sql sql = Sqls.create(""" - SELECT wid FROM question_wor qw WHERE qw.correct = 1 AND qw.wiid = @wiid - """).setParam("wiid", issueId); - //正确的选项 - Set correctOptionIdList = new HashSet<>((List) Daos.query(dao, sql.toString(), Sqls.callback.strList())); - //用户选的选项 - Set userSelectedOptionIdList = replyList.stream().map(v -> v.getRwid()).collect(Collectors.toSet()); - - int count = dao.count(QuestionAnswerUserScore.class, Cnd.where("questionId", "=", questionId).and("subjectId", "=", issueId).and("answerDate", "=", DateUtil.format(new Date(), "yyyy-MM-dd")).and("userId", "=", userId)); - - QuestionAnswerUserScore answerUserScore = new QuestionAnswerUserScore(); - answerUserScore.setQuestionId(questionId); - answerUserScore.setSubjectId(issueId); - answerUserScore.setUserId(userId); - answerUserScore.setAnswerDate(new Date()); - answerUserScore.setAnswerNum(count + 1); - - if (correctOptionIdList.equals(userSelectedOptionIdList)) { - answerUserScore.setSubjectScore(fraction); - } else { - answerUserScore.setSubjectScore(0f); - } - dao.insert(answerUserScore); - } - - public void insertQuestionAnswerScore2(List replyList) { - //问卷id - String questionId = replyList.get(0).getQid(); - //查询今天的题目 - List todayIssues = dao.query(Question_issue.class, Cnd.where("displayDate", "=", DateUtil.today()) - .and("iqid", "=", questionId)); - if (todayIssues != null && todayIssues.size() > 0) { - Set todayIssueIds = todayIssues.stream().map(v -> v.getIid()).collect(Collectors.toSet()); - //判断今天的题目是否已答完 - List replyIssues = dao.query(Question_reply.class, Cnd.where("userId", "=", ShiroUtil.getPrincipalProperty("id")).and("qid", "=", questionId)); - Set replyIssueIds = replyIssues.stream().map(v -> v.getIssueId()).collect(Collectors.toSet()); - if (todayIssueIds.equals(replyIssueIds)) { - //答完了 去看看今天有没有答过 - int count = dao.count(QuestionUserScore.class, Cnd.where("questionId", "=", questionId).and("userId", "=", ShiroUtil.getPrincipalProperty("id")).and("answerDate", "=", DateUtil.today())); - -// AtomicInteger sum = new AtomicInteger(); - AtomicReference sum = new AtomicReference<>(0f); - - //每题的回答记录 - Map> issueReplies = replyIssues.stream().collect(Collectors.groupingBy(v -> v.getIssueId())); - - //查出选项信息 - dao.fetchLinks(todayIssues, "wors"); - - issueReplies.forEach((k, v) -> { - //用户回答的选项 - Set userReplyOptionIds = v.stream().map(x -> x.getRwid()).collect(Collectors.toSet()); - - Optional issueOptional = todayIssues.stream().filter(x -> x.getIid().equals(k)).findFirst(); - - //正确答案 - Set correctOptionIds = issueOptional.map(x -> x.getWors().stream().filter(c -> c.getCorrect() != null && c.getCorrect()).map(z -> z.getWid()).collect(Collectors.toSet())).orElse(null); - if (userReplyOptionIds.equals(correctOptionIds)) { -// sum.addAndGet(issueOptional.map(i -> i.getFraction()).orElse(0f)); - sum.updateAndGet(v1 -> v1 + issueOptional.map(i -> i.getFraction()).orElse(0f)); - } - }); - - QuestionUserScore questionUserScore = new QuestionUserScore(); - questionUserScore.setQuestionId(questionId); - questionUserScore.setUserId((String) ShiroUtil.getPrincipalProperty("id")); - questionUserScore.setAnswerDate(new Date()); - questionUserScore.setAnswerNum(count + 1); - questionUserScore.setScore(sum.get()); - dao.insert(questionUserScore); - - } - } - } /** * 答题信息 @@ -306,8 +336,25 @@ public class QuestionH5Controller { * @return {@link Object} */ @At("/answerInfo/?") - public Object answerInfo(String id) { - List replies = questionService.dao().query(Question_reply.class, Cnd.where("qid", "=", id).and("userId", "=", ShiroUtil.getPrincipalProperty("id"))); + public Object answerInfo(String id, + @Param(required = false, value = "currentDate") String currentDate, + @Param(required = false, value = "randomCount") Integer randomCount) { + Cnd cnd = Cnd.where("qid", "=", id).and("userId", "=", ShiroUtil.getPrincipalProperty("id")); + + Question question = dao.fetch(Question.class, id); + if (question.getCtMode() == 1) { + cnd.and("DATE(rtime)", "=", currentDate); + } else if (question.getCtMode() == 2) { + if (questionService.isSameDay(question.getStartTime(), question.getEndTime())) { + + } else { + cnd.and("DATE(rtime)", "=", currentDate); + } + } else if (question.getCtMode() == 3) { + + } + + List replies = questionService.dao().query(Question_reply.class, cnd); return Result.success(replies); } @@ -346,74 +393,156 @@ public class QuestionH5Controller { return Result.success(list); } - @At("/canAnswer") - public Object canAnswer(String questionId) { + /** + * 判断用户是否答完 + * + * @param questionId 问卷ID + * @return + */ + @At + public Object checkUserFinishAnswer(String questionId, + @Param(required = false, value = "currentDate") String currentDate, + @Param(required = false, value = "randomCount") Integer randomCount) { + Question question = dao.fetch(Question.class, questionId); + if (question.getCtMode() == 3) { + QuestionRandomIssue randomIssue = dao.fetch(QuestionRandomIssue.class, Cnd.where("questionId", "=", questionId).and("userId", "=", ShiroUtil.getUserId())); + return Result.success(randomIssue.getIsFinish()); + } else if (question.getCtMode() == 2) { + //判断是否为一天答题 + DateTime startTime = DateUtil.date(question.getStartTime()); + DateTime endTime = DateUtil.date(question.getEndTime()); + long betweenDay = DateUtil.betweenDay(startTime, endTime, true); + + if (betweenDay == 0) { + //同一天 + if (randomCount != null) { + QuestionRandomIssue randomIssue = dao.fetch(QuestionRandomIssue.class, Cnd.where("questionId", "=", questionId).and("userId", "=", ShiroUtil.getUserId()).and("num", "=", randomCount)); + return Result.success(randomIssue.getIsFinish()); + } + return Result.success(false); + } else { + //不是同一天 判断今天是否答完就行了 + QuestionRandomIssue randomIssue = dao.fetch(QuestionRandomIssue.class, Cnd.where("questionId", "=", questionId).and("userId", "=", ShiroUtil.getUserId()).and("displayDate", "=", DateUtil.today())); + //时间过了 + if (DateUtil.parseDate(currentDate).isBefore(DateUtil.parseDate(DateUtil.today()))) { + return Result.success(true); + } + return Result.success(randomIssue.getIsFinish()); + } + } else if (question.getCtMode() == 1) { + QuestionRandomIssue randomIssue = dao.fetch(QuestionRandomIssue.class, Cnd.where("questionId", "=", questionId).and("userId", "=", ShiroUtil.getUserId()).and("displayDate", "=", currentDate)); + //时间过了 + if (DateUtil.parseDate(currentDate).isBefore(DateUtil.parseDate(DateUtil.today()))) { + return Result.success(true); + } + return Result.success(randomIssue.getIsFinish()); + } + return Result.success(false); + } + + @At + public Object checkCanAnswerAgain(String questionId, + @Param(required = false, value = "currentDate") String currentDate, + @Param(required = false, value = "randomCount") Integer randomCount) { + Question question = dao.fetch(Question.class, questionId); + if (question.getCanRepeatedAnswer()) { + //0次则不限制 + if (question.getCanRepeatedAnswerNum() == 0) { + return Result.success(true); + } + + if (question.getCtMode() == 3) { + int count = dao.count(QuestionUserScore.class, Cnd.where("questionId", "=", questionId).and("userId", "=", ShiroUtil.getUserId())); + return Result.success(count < question.getCanRepeatedAnswerNum()); + } else if (question.getCtMode() == 2) { + //判断是否为一天答题 + DateTime startTime = DateUtil.date(question.getStartTime()); + DateTime endTime = DateUtil.date(question.getEndTime()); + long betweenDay = DateUtil.betweenDay(startTime, endTime, true); + if (betweenDay == 0) { + return Result.success(false); + } else { + int count = dao.count(QuestionUserScore.class, Cnd.where("questionId", "=", questionId).and("userId", "=", ShiroUtil.getUserId()).and("answerDate", "=", DateUtil.today())); + return Result.success(count < question.getCanRepeatedAnswerNum()); + } + } else if (question.getCtMode() == 1) { + int count = dao.count(QuestionUserScore.class, Cnd.where("questionId", "=", questionId).and("userId", "=", ShiroUtil.getUserId()).and("answerDate", "=", currentDate)); + //时间过了 + if (Lang.isNotEmpty(currentDate) && DateUtil.parseDate(currentDate).isBefore(DateUtil.parseDate(DateUtil.today()))) { + return Result.success(false); + } + return Result.success(count < question.getCanRepeatedAnswerNum()); + } + } + return Result.success(false); + } + + /** + * 获取得分 + * + * @param questionId + * @param currentDate + * @param randomCount + * @return + */ + @At + public Object getScore(String questionId, + @Param(required = false, value = "currentDate") String currentDate, + @Param(required = false, value = "randomCount") Integer randomCount) { Question question = dao.fetch(Question.class, questionId); - //每天的题目数量 - int issueCount = dao.count(Question_issue.class, Cnd.where("iqid", "=", questionId).and("displayDate", "=", DateUtil.format(new Date(), "yyyy-MM-dd"))); + if (question.getCtMode() == 1) { + QuestionUserScore userScore = dao.fetch(QuestionUserScore.class, Cnd.where("questionId", "=", questionId) + .and("userId", "=", ShiroUtil.getUserId()) + .and("displayDate", "=", currentDate) + .desc(question.getCalcScoreMode() == 1 ? "score" : "answerNum")); + return Result.success().addData(Optional.ofNullable(userScore).map(v -> v.getScore()).orElse(0)); + } else if (question.getCtMode() == 2) { + DateTime startTime = DateUtil.date(question.getStartTime()); + DateTime endTime = DateUtil.date(question.getEndTime()); + long betweenDay = DateUtil.betweenDay(startTime, endTime, true); - //当前最大答题次数 - int maxCount = dao.func(QuestionAnswerUserScore.class, "max", "answerNum", Cnd.where("questionId", "=", questionId).and("userId", "=", ShiroUtil.getPrincipalProperty("id"))); - - //最大答题次数已经答了多少题 - int answerIssue = dao.count(QuestionAnswerUserScore.class, Cnd.where("questionId", "=", questionId).and("userId", "=", ShiroUtil.getPrincipalProperty("id")).and("answerNum", "=", maxCount)); - - //今天的题目已经答了几次 - int todayMaxCount = dao.func(QuestionAnswerUserScore.class, "max", "answerNum", - Cnd.where("questionId", "=", questionId) - .and("userId", "=", ShiroUtil.getPrincipalProperty("id")) - .and("answerDate", "=", DateUtil.format(new Date(), "yyyy-MM-dd")) - ); - - - - - if (question.getAnswerTotalSum() != 0 && maxCount < question.getAnswerTotalSum()) { - //今天的题目需要全部答完 - -// //今天所有的题目 -// int todayAllIssues = dao.count(Question_issue.class, Cnd.where("iqid", "=", questionId).and("displayDate", "=", DateUtil.format(new Date(), "yyyy-MM-dd"))); -// //今天已经答了的题目 -// int todayHasAnswerIssues = dao.count(QuestionAnswerUserScore.class, Cnd.where("questionId", "=", questionId) -// .and("userId", "=", ShiroUtil.getPrincipalProperty("id")) -// .and("answerDate", "=", DateUtil.format(new Date(), "yyyy-MM-dd")) -// .and("answerNum", "=", todayMaxCount) -// -// ); - - return Result.success(true); + if (betweenDay == 0) { + QuestionUserScore userScore = dao.fetch(QuestionUserScore.class, Cnd.where("questionId", "=", questionId) + .and("userId", "=", ShiroUtil.getUserId()) +// .and("displayDate", "=", currentDate) + .and("answerNum", "=", randomCount) + .desc("answerNum")); + return Result.success().addData(Optional.ofNullable(userScore).map(v -> v.getScore()).orElse(0)); + } else { + QuestionUserScore userScore = dao.fetch(QuestionUserScore.class, Cnd.where("questionId", "=", questionId) + .and("userId", "=", ShiroUtil.getUserId()) + .and("displayDate", "=", currentDate) + .desc(question.getCalcScoreMode() == 1 ? "score" : "answerNum")); + return Result.success().addData(Optional.ofNullable(userScore).map(v -> v.getScore()).orElse(0)); + } + } else if (question.getCtMode() == 3) { + QuestionUserScore userScore = dao.fetch(QuestionUserScore.class, Cnd.where("questionId", "=", questionId) + .and("userId", "=", ShiroUtil.getUserId()) + .desc(question.getCalcScoreMode() == 1 ? "score" : "answerNum")); + return Result.success().addData(Optional.ofNullable(userScore).map(v -> v.getScore()).orElse(0)); } - - if (question.getQrkdcs() != 0 && question.getQrkdcs() > todayMaxCount) { - return Result.success(true); - } - - if (answerIssue < issueCount) { - return Result.success(true); - } - - return Result.success(false); - -// return Result.success(answerIssue < issueCount || (question.getQrkdcs() > 0 && question.getQrkdcs() > todayMaxCount)); - + return Result.success(); } - @At("/todayMaxScore") - public Object todayMaxScore(String questionId) { - List userScores = dao.query(QuestionUserScore.class, Cnd.where("questionId", "=", questionId) - .and("userId", "=", ShiroUtil.getPrincipalProperty("id")) - .and("answerDate", "=", DateUtil.format(new Date(), "yyyy-MM-dd"))); - - Map numScoreMap = userScores.stream().collect(Collectors.toMap(v -> v.getAnswerNum(), v -> v.getScore())); - -// List list = dao.query(QuestionAnswerUserScore.class, Cnd.where("questionId", "=", questionId) -// .and("userId", "=", ShiroUtil.getPrincipalProperty("id")) -// .and("answerDate", "=", DateUtil.format(new Date(), "yyyy-MM-dd"))); -// -// Map numScoreMap = list.stream().collect(Collectors.groupingBy(v -> v.getAnswerNum(), Collectors.summingInt(v -> v.getSubjectScore()))); - return Result.success(numScoreMap); + /** + * 获取用户已回答的轮数 针对单天随机 + * + * @param questionId 问卷ID + * @return + */ + @At + public Object getUserHasAnswerRandomNum(String questionId) { + Question question = dao.fetch(Question.class, questionId); + //判断是否为一天答题 + DateTime startTime = DateUtil.date(question.getStartTime()); + DateTime endTime = DateUtil.date(question.getEndTime()); + long betweenDay = DateUtil.betweenDay(startTime, endTime, true); + if (question.getCtMode() == 2 && betweenDay == 0) { + int count = dao.count(QuestionRandomIssue.class, Cnd.where("questionId", "=", questionId) + .and("userId", "=", ShiroUtil.getUserId())); + return Result.success().addData(count); + } + return Result.success().addData(0); } - - } diff --git a/src/main/java/io/v/nutz/question/controller/QuestionH5CtguController.java b/src/main/java/io/v/nutz/question/controller/QuestionH5CtguController.java deleted file mode 100644 index 08026e8..0000000 --- a/src/main/java/io/v/nutz/question/controller/QuestionH5CtguController.java +++ /dev/null @@ -1,273 +0,0 @@ -package io.v.nutz.question.controller; - -import cn.hutool.core.date.DateUtil; -import cn.wizzer.framework.base.Result; -import io.v.nutz.dao.CndPlus; -import io.v.nutz.question.model.*; -import io.v.nutz.question.service.QuestionService; -import io.v.nutz.util.ShiroUtil; -import org.nutz.aop.interceptor.ioc.TransAop; -import org.nutz.dao.Cnd; -import org.nutz.dao.Dao; -import org.nutz.dao.Sqls; -import org.nutz.dao.sql.Sql; -import org.nutz.dao.util.Daos; -import org.nutz.ioc.aop.Aop; -import org.nutz.ioc.loader.annotation.Inject; -import org.nutz.ioc.loader.annotation.IocBean; -import org.nutz.lang.util.NutMap; -import org.nutz.mvc.annotation.At; -import org.nutz.mvc.annotation.Ok; -import org.nutz.mvc.annotation.Param; -import org.springframework.web.bind.annotation.PathVariable; - -import java.util.*; -import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Collectors; - -/** - * 三峡答题 - * - * @author jug - * @date 2023/10/20 - */ -@IocBean -@At("/mobile/question/h5ctgu") -@Ok("json:full") -public class QuestionH5CtguController { - - @Inject - private QuestionService questionService; - - @Inject - private Dao dao; - - @At("/score") - @Ok("beetl:/mobile/question/ctgu_answer/score.html") - public void score() { - - } - - @At("/answerSheet") - @Ok("beetl:/mobile/question/ctgu_answer/answerSheet.html") - public void answerSheet(){ - - } - - - /** - * 问卷信息 - * - * @param id id - * @return {@link Object} - */ - @At("/questionInfo/{id}") - public Object questionInfo(@PathVariable String id) { - Question question = questionService.fetch(id); - dao.fetchLinks(question, "issues", Cnd.NEW().asc("ipxbh")); - for (Question_issue issue : question.getIssues()) { - questionService.fetchLinks(issue, "wors", Cnd.NEW().asc("wpxbh")); - } -// List issues = question.getIssues(); -// Collections.shuffle(issues.subList(0,issues.size())); -// Collections.shuffle(issues.subList(22,issues.size())); - return Result.success(question); - } - - /** - * 保存、提交答案 - * - * @param replies - * @param questionId - * @param isSave - * @return {@link Object} - */ - @At - @Aop(TransAop.READ_COMMITTED) - public Object saveAnswer(@Param("replies") NutMap[] replies, String questionId, Boolean isSave) { - - dao.clear(Question_reply.class, Cnd.where("userId", "=", ShiroUtil.getUserId()).and("qid", "=", questionId)); - - List replyList = new ArrayList<>(); - for (NutMap reply : replies) { - for (String userSelect : reply.getList("userSelect", String.class)) { - Question_reply questionReply = new Question_reply(); - questionReply.setRwid(userSelect); - questionReply.setIssueId(reply.getString("issueId")); - replyList.add(questionReply); - } - } - - for (Question_reply questionReply : replyList) { - questionReply.setQid(questionId); - questionReply.setRtime(DateUtil.now()); - questionReply.setUserId(ShiroUtil.getUserId()); - questionReply.setSave(isSave); - } - - dao.insert(replyList); - - if (!isSave) { - //全部答完提交计算分数 - Sql answerSql = Sqls.create("SELECT count(DISTINCT issueId) as count FROM Question_reply WHERE qid=@questionId AND userId=@userId AND save=0"); - answerSql.setParam("questionId", questionId); - answerSql.setParam("userId", ShiroUtil.getUserId()); - NutMap answerCountMap = (NutMap) Daos.query(dao, answerSql.toString(), Sqls.callback.map()); - int answerCount = answerCountMap.getInt("count", 0); - - int issueCount = dao.count(Question_issue.class, Cnd.where("iqid", "=", questionId)); - - if (answerCount == issueCount) { - insertQuestionAnswerScore2(null, questionId); - } - } - - return Result.success(); - } - - /** - * 回答信息 - * - * @param id - * @return {@link Object} - */ - @At("/answerInfo/?") - public Object answerInfo(String id) { - List replies = questionService.dao().query(Question_reply.class, Cnd.where("qid", "=", id).and("userId", "=", ShiroUtil.getUserId())); - return Result.success(replies); - } - - /** - * 检查用户是否答完 - * - * @param questionId - * @return {@link Object} - */ - @At - public Object checkUserFinishAnswer(String questionId) { - Sql answerSql = Sqls.create("SELECT count(DISTINCT issueId) as count FROM Question_reply WHERE qid=@questionId AND userId=@userId AND save=0"); - answerSql.setParam("questionId", questionId); - answerSql.setParam("userId", ShiroUtil.getUserId()); - NutMap answerCountMap = (NutMap) Daos.query(dao, answerSql.toString(), Sqls.callback.map()); - int answerCount = answerCountMap.getInt("count", 0); - -// int answerCount = dao.count(Question_reply.class, Cnd.where("userId", "=", ShiroUtil.getUserId()).and("qid", "=", questionId).and("save", "=", 0)); - int issueCount = dao.count(Question_issue.class, Cnd.where("iqid", "=", questionId)); - return Result.success(answerCount == issueCount); - } - - @At - public Object getScore(String questionId) { - int fullScore = dao.func(Question_issue.class, "sum", "fraction", Cnd.where("iqid", "=", questionId)); - - //历史最高分 - Integer maxScore = dao.func(QuestionUserScore.class, "max", "score", Cnd.where("questionId", "=", questionId).and("userId", "=", ShiroUtil.getUserId())); - - //当前得分 - Float score = dao.fetch(QuestionUserScore.class, Cnd.where("questionId", "=", questionId).and("userId", "=", ShiroUtil.getUserId()).desc("answerNum")).getScore(); - - //回答次数 - int answerCount = dao.count(QuestionUserScore.class, Cnd.where("questionId", "=", questionId).and("userId", "=", ShiroUtil.getUserId())); - - String qtitle = dao.fetch(Question.class, questionId).getQtitle(); - Sql sql = Sqls.create("select username,loginname,sex,unitname,unionname from `user` where id = @id").setParam("id", ShiroUtil.getUserId()); - NutMap userMap = (NutMap) Daos.query(dao, sql.toString(), Sqls.callback.map()); - - NutMap map = NutMap.NEW(); - map.put("maxScore", maxScore); - map.put("score", score); - map.put("title", qtitle); - map.put("user", userMap); - map.put("answerCount", answerCount); - - double percentage = (double) score / fullScore * 100; - String message; - - if (percentage >= 90) { - message = "你真棒!这是一个优秀的成绩!"; - } else if (percentage >= 80) { - message = "不错哦!你取得了一个良好的成绩!"; - } else if (percentage >= 70) { - message = "还不错,继续努力,你可以取得更好的成绩!"; - } else if (percentage >= 60) { - message = "及格了,但还有进步的空间,请继续努力!"; - } else { - message = "很遗憾,你没有及格,但别灰心,下次会更好的!"; - } - - map.put("message", message); - return Result.success(map); - } - - @At("/canAnswer") - public Object canAnswer(String questionId) { - Question question = dao.fetch(Question.class, questionId); - - //题目数量 - int issueCount = dao.count(Question_issue.class, Cnd.where("iqid", "=", questionId)); - - //当前最大答题次数 - int maxCount = dao.func(QuestionAnswerUserScore.class, "max", "answerNum", Cnd.where("questionId", "=", questionId).and("userId", "=", ShiroUtil.getUserId())); - - //最大答题次数已经答了多少题 - int answerIssue = dao.count(QuestionAnswerUserScore.class, Cnd.where("questionId", "=", questionId).and("userId", "=", ShiroUtil.getUserId()).and("answerNum", "=", maxCount)); - - //今天的题目已经答了几次 - int todayMaxCount = dao.func(QuestionAnswerUserScore.class, "max", "answerNum", Cnd.where("questionId", "=", questionId).and("userId", "=", io.v.nutz.web.commons.utils.ShiroUtil.getPrincipalProperty("id")).and("answerDate", "=", DateUtil.format(new Date(), "yyyy-MM-dd"))); - - - if (question.getAnswerTotalSum() != 0 && maxCount < question.getAnswerTotalSum()) { - return Result.success(true); - } - - if (question.getQrkdcs() != 0 && question.getQrkdcs() > todayMaxCount) { - return Result.success(true); - } - - if (answerIssue < issueCount) { - return Result.success(true); - } - return Result.success(false); - } - - private void insertQuestionAnswerScore2(List replyList, String questionId) { - int answerNum = dao.func(QuestionUserScore.class, "max", "answerNum", Cnd.where("questionId", "=", questionId).and("userId", "=", ShiroUtil.getUserId())); - - //查询题目及选项信息 - List issues = dao.query(Question_issue.class, Cnd.where("iqid", "=", questionId)); - dao.fetchLinks(issues, "wors"); - -// AtomicReference score = new AtomicReference<>(); - AtomicReference score = new AtomicReference<>((float) 0); - - - List replyIssues = dao.query(Question_reply.class, Cnd.where("userId", "=", io.v.nutz.web.commons.utils.ShiroUtil.getPrincipalProperty("id")).and("qid", "=", questionId)); - - //每题的回答记录 - Map> issueReplies = replyIssues.stream().collect(Collectors.groupingBy(v -> v.getIssueId())); - - issueReplies.forEach((k, v) -> { - //用户回答的选项 - Set userReplyOptionIds = v.stream().map(x -> x.getRwid()).collect(Collectors.toSet()); - - //正确的选项 - Set correctOptionIds = issues.stream().filter(x -> x.getIid().equals(k)).findFirst().map(x -> x.getWors().stream().filter(c -> c.getCorrect() != null && c.getCorrect()).map(c -> c.getWid()).collect(Collectors.toSet())).orElse(null); - - if (userReplyOptionIds.equals(correctOptionIds)) { - //该题的分数 - double sum = issues.stream().filter(x -> x.getIid().equals(k)).mapToDouble(x -> x.getFraction() == null ? 0 : x.getFraction()).sum(); -// score.addAndGet(sum); - score.updateAndGet(v1 -> (float) (v1 + sum)); - } - }); - - QuestionUserScore questionUserScore = new QuestionUserScore(); - questionUserScore.setQuestionId(questionId); - questionUserScore.setUserId(ShiroUtil.getUserId()); - questionUserScore.setAnswerDate(new Date()); - questionUserScore.setAnswerNum(answerNum + 1); - questionUserScore.setScore(score.get()); - dao.insert(questionUserScore); - } - -} diff --git a/src/main/java/io/v/nutz/question/controller/QuestionMangeController.java b/src/main/java/io/v/nutz/question/controller/QuestionMangeController.java index 9893685..d4468c6 100644 --- a/src/main/java/io/v/nutz/question/controller/QuestionMangeController.java +++ b/src/main/java/io/v/nutz/question/controller/QuestionMangeController.java @@ -1,11 +1,12 @@ package io.v.nutz.question.controller; +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.StrUtil; import cn.hutool.http.HtmlUtil; import cn.wizzer.framework.base.Result; import io.v.nutz.annontation.ViReturn; -import io.v.nutz.base.utils.DateUtil; import io.v.nutz.modules.models.PageForm; import io.v.nutz.question.model.Question; import io.v.nutz.question.model.Question_issue; @@ -18,7 +19,7 @@ import io.v.nutz.question.service.QuestionWorService; import io.v.nutz.sys.models.Sys_task; import io.v.nutz.sys.services.SysTaskService; import io.v.nutz.task.services.TaskPlatformService; -import io.v.nutz.util.ShiroUtil; +import io.v.nutz.web.commons.utils.ShiroUtil; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.nutz.aop.interceptor.ioc.TransAop; import org.nutz.dao.Chain; @@ -30,18 +31,18 @@ import org.nutz.ioc.aop.Aop; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.json.Json; +import org.nutz.lang.Lang; import org.nutz.lang.Strings; import org.nutz.lang.random.R; +import org.nutz.lang.util.NutMap; import org.nutz.mvc.annotation.At; import org.nutz.mvc.annotation.Ok; import org.nutz.mvc.annotation.POST; import org.nutz.mvc.annotation.Param; import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; +import java.text.SimpleDateFormat; +import java.util.*; import java.util.stream.Collectors; /** @@ -98,6 +99,7 @@ public class QuestionMangeController { sql.setCondition(cnd); return Result.success().addData(questionService.listPage(pageForm.getPageNumber(), pageForm.getPageSize(), sql)); } catch (Exception e) { + e.printStackTrace(); return Result.error(); } } @@ -114,8 +116,6 @@ public class QuestionMangeController { @RequiresPermissions("question.mange") public Object upStatus(String qid, Integer flag) { try { - Assert.notBlank(qid); - Assert.notNull(flag); dao.update(Question.class, Chain.make("qflag", flag), Cnd.where("qid", "=", qid)); return Result.success(); } catch (Exception e) { @@ -131,13 +131,23 @@ public class QuestionMangeController { public Object doAdd(@Param("question") Question question) { try { question.setQflag(1); - question.setQctime(DateUtil.getDate()); + question.setQctime(DateUtil.now()); question.setQcreator(ShiroUtil.getPrincipalProperty("id").toString()); + //这里要额外判断下随机模式下的答题天数 + if (question.getQlx() == 2) { + DateTime startTime = DateUtil.date(question.getStartTime()); + DateTime endTime = DateUtil.date(question.getEndTime()); + long betweenDay = DateUtil.betweenDay(startTime, endTime, true); + if (betweenDay < 1) { + //同一天 + question.setCanRepeatedAnswer(false); + question.setCanRepeatedAnswerNum(null); + } + } + Question insert = questionService.insert(question); - int i = 1; - for (Question_issue issue : question.getIssues()) { issue.setIid(Strings.isNotBlank(issue.getIid()) ? issue.getIid() : R.UU32()); issue.setIqid(insert.getQid()); @@ -145,7 +155,6 @@ public class QuestionMangeController { if (question.getCtMode() == 2) { issue.setIpxbh(i); } - Question_issue issueInsert = questionIssueService.insert(issue); int j = 1; for (Question_wor wor : issue.getWors()) { @@ -157,9 +166,7 @@ public class QuestionMangeController { } i++; } - - raffleTask(question.getQid()); - +// raffleTask(question.getQid()); return Result.success(); } catch (Exception e) { e.printStackTrace(); @@ -173,6 +180,17 @@ public class QuestionMangeController { @ViReturn @RequiresPermissions("question.mange") public Object doEdit(@Param("question") Question question) { + //这里要额外判断下随机模式下的答题天数 + if (question.getQlx() == 2) { + DateTime startTime = DateUtil.date(question.getStartTime()); + DateTime endTime = DateUtil.date(question.getEndTime()); + long betweenDay = DateUtil.betweenDay(startTime, endTime, true); + if (betweenDay < 1) { + //同一天 + question.setCanRepeatedAnswer(false); + question.setCanRepeatedAnswerNum(null); + } + } questionService.update(question); List oldIssues = dao.query(Question_issue.class, Cnd.where("iqid", "=", question.getQid())); @@ -182,11 +200,9 @@ public class QuestionMangeController { for (int i = 0; i < issues.size(); i++) { Question_issue issue = issues.get(i); issue.setIqid(question.getQid()); -// if (question.getCtMode() == 2) { - issue.setIpxbh(i + 1); -// } - System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); - System.out.println(Json.toJson(issue)); + if (question.getCtMode() == 2) { + issue.setIpxbh(i + 1); + } dao.insertOrUpdate(issue); List wors = issue.getWors(); for (int i1 = 0; i1 < wors.size(); i1++) { @@ -241,7 +257,7 @@ public class QuestionMangeController { /** - * 删除文具按 + * 删除问卷 * * @param qid qid * @return {@link Object} @@ -311,13 +327,23 @@ public class QuestionMangeController { @At("/modifyBasicInfo") @RequiresPermissions("question.mange") @ViReturn - public Object modifyBasicInfo(Question question) { + public Object modifyBasicInfo(@Param("question") Question question) { try { Assert.notNull(question); + if (question.getQlx() == 2) { + DateTime startTime = DateUtil.date(question.getStartTime()); + DateTime endTime = DateUtil.date(question.getEndTime()); + long betweenDay = DateUtil.betweenDay(startTime, endTime, true); + if (betweenDay < 1) { + //同一天 + question.setCanRepeatedAnswer(false); + question.setCanRepeatedAnswerNum(null); + } + } if (StrUtil.isNotBlank(question.getQid())) { dao.updateIgnoreNull(question); } else { - question.setQctime(cn.hutool.core.date.DateUtil.today()); + question.setQctime(DateUtil.today()); question.setQcreator(ShiroUtil.getUserId()); question.setQflag(1); dao.insert(question); @@ -373,7 +399,7 @@ public class QuestionMangeController { Question_issue issue = issueList.get(i); issue.setIqid(questionId); // if (question.getCtMode() == 2) { - issue.setIpxbh(i + 1); + issue.setIpxbh(i + 1); // } dao.insertOrUpdate(issue); List wors = issue.getWors(); @@ -418,41 +444,41 @@ public class QuestionMangeController { * @param questionId 问题id */ private void raffleTask(String questionId) { - Question question = dao.fetch(Question.class, questionId); - - //每天凌晨一点执行 - String cron = "0 0 1 * * ?"; - cron = "30 * * * * ? "; - String cronData = Json.toJson(Map.of("questionId", questionId)); + //删除之前的任务再添加 + List oldTasks = dao.query(Sys_task.class, Cnd.where("data", "like", "%" + questionId + "%")); + for (Sys_task task : oldTasks) { + taskPlatformService.delete(task.getId(), task.getId()); + sysTaskService.delete(task.getId()); + } String jobClass = "io.v.nutz.task.job.question.QuestionRaffleJob"; - Sys_task task = sysTaskService.fetch(Cnd.where("jobClass", "=", jobClass).and("data", "=", cronData)); - + Question question = dao.fetch(Question.class, questionId); //需要抽奖 if (question.getIsRaffle() != null && question.getIsRaffle()) { - if (task != null) { + + String dateFormat = "ss mm HH dd MM ? yyyy"; + SimpleDateFormat sdf = new SimpleDateFormat(dateFormat); + + List raffleRules = question.getRaffleRule(); + for (Question.RaffleRule raffleRule : raffleRules) { + Date raffleTime = raffleRule.getRaffleTime(); + String formatTimeStr = sdf.format(raffleTime); + + NutMap raffleRuleMap = Lang.obj2nutmap(raffleRule); + raffleRuleMap.put("questionId", questionId); + + String cronData = Json.toJson(raffleRuleMap); + + Sys_task task = new Sys_task(); task.setName(HtmlUtil.cleanHtmlTag(question.getQtitle()) + "问卷调查抽奖任务"); - task.setCron(cron); - sysTaskService.updateIgnoreNull(task); - if (taskPlatformService.isExist(task.getId(), task.getId())) { - taskPlatformService.delete(task.getId(), task.getId()); - } - } else { - task = new Sys_task(); - task.setName(HtmlUtil.cleanHtmlTag(question.getQsm()) + "问卷调查抽奖任务"); task.setJobClass(jobClass); task.setNote("问卷调查抽奖任务"); - task.setCron(cron); + task.setCron(formatTimeStr); task.setData(cronData); task.setDisabled(false); sysTaskService.insert(task); - } - taskPlatformService.add(task.getId(), task.getId(), task.getJobClass(), task.getCron(), - task.getNote(), task.getData()); - } else { - if (task != null) { - taskPlatformService.delete(task.getId(), task.getId()); - sysTaskService.delete(task.getId()); + taskPlatformService.add(task.getId(), task.getId(), task.getJobClass(), task.getCron(), + task.getNote(), task.getData()); } } } diff --git a/src/main/java/io/v/nutz/question/controller/QuestionPollAnalysisController.java b/src/main/java/io/v/nutz/question/controller/QuestionPollAnalysisController.java index 8eccbe7..a81d488 100644 --- a/src/main/java/io/v/nutz/question/controller/QuestionPollAnalysisController.java +++ b/src/main/java/io/v/nutz/question/controller/QuestionPollAnalysisController.java @@ -3,8 +3,6 @@ package io.v.nutz.question.controller; import cn.afterturn.easypoi.excel.ExcelExportUtil; import cn.afterturn.easypoi.excel.annotation.Excel; import cn.afterturn.easypoi.excel.entity.ExportParams; -import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity; -import cn.hutool.http.HtmlUtil; import cn.wizzer.framework.base.Result; import io.v.nutz.base.utils.Html2Text; import io.v.nutz.modules.models.PageForm; @@ -14,7 +12,6 @@ import io.v.nutz.question.service.QuestionIssueService; import io.v.nutz.question.service.QuestionReplyService; import io.v.nutz.question.service.QuestionService; import io.v.nutz.question.service.QuestionWorService; -import io.v.nutz.sys.models.Sys_union; import lombok.Data; import org.apache.poi.ss.usermodel.Workbook; import org.apache.shiro.authz.annotation.RequiresPermissions; @@ -25,15 +22,12 @@ import org.nutz.dao.sql.Sql; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.lang.Strings; -import org.nutz.lang.util.NutMap; import org.nutz.mvc.annotation.At; import org.nutz.mvc.annotation.Ok; import org.nutz.mvc.annotation.Param; import javax.servlet.http.HttpServletResponse; -import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -75,17 +69,13 @@ public class QuestionPollAnalysisController { @Param("pageNumber") int pageNumber, @Param("pageSize") int pageSize) { try { - Sql sql = Sqls.create(""" - select r.*,u.username,u.unionname,u.unitname,u.mobile from question_reply r left join `user` u on r.userId = u.id $condition - """); Cnd cnd = Cnd.NEW(); cnd.and("rwid", "=", wid); if (Strings.isNotBlank(searchKeyword)) { cnd.where().andLike("rval", searchKeyword); } cnd.desc("rtime"); - sql.setCondition(cnd); - return Result.success().addData(questionReplyService.listPage(pageNumber, pageSize, sql)); + return Result.success().addData(questionReplyService.listPage(pageNumber, pageSize, cnd)); } catch (Exception e) { return Result.error(); } @@ -166,7 +156,6 @@ public class QuestionPollAnalysisController { } @At - @Ok("void") @RequiresPermissions("question.pollAnalysis") public void doExport(@Param(value = "questionId",required = false) String questionId, HttpServletResponse response) { try { @@ -211,52 +200,6 @@ public class QuestionPollAnalysisController { } } - @At - @Ok("void") - @RequiresPermissions("question.pollAnalysis") - public void doExportByUnion(@Param(value = "questionId",required = false) String questionId, HttpServletResponse response) throws IOException { - //查询所有工会 - List unionList = questionService.dao().query(Sys_union.class, Cnd.NEW()); - //查询所有答复 - Sql sql = Sqls.create(""" - select r.*,u.unionId,u.username,u.mobile from question_reply r left join `user` u on r.userId = u.id where r.qid = @qid and unionId is not null - """).setParam("qid", questionId); - List replyList = questionService.listMap(sql); - //查询所有题目 - List issueList = questionService.dao().query(Question_issue.class, Cnd.where("iqid", "=", questionId).asc("ipxbh")); - - //设置第一行 - List exportEntities = new ArrayList<>(); - exportEntities.add(new ExcelExportEntity("", "unionname", 30)); - for (int i = 0; i < issueList.size(); i++) { - exportEntities.add(new ExcelExportEntity(HtmlUtil.cleanHtmlTag(issueList.get(i).getIname()), i + "", 30)); - } - exportEntities.add(new ExcelExportEntity("填报人姓名", "username", 30)); - exportEntities.add(new ExcelExportEntity("填报人电话", "mobile", 30)); - - List result = new ArrayList<>(); - unionList.forEach(item -> { - NutMap nutMap = new NutMap(); - nutMap.put("unionname", item.getUnionname()); - for (int i = 0; i < issueList.size(); i++) { - int finalI = i; - NutMap map = replyList.stream().filter(o -> o.getString("unionid").equals(item.getId()) && o.getString("issueId").equals(issueList.get(finalI).getIid())).findFirst().orElse(null); - nutMap.put(i + "", map != null ? map.getString("rval") : ""); - nutMap.put("username", map != null ? map.getString("username") : ""); - nutMap.put("mobile", map != null ? map.getString("mobile") : ""); - } - result.add(nutMap); - }); - - Question question = questionService.fetch(questionId); - String fileName = question.getQtitle() + "按分工会导出"; - response.setContentType("application/octet-stream"); - response.setHeader("Content-Disposition", "attachment;filename=" + new String((fileName + ".xls").getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1)); - Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), exportEntities, result); - workbook.write(response.getOutputStream()); - workbook.close(); - } - @Data public static class ExportEntity { @Excel(name = "序号") diff --git a/src/main/java/io/v/nutz/question/controller/QuestionUnionIntentionalChoiceController.java b/src/main/java/io/v/nutz/question/controller/QuestionUnionIntentionalChoiceController.java index 5852b5c..c913316 100644 --- a/src/main/java/io/v/nutz/question/controller/QuestionUnionIntentionalChoiceController.java +++ b/src/main/java/io/v/nutz/question/controller/QuestionUnionIntentionalChoiceController.java @@ -75,7 +75,24 @@ public class QuestionUnionIntentionalChoiceController { SELECT un.id unionId, un.unionName, - (select count(1) from user where member = 1 and unionid = un.id) AS teacherSum, + (select count(1) from "user" where member = 1 and unionid = un.id) AS teacherSum1, + ( + select + count(1) + from + activity_user_scope aus + left join "user" u on u.id = aus.userId + where + groupId in ( + select + activityUserScopeId + from + question + where + qid = @questionId + ) + and u.unionId = un.id + ) as teacherSum, ( SELECT count( DISTINCT r.userId ) @@ -92,7 +109,7 @@ public class QuestionUnionIntentionalChoiceController { """).setParam("questionId", questionId); CndPlus cndPlus = CndPlus.create(); - cndPlus.andExcludeEmpty("un.id", "=", unionId); + cndPlus.andEX("un.id", "=", unionId); sql.setCondition(cndPlus); List unionList = questionService.listMap(sql); @@ -253,7 +270,7 @@ public class QuestionUnionIntentionalChoiceController { $condition """).setParam("questionId", questionId); - cnd.andExcludeEmpty("u.unionid", "=", unionId); + cnd.andEX("u.unionid", "=", unionId); cnd.groupBy("u.id"); cnd.desc("u.unioncode"); sql.setCondition(cnd); diff --git a/src/main/java/io/v/nutz/question/model/Question.java b/src/main/java/io/v/nutz/question/model/Question.java index 7722b8b..cabd140 100644 --- a/src/main/java/io/v/nutz/question/model/Question.java +++ b/src/main/java/io/v/nutz/question/model/Question.java @@ -4,6 +4,7 @@ import lombok.Data; import org.nutz.dao.entity.annotation.*; import java.io.Serializable; +import java.util.Date; import java.util.List; @@ -51,7 +52,7 @@ public class Question implements Serializable { @Column @Comment("说明") - @ColDefine(type = ColType.VARCHAR, width = 2000) + @ColDefine(customType = "text") private String qsm; @Column @@ -69,11 +70,6 @@ public class Question implements Serializable { @ColDefine(type = ColType.VARCHAR, width = 50) private String qdtjssj; - @Column - @Comment("活动时间内可答题总次数") - @ColDefine(type = ColType.INT, width = 2) - private Integer answerTotalSum; - @Many(field = "iqid") private List issues; @@ -100,16 +96,16 @@ public class Question implements Serializable { @ColDefine(type = ColType.INT) private Integer ctMode; - @Column - @Comment("单次出题数量") - @ColDefine(type = ColType.INT) - private Integer ctNum; - @Column @Comment("是否抽奖") @ColDefine(type = ColType.BOOLEAN) private Boolean isRaffle; + @Column + @Comment("抽奖时间") + @ColDefine(type = ColType.DATETIME) + private Date raffleTime; + @Column @Comment("多少分可以抽奖") @ColDefine(type = ColType.INT) @@ -120,5 +116,59 @@ public class Question implements Serializable { @ColDefine(type = ColType.INT) private Integer raffleWinnerUserNum; + @Column + @Comment("题目顺序打乱") + @ColDefine(type = ColType.BOOLEAN) + private Boolean disruptingOrder; + @Column + @Comment("随机单次出题数量") + @ColDefine(type = ColType.INT) + private Integer ranDomSingleIssueNum; + + @Column + @Comment("总共随机次数") + @ColDefine(type = ColType.INT) + private Integer ranDomSum; + + @Column + @Comment("是否多次答") + @ColDefine(type = ColType.BOOLEAN) + private Boolean canRepeatedAnswer; + + @Column + @Comment("多次答数") + @ColDefine(type = ColType.INT) + private Integer canRepeatedAnswerNum; + + @Column + @Comment("得分统计(重复答题允许下 1.保存最高分 2.保存最新得分)") + @ColDefine(type = ColType.INT) + private Integer calcScoreMode; + + @Column + @Comment("抽奖规则") + @ColDefine(type = ColType.MYSQL_JSON) + private List raffleRule; + + /** + * 抽奖规则 + */ + @Data + public static class RaffleRule { + //开始答题时间 + private Date startAnswerDate; + + //结束答题时间 + private Date endAnswerDate; + + //抽奖时间 + private Date raffleTime; + + //抽奖分数 + private Integer raffleScore; + + //抽奖人数 + private Integer raffleWinnerUserNum; + } } diff --git a/src/main/java/io/v/nutz/question/model/QuestionAnswerUserScore.java b/src/main/java/io/v/nutz/question/model/QuestionAnswerUserScore.java index 2929093..dd832a2 100644 --- a/src/main/java/io/v/nutz/question/model/QuestionAnswerUserScore.java +++ b/src/main/java/io/v/nutz/question/model/QuestionAnswerUserScore.java @@ -34,8 +34,8 @@ public class QuestionAnswerUserScore { @Column @Comment("该题得分") - @ColDefine(type = ColType.FLOAT) - private Float subjectScore; + @ColDefine(type = ColType.INT) + private Integer subjectScore; @Column @Comment("用户id") diff --git a/src/main/java/io/v/nutz/question/model/QuestionRandomIssue.java b/src/main/java/io/v/nutz/question/model/QuestionRandomIssue.java new file mode 100644 index 0000000..e7c6d85 --- /dev/null +++ b/src/main/java/io/v/nutz/question/model/QuestionRandomIssue.java @@ -0,0 +1,52 @@ +package io.v.nutz.question.model; + +import io.v.nutz.base.model.BaseModel; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.nutz.dao.entity.annotation.*; +import org.nutz.dao.interceptor.annotation.PrevInsert; + +import java.util.Date; +import java.util.List; + +@Data +@EqualsAndHashCode(callSuper = false) +@Comment("问卷随机题目") +@Table +public class QuestionRandomIssue extends BaseModel { + + @Name + @ColDefine(type = ColType.VARCHAR, width = 32) + @PrevInsert(uu32 = true) + private String id; + + @Column + @Comment("问卷的id") + @ColDefine(type = ColType.VARCHAR, width = 32) + private String questionId; + + @Column + @Comment("用户id") + @ColDefine(type = ColType.VARCHAR, width = 32) + private String userId; + + @Column + @Comment("题目") + @ColDefine(type = ColType.MYSQL_JSON) + private List issueIds; + + @Column + @Comment("回答次数") + @ColDefine(type = ColType.INT) + private Integer num; + + @Column + @Comment("是否回答完成") + @ColDefine(type = ColType.BOOLEAN) + private Boolean isFinish; + + @Column + @Comment("显示日期") + @ColDefine(type = ColType.DATE) + private Date displayDate; +} diff --git a/src/main/java/io/v/nutz/question/model/QuestionUserScore.java b/src/main/java/io/v/nutz/question/model/QuestionUserScore.java index f73c4cc..b417aa0 100644 --- a/src/main/java/io/v/nutz/question/model/QuestionUserScore.java +++ b/src/main/java/io/v/nutz/question/model/QuestionUserScore.java @@ -1,6 +1,8 @@ package io.v.nutz.question.model; +import io.v.nutz.base.model.BaseModel; import lombok.Data; +import lombok.EqualsAndHashCode; import org.nutz.dao.entity.annotation.*; import org.nutz.dao.interceptor.annotation.PrevInsert; @@ -12,9 +14,10 @@ import java.util.Date; * @author jug * @date 2023/06/20 */ +@EqualsAndHashCode(callSuper = true) @Data @Table -public class QuestionUserScore { +public class QuestionUserScore extends BaseModel { @Name @ColDefine(type = ColType.VARCHAR, width = 32) @@ -28,8 +31,8 @@ public class QuestionUserScore { @Column @Comment("得分") - @ColDefine(type = ColType.FLOAT) - private Float score; + @ColDefine(type = ColType.INT) + private Integer score; @Column @Comment("用户id") @@ -46,4 +49,13 @@ public class QuestionUserScore { @ColDefine(type = ColType.INT) private Integer answerNum; + @Column + @Comment("随机次数") + @ColDefine(type = ColType.INT) + private Integer randomNum; + + @Column + @Comment("显示日期(只针对定时定题)") + @ColDefine(type = ColType.DATE) + private Date displayDate; } diff --git a/src/main/java/io/v/nutz/question/model/QuestionWinner.java b/src/main/java/io/v/nutz/question/model/QuestionWinner.java index 7d18bd9..6361c57 100644 --- a/src/main/java/io/v/nutz/question/model/QuestionWinner.java +++ b/src/main/java/io/v/nutz/question/model/QuestionWinner.java @@ -37,4 +37,9 @@ public class QuestionWinner { @ColDefine(type = ColType.DATE) private Date winDate; + @Column + @Comment("分数") + @ColDefine(type = ColType.INT) + private Integer score; + } diff --git a/src/main/java/io/v/nutz/question/model/Question_issue.java b/src/main/java/io/v/nutz/question/model/Question_issue.java index 94d74cf..aafc1e5 100644 --- a/src/main/java/io/v/nutz/question/model/Question_issue.java +++ b/src/main/java/io/v/nutz/question/model/Question_issue.java @@ -101,8 +101,8 @@ public class Question_issue implements Serializable { @Column @Comment("该题分数") @J4EIgnore - @ColDefine(type = ColType.FLOAT, width = 4, precision = 1) - private Float fraction; + @ColDefine(type = ColType.INT, width = 4) + private Integer fraction; @Column @Comment("显示日期") diff --git a/src/main/java/io/v/nutz/question/model/Question_reply.java b/src/main/java/io/v/nutz/question/model/Question_reply.java index 938c445..f6970d6 100644 --- a/src/main/java/io/v/nutz/question/model/Question_reply.java +++ b/src/main/java/io/v/nutz/question/model/Question_reply.java @@ -5,6 +5,7 @@ import org.nutz.dao.entity.annotation.*; import org.nutz.integration.json4excel.annotation.J4EIgnore; import java.io.Serializable; +import java.util.List; /** @@ -85,4 +86,6 @@ public class Question_reply implements Serializable { @One(field = "rwid", key = "wid") private Question_wor wor; + + private List userSelect; } diff --git a/src/main/java/io/v/nutz/question/service/QuestionAnswerRankingService.java b/src/main/java/io/v/nutz/question/service/QuestionAnswerRankingService.java new file mode 100644 index 0000000..c7074a3 --- /dev/null +++ b/src/main/java/io/v/nutz/question/service/QuestionAnswerRankingService.java @@ -0,0 +1,23 @@ +package io.v.nutz.question.service; + +import io.v.nutz.base.page.Pagination; +import io.v.nutz.base.service.BaseService; +import io.v.nutz.modules.models.PageForm; +import io.v.nutz.question.model.QuestionUserScore; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * 得分 + */ +public interface QuestionAnswerRankingService extends BaseService { + + Pagination pageData(PageForm pageForm, + String questionId, + String unionId, + String unitId, + String answerDate); + + void exportXlsx(String questionId, OutputStream os) throws IOException; +} diff --git a/src/main/java/io/v/nutz/question/service/QuestionReplyService.java b/src/main/java/io/v/nutz/question/service/QuestionReplyService.java index b977747..37c9fcc 100644 --- a/src/main/java/io/v/nutz/question/service/QuestionReplyService.java +++ b/src/main/java/io/v/nutz/question/service/QuestionReplyService.java @@ -1,7 +1,6 @@ package io.v.nutz.question.service; - import io.v.nutz.question.model.Question; import io.v.nutz.question.model.Question_reply; import io.v.nutz.service.ViService; diff --git a/src/main/java/io/v/nutz/question/service/QuestionService.java b/src/main/java/io/v/nutz/question/service/QuestionService.java index 8d76193..6e6afe7 100644 --- a/src/main/java/io/v/nutz/question/service/QuestionService.java +++ b/src/main/java/io/v/nutz/question/service/QuestionService.java @@ -1,6 +1,7 @@ package io.v.nutz.question.service; import io.v.nutz.question.model.Question; +import io.v.nutz.question.model.Question_reply; import io.v.nutz.service.ViService; import org.nutz.lang.util.NutMap; @@ -15,4 +16,29 @@ public interface QuestionService extends ViService { NutMap getRankData(String qid); + + /** + * 答题 + * + * @param repliesArray 记录 + * @param questionId 问卷ID + * @param isSave 是否保存 + */ + void saveAnswer(Question_reply[] repliesArray, String questionId, Boolean isSave); + + + /** + * 是否同天 仅限随机 + * @param questionId 问卷ID + * @return + */ + boolean isSameDay(String questionId); + + /** + * 是否同天 仅限随机 + * @param startTimeTs 开始时间 + * @param endTimeTs 结束时间 + * @return + */ + boolean isSameDay(long startTimeTs,long endTimeTs); } diff --git a/src/main/java/io/v/nutz/question/service/impl/QuestionAnswerRankingServiceImpl.java b/src/main/java/io/v/nutz/question/service/impl/QuestionAnswerRankingServiceImpl.java new file mode 100644 index 0000000..a1528d2 --- /dev/null +++ b/src/main/java/io/v/nutz/question/service/impl/QuestionAnswerRankingServiceImpl.java @@ -0,0 +1,422 @@ +package io.v.nutz.question.service.impl; + +import cn.afterturn.easypoi.excel.ExcelExportUtil; +import cn.afterturn.easypoi.excel.entity.ExportParams; +import cn.afterturn.easypoi.excel.entity.enmus.ExcelType; +import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity; +import cn.hutool.core.util.StrUtil; +import io.v.nutz.base.page.Pagination; +import io.v.nutz.base.service.impl.BaseServiceImpl; +import io.v.nutz.modules.models.PageForm; +import io.v.nutz.question.model.Question; +import io.v.nutz.question.model.QuestionUserScore; +import io.v.nutz.question.service.QuestionAnswerRankingService; +import io.v.nutz.question.service.QuestionService; +import org.apache.poi.ss.usermodel.Workbook; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.dao.Sqls; +import org.nutz.dao.sql.Sql; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.lang.util.NutMap; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; + +@IocBean(args = {"refer:dao"}) +public class QuestionAnswerRankingServiceImpl extends BaseServiceImpl implements QuestionAnswerRankingService { + public QuestionAnswerRankingServiceImpl(Dao dao) { + super(dao); + } + + @Inject + private QuestionService questionService; + + @Override + public Pagination pageData(PageForm pageForm, String questionId, String unionId, String unitId, String answerDate) { + Question question = dao().fetch(Question.class, questionId); + + if (question.getCtMode() == 1) { + Sql sql = Sqls.create(""" + SELECT + qus.userId, + DATE_FORMAT( qus.answerDate, '%Y-%m-%d' ) as answerDate, + qus.score, + qus.answerNum, + u.loginname, + u.username, + u.sex, + u.unitname, + u.unionname, + u.mobile + FROM + question_user_score qus + LEFT JOIN `user` u ON u.id = qus.userId + $condition + """); + Cnd cnd = Cnd.NEW(); + cnd.and("qus.questionId", "=", questionId); + cnd.andEX("u.unionid", "=", unionId); + cnd.andEX("u.unitid", "=", unitId); + cnd.andEX("qus.answerDate", "=", answerDate); + if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) { + cnd.and(Cnd.exps("u.loginname", "like", "%" + pageForm.getSearchKeyword() + "%").or("u.username", "like", "%" + pageForm.getSearchKeyword() + "%")); + } + cnd.groupBy("qus.answerDate", "qus.userId"); + cnd.asc("qus.answerDate").asc("u.unionid").asc("u.id"); + if (question.getCalcScoreMode() == 1) { + cnd.and("qus.score", "in", Sqls.create("select max(score) from question_user_score where userId = qus.userId and questionId = qus.questionId and answerDate = qus.answerDate")); + } else if (question.getCalcScoreMode() == 2) { + cnd.and("qus.answerNum", "in", Sqls.create("select max(answerNum) from question_user_score where userId = qus.userId and questionId = qus.questionId and answerDate = qus.answerDate")); + } + sql.setCondition(cnd); + return listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); + } else if (question.getCtMode() == 2) { + if (questionService.isSameDay(question.getStartTime(), question.getEndTime())) { + Sql sql = Sqls.create(""" + SELECT + GROUP_CONCAT( qus.answerNum, '(', qus.score, ')' ORDER BY qus.answerNum SEPARATOR ', ' ) AS everyRandomScore, + sum( score ) AS score, + qus.userId, + DATE_FORMAT( qus.answerDate, '%Y-%m-%d' ) AS answerDate, + u.loginname, + u.username, + u.sex, + u.unitname, + u.unionname, + u.mobile, + q.ctMode, + q.calcScoreMode + FROM + `question_user_score` qus + LEFT JOIN `user` u ON u.id = qus.userId + LEFT JOIN question q ON q.qid = qus.questionId + $condition + """); + Cnd cnd = Cnd.NEW(); + cnd.and("qus.questionId", "=", questionId); + cnd.andEX("u.unionid", "=", unionId); + cnd.andEX("u.unitid", "=", unitId); + cnd.andEX("qus.answerDate", "=", answerDate); + if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) { + cnd.and(Cnd.exps("u.loginname", "like", "%" + pageForm.getSearchKeyword() + "%").or("u.username", "like", "%" + pageForm.getSearchKeyword() + "%")); + } + cnd.groupBy("qus.answerDate", "qus.userId"); + cnd.asc("qus.answerDate").asc("u.unionid").asc("u.id"); + sql.setCondition(cnd); + return listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); + } else { + Sql sql = Sqls.create(""" + SELECT + ( + SELECT + score\s + FROM + question_user_score\s + WHERE + userId = qus.userId\s + AND answerDate = qus.answerDate\s + AND answerNum = max( qus.answerNum )\s + AND questionId = qus.questionId + ) as lastAnswerNumScore, + ( + SELECT + score\s + FROM + question_user_score\s + WHERE + userId = qus.userId\s + AND answerDate = qus.answerDate\s + AND score = max( qus.score )\s + AND questionId = qus.questionId + ) as maxAnswerScore, + qus.userId, + DATE_FORMAT( qus.answerDate, '%Y-%m-%d' ) AS answerDate, + qus.answerNum, + max( qus.answerNum ), + min( qus.answerNum ), + qus.score, + u.loginname, + u.username, + u.sex, + u.unitname, + u.unionname, + u.mobile, + q.ctMode, + q.calcScoreMode + FROM + `question_user_score` qus + LEFT JOIN `user` u ON u.id = qus.userId + LEFT JOIN question q ON q.qid = qus.questionId + $condition + """); + Cnd cnd = Cnd.NEW(); + cnd.and("qus.questionId", "=", questionId); + cnd.andEX("u.unionid", "=", unionId); + cnd.andEX("u.unitid", "=", unitId); + cnd.andEX("qus.answerDate", "=", answerDate); + if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) { + cnd.and(Cnd.exps("u.loginname", "like", "%" + pageForm.getSearchKeyword() + "%").or("u.username", "like", "%" + pageForm.getSearchKeyword() + "%")); + } + cnd.groupBy("qus.answerDate", "qus.userId"); + cnd.asc("qus.answerDate").asc("u.unionid").asc("u.id"); + sql.setCondition(cnd); + Pagination pagination = listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); + List list = pagination.getList(); + for (NutMap map : list) { + map.put("score", map.getInt(question.getCalcScoreMode() == 1 ? "maxAnswerScore" : "lastAnswerNumScore")); + } + return pagination; + } + } else if (question.getCtMode() == 3) { + Sql sql = Sqls.create(""" + SELECT + qus.userId, + DATE_FORMAT( qus.answerDate, '%Y-%m-%d' ) as answerDate, + qus.score, + qus.answerNum, + u.loginname, + u.username, + u.sex, + u.unitname, + u.unionname, + u.mobile + FROM + question_user_score qus + LEFT JOIN `user` u ON u.id = qus.userId + $condition + """); + Cnd cnd = Cnd.NEW(); + cnd.and("qus.questionId", "=", questionId); + cnd.andEX("u.unionid", "=", unionId); + cnd.andEX("u.unitid", "=", unitId); + cnd.andEX("qus.answerDate", "=", answerDate); + if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) { + cnd.and(Cnd.exps("u.loginname", "like", "%" + pageForm.getSearchKeyword() + "%").or("u.username", "like", "%" + pageForm.getSearchKeyword() + "%")); + } + cnd.groupBy("qus.answerDate", "qus.userId"); + cnd.asc("qus.answerDate").asc("u.unionid").asc("u.id"); + if (question.getCalcScoreMode() == 1) { + cnd.and("qus.score", "in", Sqls.create("select max(score) from question_user_score where userId = qus.userId and questionId = qus.questionId and answerDate = qus.answerDate")); + } else if (question.getCalcScoreMode() == 2) { + cnd.and("qus.answerNum", "in", Sqls.create("select max(answerNum) from question_user_score where userId = qus.userId and questionId = qus.questionId and answerDate = qus.answerDate")); + } + sql.setCondition(cnd); + return listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); + } + return new Pagination(); + } + + @Override + public void exportXlsx(String questionId, OutputStream os) throws IOException { + Question question = dao().fetch(Question.class, questionId); + + if (question.getCtMode() == 1) { + Sql sql = Sqls.create(""" + SELECT + qus.userId, + DATE_FORMAT( qus.answerDate, '%Y-%m-%d' ) as answerDate, + qus.score, + qus.answerNum, + u.loginname, + u.username, + u.sex, + u.unitname, + u.unionname, + u.mobile + FROM + question_user_score qus + LEFT JOIN `user` u ON u.id = qus.userId + $condition + """); + Cnd cnd = Cnd.NEW(); + cnd.and("qus.questionId", "=", questionId); + cnd.groupBy("qus.answerDate", "qus.userId"); + cnd.asc("qus.answerDate").asc("u.unionid").asc("u.id"); + if (question.getCalcScoreMode() == 1) { + cnd.and("qus.score", "in", Sqls.create("select max(score) from question_user_score where userId = qus.userId and questionId = qus.questionId and answerDate = qus.answerDate")); + } else if (question.getCalcScoreMode() == 2) { + cnd.and("qus.answerNum", "in", Sqls.create("select max(answerNum) from question_user_score where userId = qus.userId and questionId = qus.questionId and answerDate = qus.answerDate")); + } + sql.setCondition(cnd); + List list = listMap(sql); + List exportEntities = new ArrayList<>(); + exportEntities.add(new ExcelExportEntity("工号", "loginname", 20)); + exportEntities.add(new ExcelExportEntity("姓名", "username", 20)); + exportEntities.add(new ExcelExportEntity("性别", "sex", 20)); + exportEntities.add(new ExcelExportEntity("所属单位", "unitname", 20)); + exportEntities.add(new ExcelExportEntity("所属工会", "unionname", 20)); + exportEntities.add(new ExcelExportEntity("联系方式", "mobile", 20)); + exportEntities.add(new ExcelExportEntity("分数", "score", 20)); + exportEntities.add(new ExcelExportEntity("答题日期", "answerDate", 20)); + + ExportParams exportParams = new ExportParams(); + exportParams.setType(ExcelType.XSSF); + Workbook workbook = ExcelExportUtil.exportExcel(exportParams, exportEntities, list); + workbook.write(os); + + } else if (question.getCtMode() == 2) { + if (questionService.isSameDay(question.getStartTime(), question.getEndTime())) { + Sql sql = Sqls.create(""" + SELECT + GROUP_CONCAT( qus.answerNum, '(', qus.score, ')' ORDER BY qus.answerNum SEPARATOR ', ' ) AS everyRandomScore, + sum( score ) AS score, + qus.userId, + DATE_FORMAT( qus.answerDate, '%Y-%m-%d' ) AS answerDate, + u.loginname, + u.username, + u.sex, + u.unitname, + u.unionname, + u.mobile, + q.ctMode, + q.calcScoreMode + FROM + `question_user_score` qus + LEFT JOIN `user` u ON u.id = qus.userId + LEFT JOIN question q ON q.qid = qus.questionId + $condition + """); + Cnd cnd = Cnd.NEW(); + cnd.and("qus.questionId", "=", questionId); + cnd.groupBy("qus.answerDate", "qus.userId"); + cnd.asc("qus.answerDate").asc("u.unionid").asc("u.id"); + sql.setCondition(cnd); + List list = listMap(sql); + + List exportEntities = new ArrayList<>(); + exportEntities.add(new ExcelExportEntity("工号", "loginname", 20)); + exportEntities.add(new ExcelExportEntity("姓名", "username", 20)); + exportEntities.add(new ExcelExportEntity("性别", "sex", 20)); + exportEntities.add(new ExcelExportEntity("所属单位", "unitname", 20)); + exportEntities.add(new ExcelExportEntity("所属工会", "unionname", 20)); + exportEntities.add(new ExcelExportEntity("联系方式", "mobile", 20)); + exportEntities.add(new ExcelExportEntity("分数", "score", 20)); + exportEntities.add(new ExcelExportEntity("每轮分数", "everyRandomScore", 20)); + + ExportParams exportParams = new ExportParams(); + exportParams.setType(ExcelType.XSSF); + Workbook workbook = ExcelExportUtil.exportExcel(exportParams, exportEntities, list); + workbook.write(os); + + } else { + Sql sql = Sqls.create(""" + SELECT + ( + SELECT + score\s + FROM + question_user_score\s + WHERE + userId = qus.userId\s + AND answerDate = qus.answerDate\s + AND answerNum = max( qus.answerNum )\s + AND questionId = qus.questionId + ) as lastAnswerNumScore, + ( + SELECT + score\s + FROM + question_user_score\s + WHERE + userId = qus.userId\s + AND answerDate = qus.answerDate\s + AND score = max( qus.score )\s + AND questionId = qus.questionId + ) as maxAnswerScore, + qus.userId, + DATE_FORMAT( qus.answerDate, '%Y-%m-%d' ) AS answerDate, + qus.answerNum, + max( qus.answerNum ), + min( qus.answerNum ), + qus.score, + u.loginname, + u.username, + u.sex, + u.unitname, + u.unionname, + u.mobile, + q.ctMode, + q.calcScoreMode + FROM + `question_user_score` qus + LEFT JOIN `user` u ON u.id = qus.userId + LEFT JOIN question q ON q.qid = qus.questionId + $condition + """); + Cnd cnd = Cnd.NEW(); + cnd.and("qus.questionId", "=", questionId); + + cnd.groupBy("qus.answerDate", "qus.userId"); + cnd.asc("qus.answerDate").asc("u.unionid").asc("u.id"); + sql.setCondition(cnd); + List list = listMap(sql); + for (NutMap map : list) { + map.put("score", map.getInt(question.getCalcScoreMode() == 1 ? "maxAnswerScore" : "lastAnswerNumScore")); + } + + List exportEntities = new ArrayList<>(); + exportEntities.add(new ExcelExportEntity("工号", "loginname", 20)); + exportEntities.add(new ExcelExportEntity("姓名", "username", 20)); + exportEntities.add(new ExcelExportEntity("性别", "sex", 20)); + exportEntities.add(new ExcelExportEntity("所属单位", "unitname", 20)); + exportEntities.add(new ExcelExportEntity("所属工会", "unionname", 20)); + exportEntities.add(new ExcelExportEntity("联系方式", "mobile", 20)); + exportEntities.add(new ExcelExportEntity("分数", "score", 20)); + exportEntities.add(new ExcelExportEntity("答题日期", "answerDate", 20)); + + ExportParams exportParams = new ExportParams(); + exportParams.setType(ExcelType.XSSF); + Workbook workbook = ExcelExportUtil.exportExcel(exportParams, exportEntities, list); + workbook.write(os); + } + } else if (question.getCtMode() == 3) { + Sql sql = Sqls.create(""" + SELECT + qus.userId, + DATE_FORMAT( qus.answerDate, '%Y-%m-%d' ) as answerDate, + qus.score, + qus.answerNum, + u.loginname, + u.username, + u.sex, + u.unitname, + u.unionname, + u.mobile + FROM + question_user_score qus + LEFT JOIN `user` u ON u.id = qus.userId + $condition + """); + Cnd cnd = Cnd.NEW(); + cnd.and("qus.questionId", "=", questionId); + + cnd.groupBy("qus.answerDate", "qus.userId"); + cnd.asc("qus.answerDate").asc("u.unionid").asc("u.id"); + if (question.getCalcScoreMode() == 1) { + cnd.and("qus.score", "in", Sqls.create("select max(score) from question_user_score where userId = qus.userId and questionId = qus.questionId and answerDate = qus.answerDate")); + } else if (question.getCalcScoreMode() == 2) { + cnd.and("qus.answerNum", "in", Sqls.create("select max(answerNum) from question_user_score where userId = qus.userId and questionId = qus.questionId and answerDate = qus.answerDate")); + } + sql.setCondition(cnd); + List list = listMap(sql); + + List exportEntities = new ArrayList<>(); + exportEntities.add(new ExcelExportEntity("工号", "loginname", 20)); + exportEntities.add(new ExcelExportEntity("姓名", "username", 20)); + exportEntities.add(new ExcelExportEntity("性别", "sex", 20)); + exportEntities.add(new ExcelExportEntity("所属单位", "unitname", 20)); + exportEntities.add(new ExcelExportEntity("所属工会", "unionname", 20)); + exportEntities.add(new ExcelExportEntity("联系方式", "mobile", 20)); + exportEntities.add(new ExcelExportEntity("分数", "score", 20)); + + ExportParams exportParams = new ExportParams(); + exportParams.setType(ExcelType.XSSF); + Workbook workbook = ExcelExportUtil.exportExcel(exportParams, exportEntities, list); + workbook.write(os); + } + } +} diff --git a/src/main/java/io/v/nutz/question/service/impl/QuestionReplyServiceImpl.java b/src/main/java/io/v/nutz/question/service/impl/QuestionReplyServiceImpl.java index 891694c..195ef6f 100644 --- a/src/main/java/io/v/nutz/question/service/impl/QuestionReplyServiceImpl.java +++ b/src/main/java/io/v/nutz/question/service/impl/QuestionReplyServiceImpl.java @@ -76,7 +76,7 @@ public class QuestionReplyServiceImpl extends ViServiceImpl impl String wiid = questionWor.getWiid(); Question_issue subject = dao().fetch(Question_issue.class, Cnd.where("iid", "=", wiid)); - Float fraction = subject.getFraction(); + Integer fraction = subject.getFraction(); Sql sql = Sqls.create(""" SELECT @@ -108,7 +108,7 @@ public class QuestionReplyServiceImpl extends ViServiceImpl impl if (Arrays.equals(correctOptionIdArray, userSelectedOptionIdArray)) { answerUserScore.setSubjectScore(fraction); } else { - answerUserScore.setSubjectScore(0f); + answerUserScore.setSubjectScore(0); } dao().insert(answerUserScore); diff --git a/src/main/java/io/v/nutz/question/service/impl/QuestionServiceImpl.java b/src/main/java/io/v/nutz/question/service/impl/QuestionServiceImpl.java index 5a3f876..9d1abbc 100644 --- a/src/main/java/io/v/nutz/question/service/impl/QuestionServiceImpl.java +++ b/src/main/java/io/v/nutz/question/service/impl/QuestionServiceImpl.java @@ -1,23 +1,33 @@ package io.v.nutz.question.service.impl; -import io.v.nutz.question.model.Question; -import io.v.nutz.question.model.Question_issue; +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import io.v.nutz.question.model.*; import io.v.nutz.question.service.QuestionIssueService; import io.v.nutz.question.service.QuestionReplyService; import io.v.nutz.question.service.QuestionService; import io.v.nutz.question.service.QuestionWorService; import io.v.nutz.service.impl.ViServiceImpl; +import io.v.nutz.web.commons.utils.ShiroUtil; +import org.nutz.aop.interceptor.ioc.TransAop; +import org.nutz.dao.Chain; import org.nutz.dao.Cnd; import org.nutz.dao.Dao; import org.nutz.dao.Sqls; import org.nutz.dao.entity.Record; import org.nutz.dao.sql.Sql; import org.nutz.integration.jedis.RedisService; +import org.nutz.ioc.aop.Aop; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.lang.Lang; import org.nutz.lang.util.NutMap; import org.nutz.plugins.wkcache.annotation.CacheDefaults; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + @IocBean(args = {"refer:dao"}) @CacheDefaults(cacheName = "Zgfw_question") public class QuestionServiceImpl extends ViServiceImpl implements QuestionService { @@ -81,4 +91,176 @@ public class QuestionServiceImpl extends ViServiceImpl implements Ques return res; } + + @Override + @Aop(TransAop.READ_COMMITTED) + public void saveAnswer(Question_reply[] repliesArray, String questionId, Boolean isSave) { + //删除上一次的答题记录 + //此处需判断 如果是有分多天 随机多次就要按条件删除 + + //问卷信息包含题目选项 + Question question = getQuestion(questionId); + + if (question.getCtMode() == 1) { + //删除今天的之前的答题记录 + dao().clear(Question_reply.class, Cnd.where("qid", "=", questionId).and("userId", "=", ShiroUtil.getUserId()).and("DATE(rtime)", "=", DateUtil.today())); + } else if (question.getCtMode() == 2) { + DateTime startTime = DateUtil.date(question.getStartTime()); + DateTime endTime = DateUtil.date(question.getEndTime()); + long betweenDay = DateUtil.betweenDay(startTime, endTime, true); + if (betweenDay > 0) { + //多天随机 + dao().clear(Question_reply.class, Cnd.where("qid", "=", questionId).and("userId", "=", ShiroUtil.getUserId()).and("DATE(rtime)", "=", DateUtil.today())); + } else { + //一天多次随机 删除保存的 + dao().clear(Question_reply.class, Cnd.where("qid", "=", questionId).and("userId", "=", ShiroUtil.getUserId()).and("DATE(rtime)", "=", DateUtil.today()).and("save", "=", 1)); + } + } else if (question.getCtMode() == 3) { + dao().clear(Question_reply.class, Cnd.where("qid", "=", questionId).and("userId", "=", ShiroUtil.getUserId())); + } + + List replyList = new ArrayList<>(); + for (Question_reply reply : repliesArray) { + if (Lang.isNotEmpty(reply.getUserSelect())) { + for (String userSelect : reply.getUserSelect()) { + Question_reply questionReply = new Question_reply(); + questionReply.setRwid(userSelect); + questionReply.setIssueId(reply.getIssueId()); + questionReply.setUserId(ShiroUtil.getUserId()); + questionReply.setRtime(DateUtil.now()); + questionReply.setRwid(userSelect); + questionReply.setQid(questionId); + questionReply.setSave(isSave); + replyList.add(questionReply); + } + } + } + + insert(replyList); + + //判断是否提交 提交则计算此次分数 + + if (!isSave) { + + //得分 + AtomicInteger sum = new AtomicInteger(); + + //常规模式计算全部题目的分数 + if (question.getCtMode() == 3) { + //每题的回答记录 + List replies = dao().query(Question_reply.class, Cnd.where("qid", "=", questionId).and("userId", "=", ShiroUtil.getUserId()).and("userId", "=", ShiroUtil.getUserId())); + Map> issueReplies = replies.stream().collect(Collectors.groupingBy(Question_reply::getIssueId)); + + issueReplies.forEach((k, v) -> { + //用户回答的选项 + Set userReplyOptionIds = v.stream().map(Question_reply::getRwid).collect(Collectors.toSet()); + //正确答案 + Optional issueOptional = question.getIssues().stream().filter(x -> x.getIid().equals(k)).findFirst(); + Set correctOptionIds = issueOptional.map(x -> x.getWors().stream().filter(c -> c.getCorrect() != null && c.getCorrect()).map(z -> z.getWid()).collect(Collectors.toSet())).orElse(null); + if (userReplyOptionIds.equals(correctOptionIds)) { + sum.addAndGet(issueOptional.map(i -> i.getFraction()).orElse(0)); + } + }); + + //查询之前的回答记录 + int count = dao().count(QuestionUserScore.class, Cnd.where("questionId", "=", questionId).and("userId", "=", ShiroUtil.getUserId())); + + QuestionUserScore questionUserScore = new QuestionUserScore(); + questionUserScore.setQuestionId(questionId); + questionUserScore.setUserId(ShiroUtil.getUserId()); + questionUserScore.setAnswerDate(new Date()); + questionUserScore.setAnswerNum(count + 1); + questionUserScore.setScore(sum.get()); + dao().insert(questionUserScore); + } else if (question.getCtMode() == 1) { + //每题的回答记录 + QuestionRandomIssue randomIssue = dao().fetch(QuestionRandomIssue.class, Cnd.where("questionId", "=", questionId).and("userId", "=", ShiroUtil.getUserId()).and("displayDate", "=", DateUtil.today())); + List issueIds = randomIssue.getIssueIds(); + List replies = dao().query(Question_reply.class, Cnd.where("issueId", "in", issueIds).and("qid", "=", questionId).and("userId", "=", ShiroUtil.getUserId())); + Map> issueReplies = replies.stream().collect(Collectors.groupingBy(Question_reply::getIssueId)); + issueReplies.forEach((k, v) -> { + //用户回答的选项 + Set userReplyOptionIds = v.stream().map(Question_reply::getRwid).collect(Collectors.toSet()); + //正确答案 + Optional issueOptional = question.getIssues().stream().filter(x -> x.getIid().equals(k)).findFirst(); + Set correctOptionIds = issueOptional.map(x -> x.getWors().stream().filter(c -> c.getCorrect() != null && c.getCorrect()).map(z -> z.getWid()).collect(Collectors.toSet())).orElse(null); + if (userReplyOptionIds.equals(correctOptionIds)) { + sum.addAndGet(issueOptional.map(i -> i.getFraction()).orElse(0)); + } + }); + + //查询之前的回答记录 + int count = dao().count(QuestionUserScore.class, Cnd.where("questionId", "=", questionId).and("userId", "=", ShiroUtil.getUserId()).and("displayDate", "=", DateUtil.today())); + + QuestionUserScore questionUserScore = new QuestionUserScore(); + questionUserScore.setQuestionId(questionId); + questionUserScore.setUserId(ShiroUtil.getUserId()); + questionUserScore.setAnswerDate(new Date()); + questionUserScore.setAnswerNum(count + 1); + questionUserScore.setScore(sum.get()); + questionUserScore.setDisplayDate(DateUtil.parseDate(DateUtil.today())); + dao().insert(questionUserScore); + } else if (question.getCtMode() == 2) { + + //每题的回答记录 + QuestionRandomIssue randomIssue = dao().fetch(QuestionRandomIssue.class, Cnd.where("questionId", "=", questionId).and("userId", "=", ShiroUtil.getUserId()).and("displayDate", "=", DateUtil.today()).desc("num")); + List issueIds = randomIssue.getIssueIds(); + List replies = dao().query(Question_reply.class, Cnd.where("issueId", "in", issueIds).and("qid", "=", questionId).and("userId", "=", ShiroUtil.getUserId())); + Map> issueReplies = replies.stream().collect(Collectors.groupingBy(Question_reply::getIssueId)); + issueReplies.forEach((k, v) -> { + //用户回答的选项 + Set userReplyOptionIds = v.stream().map(Question_reply::getRwid).collect(Collectors.toSet()); + //正确答案 + Optional issueOptional = question.getIssues().stream().filter(x -> x.getIid().equals(k)).findFirst(); + Set correctOptionIds = issueOptional.map(x -> x.getWors().stream().filter(c -> c.getCorrect() != null && c.getCorrect()).map(z -> z.getWid()).collect(Collectors.toSet())).orElse(null); + if (userReplyOptionIds.equals(correctOptionIds)) { + sum.addAndGet(issueOptional.map(i -> i.getFraction()).orElse(0)); + } + }); + + //同一天 + //查询之前的回答记录 + int count = dao().count(QuestionUserScore.class, Cnd.where("questionId", "=", questionId).and("userId", "=", ShiroUtil.getUserId()).and("displayDate", "=", DateUtil.today())); + + QuestionUserScore questionUserScore = new QuestionUserScore(); + questionUserScore.setQuestionId(questionId); + questionUserScore.setUserId(ShiroUtil.getUserId()); + questionUserScore.setAnswerDate(new Date()); + questionUserScore.setAnswerNum(count + 1); + questionUserScore.setScore(sum.get()); + questionUserScore.setDisplayDate(DateUtil.parseDate(DateUtil.today())); + dao().insert(questionUserScore); + } + + //随机题库设为已完成 + if (question.getCtMode() == 3) { + //常规模式下不需要判断次数 + dao().update(QuestionRandomIssue.class, Chain.make("isFinish", 1), Cnd.where("questionId", "=", questionId).and("userId", "=", ShiroUtil.getUserId())); + } else if (question.getCtMode() == 1) { + //定时定题需要加上日期的条件 + dao().update(QuestionRandomIssue.class, Chain.make("isFinish", 1), Cnd.where("questionId", "=", questionId).and("userId", "=", ShiroUtil.getUserId()).and("displayDate", "=", DateUtil.today())); + } else if (question.getCtMode() == 2) { + //随机模式 要判断是否为同一天 + + QuestionRandomIssue randomIssue = dao().fetch(QuestionRandomIssue.class, Cnd.where("questionId", "=", questionId).and("userId", "=", ShiroUtil.getUserId()).and("displayDate", "=", DateUtil.today()).desc("num")); + randomIssue.setIsFinish(true); + dao().update(randomIssue, "isFinish"); + } + + } + } + + @Override + public boolean isSameDay(String questionId) { + Question question = dao().fetch(Question.class, questionId); + return isSameDay(question.getStartTime(), question.getEndTime()); + } + + @Override + public boolean isSameDay(long startTimeTs, long endTimeTs) { + DateTime startTime = DateUtil.date(startTimeTs); + DateTime endTime = DateUtil.date(endTimeTs); + long betweenDay = DateUtil.betweenDay(startTime, endTime, true); + return betweenDay == 0; + } } diff --git a/src/main/java/io/v/nutz/task/question/QuestionRaffleJob.java b/src/main/java/io/v/nutz/task/question/QuestionRaffleJob.java new file mode 100644 index 0000000..b1f4ce8 --- /dev/null +++ b/src/main/java/io/v/nutz/task/question/QuestionRaffleJob.java @@ -0,0 +1,154 @@ +package io.v.nutz.task.question; + +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import io.v.nutz.question.model.Question; +import io.v.nutz.question.model.QuestionUserScore; +import io.v.nutz.question.model.QuestionWinner; +import io.v.nutz.question.service.QuestionService; +import io.v.nutz.sys.models.Sys_task; +import org.nutz.dao.Chain; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.lang.util.NutMap; +import org.quartz.Job; +import org.quartz.JobDataMap; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; + +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +/** + * 问卷抽奖定时任务 + * + * @author jug + * @date 2023/06/12 + */ +@IocBean +public class QuestionRaffleJob implements Job { + + @Inject + private Dao dao; + + @Inject + private QuestionService questionService; + + @Override + public void execute(JobExecutionContext context) throws JobExecutionException { + JobDataMap data = context.getJobDetail().getJobDataMap(); + DateTime startAnswerDate = DateUtil.parseDateTime(data.getString("startAnswerDate")); + DateTime endAnswerDate = DateUtil.parseDateTime(data.getString("endAnswerDate")); + DateTime raffleTime = DateUtil.parseDate(data.getString("raffleTime")); + //抽奖分数 + int raffleScore = data.getInt("raffleScore"); + //中奖者人数 + int raffleWinnerUserNum = data.getInt("raffleWinnerUserNum"); + //问卷id + String questionId = data.getString("questionId"); + + Question question = dao.fetch(Question.class, questionId); + + + //已中奖的用户 + List winners = dao.query(QuestionWinner.class, Cnd.where("questionId", "=", questionId)); + List hasWinnUserIds = winners.stream().map(v -> v.getUserId()).collect(Collectors.toList()); + + //得分记录 + List questionUserScores = dao.query(QuestionUserScore.class, Cnd.where("questionId", "=", questionId).andEX("userId", "not in", hasWinnUserIds)); + + //抽奖规则时间范围内的记录 + List thisTimeQuestionUserScores = questionUserScores.stream().filter(s -> startAnswerDate.isBeforeOrEquals(DateUtil.date(s.getCreatedAt())) && endAnswerDate.isAfter(DateUtil.date(s.getCreatedAt()))).collect(Collectors.toList()); + + //本次中奖用户 + List winnerUserIds = new ArrayList<>(); + + //每个用户的分数 + List userScoreMap = new ArrayList<>(); + + if (question.getCtMode() == 1) { + Map> userScoreListMap = thisTimeQuestionUserScores.stream().collect(Collectors.groupingBy(v -> v.getUserId())); + userScoreListMap.forEach((userId, v) -> { + Map> userDayScoreListMap = v.stream().collect(Collectors.groupingBy(x -> x.getAnswerDate())); + AtomicInteger sumScore = new AtomicInteger(); + userDayScoreListMap.forEach((day, v1) -> { + if (question.getCalcScoreMode() == 1) { + int score = v1.stream().mapToInt(s -> s.getScore()).max().orElse(0); + sumScore.addAndGet(score); + } else if (question.getCalcScoreMode() == 2) { + int maxAnswerNum = v1.stream().mapToInt(s -> s.getAnswerNum()).max().orElse(1); + Integer score = v1.stream().filter(s -> s.getAnswerNum() == maxAnswerNum).findFirst().map(s -> s.getScore()).orElse(0); + sumScore.addAndGet(score); + } + }); + userScoreMap.add(NutMap.NEW().addv("userId", userId).addv("score", sumScore.get())); + }); + } else if (question.getCtMode() == 2) { + if (questionService.isSameDay(question.getStartTime(), question.getEndTime())) { + thisTimeQuestionUserScores.stream().collect(Collectors.groupingBy(v -> v.getUserId(), Collectors.summingInt(v -> v.getScore()))).forEach((k, v) -> { + userScoreMap.add(NutMap.NEW().addv("userId", k).addv("score", v)); + }); + } else { + Map> userScoreListMap = thisTimeQuestionUserScores.stream().collect(Collectors.groupingBy(v -> v.getUserId())); + userScoreListMap.forEach((userId, v) -> { + Map> userDayScoreListMap = v.stream().collect(Collectors.groupingBy(x -> x.getAnswerDate())); + AtomicInteger sumScore = new AtomicInteger(); + userDayScoreListMap.forEach((day, v1) -> { + if (question.getCalcScoreMode() == 1) { + int score = v1.stream().mapToInt(s -> s.getScore()).max().orElse(0); + sumScore.addAndGet(score); + } else if (question.getCalcScoreMode() == 2) { + int maxAnswerNum = v1.stream().mapToInt(s -> s.getAnswerNum()).max().orElse(1); + Integer score = v1.stream().filter(s -> s.getAnswerNum() == maxAnswerNum).findFirst().map(s -> s.getScore()).orElse(0); + sumScore.addAndGet(score); + } + }); + userScoreMap.add(NutMap.NEW().addv("userId", userId).addv("score", sumScore.get())); + }); + } + } else if (question.getCtMode() == 3) { + Map> userScoreListMap = thisTimeQuestionUserScores.stream().collect(Collectors.groupingBy(v -> v.getUserId())); + userScoreListMap.forEach((userId, v) -> { + AtomicInteger sumScore = new AtomicInteger(); + if (question.getCalcScoreMode() == 1) { + int score = v.stream().mapToInt(s -> s.getScore()).max().orElse(0); + sumScore.addAndGet(score); + } else if (question.getCalcScoreMode() == 2) { + int maxAnswerNum = v.stream().mapToInt(s -> s.getAnswerNum()).max().orElse(1); + Integer score = v.stream().filter(s -> s.getAnswerNum() == maxAnswerNum).findFirst().map(s -> s.getScore()).orElse(0); + sumScore.addAndGet(score); + } + userScoreMap.add(NutMap.NEW().addv("userId", userId).addv("score", sumScore.get())); + }); + } + + //待抽奖用户 + List readyRaffleUserId = userScoreMap.stream().filter(x -> x.getInt("score") >= raffleScore).map(v -> v.getString("userId")).collect(Collectors.toList()); + + // 随机打乱用户ID列表 + Collections.shuffle(readyRaffleUserId); + + if (readyRaffleUserId.size() >= raffleWinnerUserNum) { + winnerUserIds = readyRaffleUserId.subList(0, raffleWinnerUserNum); + } else { + winnerUserIds = readyRaffleUserId.subList(0, readyRaffleUserId.size()); + } + + List questionWinners = winnerUserIds.stream().map(v -> { + QuestionWinner winner = new QuestionWinner(); + winner.setQuestionId(questionId); + winner.setUserId(v); + winner.setWinDate(new Date()); + Integer score = userScoreMap.stream().filter(x -> x.getString("userId").equals(v)).findFirst().map(x -> x.getInt("score", 0)).orElse(0); + winner.setScore(score); + return winner; + }).collect(Collectors.toList()); + dao.insert(questionWinners); + + dao.update(Sys_task.class, Chain.make("disabled", 1), Cnd.where("data", "like", "%" + raffleTime + "%").and("data", "like", "%" + questionId + "%")); + + } +} diff --git a/src/main/resources/static/assets/mobile/icon/meeting/会议(会议审核).png b/src/main/resources/static/assets/mobile/icon/meeting/会议(会议审核).png new file mode 100644 index 0000000000000000000000000000000000000000..10bd13d0fb4d031f0473093d803a80b2a4d30f40 GIT binary patch literal 87898 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