疗休养优化1.2
This commit is contained in:
@@ -12,9 +12,11 @@ import com.budwk.app.sys.models.Sys_home_template;
|
||||
import com.budwk.app.sys.models.Sys_menu;
|
||||
import com.budwk.app.sys.services.SysMenuService;
|
||||
import com.budwk.app.sys.services.SysUserService;
|
||||
import com.budwk.app.web.commons.base.Globals;
|
||||
import com.budwk.app.web.commons.auth.utils.AuthUtil;
|
||||
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
||||
import com.budwk.app.zhgh.activity.basic.models.ActivityUserScope;
|
||||
import com.budwk.app.zhgh.dayofficework.tour.models.TourSetting;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
@@ -40,6 +42,7 @@ import org.nutz.mvc.annotation.Param;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
@@ -278,6 +281,40 @@ public class SysHomeController {
|
||||
return Result.success(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询PC首页疗休养报名浮动入口。
|
||||
* 只返回本年度启用、开启首页入口且已上传图片的疗休养配置,避免首页出现空图片入口。
|
||||
*
|
||||
* @return 当前年度可展示的疗休养报名入口
|
||||
*/
|
||||
@At
|
||||
@SaCheckLogin
|
||||
@ApiOperation("PC首页疗休养报名浮动入口")
|
||||
@Ok("json")
|
||||
public Result listTourHomeSignupEntry() {
|
||||
int currentYear = LocalDate.now().getYear();
|
||||
Cnd cnd = Cnd.where(TourSetting::getDelFlag, "=", false)
|
||||
.and(TourSetting::getEnabled, "=", true)
|
||||
.and(TourSetting::getYear, "=", currentYear)
|
||||
.and(TourSetting::getHomeSignupEntryEnabled, "=", true);
|
||||
cnd.asc(TourSetting::getSortNo);
|
||||
cnd.desc(TourSetting::getUpdatedAt);
|
||||
cnd.desc(TourSetting::getCreatedAt);
|
||||
List<TourSetting> settings = dao.query(TourSetting.class, cnd);
|
||||
List<NutMap> rows = new ArrayList<>();
|
||||
for (TourSetting setting : settings) {
|
||||
// 优先使用配置上传图片,未上传时使用系统默认疗休养图片,避免首页浮动入口空图。
|
||||
String imageUrl = StrUtil.blankToDefault(setting.getHomeSignupEntryImage(), Globals.AppDomain + "/assets/platform/images/tour/疗休养图片.jpeg");
|
||||
rows.add(NutMap.NEW()
|
||||
.addv("id", setting.getId())
|
||||
.addv("year", setting.getYear())
|
||||
.addv("title", setting.getYear() + "暑期教职工疗休养")
|
||||
.addv("imageUrl", imageUrl)
|
||||
.addv("href", "/platform/tour/signup"));
|
||||
}
|
||||
return Result.success(rows);
|
||||
}
|
||||
|
||||
|
||||
@At
|
||||
@SaCheckLogin
|
||||
|
||||
+4
-4
@@ -48,12 +48,12 @@ public class TourBranchUserAssignmentController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询候选人员,候选范围由疗休养配置可参加人员范围和当前登录人所在分工会共同决定。
|
||||
* 分页查询候选人员,候选范围由疗休养配置可参加人员范围和当前登录人所在分工会共同决定;userIds 仅作为人员选择器筛选条件。
|
||||
*/
|
||||
@At
|
||||
@SaCheckPermission("tour.branchUserAssignment")
|
||||
public Result candidatePageData(PageForm pageForm, String settingId, String keyword) {
|
||||
return Result.success(tourUserAssignmentService.branchCandidatePage(pageForm, settingId, keyword));
|
||||
public Result candidatePageData(PageForm pageForm, String settingId, String keyword, @Param("userIds") String userIds) {
|
||||
return Result.success(tourUserAssignmentService.branchCandidatePage(pageForm, settingId, keyword, parseUserIds(userIds)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,7 +71,7 @@ public class TourBranchUserAssignmentController {
|
||||
@At
|
||||
@SaCheckPermission("tour.branchUserAssignment")
|
||||
public Result matterOptions(String settingId) {
|
||||
return Result.success(tourUserAssignmentService.listMatterOptions(settingId));
|
||||
return Result.success(tourUserAssignmentService.listSignupOpenMatterOptions(settingId));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+2
-2
@@ -132,7 +132,7 @@ public class TourLedgerController {
|
||||
dr.id AS directRelativeId,
|
||||
COALESCE(NULLIF(l.lineName, ''), t.lineName) AS currentLineName,
|
||||
COALESCE(NULLIF(vu.sex, ''), t.gender, '') AS currentGender,
|
||||
vu.age AS age,
|
||||
COALESCE(t.age, vu.age) AS age,
|
||||
COALESCE(NULLIF(t.idCard, ''), NULLIF(vu.idCard, ''), '') AS currentIdCard,
|
||||
COALESCE(NULLIF(t.mobile, ''), NULLIF(vu.mobile, ''), '') AS currentMobile,
|
||||
IFNULL(f.familyCount, 0) AS familyCount,
|
||||
@@ -393,7 +393,7 @@ public class TourLedgerController {
|
||||
t.jobNo,
|
||||
t.userName,
|
||||
COALESCE(NULLIF(vu.sex, ''), t.gender, '') AS gender,
|
||||
vu.age AS age,
|
||||
COALESCE(t.age, vu.age) AS age,
|
||||
COALESCE(NULLIF(t.idCard, ''), NULLIF(vu.idCard, ''), '') AS idCard,
|
||||
COALESCE(NULLIF(t.mobile, ''), NULLIF(vu.mobile, ''), '') AS mobile,
|
||||
t.unitName,
|
||||
|
||||
+3
-4
@@ -37,8 +37,6 @@ import java.util.stream.Collectors;
|
||||
@At("/platform/tour/matter")
|
||||
public class TourMatterController {
|
||||
|
||||
private static final String MOBILE_PATTERN = "^1[3-9]\\d{9}$";
|
||||
|
||||
@Inject
|
||||
private TourMatterService tourMatterService;
|
||||
|
||||
@@ -353,8 +351,9 @@ public class TourMatterController {
|
||||
if (StrUtil.isBlank(matter.getContactName())) {
|
||||
return Result.error("联系人不能为空");
|
||||
}
|
||||
if (StrUtil.isBlank(matter.getContactPhone()) || !matter.getContactPhone().matches(MOBILE_PATTERN)) {
|
||||
return Result.error("联系方式格式不正确");
|
||||
// 线路联系人电话允许填写座机、分机或其他联系说明,后端只保留必填校验。
|
||||
if (StrUtil.isBlank(matter.getContactPhone())) {
|
||||
return Result.error("联系方式不能为空");
|
||||
}
|
||||
if (matter.getMinGroupPeople() == null || matter.getMinGroupPeople() <= 0) {
|
||||
return Result.error("最少成团人数必须大于0");
|
||||
|
||||
+44
-7
@@ -154,6 +154,7 @@ public class TourMySignupController {
|
||||
IFNULL(f.familyCount, 0) AS familyCount,
|
||||
a.id AS assignmentId,
|
||||
IFNULL(a.cancelled, 0) AS assignmentCancelled,
|
||||
m.signupEndTime AS actionSignupEndTime,
|
||||
m.travelStartTime AS actionTravelStartTime,
|
||||
COALESCE(m.travelEndTime, tp.travelEndTime) AS travelEndTime,
|
||||
CASE
|
||||
@@ -309,6 +310,7 @@ public class TourMySignupController {
|
||||
m.matterName,
|
||||
m.unionId AS matterUnionId,
|
||||
m.defaultBoardingPlace,
|
||||
m.signupEndTime,
|
||||
m.travelStartTime,
|
||||
m.travelEndTime,
|
||||
CASE
|
||||
@@ -345,6 +347,8 @@ public class TourMySignupController {
|
||||
if (isTravelEnded(matter.getString("travelEndTime"))) {
|
||||
return Result.error("线路出行已结束,不能修改");
|
||||
}
|
||||
// 修改弹窗内的取消按钮也由后端返回的报名截止状态控制,避免报名结束后仍可操作。
|
||||
matter.put("canCancelSignup", isWithinSignupCancelTime(matter.getString("signupEndTime", ""), LocalDateTime.now()));
|
||||
Cnd familyCnd = Cnd.where(TourLedgerFamily::getDelFlag, "=", false)
|
||||
.and(TourLedgerFamily::getLedgerId, "=", ledger.getId());
|
||||
familyCnd.asc(TourLedgerFamily::getCreatedAt);
|
||||
@@ -549,6 +553,10 @@ public class TourMySignupController {
|
||||
if (ledger == null) {
|
||||
return Result.error("报名记录不存在");
|
||||
}
|
||||
TourMatter matter = StrUtil.isBlank(ledger.getMatterId()) ? null : tourMatterService.fetch(ledger.getMatterId());
|
||||
if (!isWithinSignupCancelTime(matter)) {
|
||||
return Result.error("报名已结束,不能取消报名");
|
||||
}
|
||||
tourLedgerFamilyService.clear(Cnd.where(TourLedgerFamily::getLedgerId, "=", ledger.getId()));
|
||||
tourLedgerDirectRelativeService.clear(Cnd.where(TourLedgerDirectRelative::getLedgerId, "=", ledger.getId()));
|
||||
tourLedgerService.clear(Cnd.where(TourLedger::getId, "=", ledger.getId()));
|
||||
@@ -676,22 +684,51 @@ public class TourMySignupController {
|
||||
item.put("cancelDeadlineDays", cancelDeadlineDays);
|
||||
item.put("canLeaveTour", StrUtil.isNotBlank(assignmentId)
|
||||
&& !assignmentCancelled
|
||||
&& isWithinLeaveTimeRange(item.getString("actionTravelStartTime", ""), cancelDeadlineDays, now));
|
||||
&& isWithinLeaveTimeRange(item.getString("actionSignupEndTime", ""), item.getString("actionTravelStartTime", ""), cancelDeadlineDays, now));
|
||||
item.put("canCancelSignup", isWithinSignupCancelTime(item.getString("actionSignupEndTime", ""), now));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前时间是否处于“出行开始前N天”到“出行开始前”的可退出范围内。
|
||||
* 判断当前报名是否还处于允许取消的报名截止时间内,报名结束后不再显示或执行取消报名。
|
||||
*/
|
||||
private boolean isWithinLeaveTimeRange(String travelStartTime, int cancelDeadlineDays, LocalDateTime now) {
|
||||
private boolean isWithinSignupCancelTime(TourMatter matter) {
|
||||
if (matter == null) {
|
||||
return false;
|
||||
}
|
||||
return isWithinSignupCancelTime(matter.getSignupEndTime(), LocalDateTime.now());
|
||||
}
|
||||
|
||||
/**
|
||||
* 报名结束时间为空时不允许取消;当前时间小于等于报名结束时间才允许取消。
|
||||
*/
|
||||
private boolean isWithinSignupCancelTime(String signupEndTime, LocalDateTime now) {
|
||||
try {
|
||||
String normalized = normalizeDateTime(travelStartTime, false);
|
||||
if (StrUtil.isBlank(normalized)) {
|
||||
String normalizedSignupEnd = normalizeDateTime(signupEndTime, true);
|
||||
if (StrUtil.isBlank(normalizedSignupEnd)) {
|
||||
return false;
|
||||
}
|
||||
LocalDateTime startTime = LocalDateTime.parse(normalized, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
||||
LocalDateTime endTime = LocalDateTime.parse(normalizedSignupEnd, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
||||
return !now.isAfter(endTime);
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前时间是否处于“报名截止时间 < 当前时间 < 出行开始前N天”的可退出范围内。
|
||||
*/
|
||||
private boolean isWithinLeaveTimeRange(String signupEndTime, String travelStartTime, int cancelDeadlineDays, LocalDateTime now) {
|
||||
try {
|
||||
String normalizedSignupEnd = normalizeDateTime(signupEndTime, true);
|
||||
String normalizedTravelStart = normalizeDateTime(travelStartTime, false);
|
||||
if (StrUtil.isBlank(normalizedSignupEnd) || StrUtil.isBlank(normalizedTravelStart)) {
|
||||
return false;
|
||||
}
|
||||
LocalDateTime signupEnd = LocalDateTime.parse(normalizedSignupEnd, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
||||
LocalDateTime startTime = LocalDateTime.parse(normalizedTravelStart, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
||||
LocalDateTime deadline = startTime.minusDays(cancelDeadlineDays);
|
||||
return !now.isBefore(deadline) && now.isBefore(startTime);
|
||||
return now.isAfter(signupEnd) && now.isBefore(deadline);
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
+3
-3
@@ -50,12 +50,12 @@ public class TourSchoolUserAssignmentController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询候选人员,候选范围由疗休养配置的可参加人员范围决定。
|
||||
* 分页查询候选人员,候选范围由疗休养配置的可参加人员范围决定;userIds 仅作为人员选择器筛选条件。
|
||||
*/
|
||||
@At
|
||||
@SaCheckPermission("tour.schoolUserAssignment")
|
||||
public Result candidatePageData(PageForm pageForm, String settingId, String unionId, String keyword) {
|
||||
return Result.success(tourUserAssignmentService.schoolCandidatePage(pageForm, settingId, unionId, keyword));
|
||||
public Result candidatePageData(PageForm pageForm, String settingId, String unionId, String keyword, @Param("userIds") String userIds) {
|
||||
return Result.success(tourUserAssignmentService.schoolCandidatePage(pageForm, settingId, unionId, keyword, parseUserIds(userIds)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+17
@@ -155,6 +155,14 @@ public class TourSettingController {
|
||||
if (tourSetting.getFillBedInfo() == null) {
|
||||
tourSetting.setFillBedInfo(true);
|
||||
}
|
||||
if (tourSetting.getHomeSignupEntryEnabled() == null) {
|
||||
tourSetting.setHomeSignupEntryEnabled(false);
|
||||
}
|
||||
if (!Boolean.TRUE.equals(tourSetting.getHomeSignupEntryEnabled())) {
|
||||
tourSetting.setHomeSignupEntryImage("");
|
||||
} else {
|
||||
tourSetting.setHomeSignupEntryImage(normalizeSingleHomeSignupEntryImage(tourSetting.getHomeSignupEntryImage()));
|
||||
}
|
||||
// 报名资格校验方式默认按人员分配表校验,后续报名校验切换会读取该配置。
|
||||
if (StrUtil.isBlank(tourSetting.getSignupEligibilityMode())) {
|
||||
tourSetting.setSignupEligibilityMode(SIGNUP_ELIGIBILITY_MODE_ASSIGNED_USER);
|
||||
@@ -226,6 +234,15 @@ public class TourSettingController {
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private String normalizeSingleHomeSignupEntryImage(String image) {
|
||||
// 首页报名入口图片仅允许保存一张,兼容逗号分隔的历史多图路径时只保留第一张。
|
||||
if (StrUtil.isBlank(image)) {
|
||||
return "";
|
||||
}
|
||||
List<String> images = StrUtil.splitTrim(image, ",");
|
||||
return Lang.isEmpty(images) ? "" : images.get(0);
|
||||
}
|
||||
|
||||
private void saveLots(TourSetting tourSetting) {
|
||||
List<TourSettingLot> lots = tourSetting.getLots();
|
||||
if (Lang.isEmpty(lots)) {
|
||||
|
||||
+103
-5
@@ -93,11 +93,76 @@ public class TourSignupController {
|
||||
}
|
||||
|
||||
@At("/h5")
|
||||
@Ok("beetl:/platform/zhghh5/dayofficework/tour/signup/index.html")
|
||||
@Ok("beetl:/platform/zhghh5/dayofficework/tour/signup/entry.html")
|
||||
@SaCheckPermission(value = {"tour.signup", "h5.tour.signup"}, mode = SaMode.OR)
|
||||
public void h5() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 移动端疗休养报名入口的信息确认页。
|
||||
* 用户先确认基础信息并保存到人员分配表,再进入线路选择页面。
|
||||
*/
|
||||
@At("/h5/confirm")
|
||||
@Ok("beetl:/platform/zhghh5/dayofficework/tour/signup/confirm.html")
|
||||
@SaCheckPermission(value = {"tour.signup", "h5.tour.signup"}, mode = SaMode.OR)
|
||||
public void h5Confirm() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 移动端疗休养报名范围外提示页。
|
||||
* 当前登录人不存在人员分配记录时,从报名入口跳转到此页并引导返回首页。
|
||||
*/
|
||||
@At("/h5/noAssignment")
|
||||
@Ok("beetl:/platform/zhghh5/dayofficework/tour/signup/noAssignment.html")
|
||||
@SaCheckPermission(value = {"tour.signup", "h5.tour.signup"}, mode = SaMode.OR)
|
||||
public void h5NoAssignment() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 移动端报名成功后展示服务须知页。
|
||||
* 用户点击“我已阅读”后返回线路选择页面。
|
||||
*/
|
||||
@At("/h5/notice")
|
||||
@Ok("beetl:/platform/zhghh5/dayofficework/tour/signup/index.html")
|
||||
@SaCheckPermission(value = {"tour.signup", "h5.tour.signup"}, mode = SaMode.OR)
|
||||
public void h5Notice() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询移动端信息确认页数据。
|
||||
* 数据优先来自人员分配表;人员分配表不存在时返回 vw_user 基础信息供展示。
|
||||
*
|
||||
* @return 信息确认表单数据和乘车地点选项
|
||||
*/
|
||||
@At("/h5/confirmInfo")
|
||||
@SaCheckPermission(value = {"tour.signup", "h5.tour.signup"}, mode = SaMode.OR)
|
||||
public Result h5ConfirmInfo() {
|
||||
return Result.success(tourUserAssignmentService.currentH5ConfirmInfo());
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存移动端信息确认页数据。
|
||||
* 保存范围限制为当前年度既有人员分配记录,避免新增分配资格影响报名校验。
|
||||
*
|
||||
* @param userName 姓名
|
||||
* @param gender 性别
|
||||
* @param age 年龄
|
||||
* @param idCard 身份证号
|
||||
* @param mobile 手机号
|
||||
* @param boardingPlace 乘车地点
|
||||
* @return 保存结果
|
||||
*/
|
||||
@At("/h5/saveConfirmInfo")
|
||||
@SaCheckPermission(value = {"tour.signup", "h5.tour.signup"}, mode = SaMode.OR)
|
||||
public Result h5SaveConfirmInfo(String userName, String gender, Integer age, String idCard, String mobile, String boardingPlace) {
|
||||
try {
|
||||
tourUserAssignmentService.saveCurrentH5ConfirmInfo(userName, gender, age, idCard, mobile, boardingPlace);
|
||||
return Result.success();
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@At("/h5/serviceNotice")
|
||||
@SaCheckPermission(value = {"tour.signup", "h5.tour.signup"}, mode = SaMode.OR)
|
||||
public Result h5ServiceNotice() {
|
||||
@@ -333,6 +398,7 @@ public class TourSignupController {
|
||||
SELECT
|
||||
m.id AS matterId,
|
||||
m.`year`,
|
||||
m.settingId,
|
||||
m.matterName,
|
||||
m.minGroupPeople,
|
||||
m.maxGroupPeople,
|
||||
@@ -502,12 +568,14 @@ public class TourSignupController {
|
||||
SELECT
|
||||
m.id AS matterId,
|
||||
m.`year`,
|
||||
m.settingId,
|
||||
m.matterName,
|
||||
m.unionId AS matterUnionId,
|
||||
m.defaultBoardingPlace,
|
||||
m.travelStartTime,
|
||||
m.travelEndTime,
|
||||
m.contactName,
|
||||
m.contactPhone,
|
||||
CASE
|
||||
WHEN IFNULL(m.travelStartTime, '') <> '' AND IFNULL(m.travelEndTime, '') <> ''
|
||||
THEN CONCAT(m.travelStartTime, ' 至 ', m.travelEndTime)
|
||||
@@ -569,6 +637,7 @@ public class TourSignupController {
|
||||
.addv("unitName", user == null ? "" : defaultIfBlank(user.getUnitName(), ""))
|
||||
.addv("unionId", user == null ? SecurityUtil.getUnionId() : defaultIfBlank(user.getUnionId(), SecurityUtil.getUnionId()))
|
||||
.addv("unionName", user == null ? "" : defaultIfBlank(user.getUnionName(), ""));
|
||||
NutMap assignment = tourUserAssignmentService.currentUserAssignmentInfo(matter.getString("settingId", ""));
|
||||
Cnd ledgerCnd = Cnd.where(TourLedger::getDelFlag, "=", false)
|
||||
.and(TourLedger::getMatterId, "=", matter.getString("matterId"))
|
||||
.and(TourLedger::getJobNo, "=", staff.getString("jobNo"));
|
||||
@@ -589,6 +658,7 @@ public class TourSignupController {
|
||||
return Result.success(NutMap.NEW()
|
||||
.addv("matter", matter)
|
||||
.addv("staff", staff)
|
||||
.addv("assignment", assignment)
|
||||
.addv("ledger", ledger)
|
||||
.addv("process", getSignupProcessInfo(ledger))
|
||||
.addv("families", families)
|
||||
@@ -803,6 +873,7 @@ public class TourSignupController {
|
||||
TourLine line = tourLedgerService.dao().fetch(TourLine.class, matter.getLineId());
|
||||
boolean directFamilyLine = line != null && Boolean.TRUE.equals(line.getDirectFamilyUnitLine());
|
||||
TourSetting setting = tourLedgerService.dao().fetch(TourSetting.class, matter.getSettingId());
|
||||
fillLedgerFromAssignmentConfirmInfo(ledger, matter.getSettingId());
|
||||
Result boardingPlaceResult = normalizeBoardingPlace(ledger, matter, setting);
|
||||
if (boardingPlaceResult != null) {
|
||||
return boardingPlaceResult;
|
||||
@@ -838,6 +909,7 @@ public class TourSignupController {
|
||||
}
|
||||
ledger.setOverCostReimbursed(Boolean.TRUE.equals(ledger.getOverCostReimbursed()));
|
||||
fillStaffInfo(ledger);
|
||||
fillLedgerFromAssignmentConfirmInfo(ledger, matter.getSettingId());
|
||||
|
||||
if (update) {
|
||||
tourLedgerService.updateIgnoreNull(ledger);
|
||||
@@ -1288,19 +1360,21 @@ public class TourSignupController {
|
||||
// Button visibility is calculated on the server so H5 follows the same time rules as backend submit actions.
|
||||
item.put("cancelDeadlineDays", cancelDeadlineDays);
|
||||
item.put("canLeaveTour", signed && StrUtil.isNotBlank(assignmentId) && !assignmentCancelled
|
||||
&& isWithinLeaveTimeRange(item.getString("travelStartTime", ""), cancelDeadlineDays, now));
|
||||
&& isWithinLeaveTimeRange(item.getString("signupEndTime", ""), item.getString("travelStartTime", ""), cancelDeadlineDays, now));
|
||||
item.put("canCancelLine", signed && StrUtil.isNotBlank(ledgerId)
|
||||
&& isWithinSignupRange(item.getString("signupStartTime", ""), item.getString("signupEndTime", ""), now));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isWithinLeaveTimeRange(String travelStartTime, int cancelDeadlineDays, LocalDateTime now) {
|
||||
private boolean isWithinLeaveTimeRange(String signupEndTime, String travelStartTime, int cancelDeadlineDays, LocalDateTime now) {
|
||||
LocalDateTime signupEnd = parseDateTimeValue(signupEndTime, true);
|
||||
LocalDateTime startTime = parseDateTimeValue(travelStartTime, false);
|
||||
if (startTime == null) {
|
||||
if (signupEnd == null || startTime == null) {
|
||||
return false;
|
||||
}
|
||||
LocalDateTime deadline = startTime.minusDays(cancelDeadlineDays);
|
||||
return !now.isBefore(deadline) && now.isBefore(startTime);
|
||||
// 退出疗休养按钮严格限定在“报名截止时间 < 当前时间 < 出行开始前N天”范围内。
|
||||
return now.isAfter(signupEnd) && now.isBefore(deadline);
|
||||
}
|
||||
|
||||
private boolean isWithinSignupRange(String signupStartTime, String signupEndTime, LocalDateTime now) {
|
||||
@@ -1638,6 +1712,30 @@ public class TourSignupController {
|
||||
ledger.setUnionName(user == null ? ledger.getUnionName() : defaultIfBlank(user.getUnionName(), ledger.getUnionName()));
|
||||
}
|
||||
|
||||
private void fillLedgerFromAssignmentConfirmInfo(TourLedger ledger, String settingId) {
|
||||
if (ledger == null || StrUtil.isBlank(settingId)) {
|
||||
return;
|
||||
}
|
||||
NutMap assignment = tourUserAssignmentService.currentUserAssignmentInfo(settingId);
|
||||
if (assignment == null || assignment.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
// 移动端信息确认页保存到人员分配表,最终报名时以该确认信息回写台账。
|
||||
ledger.setUserName(defaultIfBlank(assignment.getString("userName", ""), ledger.getUserName()));
|
||||
ledger.setGender(defaultIfBlank(assignment.getString("gender", ""), ledger.getGender()));
|
||||
Integer assignmentAge = assignment.getInt("age");
|
||||
if (assignmentAge != null) {
|
||||
ledger.setAge(assignmentAge);
|
||||
}
|
||||
ledger.setIdCard(defaultIfBlank(assignment.getString("idCard", ""), ledger.getIdCard()));
|
||||
ledger.setMobile(defaultIfBlank(assignment.getString("mobile", ""), ledger.getMobile()));
|
||||
ledger.setBoardingPlace(defaultIfBlank(assignment.getString("boardingPlace", ""), ledger.getBoardingPlace()));
|
||||
ledger.setUnitId(defaultIfBlank(assignment.getString("unitId", ""), ledger.getUnitId()));
|
||||
ledger.setUnitName(defaultIfBlank(assignment.getString("unitName", ""), ledger.getUnitName()));
|
||||
ledger.setUnionId(defaultIfBlank(assignment.getString("unionId", ""), ledger.getUnionId()));
|
||||
ledger.setUnionName(defaultIfBlank(assignment.getString("unionName", ""), ledger.getUnionName()));
|
||||
}
|
||||
|
||||
private void fillLedgerContactFallback(TourLedger ledger, NutMap staff) {
|
||||
if (ledger == null || staff == null) {
|
||||
return;
|
||||
|
||||
@@ -46,6 +46,11 @@ public class TourLedger extends BaseModel implements Serializable {
|
||||
@ColDefine(type = ColType.VARCHAR, width = 20)
|
||||
private String gender;
|
||||
|
||||
@Column
|
||||
@Comment("年龄")
|
||||
@ColDefine(type = ColType.INT, width = 3)
|
||||
private Integer age;
|
||||
|
||||
@Column
|
||||
@Comment("身份证号")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 30)
|
||||
|
||||
@@ -138,6 +138,17 @@ public class TourSetting extends BaseModel implements Serializable {
|
||||
@Default("1")
|
||||
private Boolean enabled;
|
||||
|
||||
@Column
|
||||
@Comment("是否展示PC首页报名入口")
|
||||
@ColDefine(type = ColType.BOOLEAN)
|
||||
@Default("0")
|
||||
private Boolean homeSignupEntryEnabled;
|
||||
|
||||
@Column
|
||||
@Comment("PC首页报名入口图片")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 500)
|
||||
private String homeSignupEntryImage;
|
||||
|
||||
/**
|
||||
* 标段管理沿用老疗休养配置的子表设计,后续线路、旅行社等模块可通过标段ID继续关联。
|
||||
*/
|
||||
|
||||
@@ -97,6 +97,16 @@ public class TourUserAssignment extends BaseModel implements Serializable {
|
||||
@ColDefine(type = ColType.VARCHAR, width = 20)
|
||||
private String gender;
|
||||
|
||||
@Column
|
||||
@Comment("年龄")
|
||||
@ColDefine(type = ColType.INT, width = 3)
|
||||
private Integer age;
|
||||
|
||||
@Column
|
||||
@Comment("身份证号")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 30)
|
||||
private String idCard;
|
||||
|
||||
@Column
|
||||
@Comment("手机号")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 30)
|
||||
|
||||
+42
-2
@@ -33,9 +33,10 @@ public interface TourUserAssignmentService extends BaseService<TourUserAssignmen
|
||||
* @param settingId 疗休养配置ID
|
||||
* @param unionId 所属分工会ID
|
||||
* @param keyword 姓名或工号关键字
|
||||
* @param userIds 指定候选人员ID列表,人员选择器多选查询时使用
|
||||
* @return 可分配候选人分页数据
|
||||
*/
|
||||
Pagination<NutMap> schoolCandidatePage(PageForm pageForm, String settingId, String unionId, String keyword);
|
||||
Pagination<NutMap> schoolCandidatePage(PageForm pageForm, String settingId, String unionId, String keyword, List<String> userIds);
|
||||
|
||||
/**
|
||||
* 分页查询当前登录人所在分工会的人员分配记录,列表只读取人员分配表,不读取报名台账。
|
||||
@@ -57,9 +58,10 @@ public interface TourUserAssignmentService extends BaseService<TourUserAssignmen
|
||||
* @param pageForm 分页、排序参数
|
||||
* @param settingId 疗休养配置ID
|
||||
* @param keyword 姓名或工号关键字
|
||||
* @param userIds 指定候选人员ID列表,人员选择器多选查询时使用
|
||||
* @return 当前分工会可分配候选人分页数据
|
||||
*/
|
||||
Pagination<NutMap> branchCandidatePage(PageForm pageForm, String settingId, String keyword);
|
||||
Pagination<NutMap> branchCandidatePage(PageForm pageForm, String settingId, String keyword, List<String> userIds);
|
||||
|
||||
/**
|
||||
* 查询启用的疗休养配置选项,供人员分配列表筛选和分配弹窗复用。
|
||||
@@ -77,6 +79,14 @@ public interface TourUserAssignmentService extends BaseService<TourUserAssignmen
|
||||
*/
|
||||
List<NutMap> listMatterOptions(String settingId);
|
||||
|
||||
/**
|
||||
* 查询指定配置下当前处于报名时间内的分配线路选项,供分工会人员选择线路时使用。
|
||||
*
|
||||
* @param settingId 疗休养配置ID
|
||||
* @return 当前报名时间内的事项选项列表
|
||||
*/
|
||||
List<NutMap> listSignupOpenMatterOptions(String settingId);
|
||||
|
||||
/**
|
||||
* 查询分工会选项,供校工会分配候选人和列表筛选使用。
|
||||
*
|
||||
@@ -148,6 +158,36 @@ public interface TourUserAssignmentService extends BaseService<TourUserAssignmen
|
||||
*/
|
||||
NutMap switchCurrentBranchPersonType(String id, String personType);
|
||||
|
||||
/**
|
||||
* 查询移动端疗休养信息确认页所需的当前用户信息。
|
||||
* 优先读取当前年度人员分配表;无分配记录时读取 vw_user 基础信息用于页面展示。
|
||||
*
|
||||
* @return 当前登录人的信息确认数据和乘车地点选项
|
||||
*/
|
||||
NutMap currentH5ConfirmInfo();
|
||||
|
||||
/**
|
||||
* 保存移动端信息确认页填写的人员基础信息到当前年度人员分配表。
|
||||
* 仅更新既有人员分配记录,不新增分配名单,避免绕过疗休养报名资格控制。
|
||||
*
|
||||
* @param userName 姓名
|
||||
* @param gender 性别
|
||||
* @param age 年龄
|
||||
* @param idCard 身份证号
|
||||
* @param mobile 手机号
|
||||
* @param boardingPlace 乘车地点
|
||||
*/
|
||||
void saveCurrentH5ConfirmInfo(String userName, String gender, Integer age, String idCard, String mobile, String boardingPlace);
|
||||
|
||||
/**
|
||||
* 查询指定配置下当前登录人的人员分配确认信息。
|
||||
* 报名详情和最终提交报名时使用该信息回填台账,保证移动端确认信息能落到报名台账。
|
||||
*
|
||||
* @param settingId 疗休养配置ID
|
||||
* @return 人员分配确认信息;不存在时返回空 Map
|
||||
*/
|
||||
NutMap currentUserAssignmentInfo(String settingId);
|
||||
|
||||
/**
|
||||
* 用户报名写入台账后,回填该用户在同一疗休养配置下已存在的人员分配记录。
|
||||
* 仅更新事项、线路、旅行社快照字段,不新增记录,不修改 assignSource 和人员类型。
|
||||
|
||||
+243
-13
@@ -82,13 +82,18 @@ public class TourUserAssignmentServiceImpl extends BaseServiceImpl<TourUserAssig
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pagination<NutMap> schoolCandidatePage(PageForm pageForm, String settingId, String unionId, String keyword) {
|
||||
public Pagination<NutMap> schoolCandidatePage(PageForm pageForm, String settingId, String unionId, String keyword, List<String> userIds) {
|
||||
TourSetting setting = fetchSettingForAssignment(settingId);
|
||||
if (setting == null || StrUtil.isBlank(setting.getActivityGroupId())) {
|
||||
return emptyPagination(pageForm);
|
||||
}
|
||||
Sql sql = buildCandidateSql(settingId);
|
||||
Cnd cnd = buildCandidateCnd(settingId, setting.getActivityGroupId(), unionId, keyword);
|
||||
List<String> normalizedUserIds = normalizeUserIds(userIds);
|
||||
// 人员选择器多选查询时,候选范围仍受活动组、分工会、已分配排除规则约束,再按指定人员ID精确过滤。
|
||||
if (!Lang.isEmpty(normalizedUserIds)) {
|
||||
cnd.and("u.id", "in", normalizedUserIds);
|
||||
}
|
||||
cnd.asc("u.unionname");
|
||||
cnd.asc("u.unitname");
|
||||
cnd.asc("u.loginname");
|
||||
@@ -108,12 +113,12 @@ public class TourUserAssignmentServiceImpl extends BaseServiceImpl<TourUserAssig
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pagination<NutMap> branchCandidatePage(PageForm pageForm, String settingId, String keyword) {
|
||||
public Pagination<NutMap> branchCandidatePage(PageForm pageForm, String settingId, String keyword, List<String> userIds) {
|
||||
String unionId = SecurityUtil.getUnionId();
|
||||
if (StrUtil.isBlank(unionId)) {
|
||||
return emptyPagination(pageForm);
|
||||
}
|
||||
return schoolCandidatePage(pageForm, settingId, unionId, keyword);
|
||||
return schoolCandidatePage(pageForm, settingId, unionId, keyword, userIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -134,6 +139,18 @@ public class TourUserAssignmentServiceImpl extends BaseServiceImpl<TourUserAssig
|
||||
|
||||
@Override
|
||||
public List<NutMap> listMatterOptions(String settingId) {
|
||||
return listMatterOptions(settingId, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NutMap> listSignupOpenMatterOptions(String settingId) {
|
||||
return listMatterOptions(settingId, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一查询人员分配可选线路;分工会选择线路时只返回当前报名期内的事项。
|
||||
*/
|
||||
private List<NutMap> listMatterOptions(String settingId, boolean onlySignupOpen) {
|
||||
if (StrUtil.isBlank(settingId)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
@@ -143,6 +160,8 @@ public class TourUserAssignmentServiceImpl extends BaseServiceImpl<TourUserAssig
|
||||
m.matterName,
|
||||
m.`year` AS `year`,
|
||||
m.settingId,
|
||||
m.signupStartTime,
|
||||
m.signupEndTime,
|
||||
m.lineId,
|
||||
l.lineName,
|
||||
l.lineType,
|
||||
@@ -161,6 +180,11 @@ public class TourUserAssignmentServiceImpl extends BaseServiceImpl<TourUserAssig
|
||||
cnd.and("l.id", "is not", null);
|
||||
cnd.and("l.delFlag", "=", false);
|
||||
cnd.and("l.enabled", "=", true);
|
||||
if (onlySignupOpen) {
|
||||
String now = DateUtil.now();
|
||||
cnd.and("m.signupStartTime", "<=", now);
|
||||
cnd.and("m.signupEndTime", ">=", now);
|
||||
}
|
||||
sql.setCondition(cnd);
|
||||
return listMap(sql);
|
||||
}
|
||||
@@ -336,6 +360,9 @@ public class TourUserAssignmentServiceImpl extends BaseServiceImpl<TourUserAssig
|
||||
if (matterInfo == null) {
|
||||
throw new IllegalArgumentException("分配路线不存在或未配置线路");
|
||||
}
|
||||
if (!isMatterSignupOpen(matterInfo)) {
|
||||
throw new IllegalArgumentException("当前线路不在报名时间内,不能选择");
|
||||
}
|
||||
List<NutMap> users = Collections.singletonList(fetchAssignmentUserSnapshot(assignment));
|
||||
checkProxySignupMaxGroupPeople(matterInfo, users);
|
||||
updateAssignmentMatterSnapshot(id, matterInfo, TourUserAssignment.ASSIGN_SOURCE_BRANCH_UNION);
|
||||
@@ -487,6 +514,103 @@ public class TourUserAssignmentServiceImpl extends BaseServiceImpl<TourUserAssig
|
||||
return branchTypeSwitchResult(targetPersonType, branchQuotaInfo(assignment.getSettingId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public NutMap currentH5ConfirmInfo() {
|
||||
TourSetting setting = fetchCurrentYearEnabledSetting();
|
||||
NutMap userInfo = fetchCurrentUserViewInfo();
|
||||
NutMap assignmentInfo = setting == null ? NutMap.NEW() : currentUserAssignmentInfo(setting.getId());
|
||||
boolean existsAssignment = assignmentInfo != null && StrUtil.isNotBlank(assignmentInfo.getString("assignmentId", ""));
|
||||
NutMap source = existsAssignment ? assignmentInfo : userInfo;
|
||||
return NutMap.NEW()
|
||||
.addv("settingId", setting == null ? "" : setting.getId())
|
||||
.addv("year", setting == null ? LocalDate.now().getYear() : setting.getYear())
|
||||
.addv("existsAssignment", existsAssignment)
|
||||
.addv("boardingPlaceOptions", setting == null ? "[]" : StrUtil.blankToDefault(setting.getBoardingPlace(), "[]"))
|
||||
.addv("assignment", assignmentInfo == null ? NutMap.NEW() : assignmentInfo)
|
||||
.addv("user", userInfo)
|
||||
.addv("form", NutMap.NEW()
|
||||
.addv("userName", source.getString("userName", ""))
|
||||
.addv("gender", source.getString("gender", ""))
|
||||
.addv("age", source.get("age"))
|
||||
.addv("idCard", source.getString("idCard", ""))
|
||||
.addv("mobile", source.getString("mobile", ""))
|
||||
.addv("boardingPlace", source.getString("boardingPlace", "")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveCurrentH5ConfirmInfo(String userName, String gender, Integer age, String idCard, String mobile, String boardingPlace) {
|
||||
TourSetting setting = fetchCurrentYearEnabledSetting();
|
||||
if (setting == null) {
|
||||
throw new IllegalArgumentException("当前年度暂无启用的疗休养配置");
|
||||
}
|
||||
if (StrUtil.isBlank(userName) || StrUtil.isBlank(gender)
|
||||
|| StrUtil.isBlank(idCard) || StrUtil.isBlank(mobile) || StrUtil.isBlank(boardingPlace)) {
|
||||
throw new IllegalArgumentException("请完善姓名、性别、身份证号、手机号和乘车地点");
|
||||
}
|
||||
String normalizedIdCard = idCard.trim().toUpperCase();
|
||||
if (!isValidIdCard(normalizedIdCard)) {
|
||||
throw new IllegalArgumentException("请输入正确的身份证号");
|
||||
}
|
||||
Integer calculatedAge = calculateAge(normalizedIdCard, "");
|
||||
if (calculatedAge == null) {
|
||||
throw new IllegalArgumentException("请填写正确的身份证号以计算年龄");
|
||||
}
|
||||
Cnd cnd = Cnd.where(TourUserAssignment::getSettingId, "=", setting.getId())
|
||||
.and(TourUserAssignment::getUserId, "=", SecurityUtil.getUserId())
|
||||
.and(TourUserAssignment::getDelFlag, "=", false);
|
||||
if (count(cnd) <= 0) {
|
||||
throw new IllegalArgumentException("未找到当前年度人员分配记录,暂不能确认报名信息");
|
||||
}
|
||||
// 移动端信息确认只更新既有分配记录的人员基础信息,不创建分配资格。
|
||||
update(Chain.make("userName", userName.trim())
|
||||
.add("gender", gender.trim())
|
||||
.add("age", calculatedAge)
|
||||
.add("idCard", normalizedIdCard)
|
||||
.add("mobile", mobile.trim())
|
||||
.add("boardingPlace", boardingPlace.trim()),
|
||||
cnd);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NutMap currentUserAssignmentInfo(String settingId) {
|
||||
if (StrUtil.isBlank(settingId)) {
|
||||
return NutMap.NEW();
|
||||
}
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
id AS assignmentId,
|
||||
settingId,
|
||||
matterId,
|
||||
userId,
|
||||
loginName AS jobNo,
|
||||
userName,
|
||||
gender,
|
||||
age,
|
||||
idCard,
|
||||
mobile,
|
||||
unitId,
|
||||
unitName,
|
||||
unionId,
|
||||
unionName,
|
||||
boardingPlace,
|
||||
personType,
|
||||
assignSource,
|
||||
IFNULL(cancelled, 0) AS cancelled
|
||||
FROM tour_user_assignment
|
||||
WHERE delFlag = 0
|
||||
AND settingId = @settingId
|
||||
AND userId = @userId
|
||||
ORDER BY CASE WHEN personType = 'FORMAL' THEN 0 ELSE 1 END ASC, createdAt DESC
|
||||
LIMIT 1
|
||||
""");
|
||||
sql.setParam("settingId", settingId);
|
||||
sql.setParam("userId", SecurityUtil.getUserId());
|
||||
sql.setCallback(Sqls.callback.map());
|
||||
dao().execute(sql);
|
||||
NutMap map = sql.getObject(NutMap.class);
|
||||
return map == null ? NutMap.NEW() : map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int backfillExistingAssignmentAfterSignup(String settingId, String matterId, String userId, String boardingPlace) {
|
||||
if (StrUtil.isBlank(settingId) || StrUtil.isBlank(matterId) || StrUtil.isBlank(userId)) {
|
||||
@@ -730,8 +854,10 @@ public class TourUserAssignmentServiceImpl extends BaseServiceImpl<TourUserAssig
|
||||
u.loginname AS loginName,
|
||||
u.username AS userName,
|
||||
u.sex AS gender,
|
||||
u.age AS age,
|
||||
u.idcard AS idCard,
|
||||
u.mobile AS mobile,
|
||||
u.birthday AS birthday,
|
||||
u.unitid AS unitId,
|
||||
u.unitname AS unitName,
|
||||
u.unionid AS unionId,
|
||||
@@ -884,13 +1010,14 @@ public class TourUserAssignmentServiceImpl extends BaseServiceImpl<TourUserAssig
|
||||
if (matter == null) {
|
||||
throw new IllegalArgumentException("分配路线不存在,无法取消退出");
|
||||
}
|
||||
LocalDateTime travelStartTime = parseTravelStartTime(matter.getTravelStartTime());
|
||||
LocalDateTime signupEndTime = parseMatterDateTime(matter.getSignupEndTime(), true, "分配路线未设置报名截止时间,无法取消退出", "分配路线报名截止时间格式不正确,无法取消退出");
|
||||
LocalDateTime travelStartTime = parseMatterDateTime(matter.getTravelStartTime(), false, "分配路线未设置出行开始时间,无法取消退出", "分配路线出行开始时间格式不正确,无法取消退出");
|
||||
int days = getCancelDeadlineDays();
|
||||
LocalDateTime deadline = travelStartTime.minusDays(days);
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
// 退出报名仅允许在“出行开始前N天”到“出行开始前”之间办理,出行开始时刻不能再退出。
|
||||
if (now.isBefore(deadline) || !now.isBefore(travelStartTime)) {
|
||||
throw new IllegalArgumentException("当前不在退出时间范围内,需在出行开始前" + days + "天至出行开始前办理");
|
||||
// 退出报名仅允许在“报名截止时间 < 当前时间 < 出行开始前N天”之间办理,两个边界时刻均不可办理。
|
||||
if (!now.isAfter(signupEndTime) || !now.isBefore(deadline)) {
|
||||
throw new IllegalArgumentException("当前不在退出时间范围内,需在报名截止后至出行开始前" + days + "天之前办理");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -906,18 +1033,22 @@ public class TourUserAssignmentServiceImpl extends BaseServiceImpl<TourUserAssig
|
||||
}
|
||||
}
|
||||
|
||||
private LocalDateTime parseTravelStartTime(String travelStartTime) {
|
||||
if (StrUtil.isBlank(travelStartTime)) {
|
||||
throw new IllegalArgumentException("分配路线未设置出行开始时间,无法取消退出");
|
||||
private LocalDateTime parseMatterDateTime(String value, boolean endOfDay, String blankMessage, String formatMessage) {
|
||||
if (StrUtil.isBlank(value)) {
|
||||
throw new IllegalArgumentException(blankMessage);
|
||||
}
|
||||
String normalizedTime = travelStartTime.trim();
|
||||
String normalizedTime = value.trim();
|
||||
try {
|
||||
if (normalizedTime.length() <= 10) {
|
||||
return LocalDate.parse(normalizedTime.substring(0, 10), DateTimeFormatter.ofPattern("yyyy-MM-dd")).atStartOfDay();
|
||||
LocalDate date = LocalDate.parse(normalizedTime.substring(0, 10), DateTimeFormatter.ofPattern("yyyy-MM-dd"));
|
||||
return endOfDay ? date.atTime(23, 59, 59) : date.atStartOfDay();
|
||||
}
|
||||
if (normalizedTime.length() == 16) {
|
||||
normalizedTime = normalizedTime + ":00";
|
||||
}
|
||||
return LocalDateTime.parse(normalizedTime.substring(0, 19), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
||||
} catch (DateTimeParseException e) {
|
||||
throw new IllegalArgumentException("分配路线出行开始时间格式不正确,无法取消退出");
|
||||
throw new IllegalArgumentException(formatMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -929,6 +1060,55 @@ public class TourUserAssignmentServiceImpl extends BaseServiceImpl<TourUserAssig
|
||||
.and(TourSetting::getDelFlag, "=", false));
|
||||
}
|
||||
|
||||
private TourSetting fetchCurrentYearEnabledSetting() {
|
||||
Cnd cnd = Cnd.where(TourSetting::getDelFlag, "=", false)
|
||||
.and(TourSetting::getEnabled, "=", true)
|
||||
.and(TourSetting::getYear, "=", LocalDate.now().getYear());
|
||||
cnd.asc(TourSetting::getSortNo);
|
||||
cnd.desc(TourSetting::getCreatedAt);
|
||||
return dao().fetch(TourSetting.class, cnd);
|
||||
}
|
||||
|
||||
private NutMap fetchCurrentUserViewInfo() {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
id AS userId,
|
||||
loginname AS jobNo,
|
||||
username AS userName,
|
||||
sex AS gender,
|
||||
idcard AS idCard,
|
||||
mobile,
|
||||
unitId,
|
||||
unitName,
|
||||
unionId,
|
||||
unionName,
|
||||
birthday
|
||||
FROM vw_user
|
||||
WHERE id = @userId
|
||||
LIMIT 1
|
||||
""");
|
||||
sql.setParam("userId", SecurityUtil.getUserId());
|
||||
sql.setCallback(Sqls.callback.map());
|
||||
dao().execute(sql);
|
||||
NutMap user = sql.getObject(NutMap.class);
|
||||
if (user == null) {
|
||||
user = NutMap.NEW()
|
||||
.addv("userId", SecurityUtil.getUserId())
|
||||
.addv("jobNo", SecurityUtil.getUserLoginname())
|
||||
.addv("userName", SecurityUtil.getUserUsername())
|
||||
.addv("gender", "")
|
||||
.addv("idCard", "")
|
||||
.addv("mobile", "")
|
||||
.addv("unitId", SecurityUtil.getUnitId())
|
||||
.addv("unitName", "")
|
||||
.addv("unionId", SecurityUtil.getUnionId())
|
||||
.addv("unionName", "");
|
||||
}
|
||||
user.put("age", calculateAge(user.getString("idCard", ""), user.getString("birthday", "")));
|
||||
user.put("boardingPlace", "");
|
||||
return user;
|
||||
}
|
||||
|
||||
private NutMap fetchMatterInfo(String settingId, String matterId) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
@@ -936,6 +1116,8 @@ public class TourUserAssignmentServiceImpl extends BaseServiceImpl<TourUserAssig
|
||||
m.matterName,
|
||||
m.`year` AS `year`,
|
||||
m.settingId,
|
||||
m.signupStartTime,
|
||||
m.signupEndTime,
|
||||
m.defaultBoardingPlace,
|
||||
m.maxGroupPeople,
|
||||
m.lineId,
|
||||
@@ -961,6 +1143,22 @@ public class TourUserAssignmentServiceImpl extends BaseServiceImpl<TourUserAssig
|
||||
return Lang.isEmpty(list) ? null : list.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断事项当前是否处于报名时间范围内,用于分工会选择线路的最终提交保护。
|
||||
*/
|
||||
private boolean isMatterSignupOpen(NutMap matterInfo) {
|
||||
if (matterInfo == null) {
|
||||
return false;
|
||||
}
|
||||
String signupStartTime = matterInfo.getString("signupStartTime", "");
|
||||
String signupEndTime = matterInfo.getString("signupEndTime", "");
|
||||
if (StrUtil.isBlank(signupStartTime) || StrUtil.isBlank(signupEndTime)) {
|
||||
return false;
|
||||
}
|
||||
String now = DateUtil.now();
|
||||
return signupStartTime.compareTo(now) <= 0 && signupEndTime.compareTo(now) >= 0;
|
||||
}
|
||||
|
||||
private TourUserAssignment buildSchoolAssignment(String settingId, NutMap matterInfo, String personType, NutMap user) {
|
||||
return buildAssignment(settingId, matterInfo, personType, user, TourUserAssignment.ASSIGN_SOURCE_SCHOOL_UNION);
|
||||
}
|
||||
@@ -973,6 +1171,9 @@ public class TourUserAssignmentServiceImpl extends BaseServiceImpl<TourUserAssig
|
||||
assignment.setLoginName(user.getString("loginName"));
|
||||
assignment.setUserName(user.getString("userName"));
|
||||
assignment.setGender(user.getString("gender"));
|
||||
Integer age = user.getInt("age");
|
||||
assignment.setAge(age == null ? calculateAge(user.getString("idCard"), user.getString("birthday")) : age);
|
||||
assignment.setIdCard(user.getString("idCard"));
|
||||
assignment.setMobile(user.getString("mobile"));
|
||||
assignment.setUnitId(user.getString("unitId"));
|
||||
assignment.setUnitName(user.getString("unitName"));
|
||||
@@ -1232,4 +1433,33 @@ public class TourUserAssignmentServiceImpl extends BaseServiceImpl<TourUserAssig
|
||||
private int defaultInt(Integer value) {
|
||||
return value == null || value < 0 ? 0 : value;
|
||||
}
|
||||
|
||||
private Integer calculateAge(String idCard, String birthday) {
|
||||
LocalDate birthDate = null;
|
||||
try {
|
||||
if (StrUtil.isNotBlank(idCard) && idCard.trim().length() >= 14) {
|
||||
String normalizedIdCard = idCard.trim();
|
||||
birthDate = LocalDate.parse(normalizedIdCard.substring(6, 14), DateTimeFormatter.ofPattern("yyyyMMdd"));
|
||||
} else if (StrUtil.isNotBlank(birthday) && birthday.length() >= 10) {
|
||||
birthDate = LocalDate.parse(birthday.substring(0, 10), DateTimeFormatter.ofPattern("yyyy-MM-dd"));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
birthDate = null;
|
||||
}
|
||||
if (birthDate == null) {
|
||||
return null;
|
||||
}
|
||||
LocalDate today = LocalDate.now();
|
||||
int age = today.getYear() - birthDate.getYear();
|
||||
if (today.getMonthValue() < birthDate.getMonthValue()
|
||||
|| (today.getMonthValue() == birthDate.getMonthValue() && today.getDayOfMonth() < birthDate.getDayOfMonth())) {
|
||||
age--;
|
||||
}
|
||||
return Math.max(age, 0);
|
||||
}
|
||||
|
||||
private boolean isValidIdCard(String idCard) {
|
||||
return StrUtil.isNotBlank(idCard)
|
||||
&& idCard.matches("^[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[\\dX]$");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user