From 8915ace193a16dfeefed8b2d134dbd1cceed32bc Mon Sep 17 00:00:00 2001 From: zhouhefeng Date: Thu, 28 May 2026 19:52:20 +0800 Subject: [PATCH] commit --- .../java/com/budwk/app/base/dao/CndPlus.java | 44 + .../FitnessWalkAddActivityJob.java | 196 +++ .../fitnesswalk/FitnessWalkLotteryJob.java | 203 +++ .../fitnesswalk/contants/Cascader.java | 6 +- .../fitnesswalk/contants/FitnessWalkMode.java | 2 + ...ssWalkActivityBindingRecordController.java | 88 +- .../FitnessWalkActivityManageController.java | 121 +- ...FitnessWalkActivityRelationController.java | 96 ++ .../FitnessWalkCommonController.java | 43 +- .../FitnessWalkLoginController.java | 8 +- .../FitnessWalkPunchLotteryController.java | 58 +- .../FitnessWalkPunchStatisticsController.java | 158 +- .../FitnessWalkStepManageController.java | 48 +- .../FitnessWalkStepRaffleController.java | 86 +- .../FitnessWalkStepRankingController.java | 119 +- .../FitnessWalkStepStatisticsController.java | 60 +- .../FitnessWalkStepWiningController.java | 47 +- ...nessWalkUserPunchStatisticsController.java | 105 ++ .../model/FitnessWalkActivity.java | 107 +- .../FitnessWalkActivityRelationProject.java | 43 + .../model/FitnessWalkAwardUser.java | 2 + .../model/FitnessWalkPunchLottery.java | 2 + .../model/FitnessWalkRaffleRules.java | 2 + .../model/FitnessWalkRaffleUser.java | 2 + .../fitnesswalk/model/FitnessWalkStep.java | 2 + ...essWalkActivityRelationProjectService.java | 52 + .../service/FitnessWalkActivityService.java | 36 + .../service/FitnessWalkCommonService.java | 9 + .../FitnessWalkPunchStatisticsService.java | 103 +- ...WalkRelationActivityCompletionService.java | 22 + .../service/FitnessWalkStepRaffleService.java | 2 + ...alkActivityRelationProjectServiceImpl.java | 199 +++ .../impl/FitnessWalkActivityServiceImpl.java | 53 + .../impl/FitnessWalkCommonServiceImpl.java | 197 ++- ...FitnessWalkPunchStatisticsServiceImpl.java | 980 +++++++++++- ...RelationActivityCompletionServiceImpl.java | 167 ++ .../FitnessWalkStepRaffleServiceImpl.java | 50 +- .../fitnesswalk/utils/WeAppCloudUtil.java | 70 +- .../fitnesswalk/vo/FitnessWalkDateStepVO.java | 22 + .../activity/fitnesswalk/activityManage.html | 1365 +++++++++++------ .../fitnesswalk/activityRelation.html | 223 +++ .../activity/fitnesswalk/bindingRecord.html | 229 ++- .../activity/fitnesswalk/punchStatistics.html | 339 ++-- .../zhgh/activity/fitnesswalk/stepRaffle.html | 292 ++-- .../activity/fitnesswalk/stepRanking.html | 205 +-- .../activity/fitnesswalk/stepStatistics.html | 220 +-- .../zhgh/activity/fitnesswalk/stepWining.html | 196 ++- .../fitnesswalk/userPunchStatistics.html | 298 ++++ 48 files changed, 5333 insertions(+), 1644 deletions(-) create mode 100644 src/main/java/com/budwk/app/base/dao/CndPlus.java create mode 100644 src/main/java/com/budwk/app/task/job/fitnesswalk/FitnessWalkAddActivityJob.java create mode 100644 src/main/java/com/budwk/app/task/job/fitnesswalk/FitnessWalkLotteryJob.java create mode 100644 src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkActivityRelationController.java create mode 100644 src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkUserPunchStatisticsController.java create mode 100644 src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/model/FitnessWalkActivityRelationProject.java create mode 100644 src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/FitnessWalkActivityRelationProjectService.java create mode 100644 src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/FitnessWalkActivityService.java create mode 100644 src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/FitnessWalkRelationActivityCompletionService.java create mode 100644 src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/impl/FitnessWalkActivityRelationProjectServiceImpl.java create mode 100644 src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/impl/FitnessWalkActivityServiceImpl.java create mode 100644 src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/impl/FitnessWalkRelationActivityCompletionServiceImpl.java create mode 100644 src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/vo/FitnessWalkDateStepVO.java create mode 100644 src/main/resources/views/platform/zhgh/activity/fitnesswalk/activityRelation.html create mode 100644 src/main/resources/views/platform/zhgh/activity/fitnesswalk/userPunchStatistics.html diff --git a/src/main/java/com/budwk/app/base/dao/CndPlus.java b/src/main/java/com/budwk/app/base/dao/CndPlus.java new file mode 100644 index 00000000..8417fea1 --- /dev/null +++ b/src/main/java/com/budwk/app/base/dao/CndPlus.java @@ -0,0 +1,44 @@ +package com.budwk.app.base.dao; + +import cn.hutool.core.util.StrUtil; +import com.budwk.app.base.param.PageForm; +import org.nutz.dao.Cnd; +import org.nutz.lang.Lang; +import org.nutz.lang.util.NutMap; + +/** + * 条件构造辅助类。 + * 当前健步走移植代码需要按页面查询条件动态追加字段查询和排序,空字段不应参与 SQL 条件。 + */ +public class CndPlus extends Cnd { + private static final NutMap ORDER_MAP = NutMap.NEW().addv("ascending", "asc").addv("descending", "desc"); + + public static CndPlus create() { + return new CndPlus(); + } + + public CndPlus and(PageForm pageForm) { + if (pageForm != null && StrUtil.isAllNotBlank(pageForm.getSearchName(), pageForm.getSearchKeyword())) { + this.where().andLike(pageForm.getSearchName(), pageForm.getSearchKeyword()); + } + if (pageForm != null && StrUtil.isAllNotBlank(pageForm.getPageOrderName(), pageForm.getPageOrderBy())) { + this.orderBy(pageForm.getPageOrderName(), ORDER_MAP.getString(pageForm.getPageOrderBy())); + } + return this; + } + + /** + * 仅在字段名和值都有效时追加条件,避免可选筛选项为空时生成无效 SQL。 + * + * @param name 字段名或表达式 + * @param op 比较操作符 + * @param value 比较值 + * @return 当前条件对象 + */ + public CndPlus andEX(String name, String op, Object value) { + if (StrUtil.isNotBlank(name) && !Lang.isEmpty(value)) { + this.and(name, op, value); + } + return this; + } +} diff --git a/src/main/java/com/budwk/app/task/job/fitnesswalk/FitnessWalkAddActivityJob.java b/src/main/java/com/budwk/app/task/job/fitnesswalk/FitnessWalkAddActivityJob.java new file mode 100644 index 00000000..7bc4b89a --- /dev/null +++ b/src/main/java/com/budwk/app/task/job/fitnesswalk/FitnessWalkAddActivityJob.java @@ -0,0 +1,196 @@ +package com.budwk.app.task.job.fitnesswalk; + +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.budwk.app.sys.models.Sys_task; +import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkActivity; +import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkActivityRelationProject; +import com.budwk.app.zhgh.activity.fitnesswalk.service.FitnessWalkCommonService; +import com.budwk.app.zhgh.activity.fitnesswalk.service.impl.FitnessWalkCommonServiceImpl; +import com.budwk.app.zhgh.activity.fitnesswalk.utils.WeAppCloudUtil; +import lombok.extern.slf4j.Slf4j; +import org.nutz.aop.interceptor.ioc.TransAop; +import org.nutz.dao.Chain; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.ioc.Ioc; +import org.nutz.ioc.aop.Aop; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.json.Json; +import org.nutz.mvc.Mvcs; +import org.quartz.Job; +import org.quartz.JobDataMap; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; + +import java.util.Arrays; + +/** + * @author zhf + * @date 2026/4/11 14:01 + * @description 自动创建活动 + */ +@IocBean +@Slf4j +public class FitnessWalkAddActivityJob implements Job { + @Override + @Aop(TransAop.READ_COMMITTED) + public void execute(JobExecutionContext context) throws JobExecutionException { + Ioc ioc = Mvcs.ctx().getDefaultIoc(); + Dao dao = ioc.get(Dao.class); + WeAppCloudUtil weAppCloudUtil = ioc.get(WeAppCloudUtil.class); + // Quartz 任务上下文里按接口取 Bean 容易命中 Nutz 的接口实例化异常,这里统一按实现类获取后再走 service 公共方法。 + FitnessWalkCommonService fitnessWalkCommonService = ioc.get(FitnessWalkCommonServiceImpl.class); + + JobDataMap jobDataMap = context.getJobDetail().getJobDataMap(); + String sourceActivityId = getJobString(jobDataMap, "sourceActivityId"); + String deleteId = getJobString(jobDataMap, "deleteId"); + long targetDate = getJobLong(jobDataMap, "targetDate"); + + try { + FitnessWalkActivity sourceActivity = loadSourceActivity(weAppCloudUtil, sourceActivityId); + if (sourceActivity == null) { + disableCurrentTask(dao, deleteId, sourceActivityId); + return; + } + + FitnessWalkActivity newActivity = JSON.parseObject(JSON.toJSONString(sourceActivity), FitnessWalkActivity.class); + shiftActivityDate(sourceActivity, newActivity, targetDate); + resetBranchActivityNameByRelationProject(dao, newActivity, targetDate); + long currentTime = System.currentTimeMillis(); + long normalizedTargetDate = DateUtil.beginOfDay(DateUtil.date(targetDate)).getTime(); + newActivity.set_id(null); + newActivity.setCreatedAt(currentTime); + newActivity.setUpdatedAt(currentTime); + // 自动复制出的活动补充来源标记,便于后续编辑原活动时识别哪些日期已经真正生成过活动。 + newActivity.setAutoCreateSourceActivityId(sourceActivityId); + newActivity.setAutoCreateTargetDate(normalizedTargetDate); + + JSONObject insertResult = weAppCloudUtil.request(WeAppCloudUtil.CRUD.INSERT, + "db.collection('activity').add({data:" + Json.toJson(newActivity) + "})"); + if (insertResult.containsKey("id_list") && insertResult.getJSONArray("id_list") != null && !insertResult.getJSONArray("id_list").isEmpty()) { + newActivity.set_id(insertResult.getJSONArray("id_list").getString(0)); + } + + // 自动复制出的活动也要同步刷新缓存,并补齐该活动自身的抽奖任务。 + fitnessWalkCommonService.addLotteryTask(newActivity); + fitnessWalkCommonService.addOrEditDoSaveRedis(newActivity); + } catch (Exception e) { + log.error("自动连续创建活动失败, sourceActivityId={}", sourceActivityId, e); + throw new JobExecutionException(e); + } finally { + disableCurrentTask(dao, deleteId, sourceActivityId); + } + } + + /** + * Quartz 的 JobDataMap 可能直接存的是 Long、Integer 等对象,不能一律用 getString 读取。 + * 这里统一兜底转成字符串,避免任务参数类型变化时再次触发 ClassCastException。 + */ + private String getJobString(JobDataMap jobDataMap, String key) { + Object value = jobDataMap.get(key); + return value == null ? null : String.valueOf(value); + } + + /** + * 定时任务中的日期时间参数既可能是数字,也可能是字符串。 + * 这里统一兼容处理,确保 targetDate 在测试和正式任务下都能稳定转成时间戳。 + */ + private long getJobLong(JobDataMap jobDataMap, String key) { + Object value = jobDataMap.get(key); + if (value == null) { + throw new IllegalArgumentException("定时任务缺少参数:" + key); + } + if (value instanceof Number) { + return ((Number) value).longValue(); + } + return Long.parseLong(String.valueOf(value)); + } + + /** + * 根据原活动ID查询原活动。 + * 定时任务执行时统一从云库读取最新活动内容,确保复制时拿到的是当前活动的最新配置。 + */ + private FitnessWalkActivity loadSourceActivity(WeAppCloudUtil weAppCloudUtil, String sourceActivityId) { + JSONObject jsonObject = weAppCloudUtil.request(WeAppCloudUtil.CRUD.QUERY, + "db.collection('activity').doc('" + sourceActivityId + "').get()"); + return jsonObject.getJSONArray("data").stream() + .map(item -> JSON.parseObject((String) item, FitnessWalkActivity.class)) + .findFirst() + .orElse(null); + } + + /** + * 将活动的报名时间和活动时间整体平移到目标日期。 + * 这里保留原活动的时分秒和时长,仅根据目标日期替换所属自然日。 + */ + private void shiftActivityDate(FitnessWalkActivity sourceActivity, FitnessWalkActivity newActivity, long targetDate) { + DateTime sourceStartDate = DateUtil.beginOfDay(DateUtil.date(sourceActivity.getStartTime())); + DateTime targetStartDate = DateUtil.beginOfDay(DateUtil.date(targetDate)); + long offsetMillis = targetStartDate.getTime() - sourceStartDate.getTime(); + + if (sourceActivity.getApplyTime() != null && sourceActivity.getApplyTime().length == 2) { + Long[] applyTime = Arrays.copyOf(sourceActivity.getApplyTime(), 2); + applyTime[0] = applyTime[0] + offsetMillis; + applyTime[1] = applyTime[1] + offsetMillis; + newActivity.setApplyTime(applyTime); + newActivity.setApplyStartTime(applyTime[0]); + newActivity.setApplyEndTime(applyTime[1]); + } + + if (sourceActivity.getTime() != null && sourceActivity.getTime().length == 2) { + Long[] time = Arrays.copyOf(sourceActivity.getTime(), 2); + time[0] = time[0] + offsetMillis; + time[1] = time[1] + offsetMillis; + newActivity.setTime(time); + newActivity.setStartTime(time[0]); + newActivity.setEndTime(time[1]); + } + } + + /** + * 自动连续创建活动时,分活动名称统一取“关联活动配置名称 + 日期前缀”。 + * 优先读取关联活动配置表中的主活动名称;若当前活动没有配置关联活动,则回退到原分活动名称。 + */ + private void resetBranchActivityNameByRelationProject(Dao dao, FitnessWalkActivity newActivity, long targetDate) { + if (newActivity == null) { + return; + } + String datePrefix = DateUtil.format(DateUtil.date(targetDate), "yyyy年MM月dd日"); + String targetName = null; + if (StrUtil.isNotBlank(newActivity.getRelationActivityId())) { + FitnessWalkActivityRelationProject relationProject = dao.fetch( + FitnessWalkActivityRelationProject.class, + Cnd.where("id", "=", newActivity.getRelationActivityId()) + ); + if (relationProject != null && StrUtil.isNotBlank(relationProject.getMasterActivityName())) { + targetName = relationProject.getMasterActivityName().trim(); + } + } + + if (StrUtil.isBlank(targetName) && StrUtil.isNotBlank(newActivity.getBranchActivityName())) { + targetName = newActivity.getBranchActivityName().trim(); + } + if (StrUtil.isBlank(targetName)) { + return; + } + if (targetName.startsWith(datePrefix)) { + newActivity.setBranchActivityName(targetName); + return; + } + newActivity.setBranchActivityName(datePrefix + " " + targetName); + } + + /** + * 当前任务执行完成后禁用自身,避免同一日期重复创建活动。 + */ + private void disableCurrentTask(Dao dao, String deleteId, String sourceActivityId) { + dao.update(Sys_task.class, Chain.make("disabled", 1), + Cnd.where("data", "like", "%" + deleteId + "%").and("data", "like", "%" + sourceActivityId + "%")); + } +} + + diff --git a/src/main/java/com/budwk/app/task/job/fitnesswalk/FitnessWalkLotteryJob.java b/src/main/java/com/budwk/app/task/job/fitnesswalk/FitnessWalkLotteryJob.java new file mode 100644 index 00000000..5cb22bbd --- /dev/null +++ b/src/main/java/com/budwk/app/task/job/fitnesswalk/FitnessWalkLotteryJob.java @@ -0,0 +1,203 @@ +package com.budwk.app.task.job.fitnesswalk; + +import cn.hutool.core.date.DateUtil; +import com.budwk.app.sys.models.Sys_task; +import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkAwardUser; +import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkRaffleUser; +import lombok.extern.slf4j.Slf4j; +import org.nutz.aop.interceptor.ioc.TransAop; +import org.nutz.dao.Chain; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.dao.Sqls; +import org.nutz.dao.sql.Sql; +import org.nutz.dao.util.Daos; +import org.nutz.dao.util.cri.Static; +import org.nutz.ioc.Ioc; +import org.nutz.ioc.aop.Aop; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.lang.Strings; +import org.nutz.lang.random.R; +import org.nutz.lang.util.NutMap; +import org.nutz.mvc.Mvcs; +import org.quartz.Job; +import org.quartz.JobDataMap; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; + +import java.util.*; +import java.util.stream.Collectors; + +@IocBean +@Slf4j +public class FitnessWalkLotteryJob implements Job { + + @Override + @Aop(TransAop.READ_COMMITTED) + public void execute(JobExecutionContext context) throws JobExecutionException { + Ioc ioc = Mvcs.ctx().getDefaultIoc(); + Dao dao = ioc.get(Dao.class); + + JobDataMap jobDataMap = context.getJobDetail().getJobDataMap(); + String deleteId = jobDataMap.getString("deleteId"); + + int lotteryQualificationStep = Integer.parseInt(jobDataMap.getString("lotteryQualificationStep")); + int lotteryUserNum = Integer.parseInt(jobDataMap.getString("lotteryUserNum")); + String mode = jobDataMap.getString("mode"); + String activityId = jobDataMap.getString("id"); + String lotteryQualificationDay = jobDataMap.getString("lotteryQualificationDay"); + + String awardName; + Date startDate = null; + Date endDate = null; + if ("custom".equals(mode)) { + awardName = jobDataMap.getString("awardName"); + String startTimeStr = jobDataMap.getString("startDate"); + String endTimeStr = jobDataMap.getString("endDate"); + startDate = DateUtil.parse(startTimeStr, "yyyy-MM-dd"); + endDate = DateUtil.parse(endTimeStr, "yyyy-MM-dd"); + } else { + awardName = null; + } + List userList = new ArrayList<>(); + + if ("custom".equals(mode)) { + //查询出符合条件的用户 + Sql sql = Sqls.create(""" + SELECT + u.id, + COALESCE(SUM(sub.step), 0) AS total_steps, + u.loginname, + u.username, + u.unionid, + u.unionname, + u.unitid, + u.unitname, + over3k.standardsDays + FROM + `user` u + LEFT JOIN ( + SELECT + userId, + applyDate, + step + FROM + `fitness_walk_step` + WHERE + activityId = @activityId + AND DATE(applyDate) >= @startDate + AND DATE(applyDate) <= @endDate + $lotteryQualificationDaySql + GROUP BY + userId, + applyDate + ) AS sub ON u.id = sub.userId + LEFT JOIN ( + SELECT + userId, + COUNT(DISTINCT DATE(applyDate)) AS standardsDays + FROM + `fitness_walk_step` + WHERE + activityId = @activityId + AND DATE(applyDate) >= @startDate + AND DATE(applyDate) <= @endDate + $lotteryQualificationDaySql + GROUP BY + userId + ) AS over3k ON u.id = over3k.userId + $whereSql + GROUP BY + u.id, + u.username, + u.loginname, + u.unionname, + u.unitname + HAVING + $stepHavingSql + """); + if (Strings.isNotBlank(lotteryQualificationDay)) { + sql.setVar("lotteryQualificationDaySql", new Static(" AND step >= '%s' ".formatted(lotteryQualificationStep))); + sql.setVar("stepHavingSql", new Static(" over3k.standardsDays >= '%s' ".formatted(lotteryQualificationDay))); + } else { + sql.setVar("stepHavingSql", new Static(" COALESCE(SUM(sub.step), 0) >= '%s' ".formatted(lotteryQualificationStep))); + } + sql.setVar("whereSql", new Static(" where u.loginname not in (select loginName from fitness_walk_award_user where activityId = " + activityId + " ) ")); + sql.setParam("activityId", activityId); + sql.setParam("step", lotteryQualificationStep); + sql.setParam("startDate", startDate); + sql.setParam("endDate", endDate); + userList = (List) Daos.query(dao, sql.toString(), Sqls.callback.maps()); + } else if ("everyday".equals(mode)) { + Sql sql = Sqls.create(""" + SELECT + jws.userId AS id, + u.username, + u.loginname, + u.unionid, + u.unionname, + u.unitid, + u.unitname + FROM + fitness_walk_step jws + LEFT JOIN `user` u ON u.id = jws.userId + WHERE + jws.activityId = @activityId + AND jws.step >= @step + AND jws.applyDate = @date + """); + sql.setParam("activityId", activityId); + sql.setParam("step", lotteryQualificationStep); + sql.setParam("date", DateUtil.today()); + userList = (List) Daos.query(dao, sql.toString(), Sqls.callback.maps()); + } + + // 记录达标人员,表明这些人有资格抽奖 + List raffleUserList = userList.stream().map(v -> { + FitnessWalkRaffleUser raffleUser = new FitnessWalkRaffleUser(); + raffleUser.setId(R.UU32()); + raffleUser.setActivityId(activityId); + raffleUser.setUserId(v.getString("id")); + raffleUser.setLoginName(v.getString("loginname")); + raffleUser.setUsername(v.getString("username")); + if ("custom".equals(mode)) { + raffleUser.setStandardsDays(v.getInt("standardsDays")); + raffleUser.setAwardSteps(v.getString("total_steps")); + } + raffleUser.setIsRaffle(false); + return raffleUser; + }).collect(Collectors.toList()); + + dao.fastInsert(raffleUserList); + + // 随机抽奖,中奖人数为传递过来的 lotteryUserNum + Collections.shuffle(userList); + List lotteryUserList = userList.stream().limit(lotteryUserNum).collect(Collectors.toList()); + + Date finalStartDate = startDate; + Date finalEndDate = endDate; + List awardLists = lotteryUserList.stream().map(v -> { + FitnessWalkAwardUser awardUser = new FitnessWalkAwardUser(); + awardUser.setActivityId(activityId); + awardUser.setUserId(v.getString("id")); + awardUser.setUsername(v.getString("username")); + awardUser.setLoginName(v.getString("loginname")); + awardUser.setAwardName(awardName); + awardUser.setUnionId(v.getString("unionid")); + awardUser.setUnionName(v.getString("unionname")); + awardUser.setUnitId(v.getString("unitid")); + awardUser.setUnitName(v.getString("unitname")); + awardUser.setApplyDate(new Date()); + awardUser.setStartDate(finalStartDate); + awardUser.setEndDate(finalEndDate); + awardUser.setAwardSteps(v.getString("total_steps")); + return awardUser; + }).collect(Collectors.toList()); + dao.insert(awardLists); + + // 执行完此定时任务后,禁用此定时任务确保不会下次执行 + dao.update(Sys_task.class, Chain.make("disabled", 1), Cnd.where("data", "like", "%" + deleteId + "%").and("data", "like", "%" + activityId + "%")); + } +} + + diff --git a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/contants/Cascader.java b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/contants/Cascader.java index 2e22f541..49790899 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/contants/Cascader.java +++ b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/contants/Cascader.java @@ -4,13 +4,11 @@ import lombok.Data; import java.util.List; -/** - * 获取活动的树形结构 - */ @Data public class Cascader { - private String value; private String label; private List children; } + + diff --git a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/contants/FitnessWalkMode.java b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/contants/FitnessWalkMode.java index ad699815..521efa68 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/contants/FitnessWalkMode.java +++ b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/contants/FitnessWalkMode.java @@ -16,3 +16,5 @@ public enum FitnessWalkMode { private final String desc; } + + diff --git a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkActivityBindingRecordController.java b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkActivityBindingRecordController.java index 8bb51c65..ebf1ffec 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkActivityBindingRecordController.java +++ b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkActivityBindingRecordController.java @@ -1,19 +1,18 @@ package com.budwk.app.zhgh.activity.fitnesswalk.controller; +import org.nutz.aop.interceptor.ioc.TransAop; -import cn.dev33.satoken.annotation.SaCheckPermission; import cn.hutool.core.util.StrUtil; -import cn.hutool.json.JSONObject; -import cn.hutool.json.JSONUtil; -import com.budwk.app.base.annotation.SLog; -import com.budwk.app.base.constant.RoleConstant; -import com.budwk.app.base.page.Pagination; import com.budwk.app.base.result.Result; -import com.budwk.app.web.commons.auth.utils.AuthUtil; +import com.budwk.app.base.page.Pagination; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; import com.budwk.app.web.commons.auth.utils.SecurityUtil; +import com.budwk.app.web.commons.auth.utils.AuthUtil; import com.budwk.app.zhgh.activity.fitnesswalk.utils.WeAppCloudUtil; -import io.swagger.annotations.ApiOperation; +import cn.dev33.satoken.annotation.SaCheckPermission; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.ioc.aop.Aop; import org.nutz.json.Json; import org.nutz.lang.Strings; import org.nutz.lang.util.NutMap; @@ -23,11 +22,11 @@ import org.nutz.mvc.annotation.Param; import java.io.IOException; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; @IocBean @Ok("json") -@ApiOperation("健步走-同步绑定记录") @At("/platform/fitnessWalk/bindingRecord") public class FitnessWalkActivityBindingRecordController { @@ -40,29 +39,15 @@ public class FitnessWalkActivityBindingRecordController { public void index() { } - /** - * 获取用户绑定记录 - * @param keyWord - * @param unionId - * @param searchName - * @param searchKeyword - * @param pageNumber - * @param pageSize - * @param pageOrderName - * @param pageOrderBy - * @return - * @throws IOException - */ @At - @ApiOperation("获取用户绑定记录") @SaCheckPermission("fitnessWalk.bindingRecord") public Result pageData(@Param("keyWord") String keyWord, @Param("unionId") String unionId, @Param("searchName") String searchName, @Param("searchKeyword") String searchKeyword, - @Param("pageNumber") int pageNumber, @Param("pageSize") int pageSize, + @Param("pageNumber") int pageNumber,@Param("pageSize") int pageSize, @Param("pageOrderName") String pageOrderName, - @Param("pageOrderBy") String pageOrderBy) { + @Param("pageOrderBy") String pageOrderBy) throws IOException { int skip = (pageNumber == 1 ? 0 : (pageNumber - 1)) * pageSize; StringBuilder sql = new StringBuilder("db.collection('login_record')"); @@ -74,7 +59,7 @@ public class FitnessWalkActivityBindingRecordController { sql.append("])"); sql.append(")"); } - if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) { + if (!AuthUtil.hasRoleOr("SYSADMIN", "SCHOOL_UNION_ADMIN")) { sql.append(".where({'data.unionid':'").append(SecurityUtil.getUnionId()).append("'})"); } else { if (Strings.isNotBlank(unionId)) { @@ -88,46 +73,47 @@ public class FitnessWalkActivityBindingRecordController { try { JSONObject jsonObject = weAppCloudUtil.request(WeAppCloudUtil.CRUD.QUERY, sql.toString()); - List data = jsonObject.getJSONArray("data").stream() - .map(v -> JSONUtil.toBean((String) v, NutMap.class)) - .collect(Collectors.toList()); + List data = jsonObject.getJSONArray("data").stream().map(v -> JSON.parseObject((String) v, NutMap.class)).collect(Collectors.toList()); JSONObject pager = jsonObject.getJSONObject("pager"); - Pagination pagination = new Pagination(pageNumber, pageSize, pager.getInt("Total"), data); - return Result.success().addData(pagination); - } catch (Exception e) { + return Result.success(new Pagination(pageNumber, pageSize, pager.getIntValue("Total"), data)); + } catch (Exception e){ return Result.error(e.getMessage()); } } - /** - * 删除绑定记录 - * @param ids - * @return - */ @At - @ApiOperation("删除绑定记录") - @SLog(tag = "健步走-同步绑定记录", msg = "删除绑定记录") @SaCheckPermission("fitnessWalk.bindingRecord") + @Aop(TransAop.READ_COMMITTED) public Result delete(String[] ids) { - String sql = "db.collection('login_record').where({_id:_.in(" + Json.toJson(ids) + ")}).remove()"; - weAppCloudUtil.request(WeAppCloudUtil.CRUD.DELETE, sql); - return Result.success(); + try { + String sql = "db.collection('login_record').where({_id:_.in(" + Json.toJson(ids) + ")}).remove()"; + weAppCloudUtil.request(WeAppCloudUtil.CRUD.DELETE,sql); + return Result.success(); + } catch (Exception e) { + return Result.error(); + } } - /** - * 删除所有绑定记录 - * @return - */ @At - @ApiOperation("删除所有绑定记录") - @SLog(tag = "健步走-同步绑定记录", msg = "删除所有绑定记录") @SaCheckPermission("fitnessWalk.bindingRecord") + @Aop(TransAop.READ_COMMITTED) public Result deleteAll() { - String sql = "db.collection('login_record').where({_id: _.neq('0')}).remove()"; - weAppCloudUtil.request(WeAppCloudUtil.CRUD.DELETE, sql); - return Result.success(); + try { + String sql = "db.collection('login_record').where({_id: _.neq('0')}).remove()"; + weAppCloudUtil.request(WeAppCloudUtil.CRUD.DELETE,sql); + return Result.success(); + } catch (Exception e) { + return Result.error(); + } } } + + + + + + + diff --git a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkActivityManageController.java b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkActivityManageController.java index 81a3cd25..251bad6a 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkActivityManageController.java +++ b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkActivityManageController.java @@ -1,60 +1,75 @@ package com.budwk.app.zhgh.activity.fitnesswalk.controller; +import org.nutz.aop.interceptor.ioc.TransAop; -import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.date.DateTime; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.StrUtil; -import cn.hutool.json.JSONObject; -import cn.hutool.json.JSONUtil; -import com.budwk.app.base.annotation.SLog; -import com.budwk.app.base.constant.RoleConstant; -import com.budwk.app.base.page.Pagination; import com.budwk.app.base.result.Result; +import com.budwk.app.base.page.Pagination; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.sys.models.Sys_task; import com.budwk.app.sys.services.SysTaskService; import com.budwk.app.sys.services.SysUserService; import com.budwk.app.task.services.TaskPlatformService; import com.budwk.app.web.commons.auth.utils.AuthUtil; -import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkActivity; +import com.budwk.app.zhgh.activity.fitnesswalk.service.FitnessWalkActivityService; import com.budwk.app.zhgh.activity.fitnesswalk.service.FitnessWalkCommonService; import com.budwk.app.zhgh.activity.fitnesswalk.utils.WeAppCloudUtil; -import io.swagger.annotations.ApiOperation; +import cn.dev33.satoken.annotation.SaCheckPermission; import org.nutz.dao.Cnd; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.ioc.aop.Aop; import org.nutz.json.Json; import org.nutz.lang.Lang; import org.nutz.lang.Strings; +import org.nutz.lang.random.R; +import org.nutz.lang.tmpl.Tmpl; import org.nutz.lang.util.NutMap; +import org.nutz.log.Log; +import org.nutz.log.Logs; import org.nutz.mvc.annotation.AdaptBy; 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 org.nutz.plugins.wkcache.annotation.CacheRemove; +import org.nutz.plugins.wkcache.annotation.CacheRemoveAll; +import org.nutz.trans.Trans; +import java.io.IOException; +import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*; import java.util.stream.Collectors; -@IocBean -@Ok("json") -@ApiOperation("健步走-活动管理") @At("/platform/fitnessWalk/activityManage") +@Ok("json") +@IocBean public class FitnessWalkActivityManageController { + private static final Log log = Logs.get(); + @Inject private WeAppCloudUtil weAppCloudUtil; @Inject + private TaskPlatformService taskPlatformService; + @Inject private SysTaskService sysTaskService; @Inject private SysUserService sysUserService; - @Inject - private TaskPlatformService taskPlatformService; + @Inject private FitnessWalkCommonService fitnessWalkCommonService; + @Inject + private FitnessWalkActivityService fitnessWalkActivityService; + @At("") @Ok("beetl:/platform/zhgh/activity/fitnesswalk/activityManage.html") @SaCheckPermission("fitnessWalk.activityManage") @@ -62,12 +77,8 @@ public class FitnessWalkActivityManageController { } @At - @ApiOperation("获取活动数据") @SaCheckPermission("fitnessWalk.activityManage") - public Result pageData(@Param("year") Integer year, @Param("unionId") String unionId, @Param("searchName") String searchName, - @Param("searchKeyword") String searchKeyword, @Param("pageNumber") int pageNumber, - @Param("pageSize") int pageSize, @Param("pageOrderName") String pageOrderName, - @Param("pageOrderBy") String pageOrderBy) { + public Result pageData(@Param("year") Integer year, @Param("unionId") String unionId, @Param("searchName") String searchName, @Param("searchKeyword") String searchKeyword, @Param("pageNumber") int pageNumber, @Param("pageSize") int pageSize, @Param("pageOrderName") String pageOrderName, @Param("pageOrderBy") String pageOrderBy) { int skip = (pageNumber == 1 ? 0 : (pageNumber - 1)) * pageSize; @@ -80,7 +91,7 @@ public class FitnessWalkActivityManageController { sql.append("])"); sql.append(")"); } - if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) { + if (!AuthUtil.hasRoleOr("SYSADMIN", "SCHOOL_UNION_ADMIN")) { sql.append(".where({unionId:'").append(SecurityUtil.getUnionId()).append("'})"); } else if (StrUtil.isNotBlank(unionId)) { sql.append(".where({unionId:'").append(unionId).append("'})"); @@ -90,29 +101,49 @@ public class FitnessWalkActivityManageController { sql.append(".get()"); try { JSONObject jsonObject = weAppCloudUtil.request(WeAppCloudUtil.CRUD.QUERY, sql.toString()); - List data = jsonObject.getJSONArray("data").stream() - .map(v -> JSONUtil.toBean((String) v, FitnessWalkActivity.class)) - .collect(Collectors.toList()); + List data = jsonObject.getJSONArray("data").stream().map(v -> JSON.parseObject((String) v, FitnessWalkActivity.class)).collect(Collectors.toList()); JSONObject pager = jsonObject.getJSONObject("pager"); - Pagination pagination = new Pagination(pageNumber, pageSize, pager.getInt("Total"), data); - return Result.success().addData(pagination); + return Result.success(new Pagination(pageNumber, pageSize, pager.getIntValue("Total"), data)); } catch (Exception e) { return Result.error(e.getMessage()); } } + /** + * 获取当前年份关联活动下拉数据。 + * 下拉数据统一由 service 生成,避免页面自己拼名称或数量导致规则不一致。 + */ + @At + @SaCheckPermission("fitnessWalk.activityManage") + public Result relationActivityOptions() { + return Result.success(fitnessWalkActivityService.getCurrentYearRelationActivityOptions()); + } + + /** + * 获取福利项目下拉数据。 + * 活动页面统一通过当前年份加载福利项目,避免前端直接跨模块取数。 + */ + @At + @SaCheckPermission("fitnessWalk.activityManage") + public Result welfareProjectOptions(@Param("year") Integer year) { + return Result.success(fitnessWalkActivityService.getWelfareProjectOptions(year)); + } + @At - @ApiOperation("添加活动") - @SLog(tag = "健步走-活动管理", msg = "添加活动") - @SaCheckPermission("fitnessWalk.activityManage") @AdaptBy(type = UploadAdaptor.class, args = {"ioc:fileUpload"}) - public Result doAdd(@Param("fitnessWalkActivity") FitnessWalkActivity fitnessWalkActivity, + @SaCheckPermission("fitnessWalk.activityManage") + @Aop(TransAop.READ_COMMITTED) + public Result doAdd(String data, @Param("fitnessWalkActivity") FitnessWalkActivity fitnessWalkActivity, @Param("file") TempFile tempFile, @Param("stepFiles") TempFile[] stepFiles, @Param("certificateFile") TempFile certificateTempFile) { try { + long currentTime = System.currentTimeMillis(); + fitnessWalkActivity.setCreatedAt(currentTime); + fitnessWalkActivity.setUpdatedAt(currentTime); + //设置时间 if ("punch".equals(fitnessWalkActivity.getActivityModel())) { fitnessWalkActivity.setApplyStartTime(fitnessWalkActivity.getApplyTime()[0]); @@ -140,8 +171,7 @@ public class FitnessWalkActivityManageController { fitnessWalkActivity.setCompletionCertificateCover(fileId); } - if (AuthUtil.hasRoleOr(RoleConstant.BRANCH_UNION_CHAIRMAN.name(), RoleConstant.BRANCH_UNION_OPERATOR.name(), RoleConstant.BRANCH_UNION_ADMIN.name()) - && !AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) { + if (AuthUtil.hasRoleOr("BRANCH_UNION_CHAIRMAN", "BRANCH_UNION_OPERATOR", "BRANCH_UNION_ADMIN") && !AuthUtil.hasRoleOr("SYSADMIN", "SCHOOL_UNION_ADMIN")) { fitnessWalkActivity.setUnionId(SecurityUtil.getUnionId()); } else { fitnessWalkActivity.setUnionId(""); @@ -153,7 +183,12 @@ public class FitnessWalkActivityManageController { try { JSONObject jsonObject = weAppCloudUtil.request(WeAppCloudUtil.CRUD.INSERT, sql.toString()); + // 新增成功后回填云库生成的活动ID,后续创建定时任务和刷新缓存都依赖这个ID。 + if (jsonObject.containsKey("id_list") && jsonObject.getJSONArray("id_list") != null && !jsonObject.getJSONArray("id_list").isEmpty()) { + fitnessWalkActivity.set_id(jsonObject.getJSONArray("id_list").getString(0)); + } fitnessWalkCommonService.addLotteryTask(fitnessWalkActivity); + fitnessWalkCommonService.addCreateActivityTask(fitnessWalkActivity); fitnessWalkCommonService.addOrEditDoSaveRedis(fitnessWalkActivity); return Result.success(); } catch (Exception e) { @@ -168,7 +203,6 @@ public class FitnessWalkActivityManageController { @At @Ok("json:full") - @ApiOperation("根据Id获取健步走活动") @SaCheckPermission("fitnessWalk.activityManage") public Result findOne(String id) { return Result.success(fitnessWalkCommonService.getActivity(id)); @@ -176,7 +210,6 @@ public class FitnessWalkActivityManageController { @At @Ok("json") - @ApiOperation("获取附件") @SaCheckPermission("fitnessWalk.activityManage") public Result getFile(String pic) { try { @@ -188,7 +221,7 @@ public class FitnessWalkActivityManageController { imgmap.put("id", imgurl); imgmap.put("url", imgurl); res.setv("img", imgmap); - return Result.success().addData(res); + return Result.success(res); } catch (Exception e) { return Result.error(); } @@ -196,13 +229,15 @@ public class FitnessWalkActivityManageController { @At - @ApiOperation("修改健步走活动") - @SaCheckPermission("fitnessWalk.activityManage") @AdaptBy(type = UploadAdaptor.class, args = {"ioc:fileUpload"}) + @SaCheckPermission("fitnessWalk.activityManage") + @Aop(TransAop.READ_COMMITTED) public Result doEdit(@Param("fitnessWalkActivity") FitnessWalkActivity fitnessWalkActivity, @Param("file") TempFile tempFile, @Param("stepFiles") TempFile[] stepFiles, @Param("certificateFile") TempFile certificateTempFile) { + fitnessWalkActivity.setUpdatedAt(System.currentTimeMillis()); + //设置时间 if ("punch".equals(fitnessWalkActivity.getActivityModel())) { fitnessWalkActivity.setApplyStartTime(fitnessWalkActivity.getApplyTime()[0]); @@ -231,8 +266,7 @@ public class FitnessWalkActivityManageController { } - if (AuthUtil.hasRoleOr(RoleConstant.BRANCH_UNION_CHAIRMAN.name(), RoleConstant.BRANCH_UNION_OPERATOR.name(), RoleConstant.BRANCH_UNION_ADMIN.name()) - && !AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) { + if (AuthUtil.hasRoleOr("BRANCH_UNION_CHAIRMAN", "BRANCH_UNION_OPERATOR", "BRANCH_UNION_ADMIN") && !AuthUtil.hasRoleOr("SYSADMIN", "SCHOOL_UNION_ADMIN")) { fitnessWalkActivity.setUnionId(SecurityUtil.getUnionId()); } else { fitnessWalkActivity.setUnionId(""); @@ -248,6 +282,7 @@ public class FitnessWalkActivityManageController { try { JSONObject jsonObject = weAppCloudUtil.request(WeAppCloudUtil.CRUD.UPDATE, sql.toString()); fitnessWalkCommonService.addLotteryTask(fitnessWalkActivity); + fitnessWalkCommonService.addCreateActivityTask(fitnessWalkActivity); fitnessWalkCommonService.addOrEditDoSaveRedis(fitnessWalkActivity); return Result.success(); } catch (Exception e) { @@ -257,9 +292,8 @@ public class FitnessWalkActivityManageController { @At - @ApiOperation("删除健步走活动") - @SLog(tag = "健步走-活动管理", msg = "删除活动") @SaCheckPermission("fitnessWalk.activityManage") + @Aop(TransAop.READ_COMMITTED) public Result delete(String id) { try { weAppCloudUtil.request(WeAppCloudUtil.CRUD.DELETE, "db.collection('activity').doc('" + id + "').remove()"); @@ -319,7 +353,7 @@ public class FitnessWalkActivityManageController { Sys_task sys_task = new Sys_task(); sys_task.setName(name); sys_task.setNote(id); - sys_task.setJobClass("io.v.nutz.task.job.walking.WalkingNotifyJob"); + sys_task.setJobClass("com.budwk.app.task.job.walking.WalkingNotifyJob"); sys_task.setData(Json.toJson(Map.of("groupId", groupId))); sys_task.setDisabled(false); @@ -335,3 +369,10 @@ public class FitnessWalkActivityManageController { } } } + + + + + + + diff --git a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkActivityRelationController.java b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkActivityRelationController.java new file mode 100644 index 00000000..220f4832 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkActivityRelationController.java @@ -0,0 +1,96 @@ +package com.budwk.app.zhgh.activity.fitnesswalk.controller; +import org.nutz.aop.interceptor.ioc.TransAop; + +import com.budwk.app.base.result.Result; +import com.budwk.app.base.param.PageForm; +import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkActivityRelationProject; +import com.budwk.app.zhgh.activity.fitnesswalk.service.FitnessWalkActivityRelationProjectService; +import com.budwk.app.zhgh.activity.fitnesswalk.service.FitnessWalkRelationActivityCompletionService; +import cn.dev33.satoken.annotation.SaCheckPermission; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.ioc.aop.Aop; +import org.nutz.mvc.annotation.At; +import org.nutz.mvc.annotation.Ok; + +/** + * 健步走活动关联菜单。 + * 这里新增“关联项目”管理能力,主活动名称与达标活动数量统一落在 MySQL 中。 + */ +@IocBean +@Ok("json") +@At("/platform/fitnessWalk/activityRelation") +public class FitnessWalkActivityRelationController { + + @Inject + private FitnessWalkActivityRelationProjectService relationProjectService; + + @Inject + private FitnessWalkRelationActivityCompletionService relationActivityCompletionService; + + @At("") + @Ok("beetl:/platform/zhgh/activity/fitnesswalk/activityRelation.html") + @SaCheckPermission("fitnessWalk.activityRelation") + public void index() { + } + + /** + * 查询关联项目分页列表。 + */ + @At + @SaCheckPermission("fitnessWalk.activityRelation") + public Result pageData(PageForm pageForm) { + return Result.success(relationProjectService.pageData(pageForm)); + } + + /** + * 保存关联项目。 + * 支持新增和编辑,保存字段仅包含主活动名称和达标活动数量。 + */ + @At + @SaCheckPermission("fitnessWalk.activityRelation") + @Aop(TransAop.READ_COMMITTED) + public Result doSave(FitnessWalkActivityRelationProject relationProject) { + return relationProjectService.saveRelationProject(relationProject); + } + + /** + * 删除关联项目。 + */ + @At + @SaCheckPermission("fitnessWalk.activityRelation") + @Aop(TransAop.READ_COMMITTED) + public Result doRemove(String id) { + return relationProjectService.deleteRelationProject(id); + } + + /** + * 查看关联项目下已关联的活动。 + * 活动列表按活动数据中保存的关联ID查询,避免列表展示与实际活动数据不一致。 + */ + @At + @SaCheckPermission("fitnessWalk.activityRelation") + public Result relationActivityList(String id) { + return Result.success(relationProjectService.getRelationActivityList(id)); + } + + /** + * 判断当前登录人是否满足某个关联项目的达标要求。 + * controller 仅负责接参与返回结果,具体的关联活动和打卡完成判断统一放在独立 service 中处理。 + */ + @At + @SaCheckPermission("fitnessWalk.activityRelation") + public Result isCurrentUserRelationActivityQualified(String relationActivityId,String userId) { + return Result.success(relationActivityCompletionService.isCurrentUserRelationActivityQualified(relationActivityId,userId)); + } + + + +} + + + + + + + diff --git a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkCommonController.java b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkCommonController.java index ae74afdf..2ce23e61 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkCommonController.java +++ b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkCommonController.java @@ -5,48 +5,13 @@ import org.nutz.mvc.annotation.At; import org.nutz.mvc.annotation.Ok; /** - * 健身走公共答题 + * 健步走公共接口。 + * 当前项目未启用来源项目中的答题模块,相关小程序白名单接口保留路由类,避免引入不存在的题库依赖。 */ @IocBean @At("/platform/fitnessWalk/common") @Ok("json:full") public class FitnessWalkCommonController { - -// @Inject -// private QuestionService questionService; -// -// @Inject -// private QuestionIssueService questionIssueService; -// -// @Inject -// private QuestionWorService questionWorService; -// -// @Inject -// private QuestionReplyService questionReplyService; -// -// @At -// public Object getQuestion() { -// return questionService.query(Cnd.where("qflag", "=", 2)); -// } -// -// @At -// public Object getIssueById(String id) { -// List questionIssues = questionIssueService.query(Cnd.where("iqid", "=", id).asc("ipxbh")); -// questionIssues.forEach(issue -> { -// issue.setWors(questionWorService.query(Cnd.where("wiid", "=", issue.getIid()).asc("wpxbh"))); -// }); -// return questionIssues; -// } -// -// @At -// public Object saveAnswer(@Param("reply") Question_reply[] replys) { -// for (Question_reply reply : replys) { -// reply.setSave(false); -// reply.setRidentity(R.UU32()); -// reply.setRtime(DateUtil.getDate()); -// questionReplyService.insert(reply); -// } -// return null; -// } - } + + diff --git a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkLoginController.java b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkLoginController.java index 0aa14a13..f1756b22 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkLoginController.java +++ b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkLoginController.java @@ -26,7 +26,7 @@ public class FitnessWalkLoginController { private SysUserService sysUserService; @At("/") - public Object index(String username, String loginname) { + public Result index(String username, String loginname) { try { Sql sql = Sqls.create(""" SELECT @@ -40,7 +40,7 @@ public class FitnessWalkLoginController { u.unitid, u.member, unit.unionid, - un.unionname, + un.name unionname, group_concat(sr.`code`) roles FROM sys_user u @@ -73,3 +73,7 @@ public class FitnessWalkLoginController { } } } + + + + diff --git a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkPunchLotteryController.java b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkPunchLotteryController.java index d1fbebd6..06f66231 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkPunchLotteryController.java +++ b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkPunchLotteryController.java @@ -1,22 +1,28 @@ package com.budwk.app.zhgh.activity.fitnesswalk.controller; +import org.nutz.aop.interceptor.ioc.TransAop; +import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.StrUtil; -import cn.hutool.json.JSONObject; -import cn.hutool.json.JSONUtil; import com.budwk.app.base.result.Result; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; import com.budwk.app.sys.models.Sys_user; +import com.budwk.app.base.annotation.SLog; +import com.budwk.app.web.commons.auth.utils.AuthUtil; import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkActivity; import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkPunchLottery; import com.budwk.app.zhgh.activity.fitnesswalk.service.FitnessWalkCommonService; import com.budwk.app.zhgh.activity.fitnesswalk.utils.WeAppCloudUtil; -import io.swagger.annotations.ApiOperation; +import cn.dev33.satoken.annotation.SaCheckPermission; import org.nutz.dao.Chain; import org.nutz.dao.Cnd; import org.nutz.integration.jedis.RedisService; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.ioc.aop.Aop; import org.nutz.lang.Lang; import org.nutz.lang.random.R; +import org.nutz.lang.util.NutMap; import org.nutz.mvc.annotation.At; import org.nutz.mvc.annotation.Ok; @@ -29,7 +35,6 @@ import java.util.stream.Collectors; */ @IocBean @Ok("json") -@ApiOperation("健步走打卡抽奖") @At("/platform/fitnessWalk/punchLottery") public class FitnessWalkPunchLotteryController { @@ -58,7 +63,6 @@ public class FitnessWalkPunchLotteryController { * @return */ @At - @ApiOperation("打卡模式的抽奖") public Result judgeWinningToPunch(String activityId, String userId) { try { if (StrUtil.isBlank(activityId) && StrUtil.isBlank(userId)) { @@ -68,23 +72,21 @@ public class FitnessWalkPunchLotteryController { lock.lock(); int winingCount = fitnessWalkCommonService.dao().count(FitnessWalkPunchLottery.class, Cnd.where("activityId", "=", activityId).and("userId", "=", userId)); if (winingCount > 0) { - return null; + return Result.success(); } - // 把中奖的用户直接存到redis里面去 String redisKey = this.getClass().getName() + "#judgeWinningToPunch#activityid:" + activityId + "#PRIZELIST:"; String getPrizeIndexesKey = this.getClass().getName() + "#judgeWinningToPunch#activityid:" + activityId + "#GETPRIZEINDEXES:"; // redisService.del(getPrizeIndexesKey); - // 获取奖品列表 List punchLotteryPrizes = new ArrayList<>(); String punchLotteryPrizesJson = redisService.get(redisKey); if (StrUtil.isNotBlank(punchLotteryPrizesJson)) { - punchLotteryPrizes = JSONUtil.toList(punchLotteryPrizesJson, FitnessWalkActivity.PunchLotteryPrize.class); + punchLotteryPrizes = JSON.parseArray(punchLotteryPrizesJson, FitnessWalkActivity.PunchLotteryPrize.class); } else { JSONObject prizeJsonObject = weAppCloudUtil.request(WeAppCloudUtil.CRUD.QUERY, "db.collection('activity').doc('%s').field({punchLotteryPrizes: true}).get()".formatted(activityId)); - List prizeList = JSONUtil.parseObj((String) prizeJsonObject.getJSONArray("data").get(0)).getJSONArray("punchLotteryPrizes").toList(FitnessWalkActivity.PunchLotteryPrize.class); - redisService.set(redisKey, JSONUtil.toJsonStr(prizeList)); + List prizeList = JSON.parseObject((String) prizeJsonObject.getJSONArray("data").get(0)).getJSONArray("punchLotteryPrizes").toJavaList(FitnessWalkActivity.PunchLotteryPrize.class); + redisService.set(redisKey, JSON.toJSONString(prizeList)); punchLotteryPrizes = prizeList; } @@ -95,14 +97,14 @@ public class FitnessWalkPunchLotteryController { if (!redisService.exists(getPrizeIndexesKey)) { JSONObject jsonObject = weAppCloudUtil.request(WeAppCloudUtil.CRUD.QUERY, "db.collection('register').where({activity_id:'%s'}).count()".formatted(activityId)); JSONObject pager = jsonObject.getJSONObject("pager"); - String total = pager.getStr("Total"); + String total = pager.getString("Total"); //报名人数的一半,从这里的人中取数(最多抽取人数) Integer lotteryNum = (int) Math.ceil(Double.parseDouble(total) / 2); winningUserIndexList = generateRandomNumber(1, lotteryNum, sum); - redisService.set(getPrizeIndexesKey, JSONUtil.toJsonStr(winningUserIndexList)); + redisService.set(getPrizeIndexesKey, JSON.toJSONString(winningUserIndexList)); } else { String data = redisService.get(getPrizeIndexesKey); - winningUserIndexList = JSONUtil.toList(data, Integer.class); + winningUserIndexList = JSONObject.parseArray(data, Integer.class); } List punchLotteryList = fitnessWalkCommonService.dao().query(FitnessWalkPunchLottery.class, Cnd.where("activityId", "=", activityId)); @@ -123,7 +125,7 @@ public class FitnessWalkPunchLotteryController { if (winningUserIndexList.contains((punchLotteryList.size()))) { //奖项map - Map prizeMap = punchLotteryPrizes.stream().collect(Collectors.toMap(FitnessWalkActivity.PunchLotteryPrize::getValue, FitnessWalkActivity.PunchLotteryPrize::getName)); + Map prizeMap = punchLotteryPrizes.stream().collect(Collectors.toMap(v -> v.getValue(), v -> v.getName())); //奖项池 List prizeIdList = new ArrayList<>(); @@ -135,11 +137,14 @@ public class FitnessWalkPunchLotteryController { }); //已经中奖的奖项ID集合 - List winPrizeIdList = punchLotteryList.stream().filter(FitnessWalkPunchLottery::getIsWin).map(FitnessWalkPunchLottery::getPrizeId).toList(); - winPrizeIdList.forEach(prizeIdList::remove); + List winPrizeIdList = punchLotteryList.stream().filter(v -> v.getIsWin()).map(v -> v.getPrizeId()).collect(Collectors.toList()); + winPrizeIdList.forEach(prizeId -> { + int i = prizeIdList.indexOf(prizeId); + prizeIdList.remove(i); + }); Random random = new Random(); - if (Lang.isNotEmpty(prizeIdList.size())) { + if (prizeIdList.size() != 0) { int index = random.nextInt(prizeIdList.size()); // 获取随机元素 @@ -161,7 +166,7 @@ public class FitnessWalkPunchLotteryController { } } catch (Exception e) { e.printStackTrace(); - return null; + return Result.error(e.getMessage()); } finally { if (lock.isLocked()) { lock.unlock(); @@ -170,7 +175,6 @@ public class FitnessWalkPunchLotteryController { } @At - @ApiOperation("获取中奖记录") public Result getLotteryRecord(String activityId, String userId) { List list = fitnessWalkCommonService.dao().query(FitnessWalkPunchLottery.class, Cnd.where("activityId", "=", activityId) .and("userId", "=", userId)); @@ -185,7 +189,7 @@ public class FitnessWalkPunchLotteryController { * @return */ @At - @ApiOperation("兑奖") + @Aop(TransAop.READ_COMMITTED) public Result doExchange(String id) { int count = fitnessWalkCommonService.dao().count(FitnessWalkPunchLottery.class, Cnd.where("id", "=", id).and("isExchange", "=", 1)); @@ -209,7 +213,7 @@ public class FitnessWalkPunchLotteryController { * @return */ @At - @ApiOperation("改为已读") + @Aop(TransAop.READ_COMMITTED) public Result doReadLottery(String activityId, String userId) { fitnessWalkCommonService.dao().update(FitnessWalkPunchLottery.class, Chain.make("isRead", 1), Cnd.where("userId", "=", userId).and("activityId", "=", activityId)); @@ -223,7 +227,6 @@ public class FitnessWalkPunchLotteryController { * @return */ @At - @ApiOperation("查看获奖人员") public Result getPunchWining(String activityId) { List punchLotteryList = fitnessWalkCommonService.dao().query(FitnessWalkPunchLottery.class, Cnd.where("isWin", "=", 1).and("activityId", "=", activityId)); @@ -232,7 +235,7 @@ public class FitnessWalkPunchLotteryController { /** - * 抽奖方法的工具类,生成随机数,该随机数为中奖人 + * 生成随机数,该随机数为中奖人 * * @param minNum 最小值 * @param maxNum 最多抽取多少人 @@ -256,3 +259,10 @@ public class FitnessWalkPunchLotteryController { return new ArrayList<>(randomNumbers); } } + + + + + + + diff --git a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkPunchStatisticsController.java b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkPunchStatisticsController.java index a26ffc63..9dc6d6dd 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkPunchStatisticsController.java +++ b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkPunchStatisticsController.java @@ -2,22 +2,34 @@ package com.budwk.app.zhgh.activity.fitnesswalk.controller; import cn.afterturn.easypoi.excel.ExcelExportUtil; import cn.afterturn.easypoi.excel.entity.ExportParams; +import cn.afterturn.easypoi.excel.entity.enmus.ExcelType; import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity; -import cn.dev33.satoken.annotation.SaCheckPermission; -import cn.hutool.json.JSONObject; +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.core.util.URLUtil; import com.budwk.app.base.result.Result; +import com.alibaba.fastjson.JSONObject; import com.budwk.app.base.service.BaseService; import com.budwk.app.base.utils.CommonDownloadUtil; +import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.sys.models.Sys_union; +import com.budwk.app.zhgh.activity.fitnesswalk.contants.Cascader; import com.budwk.app.zhgh.activity.fitnesswalk.contants.FitnessWalkMode; +import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkActivity; import com.budwk.app.zhgh.activity.fitnesswalk.service.FitnessWalkCommonService; import com.budwk.app.zhgh.activity.fitnesswalk.service.FitnessWalkPunchStatisticsService; import com.budwk.app.zhgh.activity.fitnesswalk.utils.WeAppCloudUtil; -import io.swagger.annotations.ApiOperation; +import org.apache.commons.lang3.StringUtils; import org.apache.poi.ss.usermodel.Workbook; +import cn.dev33.satoken.annotation.SaCheckPermission; +import org.nutz.dao.Cnd; import org.nutz.dao.Dao; +import org.nutz.dao.Sqls; +import org.nutz.dao.sql.Sql; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.lang.Strings; import org.nutz.lang.util.NutMap; import org.nutz.mvc.annotation.At; import org.nutz.mvc.annotation.Ok; @@ -25,16 +37,18 @@ import org.nutz.mvc.annotation.Param; import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Optional; +import java.util.stream.Collectors; /** * 打卡统计 */ @IocBean @Ok("json") -@ApiOperation("健步走打卡统计") @At("/platform/fitnessWalk/punchStatistics") public class FitnessWalkPunchStatisticsController { @@ -68,99 +82,110 @@ public class FitnessWalkPunchStatisticsController { */ @At @Ok("json:full") - @ApiOperation("打卡记录统计") @SaCheckPermission("fitnessWalk.punchStatistics") - public Result pageData(String activityId, String unionId) { - List list = fitnessWalkPunchStatisticsService.unionRegCheckInNum(activityId, unionId); + public Result pageData(String mainActivityId, String subActivityId, String activityId, String unionId) { + List list = fitnessWalkPunchStatisticsService.unionRegCheckInNum(mainActivityId, subActivityId, activityId, unionId); return Result.success(list); } @At - @ApiOperation("获取活动列表") @SaCheckPermission("fitnessWalk.punchStatistics") public Result getCascadersActivity(@Param("year") int year) { - return Result.success().addData(fitnessWalkCommonService.cascaderActivity(year, FitnessWalkMode.PUNCH)); + return Result.success(fitnessWalkCommonService.cascaderActivity(year, FitnessWalkMode.PUNCH)); } @At @Ok("json:full") - @ApiOperation("根据工会查询获取报名人员") @SaCheckPermission("fitnessWalk.punchStatistics") - public Result unionRegistrationData(String activityId, String unionId) { - List list = fitnessWalkPunchStatisticsService.unionRegistrationData(activityId, unionId); + public Result unionRegistrationData(String mainActivityId, String subActivityId, String activityId, String unionId) { + List list = fitnessWalkPunchStatisticsService.unionRegistrationData(mainActivityId, subActivityId, activityId, unionId); return Result.success(list); } @At @Ok("json:full") - @ApiOperation("根据工会查询获取打卡人员") @SaCheckPermission("fitnessWalk.punchStatistics") - public Result unionCheckInData(String activityId, String unionId) { - List list = fitnessWalkPunchStatisticsService.unionCheckInData(activityId, unionId); + public Result unionCheckInData(String mainActivityId, String subActivityId, String activityId, String unionId) { + List list = fitnessWalkPunchStatisticsService.unionCheckInData(mainActivityId, subActivityId, activityId, unionId); return Result.success(list); } - + /** + * 导出各分工会报名人数、打卡人数 + * + * @param activityId + */ @At @Ok("void") - @ApiOperation("导出各分工会报名人数、打卡人数") @SaCheckPermission("fitnessWalk.punchStatistics") - public void exportUnionRegCheckInNum(HttpServletResponse response, String activityId) { - List list = fitnessWalkPunchStatisticsService.unionRegCheckInNum(activityId, null); + public void exportUnionRegCheckInNum(HttpServletResponse response, String mainActivityId, String subActivityId, String activityId) { + List list = fitnessWalkPunchStatisticsService.unionRegCheckInNum(mainActivityId, subActivityId, activityId, null); List entities = new ArrayList<>(); entities.add(new ExcelExportEntity("分工会", "unionname", 20)); entities.add(new ExcelExportEntity("报名人数", "regCount", 20)); entities.add(new ExcelExportEntity("打卡人数", "checkInCount", 20)); try { - Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), entities, list); - workbook.write(response.getOutputStream()); - + ExportParams exportParams = new ExportParams(); + exportParams.setType(ExcelType.XSSF); + Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entities, list); CommonDownloadUtil.download("各分工会统计人数.xlsx", workbook, response); - } catch (IOException e) { + } catch (Exception e) { throw new RuntimeException(e); } } - + /** + * 导出某个分工会的报名人员 + * + * @param response + * @param activityId + * @param unionId + */ @At @Ok("void") - @ApiOperation("导出某个分工会的报名人员") @SaCheckPermission("fitnessWalk.punchStatistics") - public void exportRegUsers(HttpServletResponse response, String activityId, String unionId) { + public void exportRegUsers(HttpServletResponse response, String mainActivityId, String subActivityId, String activityId, String unionId) { Sys_union union = dao.fetch(Sys_union.class, unionId); String unionName = Optional.ofNullable(union).map(Sys_union::getName).orElse(null); - List list = fitnessWalkPunchStatisticsService.unionRegistrationData(activityId, unionId); + List list = fitnessWalkPunchStatisticsService.unionRegistrationData(mainActivityId, subActivityId, activityId, unionId); List entities = new ArrayList<>(); + entities.add(new ExcelExportEntity("活动名称", "activityName", 30)); entities.add(new ExcelExportEntity("工号", "loginname", 20)); entities.add(new ExcelExportEntity("姓名", "username", 20)); entities.add(new ExcelExportEntity("单位", "unitname", 20)); - entities.add(new ExcelExportEntity("手机号", "mobile", 20)); +// entities.add(new ExcelExportEntity("手机号", "mobile", 20)); entities.add(new ExcelExportEntity("报名时间", "register_time_str", 20)); try { - Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), entities, list); - workbook.write(response.getOutputStream()); - CommonDownloadUtil.download("分工会报名人员.xlsx", workbook, response); - } catch (IOException e) { + ExportParams exportParams = new ExportParams(); + exportParams.setType(ExcelType.XSSF); + Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entities, list); + CommonDownloadUtil.download(unionName + "分工会报名人员.xlsx", workbook, response); + } catch (Exception e) { throw new RuntimeException(e); } } - + /** + * 导出某个分工会的打卡人员 + * + * @param response + * @param activityId + * @param unionId + */ @At @Ok("void") - @ApiOperation("导出某个分工会的打卡人员") @SaCheckPermission("fitnessWalk.punchStatistics") - public void exportCheckInUsers(HttpServletResponse response, String activityId, String unionId) { + public void exportCheckInUsers(HttpServletResponse response, String mainActivityId, String subActivityId, String activityId, String unionId) { Sys_union union = dao.fetch(Sys_union.class, unionId); String unionName = Optional.ofNullable(union).map(Sys_union::getName).orElse(null); - List list = fitnessWalkPunchStatisticsService.unionCheckInData(activityId, unionId); + List list = fitnessWalkPunchStatisticsService.unionCheckInData(mainActivityId, subActivityId, activityId, unionId); for (JSONObject jsonObject : list) { - Boolean used = jsonObject.getBool("used"); + Boolean used = jsonObject.getBoolean("used"); if (used == null) { jsonObject.put("used", "未取得"); } else if (used) { @@ -171,19 +196,21 @@ public class FitnessWalkPunchStatisticsController { } List entities = new ArrayList<>(); + entities.add(new ExcelExportEntity("活动名称", "activityName", 30)); entities.add(new ExcelExportEntity("工号", "loginname", 20)); entities.add(new ExcelExportEntity("姓名", "username", 20)); entities.add(new ExcelExportEntity("单位", "unitname", 20)); - entities.add(new ExcelExportEntity("手机号", "mobile", 20)); +// entities.add(new ExcelExportEntity("手机号", "mobile", 20)); entities.add(new ExcelExportEntity("点位总数", "pts_size", 20)); entities.add(new ExcelExportEntity("已打卡点位数", "sign_size", 20)); - entities.add(new ExcelExportEntity("礼品券", "used", 20)); +// entities.add(new ExcelExportEntity("礼品券", "used", 20)); try { - Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), entities, list); - workbook.write(response.getOutputStream()); - CommonDownloadUtil.download(unionName + "分工会报名人员.xlsx", workbook, response); - } catch (IOException e) { + ExportParams exportParams = new ExportParams(); + exportParams.setType(ExcelType.XSSF); + Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entities, list); + CommonDownloadUtil.download(unionName + "分工会打卡人员.xlsx", workbook, response); + } catch (Exception e) { throw new RuntimeException(e); } } @@ -204,44 +231,61 @@ public class FitnessWalkPunchStatisticsController { // } + /** + * 未领取得奖品券用户 + * + * @param response + * @param activityId + */ @At @Ok("void") - @ApiOperation("导出未领取得奖品券用户") @SaCheckPermission("fitnessWalk.punchStatistics") - public void exportNotGetGiftVoucherUsers(HttpServletResponse response, String activityId) { - List list = fitnessWalkPunchStatisticsService.exportNotGetGiftVoucherUsers(activityId); + public void exportNotGetGiftVoucherUsers(HttpServletResponse response, String mainActivityId, String subActivityId, String activityId) { + List list = fitnessWalkPunchStatisticsService.exportNotGetGiftVoucherUsers(mainActivityId, subActivityId, activityId); List entities = new ArrayList<>(); entities.add(new ExcelExportEntity("工号", "loginname", 20)); entities.add(new ExcelExportEntity("姓名", "username", 20)); entities.add(new ExcelExportEntity("单位", "unitname", 20)); - entities.add(new ExcelExportEntity("手机号", "mobile", 20)); +// entities.add(new ExcelExportEntity("手机号", "mobile", 20)); try { - Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), entities, list); - workbook.write(response.getOutputStream()); + ExportParams exportParams = new ExportParams(); + exportParams.setType(ExcelType.XSSF); + Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entities, list); CommonDownloadUtil.download("未领取得奖品券人员.xlsx", workbook, response); - } catch (IOException e) { + } catch (Exception e) { throw new RuntimeException(e); } } + /** + * 未使用奖品券用户 + * + * @param response + * @param activityId + */ @At @Ok("void") - @ApiOperation("导出未使用奖品券用户") @SaCheckPermission("fitnessWalk.punchStatistics") - public void exportNotUseGiftVoucherUsers(HttpServletResponse response, String activityId) { - List list = fitnessWalkPunchStatisticsService.exportNotUseGiftVoucherUsers(activityId); + public void exportNotUseGiftVoucherUsers(HttpServletResponse response, String mainActivityId, String subActivityId, String activityId) { + List list = fitnessWalkPunchStatisticsService.exportNotUseGiftVoucherUsers(mainActivityId, subActivityId, activityId); List entities = new ArrayList<>(); entities.add(new ExcelExportEntity("工号", "loginname", 20)); entities.add(new ExcelExportEntity("姓名", "username", 20)); entities.add(new ExcelExportEntity("单位", "unitname", 20)); - entities.add(new ExcelExportEntity("手机号", "mobile", 20)); +// entities.add(new ExcelExportEntity("手机号", "mobile", 20)); try { - Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), entities, list); - workbook.write(response.getOutputStream()); + ExportParams exportParams = new ExportParams(); + exportParams.setType(ExcelType.XSSF); + Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entities, list); CommonDownloadUtil.download("导出未使用礼品券用户.xlsx", workbook, response); - } catch (IOException e) { + } catch (Exception e) { throw new RuntimeException(e); } } } + + + + + diff --git a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkStepManageController.java b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkStepManageController.java index acbfd736..5ab0b532 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkStepManageController.java +++ b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkStepManageController.java @@ -1,19 +1,25 @@ package com.budwk.app.zhgh.activity.fitnesswalk.controller; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.date.DateBetween; import cn.hutool.core.date.DateTime; import cn.hutool.core.date.DateUnit; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.StrUtil; -import com.budwk.app.base.annotation.SLog; import com.budwk.app.base.result.Result; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.budwk.app.base.page.Pagination; import com.budwk.app.base.service.BaseService; import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkActivity; +import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkAwardUser; import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkStep; import com.budwk.app.zhgh.activity.fitnesswalk.service.FitnessWalkCommonService; import com.budwk.app.zhgh.activity.fitnesswalk.utils.WeAppCloudUtil; +import com.budwk.app.zhgh.activity.fitnesswalk.vo.FitnessWalkDateStepVO; import com.budwk.app.zhgh.dayofficework.integral.model.IntegralDetail; -import io.swagger.annotations.ApiOperation; import org.nutz.aop.interceptor.ioc.TransAop; +import org.nutz.dao.Chain; import org.nutz.dao.Cnd; import org.nutz.dao.Dao; import org.nutz.dao.Sqls; @@ -24,6 +30,7 @@ import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.json.Json; import org.nutz.lang.Lang; +import org.nutz.lang.random.R; import org.nutz.lang.util.NutMap; import org.nutz.mvc.annotation.At; import org.nutz.mvc.annotation.Ok; @@ -38,7 +45,6 @@ import java.util.stream.Collectors; @IocBean @Ok("json") -@ApiOperation("健步走步数管理") @At("/platform/fitnessWalk/stepManage") public class FitnessWalkStepManageController { @@ -65,8 +71,6 @@ public class FitnessWalkStepManageController { @At @Ok("json:full") @Aop(TransAop.READ_COMMITTED) - @SLog(tag = "健步走步数管理", msg = "微信步数上传") - @ApiOperation("上传微信步数,再根据是否开启了积分,自动将步数转化成积分") public Result updateStepMonth(String activityId, String userId, String monthSteps) { try { if (StrUtil.isBlank(userId) || StrUtil.isBlank(monthSteps) || StrUtil.isBlank(activityId)) { @@ -197,11 +201,9 @@ public class FitnessWalkStepManageController { */ @At @Ok("json:full") - @ApiOperation("小程序获取开始时间至今的步数") public Result getActivityDateStep(String activityId, String userId) { try { Cnd cnd = Cnd.NEW(); - FitnessWalkActivity activity = fitnessWalkCommonService.getActivity(activityId); DateTime startTime = DateUtil.date(activity.getStartTime()); DateTime endTime = DateUtil.date(activity.getEndTime()); @@ -214,11 +216,13 @@ public class FitnessWalkStepManageController { cnd.groupBy("applyDate"); List stepList = baseService.dao().query(FitnessWalkStep.class, cnd); - List> collect = stepList.stream().map(v -> { - return Map.of("step", v.getStep(), "timestamp", v.getApplyDate().getTime()); + List list = stepList.stream().map(v -> { + // 返回给小程序的折线图数据,timestamp 允许为空以兼容历史异常日期数据。 + Long timestamp = v.getApplyDate() == null ? null : v.getApplyDate().getTime(); + return new FitnessWalkDateStepVO(v.getStep(), timestamp); }).collect(Collectors.toList()); + return Result.success(list); - return Result.success().addData(collect); } catch (Exception e) { return Result.error(e.getMessage()); @@ -227,7 +231,6 @@ public class FitnessWalkStepManageController { @At @Ok("json:full") - @ApiOperation("获取活动达标的进度条") public Result getActivityQualifyProgressBar(String activityId, String userId) { NutMap resultMap = NutMap.NEW(); @@ -246,17 +249,14 @@ public class FitnessWalkStepManageController { .and("DATE( applyDate )", "=", DateUtil.today()).and("activityId", "=", activityId) .and("step", ">", lotteryQualificationStep)); - // 今天的步数 Integer thisDayStep = userWalkStep.getStep(); - // 去计算达标的百分比 BigDecimal lotteryQualificationStepBig = new BigDecimal(lotteryQualificationStep); BigDecimal thisDayStepBig = new BigDecimal(thisDayStep); //达标的小数,保留四位 BigDecimal divide = thisDayStepBig.divide(lotteryQualificationStepBig, 4, RoundingMode.HALF_UP); BigDecimal percentValue = divide.multiply(new BigDecimal("100")); - // 格式化为两位小数 - DecimalFormat decimalFormat = new DecimalFormat("#.##"); + DecimalFormat decimalFormat = new DecimalFormat("#.##"); // 格式化为两位小数 //百分数 String formattedPercent = decimalFormat.format(percentValue); @@ -290,7 +290,7 @@ public class FitnessWalkStepManageController { List partSteps = userWalkSteps.stream() .filter(s -> (DateUtil.compare(s.getApplyDate(), rule.getStartDate()) >= 0 && DateUtil.compare(s.getApplyDate(), rule.getEndDate()) <= 0)) - .toList(); + .collect(Collectors.toList()); Integer lotteryQualificationDay = rule.getLotteryQualificationDay(); Integer lotteryQualificationStep = rule.getLotteryQualificationStep(); @@ -312,7 +312,7 @@ public class FitnessWalkStepManageController { //总步数 nutMap.put("mode", "customLotterySumStepMode"); //用户总达标步数 - nutMap.put("userSumStep", partSteps.stream().mapToInt(FitnessWalkStep::getStep).sum()); + nutMap.put("userSumStep", partSteps.stream().mapToInt(s -> s.getStep()).sum()); //总达标步数 nutMap.put("complianceSumStep", lotteryQualificationStep); @@ -323,7 +323,7 @@ public class FitnessWalkStepManageController { } resultMap.addv("customLotteryMode", result); } - return Result.success().addData(resultMap); + return Result.success(resultMap); } /** @@ -335,7 +335,6 @@ public class FitnessWalkStepManageController { */ @At @Ok("json:full") - @ApiOperation("获取用户未读的奖") public Result winningRecord(String activityId, String userId) { Sql sql = Sqls.create(""" select awardName,startDate,endDate from fitness_walk_award_user where activityId=@activityId and userId=@userId and isRead=@isRead @@ -347,6 +346,11 @@ public class FitnessWalkStepManageController { return Result.success(baseService.listMap(sql)); } + @At + @Ok("json:full") + public Result hasReadAward(String activityId) { + return Result.success(); + } /** @@ -401,3 +405,9 @@ public class FitnessWalkStepManageController { return maxPoints != null && maxPoints > 0; } } + + + + + + diff --git a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkStepRaffleController.java b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkStepRaffleController.java index 726c742f..86ebe2d2 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkStepRaffleController.java +++ b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkStepRaffleController.java @@ -4,13 +4,10 @@ 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.dev33.satoken.annotation.SaCheckPermission; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.StrUtil; -import com.budwk.app.base.annotation.SLog; -import com.budwk.app.base.page.Pagination; -import com.budwk.app.base.param.PageForm; import com.budwk.app.base.result.Result; +import com.budwk.app.base.param.PageForm; import com.budwk.app.base.service.BaseService; import com.budwk.app.base.utils.CommonDownloadUtil; import com.budwk.app.zhgh.activity.fitnesswalk.contants.FitnessWalkMode; @@ -20,11 +17,12 @@ import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkRaffleUser; import com.budwk.app.zhgh.activity.fitnesswalk.service.FitnessWalkCommonService; import com.budwk.app.zhgh.activity.fitnesswalk.service.FitnessWalkStepRaffleService; import com.budwk.app.zhgh.dayofficework.integral.model.IntegralDetail; -import io.swagger.annotations.ApiOperation; import org.apache.poi.ss.usermodel.Workbook; +import cn.dev33.satoken.annotation.SaCheckPermission; import org.nutz.aop.interceptor.ioc.TransAop; import org.nutz.dao.Chain; import org.nutz.dao.Cnd; +import com.budwk.app.base.dao.CndPlus; import org.nutz.dao.Dao; import org.nutz.dao.sql.Sql; import org.nutz.dao.util.cri.SqlExpressionGroup; @@ -32,6 +30,7 @@ import org.nutz.ioc.aop.Aop; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.lang.Lang; +import org.nutz.lang.random.R; import org.nutz.lang.util.NutMap; import org.nutz.mvc.annotation.At; import org.nutz.mvc.annotation.Ok; @@ -48,9 +47,8 @@ import java.util.List; * @Date 2024/12/27 14:43 * @注释 健步走抽奖控制器 */ -@IocBean @Ok("json") -@ApiOperation("健步走手动抽奖") +@IocBean @At("/platform/fitnessWalk/stepRaffle") public class FitnessWalkStepRaffleController { @@ -64,52 +62,51 @@ public class FitnessWalkStepRaffleController { private FitnessWalkStepRaffleService fitnessWalkStepRaffleService; + @At("") @Ok("beetl:/platform/zhgh/activity/fitnesswalk/stepRaffle.html") @SaCheckPermission("fitnessWalk.stepRaffle") public void index() { } - @At - @ApiOperation("获取抽奖列表") @SaCheckPermission("fitnessWalk.stepRaffle") public Result pageData(PageForm pageForm, @Param("activityId") String activityId, @Param("unionId") String unionId, @Param("unitId") String unitId){ Sql sql = fitnessWalkStepRaffleService.getRaffleSql(pageForm, activityId, unionId, unitId); - Pagination pagination = baseService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); - return Result.success().addData(pagination); + return Result.success(baseService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql)); } + @At - @ApiOperation("获取活动级联数据") @SaCheckPermission("fitnessWalk.stepRaffle") public Result getCascadersActivity(@Param("year") int year) { - return Result.success().addData(fitnessWalkCommonService.cascaderActivity(year, FitnessWalkMode.STEP_COUNT)); + return Result.success(fitnessWalkCommonService.cascaderActivity(year, FitnessWalkMode.STEP_COUNT)); } + /** + * 手动抽奖规则 + * @param fitnessWalkRaffleRules + * @return + */ @At @Aop(TransAop.READ_COMMITTED) - @ApiOperation("设置抽奖规则,并抽奖") - @SLog(tag = "健步走手动抽奖", msg = "设置抽奖规则并抽奖") @SaCheckPermission("fitnessWalk.stepRaffle") public Result saveRaffle(FitnessWalkRaffleRules fitnessWalkRaffleRules){ baseService.dao().insertOrUpdate(fitnessWalkRaffleRules); fitnessWalkStepRaffleService.manualLottery(fitnessWalkRaffleRules.getActivityId()); - return null; + return Result.success(); } @At - @ApiOperation("获取抽奖规则") @SaCheckPermission("fitnessWalk.stepRaffle") public Result getRaffle(String activityId){ - FitnessWalkRaffleRules raffleRules = baseService.dao().fetch(FitnessWalkRaffleRules.class, Cnd.where("activityId", "=", activityId)); - return Result.success().addData(raffleRules); + return Result.success(baseService.dao().fetch(FitnessWalkRaffleRules.class, Cnd.where("activityId", "=", activityId))); } @@ -119,10 +116,8 @@ public class FitnessWalkStepRaffleController { * @return */ @At - @ApiOperation("小程序获取奖项信息") public Result getAwardList(String activityId){ - List activityAwardList = fitnessWalkStepRaffleService.getActivityAwardList(activityId); - return Result.success().addData(activityAwardList); + return Result.success(fitnessWalkStepRaffleService.getActivityAwardList(activityId)); } @@ -131,7 +126,6 @@ public class FitnessWalkStepRaffleController { * @return */ @At - @ApiOperation("小程序获取自己的中奖信息") public Result getAwardInfo(String activityId, String userId){ Dao dao = fitnessWalkStepRaffleService.dao(); FitnessWalkAwardUser awardUser = dao.fetch(FitnessWalkAwardUser.class, Cnd.where("activityId", "=", activityId).and("userId", "=", userId)); @@ -148,7 +142,7 @@ public class FitnessWalkStepRaffleController { } else { map.put("raffleNum", 0); } - return Result.success().addData(map); + return Result.success(map); } @@ -157,7 +151,6 @@ public class FitnessWalkStepRaffleController { * @return */ @At - @ApiOperation("小程序获取查询是否可以抽奖") public Result checkRaffle(String activityId, String userId){ Dao dao = fitnessWalkStepRaffleService.dao(); FitnessWalkRaffleUser raffleUser = dao.fetch(FitnessWalkRaffleUser.class, @@ -179,8 +172,6 @@ public class FitnessWalkStepRaffleController { */ @At @Aop(TransAop.READ_COMMITTED) - @ApiOperation("抽奖成功后,修改数据库状态") - @SLog(tag = "健步走手动抽奖", msg = "抽奖成功后,修改数据库状态") public Result updateUserAward(String activityId, String userId) { fitnessWalkStepRaffleService.dao().update(FitnessWalkAwardUser.class, Chain.make("isRead", true).add("applyDate", DateUtil.date()), @@ -192,7 +183,6 @@ public class FitnessWalkStepRaffleController { // 增加积分 FitnessWalkAwardUser awardUser = dao.fetch(FitnessWalkAwardUser.class, Cnd.where("activityId", "=", activityId).and("userId", "=", userId)); IntegralDetail detail = new IntegralDetail(); - detail.setId(awardUser.getId()); detail.setYear(String.valueOf(DateUtil.thisYear())); detail.setUserId(awardUser.getUserId()); detail.setIntegral(String.valueOf(awardUser.getPoints())); @@ -200,17 +190,16 @@ public class FitnessWalkStepRaffleController { detail.setBizId(awardUser.getId()); detail.setIntegralTime(DateUtil.date()); dao.insert(detail); - return null; + return Result.success(); } @At @Ok("void") - @ApiOperation("导出中奖名单") @SaCheckPermission("fitnessWalk.stepRaffle") public void exportAwardExcel(String searchKeyword, String activityId, String lotteryTime, HttpServletResponse response) { - Cnd cnd = Cnd.NEW(); + CndPlus cnd = CndPlus.create(); cnd.andEX("activityId", "=", activityId); cnd.andEX("awardName", "=", lotteryTime); if (StrUtil.isNotBlank(searchKeyword)) { @@ -241,7 +230,6 @@ public class FitnessWalkStepRaffleController { ExportParams exportParams = new ExportParams(); exportParams.setType(ExcelType.XSSF); Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entityList, list); - workbook.write(response.getOutputStream()); CommonDownloadUtil.download("健步走中奖人员名单.xlsx", workbook, response); } catch (Exception e) { e.printStackTrace(); @@ -249,9 +237,10 @@ public class FitnessWalkStepRaffleController { } + + @At @Ok("void") - @ApiOperation("导出健步走达标人员名单") @SaCheckPermission("fitnessWalk.stepRaffle") public void exportExcel(String searchKeyword, String activityId, String unionId, String unitId, HttpServletResponse response) { @@ -259,6 +248,28 @@ public class FitnessWalkStepRaffleController { pageForm.setSearchKeyword(searchKeyword); Sql sql = fitnessWalkStepRaffleService.getRaffleSql(pageForm, activityId, unionId, unitId); + +// Sql sql = Sqls.create(""" +// SELECT +// ru.*, +// u.sex, +// u.unionname AS unionName, +// u.unitname AS unitName +// FROM +// fitness_walk_raffle_user ru left join `user` u on ru.userId = u.id +// $condition +// """); +// CndPlus cnd = CndPlus.create(); +// cnd.andEX("ru.activityId", "=", activityId); +// cnd.andEX("u.unionid", "=", unionId); +// cnd.andEX("u.unitid", "=", unitId); +// if (StrUtil.isNotBlank(searchKeyword)) { +// SqlExpressionGroup seg = new SqlExpressionGroup(); +// seg.orLike("ru.username", searchKeyword); +// seg.orLike("ru.loginName", searchKeyword); +// cnd.and(seg); +// } +// sql.setCondition(cnd); List list = fitnessWalkStepRaffleService.listMap(sql); List entityList = new ArrayList<>(); @@ -276,10 +287,17 @@ public class FitnessWalkStepRaffleController { ExportParams exportParams = new ExportParams(); exportParams.setType(ExcelType.XSSF); Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entityList, list); - workbook.write(response.getOutputStream()); CommonDownloadUtil.download("健步走达标人员名单.xlsx", workbook, response); } catch (Exception e) { e.printStackTrace(); } } } + + + + + + + + diff --git a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkStepRankingController.java b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkStepRankingController.java index 4afc3fe3..23f37eae 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkStepRankingController.java +++ b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkStepRankingController.java @@ -4,25 +4,29 @@ 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.dev33.satoken.annotation.SaCheckPermission; import cn.hutool.core.date.DateTime; import cn.hutool.core.date.DateUtil; -import com.budwk.app.base.annotation.SLog; -import com.budwk.app.base.page.Pagination; +import com.budwk.app.base.dao.CndPlus; 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.utils.CommonDownloadUtil; +import com.budwk.app.sys.models.Sys_user; +import com.budwk.app.base.annotation.SLog; +import com.budwk.app.web.commons.auth.utils.AuthUtil; import com.budwk.app.zhgh.activity.fitnesswalk.contants.FitnessWalkMode; import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkActivity; import com.budwk.app.zhgh.activity.fitnesswalk.service.FitnessWalkCommonService; -import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.apache.poi.ss.usermodel.Workbook; +import cn.dev33.satoken.annotation.SaCheckPermission; +import org.nutz.dao.Cnd; import org.nutz.dao.Sqls; import org.nutz.dao.sql.Sql; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.lang.Lang; +import org.nutz.lang.Strings; import org.nutz.lang.util.NutMap; import org.nutz.mvc.annotation.At; import org.nutz.mvc.annotation.Ok; @@ -32,14 +36,15 @@ import javax.servlet.http.HttpServletResponse; import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.Map; /** * 步数排行榜 */ -@Slf4j + @IocBean +@Slf4j @Ok("json") -@ApiOperation("健步走步数排行榜") @At("/platform/fitnessWalk/stepRanking") public class FitnessWalkStepRankingController { @@ -55,16 +60,15 @@ public class FitnessWalkStepRankingController { public void index() { } + @At - @ApiOperation("获取步数排行榜") @SaCheckPermission("fitnessWalk.stepRanking") public Result pageData(PageForm pageForm, @Param("activityId") String activityId, @Param("unionId") String unionId, @Param("unitId") String unitId, @Param("mode") String mode) { - Pagination pagination = fitnessWalkCommonService.getStepRankingPagination(pageForm, activityId, unionId, unitId, mode); - return Result.success().addData(pagination); + return Result.success(fitnessWalkCommonService.getStepRankingPagination(pageForm, activityId, unionId, unitId, mode)); } @@ -77,8 +81,7 @@ public class FitnessWalkStepRankingController { */ @At @Ok("json:full") - @ApiOperation("获取今日步数及活动范围内步数") - @SLog(type = "校工会特色活动", tag = "健身走-计步步数排行榜", msg = "小程序接口(获取今日步数及活动范围内步数)") + @SLog(type = "校工会特色活动", tag = "健身走-计步步数排行榜", msg = "小程序接口(获取今日步数及活动范围内步数)", param = true, result = true) public Result getUserStepRanking(String activityId, String mode, Integer pageNumber, Integer pageSize) { if ("day".equals(mode)) { Sql sql = Sqls.create(""" @@ -97,10 +100,8 @@ public class FitnessWalkStepRankingController { """); sql.setParam("activityId", activityId); sql.setParam("today", DateUtil.today()); - Pagination pagination = baseService.listPageMap(pageNumber, 50, sql); - return Result.success().addData(pagination); + return Result.success(baseService.listPageMap(pageNumber, 50, sql)); } else if ("week".equals(mode)) { - // 周榜 Date startOfWeek = DateUtil.beginOfWeek(new Date()); Date endOfWeek = DateUtil.endOfWeek(new Date()); @@ -124,10 +125,8 @@ public class FitnessWalkStepRankingController { sql.setParam("activityId", activityId); sql.setParam("startTime", startOfWeek); sql.setParam("endTime", endOfWeek); - Pagination pagination = baseService.listPageMap(pageNumber, 50, sql); - return Result.success().addData(pagination); + return Result.success(baseService.listPageMap(pageNumber, 50, sql)); } else if ("month".equals(mode)) { - // 月榜 Date firstDayOfMonth = DateUtil.beginOfMonth(new Date()); Date lastDayOfMonth = DateUtil.endOfMonth(new Date()); Sql sql = Sqls.create(""" @@ -150,10 +149,8 @@ public class FitnessWalkStepRankingController { sql.setParam("activityId", activityId); sql.setParam("startTime", firstDayOfMonth); sql.setParam("endTime", lastDayOfMonth); - Pagination pagination = baseService.listPageMap(pageNumber, 50, sql); - return Result.success().addData(pagination); + return Result.success(baseService.listPageMap(pageNumber, 50, sql)); } else if ("all".equals(mode)) { - // 总达标榜 FitnessWalkActivity activity = fitnessWalkCommonService.getActivity(activityId); DateTime startTime = DateUtil.date(activity.getStartTime()); DateTime endTime = DateUtil.date(activity.getEndTime()); @@ -186,10 +183,8 @@ public class FitnessWalkStepRankingController { sql.setParam("activityId", activityId); sql.setParam("startTime", startTime); sql.setParam("endTime", endTime); - Pagination pagination = baseService.listPageMap(pageNumber, 50, sql); - return Result.success().addData(pagination); + return Result.success(baseService.listPageMap(pageNumber, 50, sql)); } else if ("points".equals(mode)) { - // 积分榜 FitnessWalkActivity activity = fitnessWalkCommonService.getActivity(activityId); DateTime startTime = DateUtil.date(activity.getStartTime()); DateTime endTime = DateUtil.date(activity.getEndTime()); @@ -222,19 +217,73 @@ public class FitnessWalkStepRankingController { sql.setParam("activityId", activityId); sql.setParam("startTime", startTime); sql.setParam("endTime", endTime); - Pagination pagination = baseService.listPageMap(pageNumber, 50, sql); - return Result.success().addData(pagination); + return Result.success(baseService.listPageMap(pageNumber, 50, sql)); } else { - return null; + return Result.error("不支持的排行榜模式"); + } + } + + @At + @Ok("void") + @SLog(type = "校工会特色活动", tag = "健身走-计步步数排行榜", msg = "步数排行榜导出", param = true, result = true) + @SaCheckPermission("fitnessWalk.stepRanking") + public void exportExcel2(@Param("searchName") String searchName, + @Param("searchKeyword") String searchKeyword, + @Param("activityId") String activityId, + @Param("unionId") String unionId, + @Param("unitId") String unitId, + HttpServletResponse response) { + FitnessWalkActivity activity = fitnessWalkCommonService.getActivity(activityId); + CndPlus cnd = CndPlus.create(); + Sql sql = Sqls.create(""" + SELECT + u.id, + COALESCE(SUM(fws.step), 0) AS total_steps, + u.username, + u.loginname, + u.unionname, + u.unitname + FROM + fitness_walk_step fws + LEFT JOIN `user` u ON u.id = fws.userId + $condition + """); + + cnd.and("fws.activityId", "=", activityId); + cnd.andEX("DATE(fws.applyDate)", ">=", DateUtil.format(DateUtil.date(activity.getStartTime()), "yyyy-MM-dd")); + cnd.andEX("DATE(fws.applyDate)", "<=", DateUtil.format(DateUtil.date(activity.getEndTime()), "yyyy-MM-dd")); + cnd.andEX("u.unionId", "=", unionId); + cnd.andEX("u.unitId", "=", unitId); + cnd.groupBy("fws.userId"); + cnd.desc("total_steps"); + sql.setCondition(cnd); + + List stepRankingList = baseService.listMap(sql); + + List entityList = new ArrayList<>(); + ExcelExportEntity no = new ExcelExportEntity("序号", "no", 10); + no.setFormat("isAddIndex"); + entityList.add(no); + entityList.add(new ExcelExportEntity("姓名", "username", 20)); + entityList.add(new ExcelExportEntity("工号", "loginname", 20)); + entityList.add(new ExcelExportEntity("分工会", "unionname", 20)); + entityList.add(new ExcelExportEntity("单位", "unitname", 20)); + entityList.add(new ExcelExportEntity("活动期间总步数", "total_steps", 20)); + try { + ExportParams exportParams = new ExportParams(); + exportParams.setType(ExcelType.XSSF); + Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entityList, stepRankingList); + CommonDownloadUtil.download("活动期间总步数榜.xlsx", workbook, response); + } catch (Exception e) { + e.printStackTrace(); } } @At @Ok("void") - @ApiOperation("步数排行榜导出") - @SaCheckPermission("fitnessWalk.stepRanking") @SLog(type = "校工会特色活动", tag = "健身走-计步步数排行榜", msg = "步数排行榜导出", param = true, result = true) + @SaCheckPermission("fitnessWalk.stepRanking") public void exportExcel(@Param("searchName") String searchName, @Param("searchKeyword") String searchKeyword, @Param("activityId") String activityId, @@ -254,21 +303,25 @@ public class FitnessWalkStepRankingController { entityList.add(new ExcelExportEntity("单位", "unitname", 20)); entityList.add(new ExcelExportEntity("today".equals(mode) ? "今日步数" : "活动期间总步数", "total_steps", 20)); try { - String title = ("today".equals(mode) ? DateUtil.format(new Date(), "yyyy年MM月dd日") : "活动期间总") + "步数榜.xlsx"; + String fileName = ("today".equals(mode) ? DateUtil.format(new Date(), "yyyy年MM月dd日") : "活动期间总") + "步数榜.xlsx"; ExportParams exportParams = new ExportParams(); exportParams.setType(ExcelType.XSSF); Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entityList, stepRankingList); - workbook.write(response.getOutputStream()); - CommonDownloadUtil.download(title, workbook, response); + CommonDownloadUtil.download(fileName, workbook, response); } catch (Exception e) { e.printStackTrace(); } } @At - @ApiOperation("获取联级活动") @SaCheckPermission("fitnessWalk.stepRanking") public Result getCascadersActivity(@Param("year") int year) { - return Result.success().addData(fitnessWalkCommonService.cascaderActivity(year, FitnessWalkMode.STEP_COUNT)); + return Result.success(fitnessWalkCommonService.cascaderActivity(year, FitnessWalkMode.STEP_COUNT)); } } + + + + + + diff --git a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkStepStatisticsController.java b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkStepStatisticsController.java index 0763ad07..ff46f554 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkStepStatisticsController.java +++ b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkStepStatisticsController.java @@ -5,19 +5,24 @@ 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.dev33.satoken.annotation.SaCheckPermission; +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.result.Result; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.budwk.app.base.param.PageForm; import com.budwk.app.base.service.BaseService; import com.budwk.app.base.utils.CommonDownloadUtil; import com.budwk.app.zhgh.activity.fitnesswalk.contants.FitnessWalkMode; +import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkActivity; +import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkStep; import com.budwk.app.zhgh.activity.fitnesswalk.service.FitnessWalkCommonService; import com.budwk.app.zhgh.activity.fitnesswalk.utils.WeAppCloudUtil; -import io.swagger.annotations.ApiOperation; import org.apache.poi.ss.usermodel.Workbook; +import cn.dev33.satoken.annotation.SaCheckPermission; import org.nutz.dao.Cnd; +import com.budwk.app.base.dao.CndPlus; import org.nutz.dao.Sqls; import org.nutz.dao.sql.Sql; import org.nutz.dao.util.cri.SqlExpressionGroup; @@ -25,27 +30,30 @@ import org.nutz.integration.jedis.RedisService; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.lang.Lang; +import org.nutz.lang.Strings; import org.nutz.lang.util.NutMap; import org.nutz.mvc.annotation.At; import org.nutz.mvc.annotation.Ok; import org.nutz.mvc.annotation.Param; import javax.servlet.http.HttpServletResponse; -import java.util.ArrayList; -import java.util.List; +import java.util.*; +import java.util.stream.Collectors; @IocBean @Ok("json") -@ApiOperation("计步步数统计") @At("/platform/fitnessWalk/stepStatistics") public class FitnessWalkStepStatisticsController { @Inject private BaseService baseService; - @Inject - private RedisService redisService; + @Inject private WeAppCloudUtil weAppCloudUtil; + + @Inject + private RedisService redisService; + @Inject private FitnessWalkCommonService fitnessWalkCommonService; @@ -55,30 +63,26 @@ public class FitnessWalkStepStatisticsController { public void index() { } - /** - * 公共sql - * @return - */ + private String getSql() { return """ SELECT u.loginname, u.username, - u.unionName AS unionname, - u.unitName AS unitname, + u.unionname, + u.unitname, al.applyDate, al.step, date_format(al.applyDate,'%Y-%m-%d') as prizeData FROM `fitness_walk_step` al - LEFT JOIN `vw_user` u ON u.id = al.userId + LEFT JOIN `user` u ON u.id = al.userId $condition """; } @At @Ok("json:full") - @ApiOperation("计步步数统计") @SaCheckPermission("fitnessWalk.stepStatistics") public Result pageData(PageForm pageForm, @Param("year") Integer year, @@ -86,7 +90,7 @@ public class FitnessWalkStepStatisticsController { @Param("activityDate") String[] activityDate, @Param("unionId") String unionId, @Param("unitId") String unitId) { - Cnd cnd = Cnd.NEW(); + CndPlus cnd = CndPlus.create(); Sql sql = Sqls.create(getSql()); if (Lang.isNotEmpty(activityDate)) { cnd.and("date_format(al.applyDate,'%Y-%m-%d')", ">=", activityDate[0]); @@ -107,28 +111,23 @@ public class FitnessWalkStepStatisticsController { cnd.groupBy("al.applyDate", "al.userId"); sql.setCondition(cnd); - Pagination pagination = baseService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); - return Result.success().addData(pagination); + return Result.success(baseService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql)); } - @At - @ApiOperation("获取联级活动") @SaCheckPermission("fitnessWalk.stepStatistics") public Result getCascadersActivity(@Param("year") int year) { - return Result.success().addData(fitnessWalkCommonService.cascaderActivity(year, FitnessWalkMode.STEP_COUNT)); + return Result.success(fitnessWalkCommonService.cascaderActivity(year, FitnessWalkMode.STEP_COUNT)); } - - @Ok("void") @At("/exportExcel") - @ApiOperation("计步步数统计表导出") + @Ok("void") @SaCheckPermission("fitnessWalk.stepStatistics") public void exportExcel(@Param("activityId") String activityId, @Param("activityDate") String[] activityDate, @Param("unionId") String unionId, @Param("unitId") String unitId, HttpServletResponse response) { - Cnd cnd = Cnd.NEW(); + CndPlus cnd = CndPlus.create(); Sql sql = Sqls.create(getSql()); if (activityDate.length > 0) { cnd.and("date_format(al.applyDate,'%Y-%m-%d')", ">=", activityDate[0]); @@ -156,10 +155,15 @@ public class FitnessWalkStepStatisticsController { ExportParams exportParams = new ExportParams(); exportParams.setType(ExcelType.XSSF); Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entityList, exportList); - workbook.write(response.getOutputStream()); CommonDownloadUtil.download("计步步数统计表.xlsx", workbook, response); } catch (Exception e) { e.printStackTrace(); } } } + + + + + + diff --git a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkStepWiningController.java b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkStepWiningController.java index 5ca384d5..1ffa7345 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkStepWiningController.java +++ b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkStepWiningController.java @@ -2,22 +2,29 @@ package com.budwk.app.zhgh.activity.fitnesswalk.controller; import cn.afterturn.easypoi.excel.ExcelExportUtil; import cn.afterturn.easypoi.excel.entity.ExportParams; +import cn.afterturn.easypoi.excel.entity.enmus.ExcelType; import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity; -import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.StrUtil; -import com.budwk.app.base.annotation.SLog; import com.budwk.app.base.result.Result; +import com.budwk.app.base.param.PageForm; import com.budwk.app.base.service.BaseService; +import com.budwk.app.base.annotation.SLog; import com.budwk.app.zhgh.activity.fitnesswalk.contants.FitnessWalkMode; +import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkActivity; import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkAwardUser; import com.budwk.app.zhgh.activity.fitnesswalk.service.FitnessWalkCommonService; -import io.swagger.annotations.ApiOperation; import org.apache.poi.ss.usermodel.Workbook; +import cn.dev33.satoken.annotation.SaCheckPermission; import org.nutz.dao.Cnd; +import com.budwk.app.base.dao.CndPlus; +import org.nutz.dao.Sqls; +import org.nutz.dao.sql.Sql; import org.nutz.dao.util.cri.SqlExpressionGroup; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.lang.Lang; import org.nutz.lang.util.NutMap; import org.nutz.mvc.annotation.At; import org.nutz.mvc.annotation.Ok; @@ -27,16 +34,17 @@ import javax.servlet.http.HttpServletResponse; import java.io.BufferedOutputStream; import java.net.URLEncoder; import java.util.ArrayList; +import java.util.Date; import java.util.List; +import java.util.stream.Collectors; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; /** * 用户中奖榜 */ -@IocBean @Ok("json") -@ApiOperation("小程序计步步数中奖榜") +@IocBean @At("/platform/fitnessWalk/stepWining") public class FitnessWalkStepWiningController { @@ -62,25 +70,22 @@ public class FitnessWalkStepWiningController { */ @At @Ok("json:full") - @ApiOperation("小程序获取中奖榜") - @SLog(type = "校工会特色活动",tag = "健身走-计步步数中奖榜",msg = "获取中奖榜") + @SLog(type = "校工会特色活动",tag = "健身走-计步步数中奖榜",msg = "获取中奖榜",param = true,result = true) public Result getUserStepWining(String activityId) { - return Result.success().addData(fitnessWalkCommonService.getWiningRankingUserList(activityId)); + return Result.success(fitnessWalkCommonService.getWiningRankingUserList(activityId)); } @At - @ApiOperation("获取联级活动") @SaCheckPermission("fitnessWalk.stepWining") public Result getCascadersActivity(@Param("year") int year) { - return Result.success().addData(fitnessWalkCommonService.cascaderActivity(year, FitnessWalkMode.STEP_COUNT)); + return Result.success(fitnessWalkCommonService.cascaderActivity(year, FitnessWalkMode.STEP_COUNT)); } @At @Ok("void") - @ApiOperation("小程序中奖榜导出") + @SLog(type = "校工会特色活动",tag = "健身走-计步步数中奖榜",msg = "导出中奖榜",param = true,result = true) @SaCheckPermission("fitnessWalk.stepWining") - @SLog(type = "校工会特色活动",tag = "健身走-计步步数中奖榜",msg = "导出中奖榜") public void exportUserStepWining(String activityId, HttpServletResponse response){ List winingRankingUserList = fitnessWalkCommonService.getWiningRankingUserList(activityId); List entityList = new ArrayList<>(); @@ -103,7 +108,9 @@ public class FitnessWalkStepWiningController { List awardList = map.getAsList("awardList", NutMap.class); awardList.forEach(v->v.setv("applyDate",DateUtil.format(v.getTime("applyDate"),"yyyy-MM-dd"))); zipOutputStream.putNextEntry(new ZipEntry(map.getString("title") + ".xlsx")); - Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), entityList, awardList); + ExportParams exportParams = new ExportParams(); + exportParams.setType(ExcelType.XSSF); + Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entityList, awardList); workbook.write(zipOutputStream); zipOutputStream.closeEntry(); } @@ -115,7 +122,6 @@ public class FitnessWalkStepWiningController { } - /** * 中奖奖项列表 * @@ -123,7 +129,6 @@ public class FitnessWalkStepWiningController { * @return */ @At - @ApiOperation("小程序获取中奖奖项列表") public Result prizeOption(String activityId) { List list = fitnessWalkCommonService.prizeOption(activityId); return Result.success(list); @@ -136,9 +141,9 @@ public class FitnessWalkStepWiningController { * @return */ @At - @ApiOperation("小程序获取中奖用户") public Result prizeUsers(String lotteryTime, String activityId, String searchKeyword) { - Cnd cnd = Cnd.NEW(); + + CndPlus cnd = CndPlus.create(); cnd.andEX("activityId", "=", activityId); cnd.andEX("awardName", "=", lotteryTime); if (StrUtil.isNotBlank(searchKeyword)) { @@ -149,7 +154,15 @@ public class FitnessWalkStepWiningController { } cnd.and("isRead", "=", true); List awardUserList = fitnessWalkCommonService.dao().query(FitnessWalkAwardUser.class, cnd); +// String lotteryDate = lotteryTime.substring(0, 10); +// List awardUserList = fitnessWalkCommonService.dao().query(FitnessWalkAwardUser.class, +// Cnd.where("activityId", "=", activityId).and("date(applyDate)", "=", lotteryDate)); return Result.success(awardUserList); } } + + + + + diff --git a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkUserPunchStatisticsController.java b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkUserPunchStatisticsController.java new file mode 100644 index 00000000..d38cf5f4 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/controller/FitnessWalkUserPunchStatisticsController.java @@ -0,0 +1,105 @@ +package com.budwk.app.zhgh.activity.fitnesswalk.controller; + +import cn.afterturn.easypoi.excel.ExcelExportUtil; +import cn.afterturn.easypoi.excel.entity.ExportParams; +import cn.afterturn.easypoi.excel.entity.enmus.ExcelType; +import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity; +import com.budwk.app.base.result.Result; +import com.budwk.app.base.page.Pagination; +import com.budwk.app.base.utils.CommonDownloadUtil; +import com.budwk.app.zhgh.activity.fitnesswalk.service.FitnessWalkPunchStatisticsService; +import org.apache.poi.ss.usermodel.Workbook; +import cn.dev33.satoken.annotation.SaCheckPermission; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.lang.util.NutMap; +import org.nutz.mvc.annotation.At; +import org.nutz.mvc.annotation.Ok; +import org.nutz.mvc.annotation.Param; + +import javax.servlet.http.HttpServletResponse; +import java.util.ArrayList; +import java.util.List; + +/** + * 人员打卡情况。 + * 该页面按人员维度展示所选活动范围内的打卡完成情况,支持主活动聚合查询和分活动单独查询。 + */ +@IocBean +@Ok("json") +@At("/platform/fitnessWalk/userPunchStatistics") +public class FitnessWalkUserPunchStatisticsController { + + @Inject + private FitnessWalkPunchStatisticsService fitnessWalkPunchStatisticsService; + + @At("") + @Ok("beetl:/platform/zhgh/activity/fitnesswalk/userPunchStatistics.html") + @SaCheckPermission("fitnessWalk.userPunchStatistics") + public void index() { + } + + /** + * 查询活动级联选项。 + * 这里只返回打卡模式活动,供前端年份切换后联动刷新主活动和分活动下拉。 + */ + @At + @SaCheckPermission("fitnessWalk.userPunchStatistics") + public Result getCascadersActivity(@Param("year") int year) { + return Result.success(fitnessWalkPunchStatisticsService.userPunchCascaderActivity(year)); + } + + /** + * 查询人员打卡情况列表。 + * 当前接口按人员维度返回点位总数、已打卡点位数以及是否全部完成全部点位。 + * 当页面只选择主活动且该主活动存在 relationActivityId 时,还支持按达标/未达标筛选人员。 + */ + @At + @Ok("json:full") + @SaCheckPermission("fitnessWalk.userPunchStatistics") + public Result pageData(String mainActivityId, String subActivityId, String activityId, String unionId, String searchKeyword, + String qualifiedStatus, @Param("pageNumber") int pageNumber, @Param("pageSize") int pageSize) { + Pagination pagination = fitnessWalkPunchStatisticsService.userPunchPageData( + mainActivityId, subActivityId, activityId, unionId, searchKeyword, qualifiedStatus, pageNumber, pageSize + ); + return Result.success(pagination); + } + + /** + * 导出人员打卡情况。 + * 导出条件与页面当前可见筛选项保持一致,确保“所见即所得”。 + */ + @At + @Ok("void") + @SaCheckPermission("fitnessWalk.userPunchStatistics") + public void exportExcel(String mainActivityId, String subActivityId, String activityId, String unionId, String searchKeyword, String qualifiedStatus, + HttpServletResponse response) { + List list = fitnessWalkPunchStatisticsService.userPunchExportData(mainActivityId, subActivityId, activityId, unionId, searchKeyword, qualifiedStatus); + List entityList = new ArrayList<>(); + ExcelExportEntity no = new ExcelExportEntity("序号", "no", 10); + no.setFormat("isAddIndex"); + entityList.add(no); + entityList.add(new ExcelExportEntity("姓名", "username", 20)); + entityList.add(new ExcelExportEntity("工号", "loginname", 20)); + entityList.add(new ExcelExportEntity("分工会", "unionname", 20)); + entityList.add(new ExcelExportEntity("单位", "unitname", 40)); + entityList.add(new ExcelExportEntity("达标几个活动", "qualifiedActivityNum", 20)); + entityList.add(new ExcelExportEntity("点位总数", "pts_size", 20)); + entityList.add(new ExcelExportEntity("已打卡点位数", "sign_size", 20)); + entityList.add(new ExcelExportEntity("是否全部打卡完全部点位", "isAllSigned", 24)); + + try { + ExportParams exportParams = new ExportParams(); + exportParams.setType(ExcelType.XSSF); + Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entityList, list); + CommonDownloadUtil.download("健步走人员打卡情况.xlsx", workbook, response); + } catch (Exception e) { + e.printStackTrace(); + } + } +} + + + + + diff --git a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/model/FitnessWalkActivity.java b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/model/FitnessWalkActivity.java index 6b4823de..35b18e86 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/model/FitnessWalkActivity.java +++ b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/model/FitnessWalkActivity.java @@ -1,6 +1,5 @@ package com.budwk.app.zhgh.activity.fitnesswalk.model; -import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.io.Serializable; @@ -10,159 +9,149 @@ import java.util.List; @Data public class FitnessWalkActivity { - @ApiModelProperty(value = "小程序活动Id") private String _id; - // 主活动名称 - @ApiModelProperty(value = "主活动名称") + private String masterActivityName; - // 分支活动名称 - @ApiModelProperty(value = "分支活动名称") + private String branchActivityName; - // 活动地址 - @ApiModelProperty(value = "活动地址") + + // 关联活动ID,创建/编辑时通过下拉选择后回填 + private String relationActivityId; + + // 关联活动名称,保存时由服务层根据关联活动ID统一回填 + private String relationActivityName; + + // 关联福利项目ID,创建/编辑时通过福利下拉选择后回填 + private String welfareId; + + // 关联活动对应的达标活动数量,保存时由服务层统一回填 + private Integer relationStandardActivityNum; + private String address; - @ApiModelProperty(value = "活动礼品") private String giftName; - @ApiModelProperty(value = "活动分组Id") private String groupId; - @ApiModelProperty(value = "创建分工会Id") + // 是否自动连续创建活动 + private Boolean autoContinuousCreate; + + // 连续创建活动日期区间,页面开启自动连续创建后由用户选择开始和结束日期 + private Long[] continuousCreateDate; + + // 自动创建来源活动ID,仅自动复制出的活动会写入,用于编辑原活动时识别已落地的历史活动 + private String autoCreateSourceActivityId; + + // 自动创建对应的目标活动日期(当天00:00:00时间戳),用于避免同一天活动被重复生成 + private Long autoCreateTargetDate; + private String unionId; - @ApiModelProperty(value = "活动模式") private String activityModel; - @ApiModelProperty(value = "活动报名时间范围,时间戳模式") private Long[] applyTime; - @ApiModelProperty(value = "活动报名开始时间") private Long applyStartTime; - @ApiModelProperty(value = "活动报名结束时间") private Long applyEndTime; - @ApiModelProperty(value = "活动时间范围,时间戳模式") private Long[] time; - @ApiModelProperty(value = "活动开始时间") private Long startTime; - @ApiModelProperty(value = "活动结束时间") private Long endTime; - @ApiModelProperty(value = "活动描述") private String note; - @ApiModelProperty(value = "活动封面") private String cover; - @ApiModelProperty(value = "活动临时封面") private String tempCover; - @ApiModelProperty(value = "是否需要答题") private Boolean isQuestion; - @ApiModelProperty(value = "答题活动Id") private String questionId; - @ApiModelProperty(value = "步数模式") private String speedControlMode; //打卡完成之后 gift_voucher:礼品券 cert:完赛证书 null啥也不干 - @ApiModelProperty(value = "打卡完成之后操作") private String punchFinishAfter; //完赛证书 - @ApiModelProperty(value = "完赛证书") private String completionCertificateCover; //完赛证书临时url - @ApiModelProperty(value = "完赛证书临时url") private String tempCompletionCertificateCover; //是否抽奖 - @ApiModelProperty(value = "是否抽奖") private Boolean isLottery; //打卡模式奖品列表 - @ApiModelProperty(value = "打卡完成之后抽奖信息") private List punchLotteryPrizes; - @ApiModelProperty(value = "是否显示礼品券") private Boolean showGiftTicket; - @ApiModelProperty(value = "知识卡的图片") private List pts; - @ApiModelProperty(value = "打开点位,线路") private List linePoints; - @ApiModelProperty(value = "打开点位,线路2") private List linePoints2; //是否按顺序签到 - @ApiModelProperty(value = "是否按顺序签到") private Boolean isOrderSignIn; //是否多条线路 - @ApiModelProperty(value = "是否多条线路") private Boolean isMultipleLines; //抽奖模式 none无 everyday每天 monthly每月 custom自定义 - @ApiModelProperty(value = "抽奖模式") private String lotteryMode; //每月抽奖模式抽奖时间 - @ApiModelProperty(value = "每月抽奖模式抽奖时间") private List everyMonthLotteryTime; - @ApiModelProperty(value = "自定义模式抽奖抽奖时间规则") + //自定义模式抽奖抽奖时间规则 private List customLotteryRules; - @ApiModelProperty(value = "每日模式抽奖抽奖时间规则") + //每日模式抽奖抽奖时间规则 private DayLotteryRule dayLotteryRule; - @ApiModelProperty(value = "随机图片数组") + //随机图片数组 private List randomPics; - @ApiModelProperty(value = "模板随机图片数组") private List templateRandomPics; - @ApiModelProperty(value = "外链地址") + //外链地址 private String externalLinkAddress; - @ApiModelProperty(value = "是否开启距离限制") + //是否开启距离限制 private Boolean enableDistance; - @ApiModelProperty(value = "签到几个点位算完成") + //签到几个点位算完成 private Integer finishPointNum; - @ApiModelProperty(value = "是否自动抽奖") + // 是否自动抽奖 private Boolean isAutoLottery; - @ApiModelProperty(value = "手动抽奖,抽奖资格天数") + // 手动抽奖,抽奖资格天数 private Integer lotteryQualificationDays; - - @ApiModelProperty(value = "手动抽奖,抽奖资格步数") + // 手动抽奖,抽奖资格步数 private Integer lotteryQualificationSteps; - - @ApiModelProperty(value = "是否启用积分") + // 是否启用积分 private Boolean isPoints; - - @ApiModelProperty(value = "达标获得积分") + // 达标获得积分 private Integer lotteryQualificationPoints; - - @ApiModelProperty(value = "额外积分的达标步数") + // 额外积分的达标步数 private Integer otherSteps; - - @ApiModelProperty(value = "根据额外步数来计算的额外积分") + // 根据额外步数来计算的额外积分 private Integer otherPoints; - - @ApiModelProperty(value = "封顶积分") + // 封顶积分 private Integer maxPoints; + // 创建时间,用于筛选当年已创建的活动 + private Long createdAt; + + // 修改时间,保存编辑时同步更新 + private Long updatedAt; + @Data public static class Point implements Serializable { private String type; @@ -233,3 +222,5 @@ public class FitnessWalkActivity { } } + + diff --git a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/model/FitnessWalkActivityRelationProject.java b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/model/FitnessWalkActivityRelationProject.java new file mode 100644 index 00000000..630e0846 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/model/FitnessWalkActivityRelationProject.java @@ -0,0 +1,43 @@ +package com.budwk.app.zhgh.activity.fitnesswalk.model; + +import com.budwk.app.base.model.BaseModel; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.nutz.dao.entity.annotation.ColDefine; +import org.nutz.dao.entity.annotation.ColType; +import org.nutz.dao.entity.annotation.Column; +import org.nutz.dao.entity.annotation.Comment; +import org.nutz.dao.entity.annotation.Name; +import org.nutz.dao.entity.annotation.Table; +import org.nutz.dao.interceptor.annotation.PrevInsert; + +/** + * 健步走活动关联项目。 + * 该表用于保存用户手工创建的“主活动名称 + 达标活动数量”配置,数据统一落在 MySQL 中。 + */ +@Data +@Table("fitness_walk_activity_relation_project") +@EqualsAndHashCode(callSuper = true) +public class FitnessWalkActivityRelationProject extends BaseModel { + + private static final long serialVersionUID = 1L; + + @Name + @Column + @ColDefine(type = ColType.VARCHAR, width = 32) + @PrevInsert(uu32 = true) + @Comment("主键ID") + private String id; + + @Column + @ColDefine(type = ColType.VARCHAR, width = 100) + @Comment("主活动名称") + private String masterActivityName; + + @Column + @ColDefine(type = ColType.INT) + @Comment("达标活动数量") + private Integer standardActivityNum; +} + + diff --git a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/model/FitnessWalkAwardUser.java b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/model/FitnessWalkAwardUser.java index 20c86852..77c58257 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/model/FitnessWalkAwardUser.java +++ b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/model/FitnessWalkAwardUser.java @@ -99,3 +99,5 @@ public class FitnessWalkAwardUser extends BaseModel { @Comment("积分") private Integer points; } + + diff --git a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/model/FitnessWalkPunchLottery.java b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/model/FitnessWalkPunchLottery.java index bfaac069..ebb25ab6 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/model/FitnessWalkPunchLottery.java +++ b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/model/FitnessWalkPunchLottery.java @@ -76,3 +76,5 @@ public class FitnessWalkPunchLottery extends BaseModel { @Comment("是否兑换") private Boolean isExchange; } + + diff --git a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/model/FitnessWalkRaffleRules.java b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/model/FitnessWalkRaffleRules.java index 4898652f..e6b43a5f 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/model/FitnessWalkRaffleRules.java +++ b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/model/FitnessWalkRaffleRules.java @@ -53,3 +53,5 @@ public class FitnessWalkRaffleRules extends BaseModel { private Boolean isSimultaneously; } } + + diff --git a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/model/FitnessWalkRaffleUser.java b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/model/FitnessWalkRaffleUser.java index 19e92103..49c50f3d 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/model/FitnessWalkRaffleUser.java +++ b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/model/FitnessWalkRaffleUser.java @@ -59,3 +59,5 @@ public class FitnessWalkRaffleUser extends BaseModel { @Comment("是否已抽奖") private Boolean isRaffle; } + + diff --git a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/model/FitnessWalkStep.java b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/model/FitnessWalkStep.java index a941f73c..16b6c400 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/model/FitnessWalkStep.java +++ b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/model/FitnessWalkStep.java @@ -48,3 +48,5 @@ public class FitnessWalkStep extends BaseModel implements Serializable { @Comment("时间") private Date applyDate; } + + diff --git a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/FitnessWalkActivityRelationProjectService.java b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/FitnessWalkActivityRelationProjectService.java new file mode 100644 index 00000000..7d53fbd2 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/FitnessWalkActivityRelationProjectService.java @@ -0,0 +1,52 @@ +package com.budwk.app.zhgh.activity.fitnesswalk.service; + +import com.budwk.app.base.result.Result; +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.fitnesswalk.model.FitnessWalkActivityRelationProject; +import org.nutz.lang.util.NutMap; + +import java.util.List; + +/** + * 健步走活动关联项目 service。 + * 负责关联项目在 MySQL 中的分页查询、保存和删除。 + */ +public interface FitnessWalkActivityRelationProjectService extends BaseService { + + /** + * 查询关联项目列表。 + * + * @param pageForm 分页参数 + * @return 分页结果 + */ + Pagination pageData(PageForm pageForm); + + /** + * 保存关联项目。 + * + * @param relationProject 关联项目 + * @return 保存结果 + */ + Result saveRelationProject(FitnessWalkActivityRelationProject relationProject); + + /** + * 删除关联项目。 + * + * @param id 主键ID + * @return 删除结果 + */ + Result deleteRelationProject(String id); + + /** + * 查询某个关联项目下的活动列表。 + * 这里按活动数据中保存的关联ID查询,保证查看结果与活动实际保存的数据一致。 + * + * @param id 关联项目ID + * @return 关联活动列表 + */ + List getRelationActivityList(String id); +} + + diff --git a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/FitnessWalkActivityService.java b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/FitnessWalkActivityService.java new file mode 100644 index 00000000..218a2301 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/FitnessWalkActivityService.java @@ -0,0 +1,36 @@ +package com.budwk.app.zhgh.activity.fitnesswalk.service; + +import com.budwk.app.base.service.BaseService; +import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkActivityRelationProject; +import org.nutz.lang.util.NutMap; + +import java.util.List; + +/** + * 健步走活动管理 service。 + * 统一负责活动在 MySQL 中的查询、保存、更新、删除以及云库历史数据同步。 + */ +public interface FitnessWalkActivityService extends BaseService { + + /** + * 获取当前年份可关联的活动下拉数据。 + * 下拉内容统一返回活动ID、活动名称和达标活动数量,供创建/编辑页面直接使用。 + * + * @return 关联活动下拉数据 + */ + List getCurrentYearRelationActivityOptions(); + + /** + * 获取福利项目下拉数据。 + * 创建/编辑活动时统一从 service 层查询福利项目,避免页面直接跨模块拼查询。 + * + * @param year 年份,为空时默认当前年份 + * @return 福利项目下拉数据,仅返回页面下拉所需字段 + */ + List getWelfareProjectOptions(Integer year); + + + +} + + diff --git a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/FitnessWalkCommonService.java b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/FitnessWalkCommonService.java index 30e03419..137c1991 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/FitnessWalkCommonService.java +++ b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/FitnessWalkCommonService.java @@ -53,6 +53,13 @@ public interface FitnessWalkCommonService extends BaseService prizeOption(String activityId); } + + diff --git a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/FitnessWalkPunchStatisticsService.java b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/FitnessWalkPunchStatisticsService.java index 8e626fad..dbcc0bbe 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/FitnessWalkPunchStatisticsService.java +++ b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/FitnessWalkPunchStatisticsService.java @@ -1,7 +1,9 @@ package com.budwk.app.zhgh.activity.fitnesswalk.service; -import cn.hutool.json.JSONObject; +import com.budwk.app.base.page.Pagination; +import com.alibaba.fastjson.JSONObject; import com.budwk.app.base.service.BaseService; +import com.budwk.app.zhgh.activity.fitnesswalk.contants.Cascader; import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkAwardUser; import org.nutz.lang.util.NutMap; @@ -9,12 +11,101 @@ import java.util.List; public interface FitnessWalkPunchStatisticsService extends BaseService { - List unionRegCheckInNum(String activityId, String unionId); + /** + * 查询人员打卡统计页专用活动级联。 + * 父级使用稳定的关联项目ID,不复用公共活动级联,避免父级 value 随最新活动变化。 + * + * @param year 查询年份 + * @return 人员打卡统计页活动级联 + */ + List userPunchCascaderActivity(int year); - List unionRegistrationData(String activityId, String unionId); + /** + * 查询分工会报名人数、打卡人数。 + * 选择分活动时只查当前分活动;只选择主活动时按主活动关联项目下的全部活动汇总查询。 + * + * @param mainActivityId 主活动ID + * @param subActivityId 分活动ID + * @param activityId 兼容原页面保留的最终活动ID + * @param unionId 分工会ID + * @return 分工会统计结果 + */ + List unionRegCheckInNum(String mainActivityId, String subActivityId, String activityId, String unionId); - List unionCheckInData(String activityId, String unionId); + /** + * 查询某个分工会的报名明细。 + * 选择分活动时只查当前分活动;只选择主活动时按主活动关联项目下的全部活动汇总查询。 + * + * @param mainActivityId 主活动ID + * @param subActivityId 分活动ID + * @param activityId 兼容原页面保留的最终活动ID + * @param unionId 分工会ID + * @return 报名明细 + */ + List unionRegistrationData(String mainActivityId, String subActivityId, String activityId, String unionId); - List exportNotGetGiftVoucherUsers(String activityId); - List exportNotUseGiftVoucherUsers(String activityId); + /** + * 查询某个分工会的打卡明细。 + * 选择分活动时只查当前分活动;只选择主活动时按主活动关联项目下的全部活动汇总查询。 + * + * @param mainActivityId 主活动ID + * @param subActivityId 分活动ID + * @param activityId 兼容原页面保留的最终活动ID + * @param unionId 分工会ID + * @return 打卡明细 + */ + List unionCheckInData(String mainActivityId, String subActivityId, String activityId, String unionId); + + /** + * 导出未取得礼品券用户。 + * + * @param mainActivityId 主活动ID + * @param subActivityId 分活动ID + * @param activityId 兼容原页面保留的最终活动ID + * @return 未取得礼品券用户 + */ + List exportNotGetGiftVoucherUsers(String mainActivityId, String subActivityId, String activityId); + + /** + * 导出未使用礼品券用户。 + * + * @param mainActivityId 主活动ID + * @param subActivityId 分活动ID + * @param activityId 兼容原页面保留的最终活动ID + * @return 未使用礼品券用户 + */ + List exportNotUseGiftVoucherUsers(String mainActivityId, String subActivityId, String activityId); + + /** + * 查询人员打卡情况列表。 + * 选择分活动时只查当前分活动;只选择主活动时按主活动关联项目下的全部活动汇总到人员维度。 + * + * @param mainActivityId 主活动ID + * @param subActivityId 分活动ID + * @param activityId 兼容页面保留的最终活动ID + * @param unionId 分工会ID + * @param searchKeyword 姓名或工号关键字,空表示不过滤 + * @param qualifiedStatus 达标筛选:qualified=已达标,unqualified=未达标,all/空=全部 + * @param pageNumber 页码,从 1 开始 + * @param pageSize 每页条数 + * @return 人员打卡情况分页结果 + */ + Pagination userPunchPageData(String mainActivityId, String subActivityId, String activityId, String unionId, + String searchKeyword, String qualifiedStatus, int pageNumber, int pageSize); + + /** + * 导出人员打卡情况列表。 + * 导出会按当前筛选条件返回全部人员,不能只返回页面当前页。 + * + * @param mainActivityId 主活动ID + * @param subActivityId 分活动ID + * @param activityId 兼容页面保留的最终活动ID + * @param unionId 分工会ID + * @param searchKeyword 姓名或工号关键字,空表示不过滤 + * @param qualifiedStatus 达标筛选:qualified=已达标,unqualified=未达标,all/空=全部 + * @return 人员打卡情况全量列表 + */ + List userPunchExportData(String mainActivityId, String subActivityId, String activityId, String unionId, String searchKeyword, String qualifiedStatus); } + + diff --git a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/FitnessWalkRelationActivityCompletionService.java b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/FitnessWalkRelationActivityCompletionService.java new file mode 100644 index 00000000..7178bdcd --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/FitnessWalkRelationActivityCompletionService.java @@ -0,0 +1,22 @@ +package com.budwk.app.zhgh.activity.fitnesswalk.service; + +import com.budwk.app.base.service.BaseService; +import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkActivityRelationProject; + +/** + * 健步走关联活动完成情况 service。 + * 该 service 专门处理“按关联项目查询当前登录人的关联活动完成进度”场景,避免把业务判断堆到通用 service 中。 + */ +public interface FitnessWalkRelationActivityCompletionService extends BaseService { + + /** + * 根据活动关联项目ID,判断当前登录人是否满足关联活动达标要求。 + * 只有当当前用户完成的达标活动数等于关联配置的 standardActivityNum 时,才返回 true。 + * + * @param relationActivityId 活动关联项目ID + * @return true 代表全部合格,false 代表未达标 + */ + boolean isCurrentUserRelationActivityQualified(String relationActivityId,String userId); +} + + diff --git a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/FitnessWalkStepRaffleService.java b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/FitnessWalkStepRaffleService.java index f3d24fbc..fbecab6b 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/FitnessWalkStepRaffleService.java +++ b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/FitnessWalkStepRaffleService.java @@ -32,3 +32,5 @@ public interface FitnessWalkStepRaffleService extends BaseService + implements FitnessWalkActivityRelationProjectService { + + @Inject + private WeAppCloudUtil weAppCloudUtil; + + public FitnessWalkActivityRelationProjectServiceImpl(Dao dao) { + super(dao); + } + + @Override + public Pagination pageData(PageForm pageForm) { + PageForm currentPageForm = pageForm == null ? new PageForm() : pageForm; + Cnd cnd = buildPageCnd(currentPageForm); + Integer pageNumber = currentPageForm.getPageNumber() == null ? 1 : currentPageForm.getPageNumber(); + Integer pageSize = currentPageForm.getPageSize() == null ? 10 : currentPageForm.getPageSize(); + return listPageMap(pageNumber, pageSize, cnd); + } + + @Override + @Aop(TransAop.READ_COMMITTED) + public Result saveRelationProject(FitnessWalkActivityRelationProject relationProject) { + try { + validateAndNormalize(relationProject); + if (StrUtil.isBlank(relationProject.getId())) { + insert(relationProject); + } else { + updateIgnoreNull(relationProject); + } + return Result.success("保存成功"); + } catch (Exception e) { + return Result.error(e.getMessage()); + } + } + + @Override + @Aop(TransAop.READ_COMMITTED) + public Result deleteRelationProject(String id) { + if (StrUtil.isBlank(id)) { + return Result.error("未获取到需要删除的关联项目"); + } + delete(id); + return Result.success("删除成功"); + } + + @Override + public List getRelationActivityList(String id) { + if (StrUtil.isBlank(id)) { + return new ArrayList<>(); + } + + StringBuilder sql = new StringBuilder("db.collection('activity').where({relationActivityId:'") + .append(id) + .append("'"); + + // 非全局管理员查看时,仅返回当前工会下的关联活动,避免越权看到其他工会数据。 + if (AuthUtil.hasRoleOr("BRANCH_UNION_CHAIRMAN", "BRANCH_UNION_OPERATOR", "BRANCH_UNION_ADMIN") && !AuthUtil.hasRoleOr("SYSADMIN", "SCHOOL_UNION_ADMIN")) { + sql.append(",unionId:'").append(SecurityUtil.getUnionId()).append("'"); + } + + sql.append("}).field({") + .append("masterActivityName:true,") + .append("branchActivityName:true,") + .append("activityModel:true,") + .append("createdAt:true,") + .append("relationActivityId:true") + .append("}).orderBy('createdAt','desc').limit(1000).get()"); + + try { + JSONObject jsonObject = weAppCloudUtil.request(WeAppCloudUtil.CRUD.QUERY, sql.toString()); + return jsonObject.getJSONArray("data").stream() + .map(item -> JSON.parseObject((String) item, FitnessWalkActivity.class)) + .map(this::buildRelationActivityItem) + .collect(Collectors.toList()); + } catch (Exception e) { + throw new RuntimeException("查询关联活动失败:" + e.getMessage()); + } + } + + /** + * 组装分页查询条件。 + * 当前列表仅支持按主活动名称模糊查询,并默认按创建时间倒序展示。 + */ + private Cnd buildPageCnd(PageForm pageForm) { + Cnd cnd = Cnd.NEW(); + if (pageForm != null && StrUtil.isNotBlank(pageForm.getSearchKeyword())) { + cnd.and("masterActivityName", "like", "%" + pageForm.getSearchKeyword().trim() + "%"); + } + cnd.desc("createdAt"); + return cnd; + } + + /** + * 保存前统一校验和标准化字段。 + * 这里同时限制主活动名称唯一,避免重复创建同名关联项目影响后续配置。 + */ + private void validateAndNormalize(FitnessWalkActivityRelationProject relationProject) { + if (relationProject == null) { + throw new RuntimeException("关联项目信息不能为空"); + } + + String masterActivityName = Strings.sNull(relationProject.getMasterActivityName()).trim(); + if (Strings.isBlank(masterActivityName)) { + throw new RuntimeException("请填写主活动名称"); + } + relationProject.setMasterActivityName(masterActivityName); + + if (relationProject.getStandardActivityNum() == null || relationProject.getStandardActivityNum() <= 0) { + throw new RuntimeException("达标活动数量必须大于0"); + } + + Cnd cnd = Cnd.where("masterActivityName", "=", masterActivityName); + if (Strings.isNotBlank(relationProject.getId())) { + cnd.and("id", "<>", relationProject.getId()); + } + if (count(cnd) > 0) { + throw new RuntimeException("主活动名称已存在,请勿重复创建"); + } + } + + /** + * 将活动对象转换为查看页需要的展示数据。 + * 这里统一补充活动名称和活动模式文本,前端只负责展示,不再重复拼接字段。 + */ + private NutMap buildRelationActivityItem(FitnessWalkActivity activity) { + NutMap item = NutMap.NEW(); + item.put("activityId", activity.get_id()); + item.put("activityName", buildRelationActivityName(activity)); + item.put("activityModel", activity.getActivityModel()); + item.put("activityModelName", getActivityModelName(activity.getActivityModel())); + item.put("createdAt", activity.getCreatedAt()); + return item; + } + + /** + * 统一生成活动展示名称。 + * 没有分活动名称时仅显示主活动名称;有分活动名称时按“主活动(分活动)”展示。 + */ + private String buildRelationActivityName(FitnessWalkActivity activity) { + String masterActivityName = Strings.sNull(activity.getMasterActivityName()).trim(); + String branchActivityName = Strings.sNull(activity.getBranchActivityName()).trim(); + if (Strings.isBlank(branchActivityName)) { + return masterActivityName; + } + return masterActivityName + "(" + branchActivityName + ")"; + } + + /** + * 返回活动模式中文名称,便于查看页面直接展示。 + */ + private String getActivityModelName(String activityModel) { + if ("punch".equals(activityModel)) { + return "打卡模式"; + } + if ("stepCount".equals(activityModel)) { + return "计步模式"; + } + if ("GPS".equals(activityModel)) { + return "GPS模式"; + } + return ""; + } +} + + + + diff --git a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/impl/FitnessWalkActivityServiceImpl.java b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/impl/FitnessWalkActivityServiceImpl.java new file mode 100644 index 00000000..65dcdc4b --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/impl/FitnessWalkActivityServiceImpl.java @@ -0,0 +1,53 @@ +package com.budwk.app.zhgh.activity.fitnesswalk.service.impl; + +import cn.hutool.core.date.DateUtil; +import com.budwk.app.base.service.impl.BaseServiceImpl; +import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkActivityRelationProject; +import com.budwk.app.zhgh.activity.fitnesswalk.service.FitnessWalkActivityService; +import com.budwk.app.base.dao.CndPlus; +import org.nutz.dao.Dao; +import org.nutz.dao.Sqls; +import org.nutz.dao.sql.Sql; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.lang.util.NutMap; + +import java.util.List; + +/** + * 健步走活动管理实现。 + * 这里额外负责“关联活动”下拉构建,以及保存时的关联字段统一回填。 + */ +@IocBean(args = {"refer:dao"}) +public class FitnessWalkActivityServiceImpl extends BaseServiceImpl implements FitnessWalkActivityService { + + public FitnessWalkActivityServiceImpl(Dao dao) { + super(dao); + } + + @Override + public List getCurrentYearRelationActivityOptions() { + List projectList = dao().query(FitnessWalkActivityRelationProject.class, CndPlus.create().andEX("YEAR(FROM_UNIXTIME(createdAt/ 1000))", "=", DateUtil.thisYear())); + return projectList; + } + + @Override + public List getWelfareProjectOptions(Integer year) { + int targetYear = year == null ? DateUtil.thisYear() : year; + Sql sql = Sqls.create(""" + SELECT + id, + `name` + FROM + welfare_project + $condition + """); + CndPlus cnd = CndPlus.create(); + cnd.and("isDisabled", "=", false); + cnd.andEX("year", "=", targetYear); + sql.setCondition(cnd); + return listMap(sql); + } +} + + + diff --git a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/impl/FitnessWalkCommonServiceImpl.java b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/impl/FitnessWalkCommonServiceImpl.java index 3384be2b..acfebcee 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/impl/FitnessWalkCommonServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/impl/FitnessWalkCommonServiceImpl.java @@ -1,20 +1,21 @@ package com.budwk.app.zhgh.activity.fitnesswalk.service.impl; import cn.hutool.core.collection.CollectionUtil; +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 cn.hutool.json.JSONObject; -import cn.hutool.json.JSONUtil; -import com.budwk.app.base.constant.RoleConstant; -import com.budwk.app.base.exception.BaseException; import com.budwk.app.base.page.Pagination; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.budwk.app.base.dao.CndPlus; import com.budwk.app.base.param.PageForm; import com.budwk.app.base.service.impl.BaseServiceImpl; +import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.sys.models.Sys_task; +import com.budwk.app.sys.models.Sys_user; import com.budwk.app.sys.services.SysTaskService; import com.budwk.app.task.services.TaskPlatformService; import com.budwk.app.web.commons.auth.utils.AuthUtil; -import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.zhgh.activity.fitnesswalk.contants.Cascader; import com.budwk.app.zhgh.activity.fitnesswalk.contants.FitnessWalkMode; import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkActivity; @@ -23,12 +24,14 @@ import com.budwk.app.zhgh.activity.fitnesswalk.service.FitnessWalkCommonService; import com.budwk.app.zhgh.activity.fitnesswalk.utils.WeAppCloudUtil; import org.nutz.dao.Cnd; import org.nutz.dao.Dao; +import org.nutz.aop.interceptor.ioc.TransAop; import org.nutz.dao.Sqls; import org.nutz.dao.sql.Sql; import org.nutz.dao.util.cri.Static; import org.nutz.integration.jedis.RedisService; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.ioc.aop.Aop; import org.nutz.json.Json; import org.nutz.lang.Lang; import org.nutz.lang.Strings; @@ -38,6 +41,7 @@ import org.nutz.lang.util.NutMap; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*; +import java.util.function.Function; import java.util.stream.Collectors; @IocBean(args = {"refer:dao"}) @@ -69,16 +73,13 @@ public class FitnessWalkCommonServiceImpl extends BaseServiceImpl urlStr = new ArrayList<>(); for (String url : activity.getRandomPics()) { @@ -86,7 +87,7 @@ public class FitnessWalkCommonServiceImpl extends BaseServiceImpl JSONUtil.toBean((String) v, FitnessWalkActivity.class)) - .findFirst().orElse(null); + activity = jsonObject.getJSONArray("data").stream().map(v -> JSON.parseObject((String) v, FitnessWalkActivity.class)).findFirst().orElse(null); if (Lang.isNotEmpty(activity.getRandomPics())) { List urlStr = new ArrayList<>(); for (String url : activity.getRandomPics()) { @@ -125,15 +124,14 @@ public class FitnessWalkCommonServiceImpl extends BaseServiceImpl fitnessWalkActivities; if (redisService.exists("FitnessWalk:ActivityList")) { String dataString = redisService.get("FitnessWalk:ActivityList"); - fitnessWalkActivities = JSONUtil.toList(dataString, FitnessWalkActivity.class); + fitnessWalkActivities = JSONObject.parseArray(dataString, FitnessWalkActivity.class); } else { StringBuilder sql = new StringBuilder(); sql.append("db.collection('activity').field({") .append("masterActivityName:true,") .append("branchActivityName:true,") + .append("relationActivityId:true,") .append("activityModel:true,") + .append("createdAt:true,") .append("endTime:true,") .append("startTime:true,") .append("unionId:true") .append("}).orderBy('startTime', 'desc').get()"); JSONObject jsonObject = weAppCloudUtil.request(WeAppCloudUtil.CRUD.QUERY, sql.toString()); - fitnessWalkActivities = jsonObject.getJSONArray("data").stream() - .map(v -> JSONUtil.toBean((String) v, FitnessWalkActivity.class)).collect(Collectors.toList()); + fitnessWalkActivities = jsonObject.getJSONArray("data").stream().map(v -> JSON.parseObject((String) v, FitnessWalkActivity.class)).collect(Collectors.toList()); } - if (AuthUtil.hasRoleOr(RoleConstant.BRANCH_UNION_CHAIRMAN.name(), RoleConstant.BRANCH_UNION_OPERATOR.name(), RoleConstant.BRANCH_UNION_ADMIN.name()) - && !AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) { - fitnessWalkActivities = fitnessWalkActivities.stream() - .filter(v -> SecurityUtil.getUnionId().equals(v.getUnionId())) - .collect(Collectors.toList()); + if (AuthUtil.hasRoleOr("BRANCH_UNION_CHAIRMAN", "BRANCH_UNION_OPERATOR", "BRANCH_UNION_ADMIN") && !AuthUtil.hasRoleOr("SYSADMIN", "SCHOOL_UNION_ADMIN")) { + fitnessWalkActivities = fitnessWalkActivities.stream().filter(v -> SecurityUtil.getUnionId().equals(v.getUnionId())).collect(Collectors.toList()); } - redisService.set("FitnessWalk:ActivityList", JSONUtil.toJsonStr(fitnessWalkActivities)); + redisService.set("FitnessWalk:ActivityList", JSONObject.toJSONString(fitnessWalkActivities)); return fitnessWalkActivities; } @@ -206,13 +202,15 @@ public class FitnessWalkCommonServiceImpl extends BaseServiceImpl oldTasks = sysTaskService.dao().query(Sys_task.class, Cnd.where("data", "like", "%" + fitnessWalkActivity.get_id() + "%")); @@ -240,7 +238,7 @@ public class FitnessWalkCommonServiceImpl extends BaseServiceImpl map = new HashMap<>(5); map.put("id", fitnessWalkActivity.get_id()); // 唯一标识的删除id,用于执行定时任务完成后,将此定时任务禁用 @@ -278,7 +276,7 @@ public class FitnessWalkCommonServiceImpl extends BaseServiceImpl map = new HashMap<>(8); map.put("id", fitnessWalkActivity.get_id()); // 唯一标识的删除id,用于执行定时任务完成后,将此定时任务禁用 @@ -315,6 +313,133 @@ public class FitnessWalkCommonServiceImpl extends BaseServiceImpl oldTasks = sysTaskService.query(Cnd.where("note", "=", fitnessWalkActivity.get_id()).and("jobClass", "=", jobClass)); + DateTime todayStart = DateUtil.beginOfDay(DateUtil.date()); + Set lockedTargetDateSet = new HashSet<>(); + + // 编辑活动时,已经执行过或已经生成过活动的历史日期不再重建,避免把旧数据重新覆盖。 + for (Sys_task task : oldTasks) { + Long targetDate = getCreateTaskTargetDate(task); + if (targetDate != null && shouldKeepHistoricalCreateTask(task, targetDate, todayStart.getTime(), fitnessWalkActivity.get_id())) { + lockedTargetDateSet.add(targetDate); + continue; + } + taskPlatformService.delete(task.getId(), task.getId()); + sysTaskService.delete(task.getId()); + } + + if (!Boolean.TRUE.equals(fitnessWalkActivity.getAutoContinuousCreate()) + || fitnessWalkActivity.getContinuousCreateDate() == null + || fitnessWalkActivity.getContinuousCreateDate().length < 2 + || fitnessWalkActivity.getContinuousCreateDate()[0] == null + || fitnessWalkActivity.getContinuousCreateDate()[1] == null) { + return; + } + + DateTime startDate = DateUtil.beginOfDay(DateUtil.date(fitnessWalkActivity.getContinuousCreateDate()[0])); + DateTime endDate = DateUtil.beginOfDay(DateUtil.date(fitnessWalkActivity.getContinuousCreateDate()[1])); + if (startDate.isAfter(endDate)) { + return; + } + + DateTime now = DateUtil.date(); + for (DateTime currentDate = startDate; !currentDate.isAfter(endDate); currentDate = DateUtil.offsetDay(currentDate, 1)) { + long targetDate = currentDate.getTime(); + // 今天及以前的活动日期视为历史数据;已经落地的日期和已锁定的旧任务都不再改动。 + if (targetDate <= todayStart.getTime() + || lockedTargetDateSet.contains(targetDate) + || hasCreatedActivityForTargetDate(fitnessWalkActivity.get_id(), targetDate)) { + continue; + } + + // 每个目标活动日期都在前一天 23:59 触发创建任务。 + DateTime executeTime = DateUtil.endOfDay(DateUtil.offsetDay(currentDate, -1)); + executeTime.setField(DateField.MINUTE, 59); + executeTime.setField(DateField.SECOND, 0); + executeTime.setField(DateField.MILLISECOND, 0); + + if (!executeTime.isAfter(now)) { + continue; + } + // 每天生成一个定时任务,执行时按原活动内容复制出当天的新活动。 + Map taskData = new HashMap<>(4); + taskData.put("sourceActivityId", fitnessWalkActivity.get_id()); + taskData.put("targetDate", targetDate); + taskData.put("deleteId", R.UU32()); + taskData.put("activityName", fitnessWalkActivity.getMasterActivityName()); + + Sys_task task = new Sys_task(); + task.setName(fitnessWalkActivity.getMasterActivityName() + "自动创建活动任务"); + task.setJobClass(jobClass); + // 任务说明直接保存原活动ID,便于编辑和删除活动时统一清理相关任务。 + task.setNote(fitnessWalkActivity.get_id()); + task.setCron(new SimpleDateFormat("ss mm HH dd MM ? yyyy").format(executeTime)); + task.setData(Json.toJson(taskData)); + task.setDisabled(false); + sysTaskService.insert(task); + taskPlatformService.add(task.getId(), task.getId(), task.getJobClass(), task.getCron(), task.getNote(), task.getData()); + } + } + + /** + * 定时任务数据里会保存目标活动日期,编辑活动时要靠这个日期判断哪些任务已经属于历史数据。 + * 这里统一从任务 data 中解析 targetDate,并归一化到当天 00:00:00,避免比较时出现毫秒偏差。 + */ + private Long getCreateTaskTargetDate(Sys_task task) { + if (task == null || Strings.isBlank(task.getData())) { + return null; + } + try { + JSONObject data = JSONObject.parseObject(task.getData()); + Object targetDateObj = data.get("targetDate"); + if (targetDateObj == null) { + return null; + } + long targetDate = targetDateObj instanceof Number + ? ((Number) targetDateObj).longValue() + : Long.parseLong(String.valueOf(targetDateObj)); + return DateUtil.beginOfDay(DateUtil.date(targetDate)).getTime(); + } catch (Exception e) { + return null; + } + } + + /** + * 编辑原活动时,已经执行过的自动创建任务和已经生成过活动的日期都要保留。 + * 这样可以确保用户修改未来配置时,不会把过去已经存在的活动和任务重新删除或覆盖。 + */ + private boolean shouldKeepHistoricalCreateTask(Sys_task task, long targetDate, long todayStart, String sourceActivityId) { + return task.isDisabled() + || targetDate <= todayStart + || hasCreatedActivityForTargetDate(sourceActivityId, targetDate); + } + + /** + * 自动创建出的活动会带上来源活动ID和目标活动日期。 + * 编辑原活动时通过这两个字段判断某一天的活动是否已经落地,避免重复建任务或重复建活动。 + */ + private boolean hasCreatedActivityForTargetDate(String sourceActivityId, long targetDate) { + if (Strings.isBlank(sourceActivityId)) { + return false; + } + StringBuilder sql = new StringBuilder("db.collection('activity').where({autoCreateSourceActivityId:'"); + sql.append(sourceActivityId) + .append("',autoCreateTargetDate:") + .append(targetDate) + .append("}).count()"); + JSONObject jsonObject = weAppCloudUtil.request(WeAppCloudUtil.CRUD.QUERY, sql.toString()); + JSONObject pager = jsonObject.getJSONObject("pager"); + return pager != null && pager.getIntValue("Total") > 0; + } + @Override public Pagination getStepRankingPagination(PageForm pageForm, String activityId, String unionId, String unitId, String mode) { @@ -417,7 +542,7 @@ public class FitnessWalkCommonServiceImpl extends BaseServiceImpl",0)); sql.setCondition(cnd); return sql; } } + + + + diff --git a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/impl/FitnessWalkPunchStatisticsServiceImpl.java b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/impl/FitnessWalkPunchStatisticsServiceImpl.java index f80351b1..36b483a3 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/impl/FitnessWalkPunchStatisticsServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/impl/FitnessWalkPunchStatisticsServiceImpl.java @@ -1,25 +1,38 @@ package com.budwk.app.zhgh.activity.fitnesswalk.service.impl; +import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.StrUtil; -import cn.hutool.json.JSONObject; -import cn.hutool.json.JSONUtil; +import com.budwk.app.base.page.Pagination; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; import com.budwk.app.base.service.impl.BaseServiceImpl; +import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.sys.models.Sys_union; +import com.budwk.app.sys.models.Sys_unit; +import com.budwk.app.zhgh.activity.fitnesswalk.contants.Cascader; +import com.budwk.app.zhgh.activity.fitnesswalk.contants.FitnessWalkMode; +import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkActivity; +import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkActivityRelationProject; import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkAwardUser; +import com.budwk.app.zhgh.activity.fitnesswalk.service.FitnessWalkCommonService; import com.budwk.app.zhgh.activity.fitnesswalk.service.FitnessWalkPunchStatisticsService; import com.budwk.app.zhgh.activity.fitnesswalk.utils.WeAppCloudUtil; import org.nutz.dao.Cnd; import org.nutz.dao.Dao; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.lang.Strings; import org.nutz.lang.util.NutMap; +import com.budwk.app.web.commons.auth.utils.AuthUtil; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.stream.Collectors; @IocBean(args = {"refer:dao"}) public class FitnessWalkPunchStatisticsServiceImpl extends BaseServiceImpl implements FitnessWalkPunchStatisticsService { + private static final int EXPORT_QUERY_BATCH_SIZE = 500; + private static final String RELATION_ACTIVITY_PREFIX = "relation:"; + public FitnessWalkPunchStatisticsServiceImpl(Dao dao) { super(dao); } @@ -27,14 +40,62 @@ public class FitnessWalkPunchStatisticsServiceImpl extends BaseServiceImpl unionRegCheckInNum(String activityId, String unionId) { + public List userPunchCascaderActivity(int year) { + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.YEAR, year); + long startTs = cn.hutool.core.date.DateUtil.beginOfYear(calendar.getTime()).getTime(); + long endTs = cn.hutool.core.date.DateUtil.endOfYear(calendar.getTime()).getTime(); + + List activityList = queryPunchActivityList(startTs, endTs); + if (CollectionUtil.isEmpty(activityList)) { + return Collections.emptyList(); + } + + Map> activityGroupMap = activityList.stream() + .collect(Collectors.groupingBy(this::buildUserPunchCascaderGroupKey, LinkedHashMap::new, Collectors.toList())); + + return activityGroupMap.values().stream().map(groupList -> { + FitnessWalkActivity firstActivity = groupList.get(0); + List childActivityList = Strings.isNotBlank(firstActivity.getRelationActivityId()) + ? queryRelationActivityList(firstActivity.getRelationActivityId(), FitnessWalkMode.PUNCH.getValue()) + : groupList; + Cascader cascader = new Cascader(); + cascader.setValue(buildUserPunchCascaderParentValue(firstActivity)); + cascader.setLabel(Strings.sNull(firstActivity.getMasterActivityName())); + if (childActivityList.size() > 1) { + List children = childActivityList.stream().map(activity -> { + Cascader child = new Cascader(); + child.setValue(activity.get_id()); + child.setLabel(Strings.isBlank(activity.getBranchActivityName()) ? Strings.sNull(activity.getMasterActivityName()) : activity.getBranchActivityName()); + return child; + }).collect(Collectors.toList()); + cascader.setChildren(children); + } + return cascader; + }).collect(Collectors.toList()); + } + + @Override + public List unionRegCheckInNum(String mainActivityId, String subActivityId, String activityId, String unionId) { + List activityIds = resolveActivityIds(mainActivityId, subActivityId, activityId); + if (activityIds.isEmpty()) { + return Collections.emptyList(); + } + List unions = dao().query(Sys_union.class, Cnd.NEW().desc("unioncode")); //查询报名的数据 StringBuffer regCountSql = new StringBuffer(); regCountSql.append("db.collection('register').aggregate()"); regCountSql.append(".match({"); - regCountSql.append("activity_id:'").append(activityId).append("'"); + regCountSql.append(buildActivityMatchSql(activityIds)); if (StrUtil.isNotBlank(unionId)) { regCountSql.append(",unionid:'").append(unionId).append("'"); } @@ -43,9 +104,9 @@ public class FitnessWalkPunchStatisticsServiceImpl extends BaseServiceImpl regCountData = regCountJsonObject.getJSONArray("data").stream().map(v -> { - JSONObject rowData = JSONUtil.parseObj((String) v); - String id = rowData.getStr("_id"); - int count = rowData.getJSONObject("count").getInt("$numberInt"); + JSONObject rowData = JSONObject.parseObject((String) v); + String id = rowData.getString("_id"); + int count = rowData.getJSONObject("count").getIntValue("$numberInt"); return NutMap.NEW().addv("id", id).addv("count", count); }).collect(Collectors.toMap(v -> v.getString("id"), v -> v.getInt("count"))); @@ -53,20 +114,22 @@ public class FitnessWalkPunchStatisticsServiceImpl extends BaseServiceImpl checkInCountData = checkInCountJsonObject.getJSONArray("data").stream().map(v -> { - JSONObject rowData = JSONUtil.parseObj((String) v); - String id = rowData.getStr("unionid"); - int count = rowData.getJSONObject("count").getInt("$numberInt"); + JSONObject rowData = JSONObject.parseObject((String) v); + String id = rowData.getString("unionid"); + int count = parseCloudNumber(rowData.get("count")); return NutMap.NEW().addv("id", id).addv("count", count); }).collect(Collectors.toMap(v -> v.getString("id"), v -> v.getInt("count"))); @@ -88,15 +151,21 @@ public class FitnessWalkPunchStatisticsServiceImpl extends BaseServiceImpl unionRegistrationData(String activityId, String unionId) { + public List unionRegistrationData(String mainActivityId, String subActivityId, String activityId, String unionId) { + List activityIds = resolveActivityIds(mainActivityId, subActivityId, activityId); + if (activityIds.isEmpty()) { + return Collections.emptyList(); + } + StringBuffer sql = new StringBuffer("db.collection('register').aggregate()"); sql.append(".match({"); - sql.append("activity_id:'").append(activityId).append("',unionid:'").append(unionId).append("'"); + sql.append(buildActivityMatchSql(activityIds)).append(",unionid:'").append(unionId).append("'"); sql.append("})"); sql.append(".addFields({register_time_date:$.toDate('$register_time')})"); sql.append(""" .project({ _id:true, + activity_id:true, loginname:true, username:true, unitname:true, @@ -114,17 +183,25 @@ public class FitnessWalkPunchStatisticsServiceImpl extends BaseServiceImpl list = jsonObject.getJSONArray("data").stream().map(v -> JSONUtil.parseObj((String) v)).collect(Collectors.toList()); + List list = jsonObject.getJSONArray("data").stream().map(v -> JSONObject.parseObject((String) v)).collect(Collectors.toList()); + Map activityNameMap = fitnessWalkCommonService.getAllActivity().stream() + .filter(activity -> Strings.isNotBlank(activity.get_id())) + .collect(Collectors.toMap(FitnessWalkActivity::get_id, this::buildActivityName, (left, right) -> left)); + list.forEach(item -> item.put("activityName", activityNameMap.getOrDefault(item.getString("activity_id"), ""))); return list; } @Override - public List unionCheckInData(String activityId, String unionId) { - // 查询打卡的数据,根据分工会来 + public List unionCheckInData(String mainActivityId, String subActivityId, String activityId, String unionId) { + List activityIds = resolveActivityIds(mainActivityId, subActivityId, activityId); + if (activityIds.isEmpty()) { + return Collections.emptyList(); + } + String sql = """ db.collection('sign').aggregate() .match({ - activity_id: '%s', + %s, unionid: '%s' }) .lookup({ @@ -177,6 +254,7 @@ public class FitnessWalkPunchStatisticsServiceImpl extends BaseServiceImpl list = jsonObject.getJSONArray("data").stream().map(v -> { - JSONObject jo = JSONUtil.parseObj((String) v); - if (jo.getInt("sign_size") > jo.getInt("pts_size")) { - jo.put("sign_size", jo.getInt("pts_size")); + JSONObject jo = JSONObject.parseObject((String) v); + if (jo.getIntValue("sign_size") > jo.getIntValue("pts_size")) { + jo.put("sign_size", jo.getIntValue("pts_size")); } return jo; }).collect(Collectors.toList()); + Map activityNameMap = fitnessWalkCommonService.getAllActivity().stream() + .filter(activity -> Strings.isNotBlank(activity.get_id())) + .collect(Collectors.toMap(FitnessWalkActivity::get_id, this::buildActivityName, (left, right) -> left)); + list.forEach(item -> item.put("activityName", activityNameMap.getOrDefault(item.getString("activity_id"), ""))); return list; } @Override - public List exportNotGetGiftVoucherUsers(String activityId) { + public List exportNotGetGiftVoucherUsers(String mainActivityId, String subActivityId, String activityId) { + List activityIds = resolveActivityIds(mainActivityId, subActivityId, activityId); + if (activityIds.isEmpty()) { + return Collections.emptyList(); + } + //查询报名的人员 JSONObject regJsonObject = weAppCloudUtil.request(WeAppCloudUtil.CRUD.AGGREGATE, """ db.collection('register').aggregate([ - { $match: { activity_id: '%s' } } + { $match: { %s } } ]) .sort({ unitname: 1 }) .end() - """.formatted(activityId)); + """.formatted(buildActivityMatchSql(activityIds))); //查询打完卡获得礼品券的人员 JSONObject giftUserJsonObject = weAppCloudUtil.request(WeAppCloudUtil.CRUD.AGGREGATE, """ db.collection('gift_voucher').aggregate() - .match({ activity_id: '%s' }) + .match({ %s }) .group({ _id: '$loginname' }) .project({ _id: 0, loginname: '$_id' }) .end() - """.formatted(activityId)); + """.formatted(buildActivityMatchSql(activityIds))); List giftUserLoginNames = giftUserJsonObject.getJSONArray("data").stream().map(v -> { - JSONObject jo = JSONUtil.parseObj((String) v); - return jo.getStr("loginname"); - }).toList(); + JSONObject jo = JSON.parseObject((String) v); + return jo.getString("loginname"); + }).collect(Collectors.toList()); - List list = regJsonObject.getJSONArray("data").stream() - .map(v -> JSONUtil.parseObj((String) v)) - .filter(v -> !giftUserLoginNames.contains(v.getStr("loginname"))).collect(Collectors.toList()); + List list = regJsonObject.getJSONArray("data").stream().map(v -> JSON.parseObject((String) v)).filter(v -> !giftUserLoginNames.contains(v.getString("loginname"))).collect(Collectors.toList()); + // 基于结果集中的 unitid 二次过滤单位表并回填 unitname,确保导出单位名称准确。 + fillUnitNameByUnitId(list); return list; } + + @Override - public List exportNotUseGiftVoucherUsers(String activityId) { - //查询未使用礼品券的人员 + public List exportNotUseGiftVoucherUsers(String mainActivityId, String subActivityId, String activityId) { + List activityIds = resolveActivityIds(mainActivityId, subActivityId, activityId); + if (activityIds.isEmpty()) { + return Collections.emptyList(); + } + JSONObject jsonObject = weAppCloudUtil.request(WeAppCloudUtil.CRUD.AGGREGATE, """ db.collection('gift_voucher').aggregate() - .match({ activity_id: '%s', used: false }) + .match({ %s, used: false }) .sort({ unitname: 1 }) .end() - """.formatted(activityId)); + """.formatted(buildActivityMatchSql(activityIds))); + List list = jsonObject.getJSONArray("data").stream().map(v -> JSON.parseObject((String) v)).collect(Collectors.toList()); + // 基于结果集中的 unitid 二次过滤单位表并回填 unitname,确保导出单位名称准确。 + fillUnitNameByUnitId(list); + return list; + } + + /** + * 从结果集提取 unitid,按 unitid 批量查询单位表并回填 unitname。 + * 该方法用于导出接口,避免逐条查询导致性能问题。 + * + * @param list 需要回填 unitname 的结果集 + */ + private void fillUnitNameByUnitId(List list) { + if (CollectionUtil.isEmpty(list)) { + return; + } + Set unitIdSet = list.stream() + .map(v -> v.getString("unitid")) + .filter(StrUtil::isNotBlank) + .collect(Collectors.toSet()); + if (CollectionUtil.isEmpty(unitIdSet)) { + return; + } + List unitQuery = dao().query(Sys_unit.class, Cnd.where("id", "in", unitIdSet)); + Map unitNameMap = unitQuery.stream().collect(Collectors.toMap(Sys_unit::getId, Sys_unit::getName, (a, b) -> a)); + for (JSONObject obj : list) { + String unitName = unitNameMap.get(obj.getString("unitid")); + if (StrUtil.isNotBlank(unitName)) { + obj.put("unitname", unitName); + } + } + + } + + /** + * 以人员维度汇总打卡情况。 + * 主活动下未选择分活动时,会把该关联项目下所有活动按人员汇总后返回。 + */ + @Override + public Pagination userPunchPageData(String mainActivityId, String subActivityId, String activityId, String unionId, + String searchKeyword, String qualifiedStatus, int pageNumber, int pageSize) { + pageNumber = pageNumber < 1 ? 1 : pageNumber; + pageSize = pageSize < 1 ? 10 : pageSize; + List displayActivityIds = resolveActivityIds(mainActivityId, subActivityId, activityId); + if (displayActivityIds.isEmpty()) { + return new Pagination(pageNumber, pageSize, 0, Collections.emptyList()); + } + + QualifiedRuleContext qualifiedRuleContext = resolveQualifiedRuleContext(mainActivityId, subActivityId, activityId); + if (needQualifiedFilter(qualifiedStatus, qualifiedRuleContext)) { + List allLoginNames = queryUserLoginNames(displayActivityIds, unionId, searchKeyword, null, null); + List allUserSummaryList = buildUserPunchSummaryList(displayActivityIds, unionId, allLoginNames, qualifiedRuleContext); + List filteredList = filterByQualifiedStatus(allUserSummaryList, qualifiedRuleContext.getStandardActivityNum(), qualifiedStatus); + return new Pagination(pageNumber, pageSize, filteredList.size(), pageSubList(filteredList, pageNumber, pageSize)); + } + + int totalCount = countUserLoginNames(displayActivityIds, unionId, searchKeyword); + List pageLoginNames = queryUserLoginNames(displayActivityIds, unionId, searchKeyword, pageNumber, pageSize); + List pageList = buildUserPunchSummaryList(displayActivityIds, unionId, pageLoginNames, qualifiedRuleContext); + return new Pagination(pageNumber, pageSize, totalCount, pageList); + } + + @Override + public List userPunchExportData(String mainActivityId, String subActivityId, String activityId, String unionId, String searchKeyword, String qualifiedStatus) { + List displayActivityIds = resolveActivityIds(mainActivityId, subActivityId, activityId); + if (displayActivityIds.isEmpty()) { + return Collections.emptyList(); + } + QualifiedRuleContext qualifiedRuleContext = resolveQualifiedRuleContext(mainActivityId, subActivityId, activityId); + List allLoginNames = queryUserLoginNames(displayActivityIds, unionId, searchKeyword, null, null); + List userSummaryList = buildUserPunchSummaryList(displayActivityIds, unionId, allLoginNames, qualifiedRuleContext); + return filterByQualifiedStatus(userSummaryList, qualifiedRuleContext.getStandardActivityNum(), qualifiedStatus); + } + + /** + * 按人员维度查询登录名。 + * searchKeyword 支持按姓名或工号模糊查询;列表查询传入 pageNumber/pageSize 时只取当前页人员,导出传空时取全部人员。 + */ + private List queryUserLoginNames(List activityIds, String unionId, String searchKeyword, Integer pageNumber, Integer pageSize) { + StringBuilder sql = new StringBuilder("db.collection('register').aggregate()"); + sql.append(".match({"); + sql.append(buildRegisterMatchSql(activityIds, unionId, null, searchKeyword)); + sql.append("})"); + sql.append(""" + .group({ + _id: '$loginname', + loginname: { $first: '$loginname' }, + unionname: { $first: '$unionname' }, + unitname: { $first: '$unitname' } + }) + .sort({ unionname: 1, unitname: 1, loginname: 1 }) + """); + if (pageNumber != null && pageSize != null) { + int skip = (pageNumber <= 1 ? 0 : pageNumber - 1) * pageSize; + sql.append(".skip(").append(skip).append(")"); + sql.append(".limit(").append(pageSize).append(")"); + } + sql.append(".project({_id: 0, loginname: '$loginname'}).end()"); + + JSONObject jsonObject = weAppCloudUtil.request(WeAppCloudUtil.CRUD.AGGREGATE, sql.toString()); + if (jsonObject.getJSONArray("data") == null || jsonObject.getJSONArray("data").isEmpty()) { + return Collections.emptyList(); + } return jsonObject.getJSONArray("data").stream() - .map(v -> JSONUtil.parseObj((String) v)).collect(Collectors.toList()); + .map(item -> JSON.parseObject((String) item).getString("loginname")) + .filter(StrUtil::isNotBlank) + .collect(Collectors.toList()); + } + + /** + * 统计当前筛选条件下的人员总数。 + * 这里按 loginname 去重,和页面按人员维度展示的口径保持一致。 + */ + private int countUserLoginNames(List activityIds, String unionId, String searchKeyword) { + StringBuilder sql = new StringBuilder("db.collection('register').aggregate()"); + sql.append(".match({"); + sql.append(buildRegisterMatchSql(activityIds, unionId, null, searchKeyword)); + sql.append("})"); + sql.append(""" + .group({ _id: '$loginname' }) + .group({ _id: 0, total: $.sum(1) }) + .project({ _id: 0, total: '$total' }) + .end() + """); + + JSONObject jsonObject = weAppCloudUtil.request(WeAppCloudUtil.CRUD.AGGREGATE, sql.toString()); + if (jsonObject.getJSONArray("data") == null || jsonObject.getJSONArray("data").isEmpty()) { + return 0; + } + JSONObject rowData = JSON.parseObject((String) jsonObject.getJSONArray("data").get(0)); + return parseCloudNumber(rowData.get("total")); + } + + /** + * 按人员批次构建汇总结果。 + * 列表通常只传当前页人员;导出传全部人员后按固定批次拆开,避免单次云数据库请求过大。 + */ + private List buildUserPunchSummaryList(List displayActivityIds, + String unionId, + List loginNames, + QualifiedRuleContext qualifiedRuleContext) { + if (CollectionUtil.isEmpty(loginNames)) { + return Collections.emptyList(); + } + + Map> displayUserActivityMap = new LinkedHashMap<>(); + Map qualifiedActivityNumMap = new LinkedHashMap<>(); + for (int start = 0; start < loginNames.size(); start += EXPORT_QUERY_BATCH_SIZE) { + List batchLoginNames = loginNames.subList(start, Math.min(start + EXPORT_QUERY_BATCH_SIZE, loginNames.size())); + Map> batchDisplayMap = queryUserActivityMap(displayActivityIds, unionId, batchLoginNames); + displayUserActivityMap.putAll(batchDisplayMap); + qualifiedActivityNumMap.putAll(buildQualifiedActivityNumMap(batchDisplayMap, displayActivityIds, unionId, qualifiedRuleContext, batchLoginNames)); + } + + return loginNames.stream() + .map(displayUserActivityMap::get) + .filter(CollectionUtil::isNotEmpty) + .map(activityRows -> buildUserPunchSummary(activityRows, qualifiedActivityNumMap)) + .collect(Collectors.toList()); + } + + /** + * 查询页面当前需要展示的“人员-活动”明细。 + * 这里不再使用 register 逐条 lookup sign 的大聚合,而是拆成报名行、活动点位数、打卡点位数三段批量查询。 + */ + private Map> queryUserActivityMap(List activityIds, String unionId, List loginNames) { + List registerRows = queryRegisterActivityRows(activityIds, unionId, loginNames); + if (CollectionUtil.isEmpty(registerRows)) { + return Collections.emptyMap(); + } + + Map activityPtsSizeMap = queryActivityPtsSizeMap(activityIds); + Map signSizeMap = querySignSizeMap(activityIds, loginNames); + + return registerRows.stream().map(rowData -> { + String activityId = rowData.getString("activity_id"); + String loginname = rowData.getString("loginname"); + int ptsSize = activityPtsSizeMap.getOrDefault(activityId, 0); + int signSize = signSizeMap.getOrDefault(buildUserActivityKey(activityId, loginname), 0); + if (signSize > ptsSize && ptsSize > 0) { + signSize = ptsSize; + } + return NutMap.NEW() + .addv("activity_id", activityId) + .addv("userId", rowData.getString("userId")) + .addv("username", rowData.getString("username")) + .addv("loginname", loginname) + .addv("unionname", rowData.getString("unionname")) + .addv("unitname", rowData.getString("unitname")) + .addv("pts_size", ptsSize) + .addv("sign_size", signSize) + .addv("activityQualified", ptsSize > 0 && signSize >= ptsSize); + }).collect(Collectors.groupingBy(row -> row.getString("loginname"), LinkedHashMap::new, Collectors.toList())); + } + + /** + * 查询报名表中的人员-活动基础行。 + * 参数说明:activityIds 为活动ID范围,unionId 为分工会ID,loginNames 为当前批次人员工号;返回每人每活动一行。 + */ + private List queryRegisterActivityRows(List activityIds, String unionId, List loginNames) { + StringBuilder sql = new StringBuilder("db.collection('register').aggregate()"); + sql.append(".match({"); + sql.append(buildRegisterMatchSql(activityIds, unionId, loginNames, null)); + sql.append("})"); + sql.append(""" + .group({ + _id: { + activity_id: '$activity_id', + loginname: '$loginname' + }, + userId: { $first: '$userid' }, + loginname: { $first: '$loginname' }, + username: { $first: '$username' }, + unionname: { $first: '$unionname' }, + unitname: { $first: '$unitname' } + }) + .project({ + _id: 0, + activity_id: '$_id.activity_id', + userId: 1, + loginname: 1, + username: 1, + unionname: 1, + unitname: 1 + }) + .sort({ unionname: 1, unitname: 1, loginname: 1 }) + .end() + """); + + JSONObject jsonObject = weAppCloudUtil.request(WeAppCloudUtil.CRUD.AGGREGATE, sql.toString()); + if (jsonObject.getJSONArray("data") == null || jsonObject.getJSONArray("data").isEmpty()) { + return Collections.emptyList(); + } + return jsonObject.getJSONArray("data").stream() + .map(item -> JSON.parseObject((String) item)) + .collect(Collectors.toList()); + } + + /** + * 查询活动点位数量。 + * 返回值 Map 的 key 为 activityId,value 为该活动配置的点位总数。 + */ + private Map queryActivityPtsSizeMap(List activityIds) { + if (CollectionUtil.isEmpty(activityIds)) { + return Collections.emptyMap(); + } + + StringBuilder sql = new StringBuilder("db.collection('activity').where({"); + sql.append(buildFieldMatchSql("_id", activityIds)); + sql.append("}).field({_id:true,pts:true}).limit(1000).get()"); + + JSONObject jsonObject = weAppCloudUtil.request(WeAppCloudUtil.CRUD.QUERY, sql.toString()); + if (jsonObject.getJSONArray("data") == null || jsonObject.getJSONArray("data").isEmpty()) { + return Collections.emptyMap(); + } + return jsonObject.getJSONArray("data").stream() + .map(item -> JSON.parseObject((String) item, FitnessWalkActivity.class)) + .filter(activity -> Strings.isNotBlank(activity.get_id())) + .collect(Collectors.toMap(FitnessWalkActivity::get_id, activity -> activity.getPts() == null ? 0 : activity.getPts().size(), (left, right) -> left)); + } + + /** + * 查询人员在活动中的已打卡点位数量。 + * 这里按 activity_id + loginname + pts_code 去重,避免重复打卡把完成情况算高。 + */ + private Map querySignSizeMap(List activityIds, List loginNames) { + if (CollectionUtil.isEmpty(activityIds) || CollectionUtil.isEmpty(loginNames)) { + return Collections.emptyMap(); + } + + StringBuilder sql = new StringBuilder("db.collection('sign').aggregate()"); + sql.append(".match({"); + sql.append(buildActivityMatchSql(activityIds)); + sql.append(",loginname:_.in(").append(JSON.toJSONString(loginNames)).append(")"); + sql.append("})"); + sql.append(""" + .group({ + _id: { + activity_id: '$activity_id', + loginname: '$loginname' + }, + signedPtsCodeSet: $.addToSet('$pts_code') + }) + .project({ + _id: 0, + activity_id: '$_id.activity_id', + loginname: '$_id.loginname', + signedPtsNum: $.size('$signedPtsCodeSet') + }) + .end() + """); + + JSONObject jsonObject = weAppCloudUtil.request(WeAppCloudUtil.CRUD.AGGREGATE, sql.toString()); + if (jsonObject.getJSONArray("data") == null || jsonObject.getJSONArray("data").isEmpty()) { + return Collections.emptyMap(); + } + return jsonObject.getJSONArray("data").stream() + .map(item -> JSON.parseObject((String) item)) + .collect(Collectors.toMap( + row -> buildUserActivityKey(row.getString("activity_id"), row.getString("loginname")), + row -> parseCloudNumber(row.get("signedPtsNum")), + (left, right) -> left + )); + } + + /** + * 把“人员-活动”明细汇总成页面列表需要的“人员”维度数据。 + * 达标活动数统一使用关联活动规则口径,避免和当前展示活动范围耦合。 + */ + private NutMap buildUserPunchSummary(List activityRows, Map qualifiedActivityNumMap) { + NutMap firstRow = activityRows.get(0); + int totalPtsSize = 0; + int totalSignSize = 0; + for (NutMap activityRow : activityRows) { + int ptsSize = activityRow.getInt("pts_size"); + int signSize = activityRow.getInt("sign_size"); + totalPtsSize += ptsSize; + totalSignSize += signSize; + } + int qualifiedActivityNum = qualifiedActivityNumMap.getOrDefault(firstRow.getString("loginname"), 0); + return NutMap.NEW() + .addv("userId", firstRow.getString("userId")) + .addv("username", firstRow.getString("username")) + .addv("loginname", firstRow.getString("loginname")) + .addv("unionname", firstRow.getString("unionname")) + .addv("unitname", firstRow.getString("unitname")) + .addv("pts_size", totalPtsSize) + .addv("sign_size", totalSignSize) + .addv("qualifiedActivityNum", qualifiedActivityNum) + .addv("isAllSigned", totalPtsSize > 0 && totalSignSize >= totalPtsSize ? "是" : "否"); + } + + /** + * 统计每个人在关联活动规则下已完成的活动数。 + * 如果当前查询活动没有配置关联项目,则退回当前展示活动范围的完成数。 + */ + private Map buildQualifiedActivityNumMap(Map> displayUserActivityMap, + List displayActivityIds, + String unionId, + QualifiedRuleContext qualifiedRuleContext, + List loginNames) { + if (displayUserActivityMap.isEmpty()) { + return Collections.emptyMap(); + } + + Map> sourceActivityMap; + if (qualifiedRuleContext == null + || qualifiedRuleContext.getQualifiedActivityIds().isEmpty() + || sameActivityIds(displayActivityIds, qualifiedRuleContext.getQualifiedActivityIds())) { + sourceActivityMap = displayUserActivityMap; + } else { + sourceActivityMap = queryUserActivityMap(qualifiedRuleContext.getQualifiedActivityIds(), unionId, loginNames); + } + + return sourceActivityMap.entrySet().stream().collect(Collectors.toMap( + Map.Entry::getKey, + entry -> (int) entry.getValue().stream().filter(row -> row.getBoolean("activityQualified")).count(), + (left, right) -> left, + LinkedHashMap::new + )); + } + + /** + * 判断本次是否需要先计算全量人员后再做达标过滤。 + * 达标筛选会影响总数,不能先分页再过滤,否则页面总数和当前页数据都会不准。 + */ + private boolean needQualifiedFilter(String qualifiedStatus, QualifiedRuleContext qualifiedRuleContext) { + if (!"qualified".equals(qualifiedStatus) && !"unqualified".equals(qualifiedStatus)) { + return false; + } + return qualifiedRuleContext != null + && qualifiedRuleContext.getStandardActivityNum() != null + && qualifiedRuleContext.getStandardActivityNum() > 0; + } + + /** + * 从完整结果中截取页面当前页。 + * 仅用于达标状态筛选场景,因为该场景必须先得到筛选后的总数再分页。 + */ + private List pageSubList(List list, int pageNumber, int pageSize) { + if (CollectionUtil.isEmpty(list)) { + return Collections.emptyList(); + } + int fromIndex = Math.max(0, (pageNumber - 1) * pageSize); + if (fromIndex >= list.size()) { + return Collections.emptyList(); + } + int toIndex = Math.min(fromIndex + pageSize, list.size()); + return list.subList(fromIndex, toIndex); + } + + private boolean sameActivityIds(List left, List right) { + Set leftSet = left == null ? Collections.emptySet() : new HashSet<>(left); + Set rightSet = right == null ? Collections.emptySet() : new HashSet<>(right); + return leftSet.equals(rightSet); + } + + /** + * 达标筛选按关联项目配置生效。 + * 当页面选择“全部”时直接返回原列表;若当前活动没有解析出关联达标配置,则保底返回原列表,避免把列表直接筛空。 + */ + private List filterByQualifiedStatus(List userSummaryList, Integer standardActivityNum, String qualifiedStatus) { + // “全部”是用户主动不筛选,直接返回原列表。 + if (Strings.isBlank(qualifiedStatus) || "all".equals(qualifiedStatus)) { + return userSummaryList; + } + // 页面明确选择了“已达标/未达标”,但当前活动没有关联达标配置时,先保底返回原列表,避免页面完全查不出数据。 + if (standardActivityNum == null || standardActivityNum <= 0) { + return userSummaryList; + } + if ("qualified".equals(qualifiedStatus)) { + return userSummaryList.stream() + .filter(item -> item.getInt("qualifiedActivityNum") >= standardActivityNum) + .collect(Collectors.toList()); + } + if ("unqualified".equals(qualifiedStatus)) { + return userSummaryList.stream() + .filter(item -> item.getInt("qualifiedActivityNum") < standardActivityNum) + .collect(Collectors.toList()); + } + return userSummaryList; + } + + /** + * 查询人员打卡统计页活动列表。 + * 该方法只服务人员打卡统计页,直接从云数据库读取,避免复用公共活动级联影响其他页面。 + */ + private List queryPunchActivityList(long startTs, long endTs) { + StringBuilder sql = new StringBuilder("db.collection('activity').where({activityModel:'"); + sql.append(FitnessWalkMode.PUNCH.getValue()) + .append("',startTime:_.gte(").append(startTs).append(").and(_.lte(").append(endTs).append("))") + .append("}).field({") + .append("_id:true,") + .append("masterActivityName:true,") + .append("branchActivityName:true,") + .append("relationActivityId:true,") + .append("activityModel:true,") + .append("startTime:true,") + .append("unionId:true") + .append("}).orderBy('startTime','desc').limit(1000).get()"); + JSONObject jsonObject = weAppCloudUtil.request(WeAppCloudUtil.CRUD.QUERY, sql.toString()); + if (jsonObject.getJSONArray("data") == null || jsonObject.getJSONArray("data").isEmpty()) { + return Collections.emptyList(); + } + List activityList = jsonObject.getJSONArray("data").stream() + .map(item -> JSON.parseObject((String) item, FitnessWalkActivity.class)) + .collect(Collectors.toList()); + if (AuthUtil.hasRoleOr("BRANCH_UNION_CHAIRMAN", "BRANCH_UNION_OPERATOR", "BRANCH_UNION_ADMIN") && !AuthUtil.hasRoleOr("SYSADMIN", "SCHOOL_UNION_ADMIN")) { + activityList = activityList.stream().filter(activity -> SecurityUtil.getUnionId().equals(activity.getUnionId())).collect(Collectors.toList()); + } + return activityList; + } + + /** + * 查询关联项目下全部打卡活动。 + * 人员打卡统计页的子活动列表和主活动汇总都应使用完整关联范围,不能只取年份筛选后的活动。 + */ + private List queryRelationActivityList(String relationActivityId, String activityModel) { + if (Strings.isBlank(relationActivityId)) { + return Collections.emptyList(); + } + StringBuilder sql = new StringBuilder("db.collection('activity').where({relationActivityId:'"); + sql.append(relationActivityId) + .append("',activityModel:'").append(activityModel).append("'") + .append("}).field({") + .append("_id:true,") + .append("masterActivityName:true,") + .append("branchActivityName:true,") + .append("relationActivityId:true,") + .append("activityModel:true,") + .append("startTime:true,") + .append("unionId:true") + .append("}).orderBy('startTime','desc').limit(1000).get()"); + JSONObject jsonObject = weAppCloudUtil.request(WeAppCloudUtil.CRUD.QUERY, sql.toString()); + if (jsonObject.getJSONArray("data") == null || jsonObject.getJSONArray("data").isEmpty()) { + return Collections.emptyList(); + } + List activityList = jsonObject.getJSONArray("data").stream() + .map(item -> JSON.parseObject((String) item, FitnessWalkActivity.class)) + .collect(Collectors.toList()); + if (AuthUtil.hasRoleOr("BRANCH_UNION_CHAIRMAN", "BRANCH_UNION_OPERATOR", "BRANCH_UNION_ADMIN") && !AuthUtil.hasRoleOr("SYSADMIN", "SCHOOL_UNION_ADMIN")) { + activityList = activityList.stream().filter(activity -> SecurityUtil.getUnionId().equals(activity.getUnionId())).collect(Collectors.toList()); + } + return activityList; + } + + /** + * 构建人员打卡统计页主活动分组键。 + * 有关联项目时按关联项目聚合;历史活动没有关联项目时退回按自身活动ID展示。 + */ + private String buildUserPunchCascaderGroupKey(FitnessWalkActivity activity) { + if (Strings.isNotBlank(activity.getRelationActivityId())) { + return RELATION_ACTIVITY_PREFIX + activity.getRelationActivityId(); + } + return Strings.sNull(activity.get_id()); + } + + /** + * 构建人员打卡统计页主活动父级值。 + * relation: 前缀用于后端区分“关联项目ID”和普通活动ID。 + */ + private String buildUserPunchCascaderParentValue(FitnessWalkActivity activity) { + if (Strings.isNotBlank(activity.getRelationActivityId())) { + return RELATION_ACTIVITY_PREFIX + activity.getRelationActivityId(); + } + return activity.get_id(); + } + + private boolean isRelationActivityValue(String value) { + return Strings.sNull(value).startsWith(RELATION_ACTIVITY_PREFIX); + } + + private String getRelationActivityId(String value) { + return Strings.sNull(value).substring(RELATION_ACTIVITY_PREFIX.length()); + } + + /** + * 根据关联项目ID查询活动ID。 + * 人员打卡统计页主活动父级直接传 relation:关联项目ID 时走这里,不依赖公共活动缓存。 + */ + private List queryActivityIdsByRelationId(String relationActivityId, String activityModel) { + if (Strings.isBlank(relationActivityId)) { + return Collections.emptyList(); + } + StringBuilder sql = new StringBuilder("db.collection('activity').where({relationActivityId:'"); + sql.append(relationActivityId) + .append("',activityModel:'").append(activityModel).append("'") + .append("}).field({_id:true}).limit(1000).get()"); + JSONObject jsonObject = weAppCloudUtil.request(WeAppCloudUtil.CRUD.QUERY, sql.toString()); + if (jsonObject.getJSONArray("data") == null || jsonObject.getJSONArray("data").isEmpty()) { + return Collections.emptyList(); + } + return jsonObject.getJSONArray("data").stream() + .map(item -> JSON.parseObject((String) item).getString("_id")) + .filter(Strings::isNotBlank) + .distinct() + .collect(Collectors.toList()); + } + + /** + * 根据关联项目ID构造达标规则上下文。 + * 父级传 relation:关联项目ID 时无需先取某个最新活动,直接按关联项目计算达标范围。 + */ + private QualifiedRuleContext buildQualifiedRuleContextByRelationId(String relationActivityId, String activityModel) { + if (Strings.isBlank(relationActivityId)) { + return QualifiedRuleContext.empty(); + } + FitnessWalkActivityRelationProject relationProject = dao().fetch( + FitnessWalkActivityRelationProject.class, + Cnd.where("id", "=", relationActivityId) + ); + if (relationProject == null || relationProject.getStandardActivityNum() == null || relationProject.getStandardActivityNum() <= 0) { + return QualifiedRuleContext.empty(); + } + List qualifiedActivityIds = queryActivityIdsByRelationId(relationActivityId, activityModel); + return new QualifiedRuleContext(relationActivityId, relationProject.getStandardActivityNum(), qualifiedActivityIds); + } + + /** + * 解析当前查询的关联达标规则。 + * 筛选“已达标/未达标”时统一以关联项目下全部同模式活动作为判断范围,而不是当前列表的展示范围。 + */ + private QualifiedRuleContext resolveQualifiedRuleContext(String mainActivityId, String subActivityId, String activityId) { + String selectedActivityId = resolveSelectedActivityId(mainActivityId, subActivityId, activityId); + if (Strings.isBlank(selectedActivityId)) { + return QualifiedRuleContext.empty(); + } + + if (isRelationActivityValue(selectedActivityId)) { + return buildQualifiedRuleContextByRelationId(getRelationActivityId(selectedActivityId), FitnessWalkMode.PUNCH.getValue()); + } + + FitnessWalkActivity selectedActivity = fitnessWalkCommonService.getActivity(selectedActivityId); + if (selectedActivity == null || Strings.isBlank(selectedActivity.getRelationActivityId())) { + return QualifiedRuleContext.empty(); + } + + FitnessWalkActivityRelationProject relationProject = dao().fetch( + FitnessWalkActivityRelationProject.class, + Cnd.where("id", "=", selectedActivity.getRelationActivityId()) + ); + if (relationProject == null || relationProject.getStandardActivityNum() == null || relationProject.getStandardActivityNum() <= 0) { + return QualifiedRuleContext.empty(); + } + + List qualifiedActivityIds = fitnessWalkCommonService.getAllActivity().stream() + .filter(activity -> Strings.isNotBlank(activity.get_id())) + .filter(activity -> Strings.sNull(activity.getRelationActivityId()).equals(selectedActivity.getRelationActivityId())) + .filter(activity -> Strings.sNull(activity.getActivityModel()).equals(selectedActivity.getActivityModel())) + .map(FitnessWalkActivity::get_id) + .distinct() + .collect(Collectors.toList()); + if (qualifiedActivityIds.isEmpty()) { + qualifiedActivityIds = List.of(selectedActivityId); + } + return new QualifiedRuleContext(selectedActivity.getRelationActivityId(), relationProject.getStandardActivityNum(), qualifiedActivityIds); + } + + /** + * 统一解析当前页面真正选中的活动ID。 + * 分活动优先,其次主活动,最后兼容旧页面传入的 activityId。 + */ + private String resolveSelectedActivityId(String mainActivityId, String subActivityId, String activityId) { + if (Strings.isNotBlank(subActivityId)) { + return subActivityId; + } + if (Strings.isNotBlank(mainActivityId)) { + return mainActivityId; + } + return activityId; + } + + /** + * 按页面当前主活动/分活动选择,统一解析本次需要查询的活动ID集合。 + * 规则如下: + * 1. 选择了分活动时,只查询当前分活动; + * 2. 只选择主活动时,先读取主活动对应的 relationActivityId,再查询该关联下全部同模式活动; + * 3. 如果主活动未配置关联项目,则退回只查询当前主活动; + * 4. 最后再兼容旧页面仅传 activityId 的场景。 + */ + private List resolveActivityIds(String mainActivityId, String subActivityId, String activityId) { + if (Strings.isNotBlank(subActivityId)) { + return List.of(subActivityId); + } + + if (Strings.isNotBlank(mainActivityId)) { + if (isRelationActivityValue(mainActivityId)) { + List relationActivityIds = queryActivityIdsByRelationId(getRelationActivityId(mainActivityId), FitnessWalkMode.PUNCH.getValue()); + if (!relationActivityIds.isEmpty()) { + return relationActivityIds; + } + return Collections.emptyList(); + } + + FitnessWalkActivity mainActivity = fitnessWalkCommonService.getActivity(mainActivityId); + if (mainActivity == null) { + return List.of(mainActivityId); + } + + if (Strings.isBlank(mainActivity.getRelationActivityId())) { + return List.of(mainActivityId); + } + + List relationActivityIds = fitnessWalkCommonService.getAllActivity().stream() + .filter(activity -> Strings.isNotBlank(activity.get_id())) + .filter(activity -> Strings.sNull(activity.getRelationActivityId()).equals(mainActivity.getRelationActivityId())) + .filter(activity -> Strings.sNull(activity.getActivityModel()).equals(mainActivity.getActivityModel())) + .map(FitnessWalkActivity::get_id) + .collect(Collectors.toList()); + if (!relationActivityIds.isEmpty()) { + return relationActivityIds; + } + return List.of(mainActivityId); + } + + if (Strings.isNotBlank(activityId)) { + return List.of(activityId); + } + return Collections.emptyList(); + } + + /** + * 统一拼装 activity_id 查询条件。 + * 单个活动时仍走等值查询,多个活动时改为 _.in([...]),方便复用到不同集合的聚合语句里。 + */ + private String buildActivityMatchSql(List activityIds) { + return buildFieldMatchSql("activity_id", activityIds); + } + + /** + * 统一拼装集合字段的等值或 in 查询条件。 + * + * @param fieldName 字段名,例如 activity_id、_id + * @param ids 字段取值集合,单个值走等值,多个值走 _.in + * @return 云数据库 where/match 片段 + */ + private String buildFieldMatchSql(String fieldName, List ids) { + List validActivityIds = ids == null ? Collections.emptyList() : ids.stream() + .filter(Strings::isNotBlank) + .distinct() + .collect(Collectors.toList()); + if (validActivityIds.isEmpty()) { + return fieldName + ":''"; + } + if (validActivityIds.size() == 1) { + return fieldName + ":'" + validActivityIds.get(0) + "'"; + } + return fieldName + ":_.in(" + JSON.toJSONString(validActivityIds) + ")"; + } + + /** + * 统一构建报名表查询条件。 + * activityIds 表示活动范围,unionId 表示分工会过滤,loginNames 表示当前查询的人员批次,searchKeyword 表示姓名/工号关键字。 + */ + private String buildRegisterMatchSql(List activityIds, String unionId, List loginNames, String searchKeyword) { + StringBuilder sql = new StringBuilder(buildActivityMatchSql(activityIds)); + if (StrUtil.isNotBlank(unionId)) { + sql.append(",unionid:'").append(unionId).append("'"); + } + if (StrUtil.isNotBlank(searchKeyword)) { + String keyword = escapeCloudRegexKeyword(searchKeyword); + sql.append(",$or:["); + sql.append("{loginname:{$regex:'").append(keyword).append("',$options:'i'}},"); + sql.append("{username:{$regex:'").append(keyword).append("',$options:'i'}}"); + sql.append("]"); + } + if (CollectionUtil.isNotEmpty(loginNames)) { + sql.append(",loginname:_.in(").append(JSON.toJSONString(loginNames)).append(")"); + } + return sql.toString(); + } + + /** + * 云数据库使用 $regex 做姓名/工号模糊查询,先转义用户输入,避免特殊正则字符影响查询范围。 + */ + private String escapeCloudRegexKeyword(String searchKeyword) { + String keyword = Strings.sNull(searchKeyword).trim(); + StringBuilder escaped = new StringBuilder(); + for (int i = 0; i < keyword.length(); i++) { + char ch = keyword.charAt(i); + if (ch == '\\') { + escaped.append("\\\\\\\\"); + } else if (".^$|?*+()[]{}".indexOf(ch) >= 0) { + escaped.append("\\\\"); + } else if (ch == '\'') { + escaped.append("\\"); + } + escaped.append(ch); + } + return escaped.toString(); + } + + private String buildUserActivityKey(String activityId, String loginname) { + return Strings.sNull(activityId) + "||" + Strings.sNull(loginname); + } + + /** + * 云函数聚合返回的数字字段可能是普通数字,也可能是 {$numberInt:xx} 包装结构。 + * 这里统一做兼容,避免不同聚合阶段取值时报错。 + */ + private int parseCloudNumber(Object value) { + if (value == null) { + return 0; + } + if (value instanceof Number) { + return ((Number) value).intValue(); + } + if (value instanceof JSONObject) { + JSONObject jsonObject = (JSONObject) value; + if (jsonObject.containsKey("$numberInt")) { + return jsonObject.getIntValue("$numberInt"); + } + if (jsonObject.containsKey("$numberLong")) { + return jsonObject.getIntValue("$numberLong"); + } + } + return Integer.parseInt(String.valueOf(value)); + } + + /** + * 统一生成活动展示名称。 + * 没有分活动名称时仅显示主活动名称;有分活动名称时按“主活动(分活动)”展示。 + */ + private String buildActivityName(FitnessWalkActivity activity) { + if (activity == null) { + return ""; + } + String masterActivityName = Strings.sNull(activity.getMasterActivityName()).trim(); + String branchActivityName = Strings.sNull(activity.getBranchActivityName()).trim(); + if (Strings.isBlank(branchActivityName)) { + return masterActivityName; + } + return masterActivityName + "(" + branchActivityName + ")"; + } + + /** + * 统计页关联达标规则上下文。 + * 单独封装后,列表展示范围和达标判断范围可以清晰分离。 + */ + private static class QualifiedRuleContext { + private final String relationActivityId; + private final Integer standardActivityNum; + private final List qualifiedActivityIds; + + private QualifiedRuleContext(String relationActivityId, Integer standardActivityNum, List qualifiedActivityIds) { + this.relationActivityId = relationActivityId; + this.standardActivityNum = standardActivityNum; + this.qualifiedActivityIds = qualifiedActivityIds == null ? Collections.emptyList() : qualifiedActivityIds; + } + + private static QualifiedRuleContext empty() { + return new QualifiedRuleContext(null, null, Collections.emptyList()); + } + + public String getRelationActivityId() { + return relationActivityId; + } + + public Integer getStandardActivityNum() { + return standardActivityNum; + } + + public List getQualifiedActivityIds() { + return qualifiedActivityIds; + } } } + + diff --git a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/impl/FitnessWalkRelationActivityCompletionServiceImpl.java b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/impl/FitnessWalkRelationActivityCompletionServiceImpl.java new file mode 100644 index 00000000..63ae4967 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/impl/FitnessWalkRelationActivityCompletionServiceImpl.java @@ -0,0 +1,167 @@ +package com.budwk.app.zhgh.activity.fitnesswalk.service.impl; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.budwk.app.base.service.impl.BaseServiceImpl; +import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkActivity; +import com.budwk.app.zhgh.activity.fitnesswalk.model.FitnessWalkActivityRelationProject; +import com.budwk.app.zhgh.activity.fitnesswalk.service.FitnessWalkRelationActivityCompletionService; +import com.budwk.app.zhgh.activity.fitnesswalk.utils.WeAppCloudUtil; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.lang.Strings; +import org.nutz.lang.util.NutMap; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 健步走关联活动完成情况实现。 + * 这里统一处理“查关联活动配置 + 查当前登录人的打卡情况 + 计算是否满足达标数量”的完整流程。 + */ +@IocBean(args = {"refer:dao"}) +public class FitnessWalkRelationActivityCompletionServiceImpl extends BaseServiceImpl + implements FitnessWalkRelationActivityCompletionService { + + @Inject + private WeAppCloudUtil weAppCloudUtil; + + public FitnessWalkRelationActivityCompletionServiceImpl(Dao dao) { + super(dao); + } + + /** + * 根据关联项目ID查询当前登录人的关联活动完成情况。 + * 只有当前用户在某个活动中完成了全部点位打卡,该活动才计入“已达标活动数”。 + * 最终必须达到关联项目配置的 standardActivityNum,才算整体合格。 + */ + @Override + public boolean isCurrentUserRelationActivityQualified(String relationActivityId,String userId) { + if (Strings.isBlank(relationActivityId)) { + throw new RuntimeException("活动关联ID不能为空"); + } + + if (Strings.isBlank(userId)) { + throw new RuntimeException("未获取到当前登录人"); + } + + FitnessWalkActivityRelationProject relationProject = dao().fetch(FitnessWalkActivityRelationProject.class, + Cnd.where("id", "=", relationActivityId)); + if (relationProject == null) { + throw new RuntimeException("未找到对应的活动关联配置"); + } + + List relationActivityList = queryRelationActivityList(relationActivityId); + Map signedPtsNumMap = queryUserSignedPtsNumMap(userId, relationActivityList); + + int qualifiedActivityNum = (int) relationActivityList.stream() + .filter(activity -> isActivityCompleted(activity, signedPtsNumMap)) + .count(); + int standardActivityNum = relationProject.getStandardActivityNum() == null ? 0 : relationProject.getStandardActivityNum(); + return qualifiedActivityNum == standardActivityNum; + } + + /** + * 查询某个关联项目下的所有活动。 + * 这里只取完成情况计算需要的字段,避免无关字段增加云函数返回体积。 + */ + private List queryRelationActivityList(String relationActivityId) { + StringBuilder sql = new StringBuilder("db.collection('activity').where({relationActivityId:'") + .append(relationActivityId) + .append("'}).field({") + .append("_id:true,") + .append("masterActivityName:true,") + .append("branchActivityName:true,") + .append("pts:true,") + .append("relationActivityId:true") + .append("}).orderBy('createdAt','desc').limit(1000).get()"); + + JSONObject jsonObject = weAppCloudUtil.request(WeAppCloudUtil.CRUD.QUERY, sql.toString()); + if (jsonObject.getJSONArray("data") == null || jsonObject.getJSONArray("data").isEmpty()) { + return new ArrayList<>(); + } + + return jsonObject.getJSONArray("data").stream() + .map(item -> JSON.parseObject((String) item, FitnessWalkActivity.class)) + .collect(Collectors.toList()); + } + + /** + * 按活动维度统计当前登录人在 sign 表中已打卡的点位数量。 + * 这里按 pts_code 去重后再计数,避免同一点位重复打卡时把活动误判成已完成。 + */ + private Map queryUserSignedPtsNumMap(String userId, List relationActivityList) { + if (relationActivityList == null || relationActivityList.isEmpty()) { + return Collections.emptyMap(); + } + + List activityIds = relationActivityList.stream() + .map(FitnessWalkActivity::get_id) + .filter(Strings::isNotBlank) + .collect(Collectors.toList()); + if (activityIds.isEmpty()) { + return Collections.emptyMap(); + } + + StringBuilder sql = new StringBuilder("db.collection('sign').aggregate()"); + sql.append(".match({userid:'").append(userId).append("',activity_id:_.in(") + .append(JSON.toJSONString(activityIds)) + .append(")})"); + sql.append(".group({_id:'$activity_id',signedPtsCodeSet:$.addToSet('$pts_code')})"); + sql.append(".project({_id:0,activityId:'$_id',signedPtsNum:$.size('$signedPtsCodeSet')})"); + sql.append(".end()"); + + JSONObject jsonObject = weAppCloudUtil.request(WeAppCloudUtil.CRUD.AGGREGATE, sql.toString()); + if (jsonObject.getJSONArray("data") == null || jsonObject.getJSONArray("data").isEmpty()) { + return Collections.emptyMap(); + } + + return jsonObject.getJSONArray("data").stream().map(item -> { + JSONObject rowData = JSONObject.parseObject((String) item); + String activityId = rowData.getString("activityId"); + int signedPtsNum = parseCloudNumber(rowData.get("signedPtsNum")); + return NutMap.NEW().addv("activityId", activityId).addv("signedPtsNum", signedPtsNum); + }).collect(Collectors.toMap(v -> v.getString("activityId"), v -> v.getInt("signedPtsNum"))); + } + + /** + * 单个活动只有在存在点位,且当前用户已完成该活动全部点位打卡时,才算这个活动达标。 + */ + private boolean isActivityCompleted(FitnessWalkActivity activity, Map signedPtsNumMap) { + int totalPtsNum = activity.getPts() == null ? 0 : activity.getPts().size(); + int signedPtsNum = signedPtsNumMap.getOrDefault(activity.get_id(), 0); + if (signedPtsNum > totalPtsNum && totalPtsNum > 0) { + signedPtsNum = totalPtsNum; + } + return totalPtsNum > 0 && signedPtsNum >= totalPtsNum; + } + + /** + * 云函数聚合返回的数字字段可能是包装对象,这里统一兼容 Integer、Long 和 {$numberInt:xx} 结构。 + */ + private int parseCloudNumber(Object value) { + if (value == null) { + return 0; + } + if (value instanceof Number) { + return ((Number) value).intValue(); + } + if (value instanceof JSONObject) { + JSONObject jsonObject = (JSONObject) value; + if (jsonObject.containsKey("$numberInt")) { + return jsonObject.getIntValue("$numberInt"); + } + if (jsonObject.containsKey("$numberLong")) { + return jsonObject.getIntValue("$numberLong"); + } + } + return Integer.parseInt(String.valueOf(value)); + } +} + + diff --git a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/impl/FitnessWalkStepRaffleServiceImpl.java b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/impl/FitnessWalkStepRaffleServiceImpl.java index 13e9be54..33cb0636 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/impl/FitnessWalkStepRaffleServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/activity/fitnesswalk/service/impl/FitnessWalkStepRaffleServiceImpl.java @@ -43,14 +43,6 @@ public class FitnessWalkStepRaffleServiceImpl extends BaseServiceImpl nutMaps; if (reach.getIsSimultaneously() != null && reach.getIsSimultaneously()) { - // 可以兼得, 达标了的抽奖就是全部达标的人 + // 可以兼得 nutMaps = list; } else { - // 不可兼得, 这里要排除其他奖项的中奖人员 + // 不可兼得 nutMaps = list.stream().filter(v -> !winningRankLoginNames.contains(v.getString("loginname"))).collect(Collectors.toList()); } - // 抽奖人数 Integer lotteryUserNum = reach.getLotteryUserNum(); - // 奖项名称 String awardName = reach.getAwardName(); - // 这里是随机打乱了再去抽奖 List reachNutMaps = nutMaps.stream().collect(Collectors.collectingAndThen(Collectors.toList(), collected -> { - int size = Math.min(lotteryUserNum, collected.size()); Collections.shuffle(collected); - return collected.subList(0, size); + return collected.subList(0, lotteryUserNum - 1); })); List collect = reachNutMaps.stream().map(v -> { @@ -341,16 +329,16 @@ public class FitnessWalkStepRaffleServiceImpl extends BaseServiceImpl + +
- - - - - - - - - - - - + - - - - 批量删除  - - - - - 删除已选 - - - 全部删除 - - - + + + + 批量删除  + + + + + 删除已选 + + + 全部删除 + + + + v-loading="tableLoading" @selection-change="handleSelectionChange"> @@ -92,14 +102,9 @@ layout("/layouts/platform.html"){ const vue = new Vue({ el: '#app', + mixins: [initTableMixins], data() { return { - v: "index", - stepActive: 0, - tabLoading: false, - subDis: false, - formData: {}, - tableData: [], pageForm: { searchName: "name", searchKeyword: "", @@ -114,7 +119,7 @@ layout("/layouts/platform.html"){ } }, methods: { - dropdownCommand: function (command) { + dropdownCommand(command) { const {type, data} = command if (type == 'delete2') { this.doDelete2() @@ -122,30 +127,33 @@ layout("/layouts/platform.html"){ this.doDelete3() } }, - async doDelete(id) { + doDelete(id) { this.$confirm('是否确定删除!', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning', - callback: async (a, b) => { + callback: (a, b) => { if ("confirm" == a) {//确认后再执行 - const { - code, - data, - msg - } = await this.$axios.post(loc() + "/delete", {ids: JSON.stringify([id])}) - if (code === 0) { - this.$message({message: msg, type: 'success'}); - this.pageData() - } else { - this.$message({message: data.msg, type: 'error'}); - } + this.$axios.post(loc() + "/delete", {ids: JSON.stringify([id])}).then((res) => { + if (res.code == 0) { + this.$message({ + message: res.msg, + type: 'success' + }); + this.pageData() + } else { + this.$message({ + message: res.data.msg, + type: 'error' + }); + } + }) } } }) }, - async doDelete2() { + doDelete2() { if (!this.multipleSelection.length) { this.$notify({ @@ -160,104 +168,67 @@ layout("/layouts/platform.html"){ confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning', - callback: async (a, b) => { + callback: (a, b) => { if ("confirm" === a) {//确认后再执行 const arr = this.multipleSelection.map(v => { return v._id }) - const {code, data, msg} = await this.$axios.post(loc() + "/delete", {ids: JSON.stringify(arr)}) - if (code === 0) { - this.multipleSelection = [] - this.$message({ - message: msg, - type: 'success' - }); - this.pageData() - } else { - this.$message({ - message: data.msg, - type: 'error' - }); - } + this.$axios.post(loc() + "/delete", {ids: JSON.stringify(arr)}).then((res) => { + if (res.code == 0) { + this.multipleSelection = [] + this.$message({ + message: res.msg, + type: 'success' + }); + this.pageData() + } else { + this.$message({ + message: res.data.msg, + type: 'error' + }); + } + }) } } }) }, - async doDelete3() { + doDelete3() { this.$confirm('是否确定删除全部登录记录!', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning', - callback: async (a, b) => { + callback: (a, b) => { if ("confirm" == a) {//确认后再执行 - const {code, data, msg} = await this.$axios.post(loc() + "/deleteAll") - if (code === 0) { - this.multipleSelection = [] - this.$message({ - message: msg, - type: 'success' - }); - this.pageData() - } else { - this.$message({ - message: data.msg, - type: 'error' - }); - } + this.$axios.post(loc() + "/deleteAll").then((res) => { + if (res.code == 0) { + this.multipleSelection = [] + this.$message({ + message: res.msg, + type: 'success' + }); + this.pageData() + } else { + this.$message({ + message: res.data.msg, + type: 'error' + }); + } + }) } } }) }, handleSelectionChange(val) { this.multipleSelection = val; - }, - doSearch() { - this.tabKey = new Date().getTime() - this.pageForm.pageNumber = 1 - this.pageData() - }, - pageOrder(column) {//按字段排序 - this.pageForm.pageOrderName = column.prop; - this.pageForm.pageOrderBy = column.order; - this.pageData(); - }, - pageNumberChange(val) {//页码更新操作 - this.pageForm.pageNumber = val; - this.pageData(); - }, - pageSizeChange(val) {//分页大小更新操作 - this.pageForm.pageSize = val; - this.pageData(); - }, - pageData() {//加载分页数据 - this.tabLoading = true - this.$axios.post(loc() + "/pageData", this.pageForm) - .then(res => { - this.tabLoading = false - if (res.code === 0) { - this.tableData = data.data.list; - this.pageForm.totalCount = data.data.totalCount; - } else { - this.$message({ - message: data.msg, - type: 'error' - }); - } - }) - }, - + } }, - async created() { + created() { this.pageData(); - - const hasAdminRole = this.$auth.hasRoleOr("SYSADMIN, SCHOOL_UNION_ADMIN") - if (hasAdminRole) { - this.unionList = await this.$businessTool.listUnion() - } else { - this.unionList = await this.$businessTool.listUnion(this.$store.user.union.id) - } + this.$businessTool.listUnion(null).then((res) => { + this.unionList = res + }) } }) diff --git a/src/main/resources/views/platform/zhgh/activity/fitnesswalk/punchStatistics.html b/src/main/resources/views/platform/zhgh/activity/fitnesswalk/punchStatistics.html index 3c0f2ef1..a099369a 100644 --- a/src/main/resources/views/platform/zhgh/activity/fitnesswalk/punchStatistics.html +++ b/src/main/resources/views/platform/zhgh/activity/fitnesswalk/punchStatistics.html @@ -5,55 +5,86 @@ layout("/layouts/platform.html"){ - - - - - - - + + + + + + + + @@ -111,15 +150,24 @@ layout("/layouts/platform.html"){
- - - - - - - - - + + + + + + + + + +