fix: 优化活动人数限制2

This commit is contained in:
2026-09-11 14:23:37 +08:00
parent f3c3adaa80
commit 52d5591d11
9 changed files with 139 additions and 22 deletions
@@ -73,6 +73,7 @@ public class TrainSignUpAdjustController {
public Result pageData(PageForm pageForm,
@Param(value = "activityId") String activityId) {
Pagination pagination = trainSignUpActivityStatisticsService.pageData(pageForm, activityId);
fillMaxPeopleNumber(pagination.getList());
return Result.success(pagination);
}
@@ -103,9 +104,32 @@ public class TrainSignUpAdjustController {
c.put("registerNum", trainSignUpActivityStatisticsService.queryCourseCount(c.getString("id"), c.getString("courseType")));
c.put("hasWaitingNum", trainSignUpActivityStatisticsService.queryCourseWaitCount(c.getString("id"), c.getString("courseType")));
});
fillMaxPeopleNumber(courseList);
return Result.success(courseList);
}
private void fillMaxPeopleNumber(List<NutMap> rows) {
if (rows == null || rows.isEmpty()) return;
List<String> courseIds = rows.stream().map(v -> v.getString("id")).toList();
List<TrainSignUpCourse> courses = dao.query(TrainSignUpCourse.class, Cnd.where("id", "in", courseIds));
List<TrainSignUpActivityCourse> times = dao.query(TrainSignUpActivityCourse.class, Cnd.where("courseId", "in", courseIds));
NutMap maximums = NutMap.NEW();
for (TrainSignUpCourse course : courses) {
Long maximum = course.getCoursePeopleNumber() > 0 ? (long) course.getCoursePeopleNumber() : null;
if (Boolean.TRUE.equals(course.getCourseIsLimitApply())) {
long timeTotal = times.stream().filter(v -> course.getId().equals(v.getCourseId()))
.mapToLong(v -> v.getCourseLimitNum() == null ? 0 : v.getCourseLimitNum()).sum();
maximum = maximum == null ? timeTotal : Math.min(maximum, timeTotal);
}
if (course.getUnionLimit() != null && !course.getUnionLimit().isEmpty()) {
long unionTotal = course.getUnionLimit().stream().mapToLong(v -> v.getInt("limitCount", 0)).sum();
maximum = maximum == null ? unionTotal : Math.min(maximum, unionTotal);
}
maximums.put(course.getId(), maximum);
}
rows.forEach(v -> v.put("maxPeopleNumber", maximums.get(v.getString("id"))));
}
@At
@ApiOperation("报名用户列表")
@SaCheckPermission("trainSignUp.adjust")
@@ -194,6 +194,9 @@ public class TrainSignUpApplyController {
}
}
}
Date now = new Date();
c.put("allTimesExpired", !courseTimes.isEmpty() && courseTimes.stream()
.allMatch(v -> v.getCourseEndTime() != null && !v.getCourseEndTime().after(now)));
});
return Result.success(pagination);
}
@@ -70,6 +70,28 @@ public class TrainSignUpActivityStatisticsController {
@Inject
private Dao dao;
private void fillMaxPeopleNumber(List<NutMap> rows) {
if (rows == null || rows.isEmpty()) return;
List<String> courseIds = rows.stream().map(v -> v.getString("id")).toList();
List<TrainSignUpCourse> courses = dao.query(TrainSignUpCourse.class, Cnd.where("id", "in", courseIds));
List<TrainSignUpActivityCourse> times = dao.query(TrainSignUpActivityCourse.class, Cnd.where("courseId", "in", courseIds));
NutMap maximums = NutMap.NEW();
for (TrainSignUpCourse course : courses) {
Long maximum = course.getCoursePeopleNumber() > 0 ? (long) course.getCoursePeopleNumber() : null;
if (Boolean.TRUE.equals(course.getCourseIsLimitApply())) {
long timeTotal = times.stream().filter(v -> course.getId().equals(v.getCourseId()))
.mapToLong(v -> v.getCourseLimitNum() == null ? 0 : v.getCourseLimitNum()).sum();
maximum = maximum == null ? timeTotal : Math.min(maximum, timeTotal);
}
if (course.getUnionLimit() != null && !course.getUnionLimit().isEmpty()) {
long unionTotal = course.getUnionLimit().stream().mapToLong(v -> v.getInt("limitCount", 0)).sum();
maximum = maximum == null ? unionTotal : Math.min(maximum, unionTotal);
}
maximums.put(course.getId(), maximum);
}
rows.forEach(v -> v.put("maxPeopleNumber", maximums.get(v.getString("id"))));
}
@At("")
@SaCheckPermission("trainSignUp.statistics")
@Ok("beetl:/platform/zhgh/activity/trainSignUp/statistics/index.html")
@@ -82,6 +104,7 @@ public class TrainSignUpActivityStatisticsController {
public Result pageData(PageForm pageForm,
@Param(value = "activityId") String activityId) {
Pagination pagination = trainSignUpActivityStatisticsService.pageData(pageForm, activityId);
fillMaxPeopleNumber(pagination.getList());
return Result.success(pagination);
}
@@ -75,7 +75,7 @@ const courseList = {
</el-table-column>
<el-table-column label="操作" width="100">
<template v-slot="{row}">
<el-button v-if="!row.isSign" type="primary" size="mini" @click="onSign(row)">我要报名</el-button>
<el-button v-if="!row.isSign" type="primary" size="mini" :style="(isAllTimesExpired(row) || isQuotaFull(row)) ? { color: '#c0c4cc', borderColor: '#e4e7ed', cursor: 'not-allowed' } : {}" @click="onSign(row)">我要报名</el-button>
<el-button v-else type="danger" size="mini" @click="onCancel(row)">取消报名</el-button>
</template>
</el-table-column>
@@ -152,6 +152,21 @@ const courseList = {
}
},
methods: {
isQuotaFull(row) {
if (!Number(row.coursePeopleNumber) && row.quotaScope !== "本工会") return false
const remaining = Number(row.coursePeopleNumber) - (Number(row.hasRegisterNum) + Number(row.courseReservedNumber))
const waitingRemaining = Number(row.waitingNum) - Number(row.hasWaitingNum)
return remaining <= 0 && !(row.reserveMode === 2 && waitingRemaining > 0)
},
isAllTimesExpired(row) {
if (row.allTimesExpired === true) return true
const times = row.courseTimes || []
const now = this.$moment()
return times.length > 0 && times.every(item => {
const endTime = this.$moment(item.courseEndTime)
return endTime.isValid() && endTime.isSameOrBefore(now)
})
},
async onPreview(introduce) {
try {
const parser = new DOMParser();
@@ -187,6 +202,20 @@ const courseList = {
this.queryCourseAssort()
},
onSign(row) {
if (this.isAllTimesExpired(row)) {
this.$alert("该活动所有时段均已结束,无法报名", "提示", {
confirmButtonText: "确定",
type: "warning"
})
return
}
if (this.isQuotaFull(row)) {
this.$alert("当前没有可用报名名额,暂无法报名", "提示", {
confirmButtonText: "确定",
type: "warning"
})
return
}
const user = this.$store.state.user
const missingFields = []
if (!user?.unit?.name) missingFields.push("所属单位")
@@ -41,14 +41,9 @@ layout("/layouts/platform.html"){
<el-table-column label="类型" prop="courseType" sortable show-overflow-tooltip></el-table-column>
<el-table-column :label="trainType + '地点'" prop="courseLocation" sortable show-overflow-tooltip></el-table-column>
<el-table-column label="负责人" prop="courseInstructor" sortable show-overflow-tooltip></el-table-column>
<el-table-column label="最多报名人数" prop="coursePeopleNumber" sortable align="center" header-align="center" show-overflow-tooltip>
<el-table-column label="最多报名人数" prop="maxPeopleNumber" sortable align="center" header-align="center" show-overflow-tooltip>
<template v-slot="{row}">
<span v-if="row.reserveMode === 1">
{{row.coursePeopleNumber}}
</span>
<span v-if="row.reserveMode === 2">
{{(row.coursePeopleNumber + row.waitingNum) + '(候补占' + row.waitingNum + ')'}}
</span>
{{row.maxPeopleNumber == null ? '—' : row.maxPeopleNumber}}
</template>
</el-table-column>
<el-table-column label="预留人数" prop="courseReservedNumber" sortable show-overflow-tooltip></el-table-column>
@@ -98,7 +93,7 @@ layout("/layouts/platform.html"){
{ label: "类型", prop: "courseType", sortable: true },
{ label: "地点", prop: "courseLocation" },
{ label: "教师", prop: "courseInstructor", sortable: true },
{ label: "最多报名人数", prop: "coursePeopleNumber", sortable: true },
{ label: "最多报名人数", prop: "maxPeopleNumber", sortable: true },
{ label: "已报名人数", prop: "registerNum", sortable: true }
],
trainType: "培训班",
@@ -41,14 +41,9 @@ layout("/layouts/platform.html"){
sortable
show-overflow-tooltip
></el-table-column>
<el-table-column label="最多报名人数" prop="coursePeopleNumber" sortable show-overflow-tooltip>
<el-table-column label="最多报名人数" prop="maxPeopleNumber" sortable show-overflow-tooltip>
<template v-slot="{row}">
<span v-if="row.reserveMode === 1">
{{row.coursePeopleNumber}}
</span>
<span v-if="row.reserveMode === 2">
{{(row.coursePeopleNumber + row.waitingNum) + '(候补占' + row.waitingNum + ')'}}
</span>
{{row.maxPeopleNumber == null ? '—' : row.maxPeopleNumber}}
</template>
</el-table-column>
<el-table-column label="预留人数" prop="courseReservedNumber" sortable show-overflow-tooltip></el-table-column>
@@ -98,7 +93,7 @@ layout("/layouts/platform.html"){
{ label: "类型", prop: "courseType", sortable: true },
{ label: "地点", prop: "courseLocation" },
{ label: "教师", prop: "courseInstructor", sortable: true },
{ label: "最多报名人数", prop: "coursePeopleNumber", sortable: true },
{ label: "最多报名人数", prop: "maxPeopleNumber", sortable: true },
{ label: "已报名人数", prop: "registerNum", sortable: true }
],
}
@@ -67,8 +67,8 @@ const userInfo = {
header-align="center"
show-overflow-tooltip
></el-table-column>
<el-table-column label="最多报名人数" prop="coursePeopleNumber" align="center" header-align="center">
<template v-slot="{row}">{{row.coursePeopleNumber === 0 ? '不限制' : row.coursePeopleNumber}}</template>
<el-table-column label="最多报名人数" prop="maxPeopleNumber" align="center" header-align="center">
<template v-slot="{row}">{{row.maxPeopleNumber == null ? '—' : row.maxPeopleNumber}}</template>
</el-table-column>
<el-table-column label="预留名额" prop="courseReservedNumber" align="center" header-align="center"></el-table-column>
<el-table-column label="已报名人数" prop="registerNum" align="center" header-align="center"></el-table-column>
@@ -89,8 +89,25 @@ const applyForm = {
})
},
onCourseConfirm(val){
if (!val || val.disabled || val.endTime <= Date.now()) {
this.$toast("该时段不可报名,请选择其他时段")
const activeTimes = this.courseTimeSelectList.filter(item => item.endTime > Date.now())
if (activeTimes.length === 0) {
this.$toast("当前没有未结束的报名时段,无法报名")
return
}
if (activeTimes.every(item => item.disabled)) {
this.$toast("当前可选时段名额均已满,暂无法报名")
return
}
if (!val) {
this.$toast("请选择报名时段")
return
}
if (val.endTime <= Date.now()) {
this.$toast("所选时段已结束,请选择其他时段")
return
}
if (val.disabled) {
this.$toast("所选时段名额已满,请选择其他时段")
return
}
this.formData.activityCourseId = val.value
@@ -66,7 +66,7 @@ layout("/layouts/platform_h5.html"){
<span>微信群二维码</span>
</div>
<template v-if="$moment().isBefore($moment(activity.activitySignUpEndTime))">
<div v-if="row.isSign === false" class="action-btn" @click="onApply(row)">
<div v-if="row.isSign === false" class="action-btn" :style="(isAllTimesExpired(row) || isQuotaFull(row)) ? { color: '#c8c9cc', cursor: 'not-allowed' } : {}" @click="onApply(row)">
<i class="fa fa-sign-in"></i>
<span>我要报名</span>
</div>
@@ -131,6 +131,21 @@ layout("/layouts/platform_h5.html"){
}
},
methods: {
isQuotaFull(row) {
if (!Number(row.coursePeopleNumber) && row.quotaScope !== "本工会") return false
const remaining = Number(row.coursePeopleNumber) - (Number(row.hasRegisterNum) + Number(row.courseReservedNumber))
const waitingRemaining = Number(row.waitingNum) - Number(row.hasWaitingNum)
return remaining <= 0 && !(row.reserveMode === 2 && waitingRemaining > 0)
},
isAllTimesExpired(row) {
if (row.allTimesExpired === true) return true
const times = row.courseTimes || []
const now = this.$moment()
return times.length > 0 && times.every(item => {
const endTime = this.$moment(item.courseEndTime)
return endTime.isValid() && endTime.isSameOrBefore(now)
})
},
tabClick(name) {
this.pageForm.assortTypes = []
this.pageForm.assortTypes.push(name)
@@ -160,6 +175,22 @@ layout("/layouts/platform_h5.html"){
this.$refs.timesRef.onOpen(row, timeQuotas)
},
onApply(row) {
if (this.isAllTimesExpired(row)) {
vant.Dialog.alert({
title: '提示',
message: '该活动所有时段均已结束,无法报名',
confirmButtonColor: '#1867b0'
})
return
}
if (this.isQuotaFull(row)) {
vant.Dialog.alert({
title: '提示',
message: '当前没有可用报名名额,暂无法报名',
confirmButtonColor: '#1867b0'
})
return
}
const user = this.$store.state.user
const missingFields = []
if (!user?.unit?.name) missingFields.push("所属单位")