香樟工会讲堂:

1、活动管理:微信二维码删除无效
2、活动报名控制不签到两次,一年之内不能报名。一年之后,自动解除限制。
3、报名统计 查看页面添加工号/姓名查询
This commit is contained in:
2026-05-14 16:59:22 +08:00
parent 9061d1e266
commit 6b9beb7a5e
12 changed files with 512 additions and 71 deletions
@@ -2,8 +2,6 @@ package com.budwk.app.zhgh.activity.trainSignUp.controller.manage;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaMode;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.annotation.SLog;
@@ -432,31 +430,10 @@ public class TrainSignUpApplyController {
@SaCheckPermission(value = {"trainSignUp.apply", "h5.trainSignUp.apply"}, mode = SaMode.OR)
@SLog(tag = "品牌活动-活动报名", msg = "判断缺席次数")
public Result validSignCount(@Param("activityId") String activityId) {
// 缺席2次,就1年内不能报名
// 查询今天,然后往前推1年,这段时间有没有缺席超过2次
DateTime today = DateUtil.date();
DateTime lastYear = DateUtil.offset(today, DateField.YEAR, -1);
// 找需要签到的分活动
List<TrainSignUpActivity> activityList = dao.query(TrainSignUpActivity.class, Cnd.where("year", "in", Lang.array(DateUtil.thisYear(), DateUtil.thisYear() - 1)));
List<String> activityIdList = activityList.stream().map(TrainSignUpActivity::getId).toList();
List<TrainSignUpCourse> courses = dao.query(TrainSignUpCourse.class, Cnd.where("isMobileSign", "=", true).and("activityId", "in", activityIdList));
List<String> courseIdList = courses.stream().map(TrainSignUpCourse::getId).toList();
int count = dao.count(
TrainSignUpUserCourse.class,
Cnd.where("userId", "=", SecurityUtil.getUserId())
.and("courseStartTime", ">", DateUtil.format(lastYear, "yyyy-MM-dd HH:mm:ss"))
.and("courseEndTime", "<", DateUtil.format(today, "yyyy-MM-dd HH:mm:ss"))
.and("isAttend", "=", false)
.and("courseId", "in", courseIdList)
);
if(count >= 2) {
if (trainSignUpActivityService.isAbsenceSignUpLimited(SecurityUtil.getUserId())) {
return Result.error(99, "您已连续2次未签到活动,不能报名,请联系校工会");
}
return Result.success();
return Result.success();
}
@At
@@ -121,8 +121,9 @@ public class TrainSignUpActivityStatisticsController {
@At
@ApiOperation("报名人员列表")
@SaCheckPermission("trainSignUp.statistics")
public Result registerUserList(@Param(value = "courseId") String courseId) {
return Result.success(trainSignUpActivityStatisticsService.registerUserList(courseId));
public Result registerUserList(@Param(value = "courseId") String courseId,
@Param("keyword") String keyword) {
return Result.success(trainSignUpActivityStatisticsService.registerUserList(courseId, keyword));
}
@At
@@ -151,8 +152,9 @@ public class TrainSignUpActivityStatisticsController {
@SaCheckPermission("trainSignUp.statistics")
public Result getSignInfo(PageForm pageForm,
@Param("courseId") String courseId,
String timeId) {
return Result.success(trainSignUpActivityStatisticsService.getSignInfo(pageForm, courseId, timeId));
@Param("timeId") String timeId,
@Param("keyword") String keyword) {
return Result.success(trainSignUpActivityStatisticsService.getSignInfo(pageForm, courseId, timeId, keyword));
}
@At
@@ -110,6 +110,14 @@ public interface TrainSignUpActivityService extends BaseService<TrainSignUpActiv
*/
boolean isSignCourseByUser(String courseId, String userId);
/**
* 判断用户是否在配置年限内缺席达到限制次数。
*
* @param userId 当前报名用户ID
* @return true 表示用户被缺席规则限制报名
*/
boolean isAbsenceSignUpLimited(String userId);
/**
* 手机端签到
*
@@ -27,10 +27,11 @@ public interface TrainSignUpActivityStatisticsService extends BaseService<TrainS
/**
* 该课程下的报名人员信息
*
* @param courseId
* @return
* @param courseId 培训班ID
* @param keyword 工号/姓名,支持模糊查询
* @return 报名人员列表
*/
List<NutMap> registerUserList(String courseId);
List<NutMap> registerUserList(String courseId, String keyword);
List<NutMap> getTaleColumnInfo(String courseId);
@@ -45,10 +46,13 @@ public interface TrainSignUpActivityStatisticsService extends BaseService<TrainS
/**
* 获取每个课程的签到情况
*
* @param courseId
* @return k->每个培训班每节课的上课时间 v->上课记录list
* @param pageForm 分页参数
* @param courseId 培训班ID
* @param timeId 课次ID
* @param keyword 工号/姓名,支持模糊查询
* @return 签到分页列表
*/
Pagination getSignInfo(PageForm pageForm, String courseId, String timeId);
Pagination getSignInfo(PageForm pageForm, String courseId, String timeId, String keyword);
/**
* 报名人员list 导出
@@ -1,10 +1,14 @@
package com.budwk.app.zhgh.activity.trainSignUp.service.impl;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
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.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.views.View_user;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.activity.trainSignUp.models.*;
@@ -38,6 +42,10 @@ import java.util.stream.Collectors;
@IocBean(args = {"refer:dao"})
public class TrainSignUpActivityServiceImpl extends BaseServiceImpl<TrainSignUpActivity> implements TrainSignUpActivityService {
private static final String ABSENCE_LIMIT_YEARS_CONFIG_KEY = "train_sign_up_absence_limit_years";
private static final int DEFAULT_ABSENCE_LIMIT_YEARS = 1;
private static final int ABSENCE_LIMIT_COUNT = 2;
@Inject
private TrainSignUpActivityStatisticsService statisticsService;
@@ -486,6 +494,39 @@ public class TrainSignUpActivityServiceImpl extends BaseServiceImpl<TrainSignUpA
return dao().count(TrainSignUpUser.class, Cnd.where("courseId", "=", courseId).and("userId", "=", userId)) > 0;
}
@Override
public boolean isAbsenceSignUpLimited(String userId) {
int limitYears = getAbsenceLimitYears();
DateTime today = DateUtil.date();
DateTime limitStartTime = DateUtil.offset(today, DateField.YEAR, -limitYears);
// 只统计配置年限内、需要手机签到且课程已结束的未签到记录,超过期限的缺席自动解除限制。
int count = dao().count(
TrainSignUpUserCourse.class,
Cnd.where("userId", "=", userId)
.and("courseStartTime", ">", DateUtil.format(limitStartTime, "yyyy-MM-dd HH:mm:ss"))
.and("courseEndTime", "<", DateUtil.format(today, "yyyy-MM-dd HH:mm:ss"))
.and("isAttend", "=", false)
.and(new Static("courseId in (select id from train_sign_up_course where isMobileSign = 1)"))
);
return count >= ABSENCE_LIMIT_COUNT;
}
private int getAbsenceLimitYears() {
Sys_config config = dao().fetch(Sys_config.class, ABSENCE_LIMIT_YEARS_CONFIG_KEY);
if (config == null || StrUtil.isBlank(config.getConfigValue())) {
return DEFAULT_ABSENCE_LIMIT_YEARS;
}
try {
int limitYears = Integer.parseInt(config.getConfigValue().trim());
// 配置必须为正整数;缺失或非法时使用默认1年,避免误放开报名限制。
return limitYears > 0 ? limitYears : DEFAULT_ABSENCE_LIMIT_YEARS;
} catch (NumberFormatException e) {
log.warn("培训报名缺席限制年限配置非法,configKey={}, configValue={}", ABSENCE_LIMIT_YEARS_CONFIG_KEY, config.getConfigValue());
return DEFAULT_ABSENCE_LIMIT_YEARS;
}
}
@Override
public void doQd(String id) {
NutMap updateMap = NutMap.NEW();
@@ -75,7 +75,7 @@ public class TrainSignUpActivityStatisticsServiceImpl extends BaseServiceImpl<Tr
}
@Override
public List<NutMap> registerUserList(String courseId) {
public List<NutMap> registerUserList(String courseId, String keyword) {
Sql sql = Sqls.create("""
SELECT
u.loginname,
@@ -94,9 +94,15 @@ public class TrainSignUpActivityStatisticsServiceImpl extends BaseServiceImpl<Tr
LEFT JOIN `vw_user` u ON u.id = tsuu.userId
$condition
ORDER BY FIELD( tsuu.state, 1, 3, 2, 4 ),tsuu.signUpTime desc,u.unionid desc, u.unitid desc
""");
""");
Cnd cnd = Cnd.NEW();
cnd.andEX("tsuu.courseId", "=", courseId);
// 工号/姓名共用一个搜索框,输入内容命中任一字段即可返回。
if (StrUtil.isNotBlank(keyword)) {
SqlExpressionGroup group = new SqlExpressionGroup();
group.andLike("u.loginname", keyword).orLike("u.username", keyword);
cnd.and(group);
}
sql.setCondition(cnd);
List<NutMap> list = listMap(sql);
for (NutMap o : list) {
@@ -158,7 +164,7 @@ public class TrainSignUpActivityStatisticsServiceImpl extends BaseServiceImpl<Tr
}
@Override
public Pagination getSignInfo(PageForm pageForm, String courseId, String timeId) {
public Pagination getSignInfo(PageForm pageForm, String courseId, String timeId, String keyword) {
Sql sql = Sqls.create("""
SELECT
tsuuc.id,
@@ -178,6 +184,12 @@ public class TrainSignUpActivityStatisticsServiceImpl extends BaseServiceImpl<Tr
Cnd cnd = Cnd.NEW();
cnd.and("tsuuc.courseId", "=", courseId);
cnd.and("tsuuc.activityCourseId", "=", timeId);
// 签到情况页签使用独立关键字,只过滤当前课次的签到列表。
if (StrUtil.isNotBlank(keyword)) {
SqlExpressionGroup group = new SqlExpressionGroup();
group.andLike("u.loginname", keyword).orLike("u.username", keyword);
cnd.and(group);
}
cnd.desc("isAttend");
cnd.desc("attendTime");
sql.setCondition(cnd);
@@ -28,11 +28,15 @@ 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.mvc.annotation.AdaptBy;
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.nutz.mvc.upload.TempFile;
import org.nutz.mvc.upload.UploadAdaptor;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
/**
@@ -55,6 +59,8 @@ public class OpinionMineController {
private OpinionCommonService opinionCommonService;
@Inject
private FlowEngine flowEngine;
@Inject
private OpinionWriteService opinionWriteService;
@At("")
@Ok("beetl:/platform/zhgh/democratic/opinion/transact/mine/index.html")
@@ -139,4 +145,22 @@ public class OpinionMineController {
OpinionInfo info = dao.fetch(OpinionInfo.class, id);
return Result.success(info);
}
@At
@SaCheckPermission("opinion.mine")
@ApiOperation("意见导入")
@SLog(tag = "教代会意见系统-我的意见", msg = "意见导入")
@Aop(TransAop.READ_COMMITTED)
@AdaptBy(type = UploadAdaptor.class, args = {"ioc:fileUpload"})
public Result importOpinion(@Param("file") TempFile file) {
return Result.success(opinionWriteService.importOpinion(file));
}
@At
@Ok("void")
@SaCheckPermission("opinion.mine")
@ApiOperation("意见导入模板下载")
public void downloadImportTemplate(HttpServletResponse response) {
opinionWriteService.downloadImportTemplate(response);
}
}
@@ -1,11 +1,12 @@
package com.budwk.app.zhgh.democratic.opinion.service;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.sys.models.Sys_dict;
import com.budwk.app.base.model.ExcelImportRes;
import com.budwk.app.zhgh.democratic.opinion.mode.OpinionImportExcelMode;
import com.budwk.app.zhgh.democratic.opinion.models.OpinionInfo;
import com.budwk.app.zhgh.democratic.proposal.models.ProposalInfo;
import org.nutz.mvc.upload.TempFile;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
public interface OpinionWriteService extends BaseService<OpinionInfo> {
@@ -17,4 +18,19 @@ public interface OpinionWriteService extends BaseService<OpinionInfo> {
*/
String generateOpinionCode(String sessionId,String delegationId);
/**
* 下载意见导入模板。
*
* @param response 文件响应对象
*/
void downloadImportTemplate(HttpServletResponse response);
/**
* 导入意见并在校验全部通过后直接提交流程。
*
* @param file 上传的 Excel 文件
* @return 导入结果,包含成功数和失败明细
*/
ExcelImportRes<OpinionImportExcelMode> importOpinion(TempFile file);
}
@@ -1,30 +1,54 @@
package com.budwk.app.zhgh.democratic.opinion.service.impl;
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.model.ExcelImportRes;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.sys.models.Sys_dict;
import com.budwk.app.sys.services.SysDictService;
import com.budwk.app.base.utils.CommonDownloadUtil;
import com.budwk.app.flow.constant.FlowConst;
import com.budwk.app.flow.engine.FlowEngine;
import com.budwk.app.flow.entity.ProcessInstance;
import com.budwk.app.flow.entity.ProcessTask;
import com.budwk.app.flow.enums.ProcessSubmitTypeEnum;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.democratic.opinion.mode.OpinionImportExcelMode;
import com.budwk.app.zhgh.democratic.opinion.models.OpinionInfo;
import com.budwk.app.zhgh.democratic.opinion.models.OpinionType;
import com.budwk.app.zhgh.democratic.opinion.service.OpinionWriteService;
import com.budwk.app.zhgh.democratic.proposal.models.ProposalInfo;
import com.budwk.app.zhgh.democratic.proposal.service.ProposalWriteService;
import com.budwk.app.zhgh.democratic.proposal.models.ProposalUndertake;
import com.budwk.app.zhgh.democratic.teachercongress.delegate.models.Teacher_congress_delegate;
import com.budwk.app.zhgh.democratic.teachercongress.delegation.models.Teacher_congress_delegation;
import com.budwk.app.zhgh.democratic.teachercongress.discussiongroup.models.Teacher_congress_discussion_group;
import com.budwk.app.zhgh.democratic.teachercongress.prepare.models.Teacher_congress_session;
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.entity.Record;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.upload.TempFile;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@IocBean(args = {"refer:dao"})
public class OpinionWriteServiceImpl extends BaseServiceImpl<OpinionInfo> implements OpinionWriteService {
@Inject
private FlowEngine flowEngine;
public OpinionWriteServiceImpl(Dao dao) {
super(dao);
}
@@ -57,4 +81,270 @@ public class OpinionWriteServiceImpl extends BaseServiceImpl<OpinionInfo> implem
}
return opinionCode;
}
@Override
public void downloadImportTemplate(HttpServletResponse response) {
List<ExcelExportEntity> entities = new ArrayList<>();
entities.add(new ExcelExportEntity("所属讨论组", "discussionGroupName", 25));
entities.add(new ExcelExportEntity("建议人", "suggestUserName", 20));
entities.add(new ExcelExportEntity("建议人所属代表团", "delegationName", 25));
entities.add(new ExcelExportEntity("所属教代会", "sessionName", 25));
entities.add(new ExcelExportEntity("意见类别", "typeName", 20));
entities.add(new ExcelExportEntity("意见时间", "createTime", 20));
entities.add(new ExcelExportEntity("建议承办单位", "suggestUnits", 35));
entities.add(new ExcelExportEntity("意见和建议", "name", 50));
ExportParams exportParams = new ExportParams();
exportParams.setType(ExcelType.XSSF);
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entities, Collections.emptyList());
CommonDownloadUtil.download("意见导入模板.xlsx", workbook, response);
}
@Override
public ExcelImportRes<OpinionImportExcelMode> importOpinion(TempFile file) {
List<OpinionImportExcelMode> importRows = ExcelImportUtil.importExcel(file.getFile(), OpinionImportExcelMode.class, new ImportParams());
List<OpinionInfo> opinionList = new ArrayList<>();
ExcelImportRes<OpinionImportExcelMode> excelImportRes = new ExcelImportRes<>();
excelImportRes.setTotalRecords(importRows.size());
// 先整表校验并组装入库对象;存在任意错误时不启动流程,避免导入半成功。
for (int i = 0; i < importRows.size(); i++) {
OpinionImportExcelMode row = importRows.get(i);
if (isEmptyRow(row)) {
continue;
}
OpinionInfo opinionInfo = buildOpinionInfo(row, i);
if (StrUtil.isBlank(row.getErrMsg())) {
opinionList.add(opinionInfo);
}
}
List<OpinionImportExcelMode> errorDetails = importRows.stream()
.filter(row -> StrUtil.isNotBlank(row.getErrMsg()))
.collect(Collectors.toList());
excelImportRes.setErrorDetails(errorDetails);
excelImportRes.setFailedCount(errorDetails.size());
if (excelImportRes.getFailedCount() > 0) {
excelImportRes.setSuccessCount(0);
return excelImportRes;
}
for (OpinionInfo opinionInfo : opinionList) {
submitImportedOpinion(opinionInfo);
}
excelImportRes.setSuccessCount(opinionList.size());
return excelImportRes;
}
private OpinionInfo buildOpinionInfo(OpinionImportExcelMode row, int rowIndex) {
List<String> errors = new ArrayList<>();
// 导入模板只填写业务字段,记录人固定取当前登录人并统一校验跨表关系。
Teacher_congress_session session = fetchSession(row.getSessionName());
if (session == null) {
errors.add("所属教代会不存在");
}
Teacher_congress_discussion_group discussionGroup = session == null ? null : fetchDiscussionGroup(session.getId(), row.getDiscussionGroupName());
if (discussionGroup == null) {
errors.add("所属讨论组不存在");
}
Record recorderUser = fetchCurrentUser();
if (recorderUser == null) {
errors.add("当前登录人信息不存在");
}
if (discussionGroup != null && recorderUser != null && !isRecorderInDiscussionGroup(recorderUser, discussionGroup)) {
errors.add("当前登录人不属于所属讨论组");
}
Teacher_congress_delegation delegation = session == null ? null : fetchDelegation(session.getId(), row.getDelegationName());
if (delegation == null) {
errors.add("建议人所属代表团不存在");
}
Teacher_congress_delegate suggestUser = session == null || delegation == null ? null : fetchDelegate(session.getId(), delegation.getId(), row.getSuggestUserName());
if (suggestUser == null) {
errors.add("建议人不存在或不属于建议人所属代表团");
}
OpinionType type = fetchOpinionType(row.getTypeName());
if (type == null) {
errors.add("意见类别不存在");
}
if (StrUtil.isBlank(row.getCreateTime())) {
errors.add("意见时间不能为空");
}
List<String> suggestUnits = parseSuggestUnits(row.getSuggestUnits(), errors);
if (StrUtil.isBlank(row.getName())) {
errors.add("意见和建议不能为空");
}
if (!errors.isEmpty()) {
// Excel 行号按用户看到的表头后第一行数据计算,便于直接定位错误。
row.setErrInfo(StrUtil.join("", errors), rowIndex + 2);
return null;
}
OpinionInfo opinionInfo = new OpinionInfo();
opinionInfo.setName(StrUtil.trim(row.getName()));
opinionInfo.setBrief(StrUtil.trim(row.getName()));
opinionInfo.setTypeId(type.getId());
opinionInfo.setSessionId(session.getId());
opinionInfo.setCreateTime(StrUtil.trim(row.getCreateTime()));
opinionInfo.setSuggestUnits(suggestUnits);
opinionInfo.setCreateUserId(suggestUser.getUserId());
opinionInfo.setCreateUserName(suggestUser.getUserName());
opinionInfo.setCreateUserLoginName(suggestUser.getLoginName());
opinionInfo.setDelegationId(delegation.getId());
opinionInfo.setMobile(suggestUser.getMobile());
opinionInfo.setUnitName(suggestUser.getUnitName());
opinionInfo.setSuggestUserId(recorderUser.getString("id"));
opinionInfo.setSuggestUserName(recorderUser.getString("username"));
opinionInfo.setSuggestUserLoginName(recorderUser.getString("loginname"));
opinionInfo.setSuggestUserDelegationId(discussionGroup.getId());
opinionInfo.setSuggestUserDelegationName(discussionGroup.getName());
return opinionInfo;
}
private void submitImportedOpinion(OpinionInfo opinionInfo) {
// 编号必须在逐条入库前生成,确保同一代表团连续导入时序号不会重复。
if (StrUtil.isBlank(opinionInfo.getCode())) {
opinionInfo.setCode(generateOpinionCode(opinionInfo.getSessionId(), opinionInfo.getDelegationId()));
}
insert(opinionInfo);
Dict args = Dict.create();
args.set(FlowConst.SUBMIT_TYPE, ProcessSubmitTypeEnum.APPLY.getCode());
args.set(FlowConst.FORM_DATA, opinionInfo);
ProcessInstance instance = flowEngine.startProcessInstanceByKey("JDHYJ", opinionInfo.getId(), SecurityUtil.getUserId(), args);
List<ProcessTask> doingTaskList = flowEngine.processTaskService().getDoingTaskList(instance.getId(), null);
for (ProcessTask task : doingTaskList) {
flowEngine.executeProcessTask(task.getId(), SecurityUtil.getUserId(), args);
}
}
private Teacher_congress_session fetchSession(String sessionName) {
if (StrUtil.isBlank(sessionName)) {
return null;
}
return dao().fetch(Teacher_congress_session.class,
Cnd.where("fullName", "=", StrUtil.trim(sessionName)).and("delFlag", "=", false));
}
private Teacher_congress_discussion_group fetchDiscussionGroup(String sessionId, String discussionGroupName) {
if (StrUtil.isBlank(discussionGroupName)) {
return null;
}
return dao().fetch(Teacher_congress_discussion_group.class,
Cnd.where("sessionId", "=", sessionId)
.and("name", "=", StrUtil.trim(discussionGroupName))
.and("delFlag", "=", false));
}
private Teacher_congress_delegation fetchDelegation(String sessionId, String delegationName) {
if (StrUtil.isBlank(delegationName)) {
return null;
}
return dao().fetch(Teacher_congress_delegation.class,
Cnd.where("sessionId", "=", sessionId)
.and("name", "=", StrUtil.trim(delegationName))
.and("delFlag", "=", false));
}
private Teacher_congress_delegate fetchDelegate(String sessionId, String delegationId, String suggestUserName) {
if (StrUtil.isBlank(suggestUserName)) {
return null;
}
String userKey = normalizeUserKey(suggestUserName);
List<Teacher_congress_delegate> delegates = dao().query(Teacher_congress_delegate.class,
Cnd.where("sessionId", "=", sessionId)
.and("delegationId", "=", delegationId)
.and("delFlag", "=", false)
.and(Cnd.exps("userName", "=", userKey).or("loginName", "=", userKey)));
return delegates.size() == 1 ? delegates.get(0) : null;
}
private OpinionType fetchOpinionType(String typeName) {
if (StrUtil.isBlank(typeName)) {
return null;
}
String name = StrUtil.trim(typeName);
List<OpinionType> types = dao().query(OpinionType.class,
Cnd.where("name", "=", name).or("code", "=", name).and("delFlag", "=", false));
return types.size() == 1 ? types.get(0) : null;
}
private List<String> parseSuggestUnits(String suggestUnitText, List<String> errors) {
List<String> unitNames = new ArrayList<>();
if (StrUtil.isBlank(suggestUnitText)) {
errors.add("建议承办单位不能为空");
return unitNames;
}
String[] items = suggestUnitText.split("[,]");
for (String item : items) {
String unitName = StrUtil.trim(item);
if (StrUtil.isBlank(unitName)) {
continue;
}
ProposalUndertake undertake = dao().fetch(ProposalUndertake.class,
Cnd.where("name", "=", unitName).and("delFlag", "=", false));
if (undertake == null) {
errors.add("建议承办单位不存在:" + unitName);
continue;
}
unitNames.add(undertake.getName());
}
if (unitNames.isEmpty()) {
errors.add("建议承办单位不能为空");
}
return unitNames;
}
private Record fetchCurrentUser() {
Sql sql = Sqls.create("""
SELECT
id,
username,
loginname
FROM vw_user
WHERE id = @userId
""");
sql.setParam("userId", SecurityUtil.getUserId());
sql.setCallback(Sqls.callback.record());
dao().execute(sql);
return sql.getObject(Record.class);
}
private boolean isRecorderInDiscussionGroup(Record recorderUser, Teacher_congress_discussion_group discussionGroup) {
String userId = recorderUser.getString("id");
String loginName = recorderUser.getString("loginname");
String userName = recorderUser.getString("username");
return StrUtil.equals(userId, discussionGroup.getRecorderUserId())
|| StrUtil.equals(loginName, discussionGroup.getRecorderUserCode())
|| StrUtil.equals(userName, discussionGroup.getRecorderUserName());
}
private String normalizeUserKey(String userText) {
String value = StrUtil.trim(userText);
if (value.contains(" - ")) {
return StrUtil.trim(StrUtil.subAfter(value, " - ", true));
}
if (value.contains("-")) {
return StrUtil.trim(StrUtil.subAfter(value, "-", true));
}
return value;
}
private boolean isEmptyRow(OpinionImportExcelMode row) {
return StrUtil.isBlank(row.getDiscussionGroupName())
&& StrUtil.isBlank(row.getSuggestUserName())
&& StrUtil.isBlank(row.getDelegationName())
&& StrUtil.isBlank(row.getSessionName())
&& StrUtil.isBlank(row.getTypeName())
&& StrUtil.isBlank(row.getCreateTime())
&& StrUtil.isBlank(row.getSuggestUnits())
&& StrUtil.isBlank(row.getName());
}
}
@@ -106,13 +106,14 @@ layout("/layouts/platform.html"){
},
methods: {
async openSign(row) {
const resp = await this.$axios.post(loc() + "/signChange", { courseId: row.id, openOtherUnion: !row.openOtherUnion })
if (resp.code === 0) {
this.$message.success(resp.msg)
this.doSearch()
} else {
this.$message.warning(resp.msg)
}
return this.$axios.post(loc() + "/signChange", { courseId: row.id, openOtherUnion: !row.openOtherUnion }).then((resp) => {
if (resp.code === 0) {
this.$message.success(resp.msg)
this.doSearch()
} else {
this.$message.warning(resp.msg)
}
})
},
activityChange(val) {
this.$nextTick(() => {
@@ -130,12 +131,13 @@ layout("/layouts/platform.html"){
await this.doSearch()
},
async getActivityList() {
const resp = await this.$axios.post(loc() + "/activityList", { year: this.pageForm.year })
this.activityList = resp.data
if (this.activityList && this.activityList.length > 0) {
this.pageForm.activityId = this.activityList[0].id
this.activityChange(this.activityList[0].id)
}
return this.$axios.post(loc() + "/activityList", { year: this.pageForm.year }).then((resp) => {
this.activityList = resp.data
if (this.activityList && this.activityList.length > 0) {
this.pageForm.activityId = this.activityList[0].id
this.activityChange(this.activityList[0].id)
}
})
},
async initData() {
await this.getActivityList()
@@ -3,6 +3,15 @@ const info = {
<div>
<el-tabs>
<el-tab-pane label="报名人员">
<el-form :inline="true" :model="registerQueryForm" class="mt20" size="small" @submit.native.prevent>
<el-form-item label="工号/姓名">
<el-input clearable placeholder="请输入工号或姓名" v-model="registerQueryForm.keyword" @keyup.enter.native.prevent="registerUserList()"></el-input>
</el-form-item>
<el-form-item>
<el-button @click="registerUserList" type="primary">查询</el-button>
<el-button @click="resetRegisterQuery">重置</el-button>
</el-form-item>
</el-form>
<el-table :data="registerUserTableData" class="mt20" max-height="600">
<el-table-column
:label="column.label"
@@ -24,6 +33,15 @@ const info = {
</el-table>
</el-tab-pane>
<el-tab-pane label="签到情况" v-if="clickRow.isMobileSign === true && timeList.length > 0">
<el-form :inline="true" :model="signQueryForm" class="mt20" size="small" @submit.native.prevent>
<el-form-item label="工号/姓名">
<el-input clearable placeholder="请输入工号或姓名" v-model="signQueryForm.keyword" @keyup.enter.native.prevent="doSearch()"></el-input>
</el-form-item>
<el-form-item>
<el-button @click="doSearch" type="primary">查询</el-button>
<el-button @click="resetSignQuery">重置</el-button>
</el-form-item>
</el-form>
<el-tabs style="height: 660px" tab-position="left" class="mt20" @tab-click="tabClick">
<el-tab-pane v-for="item,index in timeList" :key="index">
<span slot="label">
@@ -87,11 +105,20 @@ const info = {
signUserList: [],
timeId: '',
pageForm: {},
registerQueryForm: {
keyword: ""
},
signQueryForm: {
keyword: ""
},
}
},
methods: {
async onOpen(row) {
this.clickRow = row
this.registerQueryForm.keyword = ""
this.signQueryForm.keyword = ""
this.pageForm.pageNumber = 1
await this.registerUserList()
await this.selectTimes()
await this.getTaleColumnInfo()
@@ -100,6 +127,14 @@ const info = {
this.pageForm.pageNumber = 1
await this.selectSignUsers(this.timeId)
},
async resetRegisterQuery() {
this.registerQueryForm.keyword = ""
await this.registerUserList()
},
async resetSignQuery() {
this.signQueryForm.keyword = ""
await this.doSearch()
},
async pageNumberChange(val) {
this.pageForm.pageNumber = val
await this.selectSignUsers(this.timeId)
@@ -126,25 +161,41 @@ const info = {
await this.selectSignUsers(this.timeList[val.index].id)
},
async registerUserList() {
const res = await this.$axios.post(loc() + "/registerUserList", { courseId: this.clickRow.id })
this.registerUserTableData = res.data
const params = {
courseId: this.clickRow.id
}
const keyword = this.registerQueryForm.keyword ? this.registerQueryForm.keyword.trim() : ""
if (keyword) {
params.keyword = keyword
}
return this.$axios.post(loc() + "/registerUserList", params).then((res) => {
this.registerUserTableData = res.data
})
},
async selectTimes() {
const res = await this.$axios.post(loc() + "/selectTimes", { courseId: this.clickRow.id })
this.timeList = res.data
if(this.timeList.length > 0) {
await this.selectSignUsers(this.timeList[0].id)
}
return this.$axios.post(loc() + "/selectTimes", { courseId: this.clickRow.id }).then(async (res) => {
this.timeList = res.data
if(this.timeList.length > 0) {
await this.selectSignUsers(this.timeList[0].id)
}
})
},
async selectSignUsers(timeId) {
this.timeId = timeId
this.$set(this.pageForm, 'courseId', this.clickRow.id)
this.$set(this.pageForm, 'timeId', timeId)
const res = await this.$axios.post(loc() + "/getSignInfo", this.pageForm)
if (res.code === 0) {
this.signUserList = res.data.list
this.pageForm.totalCount = res.data.totalCount
const keyword = this.signQueryForm.keyword ? this.signQueryForm.keyword.trim() : ""
if (keyword) {
this.$set(this.pageForm, 'keyword', keyword)
} else {
this.$delete(this.pageForm, 'keyword')
}
return this.$axios.post(loc() + "/getSignInfo", this.pageForm).then((res) => {
if (res.code === 0) {
this.signUserList = res.data.list
this.pageForm.totalCount = res.data.totalCount
}
})
},
async getTaleColumnInfo() {
const res = await $.get(loc() + '/getTaleColumnInfo', { courseId: this.clickRow.id })
@@ -22,7 +22,9 @@ layout("/layouts/platform.html"){
</search>
</el-card>
<el-card shadow="never">
<table-tool :columns.sync="tableColumns"></table-tool>
<table-tool :columns.sync="tableColumns">
<el-button size="small" type="primary" icon="el-icon-upload2" @click="showImportDialog = true">导入意见</el-button>
</table-tool>
<el-table :data="tableData" @sort-change="pageOrder" header-align="center" style="width: 100%" row-key="id">
<el-table-column label="序号" width="50" type="index" :index="indexMethod" fixed="left"></el-table-column>
<el-table-column
@@ -66,6 +68,17 @@ layout("/layouts/platform.html"){
</template>
</guava>
<excel-import
ref="excelImportRef"
url="/platform/opinion/mine/importOpinion"
template_url="/platform/opinion/mine/downloadImportTemplate"
:visible.sync="showImportDialog"
title="意见导入"
width="700px"
@import-success="doSearch"
:extra_params="{}"
></excel-import>
</div>
<script>
@@ -82,6 +95,7 @@ layout("/layouts/platform.html"){
return {
sessionOptions: [],
businessNo: null,
showImportDialog: false,
tableColumns: [
{label: "意见编号", prop: "code"},
{label: "意见和建议", prop: "name", width: "200px"},