From 351806c339f13801a69f2972dcb63cd4ef508a27 Mon Sep 17 00:00:00 2001 From: zhouhefeng Date: Wed, 10 Jun 2026 14:38:57 +0800 Subject: [PATCH] commit --- .../manage/TrainSignUpApplyController.java | 18 +- .../manage/TrainSignUpManageController.java | 29 +- .../TrainSignUpUserManageController.java | 300 +----------------- .../models/TrainSignUpBlackList.java | 8 + .../service/TrainSignUpActivityService.java | 11 + .../service/TrainSignUpBlackListService.java | 31 +- .../impl/TrainSignUpActivityServiceImpl.java | 46 +++ .../impl/TrainSignUpUserServiceImpl.java | 180 +++++++++-- .../activity/trainSignUp/manage/index.html | 64 +++- .../trainSignUp/userManage/index.html | 228 ++++--------- 10 files changed, 397 insertions(+), 518 deletions(-) diff --git a/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/controller/manage/TrainSignUpApplyController.java b/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/controller/manage/TrainSignUpApplyController.java index 1e733c83..599a1cad 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/controller/manage/TrainSignUpApplyController.java +++ b/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/controller/manage/TrainSignUpApplyController.java @@ -18,6 +18,7 @@ import com.budwk.app.zhgh.activity.basic.models.ActivityUserScope; import com.budwk.app.zhgh.activity.trainSignUp.models.*; import com.budwk.app.zhgh.activity.trainSignUp.service.TrainSignUpActivityService; import com.budwk.app.zhgh.activity.trainSignUp.service.TrainSignUpActivityStatisticsService; +import com.budwk.app.zhgh.activity.trainSignUp.service.TrainSignUpBlackListService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; @@ -48,10 +49,13 @@ import java.util.stream.Collectors; public class TrainSignUpApplyController { private final ReentrantLock lock = new ReentrantLock(true); + private static final String SIGN_UP_LIMIT_MSG = "您已被限制报名香樟工会讲堂,请联系校工会。"; @Inject private TrainSignUpActivityService trainSignUpActivityService; @Inject + private TrainSignUpBlackListService trainSignUpBlackListService; + @Inject private SysDictService dictService; @Inject private TrainSignUpActivityStatisticsService statisticsService; @@ -218,6 +222,9 @@ public class TrainSignUpApplyController { @Param(value = "currentFamilyNumber") Integer currentFamilyNumber,String IsVerifyUserApply) { try { lock.lock(); + if (isLimitedByAbsenceBlackList()) { + return Result.error(SIGN_UP_LIMIT_MSG); + } if (StrUtil.isBlank(courseId)) { return Result.error("报名信息为空"); } @@ -350,6 +357,9 @@ public class TrainSignUpApplyController { public Result doSignUp(TrainSignUpUser trainSignUpUser) { try { lock.lock(); + if (isLimitedByAbsenceBlackList()) { + return Result.error(SIGN_UP_LIMIT_MSG); + } boolean courseByUser = trainSignUpActivityService.isSignCourseByUser(trainSignUpUser.getCourseId(), SecurityUtil.getUserId()); if (courseByUser) { return Result.error("您已报过该活动"); @@ -430,12 +440,16 @@ public class TrainSignUpApplyController { @SaCheckPermission(value = {"trainSignUp.apply", "h5.trainSignUp.apply"}, mode = SaMode.OR) @SLog(tag = "品牌活动-活动报名", msg = "判断缺席次数") public Result validSignCount(@Param("activityId") String activityId) { - if (trainSignUpActivityService.isAbsenceSignUpLimited(SecurityUtil.getUserId())) { - return Result.error(99, "您已连续2次未签到活动,不能报名,请联系校工会"); + if (isLimitedByAbsenceBlackList()) { + return Result.error(99, SIGN_UP_LIMIT_MSG); } return Result.success(); } + private boolean isLimitedByAbsenceBlackList() { + return trainSignUpBlackListService.autoBlackByYearAndUser(DateUtil.year(new Date()), SecurityUtil.getUserId()); + } + @At @ApiOperation("活动代人报名") @SaCheckPermission(value = {"trainSignUp.apply"}, mode = SaMode.OR) diff --git a/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/controller/manage/TrainSignUpManageController.java b/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/controller/manage/TrainSignUpManageController.java index 1160ec1a..d18fabcc 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/controller/manage/TrainSignUpManageController.java +++ b/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/controller/manage/TrainSignUpManageController.java @@ -10,11 +10,8 @@ import com.budwk.app.flow.entity.ProcessInstance; import com.budwk.app.flow.enums.ProcessInstanceStateEnum; import com.budwk.app.sys.models.Sys_home_activity; import com.budwk.app.sys.models.Sys_unit; -import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.zhgh.activity.trainSignUp.models.*; import com.budwk.app.zhgh.activity.trainSignUp.service.TrainSignUpActivityService; -import com.budwk.app.sys.models.Sys_user; -import com.budwk.app.sys.services.SysMsgService; import com.budwk.app.zhgh.club.model.SysClub; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @@ -26,6 +23,8 @@ 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.json.Json; +import org.nutz.lang.Lang; import org.nutz.lang.util.NutMap; import org.nutz.mvc.annotation.At; import org.nutz.mvc.annotation.Ok; @@ -53,8 +52,6 @@ public class TrainSignUpManageController { @Inject private TrainSignUpActivityService trainSignUpActivityManageService; @Inject - private SysMsgService sysMsgService; - @Inject private Dao dao; @At("") @@ -245,24 +242,14 @@ public class TrainSignUpManageController { @Ok("json:full") @ApiOperation("通知报名人员") @SaCheckPermission("trainSignUp.manage") - public Result sendNotify(@Param("activityId") String activityId, @Param("content") String content) { - if (StrUtil.isBlank(activityId) || StrUtil.isBlank(content)) { + public Result sendNotify(@Param("activityId") String activityId, @Param("courseIds") String courseIds, @Param("content") String content) { + if (StrUtil.isBlank(activityId) || StrUtil.isBlank(courseIds) || StrUtil.isBlank(content)) { return Result.error("参数不完整"); } - // 查询该活动下所有报名人员(state=1为正常报名) - List userList = dao.query(TrainSignUpUser.class, Cnd.where("activityId", "=", activityId).and("state", "=", 1)); - if (userList.isEmpty()) { - return Result.error("该活动暂无报名人员"); + List courseIdList = Json.fromJsonAsList(String.class, courseIds); + if (Lang.isEmpty(courseIdList)) { + return Result.error("请选择要通知的分活动"); } - // 获取所有报名人员的userId - List userIds = userList.stream().map(TrainSignUpUser::getUserId).distinct().toList(); - // 查询用户信息获取loginname - List sysUsers = dao.query(Sys_user.class, Cnd.where("id", "in", userIds)); - List loginNames = sysUsers.stream().map(Sys_user::getLoginname).filter(StrUtil::isNotBlank).toList(); - if (loginNames.isEmpty()) { - return Result.error("未找到有效的报名人员"); - } - sysMsgService.sendMsg(loginNames, "香樟工会讲堂活动通知", content, SecurityUtil.getUserId()); - return Result.success("通知发送成功"); + return trainSignUpActivityManageService.sendNotify(activityId, courseIdList, content); } } diff --git a/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/controller/manage/TrainSignUpUserManageController.java b/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/controller/manage/TrainSignUpUserManageController.java index e5dcd61d..38330cf4 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/controller/manage/TrainSignUpUserManageController.java +++ b/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/controller/manage/TrainSignUpUserManageController.java @@ -1,42 +1,24 @@ package com.budwk.app.zhgh.activity.trainSignUp.controller.manage; -import cn.afterturn.easypoi.excel.entity.ExportParams; -import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity; -import cn.afterturn.easypoi.excel.export.ExcelExportService; import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.StrUtil; import com.budwk.app.base.annotation.SLog; import com.budwk.app.base.page.Pagination; import com.budwk.app.base.param.PageForm; import com.budwk.app.base.result.Result; -import com.budwk.app.base.utils.PageUtil; -import com.budwk.app.zhgh.activity.trainSignUp.models.TrainSignUpActivity; -import com.budwk.app.zhgh.activity.trainSignUp.models.TrainSignUpCourse; -import com.budwk.app.zhgh.activity.trainSignUp.models.TrainSignUpUser; import com.budwk.app.zhgh.activity.trainSignUp.service.TrainSignUpBlackListService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; -import org.apache.poi.hssf.usermodel.HSSFWorkbook; -import org.apache.poi.ss.usermodel.Workbook; -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.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 org.nutz.mvc.annotation.At; import org.nutz.mvc.annotation.Ok; import org.nutz.mvc.annotation.Param; -import javax.servlet.http.HttpServletResponse; -import java.net.URLEncoder; -import java.util.*; -import java.util.stream.Collectors; - /** * @author zxy * @Description 人员管理 @@ -48,8 +30,6 @@ import java.util.stream.Collectors; @At("/platform/trainSignUp/userManage") public class TrainSignUpUserManageController { - @Inject - private Dao dao; @Inject private TrainSignUpBlackListService trainSignUpBlackListService; @@ -63,288 +43,34 @@ public class TrainSignUpUserManageController { @ApiOperation("分页查询") @SaCheckPermission("trainSignUp.userManage") public Result pageData(PageForm pageForm, - @Param(value = "activityId") String activityId, - @Param(value = "courseId") String courseId, @Param(value = "year") Integer year, @Param(value = "userKeyWord") String userKeyWord) { + // 接口参数:pageForm 传分页和排序参数,year 传统计年度,userKeyWord 传姓名或工号关键字; + // 返回 Result,列表包含用户信息、年度签到统计、当前黑名单状态和最近解封时间。 + Integer queryYear = year == null ? DateUtil.thisYear() : year; Cnd cnd = Cnd.NEW(); - cnd.andEX("tsuu.activityId", "=", activityId); - cnd.andEX("tsuu.courseId", "=", courseId); - cnd.andEX("act.year", "=", year); if (StrUtil.isNotBlank(userKeyWord)) { + // 人员关键词支持姓名和工号;搜索时允许展示未报名但存在于用户视图中的人员,便于管理员手动拉黑。 SqlExpressionGroup seg = new SqlExpressionGroup(); cnd.and(seg.andLike("u.username", userKeyWord).orLike("u.loginname", userKeyWord)); - } - if(Strings.isNotBlank(pageForm.getPageOrderName()) && Strings.isNotBlank(pageForm.getPageOrderBy())){ - cnd.orderBy(pageForm.getPageOrderName(), PageUtil.getOrder(pageForm.getPageOrderBy())); + } else { + // 默认列表只展示本年度有报名统计或已有黑名单记录的人员,避免一次性铺开全部用户。 + cnd.and(new Static("(signStats.userId is not null OR tsubl.userId is not null)")); } - Pagination pagination = trainSignUpBlackListService.pageData(pageForm, cnd, activityId, courseId); + Pagination pagination = trainSignUpBlackListService.pageData(pageForm, cnd, queryYear); return Result.success(pagination); } @At @ApiOperation("报名人员处理") @SaCheckPermission("trainSignUp.userManage") - @SLog(tag = "品牌活动-人员管理", msg = "报名人员处理") + @SLog(tag = "品牌活动-人员管理", msg = "报名人员处理") public Result doHandleUser(@Param("userId") String userId) { + // 接口参数:userId 传被处理人员ID;返回 Result,code=0 表示本次拉黑或解封处理成功。 + // 手动处理只切换当前黑名单状态:拉黑时禁止后续报名;解封时记录 lastUnblockTime,后续自动拉黑从该时间重新累计缺席。 trainSignUpBlackListService.doHandleUser(userId); return Result.success(); } - @At - @ApiOperation("根据活动Id获取子活动") - @SaCheckPermission("trainSignUp.userManage") - public Result getCourseByActivityId(@Param("activityId") String activityId) { - List list = dao.query(TrainSignUpCourse.class, Cnd.where("activityId", "=", activityId)); - return Result.success(list); - } - - @At - @ApiOperation("获取子活动具体时间") - @SaCheckPermission("trainSignUp.userManage") - public Result attendClassRecord(String userId) { - trainSignUpBlackListService.attendClassRecord(userId); - return Result.success(); - } - - @At - @ApiOperation("获取候补人员") - @SaCheckPermission("trainSignUp.userManage") - public Result getReserveUser(String courseId) { - Sql sql = Sqls.create(""" - SELECT - uc.* , - (select signUpTime from train_sign_up_user su where su.activityId=uc.activityId and su.courseId = uc.courseId and su.userId=uc.userId) as signUpTime, - (select state from train_sign_up_user su where su.activityId=uc.activityId and su.courseId = uc.courseId and su.userId=uc.userId) as state, - u.username, - u.loginname, - u.mobile, - u.unitName, - u.unionName - FROM - train_sign_up_user_course uc LEFT JOIN `vw_user` u on uc.userId = u.id - WHERE uc.courseId = @courseId and uc.isAttend = false HAVING state = 2 order by signUpTime desc - """).setParam("courseId", courseId); - return Result.success(trainSignUpBlackListService.listMap(sql)); - } - - @At - @ApiOperation("补充人员") - @SaCheckPermission("trainSignUp.userManage") - @SLog(tag = "品牌活动-人员管理", msg = "补充人员") - public Result reserveSingUp(String[] ids, String courseId) { - //先查询这个课程有多少个未签到的人员 - Sql sql = Sqls.create(""" - SELECT - uc.* , - (select signUpTime from train_sign_up_user su where su.activityId=uc.activityId and su.courseId = uc.courseId and su.userId=uc.userId) as signUpTime, - (select state from train_sign_up_user su where su.activityId=uc.activityId and su.courseId = uc.courseId and su.userId=uc.userId) as state - FROM - train_sign_up_user_course uc - WHERE uc.courseId = @courseId and uc.isAttend = false HAVING state = 1 order by signUpTime desc - """).setParam("courseId", courseId); - List list = trainSignUpBlackListService.listMap(sql); - if(ids.length > list.size()) { - return Result.error("您选择了" + ids.length + "位,未签到人员只有" + list.size() + "位"); - } - //ids的长度为几,就搞几个 - List mapList = list.subList(0, ids.length); - List idList = mapList.stream().map(o -> o.getString("userId")).collect(Collectors.toList()); - //将这几个没签到的设置为4 - dao.update(TrainSignUpUser.class, Chain.make("state", 4), Cnd.where("courseId", "=", courseId) - .and("userId", "in", idList)); - //将补充的设置为1 - dao.update(TrainSignUpUser.class, Chain.make("state", 1), Cnd.where("courseId", "=", courseId) - .and("userId", "in", ids)); - return Result.success(); - } - - @At - @Ok("void") - @ApiOperation("导出签到人员") - public void exportSignPerson(@Param(value = "activityId") String activityId, - HttpServletResponse response) { - TrainSignUpActivity activity = dao.fetch(TrainSignUpActivity.class, activityId); - - Sql sql = Sqls.create(""" - SELECT - uc.*, - DATE_FORMAT(uc.courseStartTime, '%Y-%m-%d %H:%i:%s') as courseStartTimeExcel, - DATE_FORMAT(uc.courseEndTime, '%Y-%m-%d %H:%i:%s') as courseEndTimeExcel, - DATE_FORMAT(uc.attendTime, '%Y-%m-%d %H:%i:%s') as attendTimeExcel, - u.username, - u.loginname, - u.unitName, - u.unionName, - u.sex - FROM - train_sign_up_user_course uc - left join train_sign_up_course course on uc.courseId = course.id - left join `vw_user` u on u.id = uc.userId - $condition - """); - Cnd cnd = Cnd.NEW(); - cnd.and("uc.activityId", "=", activityId); - cnd.and("course.isMobileSign", "=", true); - cnd.desc("isAttend").desc("attendTime"); - sql.setCondition(cnd); - - List userList = trainSignUpBlackListService.listMap(sql); - - List courseList = dao.query(TrainSignUpCourse.class, Cnd.where("activityId", "=", activityId)); - - List> sheetsList = new ArrayList<>(); - - List excelCommonExportEntity = new ArrayList<>(); - excelCommonExportEntity.add(new ExcelExportEntity("姓名", "username", 20)); - excelCommonExportEntity.add(new ExcelExportEntity("工号", "loginname", 20)); - excelCommonExportEntity.add(new ExcelExportEntity("单位", "unitName", 20)); - excelCommonExportEntity.add(new ExcelExportEntity("分工会", "unionName", 20)); - excelCommonExportEntity.add(new ExcelExportEntity("性别", "sex", 20)); - excelCommonExportEntity.add(new ExcelExportEntity("开始时间", "courseStartTimeExcel", 22)); - excelCommonExportEntity.add(new ExcelExportEntity("结束时间", "courseEndTimeExcel", 22)); - excelCommonExportEntity.add(new ExcelExportEntity("是否签到", "isAttend", 20)); - excelCommonExportEntity.add(new ExcelExportEntity("签到时间", "attendTimeExcel", 22)); - - for (TrainSignUpCourse c : courseList) { - if(!c.isMobileSign()) { - continue; - } - String courseId = c.getId(); - String courseName = c.getCourseName(); - List v = userList.stream().filter(x -> x.getString("courseId").equals(courseId)).collect(Collectors.toList()); - ExportParams userExportParams = new ExportParams(); - userExportParams.setSheetName(courseName); - - List currentEntities = new ArrayList<>(excelCommonExportEntity); - for (NutMap userSignData : v) { - if(!userSignData.getBoolean("isAttend")) { - userSignData.put("isAttend", "未签到"); - userSignData.put("attendTimeExcel", "未签到"); - }else { - userSignData.put("isAttend", "已签到"); - } - } - - Map userExportMap = new HashMap<>(); - userExportMap.put("name", courseName); - userExportMap.put("title", userExportParams); - userExportMap.put("entity", currentEntities); - userExportMap.put("data", v); - - sheetsList.add(userExportMap); - } - try { - String fileName = activity.getActivityName() + "签到人员名单.xls"; - String disposition = "attachment;filename=" + URLEncoder.encode(fileName, "utf-8"); - response.setCharacterEncoding("utf-8"); - response.setContentType("application/octet-stream"); - response.setHeader("Content-Disposition", disposition); - - Workbook workbook = new HSSFWorkbook(); - for (Map map : sheetsList) { - ExcelExportService service = new ExcelExportService(); - service.createSheetForMap(workbook,(ExportParams) map.get("title"),(List) map.get("entity"),(Collection) map.get("data")); - } - - workbook.write(response.getOutputStream()); - workbook.close(); - } catch (Exception e) { - e.printStackTrace(); - } - } - - @At - @Ok("void") - @ApiOperation("导出领取人员") - public void exportGiftPerson(@Param(value = "activityId") String activityId, - HttpServletResponse response) { - TrainSignUpActivity activity = dao.fetch(TrainSignUpActivity.class, activityId); - - Sql sql = Sqls.create(""" - SELECT - uc.*, - DATE_FORMAT(uc.courseStartTime, '%Y-%m-%d %H:%i:%s') as courseStartTimeExcel, - DATE_FORMAT(uc.courseEndTime, '%Y-%m-%d %H:%i:%s') as courseEndTimeExcel, - DATE_FORMAT(uc.receiveTime, '%Y-%m-%d %H:%i:%s') as receiveTimeExcel, - u.username, - u.loginname, - u.unitName, - u.unionName, - u.sex - FROM - train_sign_up_user_course uc - left join train_sign_up_course course on uc.courseId = course.id - left join `vw_user` u on u.id = uc.userId - $condition - """); - Cnd cnd = Cnd.NEW(); - cnd.and("uc.activityId", "=", activityId); - cnd.and("course.isReceiveGift", "=", true).and("course.giftType" ,"=", 1); - cnd.desc("isReceive").desc("receiveTime"); - sql.setCondition(cnd); - - List userList = trainSignUpBlackListService.listMap(sql); - - List courseList = dao.query(TrainSignUpCourse.class, Cnd.where("activityId", "=", activityId)); - - List> sheetsList = new ArrayList<>(); - - List excelCommonExportEntity = new ArrayList<>(); - excelCommonExportEntity.add(new ExcelExportEntity("姓名", "username", 20)); - excelCommonExportEntity.add(new ExcelExportEntity("工号", "loginname", 20)); - excelCommonExportEntity.add(new ExcelExportEntity("单位", "unitName", 20)); - excelCommonExportEntity.add(new ExcelExportEntity("分工会", "unionName", 20)); - excelCommonExportEntity.add(new ExcelExportEntity("性别", "sex", 20)); - excelCommonExportEntity.add(new ExcelExportEntity("开始时间", "courseStartTimeExcel", 22)); - excelCommonExportEntity.add(new ExcelExportEntity("结束时间", "courseEndTimeExcel", 22)); - excelCommonExportEntity.add(new ExcelExportEntity("是否领取", "isReceive", 20)); - excelCommonExportEntity.add(new ExcelExportEntity("领取时间", "receiveTimeExcel", 22)); - - for (TrainSignUpCourse c : courseList) { - String courseId = c.getId(); - String courseName = c.getCourseName(); - List v = userList.stream().filter(x -> x.getString("courseId").equals(courseId)).collect(Collectors.toList()); - ExportParams userExportParams = new ExportParams(); - userExportParams.setSheetName(courseName); - - List currentEntities = new ArrayList<>(excelCommonExportEntity); - for (NutMap userSignData : v) { - if(!userSignData.getBoolean("isReceive")) { - userSignData.put("isReceive", "未领取"); - userSignData.put("receiveTimeExcel", "未领取"); - }else { - userSignData.put("isReceive", "已领取"); - } - } - - Map userExportMap = new HashMap<>(); - userExportMap.put("name", courseName); - userExportMap.put("title", userExportParams); - userExportMap.put("entity", currentEntities); - userExportMap.put("data", v); - - sheetsList.add(userExportMap); - } - try { - String fileName = activity.getActivityName() + "礼品领取人员名单.xls"; - String disposition = "attachment;filename=" + URLEncoder.encode(fileName, "utf-8"); - response.setCharacterEncoding("utf-8"); - response.setContentType("application/octet-stream"); - response.setHeader("Content-Disposition", disposition); - - Workbook workbook = new HSSFWorkbook(); - for (Map map : sheetsList) { - ExcelExportService service = new ExcelExportService(); - service.createSheetForMap(workbook,(ExportParams) map.get("title"),(List) map.get("entity"),(Collection) map.get("data")); - } - - workbook.write(response.getOutputStream()); - workbook.close(); - } catch (Exception e) { - e.printStackTrace(); - } - } - } diff --git a/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/models/TrainSignUpBlackList.java b/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/models/TrainSignUpBlackList.java index 735fe501..7bb6d94b 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/models/TrainSignUpBlackList.java +++ b/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/models/TrainSignUpBlackList.java @@ -6,6 +6,8 @@ import lombok.experimental.Accessors; import org.nutz.dao.entity.annotation.*; import org.nutz.dao.interceptor.annotation.PrevInsert; +import java.util.Date; + @Data @Accessors(chain = true) @Comment("品牌活动黑名单(香樟工会)") @@ -28,4 +30,10 @@ public class TrainSignUpBlackList { @Comment("是否禁用") private Boolean isDisabled; + @Column + @ColDefine(type = ColType.DATETIME) + @Comment("最近解封时间,用于重置本年度自动拉黑缺席统计起点") + private Date lastUnblockTime; + + } diff --git a/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/service/TrainSignUpActivityService.java b/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/service/TrainSignUpActivityService.java index afd89510..6d60aaca 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/service/TrainSignUpActivityService.java +++ b/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/service/TrainSignUpActivityService.java @@ -2,6 +2,7 @@ package com.budwk.app.zhgh.activity.trainSignUp.service; import com.budwk.app.base.page.Pagination; import com.budwk.app.base.param.PageForm; +import com.budwk.app.base.result.Result; import com.budwk.app.base.service.BaseService; import com.budwk.app.zhgh.activity.trainSignUp.models.TrainSignUpActivity; import com.budwk.app.zhgh.activity.trainSignUp.models.TrainSignUpCourse; @@ -140,4 +141,14 @@ public interface TrainSignUpActivityService extends BaseService courseIds, String content); } diff --git a/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/service/TrainSignUpBlackListService.java b/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/service/TrainSignUpBlackListService.java index 148a8fdb..7f0266a3 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/service/TrainSignUpBlackListService.java +++ b/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/service/TrainSignUpBlackListService.java @@ -17,20 +17,39 @@ import java.util.List; public interface TrainSignUpBlackListService extends BaseService { /** - * 分页 + * 年度人员黑名单分页。 * - * @param pageForm 分页 - * @return Pagination + * @param pageForm 分页参数,包含页码、每页数量、排序字段和排序方向 + * @param cnd 人员查询条件,搜索时按姓名/工号过滤,未搜索时只展示年度报名人员或已有黑名单记录人员 + * @param year 统计年度,对应 train_sign_up_activity.year + * @return Pagination 列表字段包含用户基础信息、年度报名统计、当前黑名单状态和最近解封时间 */ - Pagination pageData(PageForm pageForm, Cnd cnd, String activityId, String courseId); + Pagination pageData(PageForm pageForm, Cnd cnd, Integer year); /** - * 拉黑、解封用户 + * 手动拉黑或解封用户。 * - * @param userId + * @param userId 用户ID;当前无记录时新增拉黑,当前已拉黑时解封并记录 lastUnblockTime,当前已解封时再次拉黑 */ void doHandleUser(String userId); + /** + * 判断用户是否处于品牌活动黑名单中。 + * + * @param userId 当前报名用户ID + * @return true 表示用户已被拉黑,不能继续报名品牌活动 + */ + boolean isBlackListed(String userId); + + /** + * 按年度自动判断用户是否应被拉黑。 + * + * @param year 当前统计年度,统计 train_sign_up_activity.year 等于该年度的报名签到记录 + * @param userId 当前报名用户ID + * @return true 表示用户当前已被拉黑或已达到年度自动拉黑条件,不能继续报名 + */ + boolean autoBlackByYearAndUser(Integer year, String userId); + /** * 上课记录 * diff --git a/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/service/impl/TrainSignUpActivityServiceImpl.java b/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/service/impl/TrainSignUpActivityServiceImpl.java index 3674d9d2..001ff02f 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/service/impl/TrainSignUpActivityServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/service/impl/TrainSignUpActivityServiceImpl.java @@ -6,9 +6,12 @@ import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.StrUtil; import com.budwk.app.base.page.Pagination; import com.budwk.app.base.param.PageForm; +import com.budwk.app.base.result.Result; import com.budwk.app.base.service.impl.BaseServiceImpl; import com.budwk.app.sys.models.Sys_home_activity; import com.budwk.app.sys.models.Sys_config; +import com.budwk.app.sys.models.Sys_user; +import com.budwk.app.sys.services.SysMsgService; import com.budwk.app.sys.views.View_user; import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.zhgh.activity.trainSignUp.models.*; @@ -48,6 +51,8 @@ public class TrainSignUpActivityServiceImpl extends BaseServiceImpl courseIds, String content) { + if (StrUtil.isBlank(activityId) || Lang.isEmpty(courseIds) || StrUtil.isBlank(content)) { + return Result.error("参数不完整"); + } + + // 按主活动和所选分活动限定通知范围,仅发送给正常报名人员;state=3 是候补转正常后的状态。 + List userList = dao().query( + TrainSignUpUser.class, + Cnd.where("activityId", "=", activityId) + .and("courseId", "in", courseIds) + .and("state", "in", List.of(1, 3)) + ); + if (Lang.isEmpty(userList)) { + return Result.error("所选分活动暂无报名人员"); + } + + List userIds = userList.stream() + .map(TrainSignUpUser::getUserId) + .filter(StrUtil::isNotBlank) + .distinct() + .toList(); + if (Lang.isEmpty(userIds)) { + return Result.error("未找到有效的报名人员"); + } + + List sysUsers = dao().query(Sys_user.class, Cnd.where("id", "in", userIds)); + List loginNames = sysUsers.stream() + .map(Sys_user::getLoginname) + .filter(StrUtil::isNotBlank) + .distinct() + .toList(); + if (Lang.isEmpty(loginNames)) { + return Result.error("未找到有效的报名人员"); + } + + sysMsgService.sendMsg(loginNames, "香樟工会讲堂活动通知", content, SecurityUtil.getUserId()); + return Result.success("通知发送成功"); + } } diff --git a/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/service/impl/TrainSignUpUserServiceImpl.java b/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/service/impl/TrainSignUpUserServiceImpl.java index e06c6690..e2e1f8ea 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/service/impl/TrainSignUpUserServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/service/impl/TrainSignUpUserServiceImpl.java @@ -7,15 +7,19 @@ import com.budwk.app.base.service.impl.BaseServiceImpl; import com.budwk.app.base.utils.PageUtil; import com.budwk.app.zhgh.activity.trainSignUp.models.TrainSignUpBlackList; import com.budwk.app.zhgh.activity.trainSignUp.service.TrainSignUpBlackListService; +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.sql.Criteria; import org.nutz.dao.sql.Sql; +import org.nutz.ioc.aop.Aop; import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.lang.Lang; import org.nutz.lang.util.NutMap; +import java.util.Calendar; +import java.util.Date; import java.util.List; /** @@ -31,41 +35,63 @@ public class TrainSignUpUserServiceImpl extends BaseServiceImpl courseEndTime $var) AS absentCount, - ( SELECT count( 1 ) FROM train_sign_up_user_course WHERE userId = tsuu.userId AND isAttend = 1 and tsuu.state!=2 AND now()> courseEndTime $var) AS signCount, - ( SELECT count( 1 ) FROM train_sign_up_user_course WHERE userId = tsuu.userId and tsuu.state!=2 $var) AS totalCount, - if(tsubl.isDisabled=1,true,false) isDisabled, - group_CONCAT( tsuc.courseName ) AS courseNames + u.username, + u.loginname, + u.mobile, + u.unitName, + u.unionName, + IFNULL(signStats.totalCount, 0) AS totalCount, + IFNULL(signStats.signCount, 0) AS signCount, + IFNULL(signStats.absentCount, 0) AS absentCount, + if(tsubl.isDisabled=1,true,false) isDisabled, + tsubl.lastUnblockTime FROM - `train_sign_up_user` tsuu - LEFT JOIN `vw_user` u ON u.id = tsuu.userId - LEFT JOIN train_sign_up_activity act on act.id = tsuu.activityId - LEFT JOIN train_sign_up_course tsuc ON tsuc.id = tsuu.courseId - LEFT JOIN train_sign_up_black_list tsubl on tsubl.userId = tsuu.userId + `vw_user` u + LEFT JOIN train_sign_up_black_list tsubl on tsubl.userId = u.id + LEFT JOIN ( + SELECT + applyStats.userId, + sum(case when applyStats.lastCourseEndTime < now() then 1 else 0 end) AS totalCount, + sum(case when applyStats.lastCourseEndTime < now() and applyStats.hasAttend = 1 then 1 else 0 end) AS signCount, + sum(case when applyStats.lastCourseEndTime < now() and applyStats.hasAttend = 0 then 1 else 0 end) AS absentCount + FROM + ( + SELECT + su.userId, + su.activityId, + su.courseId, + max(case when uc.isAttend = true then 1 else 0 end) AS hasAttend, + max(uc.courseEndTime) AS lastCourseEndTime + FROM + train_sign_up_user su + LEFT JOIN train_sign_up_activity act ON act.id = su.activityId + LEFT JOIN train_sign_up_course course ON course.id = su.courseId + LEFT JOIN train_sign_up_user_course uc ON uc.activityId = su.activityId + AND uc.courseId = su.courseId + AND uc.userId = su.userId + WHERE + act.year = @year + AND su.state != 2 + AND course.isMobileSign = 1 + GROUP BY + su.userId, + su.activityId, + su.courseId + ) applyStats + GROUP BY + applyStats.userId + ) signStats ON signStats.userId = u.id $condition """); - cnd.groupBy("tsuu.userId"); - Criteria varCnd = Cnd.cri(); - varCnd.where().setTop(false); - varCnd.where().andEX("activityId", "=", activityId); - varCnd.where().andEX("courseId", "=", courseId); - if (!varCnd.where().isEmpty()) { - sql.vars().set("var", "and " + varCnd.toSql(null)); - } + sql.setParam("year", year); if (StrUtil.isAllBlank(pageForm.getPageOrderName(), pageForm.getPageOrderBy())) { - cnd.desc("tsuu.unitId"); + cnd.desc("u.unitName").asc("u.loginname"); } else { cnd.orderBy(pageForm.getPageOrderName(), PageUtil.getOrder(pageForm.getPageOrderBy())); } @@ -75,6 +101,7 @@ public class TrainSignUpUserServiceImpl extends BaseServiceImpl @countStartTime + """); + sql.setParam("userId", userId); + sql.setParam("year", year); + sql.setParam("countStartTime", countStartTime); + + if (count(sql) < 2) { + return false; + } + + if (blackRecord == null) { + TrainSignUpBlackList blackList = new TrainSignUpBlackList(); + blackList.setUserId(userId); + blackList.setIsDisabled(true); + dao().insert(blackList); + } else { + dao().update(TrainSignUpBlackList.class, + Chain.make("isDisabled", true), + Cnd.where("userId", "=", userId)); + } + return true; + } + + private Date getYearStartTime(Integer year) { + Calendar calendar = Calendar.getInstance(); + calendar.clear(); + calendar.set(Calendar.YEAR, year); + calendar.set(Calendar.MONTH, Calendar.JANUARY); + calendar.set(Calendar.DAY_OF_MONTH, 1); + return calendar.getTime(); + } + @Override public List attendClassRecord(String userId) { Sql sql = Sqls.create(""" diff --git a/src/main/resources/views/platform/zhgh/activity/trainSignUp/manage/index.html b/src/main/resources/views/platform/zhgh/activity/trainSignUp/manage/index.html index 377cd4a0..7d6ea24c 100644 --- a/src/main/resources/views/platform/zhgh/activity/trainSignUp/manage/index.html +++ b/src/main/resources/views/platform/zhgh/activity/trainSignUp/manage/index.html @@ -130,6 +130,21 @@ layout("/layouts/platform.html"){ :visible.sync="notifyDialogVisible" :close-on-click-modal="false" width="50%"> + + + + 取 消 - 发 送 + 发 送 @@ -175,6 +190,10 @@ layout("/layouts/platform.html"){ notifyDialogVisible: false, notifyContent: '', notifyActivityId: '', + notifyCourseIds: [], + notifyCourseList: [], + notifyCourseLoading: false, + notifyLoading: false, } }, methods: { @@ -206,28 +225,51 @@ layout("/layouts/platform.html"){ openNotifyDialog(row) { this.notifyActivityId = row.id this.notifyContent = '' + this.notifyCourseIds = [] + this.notifyCourseList = [] this.notifyDialogVisible = true + this.notifyCourseLoading = true + this.$axios.post("/platform/trainSignUp/manage/findOne", { id: row.id }) + .then((resp) => { + if (resp.code === 0) { + this.notifyCourseList = resp.data.courseList || [] + } else { + this.$message.warning(resp.msg) + } + }) + .finally(() => { + this.notifyCourseLoading = false + }) }, - async sendNotify() { + sendNotify() { + if (this.notifyCourseIds.length === 0) { + this.$message.warning('请选择要通知的分活动') + return + } if (!this.notifyContent.trim()) { this.$message.warning('请输入通知内容') return } - this.$confirm("确认发送通知给所有报名人员吗?", "提示", { + this.$confirm("确认发送通知给所选分活动的报名人员吗?", "提示", { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning" - }).then(async () => { - const resp = await this.$axios.post(loc() + "/sendNotify", { + }).then(() => { + this.notifyLoading = true + this.$axios.post(loc() + "/sendNotify", { activityId: this.notifyActivityId, + courseIds: JSON.stringify(this.notifyCourseIds), content: this.notifyContent + }).then((resp) => { + if (resp.code === 0) { + this.$message.success(resp.msg) + this.notifyDialogVisible = false + } else { + this.$message.warning(resp.msg) + } + }).finally(() => { + this.notifyLoading = false }) - if (resp.code === 0) { - this.$message.success(resp.msg) - this.notifyDialogVisible = false - } else { - this.$message.warning(resp.msg) - } }).catch(() => {}) }, async activityStatusChange(notice, val, id) { diff --git a/src/main/resources/views/platform/zhgh/activity/trainSignUp/userManage/index.html b/src/main/resources/views/platform/zhgh/activity/trainSignUp/userManage/index.html index 60d557a6..926e0c15 100644 --- a/src/main/resources/views/platform/zhgh/activity/trainSignUp/userManage/index.html +++ b/src/main/resources/views/platform/zhgh/activity/trainSignUp/userManage/index.html @@ -8,6 +8,7 @@ layout("/layouts/platform.html"){ - - - - - - - - - - - - 补充人员 - 导出签到名单 - 导出领取名单 - + + -