This commit is contained in:
2026-06-10 14:38:57 +08:00
parent 06b8a74f41
commit 351806c339
10 changed files with 397 additions and 518 deletions
@@ -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.models.*;
import com.budwk.app.zhgh.activity.trainSignUp.service.TrainSignUpActivityService; 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.TrainSignUpActivityStatisticsService;
import com.budwk.app.zhgh.activity.trainSignUp.service.TrainSignUpBlackListService;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -48,10 +49,13 @@ import java.util.stream.Collectors;
public class TrainSignUpApplyController { public class TrainSignUpApplyController {
private final ReentrantLock lock = new ReentrantLock(true); private final ReentrantLock lock = new ReentrantLock(true);
private static final String SIGN_UP_LIMIT_MSG = "您已被限制报名香樟工会讲堂,请联系校工会。";
@Inject @Inject
private TrainSignUpActivityService trainSignUpActivityService; private TrainSignUpActivityService trainSignUpActivityService;
@Inject @Inject
private TrainSignUpBlackListService trainSignUpBlackListService;
@Inject
private SysDictService dictService; private SysDictService dictService;
@Inject @Inject
private TrainSignUpActivityStatisticsService statisticsService; private TrainSignUpActivityStatisticsService statisticsService;
@@ -218,6 +222,9 @@ public class TrainSignUpApplyController {
@Param(value = "currentFamilyNumber") Integer currentFamilyNumber,String IsVerifyUserApply) { @Param(value = "currentFamilyNumber") Integer currentFamilyNumber,String IsVerifyUserApply) {
try { try {
lock.lock(); lock.lock();
if (isLimitedByAbsenceBlackList()) {
return Result.error(SIGN_UP_LIMIT_MSG);
}
if (StrUtil.isBlank(courseId)) { if (StrUtil.isBlank(courseId)) {
return Result.error("报名信息为空"); return Result.error("报名信息为空");
} }
@@ -350,6 +357,9 @@ public class TrainSignUpApplyController {
public Result doSignUp(TrainSignUpUser trainSignUpUser) { public Result doSignUp(TrainSignUpUser trainSignUpUser) {
try { try {
lock.lock(); lock.lock();
if (isLimitedByAbsenceBlackList()) {
return Result.error(SIGN_UP_LIMIT_MSG);
}
boolean courseByUser = trainSignUpActivityService.isSignCourseByUser(trainSignUpUser.getCourseId(), SecurityUtil.getUserId()); boolean courseByUser = trainSignUpActivityService.isSignCourseByUser(trainSignUpUser.getCourseId(), SecurityUtil.getUserId());
if (courseByUser) { if (courseByUser) {
return Result.error("您已报过该活动"); return Result.error("您已报过该活动");
@@ -430,12 +440,16 @@ public class TrainSignUpApplyController {
@SaCheckPermission(value = {"trainSignUp.apply", "h5.trainSignUp.apply"}, mode = SaMode.OR) @SaCheckPermission(value = {"trainSignUp.apply", "h5.trainSignUp.apply"}, mode = SaMode.OR)
@SLog(tag = "品牌活动-活动报名", msg = "判断缺席次数") @SLog(tag = "品牌活动-活动报名", msg = "判断缺席次数")
public Result validSignCount(@Param("activityId") String activityId) { public Result validSignCount(@Param("activityId") String activityId) {
if (trainSignUpActivityService.isAbsenceSignUpLimited(SecurityUtil.getUserId())) { if (isLimitedByAbsenceBlackList()) {
return Result.error(99, "您已连续2次未签到活动,不能报名,请联系校工会"); return Result.error(99, SIGN_UP_LIMIT_MSG);
} }
return Result.success(); return Result.success();
} }
private boolean isLimitedByAbsenceBlackList() {
return trainSignUpBlackListService.autoBlackByYearAndUser(DateUtil.year(new Date()), SecurityUtil.getUserId());
}
@At @At
@ApiOperation("活动代人报名") @ApiOperation("活动代人报名")
@SaCheckPermission(value = {"trainSignUp.apply"}, mode = SaMode.OR) @SaCheckPermission(value = {"trainSignUp.apply"}, mode = SaMode.OR)
@@ -10,11 +10,8 @@ import com.budwk.app.flow.entity.ProcessInstance;
import com.budwk.app.flow.enums.ProcessInstanceStateEnum; import com.budwk.app.flow.enums.ProcessInstanceStateEnum;
import com.budwk.app.sys.models.Sys_home_activity; import com.budwk.app.sys.models.Sys_home_activity;
import com.budwk.app.sys.models.Sys_unit; 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.models.*;
import com.budwk.app.zhgh.activity.trainSignUp.service.TrainSignUpActivityService; 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 com.budwk.app.zhgh.club.model.SysClub;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
@@ -26,6 +23,8 @@ import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql; import org.nutz.dao.sql.Sql;
import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean; 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.lang.util.NutMap;
import org.nutz.mvc.annotation.At; import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok; import org.nutz.mvc.annotation.Ok;
@@ -53,8 +52,6 @@ public class TrainSignUpManageController {
@Inject @Inject
private TrainSignUpActivityService trainSignUpActivityManageService; private TrainSignUpActivityService trainSignUpActivityManageService;
@Inject @Inject
private SysMsgService sysMsgService;
@Inject
private Dao dao; private Dao dao;
@At("") @At("")
@@ -245,24 +242,14 @@ public class TrainSignUpManageController {
@Ok("json:full") @Ok("json:full")
@ApiOperation("通知报名人员") @ApiOperation("通知报名人员")
@SaCheckPermission("trainSignUp.manage") @SaCheckPermission("trainSignUp.manage")
public Result sendNotify(@Param("activityId") String activityId, @Param("content") String content) { public Result sendNotify(@Param("activityId") String activityId, @Param("courseIds") String courseIds, @Param("content") String content) {
if (StrUtil.isBlank(activityId) || StrUtil.isBlank(content)) { if (StrUtil.isBlank(activityId) || StrUtil.isBlank(courseIds) || StrUtil.isBlank(content)) {
return Result.error("参数不完整"); return Result.error("参数不完整");
} }
// 查询该活动下所有报名人员(state=1为正常报名) List<String> courseIdList = Json.fromJsonAsList(String.class, courseIds);
List<TrainSignUpUser> userList = dao.query(TrainSignUpUser.class, Cnd.where("activityId", "=", activityId).and("state", "=", 1)); if (Lang.isEmpty(courseIdList)) {
if (userList.isEmpty()) { return Result.error("请选择要通知的分活动");
return Result.error("该活动暂无报名人员");
} }
// 获取所有报名人员的userId return trainSignUpActivityManageService.sendNotify(activityId, courseIdList, content);
List<String> userIds = userList.stream().map(TrainSignUpUser::getUserId).distinct().toList();
// 查询用户信息获取loginname
List<Sys_user> sysUsers = dao.query(Sys_user.class, Cnd.where("id", "in", userIds));
List<String> 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("通知发送成功");
} }
} }
@@ -1,42 +1,24 @@
package com.budwk.app.zhgh.activity.trainSignUp.controller.manage; 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.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.annotation.SLog; import com.budwk.app.base.annotation.SLog;
import com.budwk.app.base.page.Pagination; import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm; import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result; 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 com.budwk.app.zhgh.activity.trainSignUp.service.TrainSignUpBlackListService;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; 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.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.SqlExpressionGroup;
import org.nutz.dao.util.cri.Static;
import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean; 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.At;
import org.nutz.mvc.annotation.Ok; import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param; 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 * @author zxy
* @Description 人员管理 * @Description 人员管理
@@ -48,8 +30,6 @@ import java.util.stream.Collectors;
@At("/platform/trainSignUp/userManage") @At("/platform/trainSignUp/userManage")
public class TrainSignUpUserManageController { public class TrainSignUpUserManageController {
@Inject
private Dao dao;
@Inject @Inject
private TrainSignUpBlackListService trainSignUpBlackListService; private TrainSignUpBlackListService trainSignUpBlackListService;
@@ -63,288 +43,34 @@ public class TrainSignUpUserManageController {
@ApiOperation("分页查询") @ApiOperation("分页查询")
@SaCheckPermission("trainSignUp.userManage") @SaCheckPermission("trainSignUp.userManage")
public Result pageData(PageForm pageForm, public Result pageData(PageForm pageForm,
@Param(value = "activityId") String activityId,
@Param(value = "courseId") String courseId,
@Param(value = "year") Integer year, @Param(value = "year") Integer year,
@Param(value = "userKeyWord") String userKeyWord) { @Param(value = "userKeyWord") String userKeyWord) {
// 接口参数:pageForm 传分页和排序参数,year 传统计年度,userKeyWord 传姓名或工号关键字;
// 返回 Result<Pagination>,列表包含用户信息、年度签到统计、当前黑名单状态和最近解封时间。
Integer queryYear = year == null ? DateUtil.thisYear() : year;
Cnd cnd = Cnd.NEW(); Cnd cnd = Cnd.NEW();
cnd.andEX("tsuu.activityId", "=", activityId);
cnd.andEX("tsuu.courseId", "=", courseId);
cnd.andEX("act.year", "=", year);
if (StrUtil.isNotBlank(userKeyWord)) { if (StrUtil.isNotBlank(userKeyWord)) {
// 人员关键词支持姓名和工号;搜索时允许展示未报名但存在于用户视图中的人员,便于管理员手动拉黑。
SqlExpressionGroup seg = new SqlExpressionGroup(); SqlExpressionGroup seg = new SqlExpressionGroup();
cnd.and(seg.andLike("u.username", userKeyWord).orLike("u.loginname", userKeyWord)); cnd.and(seg.andLike("u.username", userKeyWord).orLike("u.loginname", userKeyWord));
} } else {
if(Strings.isNotBlank(pageForm.getPageOrderName()) && Strings.isNotBlank(pageForm.getPageOrderBy())){ // 默认列表只展示本年度有报名统计或已有黑名单记录的人员,避免一次性铺开全部用户。
cnd.orderBy(pageForm.getPageOrderName(), PageUtil.getOrder(pageForm.getPageOrderBy())); 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); return Result.success(pagination);
} }
@At @At
@ApiOperation("报名人员处理") @ApiOperation("报名人员处理")
@SaCheckPermission("trainSignUp.userManage") @SaCheckPermission("trainSignUp.userManage")
@SLog(tag = "品牌活动-人员管理", msg = "报名人员处理") @SLog(tag = "品牌活动-人员管理", msg = "报名人员处理")
public Result doHandleUser(@Param("userId") String userId) { public Result doHandleUser(@Param("userId") String userId) {
// 接口参数:userId 传被处理人员ID;返回 Result,code=0 表示本次拉黑或解封处理成功。
// 手动处理只切换当前黑名单状态:拉黑时禁止后续报名;解封时记录 lastUnblockTime,后续自动拉黑从该时间重新累计缺席。
trainSignUpBlackListService.doHandleUser(userId); trainSignUpBlackListService.doHandleUser(userId);
return Result.success(); return Result.success();
} }
@At
@ApiOperation("根据活动Id获取子活动")
@SaCheckPermission("trainSignUp.userManage")
public Result getCourseByActivityId(@Param("activityId") String activityId) {
List<TrainSignUpCourse> 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<NutMap> list = trainSignUpBlackListService.listMap(sql);
if(ids.length > list.size()) {
return Result.error("您选择了" + ids.length + "位,未签到人员只有" + list.size() + "");
}
//ids的长度为几,就搞几个
List<NutMap> mapList = list.subList(0, ids.length);
List<String> 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<NutMap> userList = trainSignUpBlackListService.listMap(sql);
List<TrainSignUpCourse> courseList = dao.query(TrainSignUpCourse.class, Cnd.where("activityId", "=", activityId));
List<Map<String, Object>> sheetsList = new ArrayList<>();
List<ExcelExportEntity> 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<NutMap> v = userList.stream().filter(x -> x.getString("courseId").equals(courseId)).collect(Collectors.toList());
ExportParams userExportParams = new ExportParams();
userExportParams.setSheetName(courseName);
List<ExcelExportEntity> currentEntities = new ArrayList<>(excelCommonExportEntity);
for (NutMap userSignData : v) {
if(!userSignData.getBoolean("isAttend")) {
userSignData.put("isAttend", "未签到");
userSignData.put("attendTimeExcel", "未签到");
}else {
userSignData.put("isAttend", "已签到");
}
}
Map<String, Object> 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<String, Object> map : sheetsList) {
ExcelExportService service = new ExcelExportService();
service.createSheetForMap(workbook,(ExportParams) map.get("title"),(List<ExcelExportEntity>) 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<NutMap> userList = trainSignUpBlackListService.listMap(sql);
List<TrainSignUpCourse> courseList = dao.query(TrainSignUpCourse.class, Cnd.where("activityId", "=", activityId));
List<Map<String, Object>> sheetsList = new ArrayList<>();
List<ExcelExportEntity> 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<NutMap> v = userList.stream().filter(x -> x.getString("courseId").equals(courseId)).collect(Collectors.toList());
ExportParams userExportParams = new ExportParams();
userExportParams.setSheetName(courseName);
List<ExcelExportEntity> currentEntities = new ArrayList<>(excelCommonExportEntity);
for (NutMap userSignData : v) {
if(!userSignData.getBoolean("isReceive")) {
userSignData.put("isReceive", "未领取");
userSignData.put("receiveTimeExcel", "未领取");
}else {
userSignData.put("isReceive", "已领取");
}
}
Map<String, Object> 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<String, Object> map : sheetsList) {
ExcelExportService service = new ExcelExportService();
service.createSheetForMap(workbook,(ExportParams) map.get("title"),(List<ExcelExportEntity>) map.get("entity"),(Collection<?>) map.get("data"));
}
workbook.write(response.getOutputStream());
workbook.close();
} catch (Exception e) {
e.printStackTrace();
}
}
} }
@@ -6,6 +6,8 @@ import lombok.experimental.Accessors;
import org.nutz.dao.entity.annotation.*; import org.nutz.dao.entity.annotation.*;
import org.nutz.dao.interceptor.annotation.PrevInsert; import org.nutz.dao.interceptor.annotation.PrevInsert;
import java.util.Date;
@Data @Data
@Accessors(chain = true) @Accessors(chain = true)
@Comment("品牌活动黑名单(香樟工会)") @Comment("品牌活动黑名单(香樟工会)")
@@ -28,4 +30,10 @@ public class TrainSignUpBlackList {
@Comment("是否禁用") @Comment("是否禁用")
private Boolean isDisabled; private Boolean isDisabled;
@Column
@ColDefine(type = ColType.DATETIME)
@Comment("最近解封时间,用于重置本年度自动拉黑缺席统计起点")
private Date lastUnblockTime;
} }
@@ -2,6 +2,7 @@ package com.budwk.app.zhgh.activity.trainSignUp.service;
import com.budwk.app.base.page.Pagination; import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm; 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.base.service.BaseService;
import com.budwk.app.zhgh.activity.trainSignUp.models.TrainSignUpActivity; 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.TrainSignUpCourse;
@@ -140,4 +141,14 @@ public interface TrainSignUpActivityService extends BaseService<TrainSignUpActiv
* @param trainSignUpUser * @param trainSignUpUser
*/ */
void doSignUpInsteadApply(TrainSignUpUser trainSignUpUser); void doSignUpInsteadApply(TrainSignUpUser trainSignUpUser);
/**
* 按活动下的分活动通知报名人员。
*
* @param activityId 主活动ID,用于限定本次通知所属活动
* @param courseIds 分活动ID列表,至少选择一个;仅通知这些分活动下正常报名人员
* @param content 通知正文内容
* @return Result,成功时返回通知发送成功,失败时返回无报名人员或无有效接收人等原因
*/
Result sendNotify(String activityId, List<String> courseIds, String content);
} }
@@ -17,20 +17,39 @@ import java.util.List;
public interface TrainSignUpBlackListService extends BaseService<TrainSignUpBlackList> { public interface TrainSignUpBlackListService extends BaseService<TrainSignUpBlackList> {
/** /**
* 分页 * 年度人员黑名单分页
* *
* @param pageForm 分页 * @param pageForm 分页参数,包含页码、每页数量、排序字段和排序方向
* @return Pagination * @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); 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);
/** /**
* 上课记录 * 上课记录
* *
@@ -6,9 +6,12 @@ import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.page.Pagination; import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm; 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.base.service.impl.BaseServiceImpl;
import com.budwk.app.sys.models.Sys_home_activity; import com.budwk.app.sys.models.Sys_home_activity;
import com.budwk.app.sys.models.Sys_config; 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.sys.views.View_user;
import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.activity.trainSignUp.models.*; import com.budwk.app.zhgh.activity.trainSignUp.models.*;
@@ -48,6 +51,8 @@ public class TrainSignUpActivityServiceImpl extends BaseServiceImpl<TrainSignUpA
@Inject @Inject
private TrainSignUpActivityStatisticsService statisticsService; private TrainSignUpActivityStatisticsService statisticsService;
@Inject
private SysMsgService sysMsgService;
public TrainSignUpActivityServiceImpl(Dao dao) { public TrainSignUpActivityServiceImpl(Dao dao) {
super(dao); super(dao);
@@ -621,4 +626,45 @@ public class TrainSignUpActivityServiceImpl extends BaseServiceImpl<TrainSignUpA
asyncInsertUserCourse(trainSignUpUser.getActivityId(), trainSignUpUser.getCourseId(), trainSignUpUser.getUserId()); asyncInsertUserCourse(trainSignUpUser.getActivityId(), trainSignUpUser.getCourseId(), trainSignUpUser.getUserId());
} }
} }
@Override
@Aop(TransAop.READ_COMMITTED)
public Result sendNotify(String activityId, List<String> courseIds, String content) {
if (StrUtil.isBlank(activityId) || Lang.isEmpty(courseIds) || StrUtil.isBlank(content)) {
return Result.error("参数不完整");
}
// 按主活动和所选分活动限定通知范围,仅发送给正常报名人员;state=3 是候补转正常后的状态。
List<TrainSignUpUser> 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<String> userIds = userList.stream()
.map(TrainSignUpUser::getUserId)
.filter(StrUtil::isNotBlank)
.distinct()
.toList();
if (Lang.isEmpty(userIds)) {
return Result.error("未找到有效的报名人员");
}
List<Sys_user> sysUsers = dao().query(Sys_user.class, Cnd.where("id", "in", userIds));
List<String> 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("通知发送成功");
}
} }
@@ -7,15 +7,19 @@ import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.base.utils.PageUtil; import com.budwk.app.base.utils.PageUtil;
import com.budwk.app.zhgh.activity.trainSignUp.models.TrainSignUpBlackList; import com.budwk.app.zhgh.activity.trainSignUp.models.TrainSignUpBlackList;
import com.budwk.app.zhgh.activity.trainSignUp.service.TrainSignUpBlackListService; 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.Cnd;
import org.nutz.dao.Dao; import org.nutz.dao.Dao;
import org.nutz.dao.Sqls; import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Criteria;
import org.nutz.dao.sql.Sql; import org.nutz.dao.sql.Sql;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Lang; import org.nutz.lang.Lang;
import org.nutz.lang.util.NutMap; import org.nutz.lang.util.NutMap;
import java.util.Calendar;
import java.util.Date;
import java.util.List; import java.util.List;
/** /**
@@ -31,41 +35,63 @@ public class TrainSignUpUserServiceImpl extends BaseServiceImpl<TrainSignUpBlack
} }
@Override @Override
public Pagination pageData(PageForm pageForm, Cnd cnd, String activityId, String courseId) { public Pagination pageData(PageForm pageForm, Cnd cnd, Integer year) {
// 年度黑名单列表按一次报名统计签到结果:同一 userId + activityId + courseId 下多个签到时间段只算一次。
// 只要该报名任意时间段已签到,就算已签到;全部未签到且最后一个时间段已结束,才算缺席。
Sql sql = Sqls.create(""" Sql sql = Sqls.create("""
SELECT SELECT
u.id AS userId, u.id AS userId,
u.username, u.username,
u.loginname, u.loginname,
ifnull(tsuu.mobile, u.mobile) as mobile, u.mobile,
u.unitName, u.unitName,
u.unionName, u.unionName,
tsuc.courseName, IFNULL(signStats.totalCount, 0) AS totalCount,
tsuu.state, IFNULL(signStats.signCount, 0) AS signCount,
( SELECT count( 1 ) FROM train_sign_up_user_course WHERE userId = tsuu.userId $var) courseTotal, IFNULL(signStats.absentCount, 0) AS absentCount,
( SELECT count( 1 ) FROM train_sign_up_user_course WHERE userId = tsuu.userId AND isAttend = 0 and tsuu.state!=2 AND now()> courseEndTime $var) AS absentCount, if(tsubl.isDisabled=1,true,false) isDisabled,
( 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, tsubl.lastUnblockTime
( 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
FROM FROM
`train_sign_up_user` tsuu `vw_user` u
LEFT JOIN `vw_user` u ON u.id = tsuu.userId LEFT JOIN train_sign_up_black_list tsubl on tsubl.userId = u.id
LEFT JOIN train_sign_up_activity act on act.id = tsuu.activityId LEFT JOIN (
LEFT JOIN train_sign_up_course tsuc ON tsuc.id = tsuu.courseId SELECT
LEFT JOIN train_sign_up_black_list tsubl on tsubl.userId = tsuu.userId 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 $condition
"""); """);
cnd.groupBy("tsuu.userId"); sql.setParam("year", year);
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));
}
if (StrUtil.isAllBlank(pageForm.getPageOrderName(), pageForm.getPageOrderBy())) { if (StrUtil.isAllBlank(pageForm.getPageOrderName(), pageForm.getPageOrderBy())) {
cnd.desc("tsuu.unitId"); cnd.desc("u.unitName").asc("u.loginname");
} else { } else {
cnd.orderBy(pageForm.getPageOrderName(), PageUtil.getOrder(pageForm.getPageOrderBy())); cnd.orderBy(pageForm.getPageOrderName(), PageUtil.getOrder(pageForm.getPageOrderBy()));
} }
@@ -75,6 +101,7 @@ public class TrainSignUpUserServiceImpl extends BaseServiceImpl<TrainSignUpBlack
@Override @Override
@Aop(TransAop.READ_COMMITTED)
public void doHandleUser(String userId) { public void doHandleUser(String userId) {
TrainSignUpBlackList blackRecord = dao().fetch(TrainSignUpBlackList.class, Cnd.where("userId", "=", userId)); TrainSignUpBlackList blackRecord = dao().fetch(TrainSignUpBlackList.class, Cnd.where("userId", "=", userId));
if (Lang.isEmpty(blackRecord)) { if (Lang.isEmpty(blackRecord)) {
@@ -82,14 +109,103 @@ public class TrainSignUpUserServiceImpl extends BaseServiceImpl<TrainSignUpBlack
blackList.setUserId(userId); blackList.setUserId(userId);
blackList.setIsDisabled(true); blackList.setIsDisabled(true);
dao().insert(blackList); dao().insert(blackList);
} else if (Boolean.TRUE.equals(blackRecord.getIsDisabled())) {
dao().update(TrainSignUpBlackList.class,
Chain.make("isDisabled", false).add("lastUnblockTime", new Date()),
Cnd.where("userId", "=", userId));
} else { } else {
// blackRecord.setIsDisabled(!blackRecord.getIsDisabled()); dao().update(TrainSignUpBlackList.class,
// dao().update(blackRecord); Chain.make("isDisabled", true),
dao().delete(blackRecord); Cnd.where("userId", "=", userId));
} }
} }
@Override
public boolean isBlackListed(String userId) {
return dao().count(TrainSignUpBlackList.class, Cnd.where("userId", "=", userId)
.and("isDisabled", "=", true)) > 0;
}
@Override
@Aop(TransAop.READ_COMMITTED)
public boolean autoBlackByYearAndUser(Integer year, String userId) {
TrainSignUpBlackList blackRecord = dao().fetch(TrainSignUpBlackList.class, Cnd.where("userId", "=", userId));
if (blackRecord != null && Boolean.TRUE.equals(blackRecord.getIsDisabled())) {
return true;
}
Date yearStartTime = getYearStartTime(year);
Date countStartTime = yearStartTime;
if (blackRecord != null && blackRecord.getLastUnblockTime() != null
&& blackRecord.getLastUnblockTime().after(yearStartTime)) {
countStartTime = blackRecord.getLastUnblockTime();
}
// 自动拉黑按一次报名累计缺席:同一报名下多个签到时间段全部未签到,只计 1 次缺席。
Sql sql = Sqls.create("""
SELECT
count(1)
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
su.userId = @userId
AND act.year = @year
AND su.state != 2
AND course.isMobileSign = 1
GROUP BY
su.userId,
su.activityId,
su.courseId
) applyStats
WHERE
applyStats.hasAttend = 0
AND applyStats.lastCourseEndTime < now()
AND applyStats.lastCourseEndTime > @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 @Override
public List<NutMap> attendClassRecord(String userId) { public List<NutMap> attendClassRecord(String userId) {
Sql sql = Sqls.create(""" Sql sql = Sqls.create("""
@@ -130,6 +130,21 @@ layout("/layouts/platform.html"){
:visible.sync="notifyDialogVisible" :visible.sync="notifyDialogVisible"
:close-on-click-modal="false" :close-on-click-modal="false"
width="50%"> width="50%">
<el-select
v-model="notifyCourseIds"
multiple
filterable
clearable
:loading="notifyCourseLoading"
placeholder="请选择要通知的分活动"
style="width: 100%; margin-bottom: 15px">
<el-option
v-for="item in notifyCourseList"
:key="item.id"
:label="item.courseName"
:value="item.id">
</el-option>
</el-select>
<el-input <el-input
type="textarea" type="textarea"
:rows="6" :rows="6"
@@ -138,7 +153,7 @@ layout("/layouts/platform.html"){
</el-input> </el-input>
<span slot="footer" class="dialog-footer"> <span slot="footer" class="dialog-footer">
<el-button @click="notifyDialogVisible = false">取 消</el-button> <el-button @click="notifyDialogVisible = false">取 消</el-button>
<el-button type="primary" @click="sendNotify">发 送</el-button> <el-button type="primary" :loading="notifyLoading" @click="sendNotify">发 送</el-button>
</span> </span>
</el-dialog> </el-dialog>
</guava> </guava>
@@ -175,6 +190,10 @@ layout("/layouts/platform.html"){
notifyDialogVisible: false, notifyDialogVisible: false,
notifyContent: '', notifyContent: '',
notifyActivityId: '', notifyActivityId: '',
notifyCourseIds: [],
notifyCourseList: [],
notifyCourseLoading: false,
notifyLoading: false,
} }
}, },
methods: { methods: {
@@ -206,28 +225,51 @@ layout("/layouts/platform.html"){
openNotifyDialog(row) { openNotifyDialog(row) {
this.notifyActivityId = row.id this.notifyActivityId = row.id
this.notifyContent = '' this.notifyContent = ''
this.notifyCourseIds = []
this.notifyCourseList = []
this.notifyDialogVisible = true 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()) { if (!this.notifyContent.trim()) {
this.$message.warning('请输入通知内容') this.$message.warning('请输入通知内容')
return return
} }
this.$confirm("确认发送通知给所报名人员吗?", "提示", { this.$confirm("确认发送通知给所选分活动的报名人员吗?", "提示", {
confirmButtonText: "确定", confirmButtonText: "确定",
cancelButtonText: "取消", cancelButtonText: "取消",
type: "warning" type: "warning"
}).then(async () => { }).then(() => {
const resp = await this.$axios.post(loc() + "/sendNotify", { this.notifyLoading = true
this.$axios.post(loc() + "/sendNotify", {
activityId: this.notifyActivityId, activityId: this.notifyActivityId,
courseIds: JSON.stringify(this.notifyCourseIds),
content: this.notifyContent 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(() => {}) }).catch(() => {})
}, },
async activityStatusChange(notice, val, id) { async activityStatusChange(notice, val, id) {
@@ -8,6 +8,7 @@ layout("/layouts/platform.html"){
<search @search="doSearch"> <search @search="doSearch">
<search-item label="年度"> <search-item label="年度">
<el-date-picker <el-date-picker
@change="doSearch"
placeholder="请选择年度" placeholder="请选择年度"
type="year" type="year"
style="width: 100%" style="width: 100%"
@@ -15,31 +16,17 @@ layout("/layouts/platform.html"){
value-format="yyyy" value-format="yyyy"
></el-date-picker> ></el-date-picker>
</search-item> </search-item>
<search-item label="活动名称">
<el-select @change="activityChange" style="width: 100%" v-model="pageForm.activityId" filterable>
<el-option :label="item.activityName" :value="item.id" v-for="item in activityList" :key="item.id"></el-option>
</el-select>
</search-item>
<search-item :label="trainType + ''">
<el-select @change="courseChange" :placeholder="'请选择' + trainType" style="width: 100%" v-model="pageForm.courseId" filterable>
<el-option :label="item.courseName" :value="item.id" v-for="item in courseList" :key="item.id"></el-option>
</el-select>
</search-item>
<search-item label="人员信息"> <search-item label="人员信息">
<el-input clearable placeholder="输入姓名或者工号搜索" v-model="pageForm.userKeyWord"></el-input> <el-input clearable placeholder="输入姓名或者工号搜索" v-model="pageForm.userKeyWord"></el-input>
</search-item> </search-item>
</search> </search>
</el-card> </el-card>
<el-card class="mt10" shadow="never"> <el-card class="mt10" shadow="never">
<table-tool ref="tool" :label="trainType + '(温馨提示:如需补充人员,请在上面选择具体的' + trainType + ''"> <table-tool ref="tool" label="年度人员黑名单管理"></table-tool>
<el-button @click="openReserve" type="primary" v-if="reserveMode === 2" size="small">补充人员</el-button>
<el-button @click="exportSignPerson" type="primary" size="small">导出签到名单</el-button>
<el-button @click="exportGiftPerson" type="primary" size="small">导出领取名单</el-button>
</table-tool>
<el-table :data="tableData" :size="tableSize" @sort-change="pageOrder"> <el-table :data="tableData" :size="tableSize" @sort-change="pageOrder">
<el-table-column :index="indexMethod" label="序号" type="index" width="80px"></el-table-column> <el-table-column :index="indexMethod" label="序号" type="index" width="80px"></el-table-column>
<el-table-column <el-table-column
:label="column.prop === 'courseNames' ? trainType : column.label" :label="column.label"
:prop="column.prop" :prop="column.prop"
:sortable="column.sortable" :sortable="column.sortable"
align="center" align="center"
@@ -48,59 +35,47 @@ layout("/layouts/platform.html"){
v-for="(column,index) in tableColumns" v-for="(column,index) in tableColumns"
:key="index" :key="index"
> >
<template v-slot:header>
<span>{{ column.label }}</span>
<el-tooltip
:content="column.help"
effect="dark"
placement="top"
v-if="column.help"
>
<i class="el-icon-question text-info ml5" style="cursor: pointer"></i>
</el-tooltip>
</template>
<template v-slot="{row}" v-if="column.prop==='isDisabled'"> <template v-slot="{row}" v-if="column.prop==='isDisabled'">
<span class="text-danger" v-if="row.isDisabled"></span> <span class="text-danger" v-if="row.isDisabled"></span>
<span class="text-success" v-else></span> <span class="text-success" v-else></span>
</template> </template>
<template v-slot="{row}" v-else-if="column.prop==='state'"> <template v-slot="{row}" v-else-if="column.prop==='lastUnblockTime'">
<span class="text-success" v-if="row.state === 1">正常报名</span> <span v-if="row.lastUnblockTime">{{ formatDateTime(row.lastUnblockTime) }}</span>
<span class="text-warning" v-else-if="row.state === 2">候补报名</span> <span v-else>-</span>
<span class="text-success" v-else-if="row.state === 3">正常报名(候补)</span>
<span class="text-info" v-else-if="row.state === 4">无效报名(缺席)</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" width="150px"> <el-table-column label="操作" width="150px">
<template v-slot="{row}"> <template v-slot="{row}">
<el-button @click="handleUser(row.userId)" size="mini" type="danger" v-if="!row.isDisabled">拉黑</el-button> <el-button @click="handleUser(row)" size="mini" type="danger" v-if="!row.isDisabled">拉黑</el-button>
<el-button @click="handleUser(row.userId)" size="mini" type="success" v-if="row.isDisabled">解封</el-button> <el-button @click="handleUser(row)" size="mini" type="success" v-if="row.isDisabled">解封</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<!--#include("/layouts/pagination.html"){}#--> <!--#include("/layouts/pagination.html"){}#-->
</el-card> </el-card>
</guava> </guava>
<el-dialog title="补充人员" :visible.sync="reserveDialogVisible" width="55%" top="3%">
<vi-title title="以下为候补人员,已为您按照报名时间降序排列"></vi-title>
<el-table :data="reserveTableData" ref="multipleTable" @selection-change="handleSelectionChange">
<el-table-column :selectable="(row, index) => {return row.state === 2}" type="selection" width="55"></el-table-column>
<el-table-column prop="username" label="姓名"></el-table-column>
<el-table-column prop="loginname" label="工号"></el-table-column>
<el-table-column prop="mobile" label="联系方式"></el-table-column>
<el-table-column prop="unitName" label="单位"></el-table-column>
<el-table-column prop="unionName" label="工会"></el-table-column>
<el-table-column prop="signUpTime" label="报名时间"></el-table-column>
</el-table>
<span slot="footer" class="dialog-footer">
<el-button @click="reserveDialogVisible = false">取 消</el-button>
<el-button type="primary" @click="reserveDo">确 定</el-button>
</span>
</el-dialog>
</div> </div>
<script nonce="${cspNonce!}"> <script nonce="${cspNonce!}">
new Vue({ new Vue({
el: "#app", el: "#app",
dicts: ["TRAIN_SIGNUP_TYPE"],
mixins: [initTableMixins], mixins: [initTableMixins],
data() { data() {
return { return {
reserveDialogVisible: false,
reserveTableData: [],
activityList: [],
pageForm: { pageForm: {
year: new Date().getFullYear().toString(), year: new Date().getFullYear().toString(),
activityId: null userKeyWord: ""
}, },
tableColumns: [ tableColumns: [
{ label: "姓名", prop: "username" }, { label: "姓名", prop: "username" },
@@ -108,131 +83,66 @@ layout("/layouts/platform.html"){
{ label: "联系方式", prop: "mobile" }, { label: "联系方式", prop: "mobile" },
{ label: "单位", prop: "unitName", sortable: true }, { label: "单位", prop: "unitName", sortable: true },
{ label: "分工会", prop: "unionName", sortable: true }, { label: "分工会", prop: "unionName", sortable: true },
{ label: "课程", prop: "courseNames", sortable: true }, {
{ label: "总次数", prop: "totalCount", sortable: true }, label: "年度总次数",
{ label: "签到次数", prop: "signCount", sortable: true }, prop: "totalCount",
{ label: "缺席次数", prop: "absentCount", sortable: true }, sortable: true,
{ label: "是否黑名单", prop: "isDisabled", sortable: true } help: "所选年度内,非候补报名、课程已结束、开启手机签到的报名活动/课程次数。同一报名下多个签到时间段只算一次。"
], },
trainType: "培训班", {
courseList: [], label: "签到次数",
course: {}, prop: "signCount",
reserveMode: 1, sortable: true,
multipleSelection: [], help: "所选年度内,课程已结束、开启手机签到,并且人员至少完成一次签到的报名活动/课程次数。"
},
{
label: "缺席次数",
prop: "absentCount",
sortable: true,
help: "所选年度内,课程已结束、开启手机签到,并且人员没有任何签到记录的报名活动/课程次数。候补报名不计入。"
},
{
label: "是否黑名单",
prop: "isDisabled",
sortable: true,
help: "是表示当前人员已被限制报名香樟工会讲堂;否表示当前未被限制。"
},
{
label: "最近解封时间",
prop: "lastUnblockTime",
sortable: true,
help: "管理员最近一次手动解封时间。解封后,自动拉黑的缺席次数会从该时间之后重新累计。"
}
]
} }
}, },
methods: { methods: {
handleSelectionChange(val) { formatDateTime(val) {
this.multipleSelection = val return this.$moment(val).format("YYYY-MM-DD HH:mm:ss")
}, },
async openReserve() { handleUser(row) {
const activity = this.activityList.find((o) => o.id === this.pageForm.activityId) const actionName = row.isDisabled ? "解封" : "拉黑"
if (this.$moment().unix() < this.$moment(activity.activityEndTime).unix()) { const userName = row.username + "" + row.loginname + ""
this.$alert("此活动还未结束,无法补充", "提示", { this.$confirm("确定要" + actionName + userName + "吗?", "提示", {
confirmButtonText: "确定"
})
return
}
const resp = await this.$axios.post("/platform/trainSignUp/userManage/getReserveUser", { courseId: this.course.id })
this.reserveTableData = resp.data
this.reserveDialogVisible = true
},
reserveDo() {
if (this.multipleSelection.length === 0) {
this.$message.warning("请先在左侧多选框中选择要补充的人员")
return
}
this.$confirm("您选择了" + this.multipleSelection.length + "位教职工,确定要进行补充操作吗?", "提示", {
confirmButtonText: "确定", confirmButtonText: "确定",
cancelButtonText: "取消", cancelButtonText: "取消",
type: "warning" type: "warning"
}) })
.then(async () => { .then(() => {
const ids = this.multipleSelection.map((o) => o.userId) $.post(loc() + "/doHandleUser", { userId: row.userId })
const resp = await this.$axios.post("/platform/trainSignUp/userManage/reserveSingUp", { .then((resp) => {
ids: JSON.stringify(ids), if (resp.code === 0) {
courseId: this.course.id this.$message.success(resp.msg)
}) this.doSearch()
if (resp.code === 0) { }
this.$message.success(resp.msg) })
this.doSearch() .always(() => {})
this.multipleSelection = []
this.$refs.multipleTable.clearSelection()
this.reserveDialogVisible = false
}
}) })
.catch(() => {}) .catch(() => {})
}, }
courseChange(val) {
const course = this.courseList.find((o) => o.id === val)
this.course = course
this.reserveMode = course.reserveMode
if (this.reserveMode === 2) {
this.tableColumns.push({ label: "报名状态", prop: "state", sortable: true })
} else {
const o = this.tableColumns.find((o) => o.label === "报名状态")
if (o !== null && o !== undefined) {
this.tableColumns.splice(this.tableColumns.length - 1, 1)
}
}
this.$refs.tool.app = this
this.doSearch()
},
exportSignPerson() {
this.exportPro(loc() + "/exportSignPerson", { activityId: this.pageForm.activityId })
},
exportGiftPerson() {
this.exportPro(loc() + "/exportGiftPerson", { activityId: this.pageForm.activityId })
},
exportPro(url, param) {
if (param.activityId === null) {
this.$message.warning("请选择活动")
return
}
this.$downLoad(url, param)
},
async activityChange(val) {
this.$nextTick(async () => {
const activity = this.activityList.find((o) => o.id === val)
const type = this.dict.type.TRAIN_SIGNUP_TYPE?.find(o => o.code === activity.trainType)
this.trainType = type?.name || "培训班"
const resp = await this.$axios.post(loc() + "/getCourseByActivityId", { activityId: val })
this.courseList = resp.data
if(this.courseList.length > 0) {
this.$set(this.pageForm, "courseId", this.courseList[0].id)
} else {
this.pageForm.courseId = ""
}
this.doSearch()
})
},
async handleUser(userId) {
const resp = await $.post(loc() + "/doHandleUser", { userId })
if (resp.code === 0) {
this.$message.success(resp.msg)
this.doSearch()
}
},
async yearChange() {
this.pageForm.activityId = null
await this.getActivityList()
await this.doSearch()
},
async getActivityList() {
const resp = await this.$axios.post("/platform/trainSignUp/statistics/activityList", { year: this.pageForm.year })
this.activityList = resp.data
if (this.activityList && this.activityList.length > 0) {
this.pageForm.activityId = this.activityList[0].id
await this.activityChange(this.activityList[0].id)
}
},
async initData() {
await this.getActivityList()
},
}, },
async created() { created() {
await this.initData() this.pageData()
await this.pageData()
} }
}) })
</script> </script>