commit
This commit is contained in:
+14
@@ -205,6 +205,20 @@ public class TheRapyRecuperationBaseManagerController {
|
|||||||
return dao.fetchLinks(management, "travelAgency");
|
return dao.fetchLinks(management, "travelAgency");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询酒店每日名额占用和剩余情况。
|
||||||
|
*
|
||||||
|
* @param id 酒店/目的地Id
|
||||||
|
* @return 每日名额列表
|
||||||
|
*/
|
||||||
|
@At("/listDailyQuota")
|
||||||
|
@ViReturn
|
||||||
|
@RequiresAuthentication
|
||||||
|
public Object listDailyQuota(@Param("id") String id) {
|
||||||
|
Assert.notBlank(id);
|
||||||
|
return theRapyRecuperationBaseManagerService.listDailyQuota(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新增/修改
|
* 新增/修改
|
||||||
|
|||||||
+10
@@ -2,6 +2,8 @@ package io.v.nutz.zhgh.therapyRecuperation.service.baseManage;
|
|||||||
|
|
||||||
import org.nutz.lang.util.NutMap;
|
import org.nutz.lang.util.NutMap;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author JyuHsin
|
* @Author JyuHsin
|
||||||
* @Date 2023/6/7
|
* @Date 2023/6/7
|
||||||
@@ -11,6 +13,14 @@ public interface TheRapyRecuperationBaseManagerService {
|
|||||||
|
|
||||||
NutMap selectBaseAllInfo(String id);
|
NutMap selectBaseAllInfo(String id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询酒店每日名额占用和剩余情况。
|
||||||
|
*
|
||||||
|
* @param id 酒店/目的地Id
|
||||||
|
* @return 每日名额列表
|
||||||
|
*/
|
||||||
|
List<NutMap> listDailyQuota(String id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 复制上一年度目的地到目标年度,只复制目的地基础信息,不复制报名数据。
|
* 复制上一年度目的地到目标年度,只复制目的地基础信息,不复制报名数据。
|
||||||
*
|
*
|
||||||
|
|||||||
+158
@@ -4,9 +4,13 @@ import cn.hutool.core.date.DateUtil;
|
|||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import io.v.nutz.base.service.impl.BaseServiceImpl;
|
import io.v.nutz.base.service.impl.BaseServiceImpl;
|
||||||
import io.v.nutz.base.utils.Vi;
|
import io.v.nutz.base.utils.Vi;
|
||||||
|
import io.v.nutz.sys.models.SysHoliday;
|
||||||
import io.v.nutz.web.commons.utils.ShiroUtil;
|
import io.v.nutz.web.commons.utils.ShiroUtil;
|
||||||
|
import io.v.nutz.zhgh.therapyRecuperation.constant.TheRapyRecuperationState;
|
||||||
|
import io.v.nutz.zhgh.therapyRecuperation.model.TheRapyRecuperationBaseDailyQuota;
|
||||||
import io.v.nutz.zhgh.therapyRecuperation.model.TheRapyRecuperationBaseManagement;
|
import io.v.nutz.zhgh.therapyRecuperation.model.TheRapyRecuperationBaseManagement;
|
||||||
import io.v.nutz.zhgh.therapyRecuperation.model.TheRapyRecuperationConfig;
|
import io.v.nutz.zhgh.therapyRecuperation.model.TheRapyRecuperationConfig;
|
||||||
|
import io.v.nutz.zhgh.therapyRecuperation.model.TheRapyRecuperationEnroll;
|
||||||
import io.v.nutz.zhgh.therapyRecuperation.model.TheRapyRecuperationLot;
|
import io.v.nutz.zhgh.therapyRecuperation.model.TheRapyRecuperationLot;
|
||||||
import io.v.nutz.zhgh.therapyRecuperation.service.TheRapyRecuperationConfigService;
|
import io.v.nutz.zhgh.therapyRecuperation.service.TheRapyRecuperationConfigService;
|
||||||
import io.v.nutz.zhgh.therapyRecuperation.service.baseManage.TheRapyRecuperationBaseManagerService;
|
import io.v.nutz.zhgh.therapyRecuperation.service.baseManage.TheRapyRecuperationBaseManagerService;
|
||||||
@@ -18,11 +22,21 @@ import org.nutz.dao.sql.Sql;
|
|||||||
import org.nutz.ioc.aop.Aop;
|
import org.nutz.ioc.aop.Aop;
|
||||||
import org.nutz.ioc.loader.annotation.Inject;
|
import org.nutz.ioc.loader.annotation.Inject;
|
||||||
import org.nutz.ioc.loader.annotation.IocBean;
|
import org.nutz.ioc.loader.annotation.IocBean;
|
||||||
|
import org.nutz.lang.Lang;
|
||||||
import org.nutz.lang.util.NutMap;
|
import org.nutz.lang.util.NutMap;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author JyuHsin
|
* @Author JyuHsin
|
||||||
@@ -63,6 +77,150 @@ public class TheRapyRecuperationBaseManagerServiceImpl extends BaseServiceImpl i
|
|||||||
return nutMap;
|
return nutMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询酒店每日名额占用和剩余情况,优先使用每日名额占用表,未初始化的历史日期按报名表兜底统计。
|
||||||
|
*
|
||||||
|
* @param id 酒店/目的地Id
|
||||||
|
* @return 每日名额列表
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<NutMap> listDailyQuota(String id) {
|
||||||
|
TheRapyRecuperationBaseManagement management = dao().fetch(TheRapyRecuperationBaseManagement.class, id);
|
||||||
|
if (management == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
TheRapyRecuperationConfig config = configService.requireByYear(management.getYear());
|
||||||
|
if (config == null || config.getFragmentPlayStartTime() == null || config.getFragmentPlayEndTime() == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, TheRapyRecuperationBaseDailyQuota> quotaMap = queryDailyQuotaMap(id);
|
||||||
|
Map<String, Integer> enrollCountMap = countDailyQuotaByEnroll(management);
|
||||||
|
Set<String> holidays = queryHolidaySet(management.getYear());
|
||||||
|
List<Integer> weeks = management.getWeekCheckIn();
|
||||||
|
|
||||||
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||||
|
LocalDate startDate = LocalDate.parse(DateUtil.formatDate(config.getFragmentPlayStartTime()), formatter);
|
||||||
|
LocalDate endDate = LocalDate.parse(DateUtil.formatDate(config.getFragmentPlayEndTime()), formatter);
|
||||||
|
List<NutMap> rows = new ArrayList<>();
|
||||||
|
LocalDate currentDate = startDate;
|
||||||
|
while (!currentDate.isAfter(endDate)) {
|
||||||
|
String currentDateString = currentDate.format(formatter);
|
||||||
|
if (isDisabledQuotaDate(currentDate, currentDateString, holidays, weeks)) {
|
||||||
|
currentDate = currentDate.plusDays(1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
TheRapyRecuperationBaseDailyQuota quota = quotaMap.get(currentDateString);
|
||||||
|
int maxSignCount = quota != null && quota.getMaxSignCount() != null ? quota.getMaxSignCount() : Objects.requireNonNullElse(management.getMaxSignCount(), 0);
|
||||||
|
int usedSignCount = quota != null && quota.getUsedSignCount() != null ? quota.getUsedSignCount() : enrollCountMap.getOrDefault(currentDateString, 0);
|
||||||
|
rows.add(buildDailyQuotaRow(currentDateString, maxSignCount, usedSignCount));
|
||||||
|
currentDate = currentDate.plusDays(1);
|
||||||
|
}
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, TheRapyRecuperationBaseDailyQuota> queryDailyQuotaMap(String id) {
|
||||||
|
List<TheRapyRecuperationBaseDailyQuota> quotas = dao().query(TheRapyRecuperationBaseDailyQuota.class,
|
||||||
|
Cnd.where("baseManagementId", "=", id).asc("quotaDate"));
|
||||||
|
return quotas.stream().collect(Collectors.toMap(TheRapyRecuperationBaseDailyQuota::getQuotaDate, quota -> quota, (oldValue, newValue) -> newValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 报名表兜底统计只用于未生成每日占用表的日期,保证历史数据在查看页仍然可见。
|
||||||
|
*
|
||||||
|
* @param management 酒店/目的地
|
||||||
|
* @return key为日期,value为覆盖该日期的报名人数
|
||||||
|
*/
|
||||||
|
private Map<String, Integer> countDailyQuotaByEnroll(TheRapyRecuperationBaseManagement management) {
|
||||||
|
Cnd cnd = Cnd.where("takePartInBaseManagementId", "=", management.getId())
|
||||||
|
.and("specificTime", "is not", null)
|
||||||
|
.and("isNormal", "=", true)
|
||||||
|
.and("stateId", "in", Lang.array(TheRapyRecuperationState.UNIT, TheRapyRecuperationState.LINEUNIT, TheRapyRecuperationState.SCHOOL, TheRapyRecuperationState.PASS))
|
||||||
|
.and("YEAR(signingUptime)", "=", management.getYear());
|
||||||
|
List<TheRapyRecuperationEnroll> enrolls = dao().query(TheRapyRecuperationEnroll.class, cnd);
|
||||||
|
Map<String, Integer> countMap = new HashMap<>();
|
||||||
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||||
|
for (TheRapyRecuperationEnroll enroll : enrolls) {
|
||||||
|
for (LocalDate day : parseSpecificTimeDays(enroll.getSpecificTime(), management.getYear())) {
|
||||||
|
String dayString = day.format(formatter);
|
||||||
|
countMap.put(dayString, countMap.getOrDefault(dayString, 0) + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return countMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> queryHolidaySet(Integer year) {
|
||||||
|
List<SysHoliday> holidayList = dao().query(SysHoliday.class, Cnd.where("year(day)", "=", year).asc("day"));
|
||||||
|
Set<String> holidays = holidayList.stream().map(SysHoliday::getDay).collect(Collectors.toCollection(HashSet::new));
|
||||||
|
Map<Integer, List<SysHoliday>> listMap = holidayList.stream().collect(Collectors.groupingBy(o -> DateUtil.month(DateUtil.parse(o.getDay()))));
|
||||||
|
listMap.forEach((k, v) -> {
|
||||||
|
SysHoliday sysHoliday = v.get(0);
|
||||||
|
Date date = DateUtil.parse(sysHoliday.getDay());
|
||||||
|
holidays.add(DateUtil.formatDate(date));
|
||||||
|
});
|
||||||
|
return holidays;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isDisabledQuotaDate(LocalDate currentDate, String currentDateString, Set<String> holidays, List<Integer> weeks) {
|
||||||
|
if (holidays.contains(currentDateString)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
int dayOfWeek = currentDate.getDayOfWeek().getValue() % 7;
|
||||||
|
return Lang.isNotEmpty(weeks) && !weeks.contains(dayOfWeek);
|
||||||
|
}
|
||||||
|
|
||||||
|
private NutMap buildDailyQuotaRow(String quotaDate, int maxSignCount, int usedSignCount) {
|
||||||
|
int remainingSignCount = maxSignCount <= 0 ? 0 : Math.max(maxSignCount - usedSignCount, 0);
|
||||||
|
boolean full = maxSignCount > 0 && usedSignCount >= maxSignCount;
|
||||||
|
String statusText = maxSignCount <= 0 ? "不限制" : full ? "已满" : "未满";
|
||||||
|
return new NutMap()
|
||||||
|
.setv("quotaDate", quotaDate)
|
||||||
|
.setv("maxSignCount", maxSignCount)
|
||||||
|
.setv("usedSignCount", usedSignCount)
|
||||||
|
.setv("remainingSignCount", remainingSignCount)
|
||||||
|
.setv("remainingText", maxSignCount <= 0 ? "不限制" : String.valueOf(remainingSignCount))
|
||||||
|
.setv("statusText", statusText)
|
||||||
|
.setv("full", full);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 把“06月01日-06月03日”格式的报名时间拆成指定年度的逐日日期。
|
||||||
|
*
|
||||||
|
* @param specificTime 报名时间段
|
||||||
|
* @param year 年度
|
||||||
|
* @return 报名时间段包含的所有日期
|
||||||
|
*/
|
||||||
|
private List<LocalDate> parseSpecificTimeDays(String specificTime, Integer year) {
|
||||||
|
if (StrUtil.isBlank(specificTime) || !specificTime.contains("-")) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
String[] times = specificTime.split("-");
|
||||||
|
if (times.length != 2 || StrUtil.isBlank(times[0]) || StrUtil.isBlank(times[1])) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
|
||||||
|
LocalDate start;
|
||||||
|
LocalDate end;
|
||||||
|
try {
|
||||||
|
Integer targetYear = year == null ? DateUtil.thisYear() : year;
|
||||||
|
start = LocalDate.parse(targetYear + "年" + times[0].trim(), formatter);
|
||||||
|
end = LocalDate.parse(targetYear + "年" + times[1].trim(), formatter);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
if (end.isBefore(start)) {
|
||||||
|
end = end.plusYears(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<LocalDate> days = new ArrayList<>();
|
||||||
|
LocalDate current = start;
|
||||||
|
while (!current.isAfter(end)) {
|
||||||
|
days.add(current);
|
||||||
|
current = current.plusDays(1);
|
||||||
|
}
|
||||||
|
return days;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 复制上一年度目的地到目标年度,排序号从当前最大排序号后递增,避免复用历史目的地排序号。
|
* 复制上一年度目的地到目标年度,排序号从当前最大排序号后递增,避免复用历史目的地排序号。
|
||||||
* 复制时只生成目的地基础数据,不复制报名记录,防止目标年度业务挂到旧年度报名数据。
|
* 复制时只生成目的地基础数据,不复制报名记录,防止目标年度业务挂到旧年度报名数据。
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
|
<el-tabs v-model="activeTab" @tab-click="handleTabClick">
|
||||||
|
<el-tab-pane label="基地信息" name="baseInfo">
|
||||||
<div class="panel panel-default mt20" style="border: none">
|
<div class="panel panel-default mt20" style="border: none">
|
||||||
<div class="panel-heading" style="background: #fafafa;border:1px solid #ebeef5;border-bottom: none">
|
<div class="panel-heading" style="background: #fafafa;border:1px solid #ebeef5;border-bottom: none">
|
||||||
<h3 class="panel-title">
|
<h3 class="panel-title">
|
||||||
@@ -51,6 +53,22 @@
|
|||||||
<div v-html="viewData.content"></div>
|
<div v-html="viewData.content"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="房间剩余情况" name="dailyQuota">
|
||||||
|
<el-table :data="quotaRows" v-loading="quotaLoading" border class="mt20" size="small" style="width: 100%">
|
||||||
|
<el-table-column align="center" label="可报名日期" prop="quotaDate"></el-table-column>
|
||||||
|
<el-table-column align="center" label="每日最高人数" prop="maxSignCount"></el-table-column>
|
||||||
|
<el-table-column align="center" label="已占用人数" prop="usedSignCount"></el-table-column>
|
||||||
|
<el-table-column align="center" label="剩余人数" prop="remainingText"></el-table-column>
|
||||||
|
<el-table-column align="center" label="状态">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-tag :type="scope.row.full ? 'danger' : 'success'" size="small">{{ scope.row.statusText }}</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -63,17 +81,46 @@ module.exports = {
|
|||||||
travelAgency: {}
|
travelAgency: {}
|
||||||
},
|
},
|
||||||
modifyBdList: [],
|
modifyBdList: [],
|
||||||
|
activeTab: 'baseInfo',
|
||||||
|
currentBaseId: '',
|
||||||
|
quotaLoading: false,
|
||||||
|
quotaLoaded: false,
|
||||||
|
quotaRows: [],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
openView(id, obj) {
|
openView(id, obj) {
|
||||||
this.modifyBdList = obj
|
this.$set(this, 'modifyBdList', obj)
|
||||||
|
this.$set(this, 'currentBaseId', id)
|
||||||
|
this.$set(this, 'activeTab', 'baseInfo')
|
||||||
|
this.$set(this, 'quotaLoaded', false)
|
||||||
|
this.$set(this, 'quotaRows', [])
|
||||||
this.findOne(id)
|
this.findOne(id)
|
||||||
},
|
},
|
||||||
async findOne(id) {
|
async findOne(id) {
|
||||||
const res = await $.get('/platform/theRapyRecuperation/baseManagement/selectBaseManageById/' + id)
|
const res = await $.get('/platform/theRapyRecuperation/baseManagement/selectBaseManageById/' + id)
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
this.viewData = res.data
|
this.$set(this, 'viewData', res.data)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleTabClick(tab) {
|
||||||
|
if (tab.name === 'dailyQuota' && !this.quotaLoaded) {
|
||||||
|
this.findDailyQuota()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async findDailyQuota() {
|
||||||
|
if (!this.currentBaseId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.$set(this, 'quotaLoading', true)
|
||||||
|
try {
|
||||||
|
const res = await $.get('/platform/theRapyRecuperation/baseManagement/listDailyQuota', {id: this.currentBaseId})
|
||||||
|
if (res.code === 0) {
|
||||||
|
this.$set(this, 'quotaRows', res.data || [])
|
||||||
|
this.$set(this, 'quotaLoaded', true)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this.$set(this, 'quotaLoading', false)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user