From 3f1a209f9910421758b863a6f3bdc578821591f2 Mon Sep 17 00:00:00 2001 From: zhouhefeng Date: Wed, 7 Oct 2026 12:00:27 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=81=A5=E6=AD=A5=E8=B5=B0?= =?UTF-8?q?=E6=8A=BD=E5=A5=96=E8=A7=84=E5=88=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FitnessWalkStepRaffleController.java | 54 +++++---- .../service/FitnessWalkStepRaffleService.java | 18 ++- .../FitnessWalkStepRaffleServiceImpl.java | 107 +++++++++++++++--- .../zhgh/activity/fitnesswalk/stepRaffle.html | 59 ++++++---- 4 files changed, 176 insertions(+), 62 deletions(-) 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 86ebe2d..a75d328 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 @@ -22,10 +22,8 @@ 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; import org.nutz.ioc.aop.Aop; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; @@ -89,17 +87,17 @@ public class FitnessWalkStepRaffleController { /** - * 手动抽奖规则 - * @param fitnessWalkRaffleRules - * @return + * 保存手动抽奖规则并返回实际抽取结果。 + * @param fitnessWalkRaffleRules 活动 ID 及奖项规则,包含奖项名称、抽奖人数、类型、位次和兼得设置 + * @return Result,code 为 0 表示抽奖完成,msg 包含人数不足奖项的实际抽取说明 */ @At @Aop(TransAop.READ_COMMITTED) @SaCheckPermission("fitnessWalk.stepRaffle") public Result saveRaffle(FitnessWalkRaffleRules fitnessWalkRaffleRules){ baseService.dao().insertOrUpdate(fitnessWalkRaffleRules); - fitnessWalkStepRaffleService.manualLottery(fitnessWalkRaffleRules.getActivityId()); - return Result.success(); + String message = fitnessWalkStepRaffleService.manualLottery(fitnessWalkRaffleRules.getActivityId()); + return Result.success(message); } @@ -194,26 +192,37 @@ public class FitnessWalkStepRaffleController { } + /** + * 后台查看当前活动的全部中奖人员。 + * + * @param activityId 当前活动 ID,必填 + * @param lotteryTime 奖项名称(沿用页面参数名),空值查询全部奖项 + * @param searchKeyword 姓名或工号关键字,空值查询全部中奖人员 + * @return Result,data 为中奖人员 Map 列表,isRaffleText 表示是否已在手机端抽奖 + */ + @At + @SaCheckPermission("fitnessWalk.stepRaffle") + public Result awardUsers(String activityId, String lotteryTime, String searchKeyword) { + if (StrUtil.isBlank(activityId)) { + return Result.error("请选择活动"); + } + return Result.success(fitnessWalkStepRaffleService.getAwardUsers(activityId, lotteryTime, searchKeyword)); + } + + /** + * 导出当前筛选条件下的全部中奖人员,包含手机端是否已抽奖。 + * + * @param searchKeyword 姓名或工号关键字,空值不限制人员 + * @param activityId 当前活动 ID,空值导出空名单 + * @param lotteryTime 奖项名称(沿用页面参数名),空值不限制奖项 + * @param response HTTP 下载响应,写入包含人员、奖项、是否抽奖及中奖时间的 XSSF 工作簿 + */ @At @Ok("void") @SaCheckPermission("fitnessWalk.stepRaffle") public void exportAwardExcel(String searchKeyword, String activityId, String lotteryTime, HttpServletResponse response) { - CndPlus cnd = CndPlus.create(); - cnd.andEX("activityId", "=", activityId); - cnd.andEX("awardName", "=", lotteryTime); - if (StrUtil.isNotBlank(searchKeyword)) { - SqlExpressionGroup seg = new SqlExpressionGroup(); - seg.orLike("username", searchKeyword); - seg.orLike("loginName", searchKeyword); - cnd.and(seg); - } - cnd.and("isRead", "=", true); - List list = fitnessWalkCommonService.dao().query(FitnessWalkAwardUser.class, cnd); - - for (FitnessWalkAwardUser awardUser : list) { - awardUser.setApplyDate(awardUser.getApplyDate()); - } + List list = fitnessWalkStepRaffleService.getAwardUsers(activityId, lotteryTime, searchKeyword); List entityList = new ArrayList<>(); ExcelExportEntity no = new ExcelExportEntity("序号", "no", 10); @@ -224,6 +233,7 @@ public class FitnessWalkStepRaffleController { entityList.add(new ExcelExportEntity("分工会", "unionName", 20)); entityList.add(new ExcelExportEntity("单位", "unitName", 40)); entityList.add(new ExcelExportEntity("奖项名称", "awardName", 20)); + entityList.add(new ExcelExportEntity("是否抽奖", "isRaffleText", 15)); entityList.add(new ExcelExportEntity("中奖时间", "applyDate2", 20)); try { 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 fbecab6..126c1dd 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 @@ -19,6 +19,17 @@ public interface FitnessWalkStepRaffleService extends BaseService getAwardUsers(String activityId, String awardName, String searchKeyword); + /** * 获取活动的奖项 * @param activityId @@ -27,10 +38,11 @@ public interface FitnessWalkStepRaffleService extends BaseService getActivityAwardList(String activityId); /** - * 手动抽奖 - * @param activityId 活动id + * 按规则重新抽奖,人数不足时按实际可抽人数抽取。 + * @param activityId 活动 ID,用于查询资格及奖项规则、替换本活动中奖名单 + * @return 完成提示,包含人数不足奖项的配置人数及实际抽取人数 */ - void manualLottery(String activityId); + String manualLottery(String activityId); } 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 7d84b7e..3777645 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,6 +43,16 @@ public class FitnessWalkStepRaffleServiceImpl extends BaseServiceImpl= '%s' ".formatted(effectiveLotteryQualificationSteps))); cnd.having(Cnd.NEW().and(new Static(" over3k.standardsDays >= '%s' ".formatted(lotteryQualificationDays)))); @@ -130,13 +143,52 @@ public class FitnessWalkStepRaffleServiceImpl extends BaseServiceImpl getAwardUsers(String activityId, String awardName, String searchKeyword) { + // 未选活动时不查询,防止空筛选条件返回其他活动的中奖人员。 + if (Strings.isBlank(activityId)) { + return Collections.emptyList(); + } + Sql sql = Sqls.create(""" + SELECT + id, activityId, userId, loginName, username, + unionName, unitName, awardName, + CASE WHEN isRead = 1 THEN '是' ELSE '否' END AS isRaffleText, + CASE WHEN isRead = 1 THEN COALESCE(DATE_FORMAT(applyDate, '%Y-%m-%d %H:%i:%s'), '') + ELSE '' END AS applyDate2 + FROM fitness_walk_award_user + $condition + """); + Cnd cnd = Cnd.where("activityId", "=", activityId); + if (Strings.isNotBlank(awardName)) { + cnd.and("awardName", "=", awardName); + } + if (Strings.isNotBlank(searchKeyword)) { + SqlExpressionGroup seg = new SqlExpressionGroup(); + seg.orLike("username", searchKeyword); + seg.orLike("loginName", searchKeyword); + cnd.and(seg); + } + // 手机端抽奖成功会将 isRead 置为 true;false 和历史空值均表示尚未抽奖,仅展示状态,不据此过滤人员。 + sql.setCondition(cnd); + return listMap(sql); + } + /** * 获取活动的奖项 * @param activityId @@ -216,17 +268,14 @@ public class FitnessWalkStepRaffleServiceImpl extends BaseServiceImpl list = listMap(sql); + // 无达标日期时按 0 天保存,候选人员及中奖记录均避免使用 NutMap 默认的 -1。 List raffleUserList = list.stream().map(v -> { FitnessWalkRaffleUser raffleUser = new FitnessWalkRaffleUser(); raffleUser.setId(R.UU32()); @@ -243,16 +293,17 @@ public class FitnessWalkStepRaffleServiceImpl extends BaseServiceImpl winningRankLoginNames = new HashSet<>(); // 获奖人员 List winningUsers = new ArrayList<>(); + // 收集各奖项人数不足的说明,抽奖完成后一次性返回给页面。 + List shortageMessages = new ArrayList<>(); // 获取抽奖规则 FitnessWalkRaffleRules rules = fetch(Cnd.where("activityId", "=", activityId)); @@ -272,8 +323,12 @@ public class FitnessWalkStepRaffleServiceImpl extends BaseServiceImpl v.getInt("total_steps")).reversed()) .toList(); - // 截取当前奖项的中奖人员 - List rankNutMaps = canLotteryUsers.subList(0, lotteryUserNum); + // 排除前序中奖人员后,最多抽取当前剩余人数;没有剩余人员时取空名单并继续后续奖项。 + int actualNum = Math.min(lotteryUserNum, canLotteryUsers.size()); + if (actualNum < lotteryUserNum) { + shortageMessages.add(awardName + ":设置 " + lotteryUserNum + " 人,实际可抽 " + canLotteryUsers.size() + " 人,已抽取 " + actualNum + " 人"); + } + List rankNutMaps = canLotteryUsers.subList(0, actualNum); // 收集成中奖人员存储 List collect = rankNutMaps.stream().map(v -> { FitnessWalkAwardUser awardUser = new FitnessWalkAwardUser(); @@ -289,7 +344,7 @@ public class FitnessWalkStepRaffleServiceImpl extends BaseServiceImpl reachList = manualLotteryRules.stream().filter(v -> "达标".equals(v.getRaffleType())) - .sorted(Comparator.comparing(FitnessWalkRaffleRules.ManualLotteryRules::getRankingPosition)).toList(); + .sorted(Comparator.comparing(FitnessWalkRaffleRules.ManualLotteryRules::getRankingPosition, Comparator.nullsLast(Comparator.naturalOrder()))).toList(); for (FitnessWalkRaffleRules.ManualLotteryRules reach : reachList) { List nutMaps; @@ -317,9 +373,15 @@ public class FitnessWalkStepRaffleServiceImpl extends BaseServiceImpl reachNutMaps = nutMaps.stream().collect(Collectors.collectingAndThen(Collectors.toList(), collected -> { Collections.shuffle(collected); - return collected.subList(0, lotteryUserNum - 1); + return collected.subList(0, actualNum); })); List collect = reachNutMaps.stream().map(v -> { @@ -336,7 +398,7 @@ public class FitnessWalkStepRaffleServiceImpl extends BaseServiceImpl
返回 - 提交 + 提交
@@ -211,7 +211,7 @@ layout("/layouts/platform.html"){ 导出中奖人员 - + @@ -219,6 +219,7 @@ layout("/layouts/platform.html"){ + @@ -300,32 +301,52 @@ layout("/layouts/platform.html"){ this.awardPageData() }) }, + /** + * 使用当前活动、奖项和姓名工号筛选后台完整中奖名单,手机端抽奖状态仅作为展示字段。 + */ awardPageData() { - this.awardPageForm.activityId = this.pageForm.activityId - this.$axios.post('/platform/fitnessWalk/stepWining/prizeUsers', this.awardPageForm).then((resp) => { - if (resp.code === 0) { - this.awardTableData = resp.data - } - }) + this.$set(this.awardPageForm, 'activityId', this.pageForm.activityId) + this.$set(this, 'awardTableData', []) + this.$set(this, 'tableLoading', true) + this.$axios.post('/platform/fitnessWalk/stepRaffle/awardUsers', this.awardPageForm) + .then((resp) => { + if (resp.code === 0) { + this.$set(this, 'awardTableData', resp.data) + } + }) + .finally(() => { + this.$set(this, 'tableLoading', false) + }) }, exportAwardExcel(){ const {searchKeyword,activityId,lotteryTime} = this.awardPageForm window.open( loc() + '/exportAwardExcel?searchKeyword=' + searchKeyword + '&activityId=' + activityId +'&lotteryTime=' + lotteryTime) }, + /** + * 提交当前活动的奖项规则,展示后端返回的实际抽取结果;提交期间避免重复触发重抽。 + */ saveRaffle() { - console.log(this.formData) + if (this.formLoading) { + return + } const formData = Object.assign({}, this.formData) - formData.activityId = this.pageForm.activityId - formData.manualLotteryRules = JSON.stringify(this.formData.manualLotteryRules) - this.$axios.post('/platform/fitnessWalk/stepRaffle/saveRaffle', formData).then((resp) => { - if (resp.code === 0) { - this.$message.success('保存成功') - this.$refs.guava.index() - } else { - this.$message.warning(resp.msg) - } - }) + this.$set(formData, 'activityId', this.pageForm.activityId) + this.$set(formData, 'manualLotteryRules', JSON.stringify(this.formData.manualLotteryRules)) + this.$set(this, 'formLoading', true) + this.$axios.post('/platform/fitnessWalk/stepRaffle/saveRaffle', formData) + .then((resp) => { + if (resp.code === 0) { + // 多个奖项不足时展示完整汇总,并延长提示时间方便核对实际抽取人数。 + this.$message.success({message: resp.msg || '抽奖完成', duration: 10000, showClose: true}) + this.$refs.guava.index() + } else { + this.$message.warning(resp.msg) + } + }) + .finally(() => { + this.$set(this, 'formLoading', false) + }) }, exportExcel(){ const {searchKeyword,activityId,unionId,unitId} = this.pageForm