diff --git a/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/controller/manage/TrainSignUpActivityAddController.java b/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/controller/manage/TrainSignUpActivityAddController.java index d48c08e..aab1e84 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/controller/manage/TrainSignUpActivityAddController.java +++ b/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/controller/manage/TrainSignUpActivityAddController.java @@ -6,6 +6,7 @@ import com.budwk.app.base.result.Result; import com.budwk.app.zhgh.activity.trainSignUp.models.TrainSignUpActivity; import com.budwk.app.zhgh.activity.trainSignUp.models.TrainSignUpUser; import com.budwk.app.zhgh.activity.trainSignUp.service.TrainSignUpActivityService; +import com.budwk.app.zhgh.welfare.service.WelfareProjectService; import lombok.extern.slf4j.Slf4j; import org.nutz.dao.Cnd; import org.nutz.dao.Dao; @@ -38,6 +39,9 @@ public class TrainSignUpActivityAddController { @Inject private Dao dao; + @Inject + private WelfareProjectService welfareProjectService; + @At("/add") @SaCheckPermission("trainSingUp.manage.activity.add") @Ok("beetl:/platform/zhgh/activity/trainSingUp/add/index.html") @@ -97,4 +101,17 @@ public class TrainSignUpActivityAddController { return Result.success().addData(query); } + /** + * 查询可用于签到自动选择的福利项目。 + * 这里只返回选择时间未结束的项目,包含未开始和进行中的福利项目。 + * + * @return 福利项目列表 + */ + @At + @Ok("json:full") + @SaCheckPermission("trainSingUp.manage.activity") + public Result getAvailableWelfareProjectList() { + return Result.success().addData(welfareProjectService.getAvailableSignWelfareProjects()); + } + } diff --git a/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/controller/mobile/TrainScanCodeController.java b/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/controller/mobile/TrainScanCodeController.java index 1b0ca2c..ee1d7b6 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/controller/mobile/TrainScanCodeController.java +++ b/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/controller/mobile/TrainScanCodeController.java @@ -1,18 +1,9 @@ package com.budwk.app.zhgh.activity.trainSignUp.controller.mobile; import cn.dev33.satoken.annotation.SaCheckPermission; -import cn.hutool.core.date.DateUtil; -import cn.hutool.core.util.ObjectUtil; -import com.aspose.slides.internal.og.add; import com.budwk.app.base.result.Result; -import com.budwk.app.sys.models.Sys_user; import com.budwk.app.web.commons.auth.utils.SecurityUtil; -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.models.TrainSignUpUserCourse; -import org.nutz.dao.Cnd; -import org.nutz.dao.Dao; +import com.budwk.app.zhgh.activity.trainSignUp.service.TrainSignUpActivityService; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.lang.util.NutMap; @@ -20,10 +11,6 @@ import org.nutz.mvc.annotation.At; import org.nutz.mvc.annotation.Ok; import org.nutz.mvc.annotation.Param; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - /** * @ClassName TrainScanCodeController * @Author JyuHsin @@ -37,7 +24,7 @@ import java.util.Map; public class TrainScanCodeController { @Inject - private Dao dao; + private TrainSignUpActivityService trainSignUpActivityService; @At("") @Ok("beetl:/platform/zhghh5/activity/trainSignUp/scanCode/index.html") @@ -48,68 +35,22 @@ public class TrainScanCodeController { @At @SaCheckPermission("h5.trainSignUp.sign") public Result getSignData(@Param("courseId") String courseId) { - String userId = SecurityUtil.getUserId(); - Sys_user user = dao.fetch(Sys_user.class, userId); - if (!(user.getMember() || List.of("年薪制", "境外聘用", "劳务派遣一类", "劳务派遣二类").contains(ObjectUtil.defaultIfBlank(user.getPreparedBy(), "")))) { - return Result.error(99, "您没有权限参与此次活动"); + NutMap signResult = trainSignUpActivityService.handleScanCodeSign(courseId, SecurityUtil.getUserId()); + if (signResult.getInt("code") == 0) { + return Result.success(signResult.getString("msg")).addData(signResult.get("data")); } - - TrainSignUpCourse course = dao.fetch(TrainSignUpCourse.class, courseId); - TrainSignUpActivity activity = dao.fetch(TrainSignUpActivity.class, course.getActivityId()); - - if(!(DateUtil.date().isAfter(activity.getActivityStartTime()) && DateUtil.date().isBefore(activity.getActivityEndTime()))) { - return Result.error(99, "不在活动时间范围内"); - } - - int count = dao.count(TrainSignUpUserCourse.class, - Cnd.where("activityId", "=", course.getActivityId()) - .and("userId", "=", SecurityUtil.getUserId()) - .and("isAttend", "=", true) - ); - Map resultMap = Map.of("signCount", count); - - // 查询用户是有报名记录 - TrainSignUpUserCourse userCourse = dao.fetch(TrainSignUpUserCourse.class, Cnd.where("courseId", "=", courseId).and("userId", "=", SecurityUtil.getUserId())); - if (userCourse == null) { - return Result.error(99, "您没有权限签到").addData(resultMap); - } - if (userCourse.isAttend()) { - return Result.error(99, "此点位,您已经签到").addData(resultMap); - } - - userCourse.setAttend(true); - userCourse.setAttendTime(DateUtil.date()); - - dao.update(userCourse); - count = count + 1; - return Result.success("签到成功").addData(Map.of("signCount", count)); + return Result.error(signResult.getInt("code"), signResult.getString("msg")).addData(signResult.get("data")); } @At @SaCheckPermission("h5.trainSignUp.sign") - public Result saveAddress(@Param("courseId") String courseId, - @Param("name") String name, - @Param("tel") String tel, - @Param("province") String province, - @Param("city") String city, - @Param("county") String county, - @Param("addressDetail") String addressDetail) { - - TrainSignUpUser signUpUser = dao.fetch(TrainSignUpUser.class, Cnd.where("courseId", "=", courseId).and("userId", "=", SecurityUtil.getUserId())); - - List mobileColumnsValue = new ArrayList<>(); - - Map nameMap = Map.of("columnCode", "name", "columnName", "姓名", "columnValue", name, "columnFormType", "INPUT"); - mobileColumnsValue.add(NutMap.WRAP(nameMap)); - Map telMap = Map.of("columnCode", "tel", "columnName", "电话", "columnValue", tel, "columnFormType", "INPUT"); - mobileColumnsValue.add(NutMap.WRAP(telMap)); - - String address = province + city + county + addressDetail; - Map addressMap = Map.of("columnCode", "address", "columnName", "收货地址", "columnValue", address, "columnFormType", "INPUT"); - mobileColumnsValue.add(NutMap.WRAP(addressMap)); - - signUpUser.setMobileColumnsValue(mobileColumnsValue); - dao.update(signUpUser); - return Result.success("保存成功"); + public Result saveDynamicForm(@Param("courseId") String courseId, + @Param("mobileColumnsValue") String mobileColumnsValue) { + try { + trainSignUpActivityService.saveScanCodeDynamicForm(courseId, SecurityUtil.getUserId(), mobileColumnsValue); + return Result.success("保存成功"); + } catch (IllegalArgumentException e) { + return Result.error(99, e.getMessage()); + } } } diff --git a/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/controller/statistics/TrainSignUpActivityStatisticsController.java b/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/controller/statistics/TrainSignUpActivityStatisticsController.java index 25c498c..edf72c1 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/controller/statistics/TrainSignUpActivityStatisticsController.java +++ b/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/controller/statistics/TrainSignUpActivityStatisticsController.java @@ -10,6 +10,7 @@ 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.utils.CommonDownloadUtil; import com.budwk.app.zhgh.activity.trainSignUp.models.TrainMobileSignColumn; import com.budwk.app.zhgh.activity.trainSignUp.models.TrainSignUpActivity; import com.budwk.app.zhgh.activity.trainSignUp.models.TrainSignUpCourse; @@ -241,4 +242,42 @@ public class TrainSignUpActivityStatisticsController { zipOutputStream.flush(); zipOutputStream.close(); } + + /** + * 导出活动填写信息。 + * 导出结果按活动汇总为一个Excel文件,每个用户一条记录。 + * + * @param activityId 活动ID + * @param response HTTP响应对象 + */ + @At + @Ok("void") + @SaCheckPermission("trainSingUp.statistics.activity") + public void exportFillForm(@Param(value = "activityId") String activityId, + HttpServletResponse response) throws IOException { + try { + Workbook workbook = trainSignUpActivityStatisticsService.exportFillFormWorkbook(activityId); + CommonDownloadUtil.download("填写信息.xlsx", workbook, response); + } catch (IllegalArgumentException e) { + log.warn("导出填写信息参数校验失败,activityId={}", activityId, e); + renderExportError(response, e.getMessage()); + } catch (Exception e) { + log.error("导出填写信息失败,activityId={}", activityId, e); + renderExportError(response, "导出填写信息失败"); + } + } + + /** + * 下载接口返回二进制流时,前端会以blob方式处理。 + * 这里统一返回application/json错误结构,便于前端下载组件直接弹出业务提示信息。 + * + * @param response HTTP响应对象 + * @param msg 返回给前端的错误提示 + */ + private void renderExportError(HttpServletResponse response, String msg) throws IOException { + response.reset(); + response.setCharacterEncoding(StandardCharsets.UTF_8.name()); + response.setContentType("application/json;charset=UTF-8"); + response.getWriter().write(Result.error(msg).toJsonString()); + } } diff --git a/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/models/TrainSignUpActivity.java b/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/models/TrainSignUpActivity.java index 576f2d2..64ce77d 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/models/TrainSignUpActivity.java +++ b/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/models/TrainSignUpActivity.java @@ -116,6 +116,21 @@ public class TrainSignUpActivity extends BaseModel implements Serializable, SysH @ColDefine(type = ColType.VARCHAR, width = 20) private String trainType; + @Column + @ColDefine(type = ColType.INT) + @Comment("签到点位数量") + private Integer signPointCount; + + @Column + @ColDefine(type = ColType.INT) + @Comment("签到处理方式 1.系统自动弹出填写信息 2.系统自动选择礼物或福利 3.无") + private Integer signHandleType; + + @Column + @ColDefine(type = ColType.VARCHAR, width = 32) + @Comment("签到自动选择的福利项目ID") + private String welfareProjectId; + @Override public Sys_home_activity covertToSysHomeActivity() { Sys_home_activity sysHomeActivity = new Sys_home_activity(); 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 ee417a2..09ac2f6 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 @@ -132,4 +132,22 @@ public interface TrainSignUpActivityService extends BaseService qdInfoByUserId(String userId, String activityId); + /** + * 扫描签到二维码后,完成当前点位签到,并返回活动达标状态与后续处理结果。 + * + * @param courseId 当前扫码点位所属课程ID + * @param userId 当前登录用户ID + * @return 返回签到数量、达标数量、处理方式以及处理提示 + */ + NutMap handleScanCodeSign(String courseId, String userId); + + /** + * 保存扫码达标后的动态表单信息。 + * + * @param courseId 当前扫码点位所属课程ID + * @param userId 当前登录用户ID + * @param mobileColumnsValue 动态表单字段和值JSON + */ + void saveScanCodeDynamicForm(String courseId, String userId, String mobileColumnsValue); + } diff --git a/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/service/TrainSignUpActivityStatisticsService.java b/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/service/TrainSignUpActivityStatisticsService.java index ab5ebf1..88e0ef1 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/service/TrainSignUpActivityStatisticsService.java +++ b/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/service/TrainSignUpActivityStatisticsService.java @@ -4,6 +4,7 @@ import com.budwk.app.base.page.Pagination; import com.budwk.app.base.param.PageForm; import com.budwk.app.base.service.BaseService; import com.budwk.app.zhgh.activity.trainSignUp.models.TrainSignUpUser; +import org.apache.poi.ss.usermodel.Workbook; import org.nutz.lang.util.NutMap; import java.util.List; @@ -56,4 +57,13 @@ public interface TrainSignUpActivityStatisticsService extends BaseService baoMingUserList(String activityId); + /** + * 按活动导出填写信息。 + * 导出前会校验活动下全部课程类型必须一致,导出结果按用户汇总为一人一行。 + * + * @param activityId 活动ID + * @return Excel工作簿对象 + */ + Workbook exportFillFormWorkbook(String activityId); + } 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 cd6e00e..ee7fa24 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 @@ -1,13 +1,18 @@ package com.budwk.app.zhgh.activity.trainSignUp.service.impl; +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.views.View_user; import com.budwk.app.web.commons.auth.utils.SecurityUtil; +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.welfare.model.WelfareList; +import com.budwk.app.zhgh.welfare.model.WelfareProject; import lombok.extern.slf4j.Slf4j; import org.nutz.aop.interceptor.async.Async; import org.nutz.aop.interceptor.ioc.TransAop; @@ -19,13 +24,16 @@ import org.nutz.dao.sql.Sql; import org.nutz.dao.util.cri.Static; import org.nutz.ioc.aop.Aop; import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.json.Json; import org.nutz.lang.Lang; import org.nutz.lang.util.NutMap; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; @@ -533,4 +541,333 @@ public class TrainSignUpActivityServiceImpl extends BaseServiceImpl formData; + try { + formData = Json.fromJsonAsList(NutMap.class, mobileColumnsValue); + } catch (Exception e) { + throw new IllegalArgumentException("活动报名信息格式不正确"); + } + signUpUser.setMobileColumnsValue(formData); + dao().update(signUpUser); + } + + /** + * 组装扫码页所需的活动进度与处理结果。 + * 这里会在用户达标后按活动配置自动触发后续处理,并保证重复进入页面不会重复入库。 + * + * @param activity 当前活动 + * @param courseId 当前扫码点位所属课程ID + * @param userId 当前登录用户ID + * @return 扫码页展示数据 + */ + private NutMap buildScanCodeData(TrainSignUpActivity activity, String courseId, String userId) { + int signCount = dao().count(TrainSignUpUserCourse.class, + Cnd.where("activityId", "=", activity.getId()) + .and("userId", "=", userId) + .and("isAttend", "=", true) + ); + int signPointCount = getActivitySignPointCount(activity); + boolean isReachTarget = signCount >= signPointCount; + Integer signHandleType = activity.getSignHandleType() == null ? 3 : activity.getSignHandleType(); + List dynamicColumnsData = buildScanCodeDynamicColumns(courseId, userId); + NutMap data = NutMap.NEW(); + data.addv("signCount", signCount); + data.addv("signPointCount", signPointCount); + data.addv("signHandleType", signHandleType); + data.addv("welfareProjectId", activity.getWelfareProjectId()); + data.addv("isReachTarget", isReachTarget); + data.addv("dynamicColumnsData", dynamicColumnsData); + data.addv("needDynamicForm", isReachTarget && Integer.valueOf(1).equals(signHandleType) && !dynamicColumnsData.isEmpty()); + data.addv("needWelfareSelect", false); + data.addv("welfareJumpUrl", ""); + + String handleMsg = "当前活动已达标"; + String handleStatus = "NONE"; + if (isReachTarget) { + NutMap handleResult = handleReachTarget(activity, userId); + handleStatus = handleResult.getString("handleStatus", handleStatus); + if (StrUtil.isNotBlank(handleResult.getString("handleMsg"))) { + handleMsg = handleResult.getString("handleMsg"); + } + if (handleResult.containsKey("needWelfareSelect")) { + data.put("needWelfareSelect", handleResult.getBoolean("needWelfareSelect")); + } + if (handleResult.containsKey("welfareJumpUrl")) { + data.put("welfareJumpUrl", handleResult.getString("welfareJumpUrl")); + } + if (Integer.valueOf(1).equals(signHandleType) && dynamicColumnsData.isEmpty()) { + handleMsg = "当前活动已达标"; + } + } + data.addv("handleStatus", handleStatus); + data.addv("handleMsg", handleMsg); + return data; + } + + /** + * 达标后按活动配置处理: + * 1. 返回前端展示动态表单; + * 2. 自动补齐福利名单,并引导用户跳转到福利选择页; + * 3. 不做额外处理,仅返回完成提示。 + * + * @param activity 当前活动 + * @param userId 当前登录用户ID + * @return 处理结果说明 + */ + private NutMap handleReachTarget(TrainSignUpActivity activity, String userId) { + Integer signHandleType = activity.getSignHandleType() == null ? 3 : activity.getSignHandleType(); + if (Integer.valueOf(1).equals(signHandleType)) { + return NutMap.NEW().addv("handleStatus", "PENDING_FORM").addv("handleMsg", "当前活动已达标,请补充活动报名信息"); + } + if (Integer.valueOf(2).equals(signHandleType)) { + return buildWelfareSelectResult(activity.getWelfareProjectId(), userId); + } + return NutMap.NEW().addv("handleStatus", "NONE").addv("handleMsg", "当前活动已达标"); + } + + /** + * 生成扫码完成后展示的动态表单字段。 + * 表单字段沿用当前课程类型对应的报名动态字段配置,并优先回填用户已经保存过的 mobileColumnsValue。 + * + * @param courseId 当前扫码点位所属课程ID + * @param userId 当前登录用户ID + * @return 动态表单字段列表 + */ + private List buildScanCodeDynamicColumns(String courseId, String userId) { + TrainSignUpCourse course = dao().fetch(TrainSignUpCourse.class, courseId); + if (course == null || StrUtil.isBlank(course.getCourseType())) { + return new ArrayList<>(); + } + TrainSignUpType trainSignUpType = dao().fetch(TrainSignUpType.class, course.getCourseType()); + if (trainSignUpType == null) { + return new ArrayList<>(); + } + dao().fetchLinks(trainSignUpType, "trainMobileSignColumnList", Cnd.NEW().asc("columnIndex")); + List trainMobileSignColumnList = trainSignUpType.getTrainMobileSignColumnList(); + if (trainMobileSignColumnList == null || trainMobileSignColumnList.isEmpty()) { + return new ArrayList<>(); + } + + TrainSignUpUser signUpUser = dao().fetch(TrainSignUpUser.class, + Cnd.where("courseId", "=", courseId).and("userId", "=", userId)); + Map currentValueMap = new LinkedHashMap<>(); + if (signUpUser != null && signUpUser.getMobileColumnsValue() != null) { + signUpUser.getMobileColumnsValue().forEach(item -> currentValueMap.put(item.getString("columnCode"), item)); + } + + List dynamicColumnsData = new ArrayList<>(); + trainMobileSignColumnList.forEach(item -> { + NutMap columnMap = NutMap.NEW(); + columnMap.addv("id", item.getId()); + columnMap.addv("typeId", item.getTypeId()); + columnMap.addv("columnName", item.getColumnName()); + columnMap.addv("columnCode", item.getColumnCode()); + columnMap.addv("columnType", item.getColumnType()); + columnMap.addv("selectValues", item.getSelectValues()); + columnMap.addv("isRequired", item.getIsRequired()); + columnMap.addv("columnFormType", item.getColumnFormType()); + columnMap.addv("fileNumber", item.getFileNumber()); + columnMap.addv("fileType", item.getFileType()); + columnMap.addv("columnIndex", item.getColumnIndex()); + NutMap currentValue = currentValueMap.get(item.getColumnCode()); + columnMap.addv("columnValue", currentValue == null ? "" : currentValue.getString("columnValue")); + dynamicColumnsData.add(columnMap); + }); + return dynamicColumnsData; + } + + /** + * 构造福利选择结果。 + * 达标后不再自动替用户选择福利,只自动补齐福利名单并返回福利选择页地址。 + * + * @param welfareProjectId 福利项目ID + * @param userId 当前登录用户ID + * @return 福利选择处理结果 + */ + private NutMap buildWelfareSelectResult(String welfareProjectId, String userId) { + if (StrUtil.isBlank(welfareProjectId)) { + return NutMap.NEW().addv("handleStatus", "FAILED").addv("handleMsg", "当前活动未配置福利项目,请联系管理员"); + } + WelfareProject welfareProject = dao().fetch(WelfareProject.class, welfareProjectId); + if (welfareProject == null) { + return NutMap.NEW().addv("handleStatus", "FAILED").addv("handleMsg", "未找到对应福利项目,请联系管理员"); + } + + try { + ensureWelfareList(welfareProjectId, userId); + } catch (IllegalArgumentException e) { + return NutMap.NEW().addv("handleStatus", "FAILED").addv("handleMsg", e.getMessage()); + } + return NutMap.NEW() + .addv("handleStatus", "PENDING_WELFARE") + .addv("handleMsg", "当前活动已达标,请前往选择福利") + .addv("needWelfareSelect", true) + .addv("welfareJumpUrl", "/platform/h5/welfare/userSelect/index?id=" + welfareProjectId); + } + + /** + * 补齐福利名单。 + * 自动福利处理依赖 welfare_list 中存在用户记录,因此这里会先根据视图用户信息补齐名单。 + * + * @param welfareProjectId 福利项目ID + * @param userId 当前登录用户ID + */ + private void ensureWelfareList(String welfareProjectId, String userId) { + int count = dao().count(WelfareList.class, Cnd.where("projectId", "=", welfareProjectId).and("userId", "=", userId)); + if (count > 0) { + return; + } + View_user user = dao().fetch(View_user.class, Cnd.where("id", "=", userId)); + if (user == null) { + throw new IllegalArgumentException("未找到当前用户信息"); + } + WelfareList welfareList = new WelfareList(); + welfareList.setProjectId(welfareProjectId); + welfareList.setUserId(userId); + welfareList.setWelfareUnitId(user.getUnitId()); + welfareList.setWelfareUnitName(user.getUnitName()); + welfareList.setWelfareUnionId(user.getUnionId()); + welfareList.setWelfareUnionName(user.getUnionName()); + welfareList.setPersonType(user.getPersonType()); + welfareList.setPreparedBy(user.getPreparedBy()); + welfareList.setUserState(user.getUserState()); + welfareList.setIsReceive(false); + dao().insert(welfareList); + } + + /** + * 解析活动达标点位数。 + * 优先使用活动显式配置的 signPointCount;若历史数据未配置,则回退为活动下启用手机签到的课程数量。 + * + * @param activity 当前活动 + * @return 达标所需签到点位数 + */ + private int getActivitySignPointCount(TrainSignUpActivity activity) { + if (activity.getSignPointCount() != null && activity.getSignPointCount() > 0) { + return activity.getSignPointCount(); + } + int mobileSignCourseCount = dao().count(TrainSignUpCourse.class, + Cnd.where("activityId", "=", activity.getId()).and("isMobileSign", "=", true)); + return Math.max(mobileSignCourseCount, 1); + } + + /** + * 统一组装扫码接口返回结构。 + * + * @param code 返回码 + * @param msg 返回提示 + * @param data 返回数据 + * @return 扫码接口结果 + */ + private NutMap buildScanCodeResponse(int code, String msg, NutMap data) { + return NutMap.NEW().addv("code", code).addv("msg", msg).addv("data", data); + } + } diff --git a/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/service/impl/TrainSignUpActivityStatisticsServiceImpl.java b/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/service/impl/TrainSignUpActivityStatisticsServiceImpl.java index 3982452..d0dbbe2 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/service/impl/TrainSignUpActivityStatisticsServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/activity/trainSignUp/service/impl/TrainSignUpActivityStatisticsServiceImpl.java @@ -1,21 +1,33 @@ package com.budwk.app.zhgh.activity.trainSignUp.service.impl; +import cn.afterturn.easypoi.excel.ExcelExportUtil; +import cn.afterturn.easypoi.excel.entity.ExportParams; +import cn.afterturn.easypoi.excel.entity.enmus.ExcelType; +import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity; import cn.hutool.core.util.StrUtil; import 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.zhgh.activity.trainSignUp.models.TrainMobileSignColumn; +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.TrainSignUpType; import com.budwk.app.zhgh.activity.trainSignUp.models.TrainSignUpUser; import com.budwk.app.zhgh.activity.trainSignUp.service.TrainSignUpActivityStatisticsService; import lombok.extern.slf4j.Slf4j; +import org.apache.poi.ss.usermodel.Workbook; import org.nutz.dao.Cnd; import org.nutz.dao.Dao; import org.nutz.dao.Sqls; import org.nutz.dao.sql.Sql; import org.nutz.dao.util.cri.SqlExpressionGroup; import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.json.Json; import org.nutz.lang.util.NutMap; +import java.util.ArrayList; import java.util.Comparator; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -178,4 +190,130 @@ public class TrainSignUpActivityStatisticsServiceImpl extends BaseServiceImpl courseList = dao().query(TrainSignUpCourse.class, Cnd.where("activityId", "=", activityId).asc("orderNum")); + List courseTypeList = courseList.stream() + .map(TrainSignUpCourse::getCourseType) + .filter(StrUtil::isNotBlank) + .distinct() + .collect(Collectors.toList()); + if (courseTypeList.size() > 1) { + throw new IllegalArgumentException("当前活动存在多个课程类型,不能导出填写信息"); + } + + List trainMobileSignColumnList = new ArrayList<>(); + if (!courseTypeList.isEmpty()) { + TrainSignUpType trainSignUpType = dao().fetch(TrainSignUpType.class, courseTypeList.get(0)); + if (trainSignUpType != null) { + dao().fetchLinks(trainSignUpType, "trainMobileSignColumnList", Cnd.NEW().asc("columnIndex")); + if (trainSignUpType.getTrainMobileSignColumnList() != null) { + trainMobileSignColumnList = trainSignUpType.getTrainMobileSignColumnList(); + } + } + } + + Sql userSql = Sqls.create(""" + SELECT + ts.userId, + u.username, + u.loginname, + u.unitName, + u.unionName, + u.sex, + ts.mobileColumnsValue, + ts.signUpTime + FROM + train_sign_up_user ts + LEFT JOIN vw_user u ON u.id = ts.userId + WHERE + ts.activityId = @activityId + ORDER BY + ts.signUpTime DESC + """); + userSql.setParam("activityId", activityId); + List userRecordList = listMap(userSql); + + Sql signSql = Sqls.create(""" + SELECT + tsuuc.userId, + GROUP_CONCAT(DISTINCT tsc.courseName ORDER BY tsc.orderNum SEPARATOR '、') AS signCourseNames + FROM + train_sign_up_user_course tsuuc + LEFT JOIN train_sign_up_course tsc ON tsc.id = tsuuc.courseId + WHERE + tsuuc.activityId = @activityId + AND tsuuc.isAttend = 1 + GROUP BY + tsuuc.userId + """); + signSql.setParam("activityId", activityId); + Map signCourseMap = listMap(signSql).stream() + .collect(Collectors.toMap(item -> item.getString("userId"), item -> item.getString("signCourseNames", ""), (v1, v2) -> v1)); + + Map> userGroupMap = userRecordList.stream() + .filter(item -> StrUtil.isNotBlank(item.getString("userId"))) + .collect(Collectors.groupingBy(item -> item.getString("userId"), LinkedHashMap::new, Collectors.toList())); + + List exportList = new ArrayList<>(); + for (Map.Entry> entry : userGroupMap.entrySet()) { + List sameUserRecordList = entry.getValue(); + NutMap baseUserData = sameUserRecordList.get(0); + NutMap exportRow = NutMap.NEW(); + exportRow.addv("username", baseUserData.getString("username")); + exportRow.addv("loginname", baseUserData.getString("loginname")); + exportRow.addv("unitName", baseUserData.getString("unitName")); + exportRow.addv("unionName", baseUserData.getString("unionName")); + exportRow.addv("sex", baseUserData.getString("sex")); + exportRow.addv("signCourseNames", signCourseMap.getOrDefault(entry.getKey(), "")); + + NutMap formRecord = sameUserRecordList.stream() + .filter(item -> StrUtil.isNotBlank(item.getString("mobileColumnsValue"))) + .findFirst() + .orElse(null); + Map formValueMap = new HashMap<>(); + if (formRecord != null) { + List mobileColumnsValue = Json.fromJsonAsList(NutMap.class, formRecord.getString("mobileColumnsValue")); + for (NutMap columnValue : mobileColumnsValue) { + formValueMap.put(columnValue.getString("columnCode"), columnValue.get("columnValue")); + } + } + for (TrainMobileSignColumn trainMobileSignColumn : trainMobileSignColumnList) { + Object columnValue = formValueMap.get(trainMobileSignColumn.getColumnCode()); + exportRow.addv(trainMobileSignColumn.getColumnCode(), columnValue == null ? "" : String.valueOf(columnValue)); + } + exportList.add(exportRow); + } + + List exportEntities = new ArrayList<>(); + exportEntities.add(new ExcelExportEntity("姓名", "username", 20)); + exportEntities.add(new ExcelExportEntity("工号", "loginname", 20)); + exportEntities.add(new ExcelExportEntity("单位", "unitName", 30)); + exportEntities.add(new ExcelExportEntity("分工会", "unionName", 30)); + exportEntities.add(new ExcelExportEntity("性别", "sex", 10)); + exportEntities.add(new ExcelExportEntity("打卡了哪几个活动", "signCourseNames", 40)); + for (TrainMobileSignColumn trainMobileSignColumn : trainMobileSignColumnList) { + exportEntities.add(new ExcelExportEntity(trainMobileSignColumn.getColumnName(), trainMobileSignColumn.getColumnCode(), 25)); + } + + ExportParams exportParams = new ExportParams(); + exportParams.setTitle(activity.getActivityName() + "填写信息"); + exportParams.setSheetName("填写信息"); + exportParams.setType(ExcelType.XSSF); + return ExcelExportUtil.exportExcel(exportParams, exportEntities, exportList); + } } diff --git a/src/main/java/com/budwk/app/zhgh/welfare/controller/WelfareUserSelectController.java b/src/main/java/com/budwk/app/zhgh/welfare/controller/WelfareUserSelectController.java index 0671469..c24cbf0 100644 --- a/src/main/java/com/budwk/app/zhgh/welfare/controller/WelfareUserSelectController.java +++ b/src/main/java/com/budwk/app/zhgh/welfare/controller/WelfareUserSelectController.java @@ -89,15 +89,18 @@ public class WelfareUserSelectController { public Result confirmSelect(@Param("selections") WelfareUserSelection[] selections, String projectId) { WelfareProject project = dao.fetch(WelfareProject.class, projectId); if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) { - if(project.getChoiceTimeEnd().getTime() Objects.requireNonNullElse(selection.getSelectNum(), 0)).sum(); + if (project.getMultiSelectNum() == null) { + project.setMultiSelectNum(1); + } if (sum > project.getMultiSelectNum()) { return Result.error("选择的数量不能超过" + project.getMultiSelectNum()); } diff --git a/src/main/java/com/budwk/app/zhgh/welfare/service/WelfareProjectService.java b/src/main/java/com/budwk/app/zhgh/welfare/service/WelfareProjectService.java index 3f1a2ab..d7efffb 100644 --- a/src/main/java/com/budwk/app/zhgh/welfare/service/WelfareProjectService.java +++ b/src/main/java/com/budwk/app/zhgh/welfare/service/WelfareProjectService.java @@ -50,4 +50,12 @@ public interface WelfareProjectService extends BaseService { * @return */ List getWelfareList(Integer year); + + /** + * 查询选择时间未结束的福利项目。 + * 这里用于培训报名签到配置,下拉项只展示未开始或进行中的福利项目。 + * + * @return 可用于签到配置的福利项目列表 + */ + List getAvailableSignWelfareProjects(); } diff --git a/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareProjectServiceImpl.java b/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareProjectServiceImpl.java index 4e48851..5e4591e 100644 --- a/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareProjectServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareProjectServiceImpl.java @@ -169,6 +169,15 @@ public class WelfareProjectServiceImpl extends BaseServiceImpl i return list; } + @Override + public List getAvailableSignWelfareProjects() { + Cnd cnd = Cnd.NEW(); + cnd.and(WelfareProject::getIsDisabled, "=", false); + cnd.and(new Static("choiceTimeEnd >= now()")); + cnd.asc("choiceTimeStart").desc("createdAt"); + return dao().query(WelfareProject.class, cnd); + } + private void joinWelfareList(List users, String projectId) { List lists = new ArrayList<>(); for (NutMap user : users) { diff --git a/src/main/resources/views/platform/zhgh/activity/trainSingUp/add/include_activity_edit.html b/src/main/resources/views/platform/zhgh/activity/trainSingUp/add/include_activity_edit.html index a365897..ca227ed 100644 --- a/src/main/resources/views/platform/zhgh/activity/trainSingUp/add/include_activity_edit.html +++ b/src/main/resources/views/platform/zhgh/activity/trainSingUp/add/include_activity_edit.html @@ -266,6 +266,37 @@ + +
签到扩展配置
+ + + + + + + 系统自动弹出填写信息 + 系统自动选择礼物或福利 + + + + + + + + + 返回 diff --git a/src/main/resources/views/platform/zhgh/activity/trainSingUp/add/index.html b/src/main/resources/views/platform/zhgh/activity/trainSingUp/add/index.html index 9aae840..4dc40ea 100644 --- a/src/main/resources/views/platform/zhgh/activity/trainSingUp/add/index.html +++ b/src/main/resources/views/platform/zhgh/activity/trainSingUp/add/index.html @@ -43,12 +43,15 @@ layout("/layouts/platform.html"){ use: false, historicalActList: [], historicalAct: "", + welfareProjectList: [], unionList: [], trainType: "培训班", trainTypeList: [], fromEdit: "", formData: { notice: false, + signHandleType: 3, + welfareProjectId: "", courseList: [ { orderNum: 1, courseName: "", courseTimeList: [], isMobileSign: false, isReceiveGift: false, reserveMode: 1 }, { orderNum: 2, courseName: "", courseTimeList: [], isMobileSign: false, isReceiveGift: false, reserveMode: 1 } @@ -112,6 +115,9 @@ layout("/layouts/platform.html"){ } }, computed: { + allCourseSignEnabled() { + return Array.isArray(this.formData.courseList) && this.formData.courseList.length > 0 && this.formData.courseList.every((item) => item.isMobileSign === true) + }, unionUserNumCalcTips() { return this.unionUserNumCalc.map((v) => { return ( @@ -168,9 +174,24 @@ layout("/layouts/platform.html"){ this.$set(this.formData, "activityGroupName", "全部教职工") } }, + handleActivitySignHandleTypeChange(val) { + if (val !== 2) { + this.$set(this.formData, "welfareProjectId", "") + } + }, + buildDefaultCourse(orderNum) { + return { + orderNum: orderNum, + courseName: "", + courseTimeList: [], + isMobileSign: false, + isReceiveGift: false, + reserveMode: 1 + } + }, //添加培训班 addCourse() { - this.formData.courseList.push({ courseTimeList: [], isMobileSign: false, isReceiveGift: false, reserveMode: 1 }) + this.formData.courseList.push(this.buildDefaultCourse(this.formData.courseList.length + 1)) }, //删除培训班 async removeTableCourse(index) { @@ -393,6 +414,21 @@ layout("/layouts/platform.html"){ return } } + if (this.allCourseSignEnabled && (!Number.isInteger(this.formData.signPointCount) || this.formData.signPointCount <= 0)) { + this.$message.warning("签到点位数量填写有误,请核查!") + return + } + if ( + this.allCourseSignEnabled && + (this.formData.signHandleType === "" || this.formData.signHandleType === undefined || this.formData.signHandleType === null) + ) { + this.$message.warning("签到处理方式填写有误,请核查!") + return + } + if (this.allCourseSignEnabled && this.formData.signHandleType === 2 && !this.formData.welfareProjectId) { + this.$message.warning("请选择福利项目!") + return + } } const cloneData = clone(this.formData) @@ -425,6 +461,11 @@ layout("/layouts/platform.html"){ } }) cloneData.typeLimits = limitNumArray + if (!this.allCourseSignEnabled) { + cloneData.signPointCount = null + cloneData.signHandleType = 3 + cloneData.welfareProjectId = "" + } const resp = await this.$axios.post("/platform/trainSingUp/manage/activity/doHandle", { data: JSON.stringify(cloneData) }) if (resp.code === 0) { @@ -455,6 +496,12 @@ layout("/layouts/platform.html"){ } } }) + if (this.formData.signHandleType === undefined || this.formData.signHandleType === null) { + this.$set(this.formData, "signHandleType", 3) + } + if (this.formData.welfareProjectId === undefined || this.formData.welfareProjectId === null) { + this.$set(this.formData, "welfareProjectId", "") + } } } else { } @@ -601,6 +648,11 @@ layout("/layouts/platform.html"){ const resp = await this.$axios.post("/platform/trainSingUp/manage/activity/getHistoricalActList", {}) return resp.data }, + getAvailableWelfareProjectList() { + return this.$axios.post("/platform/trainSingUp/manage/activity/getAvailableWelfareProjectList", {}).then((resp) => { + return resp.code === 0 ? resp.data : [] + }) + }, async historicalActChange(val) { const resp = await this.$axios.post("/platform/trainSingUp/manage/activity/findOne", { id: val }) if (resp.code === 0) { @@ -618,6 +670,12 @@ layout("/layouts/platform.html"){ } }) this.formData.id = "" + if (this.formData.signHandleType === undefined || this.formData.signHandleType === null) { + this.$set(this.formData, "signHandleType", 3) + } + if (this.formData.welfareProjectId === undefined || this.formData.welfareProjectId === null) { + this.$set(this.formData, "welfareProjectId", "") + } } }, async getActivityGroup() { @@ -633,6 +691,7 @@ layout("/layouts/platform.html"){ }) await this.initData() this.activityGroupList = await this.getActivityGroup() + this.welfareProjectList = await this.getAvailableWelfareProjectList() this.$businessTool.getDictOptions("CAMPUS").then((data) => { this.campusList = data }) diff --git a/src/main/resources/views/platform/zhgh/activity/trainSingUp/manage/include_activity_view.html b/src/main/resources/views/platform/zhgh/activity/trainSingUp/manage/include_activity_view.html index 5a219bf..363fa2b 100644 --- a/src/main/resources/views/platform/zhgh/activity/trainSingUp/manage/include_activity_view.html +++ b/src/main/resources/views/platform/zhgh/activity/trainSingUp/manage/include_activity_view.html @@ -8,6 +8,20 @@ {{viewData.activityEndTime}} {{viewData.activitySignUpStartTime}} {{viewData.activitySignUpEndTime}} + + {{viewData.signPointCount || 0}} + - + + + + {{getActivitySignHandleTypeName(viewData.signHandleType)}} + + - + + + {{getWelfareProjectName(viewData.welfareProjectId)}} + - + diff --git a/src/main/resources/views/platform/zhgh/activity/trainSingUp/manage/index.html b/src/main/resources/views/platform/zhgh/activity/trainSingUp/manage/index.html index d690c03..11abe38 100644 --- a/src/main/resources/views/platform/zhgh/activity/trainSingUp/manage/index.html +++ b/src/main/resources/views/platform/zhgh/activity/trainSingUp/manage/index.html @@ -184,6 +184,7 @@ layout("/layouts/platform.html"){ courseDialog: false, trainType: "", trainTypeList: [], + welfareProjectList: [], campusList: [], tableColumns: [ { label: "活动名称", prop: "activityName" }, @@ -221,6 +222,30 @@ layout("/layouts/platform.html"){ this.trainType = type ? type.name : "培训班" this.courseDialog = true }, + getActivitySignHandleTypeName(type) { + if (type === 1) { + return "系统自动弹出填写信息" + } + if (type === 2) { + return "系统自动选择礼物或福利" + } + if (type === 3) { + return "无" + } + return "未设置" + }, + getWelfareProjectName(welfareProjectId) { + const project = this.welfareProjectList.find((item) => item.id === welfareProjectId) + return project ? project.name : (welfareProjectId || "-") + }, + getAvailableWelfareProjectList() { + return this.$axios.post("/platform/trainSingUp/manage/activity/getAvailableWelfareProjectList", {}).then((resp) => { + return resp.code === 0 ? resp.data : [] + }) + }, + isAllCourseSignEnabled(courseList) { + return Array.isArray(courseList) && courseList.length > 0 && courseList.every((item) => item.isMobileSign === true) + }, makeCourseCode(row) { const params = { courseId: row.id @@ -331,6 +356,7 @@ layout("/layouts/platform.html"){ this.$businessTool.getDictOptions("TRAIN_SIGNUP_TYPE").then((data) => { this.trainTypeList = data }) + this.welfareProjectList = await this.getAvailableWelfareProjectList() this.$businessTool.getDictOptions("CAMPUS").then((data) => { this.campusList = data }) diff --git a/src/main/resources/views/platform/zhgh/activity/trainSingUp/statistics/index.html b/src/main/resources/views/platform/zhgh/activity/trainSingUp/statistics/index.html index 9d0f63b..1f1c25e 100644 --- a/src/main/resources/views/platform/zhgh/activity/trainSingUp/statistics/index.html +++ b/src/main/resources/views/platform/zhgh/activity/trainSingUp/statistics/index.html @@ -33,6 +33,7 @@ layout("/layouts/platform.html"){ 导出报名人员 + 导出填写信息 @@ -127,6 +128,9 @@ layout("/layouts/platform.html"){ exportSignUser() { this.$downLoad(loc() + "/exportSignUser", { activityId: this.pageForm.activityId }) }, + exportFillForm() { + this.$downLoad(loc() + "/exportFillForm", { activityId: this.pageForm.activityId }) + }, async yearChange() { this.pageForm.activityId = null await this.getActivityList() diff --git a/src/main/resources/views/platform/zhghh5/activity/trainSignUp/scanCode/index.html b/src/main/resources/views/platform/zhghh5/activity/trainSignUp/scanCode/index.html index 7d3c5c1..55711ea 100644 --- a/src/main/resources/views/platform/zhghh5/activity/trainSignUp/scanCode/index.html +++ b/src/main/resources/views/platform/zhghh5/activity/trainSignUp/scanCode/index.html @@ -5,17 +5,21 @@ layout("/layouts/platform_h5.html"){ body { background: white; } + .weui-msg { background: white; height: auto; min-height: auto; } + .weui-msg__title { font-weight: normal; } + .weui-msg__msg { font-weight: bolder; } + .success_tooltip { width: 90%; margin: 10px auto 0; @@ -26,19 +30,56 @@ layout("/layouts/platform_h5.html"){ font-size: 13px; color: #666666; } - .result-dialog .van-dialog__footer { - display: none !important; + + .joinPopup { + width: 100%; + height: 100%; + background-color: #f6f7f9; + } + + .join_content { + padding: 20px; + height: calc(100vh - 46px - 50px); + overflow-y: auto; + } + + .join_submit { + width: 100% !important; + } + + .van-card-header { + padding: 14px 20px; + border-bottom: 1px solid #ebeef5; + box-sizing: border-box; + color: #0e78c5; + font-weight: bold; + } + + .van-card-body { + padding: 0 10px 0 10px; + } + + .van-doc-card-two { + background-color: #fff; + border-radius: 10px; + box-shadow: 0 8px 12px #ebedf0; + line-height: 25px; + font-size: 12px; + } + + .footer .van-button { + border-radius: 6px; }
-