commit
This commit is contained in:
@@ -68,11 +68,11 @@ public class NutShiroProcessor extends AbstractProcessor {
|
||||
//不需要认证授权的url
|
||||
String[] authIgnoreUrlArr = Lang.array("/",
|
||||
"/sso/login",
|
||||
// "/platform/login",
|
||||
"/platform/login",
|
||||
"/platform/login(/doLogin|/logout|/captcha)",
|
||||
"/platform/qywechat/.*",
|
||||
"/platform/home/(500|403|404|UnknownAccountError|LockedAccountError)",
|
||||
// "/mobile/login",
|
||||
"/mobile/login",
|
||||
"/mobile/login/doLogin",
|
||||
"/platform/jsz/login",
|
||||
"/platform/activity/basic/scope/getScopeUser",
|
||||
|
||||
+53
-6
@@ -3,6 +3,7 @@ package io.v.nutz.zhgh.activity.controller.basic;
|
||||
import cn.afterturn.easypoi.excel.ExcelExportUtil;
|
||||
import cn.afterturn.easypoi.excel.entity.ExportParams;
|
||||
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
|
||||
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.wizzer.framework.base.Result;
|
||||
import io.v.nutz.base.utils.ViTool;
|
||||
@@ -31,9 +32,12 @@ import org.nutz.json.Json;
|
||||
import org.nutz.lang.Lang;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
import org.nutz.mvc.annotation.At;
|
||||
import org.nutz.mvc.annotation.AdaptBy;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
import org.nutz.mvc.annotation.POST;
|
||||
import org.nutz.mvc.annotation.Param;
|
||||
import org.nutz.mvc.upload.TempFile;
|
||||
import org.nutz.mvc.upload.UploadAdaptor;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
@@ -105,7 +109,8 @@ public class ActivityBasicScopeController {
|
||||
@Param(value = "clubId", required = false) String clubId,
|
||||
@Param(value = "reverseSelection") boolean reverseSelection,
|
||||
@Param(value = "activityGroupId", required = false) Integer activityGroupId,
|
||||
@Param(value = "activityUserCnd", required = false) String activityUserCndStr) {
|
||||
@Param(value = "activityUserCnd", required = false) String activityUserCndStr,
|
||||
@Param(value = "existsLoginNameRedisKey", required = false) String existsLoginNameRedisKey) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
DISTINCT(u.id) as id,
|
||||
@@ -127,6 +132,7 @@ public class ActivityBasicScopeController {
|
||||
""");
|
||||
try {
|
||||
Cnd cnd = getCnd(pageForm, unionId, unitId, personTypes, userStates, memberTypes, sexTypes, age, teacherMeetingId, roleIds, userId, clubId, reverseSelection, activityGroupId, activityUserCndStr);
|
||||
activityBasicScopeService.appendImportUserCnd(cnd, existsLoginNameRedisKey);
|
||||
|
||||
|
||||
sql.setCondition(cnd);
|
||||
@@ -169,7 +175,8 @@ public class ActivityBasicScopeController {
|
||||
@Param(value = "userId", required = false) String[] userId,
|
||||
@Param(value = "clubId", required = false) String clubId,
|
||||
@Param(value = "reverseSelection") boolean reverseSelection,
|
||||
@Param(value = "activityUserCnd", required = false) String activityUserCndStr) {
|
||||
@Param(value = "activityUserCnd", required = false) String activityUserCndStr,
|
||||
@Param(value = "existsLoginNameRedisKey", required = false) String existsLoginNameRedisKey) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT DISTINCT
|
||||
( u.id ) AS userId
|
||||
@@ -180,6 +187,7 @@ public class ActivityBasicScopeController {
|
||||
$condition
|
||||
""");
|
||||
Cnd cnd = getCnd(pageForm, unionId, unitId, personTypes, userStates, memberTypes, sexTypes, age, teacherMeetingId, roleIds, userId, clubId, reverseSelection, activityGroupId, activityUserCndStr);
|
||||
activityBasicScopeService.appendImportUserCnd(cnd, existsLoginNameRedisKey);
|
||||
|
||||
sql.setCondition(cnd);
|
||||
sql.setCallback(Sqls.callback.entities());
|
||||
@@ -322,7 +330,7 @@ public class ActivityBasicScopeController {
|
||||
}
|
||||
|
||||
@At
|
||||
@ViReturn
|
||||
@Ok("void")
|
||||
@RequiresPermissions("activity.basic.scope")
|
||||
public void doExportUser(PageForm pageForm,
|
||||
@Param(value = "props", required = false) String props,
|
||||
@@ -339,7 +347,9 @@ public class ActivityBasicScopeController {
|
||||
@Param(value = "clubId", required = false) String clubId,
|
||||
@Param(value = "reverseSelection") boolean reverseSelection,
|
||||
@Param(value = "activityGroupId", required = false) Integer activityGroupId,
|
||||
@Param(value = "activityUserCnd", required = false) String activityUserCndStr, HttpServletResponse response) {
|
||||
@Param(value = "activityUserCnd", required = false) String activityUserCndStr,
|
||||
@Param(value = "existsLoginNameRedisKey", required = false) String existsLoginNameRedisKey,
|
||||
HttpServletResponse response) {
|
||||
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
@@ -363,6 +373,7 @@ public class ActivityBasicScopeController {
|
||||
|
||||
try {
|
||||
Cnd cnd = getCnd(pageForm, unionId, unitId, personTypes, userStates, memberTypes, sexTypes, age, teacherMeetingId, roleIds, userId, clubId, reverseSelection, activityGroupId, activityUserCndStr);
|
||||
activityBasicScopeService.appendImportUserCnd(cnd, existsLoginNameRedisKey);
|
||||
|
||||
sql.setCondition(cnd);
|
||||
|
||||
@@ -377,14 +388,50 @@ public class ActivityBasicScopeController {
|
||||
entityList.add(new ExcelExportEntity(v, k, 40));
|
||||
});
|
||||
|
||||
ViTool.excelResponse(response, "人员名单.xls");
|
||||
Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), entityList, map);
|
||||
ViTool.excelResponse(response, "人员名单.xlsx");
|
||||
ExportParams exportParams = new ExportParams();
|
||||
exportParams.setType(ExcelType.XSSF);
|
||||
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entityList, map);
|
||||
workbook.write(response.getOutputStream());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@At
|
||||
@Ok("void")
|
||||
@RequiresPermissions("activity.basic.scope")
|
||||
public void downloadImport(HttpServletResponse response) {
|
||||
try {
|
||||
ViTool.excelResponse(response, "人员导入模板.xlsx");
|
||||
List<ExcelExportEntity> entityList = new ArrayList<>();
|
||||
entityList.add(new ExcelExportEntity("工号", "loginname", 20));
|
||||
entityList.add(new ExcelExportEntity("姓名", "username", 20));
|
||||
ExportParams exportParams = new ExportParams();
|
||||
exportParams.setType(ExcelType.XSSF);
|
||||
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entityList, new ArrayList<>());
|
||||
workbook.write(response.getOutputStream());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@At
|
||||
@ViReturn
|
||||
@RequiresPermissions("activity.basic.scope")
|
||||
public Object clearSearchCnd(String existsLoginNameRedisKey) {
|
||||
activityBasicScopeService.clearImportUserCnd(existsLoginNameRedisKey);
|
||||
return null;
|
||||
}
|
||||
|
||||
@At
|
||||
@ViReturn
|
||||
@AdaptBy(type = UploadAdaptor.class, args = {"ioc:fileUpload"})
|
||||
@RequiresPermissions("activity.basic.scope")
|
||||
public Object doImport(TempFile file) {
|
||||
return activityBasicScopeService.checkImportUser(file.getFile());
|
||||
}
|
||||
|
||||
|
||||
private Cnd getCnd(PageForm pageForm,
|
||||
String unionId,
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package io.v.nutz.zhgh.activity.controller.template;
|
||||
|
||||
import cn.afterturn.easypoi.excel.annotation.Excel;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class UserTemp {
|
||||
|
||||
@Excel(name = "工号")
|
||||
private String loginname;
|
||||
|
||||
@Excel(name = "姓名")
|
||||
private String username;
|
||||
|
||||
@Excel(name = "备注")
|
||||
private String remarks;
|
||||
|
||||
}
|
||||
@@ -4,7 +4,9 @@ import io.v.nutz.zhgh.activity.models.ActivityUserScope;
|
||||
import io.v.nutz.base.service.ViService;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -21,4 +23,27 @@ public interface ActivityBasicScopeService extends ViService<ActivityUserScope>
|
||||
*/
|
||||
void largeDataInsert(List<ActivityUserScope> list) ;
|
||||
|
||||
/**
|
||||
* 解析人员导入模板,核对系统中存在的工号,并把匹配到的工号暂存为后续查询条件。
|
||||
*
|
||||
* @param file 上传的人员导入 Excel 文件
|
||||
* @return 导入总数、成功数、错误数、错误明细和临时查询 key
|
||||
*/
|
||||
NutMap checkImportUser(File file);
|
||||
|
||||
/**
|
||||
* 将导入核对成功的工号追加到当前查询条件中。
|
||||
*
|
||||
* @param cnd 查询条件
|
||||
* @param existsLoginNameRedisKey 导入核对成功工号的临时缓存 key
|
||||
*/
|
||||
void appendImportUserCnd(Cnd cnd, String existsLoginNameRedisKey);
|
||||
|
||||
/**
|
||||
* 清理导入人员临时查询条件。
|
||||
*
|
||||
* @param existsLoginNameRedisKey 导入核对成功工号的临时缓存 key
|
||||
*/
|
||||
void clearImportUserCnd(String existsLoginNameRedisKey);
|
||||
|
||||
}
|
||||
|
||||
+123
@@ -1,24 +1,37 @@
|
||||
package io.v.nutz.zhgh.activity.services.impl;
|
||||
|
||||
import cn.afterturn.easypoi.excel.ExcelImportUtil;
|
||||
import cn.afterturn.easypoi.excel.entity.ImportParams;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import io.v.nutz.zhgh.activity.models.ActivityUserScope;
|
||||
import io.v.nutz.zhgh.activity.services.ActivityBasicScopeService;
|
||||
import io.v.nutz.base.utils.DateUtil;
|
||||
import io.v.nutz.base.service.impl.ViServiceImpl;
|
||||
import io.v.nutz.zhgh.activity.controller.template.UserTemp;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.dao.impl.NutTxDao;
|
||||
import org.nutz.dao.Sqls;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
import org.nutz.integration.jedis.RedisService;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.lang.Lang;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author zxy
|
||||
@@ -28,6 +41,11 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
@IocBean(args = {"refer:dao"})
|
||||
@Slf4j
|
||||
public class ActivityBasicScopeServiceImpl extends ViServiceImpl<ActivityUserScope> implements ActivityBasicScopeService {
|
||||
private static final String IMPORT_NO_MATCH_LOGIN_NAME = "__activity_scope_import_no_match__";
|
||||
|
||||
@Inject
|
||||
private RedisService redisService;
|
||||
|
||||
public ActivityBasicScopeServiceImpl(Dao dao) {
|
||||
super(dao);
|
||||
}
|
||||
@@ -79,4 +97,109 @@ public class ActivityBasicScopeServiceImpl extends ViServiceImpl<ActivityUserSco
|
||||
}
|
||||
log.info("结束时间" + DateUtil.getDateTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析导入模板并缓存匹配到的工号,缓存 key 会返回给前端继续用于查询和分组设置。
|
||||
*
|
||||
* @param file 上传的人员导入 Excel 文件
|
||||
* @return 导入核对结果
|
||||
*/
|
||||
@Override
|
||||
public NutMap checkImportUser(File file) {
|
||||
String matchUserLoginNamesKey = "ActivityBasicScopeService.checkImportUser.time=" + System.currentTimeMillis();
|
||||
List<UserTemp> userImportList = ExcelImportUtil.importExcel(file, UserTemp.class, new ImportParams());
|
||||
if (userImportList == null) {
|
||||
userImportList = new ArrayList<>();
|
||||
}
|
||||
Set<String> sysLoginNames = getSysLoginNames();
|
||||
List<String> existsLoginNames = new ArrayList<>();
|
||||
|
||||
for (UserTemp excelUser : userImportList) {
|
||||
if (excelUser == null || StrUtil.isBlank(excelUser.getLoginname())) {
|
||||
if (excelUser != null) {
|
||||
excelUser.setRemarks("工号不能为空");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (sysLoginNames.contains(excelUser.getLoginname())) {
|
||||
existsLoginNames.add(excelUser.getLoginname());
|
||||
} else {
|
||||
excelUser.setRemarks("系统查不到此人");
|
||||
}
|
||||
}
|
||||
|
||||
List<UserTemp> errorExcelTempUsers = userImportList.stream()
|
||||
.filter(v -> v != null && StrUtil.isNotBlank(v.getRemarks()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
NutMap nutMap = NutMap.NEW();
|
||||
nutMap.setv("totalCount", userImportList.size());
|
||||
nutMap.setv("successCount", existsLoginNames.size());
|
||||
nutMap.setv("errorCount", errorExcelTempUsers.size());
|
||||
nutMap.setv("errorList", errorExcelTempUsers.stream().map(v -> NutMap.NEW()
|
||||
.addv("工号", v.getLoginname())
|
||||
.addv("姓名", v.getUsername())
|
||||
.addv("错误原因", v.getRemarks())).collect(Collectors.toList()));
|
||||
nutMap.setv("existsLoginNameRedisKey", matchUserLoginNamesKey);
|
||||
|
||||
// 没有匹配人员时写入一个不可能存在的工号,避免后续查询误变成全量。
|
||||
String[] cacheLoginNames = Lang.isNotEmpty(existsLoginNames)
|
||||
? existsLoginNames.toArray(new String[0])
|
||||
: new String[]{IMPORT_NO_MATCH_LOGIN_NAME};
|
||||
redisService.lpush(matchUserLoginNamesKey, cacheLoginNames);
|
||||
redisService.expire(matchUserLoginNamesKey, 60 * 60);
|
||||
|
||||
return nutMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据导入核对返回的 Redis key,给现有查询条件追加工号范围。
|
||||
*
|
||||
* @param cnd 查询条件
|
||||
* @param existsLoginNameRedisKey 导入核对成功工号的临时缓存 key
|
||||
*/
|
||||
@Override
|
||||
public void appendImportUserCnd(Cnd cnd, String existsLoginNameRedisKey) {
|
||||
if (StrUtil.isBlank(existsLoginNameRedisKey)) {
|
||||
return;
|
||||
}
|
||||
List<String> loginNames = redisService.lrange(existsLoginNameRedisKey, 0, -1);
|
||||
if (Lang.isEmpty(loginNames)) {
|
||||
cnd.and("u.loginname", "=", IMPORT_NO_MATCH_LOGIN_NAME);
|
||||
return;
|
||||
}
|
||||
cnd.andEX("u.loginname", "in", loginNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理导入核对成功工号的临时缓存。
|
||||
*
|
||||
* @param existsLoginNameRedisKey 导入核对成功工号的临时缓存 key
|
||||
*/
|
||||
@Override
|
||||
public void clearImportUserCnd(String existsLoginNameRedisKey) {
|
||||
if (StrUtil.isNotBlank(existsLoginNameRedisKey)) {
|
||||
redisService.del(existsLoginNameRedisKey);
|
||||
}
|
||||
}
|
||||
|
||||
private Set<String> getSysLoginNames() {
|
||||
Sql sql = Sqls.queryString("""
|
||||
SELECT
|
||||
u.loginname
|
||||
FROM
|
||||
`user` u
|
||||
""");
|
||||
dao().execute(sql);
|
||||
String[] sysLoginNames = (String[]) sql.getResult();
|
||||
Set<String> loginNameSet = new HashSet<>();
|
||||
if (sysLoginNames != null) {
|
||||
for (String loginName : sysLoginNames) {
|
||||
if (StrUtil.isNotBlank(loginName)) {
|
||||
loginNameSet.add(loginName);
|
||||
}
|
||||
}
|
||||
}
|
||||
return loginNameSet;
|
||||
}
|
||||
}
|
||||
|
||||
+78
-1
@@ -21,6 +21,7 @@ import io.v.nutz.base.utils.ViTool;
|
||||
import io.v.nutz.sys.models.SysHoliday;
|
||||
import io.v.nutz.sys.models.User;
|
||||
import io.v.nutz.web.commons.utils.ShiroUtil;
|
||||
import io.v.nutz.zhgh.therapyRecuperation.constant.TheRapyRecuperationState;
|
||||
import io.v.nutz.zhgh.therapyRecuperation.model.*;
|
||||
import io.v.nutz.zhgh.therapyRecuperation.service.baseManage.TheRapyRecuperationBaseManagerService;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
@@ -117,6 +118,7 @@ public class TheRapyRecuperationBaseManagerController {
|
||||
tb.activityStartTime,
|
||||
tb.activityEndTime,
|
||||
tb.files,
|
||||
tb.maxSignCount,
|
||||
tb.baseContactPerson,
|
||||
tb.baseContactNumber,
|
||||
tb.files as fileId,
|
||||
@@ -286,7 +288,11 @@ public class TheRapyRecuperationBaseManagerController {
|
||||
return Result.error("参数错误");
|
||||
}
|
||||
|
||||
TheRapyRecuperationConfig config = dao.fetch(TheRapyRecuperationConfig.class, Cnd.NEW());
|
||||
// 手机端日历只使用最新提交配置,保持活动时间和页面展示一致。
|
||||
TheRapyRecuperationConfig config = dao.fetch(TheRapyRecuperationConfig.class, Cnd.where("latestConfig", "=", true));
|
||||
if (config == null) {
|
||||
return Result.error("未找到最新疗休养配置,请联系管理员");
|
||||
}
|
||||
//获取线路对应的标段
|
||||
TheRapyRecuperationBaseManagement management = dao.fetch(TheRapyRecuperationBaseManagement.class, id);
|
||||
//TheRapyRecuperationLot lot = dao.fetch(TheRapyRecuperationLot.class, management.getLotId());
|
||||
@@ -306,6 +312,7 @@ public class TheRapyRecuperationBaseManagerController {
|
||||
});
|
||||
|
||||
List<Integer> weeks = management.getWeekCheckIn();
|
||||
Set<String> fullDays = getFullBaseManagementDays(management);
|
||||
|
||||
// 解析日期
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||
@@ -330,6 +337,10 @@ public class TheRapyRecuperationBaseManagerController {
|
||||
currentDate = currentDate.plusDays(1);
|
||||
continue;
|
||||
}
|
||||
if (fullDays.contains(currentDateString)) {
|
||||
currentDate = currentDate.plusDays(1);
|
||||
continue;
|
||||
}
|
||||
// 检查是否为需要排除的周几
|
||||
int dayOfWeek = currentDate.getDayOfWeek().getValue() % 7; // 1是周一,7是周日
|
||||
if (Lang.isNotEmpty(weeks) && !weeks.contains(dayOfWeek)) {
|
||||
@@ -343,11 +354,77 @@ public class TheRapyRecuperationBaseManagerController {
|
||||
|
||||
return Map.of("days", resultDates,
|
||||
"holidays", holidays,
|
||||
"fullDays", fullDays,
|
||||
"lotValue", 0,
|
||||
"minDate", DateUtil.formatDate(config.getFragmentPlayStartTime()),
|
||||
"maxDate", DateUtil.formatDate(config.getFragmentPlayEndTime()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定酒店报名已满的日期,手机端日历用来过滤不可选入住日。
|
||||
*
|
||||
* @param management 酒店信息
|
||||
* @return 已达到每日最高报名人数的日期集合
|
||||
*/
|
||||
private Set<String> getFullBaseManagementDays(TheRapyRecuperationBaseManagement management) {
|
||||
if (management == null || management.getMaxSignCount() == null || management.getMaxSignCount() <= 0) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
List<TheRapyRecuperationEnroll> enrolls = dao.query(TheRapyRecuperationEnroll.class,
|
||||
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)", "=", DateUtil.thisYear()));
|
||||
Map<String, Integer> dayCountMap = new HashMap<>();
|
||||
for (TheRapyRecuperationEnroll enroll : enrolls) {
|
||||
for (String day : parseSpecificTimeDays(enroll.getSpecificTime())) {
|
||||
dayCountMap.put(day, dayCountMap.getOrDefault(day, 0) + 1);
|
||||
}
|
||||
}
|
||||
return dayCountMap.entrySet().stream()
|
||||
.filter(entry -> entry.getValue() >= management.getMaxSignCount())
|
||||
.map(Map.Entry::getKey)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* 把“06月01日-06月03日”格式的报名时间拆成逐日日期。
|
||||
*
|
||||
* @param specificTime 报名时间段
|
||||
* @return 报名时间段覆盖的日期集合,格式 yyyy-MM-dd
|
||||
*/
|
||||
private List<String> parseSpecificTimeDays(String specificTime) {
|
||||
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 sourceFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
|
||||
DateTimeFormatter targetFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||
LocalDate start;
|
||||
LocalDate end;
|
||||
try {
|
||||
start = LocalDate.parse(DateUtil.thisYear() + "年" + times[0].trim(), sourceFormatter);
|
||||
end = LocalDate.parse(DateUtil.thisYear() + "年" + times[1].trim(), sourceFormatter);
|
||||
} catch (Exception e) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
if (end.isBefore(start)) {
|
||||
end = end.plusYears(1);
|
||||
}
|
||||
|
||||
List<String> days = new ArrayList<>();
|
||||
LocalDate current = start;
|
||||
while (!current.isAfter(end)) {
|
||||
days.add(current.format(targetFormatter));
|
||||
current = current.plusDays(1);
|
||||
}
|
||||
return days;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 目的地导入模版
|
||||
|
||||
+26
@@ -37,10 +37,12 @@ import org.nutz.mvc.annotation.At;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
import org.nutz.mvc.upload.TempFile;
|
||||
import org.nutz.mvc.upload.UploadAdaptor;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -183,6 +185,30 @@ public class TheRapyRecuperationUserQueryController {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 下载参加人员导入模板
|
||||
*
|
||||
* @param response 响应
|
||||
*/
|
||||
@At
|
||||
@Ok("void")
|
||||
@RequiresAuthentication
|
||||
public void downloadImportTemplate(HttpServletResponse response) {
|
||||
String templatePath = "templates/travel/TravelEnrollImport.xlsx";
|
||||
try (InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(templatePath)) {
|
||||
if (inputStream == null) {
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND, "模板文件不存在");
|
||||
return;
|
||||
}
|
||||
response.setContentType("application/octet-stream");
|
||||
response.setHeader("Content-Disposition", "attachment;filename=" + new String(("参加人员导入模板.xlsx").getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1));
|
||||
IOUtils.copy(inputStream, response.getOutputStream());
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 参加人员导入
|
||||
*
|
||||
|
||||
+18
-2
@@ -94,7 +94,10 @@ public class TheRapyRecuperationSchoolUnionUserQueryController {
|
||||
state.stateColor,
|
||||
line.regionalNature,
|
||||
line.lineName,
|
||||
agency.travelAgencyName,
|
||||
COALESCE(agency.travelAgencyName, lineAgency.travelAgencyName) AS travelAgencyName,
|
||||
lot.lotName,
|
||||
lot.activityCost,
|
||||
CASE WHEN lineu.playStartTime IS NULL AND lineu.playEndTime IS NULL THEN '' ELSE CONCAT(IFNULL(DATE_FORMAT(lineu.playStartTime,'%Y-%m-%d'), ''), '至', IFNULL(DATE_FORMAT(lineu.playEndTime,'%Y-%m-%d'), '')) END AS travelTime,
|
||||
ma.baseName,
|
||||
enroll.*,
|
||||
lineu.lineId,
|
||||
@@ -107,6 +110,8 @@ public class TheRapyRecuperationSchoolUnionUserQueryController {
|
||||
LEFT JOIN the_rapy_recuperation_line_union_select lineu ON lineu.id=enroll.takePartInLineId
|
||||
LEFT JOIN the_rapy_recuperation_line line ON line.id = lineu.lineId
|
||||
LEFT JOIN the_rapy_recuperation_travel_agency agency ON agency.id = enroll.takePartInTravelAgencyId
|
||||
LEFT JOIN the_rapy_recuperation_travel_agency lineAgency ON lineAgency.id = line.travelAgencyId
|
||||
LEFT JOIN the_rapy_recuperation_lot lot ON lot.id = line.lotId
|
||||
LEFT JOIN the_rapy_recuperation_base_management ma on ma.id = enroll.takePartInBaseManagementId
|
||||
LEFT JOIN audit_state state ON state.stateId = enroll.stateId
|
||||
$condition
|
||||
@@ -293,7 +298,10 @@ public class TheRapyRecuperationSchoolUnionUserQueryController {
|
||||
state.stateColor,
|
||||
line.regionalNature,
|
||||
line.lineName,
|
||||
agency.travelAgencyName,
|
||||
COALESCE(agency.travelAgencyName, lineAgency.travelAgencyName) AS travelAgencyName,
|
||||
lot.lotName,
|
||||
lot.activityCost,
|
||||
CASE WHEN lineu.playStartTime IS NULL AND lineu.playEndTime IS NULL THEN '' ELSE CONCAT(IFNULL(DATE_FORMAT(lineu.playStartTime,'%Y-%m-%d'), ''), '至', IFNULL(DATE_FORMAT(lineu.playEndTime,'%Y-%m-%d'), '')) END AS travelTime,
|
||||
ma.baseName,
|
||||
enroll.*,
|
||||
lineu.lineId,
|
||||
@@ -306,6 +314,8 @@ public class TheRapyRecuperationSchoolUnionUserQueryController {
|
||||
LEFT JOIN the_rapy_recuperation_line_union_select lineu ON lineu.id=enroll.takePartInLineId
|
||||
LEFT JOIN the_rapy_recuperation_line line ON line.id = lineu.lineId
|
||||
LEFT JOIN the_rapy_recuperation_travel_agency agency ON agency.id = enroll.takePartInTravelAgencyId
|
||||
LEFT JOIN the_rapy_recuperation_travel_agency lineAgency ON lineAgency.id = line.travelAgencyId
|
||||
LEFT JOIN the_rapy_recuperation_lot lot ON lot.id = line.lotId
|
||||
LEFT JOIN the_rapy_recuperation_base_management ma on ma.id = enroll.takePartInBaseManagementId
|
||||
LEFT JOIN audit_state state ON state.stateId = enroll.stateId
|
||||
$condition
|
||||
@@ -378,6 +388,12 @@ public class TheRapyRecuperationSchoolUnionUserQueryController {
|
||||
excelEntities.add(new ExcelExportEntity("工会", "unionName", 20));
|
||||
excelEntities.add(new ExcelExportEntity("身份证号", "idCard", 30));
|
||||
excelEntities.add(new ExcelExportEntity("手机号", "mobile", 20));
|
||||
// 导出线路关联信息,与列表查询使用同一批筛选条件,确保列表查到什么导出什么。
|
||||
excelEntities.add(new ExcelExportEntity("出行时间", "travelTime", 30));
|
||||
excelEntities.add(new ExcelExportEntity("标段", "lotName", 20));
|
||||
excelEntities.add(new ExcelExportEntity("标段费用", "activityCost", 20));
|
||||
excelEntities.add(new ExcelExportEntity("线路", "lineName", 30));
|
||||
excelEntities.add(new ExcelExportEntity("旅行社", "travelAgencyName", 30));
|
||||
if (config.getFamilyInfo() == 2) {
|
||||
excelEntities.add(new ExcelExportEntity("与本人关系", "relation", 10));
|
||||
excelEntities.add(new ExcelExportEntity("床型", "bedType", 10));
|
||||
|
||||
+14
-65
@@ -1,32 +1,14 @@
|
||||
package io.v.nutz.zhgh.therapyRecuperation.controller.theRapyConfig;
|
||||
|
||||
import io.v.nutz.base.annontation.ViReturn;
|
||||
import io.v.nutz.base.model.msgUser;
|
||||
import io.v.nutz.base.utils.MsgApi;
|
||||
import io.v.nutz.zhgh.therapyRecuperation.model.TheRapyRecuperationBaseManagement;
|
||||
import io.v.nutz.zhgh.therapyRecuperation.model.TheRapyRecuperationConfig;
|
||||
import io.v.nutz.zhgh.therapyRecuperation.model.TheRapyRecuperationLine;
|
||||
import io.v.nutz.zhgh.therapyRecuperation.model.TheRapyRecuperationLot;
|
||||
import io.v.nutz.zhgh.therapyRecuperation.service.TheRapyRecuperationConfigService;
|
||||
import org.apache.shiro.authz.annotation.RequiresAuthentication;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.ioc.aop.Aop;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.lang.Lang;
|
||||
import org.nutz.lang.Strings;
|
||||
import org.nutz.mvc.annotation.At;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
import org.nutz.trans.Trans;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* rapy休养配置控制器
|
||||
@@ -41,9 +23,7 @@ public class TheRapyRecuperationConfigController {
|
||||
|
||||
|
||||
@Inject
|
||||
private Dao dao;
|
||||
@Inject
|
||||
private MsgApi msgApi;
|
||||
private TheRapyRecuperationConfigService configService;
|
||||
|
||||
|
||||
@At("")
|
||||
@@ -55,29 +35,23 @@ public class TheRapyRecuperationConfigController {
|
||||
@At
|
||||
@ViReturn
|
||||
@RequiresAuthentication
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public Object operation(TheRapyRecuperationConfig config, String[] lotDeleteList) {
|
||||
if (Strings.isNotBlank(config.getId())) {
|
||||
if (Lang.isNotEmpty(lotDeleteList)) {
|
||||
dao.clear(TheRapyRecuperationLot.class, Cnd.where("id", "in", lotDeleteList));
|
||||
}
|
||||
dao.updateWith(config, "lots");
|
||||
List<TheRapyRecuperationLot> collect = config.getLots().stream().filter(v -> v.getConfigId() == null).collect(Collectors.toList());
|
||||
for (TheRapyRecuperationLot obj : collect) {
|
||||
obj.setConfigId(config.getId());
|
||||
}
|
||||
dao.insert(collect);
|
||||
} else {
|
||||
dao.insertWith(config, "lots");
|
||||
}
|
||||
configService.saveConfig(config, lotDeleteList);
|
||||
return null;
|
||||
}
|
||||
|
||||
@At
|
||||
@ViReturn
|
||||
@RequiresAuthentication
|
||||
public Object findOne() {
|
||||
return dao.fetchLinks(dao.fetch(TheRapyRecuperationConfig.class), "lots",Cnd.NEW().desc("lotValue"));
|
||||
public Object findOne(Integer year) {
|
||||
return configService.findByYear(year);
|
||||
}
|
||||
|
||||
@At
|
||||
@ViReturn
|
||||
@RequiresAuthentication
|
||||
public Object copyLastYear(Integer year) {
|
||||
return configService.copyLastYear(year);
|
||||
}
|
||||
|
||||
|
||||
@@ -90,20 +64,7 @@ public class TheRapyRecuperationConfigController {
|
||||
@ViReturn
|
||||
@RequiresAuthentication
|
||||
public Object getLotsById(String lotId){
|
||||
List<String> lineNames = new ArrayList<>();
|
||||
List<String> baseNames = new ArrayList<>();
|
||||
Map<String, List<String>> map = new HashMap<>();
|
||||
List<TheRapyRecuperationLine> lineList = dao.query(TheRapyRecuperationLine.class, Cnd.where("lotId", "=", lotId));
|
||||
if (!CollectionUtils.isEmpty(lineList)){
|
||||
lineList.forEach(v-> lineNames.add(v.getLineName()));
|
||||
map.put("line",lineNames);
|
||||
}
|
||||
List<TheRapyRecuperationBaseManagement> managementList = dao.query(TheRapyRecuperationBaseManagement.class, Cnd.where("lotId", "=", lotId));
|
||||
if (!CollectionUtils.isEmpty(managementList)){
|
||||
managementList.forEach(v-> baseNames.add(v.getBaseName()));
|
||||
map.put("base",baseNames);
|
||||
}
|
||||
return map;
|
||||
return configService.getLotsById(lotId);
|
||||
}
|
||||
|
||||
|
||||
@@ -116,19 +77,7 @@ public class TheRapyRecuperationConfigController {
|
||||
@ViReturn
|
||||
@RequiresAuthentication
|
||||
public Object deleteLotById(String lotId){
|
||||
Trans.exec(()->{
|
||||
dao.clear(TheRapyRecuperationLot.class, Cnd.where("id","=",lotId));
|
||||
List<TheRapyRecuperationLine> lineList = dao.query(TheRapyRecuperationLine.class, Cnd.where("lotId", "=", lotId));
|
||||
if (!CollectionUtils.isEmpty(lineList)) {
|
||||
lineList.forEach(v -> v.setLotId(null));
|
||||
dao.update(lineList);
|
||||
}
|
||||
List<TheRapyRecuperationBaseManagement> managementList = dao.query(TheRapyRecuperationBaseManagement.class, Cnd.where("lotId", "=", lotId));
|
||||
if (!CollectionUtils.isEmpty(managementList)) {
|
||||
managementList.forEach(v -> v.setLotId(null));
|
||||
dao.update(managementList);
|
||||
}
|
||||
});
|
||||
configService.deleteLotById(lotId);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
+6
@@ -165,6 +165,12 @@ public class TheRapyRecuperationBaseManagement {
|
||||
@Comment("天数")
|
||||
private List<String> allowDay;
|
||||
|
||||
@Column
|
||||
@ColDefine(type = ColType.INT)
|
||||
@Comment("同一酒店每日最高报名人数")
|
||||
@Excel(name = "同一酒店每日最高报名人数")
|
||||
private Integer maxSignCount;
|
||||
|
||||
@Column
|
||||
@ColDefine(type = ColType.VARCHAR)
|
||||
@Comment("旅行社地点")
|
||||
|
||||
@@ -26,6 +26,16 @@ public class TheRapyRecuperationConfig {
|
||||
@Comment("配置名称")
|
||||
private String configName;
|
||||
|
||||
@Column
|
||||
@ColDefine(type = ColType.INT)
|
||||
@Comment("配置年度")
|
||||
private Integer configYear;
|
||||
|
||||
@Column
|
||||
@ColDefine(type = ColType.BOOLEAN)
|
||||
@Comment("是否最新提交配置")
|
||||
private Boolean latestConfig;
|
||||
|
||||
@Column
|
||||
@ColDefine(type = ColType.INT, width = 32)
|
||||
@Comment("参加人员范围")
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package io.v.nutz.zhgh.therapyRecuperation.service;
|
||||
|
||||
import io.v.nutz.base.service.ViService;
|
||||
import io.v.nutz.zhgh.therapyRecuperation.model.TheRapyRecuperationConfig;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
|
||||
/**
|
||||
* 疗休养基础配置服务。
|
||||
*/
|
||||
public interface TheRapyRecuperationConfigService extends ViService<TheRapyRecuperationConfig> {
|
||||
|
||||
/**
|
||||
* 按年度查询基础配置;年度为空时使用当前年度。
|
||||
*
|
||||
* @param configYear 配置年度
|
||||
* @return 年度配置
|
||||
*/
|
||||
TheRapyRecuperationConfig findByYear(Integer configYear);
|
||||
|
||||
/**
|
||||
* 保存年度基础配置,已存在配置时更新,不存在时新增。
|
||||
*
|
||||
* @param config 基础配置
|
||||
* @param lotDeleteList 需要删除的标段id
|
||||
*/
|
||||
void saveConfig(TheRapyRecuperationConfig config, String[] lotDeleteList);
|
||||
|
||||
/**
|
||||
* 将上一年度配置复制为当前年度配置。
|
||||
*
|
||||
* @param configYear 当前配置年度
|
||||
* @return 复制后的当前年度配置
|
||||
*/
|
||||
TheRapyRecuperationConfig copyLastYear(Integer configYear);
|
||||
|
||||
/**
|
||||
* 查询使用该标段的线路和目的地。
|
||||
*
|
||||
* @param lotId 标段id
|
||||
* @return 使用情况
|
||||
*/
|
||||
NutMap getLotsById(String lotId);
|
||||
|
||||
/**
|
||||
* 强制删除标段,并清空已绑定线路和目的地上的标段。
|
||||
*
|
||||
* @param lotId 标段id
|
||||
*/
|
||||
void deleteLotById(String lotId);
|
||||
}
|
||||
+236
@@ -0,0 +1,236 @@
|
||||
package io.v.nutz.zhgh.therapyRecuperation.service.impl;
|
||||
|
||||
import cn.hutool.core.date.DateTime;
|
||||
import io.v.nutz.base.service.impl.ViServiceImpl;
|
||||
import io.v.nutz.zhgh.therapyRecuperation.model.TheRapyRecuperationBaseManagement;
|
||||
import io.v.nutz.zhgh.therapyRecuperation.model.TheRapyRecuperationConfig;
|
||||
import io.v.nutz.zhgh.therapyRecuperation.model.TheRapyRecuperationLine;
|
||||
import io.v.nutz.zhgh.therapyRecuperation.model.TheRapyRecuperationLot;
|
||||
import io.v.nutz.zhgh.therapyRecuperation.service.TheRapyRecuperationConfigService;
|
||||
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||
import org.nutz.dao.Chain;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.ioc.aop.Aop;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.lang.Lang;
|
||||
import org.nutz.lang.Strings;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
import org.nutz.trans.Trans;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.temporal.ChronoField;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 疗休养基础配置服务实现。
|
||||
*/
|
||||
@IocBean(args = {"refer:dao"})
|
||||
public class TheRapyRecuperationConfigServiceImpl extends ViServiceImpl<TheRapyRecuperationConfig> implements TheRapyRecuperationConfigService {
|
||||
|
||||
private static final int DEFAULT_CONFIG_YEAR = 2025;
|
||||
|
||||
public TheRapyRecuperationConfigServiceImpl(Dao dao) {
|
||||
super(dao);
|
||||
}
|
||||
|
||||
/**
|
||||
* 年度为空时统一使用 2025 年,兼容原有无年度的基础配置。
|
||||
*
|
||||
* @param configYear 配置年度
|
||||
* @return 有效年度
|
||||
*/
|
||||
private Integer getConfigYear(Integer configYear) {
|
||||
return configYear == null ? DEFAULT_CONFIG_YEAR : configYear;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TheRapyRecuperationConfig findByYear(Integer configYear) {
|
||||
if (configYear == null) {
|
||||
return findLatestConfig();
|
||||
}
|
||||
Integer year = getConfigYear(configYear);
|
||||
TheRapyRecuperationConfig config = fetch(Cnd.where("configYear", "=", year));
|
||||
if (config == null && year == DEFAULT_CONFIG_YEAR) {
|
||||
config = fetch(Cnd.where("configYear", "is", null));
|
||||
}
|
||||
if (config == null) {
|
||||
return null;
|
||||
}
|
||||
return dao().fetchLinks(config, "lots", Cnd.NEW().desc("lotValue"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 其他业务页面不传年度时,只使用最新提交的基础配置。
|
||||
*
|
||||
* @return 最新基础配置
|
||||
*/
|
||||
private TheRapyRecuperationConfig findLatestConfig() {
|
||||
TheRapyRecuperationConfig config = fetch(Cnd.where("latestConfig", "=", true));
|
||||
if (config == null) {
|
||||
return null;
|
||||
}
|
||||
return dao().fetchLinks(config, "lots", Cnd.NEW().desc("lotValue"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public void saveConfig(TheRapyRecuperationConfig config, String[] lotDeleteList) {
|
||||
Integer year = getConfigYear(config.getConfigYear());
|
||||
config.setConfigYear(year);
|
||||
config.setLatestConfig(true);
|
||||
TheRapyRecuperationConfig oldConfig = findByYear(year);
|
||||
if (Strings.isBlank(config.getId()) && oldConfig != null) {
|
||||
config.setId(oldConfig.getId());
|
||||
}
|
||||
resetLatestConfig(config.getId());
|
||||
if (Strings.isNotBlank(config.getId())) {
|
||||
if (Lang.isNotEmpty(lotDeleteList)) {
|
||||
dao().clear(TheRapyRecuperationLot.class, Cnd.where("id", "in", lotDeleteList));
|
||||
}
|
||||
dao().updateWith(config, "lots");
|
||||
List<TheRapyRecuperationLot> lots = config.getLots() == null ? new ArrayList<>() : config.getLots();
|
||||
List<TheRapyRecuperationLot> collect = lots.stream().filter(v -> v.getConfigId() == null).collect(Collectors.toList());
|
||||
for (TheRapyRecuperationLot obj : collect) {
|
||||
obj.setConfigId(config.getId());
|
||||
}
|
||||
if (Lang.isNotEmpty(collect)) {
|
||||
dao().insert(collect);
|
||||
}
|
||||
} else {
|
||||
dao().insertWith(config, "lots");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 每次提交年度配置后,仅保留当前配置为最新配置。
|
||||
*
|
||||
* @param currentConfigId 当前配置id
|
||||
*/
|
||||
private void resetLatestConfig(String currentConfigId) {
|
||||
Cnd cnd = Cnd.NEW();
|
||||
if (Strings.isNotBlank(currentConfigId)) {
|
||||
cnd.and("id", "!=", currentConfigId);
|
||||
}
|
||||
dao().update(TheRapyRecuperationConfig.class, Chain.make("latestConfig", false), cnd);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public TheRapyRecuperationConfig copyLastYear(Integer configYear) {
|
||||
Integer year = getConfigYear(configYear);
|
||||
TheRapyRecuperationConfig currentConfig = findByYear(year);
|
||||
TheRapyRecuperationConfig lastConfig = findByYear(year - 1);
|
||||
if (lastConfig == null) {
|
||||
return null;
|
||||
}
|
||||
lastConfig.setId(currentConfig == null ? null : currentConfig.getId());
|
||||
lastConfig.setConfigYear(year);
|
||||
lastConfig.setLatestConfig(true);
|
||||
resetConfigTimeYear(lastConfig, year);
|
||||
resetLatestConfig(null);
|
||||
if (currentConfig == null) {
|
||||
copyLotsToNewConfig(lastConfig);
|
||||
dao().insertWith(lastConfig, "lots");
|
||||
} else {
|
||||
dao().clear(TheRapyRecuperationLot.class, Cnd.where("configId", "=", currentConfig.getId()));
|
||||
copyLotsToNewConfig(lastConfig);
|
||||
dao().update(lastConfig);
|
||||
for (TheRapyRecuperationLot lot : lastConfig.getLots()) {
|
||||
lot.setConfigId(currentConfig.getId());
|
||||
}
|
||||
if (Lang.isNotEmpty(lastConfig.getLots())) {
|
||||
dao().insert(lastConfig.getLots());
|
||||
}
|
||||
}
|
||||
return findByYear(year);
|
||||
}
|
||||
|
||||
/**
|
||||
* 沿用上一年度配置时,将所有年度相关时间改为当前配置年度,月日和时分秒保持不变。
|
||||
*
|
||||
* @param config 待复制配置
|
||||
* @param year 当前配置年度
|
||||
*/
|
||||
private void resetConfigTimeYear(TheRapyRecuperationConfig config, Integer year) {
|
||||
config.setCreateLineStartTime(resetDateTimeYear(config.getCreateLineStartTime(), year));
|
||||
config.setCreateLineEndTime(resetDateTimeYear(config.getCreateLineEndTime(), year));
|
||||
config.setImplodeStartTime(resetDateTimeYear(config.getImplodeStartTime(), year));
|
||||
config.setImplodeEndTime(resetDateTimeYear(config.getImplodeEndTime(), year));
|
||||
config.setFragmentStartTime(resetDateTimeYear(config.getFragmentStartTime(), year));
|
||||
config.setFragmentEndTime(resetDateTimeYear(config.getFragmentEndTime(), year));
|
||||
config.setFragmentPlayStartTime(resetDateTimeYear(config.getFragmentPlayStartTime(), year));
|
||||
config.setFragmentPlayEndTime(resetDateTimeYear(config.getFragmentPlayEndTime(), year));
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换时间年份;闰年 2 月 29 日复制到平年时,自动落到该年 2 月最后一天。
|
||||
*
|
||||
* @param dateTime 原时间
|
||||
* @param year 目标年度
|
||||
* @return 替换年份后的时间
|
||||
*/
|
||||
private DateTime resetDateTimeYear(DateTime dateTime, Integer year) {
|
||||
if (dateTime == null || year == null) {
|
||||
return dateTime;
|
||||
}
|
||||
LocalDateTime sourceTime = LocalDateTime.ofInstant(dateTime.toInstant(), ZoneId.systemDefault());
|
||||
int dayOfMonth = (int) Math.min(sourceTime.getDayOfMonth(), sourceTime.withYear(year).range(ChronoField.DAY_OF_MONTH).getMaximum());
|
||||
LocalDateTime targetTime = sourceTime.withYear(year).withDayOfMonth(dayOfMonth);
|
||||
return new DateTime(java.util.Date.from(targetTime.atZone(ZoneId.systemDefault()).toInstant()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制上一年度标段时清空主键和配置外键,让数据库生成当前年度独立数据。
|
||||
*
|
||||
* @param config 待复制配置
|
||||
*/
|
||||
private void copyLotsToNewConfig(TheRapyRecuperationConfig config) {
|
||||
if (config.getLots() == null) {
|
||||
return;
|
||||
}
|
||||
for (TheRapyRecuperationLot lot : config.getLots()) {
|
||||
lot.setId(null);
|
||||
lot.setConfigId(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public NutMap getLotsById(String lotId) {
|
||||
List<String> lineNames = new ArrayList<>();
|
||||
List<String> baseNames = new ArrayList<>();
|
||||
NutMap map = new NutMap();
|
||||
List<TheRapyRecuperationLine> lineList = dao().query(TheRapyRecuperationLine.class, Cnd.where("lotId", "=", lotId));
|
||||
if (!CollectionUtils.isEmpty(lineList)) {
|
||||
lineList.forEach(v -> lineNames.add(v.getLineName()));
|
||||
map.put("line", lineNames);
|
||||
}
|
||||
List<TheRapyRecuperationBaseManagement> managementList = dao().query(TheRapyRecuperationBaseManagement.class, Cnd.where("lotId", "=", lotId));
|
||||
if (!CollectionUtils.isEmpty(managementList)) {
|
||||
managementList.forEach(v -> baseNames.add(v.getBaseName()));
|
||||
map.put("base", baseNames);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteLotById(String lotId) {
|
||||
Trans.exec(() -> {
|
||||
dao().clear(TheRapyRecuperationLot.class, Cnd.where("id", "=", lotId));
|
||||
List<TheRapyRecuperationLine> lineList = dao().query(TheRapyRecuperationLine.class, Cnd.where("lotId", "=", lotId));
|
||||
if (!CollectionUtils.isEmpty(lineList)) {
|
||||
lineList.forEach(v -> v.setLotId(null));
|
||||
dao().update(lineList);
|
||||
}
|
||||
List<TheRapyRecuperationBaseManagement> managementList = dao().query(TheRapyRecuperationBaseManagement.class, Cnd.where("lotId", "=", lotId));
|
||||
if (!CollectionUtils.isEmpty(managementList)) {
|
||||
managementList.forEach(v -> v.setLotId(null));
|
||||
dao().update(managementList);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
+113
-3
@@ -35,6 +35,7 @@ import org.nutz.lang.util.NutMap;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.Period;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -543,8 +544,11 @@ public class TheRapyRecuperationEnrollServiceImpl extends ViServiceImpl<TheRapyR
|
||||
@Override
|
||||
public Map<Boolean, String> validSignUpInfo(String loginName, TheRapyRecuperationEnroll enrollInfo) {
|
||||
|
||||
//配置信息
|
||||
TheRapyRecuperationConfig config = dao().fetch(TheRapyRecuperationConfig.class);
|
||||
// 配置信息只取当前最新配置,避免报名校验和页面展示使用不同年度配置。
|
||||
TheRapyRecuperationConfig config = fetchLatestConfig();
|
||||
if (config == null) {
|
||||
return Map.of(false, "未找到最新疗休养配置,请联系管理员");
|
||||
}
|
||||
|
||||
if (StrUtil.isNotBlank(loginName) && loginName.startsWith("2025")) {
|
||||
return Map.of(false, "抱歉,您是今年新入职,不能报名!");
|
||||
@@ -710,7 +714,10 @@ public class TheRapyRecuperationEnrollServiceImpl extends ViServiceImpl<TheRapyR
|
||||
}
|
||||
|
||||
public Map<Boolean, String> validSignTime(TheRapyRecuperationEnroll currentEnroll) {
|
||||
TheRapyRecuperationConfig config = dao().fetch(TheRapyRecuperationConfig.class, Cnd.NEW());
|
||||
TheRapyRecuperationConfig config = fetchLatestConfig();
|
||||
if (config == null) {
|
||||
return Map.of(false, "未找到最新疗休养配置,请联系管理员");
|
||||
}
|
||||
DateTime signUpStartTime = null;
|
||||
DateTime signUpEndTime = null;
|
||||
if (StrUtil.isNotBlank(currentEnroll.getTakePartInLineId())) {
|
||||
@@ -729,7 +736,21 @@ public class TheRapyRecuperationEnrollServiceImpl extends ViServiceImpl<TheRapyR
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最新提交的疗休养配置。
|
||||
*
|
||||
* @return 最新疗休养配置
|
||||
*/
|
||||
private TheRapyRecuperationConfig fetchLatestConfig() {
|
||||
return dao().fetch(TheRapyRecuperationConfig.class, Cnd.where("latestConfig", "=", true));
|
||||
}
|
||||
|
||||
public Map<Boolean, String> validBaseManagementCount(String loginName, TheRapyRecuperationEnroll currentEnroll) {
|
||||
Map<Boolean, String> dailySignCountResult = validBaseManagementDailySignCount(currentEnroll);
|
||||
if (dailySignCountResult != null && dailySignCountResult.containsKey(false)) {
|
||||
return dailySignCountResult;
|
||||
}
|
||||
|
||||
if(StrUtil.isBlank(currentEnroll.getId())) {
|
||||
int count = dao().count(TheRapyRecuperationEnroll.class,
|
||||
Cnd.where("takePartInBaseManagementId", "=", currentEnroll.getTakePartInBaseManagementId())
|
||||
@@ -790,6 +811,95 @@ public class TheRapyRecuperationEnrollServiceImpl extends ViServiceImpl<TheRapyR
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验分段疗休养同一酒店每日最高报名人数,报名时间段中任意一天满员都不能继续报名。
|
||||
*
|
||||
* @param currentEnroll 当前报名信息
|
||||
* @return 校验失败原因;返回 null 表示未配置上限或未触发限制
|
||||
*/
|
||||
public Map<Boolean, String> validBaseManagementDailySignCount(TheRapyRecuperationEnroll currentEnroll) {
|
||||
if (StrUtil.isBlank(currentEnroll.getTakePartInBaseManagementId())
|
||||
|| StrUtil.isBlank(currentEnroll.getSpecificTime())) {
|
||||
return null;
|
||||
}
|
||||
TheRapyRecuperationBaseManagement management = dao().fetch(TheRapyRecuperationBaseManagement.class, currentEnroll.getTakePartInBaseManagementId());
|
||||
if (management == null || management.getMaxSignCount() == null || management.getMaxSignCount() <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<LocalDate> currentDays = parseSpecificTimeDays(currentEnroll.getSpecificTime());
|
||||
for (LocalDate currentDay : currentDays) {
|
||||
int signedCount = countBaseManagementSignUserByDay(currentEnroll, currentDay);
|
||||
if (signedCount + 1 > management.getMaxSignCount()) {
|
||||
return Map.of(false, "该酒店%s报名人数已满,请重新选择出行时间".formatted(currentDay.format(DateTimeFormatter.ofPattern("MM月dd日"))));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计同一酒店已报名且出行时间覆盖指定日期的教职工人数。
|
||||
*
|
||||
* @param currentEnroll 当前报名信息
|
||||
* @param currentDay 需要判断的日期
|
||||
* @return 已覆盖该日期的报名人数
|
||||
*/
|
||||
private int countBaseManagementSignUserByDay(TheRapyRecuperationEnroll currentEnroll, LocalDate currentDay) {
|
||||
Cnd cnd = Cnd.where("takePartInBaseManagementId", "=", currentEnroll.getTakePartInBaseManagementId())
|
||||
.and("specificTime", "is not", null)
|
||||
.and("isNormal", "=", true)
|
||||
.and("stateId", "in", Lang.array(TheRapyRecuperationState.UNIT, TheRapyRecuperationState.LINEUNIT, TheRapyRecuperationState.SCHOOL, TheRapyRecuperationState.PASS))
|
||||
.and("YEAR(signingUptime)", "=", DateUtil.thisYear());
|
||||
if (StrUtil.isNotBlank(currentEnroll.getId())) {
|
||||
cnd.and("id", "!=", currentEnroll.getId());
|
||||
}
|
||||
List<TheRapyRecuperationEnroll> enrolls = dao().query(TheRapyRecuperationEnroll.class, cnd);
|
||||
int count = 0;
|
||||
for (TheRapyRecuperationEnroll enroll : enrolls) {
|
||||
List<LocalDate> signDays = parseSpecificTimeDays(enroll.getSpecificTime());
|
||||
if (signDays.contains(currentDay)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* 把“06月01日-06月03日”格式的报名时间拆成逐日日期,便于做每日容量判断。
|
||||
*
|
||||
* @param specificTime 报名时间段
|
||||
* @return 报名时间段包含的所有日期
|
||||
*/
|
||||
private List<LocalDate> parseSpecificTimeDays(String specificTime) {
|
||||
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 {
|
||||
start = LocalDate.parse(DateUtil.thisYear() + "年" + times[0].trim(), formatter);
|
||||
end = LocalDate.parse(DateUtil.thisYear() + "年" + 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;
|
||||
}
|
||||
|
||||
//判断一年报几次
|
||||
public Map<Boolean, String> validCountThisYear(String loginName, int travelFrequency, Boolean validHotel) {
|
||||
Cnd cnd = Cnd.NEW();
|
||||
|
||||
@@ -279,6 +279,10 @@
|
||||
<el-card class="mt10" shadow="never">
|
||||
<table-tool :app="this" label="筛选人员">
|
||||
<template #func>
|
||||
<el-button v-if="is_sysadmin||is_A06"
|
||||
type="primary" size="medium" icon="el-icon-printer" @click="openImport">
|
||||
导入XLSX设置分组
|
||||
</el-button>
|
||||
<el-button @click="doExportUser"
|
||||
size="medium"
|
||||
type="primary" icon="el-icon-download">
|
||||
@@ -349,6 +353,57 @@
|
||||
v-loading="settingLoading">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="人员导入" :visible.sync="dialogVisible" width="50%" :close-on-click-modal="false" append-to-body>
|
||||
<el-timeline>
|
||||
<el-timeline-item timestamp="下载模板" placement="top">
|
||||
<el-card>
|
||||
<el-button size="medium" type=""
|
||||
@click="window.open('/platform/activity/basic/scope/downloadImport')"
|
||||
icon="el-icon-download">下载模板
|
||||
</el-button>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
<el-timeline-item timestamp="上传文件" placement="top">
|
||||
<el-card>
|
||||
<el-upload
|
||||
name="file"
|
||||
ref="upload"
|
||||
:on-remove="handleImportFileRemove"
|
||||
:on-change="handleImportFileChange"
|
||||
:auto-upload="false"
|
||||
:limit="1"
|
||||
:file-list="importData.fileList">
|
||||
<el-button size="medium" type="" icon="el-icon-upload"
|
||||
style="width: 200px">选择文件
|
||||
</el-button>
|
||||
<div class="el-upload__tip" slot="tip" style="color: #F56C6C">
|
||||
只能上传 xls/xlsx 文件
|
||||
</div>
|
||||
</el-upload>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
<el-timeline-item placement="top" timestamp="导入结果">
|
||||
<el-card shadow="never">
|
||||
<p>总记录数:{{ errorInfoData.totalCount }}</p>
|
||||
<p>成功数:<span class="text-success">{{ errorInfoData.successCount }}</span></p>
|
||||
<p>错误数:<span class="text-danger">{{ errorInfoData.errorCount }}</span></p>
|
||||
<el-link @click="exportErrors" type="primary" v-if="errorInfoData.errorCount>0">下载错误记录
|
||||
</el-link>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false" type="primary"
|
||||
:disabled="importLoading">取 消</el-button>
|
||||
<el-button type="primary" @click="clearSearchCnd"
|
||||
:loading="importLoading">清空查询条件</el-button>
|
||||
<el-button type="primary" @click="doImport" :loading="importLoading">核对人员</el-button>
|
||||
<el-button type="primary" @click="doImportSearch"
|
||||
:loading="importLoading">查询人员</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -405,6 +460,7 @@ module.exports = {
|
||||
roleIds: [],
|
||||
userId: [],
|
||||
activityUserCnd: '',
|
||||
existsLoginNameRedisKey: '',
|
||||
reverseSelection: false
|
||||
},
|
||||
activityUnions: [],
|
||||
@@ -434,7 +490,18 @@ module.exports = {
|
||||
setGroupName: [{required: true, message: '请输入分组名称', trigger: ['blur', 'change']}]
|
||||
},
|
||||
relatedSessionMenus: ['aca4d14498c145ceb5b24ed70776aae2', '733d7266652740a3aeac9da97ab5eeca', 'fe2d0768e26d4a80beeb159306bb8d01'],
|
||||
roleData: {}
|
||||
roleData: {},
|
||||
dialogVisible: false,
|
||||
importLoading: false,
|
||||
importData: {
|
||||
fileList: [],
|
||||
},
|
||||
errorInfoData: {
|
||||
totalCount: 0,
|
||||
successCount: 0,
|
||||
errorCount: 0,
|
||||
errorList: [],
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -461,6 +528,88 @@ module.exports = {
|
||||
'user-cnd': httpVueLoader('/components/plugins/UserCnd.vue'),
|
||||
},
|
||||
methods: {
|
||||
exportErrors() {
|
||||
const data = this.errorInfoData.errorList
|
||||
const workbook = XLSX.utils.book_new()
|
||||
const worksheet = XLSX.utils.json_to_sheet(data)
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1')
|
||||
const excelBuffer = XLSX.write(workbook, {bookType: 'xlsx', type: 'array'})
|
||||
const blob = new Blob([excelBuffer], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'})
|
||||
const url = window.URL.createObjectURL(blob)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.download = '核对记录.xlsx'
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
window.URL.revokeObjectURL(url)
|
||||
},
|
||||
doImportSearch() {
|
||||
this.doSearch()
|
||||
this.dialogVisible = false
|
||||
},
|
||||
doImport() {
|
||||
if (this.importData.fileList.length === 0) {
|
||||
this.notifyWarning("请选择文件!")
|
||||
return
|
||||
}
|
||||
const data = new FormData()
|
||||
this.importData.fileList.forEach((val) => {
|
||||
data.append("file", val.raw, val.raw.name)
|
||||
})
|
||||
|
||||
this.importLoading = true
|
||||
$.ajax({
|
||||
url: "/platform/activity/basic/scope/doImport",
|
||||
type: "post",
|
||||
data: data,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
success: (data) => {
|
||||
this.importLoading = false
|
||||
if (data.code === 0) {
|
||||
this.notifySuccess("核对成功")
|
||||
this.errorInfoData = data.data
|
||||
this.$set(this.pageForm, "existsLoginNameRedisKey", data.data.existsLoginNameRedisKey)
|
||||
} else {
|
||||
this.notifyWarning("核对失败")
|
||||
}
|
||||
},
|
||||
error: () => {
|
||||
this.notifyWarning("导入失败")
|
||||
this.importLoading = false
|
||||
}
|
||||
})
|
||||
},
|
||||
clearSearchCnd() {
|
||||
const existsLoginNameRedisKey = this.pageForm.existsLoginNameRedisKey
|
||||
this.$set(this.importData, "fileList", [])
|
||||
this.errorInfoData = {
|
||||
totalCount: 0,
|
||||
successCount: 0,
|
||||
errorCount: 0,
|
||||
errorList: [],
|
||||
}
|
||||
this.$set(this.pageForm, "existsLoginNameRedisKey", "")
|
||||
this.doSearch()
|
||||
this.dialogVisible = false
|
||||
$.get('/platform/activity/basic/scope/clearSearchCnd', {existsLoginNameRedisKey: existsLoginNameRedisKey}).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
} else {
|
||||
this.$message.error(res.msg)
|
||||
}
|
||||
})
|
||||
},
|
||||
openImport() {
|
||||
this.dialogVisible = true
|
||||
},
|
||||
handleImportFileRemove(file, fileList) {
|
||||
this.$set(this.importData, "fileList", this.fileHandleRemove(file, fileList))
|
||||
},
|
||||
handleImportFileChange(file, fileList) {
|
||||
this.$set(this.importData, "fileList", this.fileHandleChange(file, fileList, {type: ['xls', 'xlsx']}))
|
||||
},
|
||||
doExportUser() {
|
||||
const {
|
||||
userId,
|
||||
@@ -476,7 +625,8 @@ module.exports = {
|
||||
activityGroupId,
|
||||
age,
|
||||
reverseSelection,
|
||||
activityUserCnd
|
||||
activityUserCnd,
|
||||
existsLoginNameRedisKey
|
||||
} = this.pageForm
|
||||
|
||||
let props = {}
|
||||
@@ -495,6 +645,7 @@ module.exports = {
|
||||
"&reverseSelection=" + reverseSelection +
|
||||
"&clubId=" + clubId +
|
||||
"&activityGroupId=" + activityGroupId +
|
||||
"&existsLoginNameRedisKey=" + existsLoginNameRedisKey +
|
||||
"&teacherMeetingId=" + teacherMeetingId +
|
||||
"&unionId=" + unionId +
|
||||
"&unitId=" + unitId
|
||||
@@ -514,6 +665,7 @@ module.exports = {
|
||||
this.pageForm.clubId = ''
|
||||
this.pageForm.activityGroupId = ''
|
||||
this.pageForm.activityUserCnd = ''
|
||||
this.pageForm.existsLoginNameRedisKey = ''
|
||||
this.pageForm.age = [0, 0]
|
||||
this.pageForm.reverseSelection = false
|
||||
this.doSearch()
|
||||
|
||||
Binary file not shown.
@@ -527,6 +527,7 @@ layout("/mobile/platform.html"){
|
||||
minDate: new Date(2010, 0, 1),
|
||||
maxDate: new Date(2010, 0, 31),
|
||||
holidays: [],
|
||||
fullDays: [],
|
||||
timeVisible: false,
|
||||
active: 0,
|
||||
tarBarActive: 0,
|
||||
@@ -623,6 +624,12 @@ layout("/mobile/platform.html"){
|
||||
this.$refs.cal.reset(null)
|
||||
break
|
||||
}
|
||||
if(this.fullDays.includes(afterDateStr)) {
|
||||
this.$toast(afterDateStr + '报名人数已满,请更换出行时间')
|
||||
this.chooseDate = []
|
||||
this.$refs.cal.reset(null)
|
||||
break
|
||||
}
|
||||
|
||||
this.chooseDate.push(afterDateStr)
|
||||
if(i === (this.lotValue - 1)) {
|
||||
@@ -639,6 +646,10 @@ layout("/mobile/platform.html"){
|
||||
if(this.timeArray.indexOf(date) === -1) {
|
||||
day.type = 'disabled'
|
||||
}
|
||||
if(this.fullDays.includes(date)) {
|
||||
day.type = 'disabled'
|
||||
day.bottomInfo = '已满'
|
||||
}
|
||||
if(this.chooseDate.length > 0 && this.chooseDate.includes(date)) {
|
||||
day.className = 'cal_middle'
|
||||
if(date === this.chooseDate[this.chooseDate.length - 1]) {
|
||||
@@ -801,6 +812,7 @@ layout("/mobile/platform.html"){
|
||||
})
|
||||
this.timeArray = res.data.days
|
||||
this.holidays = res.data.holidays
|
||||
this.$set(this, 'fullDays', res.data.fullDays || [])
|
||||
//this.lotValue = Number(res.data.lotValue)
|
||||
this.minDate = new Date(res.data.minDate)
|
||||
this.maxDate = new Date(res.data.maxDate)
|
||||
|
||||
+17
-2
@@ -96,6 +96,9 @@ layout("/layouts/platform.html"){
|
||||
<span v-if="row.lotId===item.id">{{item.lotName}}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template scope="{row}" v-else-if="column.prop==='maxSignCount'">
|
||||
<span>{{row.maxSignCount == null || row.maxSignCount === '' || Number(row.maxSignCount) <= 0 ? '无限制' : row.maxSignCount}}</span>
|
||||
</template>
|
||||
<template scope="{row}" v-else-if="column.prop==='createUserName'">
|
||||
{{row.createUserName + '(' + row.createUnionName + ')'}}
|
||||
</template>
|
||||
@@ -202,7 +205,7 @@ layout("/layouts/platform.html"){
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :md="6" :sm="24" :xs="24">
|
||||
<el-col :md="12" :sm="24" :xs="24">
|
||||
<el-form-item label="活动范围" prop="regionalNature">
|
||||
<el-radio-group size="small" v-model="formData.regionalNature">
|
||||
<el-radio label="省内" border>省内</el-radio>
|
||||
@@ -210,7 +213,16 @@ layout("/layouts/platform.html"){
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :md="6" :sm="24" :xs="24">
|
||||
<el-col :md="12" :sm="24" :xs="24">
|
||||
<el-form-item label="每日最高人数" prop="maxSignCount">
|
||||
<el-input-number :min="0" :precision="0" placeholder="0为不限制"
|
||||
style="width: 100%" v-model="formData.maxSignCount"></el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :md="12" :sm="24" :xs="24">
|
||||
<el-form-item label="周几入住" prop="weekIn">
|
||||
<el-radio-group size="small" v-model="formData.weekIn" @input="weekChange">
|
||||
<el-radio :label="1" border>不限</el-radio>
|
||||
@@ -508,6 +520,7 @@ layout("/layouts/platform.html"){
|
||||
baseContactNumber: '',
|
||||
weekIn: 1,
|
||||
allowDay: [],
|
||||
maxSignCount: 0,
|
||||
travelAgencyPlace: '',
|
||||
},
|
||||
tableColumns: [
|
||||
@@ -515,6 +528,7 @@ layout("/layouts/platform.html"){
|
||||
{label: '目的地名称', prop: 'baseName', sortable: true},
|
||||
{label: '承担旅行社', prop: 'travelAgencyName', sortable: true},
|
||||
//{label: '标段时间', prop: 'lotId', sortable: true},
|
||||
{label: '每日最高人数', prop: 'maxSignCount', sortable: true},
|
||||
{label: '联系人', prop: 'baseContactPerson', sortable: true},
|
||||
{label: '联系人电话', prop: 'baseContactNumber', sortable: true},
|
||||
//{label: '活动时间', prop: 'activityTime', sortable: true},
|
||||
@@ -543,6 +557,7 @@ layout("/layouts/platform.html"){
|
||||
activityEndTime: [{required: true, validator: validateActivityEndTime, trigger: ['change', 'blur']}],
|
||||
estimatedCost: [{required: true, message: '请输入预计费用', trigger: ['change', 'blur']}],
|
||||
allowDay: [{required: true, message: '请选择天数', trigger: ['change', 'blur']}],
|
||||
maxSignCount: [{required: false, message: '请输入每日最高人数', trigger: ['change', 'blur']}],
|
||||
travelAgencyPlace: [{required: true, message: '请选择旅行社地点', trigger: ['change', 'blur']}],
|
||||
},
|
||||
//目的地导入
|
||||
|
||||
+98
-3
@@ -171,6 +171,8 @@ layout("/layouts/platform.html"){
|
||||
<template #func>
|
||||
<!-- <el-button icon="el-icon-s-promotion" @click="openImport" size="small" type="primary">参加人员导入-->
|
||||
<!-- </el-button>-->
|
||||
<el-button icon="el-icon-upload" @click="openImport" size="small" type="primary">参加人员导入
|
||||
</el-button>
|
||||
<el-button icon="el-icon-s-promotion" size="small" type="primary" @click="noSignExport">
|
||||
导出未报名人员
|
||||
</el-button>
|
||||
@@ -282,6 +284,48 @@ layout("/layouts/platform.html"){
|
||||
<set-up-part ref="setUpRef"></set-up-part>
|
||||
|
||||
<edit-form ref="editRef" @refresh="doSearch"></edit-form>
|
||||
|
||||
<el-dialog
|
||||
title="参加人员导入"
|
||||
:visible.sync="importVisible"
|
||||
:close-on-click-modal="false"
|
||||
width="50%">
|
||||
<el-timeline>
|
||||
<el-timeline-item timestamp="下载模板" placement="top">
|
||||
<el-card>
|
||||
<el-button size="medium" type=""
|
||||
@click="downloadImportTemplate"
|
||||
icon="el-icon-download">下载模板
|
||||
</el-button>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
<el-timeline-item timestamp="上传文件" placement="top">
|
||||
<el-card>
|
||||
<el-form>
|
||||
<el-upload
|
||||
name="file"
|
||||
ref="upload"
|
||||
:on-remove="handleImportFileRemove"
|
||||
:on-change="handleImportFileChange"
|
||||
:auto-upload="false"
|
||||
:limit="1"
|
||||
:file-list="importData.fileList">
|
||||
<el-button size="medium" type="" icon="el-icon-upload" style="width: 200px">选择文件
|
||||
</el-button>
|
||||
<div class="el-upload__tip" slot="tip" style="color: #F56C6C">
|
||||
只能上传 xls/xlsx 文件,时间格式请书写为2008-10-1
|
||||
</div>
|
||||
</el-upload>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="importVisibleClose" :disabled="importLoading">取 消</el-button>
|
||||
<el-button type="primary" @click="doImport" :loading="importLoading">确定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
@@ -318,11 +362,17 @@ layout("/layouts/platform.html"){
|
||||
{prop: 'lineOrMaName', label: '线路'},
|
||||
{prop: 'times', label: '出行时间'},
|
||||
{prop: 'isFamily', label: '是否携带家属'},
|
||||
{prop: 'isTakePartIn', label: '是否参加'}
|
||||
{prop: 'isTakePartIn', label: '是否参加', sortable: true}
|
||||
],
|
||||
viewVisible: false,
|
||||
unionOptions: [],
|
||||
config: {}
|
||||
config: {},
|
||||
// 参加人员导入弹窗和文件上传状态。
|
||||
importVisible: false,
|
||||
importLoading: false,
|
||||
importData: {
|
||||
fileList: []
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -389,7 +439,52 @@ layout("/layouts/platform.html"){
|
||||
},
|
||||
|
||||
openImport() {
|
||||
|
||||
this.$set(this.importData, 'fileList', [])
|
||||
this.importVisible = true
|
||||
},
|
||||
handleImportFileRemove(file, fileList) {
|
||||
this.$set(this.importData, 'fileList', this.fileHandleRemove(file, fileList))
|
||||
},
|
||||
handleImportFileChange(file, fileList) {
|
||||
this.$set(this.importData, 'fileList', this.fileHandleChange(file, fileList, {type: ['xls', 'xlsx']}))
|
||||
},
|
||||
downloadImportTemplate() {
|
||||
window.open('/platform/theRapyRecuperation/user/query/downloadImportTemplate')
|
||||
},
|
||||
doImport() {
|
||||
if (this.importData.fileList.length === 0) {
|
||||
this.$message.warning('请选择文件!')
|
||||
return
|
||||
}
|
||||
const data = new FormData()
|
||||
this.importData.fileList.forEach((val) => {
|
||||
data.append('file', val.raw, val.raw.name)
|
||||
})
|
||||
this.importLoading = true
|
||||
$.ajax({
|
||||
url: '/platform/theRapyRecuperation/user/query/enrollImport',
|
||||
type: 'post',
|
||||
data: data,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
success: (resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.importVisible = false
|
||||
this.doSearch()
|
||||
} else {
|
||||
this.$message.warning(resp.msg)
|
||||
}
|
||||
this.importLoading = false
|
||||
},
|
||||
error: () => {
|
||||
this.$message.warning('导入失败')
|
||||
this.importLoading = false
|
||||
}
|
||||
})
|
||||
},
|
||||
importVisibleClose() {
|
||||
this.importVisible = false
|
||||
this.doSearch()
|
||||
},
|
||||
noSignExport() {
|
||||
window.open('/platform/theRapyRecuperation/schoolUnionUserQuery/noSignExport')
|
||||
|
||||
+122
-47
@@ -27,6 +27,16 @@ layout("/layouts/platform.html"){
|
||||
<h3 style="color: rgb(24, 103, 176);font-family: Microsoft YaHei;">疗休养配置</h3>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px;">
|
||||
<el-select v-model="currentYear" style="width: 160px;" @change="changeYear">
|
||||
<el-option v-for="item in yearOptions"
|
||||
:key="item"
|
||||
:label="item + '年度'"
|
||||
:value="item"></el-option>
|
||||
</el-select>
|
||||
<el-button type="primary" plain @click="copyLastYear" :loading="copyLoading">沿用去年配置</el-button>
|
||||
</div>
|
||||
|
||||
<el-form ref="addForm" :model="formData" :rules="formRules" label-width="170px">
|
||||
<vi-title title="基本配置"></vi-title>
|
||||
<el-form-item label="配置名称" prop="configName">
|
||||
@@ -326,10 +336,18 @@ layout("/layouts/platform.html"){
|
||||
const vue = new Vue({
|
||||
el: '#app',
|
||||
data() {
|
||||
const startYear = 2025
|
||||
const yearOptions = [startYear, startYear + 1, startYear + 2, startYear + 3, startYear + 4]
|
||||
const nowYear = new Date().getFullYear()
|
||||
const currentYear = yearOptions.includes(nowYear) ? nowYear : startYear
|
||||
return {
|
||||
currentYear: currentYear,
|
||||
yearOptions: yearOptions,
|
||||
activityGroupList: [],
|
||||
subLoading: false,
|
||||
copyLoading: false,
|
||||
formData: {
|
||||
configYear: currentYear,
|
||||
isSnLine: 0,
|
||||
isSwLine: 0,
|
||||
lots: [],
|
||||
@@ -384,6 +402,52 @@ layout("/layouts/platform.html"){
|
||||
'drawer-user-scope': httpVueLoader('/components/plugins/DrawerUserScope.vue?v=1.0.1'),
|
||||
},
|
||||
methods: {
|
||||
getDefaultFormData(configYear) {
|
||||
return {
|
||||
configYear: configYear,
|
||||
isSnLine: 0,
|
||||
isSwLine: 0,
|
||||
lots: [],
|
||||
configName: '智慧工会疗休养配置',
|
||||
outsideQuota: null,
|
||||
outsideQuotaProportion: 0,
|
||||
outsideNumber: null,
|
||||
travelFrequency: null,
|
||||
groupNumber: null,
|
||||
modifyDays: null,
|
||||
modifyNumber: null,
|
||||
allLineSignUpNumber: null,
|
||||
notice: null,
|
||||
provinceStartYear: null,
|
||||
bedInfo: true,
|
||||
familyInfo: 1,
|
||||
}
|
||||
},
|
||||
async changeYear() {
|
||||
await this.findOne()
|
||||
},
|
||||
async copyLastYear() {
|
||||
if (this.formData.id) {
|
||||
const confirm = await this.$confirm('当前年度已有配置,沿用去年配置会覆盖当前年度配置,是否继续?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
})
|
||||
if (confirm !== 'confirm') return
|
||||
}
|
||||
this.$set(this, 'copyLoading', true)
|
||||
try {
|
||||
const resp = await $.post(loc() + '/copyLastYear', {year: this.currentYear})
|
||||
if (resp.code === 0 && resp.data) {
|
||||
this.$notify.success({title: '成功', message: '已沿用去年配置'});
|
||||
await this.findOne()
|
||||
} else {
|
||||
this.$notify.warning({title: '提示', message: resp.msg || '上一年度暂无可沿用配置'});
|
||||
}
|
||||
} finally {
|
||||
this.$set(this, 'copyLoading', false)
|
||||
}
|
||||
},
|
||||
async deleteLotsRow(scope) {
|
||||
//this.lotDeleteList.push(scope.row.id);
|
||||
//console.log(this.lotDeleteList)
|
||||
@@ -424,72 +488,83 @@ layout("/layouts/platform.html"){
|
||||
spinner: 'el-icon-loading',
|
||||
background: COVER_LAYER_COLOR
|
||||
})
|
||||
const cloneData = clone(this.formData)
|
||||
try {
|
||||
const cloneData = clone(this.formData)
|
||||
cloneData.configYear = this.currentYear
|
||||
|
||||
if(this.formData.createLineTimes && this.formData.createLineTimes.length > 0) {
|
||||
cloneData.createLineStartTime = this.formData.createLineTimes[0]
|
||||
cloneData.createLineEndTime = this.formData.createLineTimes[1]
|
||||
}
|
||||
if(this.formData.createLineTimes && this.formData.createLineTimes.length > 0) {
|
||||
cloneData.createLineStartTime = this.formData.createLineTimes[0]
|
||||
cloneData.createLineEndTime = this.formData.createLineTimes[1]
|
||||
}
|
||||
|
||||
if(this.formData.implodeTimes && this.formData.implodeTimes.length > 0) {
|
||||
cloneData.implodeStartTime = this.formData.implodeTimes[0]
|
||||
cloneData.implodeEndTime = this.formData.implodeTimes[1]
|
||||
}
|
||||
if(this.formData.implodeTimes && this.formData.implodeTimes.length > 0) {
|
||||
cloneData.implodeStartTime = this.formData.implodeTimes[0]
|
||||
cloneData.implodeEndTime = this.formData.implodeTimes[1]
|
||||
}
|
||||
|
||||
if(this.formData.fragmentTimes && this.formData.fragmentTimes.length > 0) {
|
||||
cloneData.fragmentStartTime = this.formData.fragmentTimes[0]
|
||||
cloneData.fragmentEndTime = this.formData.fragmentTimes[1]
|
||||
}
|
||||
if(this.formData.fragmentTimes && this.formData.fragmentTimes.length > 0) {
|
||||
cloneData.fragmentStartTime = this.formData.fragmentTimes[0]
|
||||
cloneData.fragmentEndTime = this.formData.fragmentTimes[1]
|
||||
}
|
||||
|
||||
if(this.formData.fragmentPlayTimes && this.formData.fragmentPlayTimes.length > 0) {
|
||||
cloneData.fragmentPlayStartTime = this.formData.fragmentPlayTimes[0]
|
||||
cloneData.fragmentPlayEndTime = this.formData.fragmentPlayTimes[1]
|
||||
}
|
||||
if(this.formData.fragmentPlayTimes && this.formData.fragmentPlayTimes.length > 0) {
|
||||
cloneData.fragmentPlayStartTime = this.formData.fragmentPlayTimes[0]
|
||||
cloneData.fragmentPlayEndTime = this.formData.fragmentPlayTimes[1]
|
||||
}
|
||||
|
||||
if (this.formData.files && this.formData.files.length > 0) {
|
||||
cloneData.files = JSON.stringify(this.formData.files)
|
||||
}
|
||||
if (this.lotDeleteList && this.lotDeleteList.length > 0) {
|
||||
cloneData.lotDeleteList = JSON.stringify(this.lotDeleteList)
|
||||
}
|
||||
if (this.formData.lots && this.formData.lots.length > 0) {
|
||||
cloneData.lots = JSON.stringify(this.formData.lots)
|
||||
}
|
||||
cloneData.outsideQuotaProportion = cloneData.outsideQuotaProportion / 100
|
||||
const resp = await $.post(loc() + "/operation", cloneData)
|
||||
if (resp.code === 0){
|
||||
this.$notify.success({title: '成功', message: resp.msg});
|
||||
delete cloneData.createLineTimes
|
||||
delete cloneData.implodeTimes
|
||||
delete cloneData.fragmentTimes
|
||||
delete cloneData.fragmentPlayTimes
|
||||
|
||||
if (this.formData.files && this.formData.files.length > 0) {
|
||||
cloneData.files = JSON.stringify(this.formData.files)
|
||||
}
|
||||
if (this.lotDeleteList && this.lotDeleteList.length > 0) {
|
||||
cloneData.lotDeleteList = JSON.stringify(this.lotDeleteList)
|
||||
}
|
||||
if (this.formData.lots && this.formData.lots.length > 0) {
|
||||
cloneData.lots = JSON.stringify(this.formData.lots)
|
||||
}
|
||||
cloneData.outsideQuotaProportion = cloneData.outsideQuotaProportion / 100
|
||||
const resp = await $.post(loc() + "/operation", cloneData)
|
||||
if (resp.code === 0){
|
||||
this.$notify.success({title: '成功', message: resp.msg});
|
||||
await this.findOne()
|
||||
}else {
|
||||
this.$notify.error({title: '失败', message: resp.msg});
|
||||
}
|
||||
} finally {
|
||||
loading.close()
|
||||
this.subLoading = false
|
||||
}else {
|
||||
this.$notify.error({title: '失败', message: resp.msg});
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
async findOne() {
|
||||
await this.getActivityGroup()
|
||||
const {data} = await $.get(loc() + "/findOne")
|
||||
if (data.outsideQuotaProportion) {
|
||||
data.outsideQuotaProportion = data.outsideQuotaProportion * 1000 / 10
|
||||
const {data} = await $.get(loc() + "/findOne", {year: this.currentYear})
|
||||
const formData = data || this.getDefaultFormData(this.currentYear)
|
||||
this.$set(formData, 'configYear', this.currentYear)
|
||||
if (formData.outsideQuotaProportion) {
|
||||
this.$set(formData, 'outsideQuotaProportion', formData.outsideQuotaProportion * 1000 / 10)
|
||||
}
|
||||
|
||||
if(data.createLineStartTime && data.createLineEndTime) {
|
||||
data.createLineTimes = [data.createLineStartTime, data.createLineEndTime]
|
||||
if(formData.createLineStartTime && formData.createLineEndTime) {
|
||||
this.$set(formData, 'createLineTimes', [formData.createLineStartTime, formData.createLineEndTime])
|
||||
}
|
||||
if(data.implodeStartTime && data.implodeEndTime) {
|
||||
data.implodeTimes = [data.implodeStartTime, data.implodeEndTime]
|
||||
if(formData.implodeStartTime && formData.implodeEndTime) {
|
||||
this.$set(formData, 'implodeTimes', [formData.implodeStartTime, formData.implodeEndTime])
|
||||
}
|
||||
if(data.fragmentStartTime && data.fragmentEndTime) {
|
||||
data.fragmentTimes = [data.fragmentStartTime, data.fragmentEndTime]
|
||||
if(formData.fragmentStartTime && formData.fragmentEndTime) {
|
||||
this.$set(formData, 'fragmentTimes', [formData.fragmentStartTime, formData.fragmentEndTime])
|
||||
}
|
||||
if(data.fragmentPlayStartTime && data.fragmentPlayEndTime) {
|
||||
data.fragmentPlayTimes = [data.fragmentPlayStartTime, data.fragmentPlayEndTime]
|
||||
if(formData.fragmentPlayStartTime && formData.fragmentPlayEndTime) {
|
||||
this.$set(formData, 'fragmentPlayTimes', [formData.fragmentPlayStartTime, formData.fragmentPlayEndTime])
|
||||
}
|
||||
if (!data.lots) {
|
||||
data.lots = []
|
||||
if (!formData.lots) {
|
||||
this.$set(formData, 'lots', [])
|
||||
}
|
||||
this.formData = data
|
||||
this.$set(this, 'formData', formData)
|
||||
},
|
||||
async getActivityGroup() {
|
||||
const {data} = await $.get('/platform/activity/basic/scope/getActivityUserScopeGroup')
|
||||
|
||||
Reference in New Issue
Block a user