This commit is contained in:
2026-07-16 10:07:52 +08:00
parent 1706f2d60d
commit 626b3a0713
6 changed files with 80 additions and 3 deletions
@@ -64,7 +64,7 @@ public class SysUserServiceImpl extends BaseServiceImpl<Sys_user> implements Sys
* @param user
* @return
*/
@CacheResult(cacheKey = "${args[0].id}_getRoleCodeList")
// @CacheResult(cacheKey = "${args[0].id}_getRoleCodeList")
public List<String> getRoleCodeList(Sys_user user) {
dao().fetchLinks(user, "roles");
List<String> roleNameList = new ArrayList<String>();
@@ -248,6 +248,8 @@ public class TheRapyRecuperationEnrollController {
@RequiresAuthentication
@Aop(TransAop.READ_COMMITTED)
public Object doSignUpForBaseManagement(@Param("enroll") TheRapyRecuperationEnroll enrollInfo) {
// 行锁会持续到当前事务结束,保证同一目的地的人数校验与保存不会并发执行。
enrollService.lockBaseManagementForSignUp(enrollInfo.getTakePartInBaseManagementId());
Sys_config config = dao.fetch(Sys_config.class, Cnd.where("configKey", "=", "recuperationVersion"));
Map<Boolean, String> resultMap = new HashMap<>();
if("zjxu".equals(config.getConfigValue())) {
@@ -117,6 +117,11 @@ public class TheRapyRecuperationBaseManagement {
@Excel(name = "预计费用")
private String estimatedCost;
@Column
@ColDefine(type = ColType.INT)
@Comment("最大报名人数")
private Integer maxApplyNum;
@Column
@ColDefine(type = ColType.MYSQL_JSON)
@Comment("缩略图")
@@ -64,6 +64,13 @@ public interface TheRapyRecuperationEnrollService extends ViService<TheRapyRecup
*/
void doSignUpForHotel(TheRapyRecuperationEnroll enrollInfo);
/**
* 在当前报名事务中锁定目的地记录,确保同一目的地的人数校验和保存串行执行。
*
* @param baseManagementId 目的地ID
*/
void lockBaseManagementForSignUp(String baseManagementId);
void updateSignUpHotel(TheRapyRecuperationEnroll enrollInfo);
/**
@@ -434,6 +434,19 @@ public class TheRapyRecuperationEnrollServiceImpl extends ViServiceImpl<TheRapyR
insertWith(enrollInfo, "companionList");
}
/**
* 在当前报名事务中锁定目的地记录。
*
* @param baseManagementId 目的地ID,用于锁定本次报名对应的目的地
*/
@Override
public void lockBaseManagementForSignUp(String baseManagementId) {
Sql sql = Sqls.create("SELECT id FROM the_rapy_recuperation_base_management WHERE id = @baseManagementId FOR UPDATE")
.setParam("baseManagementId", baseManagementId);
sql.setCallback(Sqls.callback.str());
dao().execute(sql);
}
/**
* 变更报名
*
@@ -467,6 +480,35 @@ public class TheRapyRecuperationEnrollServiceImpl extends ViServiceImpl<TheRapyR
updateIgnoreNull(enrollInfo);
}
/**
* 校验定点疗休养目的地是否还有报名名额。
*
* @param enrollInfo 当前提交的报名信息,包含目的地ID、报名记录ID和改报记录ID
* @param baseManagement 目的地配置,用于读取最大报名人数
* @return 达到人数上限时返回失败信息;未配置人数上限或仍有名额时返回null
*/
private Map<Boolean, String> validBaseMaxApplyNum(TheRapyRecuperationEnroll enrollInfo,
TheRapyRecuperationBaseManagement baseManagement) {
Integer maxApplyNum = baseManagement.getMaxApplyNum();
// 兼容尚未维护最大报名人数的历史目的地,避免旧数据无法继续报名。
if (maxApplyNum == null || maxApplyNum <= 0) {
return null;
}
Cnd cnd = Cnd.where("takePartInBaseManagementId", "=", baseManagement.getId())
.and("isNormal", "=", true)
.and("stateId", "not in", Lang.array(TheRapyRecuperationState.UNITFAIL,
TheRapyRecuperationState.LINEUNITFAIL, TheRapyRecuperationState.SCHOOLFAIL));
// 编辑或改报时排除原报名记录,防止当前报名人被重复计入名额。
cnd.andEX("id", "!=", enrollInfo.getId());
cnd.andEX("id", "!=", enrollInfo.getEditOther());
int appliedNum = dao().count(TheRapyRecuperationEnroll.class, cnd);
if (appliedNum >= maxApplyNum) {
return Map.of(false, "该目的地报名人数已满,最大报名人数为" + maxApplyNum + "");
}
return null;
}
/**
* 验证报名登记信息
*
@@ -583,6 +625,10 @@ public class TheRapyRecuperationEnrollServiceImpl extends ViServiceImpl<TheRapyR
if (DateUtil.compare(new Date(), signUpEndTime) > 0) {
return Map.of(false, "报名时间已过,抱歉不能报名");
}
Map<Boolean, String> maxApplyNumResult = validBaseMaxApplyNum(enrollInfo, baseManagement);
if (maxApplyNumResult != null) {
return maxApplyNumResult;
}
}
if (StrUtil.isBlank(enrollInfo.getId())) {
@@ -1221,6 +1267,10 @@ public class TheRapyRecuperationEnrollServiceImpl extends ViServiceImpl<TheRapyR
if (DateUtil.compare(new Date(), signUpEndTime) > 0) {
return Map.of(false, "报名时间已过,抱歉不能报名");
}
Map<Boolean, String> maxApplyNumResult = validBaseMaxApplyNum(enrollInfo, baseManagement);
if (maxApplyNumResult != null) {
return maxApplyNumResult;
}
}
@@ -276,7 +276,7 @@ layout("/layouts/platform.html"){
</el-row>
<el-row :gutter="20">
<el-col :md="12" :sm="24" :xs="24">
<el-col span="6">
<el-form-item label="活动开始时间" prop="activityStartTime">
<el-date-picker
placeholder="活动开始时间"
@@ -287,7 +287,7 @@ layout("/layouts/platform.html"){
</el-date-picker>
</el-form-item>
</el-col>
<el-col :md="12" :sm="24" :xs="24">
<el-col span="6">
<el-form-item label="活动结束时间" prop="activityEndTime">
<el-date-picker
placeholder="活动结束时间"
@@ -298,6 +298,16 @@ layout("/layouts/platform.html"){
</el-date-picker>
</el-form-item>
</el-col>
<el-col span="12">
<el-form-item label="最大报名人数" prop="maxApplyNum">
<el-input-number :min="1"
:precision="0"
:step="1"
placeholder="请输入最大报名人数"
style="width: 100%"
v-model="formData.maxApplyNum"></el-input-number>
</el-form-item>
</el-col>
</el-row>
</template>
@@ -320,6 +330,7 @@ layout("/layouts/platform.html"){
</el-switch>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
@@ -479,6 +490,7 @@ layout("/layouts/platform.html"){
files: [],
createMode: '',
estimatedCost: '',
maxApplyNum: null,
activityStartTime: '',
activityEndTime: '',
baseContactPerson: '',
@@ -515,6 +527,7 @@ layout("/layouts/platform.html"){
activityStartTime: [{ required: true, validator: validateActivityStartTime, trigger: ['change', 'blur'] }],
activityEndTime: [{ required: true, validator: validateActivityEndTime, trigger: ['change', 'blur'] }],
estimatedCost: [{required: true, message: '请输入预计费用', trigger: ['change', 'blur']}],
maxApplyNum: [{required: true, message: '请输入最大报名人数', trigger: ['change', 'blur']}],
},
//目的地导入
importVisible: false,