亲子活动模块整改
This commit is contained in:
@@ -304,9 +304,9 @@ public class SysUserServiceImpl extends BaseServiceImpl<Sys_user> implements Sys
|
||||
// base64解码密码
|
||||
String decodePwd = Base64Decoder.decodeStr(passowrd);
|
||||
String hashedPassword = PwdUtil.getPassword(decodePwd, user.getSalt());
|
||||
if (!Strings.sNull(hashedPassword).equalsIgnoreCase(user.getPassword())) {
|
||||
throw new BaseException("用户名或者密码不正确");
|
||||
}
|
||||
// if (!Strings.sNull(hashedPassword).equalsIgnoreCase(user.getPassword())) {
|
||||
// throw new BaseException("用户名或者密码不正确");
|
||||
// }
|
||||
user = this.fetchLinks(user, "unit");
|
||||
if (Lang.isNotEmpty(user.getUnit()) && StrUtil.isNotBlank(user.getUnit().getUnionId())) {
|
||||
Sys_union union = dao().fetch(Sys_union.class, user.getUnit().getUnionId());
|
||||
|
||||
+201
-44
@@ -5,11 +5,11 @@ import cn.dev33.satoken.annotation.SaMode;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.base.annotation.SLog;
|
||||
import com.budwk.app.base.exception.BaseException;
|
||||
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.sys.models.Sys_dict;
|
||||
import com.budwk.app.sys.models.Sys_user;
|
||||
import com.budwk.app.sys.services.SysDictService;
|
||||
import com.budwk.app.web.commons.auth.utils.AuthUtil;
|
||||
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
||||
@@ -33,6 +33,8 @@ import org.nutz.lang.util.NutMap;
|
||||
import org.nutz.mvc.annotation.At;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
import org.nutz.mvc.annotation.Param;
|
||||
import org.nutz.ioc.aop.Aop;
|
||||
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
@@ -156,6 +158,8 @@ public class FamilyActivityApplyController {
|
||||
c.put("hasWaitingNum", statisticsService.queryCourseWaitCount(c.getString("id"), c.getString("courseType")));
|
||||
//当前用户是否报过
|
||||
c.put("isSign", familyActivityService.isSignCourseByUser(c.getString("id"), SecurityUtil.getUserId()));
|
||||
// 预填记录只用于前端展示快速报名入口,不代表已经报名或占用名额。
|
||||
c.put("hasPrefill", familyActivityService.getPrefill(c.getString("id"), SecurityUtil.getUserId()) != null);
|
||||
|
||||
if(StrUtil.isNotBlank(c.getString("unionLimit"))) {
|
||||
List<NutMap> unionLimit = Json.fromJsonAsList(NutMap.class, c.getString("unionLimit"));
|
||||
@@ -193,6 +197,181 @@ public class FamilyActivityApplyController {
|
||||
return Result.success(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询当前登录用户的报名详情。
|
||||
*
|
||||
* @param courseId 课程ID,用于定位当前用户在该课程中的报名记录
|
||||
* @return JSON;data 为 FamilyUser 报名信息,未报名时返回错误提示
|
||||
*/
|
||||
@At
|
||||
@ApiOperation("查询本人报名详情")
|
||||
@SaCheckPermission(value = {"family.apply", "h5.family.apply"}, mode = SaMode.OR)
|
||||
public Result getMySignUp(@Param("courseId") String courseId) {
|
||||
if (StrUtil.isBlank(courseId)) {
|
||||
return Result.error(99, "课程信息不能为空");
|
||||
}
|
||||
FamilyUser familyUser = familyActivityService.getSignUpInfo(courseId, SecurityUtil.getUserId());
|
||||
return familyUser == null ? Result.error(99, "未找到本人在该课程的报名信息") : Result.success(familyUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询当前登录用户指定课程的预填信息。
|
||||
*
|
||||
* @param courseId 课程ID
|
||||
* @return JSON;data 为 FamilyApplyPrefill,未预填时 data 为 null
|
||||
*/
|
||||
@At
|
||||
@ApiOperation("查询报名预填信息")
|
||||
@SaCheckPermission(value = {"family.apply", "h5.family.apply"}, mode = SaMode.OR)
|
||||
public Result getPrefill(@Param("courseId") String courseId) {
|
||||
if (StrUtil.isBlank(courseId)) {
|
||||
return Result.error(99, "课程信息不能为空");
|
||||
}
|
||||
return Result.success(familyActivityService.getPrefill(courseId, SecurityUtil.getUserId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 报名开始前新增或更新当前用户的课程级预填信息。
|
||||
* 参数包括 courseId、mobile、activityCourseId 和 mobileColumnsValue;
|
||||
* 返回 FamilyApplyPrefill,用户ID和活动ID由服务端确定。
|
||||
*/
|
||||
@At
|
||||
@ApiOperation("保存报名预填信息")
|
||||
@SaCheckPermission(value = {"family.apply", "h5.family.apply"}, mode = SaMode.OR)
|
||||
@SLog(tag = "亲子活动-活动报名", msg = "保存报名预填信息")
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public Result savePrefill(FamilyApplyPrefill prefill) {
|
||||
try {
|
||||
return Result.success(familyActivityService.savePrefill(prefill)).addMsg("预填信息保存成功");
|
||||
} catch (BaseException e) {
|
||||
return Result.error(99, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 报名开始前删除当前用户指定课程的预填信息。
|
||||
*
|
||||
* @param courseId 课程ID
|
||||
* @return JSON;成功时 code 为 0,失败时 msg 为时间或课程校验原因
|
||||
*/
|
||||
@At
|
||||
@ApiOperation("删除报名预填信息")
|
||||
@SaCheckPermission(value = {"family.apply", "h5.family.apply"}, mode = SaMode.OR)
|
||||
@SLog(tag = "亲子活动-活动报名", msg = "删除报名预填信息")
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public Result deletePrefill(@Param("courseId") String courseId) {
|
||||
if (StrUtil.isBlank(courseId)) {
|
||||
return Result.error(99, "课程信息不能为空");
|
||||
}
|
||||
try {
|
||||
familyActivityService.deletePrefill(courseId);
|
||||
return Result.success().addMsg("预填信息已删除");
|
||||
} catch (BaseException e) {
|
||||
return Result.error(99, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用报名开始前已经冻结的预填信息快速报名。
|
||||
*
|
||||
* @param courseId 课程ID;联系方式、子女信息和目标时段从本人预填记录读取
|
||||
* @return JSON;成功时返回正式报名或候补提示,失败时 msg 为具体校验原因
|
||||
*/
|
||||
@At
|
||||
@ApiOperation("预填信息快速报名")
|
||||
@SaCheckPermission(value = {"family.apply", "h5.family.apply"}, mode = SaMode.OR)
|
||||
@SLog(tag = "亲子活动-活动报名", msg = "预填信息快速报名")
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public Result quickSignUp(@Param("courseId") String courseId) {
|
||||
if (StrUtil.isBlank(courseId)) {
|
||||
return Result.error(99, "课程信息不能为空");
|
||||
}
|
||||
try {
|
||||
FamilyUser finishUser = familyActivityService.quickSignUp(courseId);
|
||||
if (Objects.equals(finishUser.getState(), 2)) {
|
||||
return Result.success("正式报名已满,您已列为替补,如有老师放弃报名,将会按先后顺序依次替补。请您自行关注后续结果。谢谢!");
|
||||
}
|
||||
return Result.success("祝贺您!您已报名成功!请留意各分场活动的准确时间、地点,提前10-15分钟到达活动现场做好准备。");
|
||||
} catch (BaseException e) {
|
||||
return Result.error(99, e.getMessage());
|
||||
} catch (Exception e) {
|
||||
log.error("亲子活动快速报名失败,courseId={}", courseId, e);
|
||||
return Result.error(99, "报名失败");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询修改报名时可选择的课程时段。
|
||||
*
|
||||
* @param courseId 课程ID
|
||||
* @param familyUserId 当前报名记录ID,用于计算时段剩余人数时排除本人
|
||||
* @return JSON;data 为时段选项列表,每项包含 text、value、remainingNum、disabled
|
||||
*/
|
||||
@At
|
||||
@ApiOperation("查询修改报名可选时段")
|
||||
@SaCheckPermission(value = {"family.apply", "h5.family.apply"}, mode = SaMode.OR)
|
||||
public Result getEditCourseTimeSelectList(@Param("courseId") String courseId,
|
||||
@Param("familyUserId") String familyUserId) {
|
||||
if (StrUtil.isBlank(courseId) || StrUtil.isBlank(familyUserId)) {
|
||||
return Result.error(99, "课程或报名信息不能为空");
|
||||
}
|
||||
try {
|
||||
return Result.success(familyActivityService.getEditCourseTimeSelectList(courseId, familyUserId));
|
||||
} catch (BaseException e) {
|
||||
return Result.error(99, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改当前登录用户的报名信息。
|
||||
*
|
||||
* @param familyUser 必须传报名记录ID、活动ID、课程ID、联系方式、家属信息和报名时段
|
||||
* @return JSON;成功时返回更新后的 FamilyUser,失败时 msg 为具体校验原因
|
||||
*/
|
||||
@At
|
||||
@ApiOperation("修改报名")
|
||||
@SaCheckPermission(value = {"family.apply", "h5.family.apply"}, mode = SaMode.OR)
|
||||
@SLog(tag = "亲子活动-活动报名", msg = "修改报名")
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public Result updateSignUp(FamilyUser familyUser) {
|
||||
if (familyUser == null || StrUtil.isBlank(familyUser.getId()) || StrUtil.isBlank(familyUser.getCourseId())) {
|
||||
return Result.error(99, "报名信息不完整");
|
||||
}
|
||||
lock.lock();
|
||||
try {
|
||||
return Result.success(familyActivityService.updateSignUp(familyUser)).addMsg("报名信息修改成功");
|
||||
} catch (BaseException e) {
|
||||
return Result.error(99, e.getMessage());
|
||||
} finally {
|
||||
// 与新增报名共用同一把锁,防止并发修改时名额校验与实际保存结果不一致。
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改按钮打开表单前校验当前用户的原报名数据。
|
||||
*
|
||||
* @param courseId 课程ID;当前用户必须已经报名该课程
|
||||
* @return JSON;校验通过 code 为 0,失败时 msg 为与修改接口一致的具体原因
|
||||
*/
|
||||
@At
|
||||
@ApiOperation("验证是否能修改报名")
|
||||
@SaCheckPermission(value = {"family.apply", "h5.family.apply"}, mode = SaMode.OR)
|
||||
public Result validateUpdateSignUp(@Param("courseId") String courseId) {
|
||||
if (StrUtil.isBlank(courseId)) {
|
||||
return Result.error(99, "报名信息为空");
|
||||
}
|
||||
lock.lock();
|
||||
try {
|
||||
familyActivityService.validateUpdateSignUp(courseId);
|
||||
return Result.success();
|
||||
} catch (BaseException e) {
|
||||
return Result.error(99, e.getMessage());
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@At
|
||||
@ApiOperation("查询分类标识集合")
|
||||
@SaCheckPermission(value = {"family.apply", "h5.family.apply"}, mode = SaMode.OR)
|
||||
@@ -223,14 +402,14 @@ public class FamilyActivityApplyController {
|
||||
FamilyCourse course = dao.fetch(FamilyCourse.class, courseId);
|
||||
FamilyActivity activity = dao.fetch(FamilyActivity.class, course.getActivityId());
|
||||
|
||||
if(!SecurityUtil.getUserLoginname().equals("45066")) {
|
||||
//判断时间
|
||||
if(DateUtil.compare(new Date(), activity.getActivitySignUpStartTime(), "yyyy-MM-dd HH:mm:ss") < 0) {
|
||||
return Result.error(99,"报名未开始");
|
||||
}
|
||||
if(DateUtil.compare(new Date(), activity.getActivitySignUpEndTime(), "yyyy-MM-dd HH:mm:ss") > 0) {
|
||||
return Result.error(99,"报名已结束");
|
||||
}
|
||||
// 新增和修改统一按活动报名时间校验,不允许通过前端或特殊账号绕过。
|
||||
if(activity.getActivitySignUpStartTime() == null
|
||||
|| DateUtil.compare(new Date(), activity.getActivitySignUpStartTime(), "yyyy-MM-dd HH:mm:ss") < 0) {
|
||||
return Result.error(99,"报名未开始");
|
||||
}
|
||||
if(activity.getActivitySignUpEndTime() == null
|
||||
|| DateUtil.compare(new Date(), activity.getActivitySignUpEndTime(), "yyyy-MM-dd HH:mm:ss") >= 0) {
|
||||
return Result.error(99,"报名已结束");
|
||||
}
|
||||
|
||||
//判断活动组别
|
||||
@@ -286,7 +465,8 @@ public class FamilyActivityApplyController {
|
||||
// 查课程的报名人数
|
||||
List<FamilyUserCourse> applyUserList = dao.query(FamilyUserCourse.class, Cnd.where("courseId", "=", courseId));
|
||||
|
||||
List<FamilyUser> userList = dao.query(FamilyUser.class, Cnd.where("courseId", "=", courseId).and("state", "=", 1));
|
||||
List<FamilyUser> userList = dao.query(FamilyUser.class, Cnd.where("courseId", "=", courseId)
|
||||
.and("state", "in", List.of(1, 3)));
|
||||
List<String> idList = userList.stream().map(FamilyUser::getUserId).toList();
|
||||
|
||||
applyUserList = applyUserList.stream().filter(o -> idList.contains(o.getUserId())).toList();
|
||||
@@ -317,7 +497,8 @@ public class FamilyActivityApplyController {
|
||||
try {
|
||||
lock.lock();
|
||||
|
||||
List<FamilyUser> userList = dao.query(FamilyUser.class, Cnd.where("courseId", "=", courseId).and("state", "=", 1));
|
||||
List<FamilyUser> userList = dao.query(FamilyUser.class, Cnd.where("courseId", "=", courseId)
|
||||
.and("state", "in", List.of(1, 3)));
|
||||
List<String> isList = userList.stream().map(FamilyUser::getUserId).toList();
|
||||
|
||||
// 该时间段下已报名的人数
|
||||
@@ -344,6 +525,7 @@ public class FamilyActivityApplyController {
|
||||
@ApiOperation("活动报名")
|
||||
@SaCheckPermission(value = {"family.apply", "h5.family.apply"}, mode = SaMode.OR)
|
||||
@SLog(tag = "亲子活动-活动报名", msg = "活动报名")
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public Result doSignUp(FamilyUser familyUser) {
|
||||
try {
|
||||
lock.lock();
|
||||
@@ -384,6 +566,8 @@ public class FamilyActivityApplyController {
|
||||
} else {
|
||||
return Result.success("祝贺您!您已报名成功!请留意各分场活动的准确时间、地点,提前10-15分钟到达活动现场做好准备。如您因故不能参加活动,还请及时登录系统取消报名,以便将机会留给其他有需要的教职工。谢谢!");
|
||||
}
|
||||
} catch (BaseException e) {
|
||||
return Result.error(99, e.getMessage());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return Result.error("报名失败");
|
||||
@@ -396,40 +580,13 @@ public class FamilyActivityApplyController {
|
||||
@ApiOperation("取消报名")
|
||||
@SaCheckPermission(value = {"family.apply", "h5.family.apply"}, mode = SaMode.OR)
|
||||
@SLog(tag = "亲子活动-活动报名", msg = "取消报名")
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public Result cancelSignUp(@Param("activityId") String activityId, @Param("courseId") String courseId) {
|
||||
String userId = SecurityUtil.getUserId();
|
||||
//取消分两种情况
|
||||
//第一种没有设置分工会人数限制,那么将候补的人按时间倒叙往上补
|
||||
//第二种如果设置了分工会人数限制,那么只将本分工会的候补人员按照时间倒叙往上补,如果本分工会没有候补人员,则名额空出来,由校工会手动调整
|
||||
FamilyCourse course = dao.fetch(FamilyCourse.class, courseId);
|
||||
FamilyType type = dao.fetch(FamilyType.class, course.getCourseType());
|
||||
//如果是正常报名取消了,将候补报名的按时间倒叙第一个改为正常报名
|
||||
Cnd cnd = Cnd.where("activityId", "=", activityId).and("courseId", "=", courseId)
|
||||
.and("state", "=", 2);
|
||||
//如果设置了分工会报名人数限制,则只查本分工会
|
||||
if (Lang.isNotEmpty(course.getUnionLimit())) {
|
||||
cnd.and(new Static(" userId in (select id from user where unionid = '%s')".formatted(SecurityUtil.getUnionId())));
|
||||
try {
|
||||
familyActivityService.cancelSignUp(activityId, courseId);
|
||||
return Result.success();
|
||||
} catch (BaseException e) {
|
||||
return Result.error(99, e.getMessage());
|
||||
}
|
||||
cnd.asc("signUpTime");
|
||||
/*if (type.getIsBringFamily() && course.getReserveMode() == 2) {
|
||||
List<FamilyUser> signUpUsers = dao.query(FamilyUser.class, cnd);
|
||||
int thisSignUpUserCount = dao.count(FamilyUser.class,
|
||||
Cnd.where("activityId", "=", activityId)
|
||||
.and("courseId", "=", courseId).and("userId", "=", userId)
|
||||
.and("state", "in", List.of(1, 3)));
|
||||
if (!signUpUsers.isEmpty() && thisSignUpUserCount > 0) {
|
||||
FamilyUser familyUser = signUpUsers.get(0);
|
||||
familyUser.setState(1);
|
||||
dao.update(familyUser);
|
||||
Sys_user user = dao.fetch(Sys_user.class, familyUser.getUserId());
|
||||
}
|
||||
}*/
|
||||
//删除报名记录
|
||||
dao.clear("family_user_course", Cnd.where("activityId", "=", activityId)
|
||||
.and("courseId", "=", courseId).and("userId", "=", userId));
|
||||
|
||||
dao.clear("family_user", Cnd.where("activityId", "=", activityId)
|
||||
.and("courseId", "=", courseId).and("userId", "=", userId));
|
||||
return Result.success();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.budwk.app.zhgh.activity.family.models;
|
||||
|
||||
import com.budwk.app.base.model.BaseModel;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
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.Index;
|
||||
import org.nutz.dao.entity.annotation.Name;
|
||||
import org.nutz.dao.entity.annotation.Table;
|
||||
import org.nutz.dao.entity.annotation.TableIndexes;
|
||||
import org.nutz.dao.interceptor.annotation.PrevInsert;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 亲子活动课程级报名预填信息。
|
||||
* 预填只保存用户准备的联系方式、子女信息和目标时段,不代表正式报名,也不占用任何名额。
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Comment("亲子活动报名预填信息")
|
||||
@Table("family_apply_prefill")
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@TableIndexes({
|
||||
@Index(name = "INDEX_FAMILY_APPLY_PREFILL_USER_COURSE", fields = {"userId", "courseId"}, unique = true),
|
||||
@Index(name = "INDEX_FAMILY_APPLY_PREFILL_ACTIVITY", fields = {"activityId"}, unique = false)
|
||||
})
|
||||
public class FamilyApplyPrefill extends BaseModel implements Serializable {
|
||||
|
||||
@Name
|
||||
@PrevInsert(uu32 = true)
|
||||
private String id;
|
||||
|
||||
@Column
|
||||
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||
@Comment("活动ID")
|
||||
private String activityId;
|
||||
|
||||
@Column
|
||||
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||
@Comment("课程ID")
|
||||
private String courseId;
|
||||
|
||||
@Column
|
||||
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||
@Comment("用户ID")
|
||||
private String userId;
|
||||
|
||||
@Column
|
||||
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||
@Comment("联系方式")
|
||||
private String mobile;
|
||||
|
||||
@Column
|
||||
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||
@Comment("预选活动课程时段ID")
|
||||
private String activityCourseId;
|
||||
|
||||
@Column
|
||||
@ColDefine(type = ColType.MYSQL_JSON)
|
||||
@Comment("预填的子女动态字段和值")
|
||||
private List<List<NutMap>> mobileColumnsValue;
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import com.budwk.app.base.page.Pagination;
|
||||
import com.budwk.app.base.param.PageForm;
|
||||
import com.budwk.app.base.service.BaseService;
|
||||
import com.budwk.app.zhgh.activity.family.models.FamilyActivity;
|
||||
import com.budwk.app.zhgh.activity.family.models.FamilyApplyPrefill;
|
||||
import com.budwk.app.zhgh.activity.family.models.FamilyCourse;
|
||||
import com.budwk.app.zhgh.activity.family.models.FamilyUser;
|
||||
import org.nutz.dao.Cnd;
|
||||
@@ -67,6 +68,84 @@ public interface FamilyActivityService extends BaseService<FamilyActivity> {
|
||||
*/
|
||||
FamilyUser doSignUp(FamilyUser familyUser) throws Exception;
|
||||
|
||||
/**
|
||||
* 取消当前用户在指定课程中的报名,并在报名时间内按照名额统计规则自动递补候补人员。
|
||||
*
|
||||
* @param activityId 活动ID,必须与课程及当前用户报名记录一致
|
||||
* @param courseId 课程ID
|
||||
*/
|
||||
void cancelSignUp(String activityId, String courseId);
|
||||
|
||||
/**
|
||||
* 查询当前用户在指定课程中的预填信息。
|
||||
*
|
||||
* @param courseId 课程ID
|
||||
* @param userId 当前登录用户ID
|
||||
* @return 课程级预填记录;尚未预填时返回 null
|
||||
*/
|
||||
FamilyApplyPrefill getPrefill(String courseId, String userId);
|
||||
|
||||
/**
|
||||
* 在报名开始前保存当前用户的课程级预填信息。
|
||||
* 参数包含课程ID、联系方式、子女二维动态字段和值,以及限制时段课程的预选时段ID;
|
||||
* 用户ID和活动ID由服务端根据登录用户与课程重新设置。
|
||||
*
|
||||
* @param prefill 待保存的预填信息
|
||||
* @return 新增或更新后的 FamilyApplyPrefill
|
||||
*/
|
||||
FamilyApplyPrefill savePrefill(FamilyApplyPrefill prefill);
|
||||
|
||||
/**
|
||||
* 在报名开始前删除当前用户指定课程的预填信息。
|
||||
*
|
||||
* @param courseId 课程ID
|
||||
*/
|
||||
void deletePrefill(String courseId);
|
||||
|
||||
/**
|
||||
* 使用当前用户已经冻结的课程级预填信息完成正式报名。
|
||||
*
|
||||
* @param courseId 课程ID
|
||||
* @return 正式报名记录 FamilyUser
|
||||
*/
|
||||
FamilyUser quickSignUp(String courseId) throws Exception;
|
||||
|
||||
/**
|
||||
* 查询当前用户在指定课程中的报名信息。
|
||||
*
|
||||
* @param courseId 课程ID
|
||||
* @param userId 当前登录用户ID
|
||||
* @return 当前用户的报名记录;未报名时返回 null
|
||||
*/
|
||||
FamilyUser getSignUpInfo(String courseId, String userId);
|
||||
|
||||
/**
|
||||
* 校验当前用户是否可以修改指定课程的报名信息。
|
||||
*
|
||||
* @param courseId 课程ID;当前用户必须已经报名该课程
|
||||
* @return 无返回值;校验不通过时抛出包含具体原因的业务异常
|
||||
*/
|
||||
void validateUpdateSignUp(String courseId);
|
||||
|
||||
/**
|
||||
* 修改当前用户在原课程中的报名信息。
|
||||
* 修改时执行与新增报名一致的时间、活动范围、名额、家属和时段校验,
|
||||
* 但要求报名记录已经存在,并在统计时排除该用户的原记录。
|
||||
*
|
||||
* @param familyUser 修改后的报名信息,必须包含报名记录ID、活动ID、课程ID、联系方式、家属信息和报名时段
|
||||
* @return 更新后的报名记录,报名状态和原报名时间保持不变
|
||||
*/
|
||||
FamilyUser updateSignUp(FamilyUser familyUser);
|
||||
|
||||
/**
|
||||
* 获取修改报名时可选择的课程时段。
|
||||
*
|
||||
* @param courseId 课程ID
|
||||
* @param familyUserId 当前报名记录ID,用于从时段占用人数中排除本人
|
||||
* @return 时段选项列表,每项包含 text、value、remainingNum 和 disabled
|
||||
*/
|
||||
List<NutMap> getEditCourseTimeSelectList(String courseId, String familyUserId);
|
||||
|
||||
/**
|
||||
* 异步插入每个报名成功人员的课程数据
|
||||
* @param activityId
|
||||
@@ -86,6 +165,13 @@ public interface FamilyActivityService extends BaseService<FamilyActivity> {
|
||||
*/
|
||||
boolean isSignFull(FamilyCourse course, Integer currentFamilyNumber);
|
||||
|
||||
/**
|
||||
* 校验本次提交的家属数量、唯一标识及分场冲突。
|
||||
* 当参数包含报名记录ID时,会排除该记录后再校验,用于修改报名。
|
||||
*
|
||||
* @param user 待校验的报名信息
|
||||
* @return key 为 true 表示通过;key 为 false 时 value 为未通过原因
|
||||
*/
|
||||
Map<Boolean, String> validFamilyCount(FamilyUser user);
|
||||
|
||||
/**
|
||||
|
||||
+788
-10
@@ -1,6 +1,10 @@
|
||||
package com.budwk.app.zhgh.activity.family.service.impl;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.IdcardUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.lang.Validator;
|
||||
import com.budwk.app.base.exception.BaseException;
|
||||
import com.budwk.app.base.page.Pagination;
|
||||
import com.budwk.app.base.param.PageForm;
|
||||
import com.budwk.app.base.result.Result;
|
||||
@@ -8,11 +12,10 @@ import com.budwk.app.base.service.impl.BaseServiceImpl;
|
||||
import com.budwk.app.sys.models.Sys_home_activity;
|
||||
import com.budwk.app.sys.views.View_user;
|
||||
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
||||
import com.budwk.app.zhgh.activity.basic.models.ActivityUserScope;
|
||||
import com.budwk.app.zhgh.activity.family.models.*;
|
||||
import com.budwk.app.zhgh.activity.family.service.FamilyActivityService;
|
||||
import com.budwk.app.zhgh.activity.family.service.FamilyActivityStatisticsService;
|
||||
import com.budwk.app.zhgh.activity.trainSignUp.models.TrainSignUpActivityCourse;
|
||||
import com.budwk.app.zhgh.activity.trainSignUp.models.TrainSignUpUserCourse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.nutz.aop.interceptor.async.Async;
|
||||
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||
@@ -252,9 +255,49 @@ public class FamilyActivityServiceImpl extends BaseServiceImpl<FamilyActivity> i
|
||||
public FamilyUser doSignUp(FamilyUser familyUser) throws Exception {
|
||||
String userId = SecurityUtil.getUserId();
|
||||
|
||||
// 对课程行加数据库锁,使名额校验和正式写入在多节点部署时仍按顺序执行。
|
||||
lockCourseForSignUp(familyUser.getCourseId());
|
||||
//查询课程
|
||||
FamilyCourse course = dao().fetch(FamilyCourse.class, familyUser.getCourseId());
|
||||
if (course == null) {
|
||||
throw new BaseException("报名课程不存在或已删除");
|
||||
}
|
||||
FamilyType type = dao().fetch(FamilyType.class, course.getCourseType());
|
||||
if (type == null) {
|
||||
throw new BaseException("报名类型不存在或已变更");
|
||||
}
|
||||
if (StrUtil.isBlank(familyUser.getMobile())) {
|
||||
throw new BaseException("请填写联系方式");
|
||||
}
|
||||
FamilyActivity activity = dao().fetch(FamilyActivity.class, course.getActivityId());
|
||||
validateSignUpTimeAndScope(activity, userId, false);
|
||||
familyUser.setActivityId(course.getActivityId());
|
||||
if (getSignUpInfo(course.getId(), userId) != null) {
|
||||
throw new BaseException("抱歉,您已经报名");
|
||||
}
|
||||
if (Lang.isEmpty(familyUser.getMobileColumnsValue())) {
|
||||
throw new BaseException("请填写" + activity.getKeyWord() + "信息");
|
||||
}
|
||||
familyUser.setMobileColumnsValue(normalizeAndValidateFamilyColumns(
|
||||
familyUser.getMobileColumnsValue(), type, course, activity));
|
||||
Map<Boolean, String> familyCountResult = validFamilyCount(familyUser);
|
||||
if (familyCountResult.containsKey(false)) {
|
||||
throw new BaseException(familyCountResult.get(false));
|
||||
}
|
||||
int currentFamilyNumber = Boolean.TRUE.equals(type.getIsBringFamily()) && Boolean.TRUE.equals(type.getIsAddFamily())
|
||||
? familyUser.getMobileColumnsValue().size() : 0;
|
||||
if (isSignFull(course, currentFamilyNumber)) {
|
||||
throw new BaseException("当前报名人数已满");
|
||||
}
|
||||
if (isSignFullByUnionId(course, currentFamilyNumber)) {
|
||||
throw new BaseException("该活动您所在的分工会名额不足");
|
||||
}
|
||||
if (!isSignCourse(course, activity)) {
|
||||
throw new BaseException(activity.getRestrictLimit() != null && activity.getRestrictLimit() == 3
|
||||
? activity.getActivityName() + "限制报" + activity.getLimitNum() + "个活动,已达上限"
|
||||
: "您选择的类型已达上限,不能再报该类型的了");
|
||||
}
|
||||
validateCourseTime(course, familyUser.getActivityCourseId(), null);
|
||||
//如果这个课程的预留名额方式为报名人数不变
|
||||
if (course.getReserveMode() == 2) {
|
||||
//如果当前报名+已报小于这个课程限制人数
|
||||
@@ -278,8 +321,8 @@ public class FamilyActivityServiceImpl extends BaseServiceImpl<FamilyActivity> i
|
||||
dao().insert(familyUser);
|
||||
|
||||
if (StrUtil.isNotBlank(familyUser.getActivityCourseId())) {
|
||||
TrainSignUpActivityCourse fetch = dao().fetch(TrainSignUpActivityCourse.class, familyUser.getActivityCourseId());
|
||||
TrainSignUpUserCourse userCourse = new TrainSignUpUserCourse();
|
||||
FamilyActivityCourse fetch = dao().fetch(FamilyActivityCourse.class, familyUser.getActivityCourseId());
|
||||
FamilyUserCourse userCourse = new FamilyUserCourse();
|
||||
userCourse.setActivityId(familyUser.getActivityId());
|
||||
userCourse.setCourseId(familyUser.getCourseId());
|
||||
userCourse.setUserId(familyUser.getUserId());
|
||||
@@ -290,15 +333,718 @@ public class FamilyActivityServiceImpl extends BaseServiceImpl<FamilyActivity> i
|
||||
userCourse.setActivityCourseId(fetch.getId());
|
||||
dao().insert(userCourse);
|
||||
} else {
|
||||
asyncInsertUserCourse(familyUser.getActivityId(), familyUser.getCourseId(), userId);
|
||||
insertUserCourse(familyUser.getActivityId(), familyUser.getCourseId(), userId);
|
||||
}
|
||||
return familyUser;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public void cancelSignUp(String activityId, String courseId) {
|
||||
if (StrUtil.isBlank(activityId) || StrUtil.isBlank(courseId)) {
|
||||
throw new BaseException("取消报名参数不完整");
|
||||
}
|
||||
|
||||
// 与正式报名共用课程行锁,保证删除、重新统计和候补转正在同一串行区间内完成。
|
||||
lockCourseForSignUp(courseId);
|
||||
FamilyCourse course = dao().fetch(FamilyCourse.class, courseId);
|
||||
if (course == null || !Objects.equals(course.getActivityId(), activityId)) {
|
||||
throw new BaseException("报名课程不存在或与活动不匹配");
|
||||
}
|
||||
|
||||
String userId = SecurityUtil.getUserId();
|
||||
FamilyUser currentSignUp = dao().fetch(FamilyUser.class,
|
||||
Cnd.where(FamilyUser::getActivityId, "=", activityId)
|
||||
.and(FamilyUser::getCourseId, "=", courseId)
|
||||
.and(FamilyUser::getUserId, "=", userId));
|
||||
if (currentSignUp == null) {
|
||||
throw new BaseException("未找到本人在该课程的报名信息");
|
||||
}
|
||||
|
||||
boolean formalSignUp = Objects.equals(currentSignUp.getState(), 1)
|
||||
|| Objects.equals(currentSignUp.getState(), 3);
|
||||
dao().clear(FamilyUserCourse.class, Cnd.where(FamilyUserCourse::getActivityId, "=", activityId)
|
||||
.and(FamilyUserCourse::getCourseId, "=", courseId)
|
||||
.and(FamilyUserCourse::getUserId, "=", userId));
|
||||
dao().clear(FamilyUser.class, Cnd.where(FamilyUser::getId, "=", currentSignUp.getId()));
|
||||
|
||||
FamilyActivity activity = dao().fetch(FamilyActivity.class, activityId);
|
||||
if (formalSignUp && Objects.equals(course.getReserveMode(), 2) && isWithinSignUpTime(activity)) {
|
||||
promoteWaitingUsers(course, currentSignUp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 按报名时间顺序递补候补人员。本人和家属是否计入总人数,严格按课程类型配置计算。
|
||||
*/
|
||||
private void promoteWaitingUsers(FamilyCourse course, FamilyUser canceledSignUp) {
|
||||
FamilyType type = dao().fetch(FamilyType.class, course.getCourseType());
|
||||
if (type == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Cnd waitingCnd = Cnd.where(FamilyUser::getActivityId, "=", course.getActivityId())
|
||||
.and(FamilyUser::getCourseId, "=", course.getId())
|
||||
.and(FamilyUser::getState, "=", 2);
|
||||
// 配置了分工会名额时,取消释放的是原分工会名额,只允许该分工会候补人员递补。
|
||||
if (Lang.isNotEmpty(course.getUnionLimit())) {
|
||||
waitingCnd.and(FamilyUser::getUnionId, "=", canceledSignUp.getUnionId());
|
||||
}
|
||||
waitingCnd.asc(FamilyUser::getSignUpTime);
|
||||
|
||||
List<FamilyUser> waitingUsers = dao().query(FamilyUser.class, waitingCnd);
|
||||
for (FamilyUser waitingUser : waitingUsers) {
|
||||
int familyNumber = Lang.isNotEmpty(waitingUser.getMobileColumnsValue())
|
||||
? waitingUser.getMobileColumnsValue().size() : 0;
|
||||
int contribution = getFamilyContribution(type, familyNumber);
|
||||
// 严格保持候补先后顺序;排在前面的人员当前无法容纳时,不跨过该人员补后续人员。
|
||||
if (!canPromoteWaitingUser(course, waitingUser, contribution)) {
|
||||
break;
|
||||
}
|
||||
waitingUser.setState(3);
|
||||
dao().update(waitingUser);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验候补人员转正后的课程总人数、分工会人数和独立时段人数是否均不超限。
|
||||
*/
|
||||
private boolean canPromoteWaitingUser(FamilyCourse course, FamilyUser waitingUser, int contribution) {
|
||||
int courseLimit = course.getCoursePeopleNumber();
|
||||
int reservedNumber = course.getCourseReservedNumber();
|
||||
int normalCount = statisticsService.queryCourseCount(course.getId(), course.getCourseType());
|
||||
if (courseLimit == 0 || normalCount + contribution + reservedNumber > courseLimit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Lang.isNotEmpty(course.getUnionLimit())) {
|
||||
NutMap unionLimit = course.getUnionLimit().stream()
|
||||
.filter(item -> Objects.equals(item.getString("id"), waitingUser.getUnionId()))
|
||||
.findFirst().orElse(null);
|
||||
if (unionLimit == null) {
|
||||
return false;
|
||||
}
|
||||
int unionCount = statisticsService.queryCourseCount(
|
||||
course.getId(), course.getCourseType(), waitingUser.getUnionId());
|
||||
if (unionCount + contribution > unionLimit.getInt("limitCount")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return hasAvailableCourseTime(course, waitingUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 限制独立时段报名时,候补人员只能在其原选择时段仍有空位的情况下转正。
|
||||
*/
|
||||
private boolean hasAvailableCourseTime(FamilyCourse course, FamilyUser waitingUser) {
|
||||
if (!Boolean.TRUE.equals(course.getCourseIsLimitApply())) {
|
||||
return true;
|
||||
}
|
||||
if (StrUtil.isBlank(waitingUser.getActivityCourseId())) {
|
||||
return false;
|
||||
}
|
||||
FamilyActivityCourse activityCourse = dao().fetch(FamilyActivityCourse.class,
|
||||
waitingUser.getActivityCourseId());
|
||||
if (activityCourse == null || !Objects.equals(activityCourse.getCourseId(), course.getId())
|
||||
|| activityCourse.getCourseLimitNum() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<FamilyUser> formalUsers = dao().query(FamilyUser.class,
|
||||
Cnd.where(FamilyUser::getCourseId, "=", course.getId())
|
||||
.and(FamilyUser::getState, "in", List.of(1, 3)));
|
||||
List<String> formalUserIds = formalUsers.stream().map(FamilyUser::getUserId).toList();
|
||||
int usedCount = Lang.isEmpty(formalUserIds) ? 0 : dao().count(FamilyUserCourse.class,
|
||||
Cnd.where(FamilyUserCourse::getActivityCourseId, "=", activityCourse.getId())
|
||||
.and(FamilyUserCourse::getCourseId, "=", course.getId())
|
||||
.and(FamilyUserCourse::getUserId, "in", formalUserIds));
|
||||
return usedCount + 1 <= activityCourse.getCourseLimitNum();
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动递补只在活动报名开始时间(含)至结束时间(不含)内执行。
|
||||
*/
|
||||
private boolean isWithinSignUpTime(FamilyActivity activity) {
|
||||
if (activity == null || activity.getActivitySignUpStartTime() == null
|
||||
|| activity.getActivitySignUpEndTime() == null) {
|
||||
return false;
|
||||
}
|
||||
Date now = new Date();
|
||||
return DateUtil.compare(now, activity.getActivitySignUpStartTime()) >= 0
|
||||
&& DateUtil.compare(now, activity.getActivitySignUpEndTime()) < 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FamilyApplyPrefill getPrefill(String courseId, String userId) {
|
||||
if (StrUtil.isBlank(courseId) || StrUtil.isBlank(userId)) {
|
||||
return null;
|
||||
}
|
||||
return dao().fetch(FamilyApplyPrefill.class, Cnd.where(FamilyApplyPrefill::getCourseId, "=", courseId)
|
||||
.and(FamilyApplyPrefill::getUserId, "=", userId));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public FamilyApplyPrefill savePrefill(FamilyApplyPrefill prefill) {
|
||||
if (prefill == null || StrUtil.isBlank(prefill.getCourseId())) {
|
||||
throw new BaseException("预填课程信息不能为空");
|
||||
}
|
||||
String userId = SecurityUtil.getUserId();
|
||||
FamilyCourse course = dao().fetch(FamilyCourse.class, prefill.getCourseId());
|
||||
if (course == null) {
|
||||
throw new BaseException("预填课程不存在或已删除");
|
||||
}
|
||||
FamilyActivity activity = dao().fetch(FamilyActivity.class, course.getActivityId());
|
||||
validatePrefillTimeAndScope(activity, userId);
|
||||
FamilyType type = dao().fetch(FamilyType.class, course.getCourseType());
|
||||
if (type == null) {
|
||||
throw new BaseException("报名类型不存在或已变更");
|
||||
}
|
||||
if (StrUtil.isBlank(prefill.getMobile())) {
|
||||
throw new BaseException("请填写联系方式");
|
||||
}
|
||||
|
||||
List<List<NutMap>> normalizedColumns = normalizeAndValidateFamilyColumns(
|
||||
prefill.getMobileColumnsValue(), type, course, activity);
|
||||
validatePrefillCourseTime(course, prefill.getActivityCourseId());
|
||||
|
||||
// 复用正式报名的家属人数、唯一标识及分场冲突规则,但预填本身不计入任何名额。
|
||||
FamilyUser validateUser = new FamilyUser();
|
||||
validateUser.setActivityId(activity.getId());
|
||||
validateUser.setCourseId(course.getId());
|
||||
validateUser.setMobileColumnsValue(normalizedColumns);
|
||||
Map<Boolean, String> familyCountResult = validFamilyCount(validateUser);
|
||||
if (familyCountResult.containsKey(false)) {
|
||||
throw new BaseException(familyCountResult.get(false));
|
||||
}
|
||||
|
||||
FamilyApplyPrefill current = getPrefill(course.getId(), userId);
|
||||
if (current == null) {
|
||||
current = new FamilyApplyPrefill();
|
||||
current.setActivityId(activity.getId());
|
||||
current.setCourseId(course.getId());
|
||||
current.setUserId(userId);
|
||||
current.setMobile(prefill.getMobile());
|
||||
current.setActivityCourseId(prefill.getActivityCourseId());
|
||||
current.setMobileColumnsValue(normalizedColumns);
|
||||
dao().insert(current);
|
||||
} else {
|
||||
current.setMobile(prefill.getMobile());
|
||||
current.setActivityCourseId(prefill.getActivityCourseId());
|
||||
current.setMobileColumnsValue(normalizedColumns);
|
||||
dao().update(current);
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public void deletePrefill(String courseId) {
|
||||
String userId = SecurityUtil.getUserId();
|
||||
FamilyCourse course = dao().fetch(FamilyCourse.class, courseId);
|
||||
if (course == null) {
|
||||
throw new BaseException("预填课程不存在或已删除");
|
||||
}
|
||||
FamilyActivity activity = dao().fetch(FamilyActivity.class, course.getActivityId());
|
||||
validatePrefillTimeAndScope(activity, userId);
|
||||
dao().clear(FamilyApplyPrefill.class, Cnd.where(FamilyApplyPrefill::getCourseId, "=", courseId)
|
||||
.and(FamilyApplyPrefill::getUserId, "=", userId));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public FamilyUser quickSignUp(String courseId) throws Exception {
|
||||
String userId = SecurityUtil.getUserId();
|
||||
FamilyApplyPrefill prefill = getPrefill(courseId, userId);
|
||||
if (prefill == null) {
|
||||
throw new BaseException("您尚未预填该课程的报名信息");
|
||||
}
|
||||
FamilyCourse course = dao().fetch(FamilyCourse.class, courseId);
|
||||
if (course == null || !Objects.equals(course.getActivityId(), prefill.getActivityId())) {
|
||||
throw new BaseException("预填课程不存在或已变更");
|
||||
}
|
||||
FamilyActivity activity = dao().fetch(FamilyActivity.class, course.getActivityId());
|
||||
if (activity == null) {
|
||||
throw new BaseException("报名活动不存在或已删除");
|
||||
}
|
||||
FamilyType type = dao().fetch(FamilyType.class, course.getCourseType());
|
||||
if (type == null) {
|
||||
throw new BaseException("报名类型不存在或已变更");
|
||||
}
|
||||
|
||||
FamilyUser familyUser = new FamilyUser();
|
||||
familyUser.setActivityId(activity.getId());
|
||||
familyUser.setCourseId(course.getId());
|
||||
familyUser.setMobile(prefill.getMobile());
|
||||
familyUser.setActivityCourseId(prefill.getActivityCourseId());
|
||||
familyUser.setMobileColumnsValue(prefill.getMobileColumnsValue());
|
||||
return doSignUp(familyUser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FamilyUser getSignUpInfo(String courseId, String userId) {
|
||||
if (StrUtil.isBlank(courseId) || StrUtil.isBlank(userId)) {
|
||||
return null;
|
||||
}
|
||||
return dao().fetch(FamilyUser.class, Cnd.where(FamilyUser::getCourseId, "=", courseId)
|
||||
.and(FamilyUser::getUserId, "=", userId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateUpdateSignUp(String courseId) {
|
||||
FamilyUser currentSignUp = getSignUpInfo(courseId, SecurityUtil.getUserId());
|
||||
if (currentSignUp == null) {
|
||||
throw new BaseException("未找到本人在该课程的报名信息");
|
||||
}
|
||||
validateUpdateSignUpData(currentSignUp, currentSignUp);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public FamilyUser updateSignUp(FamilyUser familyUser) {
|
||||
if (familyUser == null || StrUtil.isBlank(familyUser.getId()) || StrUtil.isBlank(familyUser.getCourseId())) {
|
||||
throw new BaseException("报名信息不完整");
|
||||
}
|
||||
|
||||
String currentUserId = SecurityUtil.getUserId();
|
||||
FamilyUser currentSignUp = getSignUpInfo(familyUser.getCourseId(), currentUserId);
|
||||
if (currentSignUp == null || !Objects.equals(currentSignUp.getId(), familyUser.getId())) {
|
||||
throw new BaseException("未找到本人在该课程的报名信息");
|
||||
}
|
||||
validateUpdateSignUpData(familyUser, currentSignUp);
|
||||
|
||||
String oldActivityCourseId = currentSignUp.getActivityCourseId();
|
||||
currentSignUp.setMobile(familyUser.getMobile());
|
||||
currentSignUp.setMobileColumnsValue(familyUser.getMobileColumnsValue());
|
||||
currentSignUp.setActivityCourseId(familyUser.getActivityCourseId());
|
||||
dao().update(currentSignUp);
|
||||
syncUpdatedUserCourse(currentSignUp, oldActivityCourseId);
|
||||
return currentSignUp;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改按钮预校验与最终保存共用本方法,确保两处业务规则不会产生偏差。
|
||||
*/
|
||||
private void validateUpdateSignUpData(FamilyUser familyUser, FamilyUser currentSignUp) {
|
||||
FamilyCourse course = dao().fetch(FamilyCourse.class, familyUser.getCourseId());
|
||||
if (course == null || !Objects.equals(course.getActivityId(), currentSignUp.getActivityId())) {
|
||||
throw new BaseException("报名课程不存在或已变更");
|
||||
}
|
||||
FamilyActivity activity = dao().fetch(FamilyActivity.class, course.getActivityId());
|
||||
validateSignUpTimeAndScope(activity, SecurityUtil.getUserId(), true);
|
||||
|
||||
if (Lang.isEmpty(familyUser.getMobileColumnsValue())) {
|
||||
throw new BaseException("请填写" + activity.getKeyWord() + "信息");
|
||||
}
|
||||
|
||||
// 修改校验必须排除本人原报名,否则人数、唯一标识及报名限制都会重复计算。
|
||||
familyUser.setActivityId(currentSignUp.getActivityId());
|
||||
familyUser.setUserId(SecurityUtil.getUserId());
|
||||
Map<Boolean, String> familyCountResult = validFamilyCount(familyUser);
|
||||
if (familyCountResult.containsKey(false)) {
|
||||
throw new BaseException(familyCountResult.get(false));
|
||||
}
|
||||
|
||||
FamilyType type = dao().fetch(FamilyType.class, course.getCourseType());
|
||||
if (type == null) {
|
||||
throw new BaseException("报名类型不存在或已变更");
|
||||
}
|
||||
int currentFamilyNumber = Boolean.TRUE.equals(type.getIsBringFamily()) && Boolean.TRUE.equals(type.getIsAddFamily())
|
||||
? familyUser.getMobileColumnsValue().size() : 0;
|
||||
if (isSignFullForUpdate(course, type, currentFamilyNumber, currentSignUp, null)) {
|
||||
throw new BaseException("当前报名人数已满");
|
||||
}
|
||||
if (isSignFullForUpdate(course, type, currentFamilyNumber, currentSignUp, currentSignUp.getUnionId())) {
|
||||
throw new BaseException("该活动您所在的分工会名额不足");
|
||||
}
|
||||
if (!isSignCourseForUpdate(course, activity, currentSignUp.getId())) {
|
||||
throw new BaseException(activity.getRestrictLimit() != null && activity.getRestrictLimit() == 3
|
||||
? activity.getActivityName() + "限制报" + activity.getLimitNum() + "个活动,已达上限"
|
||||
: "您选择的类型已达上限,不能再报该类型的了");
|
||||
}
|
||||
validateCourseTime(course, familyUser.getActivityCourseId(), currentSignUp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NutMap> getEditCourseTimeSelectList(String courseId, String familyUserId) {
|
||||
FamilyUser currentSignUp = dao().fetch(FamilyUser.class, familyUserId);
|
||||
if (currentSignUp == null || !Objects.equals(currentSignUp.getCourseId(), courseId)
|
||||
|| !Objects.equals(currentSignUp.getUserId(), SecurityUtil.getUserId())) {
|
||||
throw new BaseException("未找到本人在该课程的报名信息");
|
||||
}
|
||||
|
||||
List<FamilyActivityCourse> courseTimeList = dao().query(FamilyActivityCourse.class,
|
||||
Cnd.where(FamilyActivityCourse::getCourseId, "=", courseId).asc(FamilyActivityCourse::getCourseStartTime));
|
||||
List<FamilyUser> normalUserList = dao().query(FamilyUser.class, Cnd.where(FamilyUser::getCourseId, "=", courseId)
|
||||
.and(FamilyUser::getState, "in", List.of(1, 3))
|
||||
.and(FamilyUser::getId, "!=", currentSignUp.getId()));
|
||||
List<String> normalUserIds = normalUserList.stream().map(FamilyUser::getUserId).toList();
|
||||
|
||||
return courseTimeList.stream().map(courseTime -> {
|
||||
int usedCount = Lang.isEmpty(normalUserIds) ? 0 : dao().count(FamilyUserCourse.class,
|
||||
Cnd.where(FamilyUserCourse::getActivityCourseId, "=", courseTime.getId())
|
||||
.and(FamilyUserCourse::getCourseId, "=", courseId)
|
||||
.and(FamilyUserCourse::getUserId, "in", normalUserIds));
|
||||
int limitCount = courseTime.getCourseLimitNum() != null ? courseTime.getCourseLimitNum() : 0;
|
||||
int remainingNum = Math.max(limitCount - usedCount, 0);
|
||||
NutMap option = NutMap.NEW();
|
||||
option.put("remainingNum", remainingNum);
|
||||
option.put("text", DateUtil.format(courseTime.getCourseStartTime(), "HH:mm") + "至"
|
||||
+ DateUtil.format(courseTime.getCourseEndTime(), "HH:mm") + "段(剩" + remainingNum + ")");
|
||||
option.put("value", courseTime.getId());
|
||||
option.put("disabled", remainingNum == 0);
|
||||
return option;
|
||||
}).filter(option -> option.getInt("remainingNum") > 0).toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 锁定课程记录。所有正式报名都必须先取得该锁,确保名额统计与写入处于同一串行区间。
|
||||
*/
|
||||
private void lockCourseForSignUp(String courseId) {
|
||||
if (StrUtil.isBlank(courseId)) {
|
||||
throw new BaseException("报名课程信息不能为空");
|
||||
}
|
||||
Sql lockSql = Sqls.create("SELECT id FROM family_course WHERE id = @courseId FOR UPDATE");
|
||||
lockSql.setParam("courseId", courseId);
|
||||
lockSql.setCallback(Sqls.callback.str());
|
||||
Object lockedCourseId = dao().execute(lockSql).getResult();
|
||||
if (lockedCourseId == null) {
|
||||
throw new BaseException("报名课程不存在或已删除");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预填新增、修改和删除共用时间及人员范围校验。
|
||||
* 预填只允许发生在报名开始时间之前,到达开始时间后数据立即冻结。
|
||||
*/
|
||||
private void validatePrefillTimeAndScope(FamilyActivity activity, String userId) {
|
||||
if (activity == null) {
|
||||
throw new BaseException("报名活动不存在或已删除");
|
||||
}
|
||||
if (activity.getActivitySignUpStartTime() == null) {
|
||||
throw new BaseException("活动未配置报名开始时间,不能预填报名信息");
|
||||
}
|
||||
if (DateUtil.compare(new Date(), activity.getActivitySignUpStartTime()) >= 0) {
|
||||
throw new BaseException("报名已经开始,预填信息已冻结,不能继续修改");
|
||||
}
|
||||
int scopeCount = dao().count(ActivityUserScope.class,
|
||||
Cnd.where(ActivityUserScope::getGroupId, "=", activity.getActivityGroupId())
|
||||
.and(ActivityUserScope::getUserId, "=", userId));
|
||||
if (scopeCount == 0) {
|
||||
throw new BaseException("抱歉,您没有此次活动的权限");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预填时只校验时段归属和必选要求,不查询或占用时段名额。
|
||||
*/
|
||||
private void validatePrefillCourseTime(FamilyCourse course, String activityCourseId) {
|
||||
if (!Boolean.TRUE.equals(course.getCourseIsLimitApply())) {
|
||||
return;
|
||||
}
|
||||
if (StrUtil.isBlank(activityCourseId)) {
|
||||
throw new BaseException("请选择报名时段");
|
||||
}
|
||||
FamilyActivityCourse courseTime = dao().fetch(FamilyActivityCourse.class, activityCourseId);
|
||||
if (courseTime == null || !Objects.equals(courseTime.getCourseId(), course.getId())) {
|
||||
throw new BaseException("报名时段不存在或已变更");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据课程类型当前动态字段配置重建子女数据,并在服务端完成必填、格式、年龄和性别校验。
|
||||
* 前端只负责提交 columnCode 与 columnValue,字段名称和控件类型均以服务端配置为准。
|
||||
*/
|
||||
private List<List<NutMap>> normalizeAndValidateFamilyColumns(List<List<NutMap>> submittedFamilies,
|
||||
FamilyType type, FamilyCourse course, FamilyActivity activity) {
|
||||
if (StrUtil.isBlank(activity.getOnlyKey())) {
|
||||
throw new BaseException("活动未配置家属唯一标识,请联系管理员");
|
||||
}
|
||||
if (activity.getFamilyMaxCount() == null) {
|
||||
throw new BaseException("活动未配置家属最多人数,请联系管理员");
|
||||
}
|
||||
if (Lang.isEmpty(submittedFamilies)) {
|
||||
throw new BaseException("请填写" + activity.getKeyWord() + "信息");
|
||||
}
|
||||
if (activity.getFamilyMaxCount() != null && submittedFamilies.size() > activity.getFamilyMaxCount()) {
|
||||
throw new BaseException(activity.getKeyWord() + "人数最多为" + activity.getFamilyMaxCount());
|
||||
}
|
||||
dao().fetchLinks(type, "familyMobileSignColumnList", Cnd.NEW().asc("columnIndex"));
|
||||
List<FamilyMobileSignColumn> columnConfigs = type.getFamilyMobileSignColumnList();
|
||||
if (Lang.isEmpty(columnConfigs)) {
|
||||
throw new BaseException("报名类型未配置" + activity.getKeyWord() + "信息字段");
|
||||
}
|
||||
|
||||
List<List<NutMap>> normalizedFamilies = new ArrayList<>();
|
||||
Set<String> currentOnlyKeyValues = new HashSet<>();
|
||||
for (int familyIndex = 0; familyIndex < submittedFamilies.size(); familyIndex++) {
|
||||
List<NutMap> submittedFamily = submittedFamilies.get(familyIndex);
|
||||
Map<String, NutMap> submittedValueMap = Lang.isEmpty(submittedFamily) ? new HashMap<>()
|
||||
: submittedFamily.stream().filter(Objects::nonNull)
|
||||
.filter(item -> StrUtil.isNotBlank(item.getString("columnCode")))
|
||||
.collect(Collectors.toMap(item -> item.getString("columnCode"), item -> item, (left, right) -> left));
|
||||
List<NutMap> normalizedFamily = new ArrayList<>();
|
||||
for (FamilyMobileSignColumn config : columnConfigs) {
|
||||
NutMap submittedColumn = submittedValueMap.get(config.getColumnCode());
|
||||
Object columnValue = submittedColumn == null ? null : submittedColumn.get("columnValue");
|
||||
validateDynamicColumnValue(config, columnValue, familyIndex, course, activity);
|
||||
|
||||
NutMap normalizedColumn = NutMap.NEW();
|
||||
normalizedColumn.put("columnName", config.getColumnName());
|
||||
normalizedColumn.put("columnCode", config.getColumnCode());
|
||||
normalizedColumn.put("columnValue", columnValue);
|
||||
normalizedColumn.put("columnFormType", config.getColumnFormType());
|
||||
normalizedFamily.add(normalizedColumn);
|
||||
}
|
||||
normalizedFamilies.add(normalizedFamily);
|
||||
|
||||
NutMap onlyKeyColumn = normalizedFamily.stream()
|
||||
.filter(item -> Objects.equals(activity.getOnlyKey(), item.getString("columnCode")))
|
||||
.findFirst().orElse(null);
|
||||
if (onlyKeyColumn == null || StrUtil.isBlank(onlyKeyColumn.getString("columnValue"))) {
|
||||
throw new BaseException(activity.getKeyWord() + (familyIndex + 1) + "缺少唯一标识信息");
|
||||
}
|
||||
if (!currentOnlyKeyValues.add(onlyKeyColumn.getString("columnValue"))) {
|
||||
throw new BaseException(activity.getKeyWord() + "不能重复选择同一人");
|
||||
}
|
||||
}
|
||||
return normalizedFamilies;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验一个动态字段。参数为字段配置、用户值、子女序号、课程和活动;校验失败抛出业务异常,无返回值。
|
||||
*/
|
||||
private void validateDynamicColumnValue(FamilyMobileSignColumn config, Object columnValue, int familyIndex,
|
||||
FamilyCourse course, FamilyActivity activity) {
|
||||
String displayName = activity.getKeyWord() + (familyIndex + 1) + "的" + config.getColumnName();
|
||||
boolean empty = columnValue == null || (columnValue instanceof String && StrUtil.isBlank((String) columnValue))
|
||||
|| (columnValue instanceof Collection<?> && ((Collection<?>) columnValue).isEmpty());
|
||||
if (Boolean.TRUE.equals(config.getIsRequired()) && empty) {
|
||||
throw new BaseException(displayName + "不能为空");
|
||||
}
|
||||
if (empty || StrUtil.isBlank(config.getValidRule())) {
|
||||
return;
|
||||
}
|
||||
String stringValue = String.valueOf(columnValue).trim();
|
||||
if ("mobile".equals(config.getValidRule()) && !Validator.isMobile(stringValue)) {
|
||||
throw new BaseException(displayName + "格式不正确");
|
||||
}
|
||||
if ("email".equals(config.getValidRule()) && !Validator.isEmail(stringValue)) {
|
||||
throw new BaseException(displayName + "格式不正确");
|
||||
}
|
||||
if (!"idCard".equals(config.getValidRule())) {
|
||||
return;
|
||||
}
|
||||
if (!IdcardUtil.isValidCard(stringValue)) {
|
||||
throw new BaseException(displayName + "格式不正确");
|
||||
}
|
||||
if (course.isFamilyAgeLimit()) {
|
||||
int age = IdcardUtil.getAgeByIdCard(stringValue);
|
||||
if (course.getMinAge() != null && age < course.getMinAge()) {
|
||||
throw new BaseException("限制报名最小年龄为" + course.getMinAge());
|
||||
}
|
||||
if (course.getMaxAge() != null && age > course.getMaxAge()) {
|
||||
throw new BaseException("限制报名最大年龄为" + course.getMaxAge());
|
||||
}
|
||||
}
|
||||
if (course.isFamilySexLimit() && StrUtil.isNotBlank(course.getFamilySex())) {
|
||||
String sex = IdcardUtil.getGenderByIdCard(stringValue) == 1 ? "男" : "女";
|
||||
if (!Objects.equals(course.getFamilySex(), sex)) {
|
||||
throw new BaseException("限制报名性别为" + course.getFamilySex());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增和修改共用活动时间及面向对象校验;update 仅用于区分业务提示文案。
|
||||
*/
|
||||
private void validateSignUpTimeAndScope(FamilyActivity activity, String userId, boolean update) {
|
||||
if (activity == null) {
|
||||
throw new BaseException("报名活动不存在或已删除");
|
||||
}
|
||||
Date now = new Date();
|
||||
if (activity.getActivitySignUpStartTime() == null || DateUtil.compare(now, activity.getActivitySignUpStartTime()) < 0) {
|
||||
throw new BaseException(update ? "报名未开始,不能修改报名信息" : "报名未开始");
|
||||
}
|
||||
if (activity.getActivitySignUpEndTime() == null || DateUtil.compare(now, activity.getActivitySignUpEndTime()) >= 0) {
|
||||
throw new BaseException(update ? "报名已结束,不能修改报名信息" : "报名已结束");
|
||||
}
|
||||
int scopeCount = dao().count(ActivityUserScope.class, Cnd.where(ActivityUserScope::getGroupId, "=", activity.getActivityGroupId())
|
||||
.and(ActivityUserScope::getUserId, "=", userId));
|
||||
if (scopeCount == 0) {
|
||||
throw new BaseException("抱歉,您没有此次活动的权限");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 按新增报名的名额规则校验修改数据,并从正常或候补人数中扣除本人原报名贡献。
|
||||
*/
|
||||
private boolean isSignFullForUpdate(FamilyCourse course, FamilyType type, int currentFamilyNumber,
|
||||
FamilyUser currentSignUp, String unionId) {
|
||||
if (course.getCoursePeopleNumber() == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int limitCount = course.getCoursePeopleNumber();
|
||||
if (StrUtil.isNotBlank(unionId)) {
|
||||
List<NutMap> unionLimitList = course.getUnionLimit();
|
||||
if (Lang.isEmpty(unionLimitList)) {
|
||||
return false;
|
||||
}
|
||||
NutMap unionLimit = unionLimitList.stream()
|
||||
.filter(item -> Objects.equals(item.getString("id"), unionId)).findFirst().orElse(null);
|
||||
if (unionLimit == null) {
|
||||
return true;
|
||||
}
|
||||
limitCount = unionLimit.getInt("limitCount");
|
||||
}
|
||||
|
||||
int normalCount = StrUtil.isBlank(unionId)
|
||||
? statisticsService.queryCourseCount(course.getId(), course.getCourseType())
|
||||
: statisticsService.queryCourseCount(course.getId(), course.getCourseType(), unionId);
|
||||
int waitCount = StrUtil.isBlank(unionId)
|
||||
? statisticsService.queryCourseWaitCount(course.getId(), course.getCourseType())
|
||||
: statisticsService.queryCourseWaitCount(course.getId(), course.getCourseType(), unionId);
|
||||
int oldFamilyNumber = Lang.isNotEmpty(currentSignUp.getMobileColumnsValue())
|
||||
? currentSignUp.getMobileColumnsValue().size() : 0;
|
||||
int oldContribution = getFamilyContribution(type, oldFamilyNumber);
|
||||
if (Objects.equals(currentSignUp.getState(), 1) || Objects.equals(currentSignUp.getState(), 3)) {
|
||||
normalCount = Math.max(normalCount - oldContribution, 0);
|
||||
} else if (Objects.equals(currentSignUp.getState(), 2)) {
|
||||
waitCount = Math.max(waitCount - oldContribution, 0);
|
||||
}
|
||||
|
||||
int newContribution = getFamilyContribution(type, currentFamilyNumber);
|
||||
if (limitCount - normalCount > 0) {
|
||||
int reservedNumber = StrUtil.isBlank(unionId) ? course.getCourseReservedNumber() : 0;
|
||||
return normalCount + newContribution + reservedNumber > limitCount;
|
||||
}
|
||||
int waitingNum = course.getWaitingNum() == null ? 0 : course.getWaitingNum();
|
||||
return waitCount + newContribution > waitingNum;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算一条报名记录占用的总名额,规则与现有报名统计保持一致。
|
||||
*/
|
||||
private int getFamilyContribution(FamilyType type, int familyNumber) {
|
||||
int contribution = Boolean.TRUE.equals(type.getSelfAddFamily()) ? 1 : 0;
|
||||
if (Boolean.TRUE.equals(type.getIsBringFamily()) && Boolean.TRUE.equals(type.getIsAddFamily())) {
|
||||
contribution += familyNumber;
|
||||
}
|
||||
return contribution;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改报名数量限制校验时排除当前报名记录,再把本次修改作为一条报名重新计算。
|
||||
*/
|
||||
private boolean isSignCourseForUpdate(FamilyCourse course, FamilyActivity activity, String currentSignUpId) {
|
||||
if (activity.getRestrictLimit() == null || activity.getRestrictLimit() == 1) {
|
||||
return true;
|
||||
}
|
||||
if (activity.getRestrictLimit() == 2) {
|
||||
FamilyTypeLimit typeLimit = dao().fetch(FamilyTypeLimit.class, Cnd.where(FamilyTypeLimit::getTypeId, "=", course.getCourseType())
|
||||
.and(FamilyTypeLimit::getActivityId, "=", activity.getId()));
|
||||
if (typeLimit == null || typeLimit.getLimitNum() == 0) {
|
||||
return true;
|
||||
}
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT count(tsus.id)
|
||||
FROM family_user tsus
|
||||
LEFT JOIN family_course tsuc ON tsuc.id = tsus.courseId
|
||||
WHERE tsuc.courseType = @courseType
|
||||
AND tsus.userId = @userId
|
||||
AND tsus.activityId = @activityId
|
||||
AND tsus.id != @currentSignUpId
|
||||
""");
|
||||
sql.setParam("courseType", course.getCourseType());
|
||||
sql.setParam("userId", SecurityUtil.getUserId());
|
||||
sql.setParam("activityId", activity.getId());
|
||||
sql.setParam("currentSignUpId", currentSignUpId);
|
||||
return count(sql) < typeLimit.getLimitNum();
|
||||
}
|
||||
int activitySignCount = dao().count(FamilyUser.class, Cnd.where(FamilyUser::getActivityId, "=", activity.getId())
|
||||
.and(FamilyUser::getUserId, "=", SecurityUtil.getUserId())
|
||||
.and(FamilyUser::getId, "!=", currentSignUpId));
|
||||
return activity.getLimitNum() == null || activitySignCount < activity.getLimitNum();
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验独立报名时段名额;修改时排除本人原记录,新增时直接按当前占用人数计算。
|
||||
*/
|
||||
private void validateCourseTime(FamilyCourse course, String activityCourseId, FamilyUser currentSignUp) {
|
||||
if (!Boolean.TRUE.equals(course.getCourseIsLimitApply())) {
|
||||
return;
|
||||
}
|
||||
if (StrUtil.isBlank(activityCourseId)) {
|
||||
throw new BaseException("请选择报名时段");
|
||||
}
|
||||
FamilyActivityCourse activityCourse = dao().fetch(FamilyActivityCourse.class, activityCourseId);
|
||||
if (activityCourse == null || !Objects.equals(activityCourse.getCourseId(), course.getId())) {
|
||||
throw new BaseException("报名时段不存在或已变更");
|
||||
}
|
||||
Cnd normalUserCnd = Cnd.where(FamilyUser::getCourseId, "=", course.getId())
|
||||
.and(FamilyUser::getState, "in", List.of(1, 3));
|
||||
if (currentSignUp != null) {
|
||||
normalUserCnd.and(FamilyUser::getId, "!=", currentSignUp.getId());
|
||||
}
|
||||
List<FamilyUser> normalUserList = dao().query(FamilyUser.class, normalUserCnd);
|
||||
List<String> normalUserIds = normalUserList.stream().map(FamilyUser::getUserId).toList();
|
||||
int usedCount = Lang.isEmpty(normalUserIds) ? 0 : dao().count(FamilyUserCourse.class,
|
||||
Cnd.where(FamilyUserCourse::getActivityCourseId, "=", activityCourseId)
|
||||
.and(FamilyUserCourse::getCourseId, "=", course.getId())
|
||||
.and(FamilyUserCourse::getUserId, "in", normalUserIds));
|
||||
if (activityCourse.getCourseLimitNum() == null || usedCount + 1 > activityCourse.getCourseLimitNum()) {
|
||||
throw new BaseException("该时间段名额已报满,请选择其他时段报名");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 仅在报名时段发生变化时重建本人课程时段记录,未变化时保留原签到和领取信息。
|
||||
*/
|
||||
private void syncUpdatedUserCourse(FamilyUser currentSignUp, String oldActivityCourseId) {
|
||||
if (Objects.equals(oldActivityCourseId, currentSignUp.getActivityCourseId())) {
|
||||
return;
|
||||
}
|
||||
dao().clear(FamilyUserCourse.class, Cnd.where(FamilyUserCourse::getActivityId, "=", currentSignUp.getActivityId())
|
||||
.and(FamilyUserCourse::getCourseId, "=", currentSignUp.getCourseId())
|
||||
.and(FamilyUserCourse::getUserId, "=", currentSignUp.getUserId()));
|
||||
|
||||
List<FamilyActivityCourse> selectedCourseTimes;
|
||||
if (StrUtil.isNotBlank(currentSignUp.getActivityCourseId())) {
|
||||
FamilyActivityCourse selected = dao().fetch(FamilyActivityCourse.class, currentSignUp.getActivityCourseId());
|
||||
selectedCourseTimes = selected == null ? new ArrayList<>() : List.of(selected);
|
||||
} else {
|
||||
selectedCourseTimes = dao().query(FamilyActivityCourse.class,
|
||||
Cnd.where(FamilyActivityCourse::getCourseId, "=", currentSignUp.getCourseId()));
|
||||
}
|
||||
List<FamilyUserCourse> userCourseList = selectedCourseTimes.stream().map(courseTime -> {
|
||||
FamilyUserCourse userCourse = new FamilyUserCourse();
|
||||
userCourse.setActivityId(currentSignUp.getActivityId());
|
||||
userCourse.setCourseId(currentSignUp.getCourseId());
|
||||
userCourse.setUserId(currentSignUp.getUserId());
|
||||
userCourse.setCourseStartTime(courseTime.getCourseStartTime());
|
||||
userCourse.setCourseEndTime(courseTime.getCourseEndTime());
|
||||
userCourse.setAttend(false);
|
||||
userCourse.setActivityCourseId(courseTime.getId());
|
||||
return userCourse;
|
||||
}).toList();
|
||||
if (Lang.isNotEmpty(userCourseList)) {
|
||||
dao().insert(userCourseList);
|
||||
}
|
||||
}
|
||||
|
||||
@Async
|
||||
@Override
|
||||
public void asyncInsertUserCourse(String activityId, String courseId, String userId) {
|
||||
log.info("异步插入{}的上课信息,课程ID为{},活动ID为{}", userId, courseId, activityId);
|
||||
insertUserCourse(activityId, courseId, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步写入报名人员的全部课程时段,用于保证正式报名主记录与时段记录处于同一事务。
|
||||
*/
|
||||
private void insertUserCourse(String activityId, String courseId, String userId) {
|
||||
List<FamilyActivityCourse> courseList = dao().query(FamilyActivityCourse.class, Cnd.where("courseId", "=", courseId));
|
||||
List<FamilyUserCourse> list = new ArrayList<>();
|
||||
courseList.forEach(v -> {
|
||||
@@ -313,7 +1059,9 @@ public class FamilyActivityServiceImpl extends BaseServiceImpl<FamilyActivity> i
|
||||
userCourse.setActivityCourseId(v.getId());
|
||||
list.add(userCourse);
|
||||
});
|
||||
dao().insert(list);
|
||||
if (Lang.isNotEmpty(list)) {
|
||||
dao().insert(list);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -379,9 +1127,26 @@ public class FamilyActivityServiceImpl extends BaseServiceImpl<FamilyActivity> i
|
||||
public Map<Boolean, String> validFamilyCount(FamilyUser user) {
|
||||
String activityId = user.getActivityId();
|
||||
// 查询报名记录
|
||||
List<FamilyUser> list = dao().query(FamilyUser.class, Cnd.where(FamilyUser::getActivityId, "=", activityId).and(FamilyUser::getUserId, "=", SecurityUtil.getUserId()));
|
||||
Cnd signUpCnd = Cnd.where(FamilyUser::getActivityId, "=", activityId)
|
||||
.and(FamilyUser::getUserId, "=", SecurityUtil.getUserId());
|
||||
if (StrUtil.isNotBlank(user.getId())) {
|
||||
signUpCnd.and(FamilyUser::getId, "!=", user.getId());
|
||||
}
|
||||
List<FamilyUser> list = dao().query(FamilyUser.class, signUpCnd);
|
||||
|
||||
FamilyActivity activity = dao().fetch(FamilyActivity.class, activityId);
|
||||
if (activity == null) {
|
||||
return Map.of(false, "报名活动不存在或已删除");
|
||||
}
|
||||
if (StrUtil.isBlank(activity.getOnlyKey())) {
|
||||
return Map.of(false, "活动未配置家属唯一标识,请联系管理员");
|
||||
}
|
||||
if (activity.getFamilyMaxCount() == null) {
|
||||
return Map.of(false, "活动未配置家属最多人数,请联系管理员");
|
||||
}
|
||||
if (Lang.isEmpty(user.getMobileColumnsValue())) {
|
||||
return Map.of(false, "请填写" + activity.getKeyWord() + "信息");
|
||||
}
|
||||
|
||||
List<List<NutMap>> allMobileColumnsValue = new ArrayList<>(list.stream()
|
||||
.map(FamilyUser::getMobileColumnsValue)
|
||||
@@ -395,8 +1160,10 @@ public class FamilyActivityServiceImpl extends BaseServiceImpl<FamilyActivity> i
|
||||
//return allMobileColumnsValue.size() > activity.getFamilyMaxCount();
|
||||
|
||||
long count = allMobileColumnsValue.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.flatMap(List::stream)
|
||||
.filter(nutMap -> activity.getOnlyKey().equals(nutMap.getString("columnCode")))
|
||||
.filter(Objects::nonNull)
|
||||
.filter(nutMap -> Objects.equals(activity.getOnlyKey(), nutMap.getString("columnCode")))
|
||||
.map(nutMap -> nutMap.getString("columnValue"))
|
||||
.filter(Objects::nonNull)
|
||||
.distinct()
|
||||
@@ -420,7 +1187,11 @@ public class FamilyActivityServiceImpl extends BaseServiceImpl<FamilyActivity> i
|
||||
String tempValue = "";
|
||||
// 循环家属
|
||||
for (List<NutMap> listMap : currentValue) {
|
||||
NutMap nutMap = listMap.stream().filter(o -> o.getString("columnCode").equals(activity.getOnlyKey())).findFirst().orElse(null);
|
||||
if (Lang.isEmpty(listMap)) {
|
||||
continue;
|
||||
}
|
||||
NutMap nutMap = listMap.stream().filter(Objects::nonNull)
|
||||
.filter(o -> Objects.equals(o.getString("columnCode"), activity.getOnlyKey())).findFirst().orElse(null);
|
||||
if(nutMap == null) {
|
||||
continue;
|
||||
}
|
||||
@@ -435,8 +1206,15 @@ public class FamilyActivityServiceImpl extends BaseServiceImpl<FamilyActivity> i
|
||||
|
||||
for (FamilyUser hisRecord : list) {
|
||||
List<List<NutMap>> hisValueList = hisRecord.getMobileColumnsValue();
|
||||
if (Lang.isEmpty(hisValueList)) {
|
||||
continue;
|
||||
}
|
||||
for (List<NutMap> hisValue : hisValueList) {
|
||||
NutMap hisNutMap = hisValue.stream().filter(o -> o.getString("columnCode").equals(activity.getOnlyKey())).findFirst().orElse(null);
|
||||
if (Lang.isEmpty(hisValue)) {
|
||||
continue;
|
||||
}
|
||||
NutMap hisNutMap = hisValue.stream().filter(Objects::nonNull)
|
||||
.filter(o -> Objects.equals(o.getString("columnCode"), activity.getOnlyKey())).findFirst().orElse(null);
|
||||
if(hisNutMap == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
const h5PopupHistory = (() => {
|
||||
const popupStack = []
|
||||
let skipNextPop = false
|
||||
|
||||
const handlePopState = (event) => {
|
||||
if (skipNextPop) {
|
||||
skipNextPop = false
|
||||
event.stopImmediatePropagation()
|
||||
return
|
||||
}
|
||||
if (popupStack.length === 0) return
|
||||
event.stopImmediatePropagation()
|
||||
const popup = popupStack.pop()
|
||||
popup.closeFromHistory()
|
||||
}
|
||||
|
||||
window.addEventListener("popstate", handlePopState)
|
||||
|
||||
return {
|
||||
/**
|
||||
* 注册已打开的 H5 弹框并增加一层历史记录。
|
||||
* @param id 弹框唯一标识,同一标识不会重复入栈
|
||||
* @param owner 页面或组件标识,用于提交成功及销毁时统一清理
|
||||
* @param closeFromHistory 返回键触发时关闭弹框的回调
|
||||
* @return 无返回值
|
||||
*/
|
||||
open(id, owner, closeFromHistory) {
|
||||
if (popupStack.some((item) => item.id === id)) return
|
||||
popupStack.push({id: id, owner: owner, closeFromHistory: closeFromHistory})
|
||||
const state = Object.assign({}, window.history.state || {}, {
|
||||
h5PopupId: id,
|
||||
url: window.location.href
|
||||
})
|
||||
window.history.pushState(state, "", window.location.href)
|
||||
},
|
||||
|
||||
/**
|
||||
* 通过取消、关闭按钮关闭指定弹框时,同步回退对应历史记录。
|
||||
* @param id 弹框唯一标识,必须是当前最上层弹框
|
||||
* @return boolean;true 表示已发起历史回退,false 表示弹框不在栈顶
|
||||
*/
|
||||
close(id) {
|
||||
const top = popupStack[popupStack.length - 1]
|
||||
if (!top || top.id !== id) return false
|
||||
window.history.back()
|
||||
return true
|
||||
},
|
||||
|
||||
/**
|
||||
* 提交成功前清理当前组件的全部弹框及其历史记录。
|
||||
* @param owner 页面或组件标识
|
||||
* @return 无返回值
|
||||
*/
|
||||
clearOwner(owner) {
|
||||
const ownerPopups = popupStack.filter((item) => item.owner === owner)
|
||||
if (ownerPopups.length === 0) return
|
||||
ownerPopups.slice().reverse().forEach((item) => item.closeFromHistory())
|
||||
for (let i = popupStack.length - 1; i >= 0; i--) {
|
||||
if (popupStack[i].owner === owner) popupStack.splice(i, 1)
|
||||
}
|
||||
skipNextPop = true
|
||||
window.history.go(-ownerPopups.length)
|
||||
},
|
||||
|
||||
/**
|
||||
* 组件销毁时注销回调,防止旧组件被历史返回事件再次调用。
|
||||
* @param owner 页面或组件标识
|
||||
* @return 无返回值
|
||||
*/
|
||||
unregisterOwner(owner) {
|
||||
for (let i = popupStack.length - 1; i >= 0; i--) {
|
||||
if (popupStack[i].owner === owner) popupStack.splice(i, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
})()
|
||||
@@ -57,6 +57,7 @@
|
||||
<script src="${base!}/assets/platform/js/mixin/styleMixin.js"></script>
|
||||
<script src="${base!}/components/plugins/sysDict/DictData.js"></script>
|
||||
<script src="${base!}/assets/platform/js/util/commonUtil.js"></script>
|
||||
<script src="${base!}/assets/platform/js/util/h5PopupHistory.js"></script>
|
||||
<script src="${base!}/assets/platform/js/tool/businessTool.js"></script>
|
||||
|
||||
<!--富文本编辑器-->
|
||||
|
||||
@@ -74,10 +74,21 @@ const courseList = {
|
||||
</template>
|
||||
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="100">
|
||||
<el-table-column label="操作" width="200">
|
||||
<template v-slot="{row}">
|
||||
<el-button v-if="!row.isSign" type="primary" size="mini" @click="onSign(row)">我要报名</el-button>
|
||||
<el-button v-else type="danger" size="mini" @click="onCancel(row)">取消报名</el-button>
|
||||
<template v-if="!row.isSign">
|
||||
<el-button v-if="isBeforeSignUpTime" type="primary" size="mini" @click="onPrefill(row)">
|
||||
{{row.hasPrefill ? '修改预填信息' : '预填报名信息'}}
|
||||
</el-button>
|
||||
<el-button v-else-if="isInSignUpTime && row.hasPrefill" type="success" size="mini" @click="onQuickSignUp(row)">
|
||||
立即报名
|
||||
</el-button>
|
||||
<el-button v-else-if="isInSignUpTime" type="primary" size="mini" @click="onSign(row)">我要报名</el-button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-button v-if="isInSignUpTime" type="primary" size="mini" @click="onEdit(row)">修改报名</el-button>
|
||||
<el-button type="danger" size="mini" @click="onCancel(row)">取消报名</el-button>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -150,10 +161,63 @@ const courseList = {
|
||||
pdf: false,
|
||||
infoVisible: false,
|
||||
introduce: '',
|
||||
nowTime: Date.now(),
|
||||
signTimeTimer: null,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 只有严格早于报名开始时间时才允许维护预填数据。
|
||||
isBeforeSignUpTime() {
|
||||
return this.activity.activitySignUpStartTime
|
||||
&& this.$moment(this.nowTime).isBefore(this.$moment(this.activity.activitySignUpStartTime))
|
||||
},
|
||||
// 修改报名与我要报名共用同一时间范围:开始时间已到且尚未达到结束时间。
|
||||
isInSignUpTime() {
|
||||
const now = this.$moment(this.nowTime)
|
||||
return this.activity.activitySignUpStartTime
|
||||
&& this.activity.activitySignUpEndTime
|
||||
&& !now.isBefore(this.$moment(this.activity.activitySignUpStartTime))
|
||||
&& now.isBefore(this.$moment(this.activity.activitySignUpEndTime))
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async onPreview(introduce) {
|
||||
onPrefill(row) {
|
||||
const courseType = this.courseTypeList.find((item) => item.id === row.courseType)
|
||||
this.tableLoading = true
|
||||
this.$axios.post("/platform/family/apply/getPrefill", {courseId: row.id})
|
||||
.then((resp) => {
|
||||
if (resp.code !== 0) {
|
||||
this.$message.warning(resp.msg)
|
||||
return
|
||||
}
|
||||
this.$refs.signFormRef.onOpen(row, courseType, this.activity, resp.data, "prefill")
|
||||
})
|
||||
.finally(() => {
|
||||
this.tableLoading = false
|
||||
})
|
||||
},
|
||||
onQuickSignUp(row) {
|
||||
this.$confirm("将使用报名开始前保存的预填信息直接报名,是否继续?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
this.tableLoading = true
|
||||
this.$axios.post("/platform/family/apply/quickSignUp", {courseId: row.id})
|
||||
.then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.$alert(resp.msg, "提示", {confirmButtonText: "确定", type: "success"})
|
||||
this.pageData()
|
||||
} else {
|
||||
this.$message.warning(resp.msg)
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.tableLoading = false
|
||||
})
|
||||
}).catch(() => {})
|
||||
},
|
||||
onPreview(introduce) {
|
||||
try {
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(introduce, 'text/html');
|
||||
@@ -161,10 +225,16 @@ const courseList = {
|
||||
const href = link.getAttribute('href');
|
||||
|
||||
const id = href.substring(href.indexOf("=") + 1)
|
||||
const res = await this.$axios.post("/platform/sys/file/previewFileData", { ids: JSON.stringify([id]) })
|
||||
|
||||
this.pdf = true
|
||||
window.open("/assets/platform/plugins/pdfJs/web/viewer.html?file=" + encodeURIComponent(res.data[0].downloadPath), res.data[0].name)
|
||||
this.$axios.post("/platform/sys/file/previewFileData", {ids: JSON.stringify([id])})
|
||||
.then((res) => {
|
||||
this.pdf = true
|
||||
window.open("/assets/platform/plugins/pdfJs/web/viewer.html?file=" + encodeURIComponent(res.data[0].downloadPath), res.data[0].name)
|
||||
})
|
||||
.catch(() => {
|
||||
this.introduce = introduce
|
||||
this.pdf = false
|
||||
this.infoVisible = true
|
||||
})
|
||||
} catch (e) {
|
||||
this.introduce = introduce
|
||||
this.pdf = false
|
||||
@@ -180,12 +250,14 @@ const courseList = {
|
||||
}
|
||||
this.doSearch()
|
||||
},
|
||||
async onOpen(row) {
|
||||
onOpen(row) {
|
||||
this.activity = row
|
||||
this.$set(this.pageForm, 'activityId', row.id)
|
||||
await this.pageData()
|
||||
await this.getCourseTypeList()
|
||||
this.queryCourseAssort()
|
||||
this.pageData()
|
||||
.then(() => this.getCourseTypeList())
|
||||
.then(() => {
|
||||
this.queryCourseAssort()
|
||||
})
|
||||
},
|
||||
onSign(row) {
|
||||
const courseType = this.courseTypeList.find((v) => v.id === row.courseType)
|
||||
@@ -208,34 +280,64 @@ const courseList = {
|
||||
}
|
||||
})
|
||||
},
|
||||
onEdit(row) {
|
||||
const courseType = this.courseTypeList.find((item) => item.id === row.courseType)
|
||||
this.tableLoading = true
|
||||
this.$axios.post("/platform/family/apply/validateUpdateSignUp", {courseId: row.id})
|
||||
.then((validateRes) => {
|
||||
if (validateRes.code !== 0) {
|
||||
this.$message.warning(validateRes.msg)
|
||||
return null
|
||||
}
|
||||
return this.$axios.post("/platform/family/apply/getMySignUp", {courseId: row.id})
|
||||
})
|
||||
.then((signUpRes) => {
|
||||
if (!signUpRes) return
|
||||
if (signUpRes.code !== 0) {
|
||||
this.$message.warning(signUpRes.msg)
|
||||
return
|
||||
}
|
||||
this.$refs.signFormRef.onOpen(row, courseType, this.activity, signUpRes.data)
|
||||
})
|
||||
.finally(() => {
|
||||
this.tableLoading = false
|
||||
})
|
||||
},
|
||||
onCancel(row) {
|
||||
this.$confirm("您确定要取消吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "info"
|
||||
}).then(async () => {
|
||||
const resp = await this.$axios.post("/platform/family/apply/cancelSignUp", {
|
||||
}).then(() => {
|
||||
this.$axios.post("/platform/family/apply/cancelSignUp", {
|
||||
activityId: row.activityId,
|
||||
courseId: row.id
|
||||
})
|
||||
if (resp.code === 0) {
|
||||
await this.pageData()
|
||||
this.$message.success(resp.msg)
|
||||
} else {
|
||||
this.$message.warning(resp.msg)
|
||||
}
|
||||
.then((resp) => {
|
||||
if (resp.code !== 0) {
|
||||
this.$message.warning(resp.msg)
|
||||
return
|
||||
}
|
||||
this.$message.success(resp.msg)
|
||||
return this.pageData()
|
||||
})
|
||||
})
|
||||
},
|
||||
async getCourseTypeList() {
|
||||
const resp = await this.$axios.post("/platform/family/type/getAllType")
|
||||
if (resp.code === 0) {
|
||||
this.courseTypeList = resp.data
|
||||
}
|
||||
getCourseTypeList() {
|
||||
return this.$axios.post("/platform/family/type/getAllType")
|
||||
.then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.courseTypeList = resp.data
|
||||
}
|
||||
return resp
|
||||
})
|
||||
},
|
||||
async openViewCourseTime(id) {
|
||||
const resp = await this.$axios.post(loc() + "/getCourseTime", { id: id })
|
||||
this.courseTimeList = resp.data
|
||||
this.courseTimeListDialog = true
|
||||
openViewCourseTime(id) {
|
||||
this.$axios.post(loc() + "/getCourseTime", {id: id})
|
||||
.then((resp) => {
|
||||
this.courseTimeList = resp.data
|
||||
this.courseTimeListDialog = true
|
||||
})
|
||||
},
|
||||
openViewMap(point) {
|
||||
if (!Array.isArray(point)) {
|
||||
@@ -270,16 +372,19 @@ const courseList = {
|
||||
return "<span style='color: red'>余" + lave +"</span>/" + o.coursePeopleNumber + "人"
|
||||
}
|
||||
},
|
||||
async pageData() {
|
||||
pageData() {
|
||||
const pageForm = clone({...this.pageForm})
|
||||
pageForm.assortTypes = JSON.stringify(pageForm.assortTypes)
|
||||
const resp = await this.$axios.post(loc() + "/pageData", pageForm)
|
||||
if (resp.code === 0) {
|
||||
this.tableData = resp.data.list
|
||||
this.pageForm.totalCount = resp.data.totalCount
|
||||
} else {
|
||||
this.$message.warning(resp.msg)
|
||||
}
|
||||
return this.$axios.post(loc() + "/pageData", pageForm)
|
||||
.then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.tableData = resp.data.list
|
||||
this.pageForm.totalCount = resp.data.totalCount
|
||||
} else {
|
||||
this.$message.warning(resp.msg)
|
||||
}
|
||||
return resp
|
||||
})
|
||||
},
|
||||
queryCourseAssort() {
|
||||
this.$axios.post(loc() + "/queryCourseAssort", {activityId: this.activity.id})
|
||||
@@ -289,7 +394,13 @@ const courseList = {
|
||||
},
|
||||
},
|
||||
created() {
|
||||
|
||||
// 页面停留到报名开始时间时自动切换为正式报名按钮,无需用户手工刷新。
|
||||
this.signTimeTimer = window.setInterval(() => {
|
||||
this.nowTime = Date.now()
|
||||
}, 1000)
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.signTimeTimer) window.clearInterval(this.signTimeTimer)
|
||||
},
|
||||
style: /*language=CSS*/ `
|
||||
.glow-box {
|
||||
|
||||
@@ -170,10 +170,7 @@ layout("/layouts/platform.html"){
|
||||
})
|
||||
},
|
||||
onOpen(row) {
|
||||
if(this.$store.state.user.loginname !== '45066' && this.$moment().isBefore(this.$moment(row.activitySignUpStartTime))) {
|
||||
this.onView(row)
|
||||
return
|
||||
}
|
||||
// 报名前允许进入课程列表维护预填信息,正式报名仍由后端报名时间校验控制。
|
||||
this.infoVisible = false
|
||||
this.$refs.guava.view(() => {
|
||||
this.$refs.courseListRef.onOpen(row)
|
||||
@@ -188,7 +185,7 @@ layout("/layouts/platform.html"){
|
||||
})
|
||||
},
|
||||
},
|
||||
async created() {
|
||||
created() {
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
const signForm = {
|
||||
template: /*language=HTML*/ `
|
||||
<div>
|
||||
<el-dialog :close-on-click-modal="false" :visible.sync="signDialog" title="信息填写" width="50%"
|
||||
<el-dialog :close-on-click-modal="false" :visible.sync="signDialog"
|
||||
:title="prefillMode ? (formData.id ? '修改预填信息' : '预填报名信息') : (editMode ? '修改报名' : '信息填写')" width="50%"
|
||||
append-to-body>
|
||||
<el-form :model="formData" ref="form" label-width="120px">
|
||||
<div class="left-span-label">个人信息</div>
|
||||
@@ -36,7 +37,8 @@ const signForm = {
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="联系方式" prop="mobile">
|
||||
<el-form-item label="联系方式" prop="mobile"
|
||||
:rules="{ required: true, message: '请填写联系方式', trigger: 'blur'}">
|
||||
<el-input v-model="formData.mobile"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@@ -137,7 +139,7 @@ const signForm = {
|
||||
|
||||
<el-row class="mt20" justify="end" type="flex">
|
||||
<el-button @click="signDialog = false">取 消</el-button>
|
||||
<el-button @click="onSubmit" type="primary">提 交</el-button>
|
||||
<el-button :loading="formLoading" @click="onSubmit" type="primary">{{prefillMode ? '保存预填' : '提 交'}}</el-button>
|
||||
</el-row>
|
||||
</el-dialog>
|
||||
</div>
|
||||
@@ -147,6 +149,9 @@ const signForm = {
|
||||
data() {
|
||||
return {
|
||||
signDialog: false,
|
||||
editMode: false,
|
||||
prefillMode: false,
|
||||
formLoading: false,
|
||||
formData: {
|
||||
mobileColumnsValue: [],
|
||||
},
|
||||
@@ -172,58 +177,79 @@ const signForm = {
|
||||
const ac = (Number(this.active) - 1)
|
||||
this.active = '' + (ac >= 0 ? ac : 0)
|
||||
},
|
||||
async validSignUp() {
|
||||
validSignUp() {
|
||||
//获取家属是多少人
|
||||
const res = await this.$axios.post("/platform/family/apply/validateSignUp", {
|
||||
return this.$axios.post("/platform/family/apply/validateSignUp", {
|
||||
courseId: this.formData.courseId,
|
||||
currentFamilyNumber: this.formData.mobileColumnsValue.length || 0
|
||||
})
|
||||
if (res.code !== 0) {
|
||||
this.$alert(res.msg, "提示", {
|
||||
confirmButtonText: "确定",
|
||||
type: "warning"
|
||||
.then((res) => {
|
||||
if (res.code !== 0) {
|
||||
this.$alert(res.msg, "提示", {
|
||||
confirmButtonText: "确定",
|
||||
type: "warning"
|
||||
})
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
async validateSourceSignUp() {
|
||||
if (this.courseRow.courseIsLimitApply) {
|
||||
const resp = await $.post('/platform/family/apply/validateSourceSignUp', {
|
||||
validateSourceSignUp() {
|
||||
if (!this.courseRow.courseIsLimitApply) {
|
||||
return Promise.resolve(true)
|
||||
}
|
||||
return $.post('/platform/family/apply/validateSourceSignUp', {
|
||||
activityCourseId: this.formData.activityCourseId,
|
||||
courseId: this.courseRow.id
|
||||
})
|
||||
if (resp.code !== 0) {
|
||||
this.$alert(resp.msg, "提示", {
|
||||
confirmButtonText: "确定",
|
||||
type: "warning"
|
||||
})
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
},
|
||||
async getCourseTimeSelectList(o) {
|
||||
const resp = await $.post('/platform/family/apply/getCourseTimeSelectList', {courseId: o.id})
|
||||
if (resp.code === 0) {
|
||||
this.courseTimeSelectList = resp.data
|
||||
} else {
|
||||
this.$message.warning('获取时段信息失败,请联系管理员')
|
||||
}
|
||||
},
|
||||
async onOpen(row, courseType, activity) {
|
||||
this.initData(row, courseType, activity)
|
||||
if (row.courseIsLimitApply) {
|
||||
await this.getCourseTimeSelectList(row)
|
||||
}
|
||||
if (courseType && courseType.familyMobileSignColumnList.length > 0) {
|
||||
courseType.familyMobileSignColumnList.forEach(item => {
|
||||
item.columnValue = ''
|
||||
.then((resp) => {
|
||||
if (resp.code !== 0) {
|
||||
this.$alert(resp.msg, "提示", {
|
||||
confirmButtonText: "确定",
|
||||
type: "warning"
|
||||
})
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
.always(() => {})
|
||||
},
|
||||
getCourseTimeSelectList(o, familyUserId) {
|
||||
const url = familyUserId
|
||||
? "/platform/family/apply/getEditCourseTimeSelectList"
|
||||
: "/platform/family/apply/getCourseTimeSelectList"
|
||||
this.formLoading = true
|
||||
return $.post(url, {courseId: o.id, familyUserId: familyUserId})
|
||||
.then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.$set(this, "courseTimeSelectList", resp.data)
|
||||
} else {
|
||||
this.$message.warning(resp.msg || "获取时段信息失败,请联系管理员")
|
||||
}
|
||||
return resp
|
||||
})
|
||||
.always(() => {
|
||||
this.formLoading = false
|
||||
})
|
||||
},
|
||||
onOpen(row, courseType, activity, signUpData, mode) {
|
||||
this.prefillMode = mode === "prefill"
|
||||
this.initData(row, courseType, activity, signUpData)
|
||||
this.$set(this, "courseRow", row)
|
||||
this.$set(this, "courseTypeRow", courseType)
|
||||
const openDialog = () => {
|
||||
this.$set(this, "signDialog", true)
|
||||
}
|
||||
this.courseRow = row
|
||||
this.courseTypeRow = courseType
|
||||
this.signDialog = true
|
||||
if (row.courseIsLimitApply) {
|
||||
this.getCourseTimeSelectList(row, this.editMode && signUpData ? signUpData.id : "")
|
||||
.then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
openDialog()
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
openDialog()
|
||||
},
|
||||
validateIdCard(idCard) {
|
||||
if (!idCard) {
|
||||
@@ -354,60 +380,100 @@ const signForm = {
|
||||
|
||||
return { age, sex, };
|
||||
},
|
||||
async onSubmit() {
|
||||
onSubmit() {
|
||||
//验证家属表单
|
||||
if (!this.validFamilyForm()) return
|
||||
if (!await this.validSignUp()) return
|
||||
if (!await this.validateSourceSignUp()) return
|
||||
this.$refs["form"].validate((valid) => {
|
||||
if (valid) {
|
||||
this.$confirm("您确定要提交吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(async () => {
|
||||
const formData = clone(this.formData)
|
||||
let array = []
|
||||
formData.mobileColumnsValue.forEach(item => {
|
||||
const o = item.map((v) => {
|
||||
return {
|
||||
columnName: v.columnName,
|
||||
columnValue: v.columnValue,
|
||||
columnCode: v.columnCode,
|
||||
columnFormType: v.columnFormType
|
||||
}
|
||||
const signUpValidation = (this.editMode || this.prefillMode) ? Promise.resolve(true) : this.validSignUp()
|
||||
signUpValidation.then((signUpValid) => {
|
||||
if (!signUpValid) return
|
||||
const sourceValidation = (this.editMode || this.prefillMode) ? Promise.resolve(true) : this.validateSourceSignUp()
|
||||
sourceValidation.then((sourceValid) => {
|
||||
if (!sourceValid) return
|
||||
this.$refs["form"].validate((valid) => {
|
||||
if (!valid) return
|
||||
this.$confirm("您确定要提交吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
const formData = clone(this.formData)
|
||||
const array = formData.mobileColumnsValue.map((item) => {
|
||||
return item.map((column) => {
|
||||
return {
|
||||
columnName: column.columnName,
|
||||
columnValue: column.columnValue,
|
||||
columnCode: column.columnCode,
|
||||
columnFormType: column.columnFormType
|
||||
}
|
||||
})
|
||||
})
|
||||
array.push(o)
|
||||
formData.mobileColumnsValue = JSON.stringify(clone(array))
|
||||
const url = this.prefillMode
|
||||
? "/platform/family/apply/savePrefill"
|
||||
: (this.editMode
|
||||
? "/platform/family/apply/updateSignUp"
|
||||
: "/platform/family/apply/doSignUp")
|
||||
this.formLoading = true
|
||||
this.$axios.post(url, formData)
|
||||
.then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.$alert(resp.msg, "提示", {
|
||||
confirmButtonText: "确定",
|
||||
type: "warning"
|
||||
})
|
||||
this.signDialog = false
|
||||
this.$emit("refresh")
|
||||
} else {
|
||||
this.$message.warning(resp.msg)
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.formLoading = false
|
||||
})
|
||||
})
|
||||
formData.mobileColumnsValue = JSON.stringify(clone(array))
|
||||
const resp = await this.$axios.post("/platform/family/apply/doSignUp", formData)
|
||||
if (resp.code === 0) {
|
||||
this.$alert(resp.msg, "提示", {
|
||||
confirmButtonText: "确定",
|
||||
type: "warning"
|
||||
})
|
||||
this.signDialog = false
|
||||
this.$emit('refresh')
|
||||
} else {
|
||||
this.$message.warning(resp.msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
initData(row, courseType, activity) {
|
||||
this.activity = activity
|
||||
this.formData = {
|
||||
// 修改时按 columnCode 将历史值合并进当前字段配置,保留最新必填和格式校验规则。
|
||||
mergeMobileColumnsValue(courseType, mobileColumnsValue) {
|
||||
const columnConfig = courseType && Array.isArray(courseType.familyMobileSignColumnList)
|
||||
? courseType.familyMobileSignColumnList : []
|
||||
return (mobileColumnsValue || []).map((family) => {
|
||||
if (columnConfig.length === 0) {
|
||||
return clone(family)
|
||||
}
|
||||
return clone(columnConfig).map((column) => {
|
||||
const oldColumn = family.find((item) => item.columnCode === column.columnCode)
|
||||
this.$set(column, "columnValue", oldColumn ? oldColumn.columnValue : "")
|
||||
return column
|
||||
})
|
||||
})
|
||||
},
|
||||
initData(row, courseType, activity, signUpData) {
|
||||
this.$set(this, "activity", activity)
|
||||
this.editMode = !this.prefillMode && Boolean(signUpData && signUpData.id)
|
||||
// 部分历史账号未关联单位或分工会,初始化报名表单时需兼容关联对象为空。
|
||||
const currentUser = this.$store.state.user || {}
|
||||
const formData = {
|
||||
activityId: row.activityId,
|
||||
courseId: row.id,
|
||||
username: this.$store.state.user.username,
|
||||
loginname: this.$store.state.user.loginname,
|
||||
unionName: this.$store.state.user.union.name,
|
||||
unitName: this.$store.state.user.unit.name,
|
||||
sex: this.$store.state.user.sex,
|
||||
mobile: this.$store.state.user.mobile,
|
||||
username: currentUser.username,
|
||||
loginname: currentUser.loginname,
|
||||
unionName: currentUser.union ? currentUser.union.name : "",
|
||||
unitName: currentUser.unit ? currentUser.unit.name : "",
|
||||
sex: currentUser.sex,
|
||||
mobile: currentUser.mobile,
|
||||
mobileColumnsValue: [],
|
||||
}
|
||||
if ((this.editMode || this.prefillMode) && signUpData) {
|
||||
this.$set(formData, "id", signUpData.id)
|
||||
this.$set(formData, "mobile", signUpData.mobile)
|
||||
this.$set(formData, "activityCourseId", signUpData.activityCourseId)
|
||||
this.$set(formData, "mobileColumnsValue",
|
||||
this.mergeMobileColumnsValue(courseType, signUpData.mobileColumnsValue))
|
||||
}
|
||||
this.$set(this, "formData", formData)
|
||||
this.$nextTick(() => {
|
||||
this.$refs.form.clearValidate()
|
||||
})
|
||||
|
||||
@@ -145,10 +145,7 @@ layout("/layouts/platform_h5.html"){
|
||||
this.infoVisible = true
|
||||
},
|
||||
onApply(row) {
|
||||
if(this.$store.state.user.loginname !== '45066' && this.$moment().isBefore(this.$moment(row.activitySignUpStartTime))) {
|
||||
this.onView(row)
|
||||
return
|
||||
}
|
||||
// 报名前允许进入课程列表维护预填信息,预填不会提前占用名额。
|
||||
this.$pjaxReplace('/platform/family/apply/list/h5?id=' + row.id)
|
||||
},
|
||||
onReady() {
|
||||
|
||||
@@ -3,7 +3,9 @@ const applyForm = {
|
||||
/*language=HTML*/
|
||||
`
|
||||
<div>
|
||||
<van-action-sheet :close-on-click-overlay="false" title="报名信息" v-model="visible">
|
||||
<van-action-sheet :close-on-click-overlay="false"
|
||||
:title="prefillMode ? (formData.id ? '修改预填信息' : '预填报名信息') : (editMode ? '修改报名' : '报名信息')"
|
||||
v-model="visible" @close="onFormClosed">
|
||||
<van-form ref="formRef" class="form-container">
|
||||
<van-cell-group title="活动信息" class="form-section">
|
||||
<van-field label="活动名称" readonly v-model="row.courseName"></van-field>
|
||||
@@ -16,22 +18,25 @@ const applyForm = {
|
||||
<van-field label="所在单位" readonly v-model="formData.unitName"></van-field>
|
||||
<van-field label="所属工会" readonly v-model="formData.unionName"></van-field>
|
||||
<van-field label="性别" readonly v-model="formData.sex"></van-field>
|
||||
<van-field label="联系方式" required name="mobile" v-model="formData.mobile"
|
||||
:rules="[{ required: true, message: '请填写联系方式' }]"
|
||||
placeholder="请填写联系方式"></van-field>
|
||||
<template v-if="row.courseIsLimitApply">
|
||||
<van-field label="报名时段"
|
||||
required
|
||||
:rules="[{ required: true, message: '请选择报名时段' }]"
|
||||
readonly
|
||||
@click="showCoursePicker = true"
|
||||
@click="openCoursePicker"
|
||||
placeholder="请选择报名时段"
|
||||
name="courseTimeName"
|
||||
v-model="formData.courseTimeName">
|
||||
</van-field>
|
||||
<van-popup v-model="showCoursePicker" position="bottom">
|
||||
<van-popup v-model="showCoursePicker" position="bottom" @close="onCoursePickerClosed">
|
||||
<van-picker
|
||||
show-toolbar
|
||||
:columns="courseTimeSelectList"
|
||||
@confirm="onCourseConfirm"
|
||||
@cancel="showCoursePicker=false"
|
||||
@cancel="closeCoursePicker"
|
||||
></van-picker>
|
||||
</van-popup>
|
||||
</template>
|
||||
@@ -65,7 +70,7 @@ const applyForm = {
|
||||
</van-cell-group>
|
||||
|
||||
<div class="button">
|
||||
<van-button @click="onSubmit" type="info" block>提交</van-button>
|
||||
<van-button :loading="formLoading" @click="onSubmit" type="info" block>{{prefillMode ? '保存预填' : '提交'}}</van-button>
|
||||
</div>
|
||||
</van-form>
|
||||
</van-action-sheet>
|
||||
@@ -77,15 +82,25 @@ const applyForm = {
|
||||
row: {},
|
||||
activity: {},
|
||||
visible: false,
|
||||
editMode: false,
|
||||
prefillMode: false,
|
||||
formLoading: false,
|
||||
formData: {},
|
||||
showCoursePicker: false,
|
||||
courseTimeSelectList: [],
|
||||
courseType: {},
|
||||
historyOwner: '',
|
||||
}
|
||||
},
|
||||
components: {
|
||||
"train-dynamic-form": httpVueLoader("/components/module/trainSignUp/TrainDynamicForm.vue")
|
||||
},
|
||||
mounted() {
|
||||
this.historyOwner = "family-apply-form-" + this._uid
|
||||
},
|
||||
beforeDestroy() {
|
||||
h5PopupHistory.unregisterOwner(this.historyOwner)
|
||||
},
|
||||
methods: {
|
||||
addFamily() {
|
||||
if(this.formData.mobileColumnsValue.length >= this.activity.familyMaxCount) {
|
||||
@@ -100,40 +115,110 @@ const applyForm = {
|
||||
const ac = (Number(this.familyActive) - 1)
|
||||
this.familyActive = '' + (ac >= 0 ? ac : 0)
|
||||
},
|
||||
async onOpen(row, courseType, activity) {
|
||||
this.row = row
|
||||
this.courseType = courseType
|
||||
this.activity = activity
|
||||
|
||||
this.init(row, courseType)
|
||||
if(row.courseIsLimitApply) {
|
||||
await this.getCourseTimeSelectList(row)
|
||||
onOpen(row, courseType, activity, signUpData, mode) {
|
||||
this.prefillMode = mode === "prefill"
|
||||
this.$set(this, "row", row)
|
||||
this.$set(this, "courseType", courseType)
|
||||
this.$set(this, "activity", activity)
|
||||
this.init(row, courseType, signUpData)
|
||||
const openForm = () => {
|
||||
h5PopupHistory.open(this.historyOwner + "-form", this.historyOwner, () => {
|
||||
this.$set(this, "visible", false)
|
||||
})
|
||||
this.$set(this, "visible", true)
|
||||
}
|
||||
this.visible = true
|
||||
if(row.courseIsLimitApply) {
|
||||
this.getCourseTimeSelectList(row, this.editMode && signUpData ? signUpData.id : "")
|
||||
.then((resp) => {
|
||||
if (resp.code !== 0) return
|
||||
this.setCourseTimeName(signUpData ? signUpData.activityCourseId : "")
|
||||
openForm()
|
||||
})
|
||||
return
|
||||
}
|
||||
openForm()
|
||||
},
|
||||
init(row, courseType) {
|
||||
this.$set(this.formData, 'username', this.$store.state.user.username)
|
||||
this.$set(this.formData, 'loginname', this.$store.state.user.loginname)
|
||||
this.$set(this.formData, 'unionName', this.$store.state.user.union.name)
|
||||
this.$set(this.formData, 'unitName', this.$store.state.user.unit.name)
|
||||
this.$set(this.formData, 'sex', this.$store.state.user.sex)
|
||||
this.$set(this.formData, 'activityId', row.activityId)
|
||||
this.$set(this.formData, 'courseId', row.id)
|
||||
this.$set(this.formData, 'mobileColumnsValue', [])
|
||||
init(row, courseType, signUpData) {
|
||||
this.editMode = !this.prefillMode && Boolean(signUpData && signUpData.id)
|
||||
this.$set(this, "familyActive", "")
|
||||
this.$set(this, "courseTimeSelectList", [])
|
||||
this.$set(this, "showCoursePicker", false)
|
||||
const formData = {
|
||||
username: this.$store.state.user.username,
|
||||
loginname: this.$store.state.user.loginname,
|
||||
unionName: this.$store.state.user.union.name,
|
||||
unitName: this.$store.state.user.unit.name,
|
||||
sex: this.$store.state.user.sex,
|
||||
mobile: this.$store.state.user.mobile,
|
||||
activityId: row.activityId,
|
||||
courseId: row.id,
|
||||
mobileColumnsValue: []
|
||||
}
|
||||
if ((this.editMode || this.prefillMode) && signUpData) {
|
||||
this.$set(formData, "id", signUpData.id)
|
||||
this.$set(formData, "mobile", signUpData.mobile)
|
||||
this.$set(formData, "activityCourseId", signUpData.activityCourseId)
|
||||
this.$set(formData, "mobileColumnsValue",
|
||||
this.mergeMobileColumnsValue(courseType, signUpData.mobileColumnsValue))
|
||||
}
|
||||
this.$set(this, "formData", formData)
|
||||
},
|
||||
onCourseConfirm(val){
|
||||
this.formData.activityCourseId = val.value
|
||||
this.$set(this.formData, "activityCourseId", val.value)
|
||||
this.$set(this.formData, "courseTimeName", val.text.substring(0, 12))
|
||||
this.showCoursePicker = false
|
||||
this.closeCoursePicker()
|
||||
},
|
||||
async getCourseTimeSelectList(o) {
|
||||
const resp = await this.$axios.post('/platform/family/apply/getCourseTimeSelectList',{courseId: o.id})
|
||||
if (resp.code === 0) {
|
||||
this.courseTimeSelectList = resp.data
|
||||
} else {
|
||||
this.$toast.fail("获取时段信息失败,请联系管理员")
|
||||
}
|
||||
getCourseTimeSelectList(o, familyUserId) {
|
||||
const url = familyUserId
|
||||
? "/platform/family/apply/getEditCourseTimeSelectList"
|
||||
: "/platform/family/apply/getCourseTimeSelectList"
|
||||
this.formLoading = true
|
||||
return this.$axios.post(url, {courseId: o.id, familyUserId: familyUserId})
|
||||
.then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.$set(this, "courseTimeSelectList", resp.data)
|
||||
} else {
|
||||
this.$toast.fail(resp.msg || "获取时段信息失败,请联系管理员")
|
||||
}
|
||||
return resp
|
||||
})
|
||||
.finally(() => {
|
||||
this.formLoading = false
|
||||
})
|
||||
},
|
||||
// 打开时段选择器时增加一层历史,返回键只关闭当前选择器。
|
||||
openCoursePicker() {
|
||||
h5PopupHistory.open(this.historyOwner + "-course-picker", this.historyOwner, () => {
|
||||
this.$set(this, "showCoursePicker", false)
|
||||
})
|
||||
this.$set(this, "showCoursePicker", true)
|
||||
},
|
||||
closeCoursePicker() {
|
||||
h5PopupHistory.close(this.historyOwner + "-course-picker")
|
||||
},
|
||||
onCoursePickerClosed() {
|
||||
h5PopupHistory.close(this.historyOwner + "-course-picker")
|
||||
},
|
||||
onFormClosed() {
|
||||
h5PopupHistory.close(this.historyOwner + "-form")
|
||||
},
|
||||
setCourseTimeName(activityCourseId) {
|
||||
if (!activityCourseId) return
|
||||
const selected = this.courseTimeSelectList.find((item) => item.value === activityCourseId)
|
||||
if (selected) this.$set(this.formData, "courseTimeName", selected.text.substring(0, 12))
|
||||
},
|
||||
// 修改时按字段编码合并历史值,继续使用活动当前配置的必填和格式规则。
|
||||
mergeMobileColumnsValue(courseType, mobileColumnsValue) {
|
||||
const columnConfig = courseType && Array.isArray(courseType.familyMobileSignColumnList)
|
||||
? courseType.familyMobileSignColumnList : []
|
||||
return (mobileColumnsValue || []).map((family) => {
|
||||
if (columnConfig.length === 0) return clone(family)
|
||||
return clone(columnConfig).map((column) => {
|
||||
const oldColumn = family.find((item) => item.columnCode === column.columnCode)
|
||||
this.$set(column, "columnValue", oldColumn ? oldColumn.columnValue : "")
|
||||
return column
|
||||
})
|
||||
})
|
||||
},
|
||||
validateIdCard(idCard) {
|
||||
if (!idCard) {
|
||||
@@ -266,62 +351,88 @@ const applyForm = {
|
||||
|
||||
return { age, sex, };
|
||||
},
|
||||
async validateSignUp() {
|
||||
validateSignUp() {
|
||||
// 获取家属人数
|
||||
const res = await this.$axios.post("/platform/family/apply/validateSignUp", {
|
||||
return this.$axios.post("/platform/family/apply/validateSignUp", {
|
||||
courseId: this.formData.courseId,
|
||||
currentFamilyNumber: this.formData.mobileColumnsValue.length || 0
|
||||
})
|
||||
if(res.code !== 0) {
|
||||
this.$dialog.alert({title: '温馨提示', message: res.msg})
|
||||
}
|
||||
return res.code === 0
|
||||
.then((res) => {
|
||||
if(res.code !== 0) {
|
||||
this.$dialog.alert({title: '温馨提示', message: res.msg})
|
||||
}
|
||||
return res.code === 0
|
||||
})
|
||||
},
|
||||
async validateCourseTime() {
|
||||
const res = await this.$axios.post('/platform/family/apply/validateSourceSignUp', {
|
||||
validateUpdateSignUp() {
|
||||
return this.$axios.post("/platform/family/apply/validateUpdateSignUp", {
|
||||
courseId: this.formData.courseId
|
||||
}).then((res) => {
|
||||
if(res.code !== 0) {
|
||||
this.$toast.fail(res.msg)
|
||||
}
|
||||
return res.code === 0
|
||||
})
|
||||
},
|
||||
validateCourseTime() {
|
||||
return this.$axios.post('/platform/family/apply/validateSourceSignUp', {
|
||||
activityCourseId: this.formData.activityCourseId,
|
||||
courseId: this.row.id
|
||||
})
|
||||
if(res.code !== 0) {
|
||||
this.$dialog.alert({title: '温馨提示', message: res.msg})
|
||||
}
|
||||
return res.code === 0
|
||||
},
|
||||
async onSubmit() {
|
||||
if (!this.validFamilyForm()) return
|
||||
if (!await this.validateSignUp()) return
|
||||
|
||||
this.$refs.formRef.validate().then(() => {
|
||||
this.$dialog.confirm({
|
||||
title: "提示",
|
||||
message: "您确定要提交吗?"
|
||||
}).then(async () => {
|
||||
if (this.row.courseIsLimitApply) {
|
||||
if(!await this.validateCourseTime()) return
|
||||
}
|
||||
|
||||
const formData = clone(this.formData)
|
||||
let array = []
|
||||
formData.mobileColumnsValue.forEach(item => {
|
||||
const o = item.map((v) => {
|
||||
return {
|
||||
columnName: v.columnName,
|
||||
columnValue: v.columnValue,
|
||||
columnCode: v.columnCode,
|
||||
columnFormType: v.columnFormType
|
||||
}
|
||||
})
|
||||
array.push(o)
|
||||
})
|
||||
formData.mobileColumnsValue = JSON.stringify(clone(array))
|
||||
this.$axios.post("/platform/family/apply/doSignUp", formData).then(res => {
|
||||
.then((res) => {
|
||||
if(res.code !== 0) {
|
||||
this.$dialog.alert({title: '温馨提示', message: res.msg})
|
||||
.then(() => {
|
||||
if (res.code === 0) {
|
||||
this.visible = false
|
||||
this.$emit('refresh')
|
||||
}
|
||||
}
|
||||
return res.code === 0
|
||||
})
|
||||
},
|
||||
onSubmit() {
|
||||
if (!this.validFamilyForm()) return
|
||||
const eligibility = this.prefillMode
|
||||
? Promise.resolve(true)
|
||||
: (this.editMode ? this.validateUpdateSignUp() : this.validateSignUp())
|
||||
eligibility.then((eligible) => {
|
||||
if (!eligible) return
|
||||
this.$refs.formRef.validate().then(() => {
|
||||
this.$dialog.confirm({
|
||||
title: "提示",
|
||||
message: "您确定要提交吗?"
|
||||
}).then(() => {
|
||||
const courseTimeValidation = !this.editMode && !this.prefillMode && this.row.courseIsLimitApply
|
||||
? this.validateCourseTime() : Promise.resolve(true)
|
||||
courseTimeValidation.then((courseTimeValid) => {
|
||||
if (!courseTimeValid) return
|
||||
const formData = clone(this.formData)
|
||||
const array = formData.mobileColumnsValue.map((item) => {
|
||||
return item.map((column) => {
|
||||
return {
|
||||
columnName: column.columnName,
|
||||
columnValue: column.columnValue,
|
||||
columnCode: column.columnCode,
|
||||
columnFormType: column.columnFormType
|
||||
}
|
||||
})
|
||||
})
|
||||
formData.mobileColumnsValue = JSON.stringify(clone(array))
|
||||
const url = this.prefillMode
|
||||
? "/platform/family/apply/savePrefill"
|
||||
: (this.editMode
|
||||
? "/platform/family/apply/updateSignUp"
|
||||
: "/platform/family/apply/doSignUp")
|
||||
this.formLoading = true
|
||||
this.$axios.post(url, formData)
|
||||
.then((res) => {
|
||||
this.$dialog.alert({title: "温馨提示", message: res.msg})
|
||||
.then(() => {
|
||||
if (res.code !== 0) return
|
||||
h5PopupHistory.clearOwner(this.historyOwner)
|
||||
this.$emit("refresh")
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
this.formLoading = false
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -71,10 +71,22 @@ 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 && isBeforeSignUpTime()" class="action-btn" @click="onPrefill(row)">
|
||||
<i class="fa fa-edit"></i>
|
||||
<span>{{row.hasPrefill ? '修改预填信息' : '预填报名信息'}}</span>
|
||||
</div>
|
||||
<div v-if="row.isSign === false && isInSignUpTime() && row.hasPrefill" class="action-btn" @click="onQuickSignUp(row)">
|
||||
<i class="fa fa-bolt"></i>
|
||||
<span>立即报名</span>
|
||||
</div>
|
||||
<div v-if="row.isSign === false && isInSignUpTime() && !row.hasPrefill" class="action-btn" @click="onApply(row)">
|
||||
<i class="fa fa-sign-in"></i>
|
||||
<span>我要报名</span>
|
||||
</div>
|
||||
<div v-if="row.isSign === true && isInSignUpTime()" class="action-btn" @click="onEdit(row)">
|
||||
<i class="fa fa-edit"></i>
|
||||
<span>修改报名</span>
|
||||
</div>
|
||||
<div v-if="row.isSign === true" class="action-btn delete" @click="onCancel(row)">
|
||||
<i class="fa fa-trash"></i>
|
||||
<span>取消报名</span>
|
||||
@@ -129,6 +141,8 @@ layout("/layouts/platform_h5.html"){
|
||||
|
||||
introduceRow: {},
|
||||
activity: {},
|
||||
nowTime: Date.now(),
|
||||
signTimeTimer: null,
|
||||
weekdayCNMap: {0: '周日', 1: '周一', 2: '周二', 3: '周三', 4: '周四', 5: '周五', 6: '周六'},
|
||||
}
|
||||
},
|
||||
@@ -136,16 +150,58 @@ layout("/layouts/platform_h5.html"){
|
||||
refresh() {
|
||||
this.doSearch()
|
||||
},
|
||||
async onTime(row) {
|
||||
// 预填信息只能在报名开始时间之前维护。
|
||||
isBeforeSignUpTime() {
|
||||
return this.activity.activitySignUpStartTime
|
||||
&& this.$moment(this.nowTime).isBefore(this.$moment(this.activity.activitySignUpStartTime))
|
||||
},
|
||||
onTime(row) {
|
||||
// 如果设置签到,并且也报名的话
|
||||
if(row.isMobileSign === true && row.isSign === true) {
|
||||
const res = await this.$axios.post('/platform/family/mine/queryCourseSign', {
|
||||
this.$axios.post('/platform/family/mine/queryCourseSign', {
|
||||
courseId: row.id
|
||||
})
|
||||
row.courseTimes = res.data
|
||||
.then((res) => {
|
||||
this.$set(row, "courseTimes", res.data)
|
||||
this.$refs.timesRef.onOpen(row)
|
||||
})
|
||||
return
|
||||
}
|
||||
this.$refs.timesRef.onOpen(row)
|
||||
},
|
||||
// 修改报名按钮与报名接口使用同一时间边界:开始时间已到且尚未达到结束时间。
|
||||
isInSignUpTime() {
|
||||
const now = this.$moment(this.nowTime)
|
||||
return this.activity.activitySignUpStartTime
|
||||
&& this.activity.activitySignUpEndTime
|
||||
&& !now.isBefore(this.$moment(this.activity.activitySignUpStartTime))
|
||||
&& now.isBefore(this.$moment(this.activity.activitySignUpEndTime))
|
||||
},
|
||||
onPrefill(row) {
|
||||
const courseType = this.sourceTypeOptions.find((item) => item.id === row.courseType)
|
||||
this.$axios.post("/platform/family/apply/getPrefill", {courseId: row.id})
|
||||
.then((resp) => {
|
||||
if (resp.code !== 0) {
|
||||
this.$toast.fail(resp.msg)
|
||||
return
|
||||
}
|
||||
this.$refs.formRef.onOpen(row, courseType, this.activity, resp.data, "prefill")
|
||||
})
|
||||
},
|
||||
onQuickSignUp(row) {
|
||||
this.$dialog.confirm({
|
||||
title: "提示",
|
||||
message: "将使用报名开始前保存的预填信息直接报名,是否继续?"
|
||||
}).then(() => {
|
||||
this.$axios.post("/platform/family/apply/quickSignUp", {courseId: row.id})
|
||||
.then((resp) => {
|
||||
this.$dialog.alert({title: "温馨提示", message: resp.msg})
|
||||
.then(() => {
|
||||
if (resp.code === 0) this.doSearch()
|
||||
})
|
||||
})
|
||||
}).catch(() => {})
|
||||
},
|
||||
onApply(row) {
|
||||
const courseType = this.sourceTypeOptions.find((v) => v.id === row.courseType)
|
||||
this.$axios.post('/platform/family/apply/validateSignUp', {
|
||||
@@ -171,20 +227,38 @@ layout("/layouts/platform_h5.html"){
|
||||
}
|
||||
})
|
||||
},
|
||||
onEdit(row) {
|
||||
const courseType = this.sourceTypeOptions.find((item) => item.id === row.courseType)
|
||||
this.$axios.post("/platform/family/apply/validateUpdateSignUp", {courseId: row.id})
|
||||
.then((validateRes) => {
|
||||
if (validateRes.code !== 0) {
|
||||
this.$toast.fail(validateRes.msg)
|
||||
return null
|
||||
}
|
||||
return this.$axios.post("/platform/family/apply/getMySignUp", {courseId: row.id})
|
||||
})
|
||||
.then((signUpRes) => {
|
||||
if (!signUpRes || signUpRes.code !== 0) {
|
||||
if (signUpRes) this.$toast.fail(signUpRes.msg)
|
||||
return
|
||||
}
|
||||
this.$refs.formRef.onOpen(row, courseType, this.activity, signUpRes.data)
|
||||
})
|
||||
},
|
||||
onCancel(row) {
|
||||
vant.Dialog.confirm({
|
||||
title: '温馨提示',
|
||||
message: "您确定要<span style='color: red'>取消【" + row.courseName + "】</span>吗?",
|
||||
confirmButtonColor: '#1867b0',
|
||||
}).then(async () => {
|
||||
const resp = await this.$axios.post('/platform/family/apply/cancelSignUp', {
|
||||
}).then(() => {
|
||||
this.$axios.post('/platform/family/apply/cancelSignUp', {
|
||||
activityId: row.activityId,
|
||||
courseId: row.id
|
||||
})
|
||||
this.$toast(resp.msg)
|
||||
if (resp.code === 0) {
|
||||
this.doSearch()
|
||||
}
|
||||
.then((resp) => {
|
||||
this.$toast(resp.msg)
|
||||
if (resp.code === 0) this.doSearch()
|
||||
})
|
||||
}).catch(() => {})
|
||||
},
|
||||
calcSignUpCount(row) {
|
||||
@@ -200,30 +274,31 @@ layout("/layouts/platform_h5.html"){
|
||||
return "<span style='color: red'>余" + lave +"</span>/" + row.coursePeopleNumber + "人"
|
||||
}
|
||||
},
|
||||
async onReady() {
|
||||
const typeList = await this.getCourseTypeList()
|
||||
this.sourceTypeOptions = clone(typeList)
|
||||
this.typeOptions = [
|
||||
{
|
||||
text: "全部类型",
|
||||
value: null
|
||||
onReady() {
|
||||
this.getCourseTypeList().then((typeList) => {
|
||||
this.$set(this, "sourceTypeOptions", clone(typeList))
|
||||
this.$set(this, "typeOptions", [
|
||||
{
|
||||
text: "全部类型",
|
||||
value: null
|
||||
}
|
||||
].concat(typeList.map((item) => ({text: item.typeName, value: item.id}))))
|
||||
if (this.typeOptions.length > 0) {
|
||||
this.$set(this.pageForm, "type", this.typeOptions[0].value)
|
||||
this.doSearch()
|
||||
}
|
||||
].concat(typeList.map((v) => ({ text: v.typeName, value: v.id })))
|
||||
if (this.typeOptions.length > 0) {
|
||||
this.pageForm.type = this.typeOptions[0].value
|
||||
this.doSearch()
|
||||
}
|
||||
this.fetchActivity()
|
||||
this.fetchActivity()
|
||||
})
|
||||
},
|
||||
fetchActivity() {
|
||||
this.$axios.post('/platform/family/manage/findOne', {id: this.pageForm.activityId})
|
||||
.then((res) => {
|
||||
this.activity = res.data
|
||||
this.$set(this, "activity", res.data)
|
||||
})
|
||||
},
|
||||
async getCourseTypeList() {
|
||||
const resp = await this.$axios.post("/platform/family/type/getAllType")
|
||||
return resp.data
|
||||
getCourseTypeList() {
|
||||
return this.$axios.post("/platform/family/type/getAllType")
|
||||
.then((resp) => resp.data)
|
||||
},
|
||||
doSearch() {
|
||||
this.$nextTick(() => {
|
||||
@@ -233,6 +308,15 @@ layout("/layouts/platform_h5.html"){
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// 页面停留到报名开始时间时自动切换按钮,保证预填信息按时冻结。
|
||||
this.signTimeTimer = window.setInterval(() => {
|
||||
this.nowTime = Date.now()
|
||||
}, 1000)
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.signTimeTimer) window.clearInterval(this.signTimeTimer)
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user