diff --git a/src/main/java/com/budwk/app/base/config/DataCenterProperties.java b/src/main/java/com/budwk/app/base/config/DataCenterProperties.java index 0d01d540..4c51feeb 100644 --- a/src/main/java/com/budwk/app/base/config/DataCenterProperties.java +++ b/src/main/java/com/budwk/app/base/config/DataCenterProperties.java @@ -1,17 +1,7 @@ package com.budwk.app.base.config; -import cn.hutool.core.util.StrUtil; -import cn.hutool.http.HttpRequest; -import cn.hutool.http.HttpUtil; -import cn.hutool.json.JSONObject; -import cn.hutool.json.JSONUtil; -import com.budwk.app.base.constant.RedisConstant; -import com.budwk.app.base.exception.BaseException; -import lombok.AllArgsConstructor; import lombok.Data; -import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import org.nutz.integration.jedis.RedisService; import org.nutz.ioc.impl.PropertiesProxy; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; @@ -33,19 +23,17 @@ public class DataCenterProperties { @Inject private PropertiesProxy conf; - @Inject - private RedisService redisService; - private String appId; private String tokenUrl; - private Map codes = new HashMap<>(); + private String key; + private String secret; private Map urls = new HashMap<>(); public void init() { Map all = conf.toMap(); - appId = all.get("data-center.app-id"); tokenUrl = all.get("data-center.token-url"); - extract(all, "data-center.codes.", codes); + key = all.get("data-center.key"); + secret = all.get("data-center.secret"); extract(all, "data-center.urls.", urls); } @@ -61,59 +49,4 @@ public class DataCenterProperties { } }); } - - /** - * 读取配置 - * @param key 接口类型枚举 - * @return 相关配置 - */ - public Credential credential(String key) { - String code = codes.get(key); - String url = urls.get(key); - if (code == null || url == null) { - throw new BaseException("未知业务: " + key); - } - String token = getToken(Integer.parseInt(code), key); - return new Credential(url, token); - } - - @Getter - @AllArgsConstructor - public static class Credential { - private String url; - private String token; - } - - - /** - * 获取token - * @param code - * @param keyPrefix - * @return token - */ - private String getToken(Integer code, String keyPrefix) { - String dateCenterPrefix = RedisConstant.DATE_CENTER_PREFIX + keyPrefix; - String token = redisService.get(dateCenterPrefix); - if (StrUtil.isNotBlank(token)) { - return token; - } else { - HttpRequest httpRequest = HttpUtil.createPost(tokenUrl); - Map reqBody = Map.of( - "appId", appId, - "code", code - ); - httpRequest.body(JSONUtil.toJsonStr(reqBody)); - JSONObject resp = JSONUtil.parseObj(httpRequest.execute().body()); - - if (resp.getInt("code") != 200) { - throw new BaseException("获取token失败: " + resp.getStr("msg")); - } else { - JSONObject data = resp.getJSONObject("data"); - String tokenStr = data.getStr("token"); - Integer expire = data.getInt("expire"); - redisService.setex(dateCenterPrefix, expire, tokenStr); - return tokenStr; - } - } - } } diff --git a/src/main/java/com/budwk/app/sys/models/Sys_user.java b/src/main/java/com/budwk/app/sys/models/Sys_user.java index e6f2b39b..abe39e7b 100644 --- a/src/main/java/com/budwk/app/sys/models/Sys_user.java +++ b/src/main/java/com/budwk/app/sys/models/Sys_user.java @@ -37,7 +37,7 @@ public class Sys_user extends BaseModel implements Serializable { @Column @Comment("工号") @ColDefine(type = ColType.VARCHAR, width = 120) - @DataCenterColumn(name = "工号", key = "JGH") + @DataCenterColumn(name = "工号", key = "ZGH") private String loginname; @Column @@ -108,13 +108,12 @@ public class Sys_user extends BaseModel implements Serializable { @Column @Comment("手机号码") @ColDefine(type = ColType.VARCHAR, width = 32) - @DataCenterColumn(name = "手机号码", key = "SJHM") + @DataCenterColumn(name = "手机号码", key = "YDDH") private String mobile; @Column @Comment("学历") @ColDefine(type = ColType.VARCHAR, width = 20) - @DataCenterColumn(name = "学历", key = "ZGXLM", dict = "USER_EDUCATION") private String education; @Column @@ -131,19 +130,16 @@ public class Sys_user extends BaseModel implements Serializable { @Column @Comment("岗位类别") @ColDefine(type = ColType.VARCHAR, width = 50) - @DataCenterColumn(name = "岗位类别", key = "JRGWDJM",dict = "USER_JOB_CATEGORY") private String jobCategory; @Column @Comment("职称") @ColDefine(type = ColType.VARCHAR, width = 50) - @DataCenterColumn(name = "职称", key = "DQZC", dict = "USER_PROFESSIONAL_TITLE") private String professionalTitle; @Column @Comment("职级") @ColDefine(type = ColType.VARCHAR, width = 50) - @DataCenterColumn(name = "取得职称等级码", key = "QDZCDJM", dict = "USER_PROFESSIONAL_LEVEL") private String professionalLevel; @Column @@ -179,7 +175,7 @@ public class Sys_user extends BaseModel implements Serializable { @Column @Comment("来校时间") @ColDefine(type = ColType.VARCHAR, width = 10) - @DataCenterColumn(name = "来校时间", key = "LXRQ") + @DataCenterColumn(name = "来校时间", key = "LXNY") private String arrivalAtSchoolDate; @Column @@ -195,7 +191,7 @@ public class Sys_user extends BaseModel implements Serializable { @Column @Comment("在职状态") @ColDefine(type = ColType.VARCHAR, width = 30) - @DataCenterColumn(name = "当前状态码", key = "DQZTM", dict = "USER_STATE") + @DataCenterColumn(name = "当前状态码", key = "DQZT", dict = "USER_STATE") private String userState; @Column @@ -207,19 +203,17 @@ public class Sys_user extends BaseModel implements Serializable { @Column @Comment("在岗情况") @ColDefine(type = ColType.VARCHAR, width = 30) - @DataCenterColumn(name = "在岗情况码", key = "ZGQKM", dict = "USER_DUTY_SITUATION") private String dutySituation; @Column @Comment("教职工类别码") @ColDefine(type = ColType.VARCHAR, width = 30) - @DataCenterColumn(name = "教职工类别码", key = "JZGLBM", dict = "USER_PERSON_TYPE") + @DataCenterColumn(name = "教职工类别码", key = "RYLX", dict = "USER_PERSON_TYPE") private String personType; @Column @Comment("编制类别码") @ColDefine(type = ColType.VARCHAR, width = 30) - @DataCenterColumn(name = "编制类别码", key = "BZLBM", dict = "USER_PREPARED_BY_TYPE") private String preparedBy; @Column @@ -269,7 +263,7 @@ public class Sys_user extends BaseModel implements Serializable { @Column @ColDefine(type = ColType.VARCHAR, width = 32) - @DataCenterColumn(name = "单位", key = "DWH") + @DataCenterColumn(name = "单位", key = "SZDWH") private String unitId; @Column diff --git a/src/main/java/com/budwk/app/sys/services/impl/SysDataDictPullServiceImpl.java b/src/main/java/com/budwk/app/sys/services/impl/SysDataDictPullServiceImpl.java index a0be87f2..9f21c06b 100644 --- a/src/main/java/com/budwk/app/sys/services/impl/SysDataDictPullServiceImpl.java +++ b/src/main/java/com/budwk/app/sys/services/impl/SysDataDictPullServiceImpl.java @@ -1,28 +1,14 @@ package com.budwk.app.sys.services.impl; -import cn.hutool.http.HttpRequest; -import cn.hutool.http.HttpUtil; -import cn.hutool.json.JSONObject; -import cn.hutool.json.JSONUtil; -import com.budwk.app.base.config.DataCenterProperties; +import com.budwk.app.base.exception.BaseException; import com.budwk.app.base.service.impl.BaseServiceImpl; -import com.budwk.app.sys.models.Sys_data_dict; import com.budwk.app.sys.models.Sys_dict; import com.budwk.app.sys.services.SysDataDictPullService; -import com.budwk.app.sys.services.SysDictService; import lombok.extern.slf4j.Slf4j; 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.json.Json; -import org.nutz.lang.util.NutMap; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; /** * @version 1.0 @@ -35,14 +21,6 @@ import java.util.Map; @IocBean(args = {"refer:dao"}) public class SysDataDictPullServiceImpl extends BaseServiceImpl implements SysDataDictPullService { - @Inject - private DataCenterProperties dcPro; - @Inject - private SysDictService dictService; - - // code - private final static String KEY_PREFIX = "user_"; - public SysDataDictPullServiceImpl(Dao dao) { super(dao); } @@ -50,57 +28,7 @@ public class SysDataDictPullServiceImpl extends BaseServiceImpl implem @Override @Aop(TransAop.READ_COMMITTED) public void pullDataDict() { - Map urls = dcPro.getUrls(); - List list = urls.keySet().stream().filter(o -> o.startsWith(KEY_PREFIX.toUpperCase())).toList(); - - List dataDictList = new ArrayList<>(); - - for (String key : list) { - // 获取配置 - DataCenterProperties.Credential credential = dcPro.credential(key); - String url = credential.getUrl(); - String token = credential.getToken(); - - // 请求数据 - HttpRequest httpRequest = HttpUtil.createPost(url); - httpRequest.header("Content-Type", "application/json"); - httpRequest.header("X-H3C-TOKEN", token); - httpRequest.body(JSONUtil.toJsonStr(Map.of())); - - String resBody = httpRequest.execute().body(); - JSONObject jsonBody = JSONUtil.parseObj(resBody); - - if (jsonBody.getInt("code") != 200) { - log.error("获取{}字典数据失败,错误码: {};错误原因:{}", key, jsonBody.getInt("code"), jsonBody.getStr("msg")); - } - List data = Json.fromJsonAsList(NutMap.class, jsonBody.getStr("data")); - - // 先删除 - Sys_dict sysDict = dao().fetch(Sys_dict.class, Cnd.where(Sys_dict::getCode, "=", key)); - dao().clear(Sys_dict.class, Cnd.where(Sys_dict::getParentId, "=", sysDict.getId())); - - // 组合数据 - data.forEach(o -> { - Sys_dict dict = new Sys_dict(); - dict.setParentId(sysDict.getId()); - - dict.setPath(dictService.getSubPath("sys_dict", "path", sysDict.getPath())); - dict.setName(o.getString("xbname")); - dict.setCode(o.getString("xbname")); - dict.setDisabled(false); - dict.setHasChildren(false); - dao().insert(dict); - - Sys_data_dict sysDataDict = new Sys_data_dict(); - sysDataDict.setParentCode(key); - sysDataDict.setCode(o.getString("xbcode")); - sysDataDict.setName(o.getString("xbname")); - - dataDictList.add(sysDataDict); - - }); - - dao().insert(dataDictList); - } + // 旧数据中心字典接口已停用,避免继续误调旧token/code模式接口。 + throw new BaseException("旧数据中心字典拉取接口已停用"); } } diff --git a/src/main/java/com/budwk/app/sys/services/impl/SysDataUnitPullServiceImpl.java b/src/main/java/com/budwk/app/sys/services/impl/SysDataUnitPullServiceImpl.java index 6fb1cb84..c42ecf15 100644 --- a/src/main/java/com/budwk/app/sys/services/impl/SysDataUnitPullServiceImpl.java +++ b/src/main/java/com/budwk/app/sys/services/impl/SysDataUnitPullServiceImpl.java @@ -1,32 +1,33 @@ package com.budwk.app.sys.services.impl; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.URLUtil; +import cn.hutool.http.Header; import cn.hutool.http.HttpRequest; import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONArray; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.budwk.app.base.config.DataCenterProperties; import com.budwk.app.base.exception.BaseException; import com.budwk.app.base.service.impl.BaseServiceImpl; -import com.budwk.app.sys.enums.Api2UnitFiledMap; import com.budwk.app.sys.models.Sys_unit; import com.budwk.app.sys.services.SysDataUnitPullService; -import com.budwk.app.sys.utils.DataCenterUtil; import lombok.extern.slf4j.Slf4j; import org.nutz.aop.interceptor.ioc.TransAop; import org.nutz.dao.Dao; -import org.nutz.dao.Sqls; -import org.nutz.dao.sql.Sql; import org.nutz.ioc.aop.Aop; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; import java.nio.charset.StandardCharsets; import java.util.ArrayList; -import java.util.Arrays; +import java.util.Comparator; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; /** @@ -40,8 +41,11 @@ import java.util.stream.Collectors; @IocBean(args = {"refer:dao"}) public class SysDataUnitPullServiceImpl extends BaseServiceImpl implements SysDataUnitPullService { + private static final String DMP_UNIT_KEY = "unit"; + private static final int DMP_PAGE_SIZE = 1000; + @Inject - private DataCenterProperties dcPro; + private DataCenterProperties dataCenterProperties; public SysDataUnitPullServiceImpl(Dao dao) { super(dao); @@ -51,11 +55,7 @@ public class SysDataUnitPullServiceImpl extends BaseServiceImpl implem @Override @Aop(TransAop.READ_COMMITTED) public void updateUnits() { - Sql sql = Sqls.create("select id from sys_unit group by id"); - sql.setCallback(Sqls.callback.strList()); - dao().execute(sql); - List unitIds = sql.getList(String.class); - updateUnits(unitIds); + updateUnits(List.of()); } /** @@ -64,65 +64,235 @@ public class SysDataUnitPullServiceImpl extends BaseServiceImpl implem @Override @Aop(TransAop.READ_COMMITTED) public void updateUnits(List unitIds) { - // token url参数 - Map tokenParams = Map.of( - "grant_type", "password", - "scope", "read", - "username", "3848A3C738EFBB2C2E76E0D59B419DACA16A80F3AB0354CD", - "password", "3848A3C738EFBB2C2E76E0D59B419DACA16A80F3AB0354CD" - ); + String accessToken = getDmpAccessToken(); + List rawDataList = distinctDmpUnits(pullDmpUnits(getDmpUnitUrl(), accessToken)); + Set parentCodes = rawDataList.stream() + .map(this::getNormalizedParentCode) + .filter(StrUtil::isNotBlank) + .collect(Collectors.toSet()); - String params = URLUtil.buildQuery(tokenParams, StandardCharsets.UTF_8); - String tokenUrl = "https://bd.whcp.edu.cn/sjjkfwpt/api/bd-api/oauth/token" + (StrUtil.isBlank(params) ? "" : "?" + params); - HttpRequest tokenHttpRequest = HttpUtil.createPost(tokenUrl); - tokenHttpRequest.header("Content-Type", "application/json"); - tokenHttpRequest.header("Authorization", "Basic MTE0OjU5M2UwNGI4YTE4NWRlMzkyNjY4ZmY0ZDJhZGMyZTEw"); + rawDataList.stream() + .sorted(Comparator.comparingInt(this::getUnitLevel)) + .forEach(raw -> saveOrUpdateUnit(raw, parentCodes)); - log.info("请求token: {}", tokenHttpRequest); - String tokenResBody = tokenHttpRequest.execute().body(); + log.info("信息中心单位数据同步完成,本次接口返回单位数量: {},待检查单位编码数量: {}", rawDataList.size(), unitIds == null ? 0 : unitIds.size()); + } - log.info("请求token结果: {}", tokenResBody); + /** + * 获取信息中心开放平台访问令牌。 + * 参数来自 data-center.token-url、data-center.key、data-center.secret; + * 返回值为后续单位接口 body 中 access_token 使用的字符串。 + */ + private String getDmpAccessToken() { + String tokenUrl = dataCenterProperties.getTokenUrl(); + if (StrUtil.isBlank(tokenUrl)) { + throw new BaseException("未配置信息中心token地址: data-center.token-url"); + } + String key = dataCenterProperties.getKey(); + if (StrUtil.isBlank(key)) { + throw new BaseException("未配置信息中心token key: data-center.key"); + } + String secret = dataCenterProperties.getSecret(); + if (StrUtil.isBlank(secret)) { + throw new BaseException("未配置信息中心token secret: data-center.secret"); + } + + String params = URLUtil.buildQuery(Map.of("key", key, "secret", secret), StandardCharsets.UTF_8); + String tokenResBody = HttpUtil.createGet(tokenUrl + "?" + params).execute().body(); JSONObject tokenJsonBody = JSONUtil.parseObj(tokenResBody); - - // 请求数据 - HttpRequest httpRequest = HttpUtil.createGet("https://bd.whcp.edu.cn/sjjkfwpt/api/bd-api/get/YXSDWJBSJZLB"); - httpRequest.form(Map.of("access_token", tokenJsonBody.getStr("access_token"), "pageSize", 100, "page", 1)); - log.info("请求单位数据: {}", httpRequest); - String resBody = httpRequest.execute().body(); - log.info("请求单位数据结果: {}", resBody); - JSONObject jsonBody = JSONUtil.parseObj(resBody); - - if (jsonBody.getInt("code") != 200) { - throw new BaseException("获取单位数据失败,错误码: " + jsonBody.getInt("code") + ";错误原因:" + jsonBody.getStr("msg")); + String accessToken = tokenJsonBody.getStr("access_token"); + if (StrUtil.isBlank(accessToken) && tokenJsonBody.getJSONObject("result") != null) { + accessToken = tokenJsonBody.getJSONObject("result").getStr("access_token"); } + if (StrUtil.isBlank(accessToken) && tokenJsonBody.getJSONObject("data") != null) { + accessToken = tokenJsonBody.getJSONObject("data").getStr("access_token"); + } + if (StrUtil.isBlank(accessToken)) { + accessToken = tokenJsonBody.getStr("token"); + } + if (StrUtil.isBlank(accessToken)) { + throw new BaseException("获取信息中心token失败"); + } + return accessToken; + } - // 需要新增的单位 - List insertList = new ArrayList<>(); - // 需要更新的单位 - List updateList = new ArrayList<>(); + /** + * 读取信息中心单位接口地址。 + * 配置项为 data-center.urls.unit,返回值为单位全量接口URL。 + */ + private String getDmpUnitUrl() { + String url = dataCenterProperties.getUrls().get(DMP_UNIT_KEY); + if (StrUtil.isBlank(url)) { + throw new BaseException("未配置信息中心单位接口地址: data-center.urls." + DMP_UNIT_KEY); + } + return url; + } - // 拉取过来的数据,全部的单位,一万四千多条,这里只保留 “部门类” - List data = jsonBody.getJSONArray("data").stream() - .map(o -> (JSONObject) o) - .toList(); + /** + * 分页拉取单位接口数据。 + * 入参 url 为单位接口地址,accessToken 为信息中心令牌; + * 请求 body 包含 access_token、per_page、page; + * 返回值为 result.data 合并后的单位原始 JSON 列表。 + */ + private List pullDmpUnits(String url, String accessToken) { + List rawDataList = new ArrayList<>(); + int page = 1; + int maxPage = 1; + int total = 0; - Map api2db = Arrays.stream(Api2UnitFiledMap.values()) - .collect(Collectors.toMap(e -> e.apiField, e -> e.dbColumn)); - for (JSONObject row : data) { - Sys_unit unit = DataCenterUtil.mapJsonToBean(row, Sys_unit.class, api2db); - unit.setUnitcode(unit.getId()); - unit.setUnitTypeCode(1); + do { + HttpRequest httpRequest = HttpUtil.createPost(url); + httpRequest.header(Header.CONTENT_TYPE, "application/json"); + httpRequest.body(JSONUtil.toJsonStr(Map.of( + "access_token", accessToken, + "per_page", String.valueOf(DMP_PAGE_SIZE), + "page", String.valueOf(page) + ))); - if (unitIds.contains(unit.getId())) { - // 存在则更新 - updateList.add(unit); - } else { - // 不存在则新增 - insertList.add(unit); + log.info("请求信息中心单位数据,第{}页: {}", page, httpRequest); + String resBody = httpRequest.execute().body(); + log.info("请求信息中心单位数据第{}页结果: {}", page, resBody); + JSONObject jsonBody = JSONUtil.parseObj(resBody); + + if (jsonBody.getInt("code") != 10000) { + throw new BaseException("获取信息中心单位数据失败,错误码: " + jsonBody.getInt("code") + ";错误原因:" + jsonBody.getStr("message")); } + + JSONObject result = jsonBody.getJSONObject("result"); + if (result == null) { + break; + } + total = result.getInt("total", total); + maxPage = result.getInt("max_page", maxPage); + JSONArray data = result.getJSONArray("data"); + if (CollUtil.isNotEmpty(data)) { + rawDataList.addAll(data.stream().map(v -> (JSONObject) v).toList()); + } + log.info("信息中心单位数据拉取进度: {}/{}", rawDataList.size(), total); + page++; + } while (page <= maxPage); + + return rawDataList; + } + + /** + * 按单位代码去重,避免接口分页或源数据重复导致同一 DWDM 重复写入。 + */ + private List distinctDmpUnits(List rawDataList) { + Map unitMap = new LinkedHashMap<>(); + for (JSONObject raw : rawDataList) { + String unitCode = raw.getStr("DWDM"); + if (StrUtil.isBlank(unitCode)) { + continue; + } + unitMap.putIfAbsent(unitCode, raw); } - log.info("本次拉取单位数据,新增{}条,更新{}条", insertList.size(), updateList.size()); - dao().insert(insertList); - dao().update(updateList); + return new ArrayList<>(unitMap.values()); + } + + /** + * 保存或更新单位。 + * DWDM 对应系统单位 id/unitcode,DWMC 对应名称,LSDWH 对应父级单位号,DWCC 对应信息中心原始单位层级。 + */ + private void saveOrUpdateUnit(JSONObject raw, Set parentCodes) { + String unitCode = raw.getStr("DWDM"); + if (StrUtil.isBlank(unitCode)) { + return; + } + + String parentId = getParentId(unitCode, getNormalizedParentCode(raw)); + Sys_unit unit = fetch(unitCode); + if (unit == null) { + unit = new Sys_unit(); + unit.setId(unitCode); + unit.setUnitcode(unitCode); + unit.setPath(getUnitPath(parentId)); + setUnitValue(unit, raw, parentId, parentCodes); + dao().fastInsert(unit); + } else { + setUnitValue(unit, raw, parentId, parentCodes); + if (StrUtil.isBlank(unit.getPath())) { + unit.setPath(getUnitPath(parentId)); + } + dao().updateIgnoreNull(unit); + } + } + + /** + * 根据接口字段给系统单位赋值。 + * DWYXBS 表示单位是否有效,DWCC 表示信息中心原始单位层次; + * 系统内置根单位占用一级层级,因此保存时统一将 DWCC 加一。 + */ + private void setUnitValue(Sys_unit unit, JSONObject raw, String parentId, Set parentCodes) { + String unitCode = raw.getStr("DWDM"); + String unitName = raw.getStr("DWMC"); + String aliasName = StrUtil.blankToDefault(raw.getStr("DWJC"), unitName); + Integer rawUnitLevel = raw.getInt("DWCC"); + Integer unitLevel = getNormalizedUnitLevel(raw); + boolean availableBusinessUnit = "是".equals(raw.getStr("DWYXBS")) && Integer.valueOf(2).equals(rawUnitLevel); + + unit.setParentId(parentId); + unit.setName(unitName); + unit.setAliasName(aliasName); + unit.setUnitcode(unitCode); + unit.setAddress(raw.getStr("DWDZ")); + unit.setUnitLevel(unitLevel); + unit.setUnitType(raw.getStr("DWLB")); + unit.setUnitTypeCode(availableBusinessUnit ? 1 : 0); + unit.setHasChildren(parentCodes.contains(unitCode)); + } + + /** + * 计算父级单位号。 + * LSDWH 为空、等于自身或本地不存在该父级时按根级处理;系统根单位 0 允许直接作为父级。 + */ + private String getParentId(String unitCode, String parentCode) { + if (StrUtil.isBlank(parentCode) || unitCode.equals(parentCode)) { + return ""; + } + if ("0".equals(parentCode)) { + return parentCode; + } + Sys_unit parentUnit = fetch(parentCode); + return parentUnit == null ? "" : parentCode; + } + + /** + * 根据父级 path 生成当前单位 path。 + * 父级为空时生成根节点 path,父级存在时在父级 path 下生成子节点 path。 + */ + private String getUnitPath(String parentId) { + String parentPath = ""; + if (StrUtil.isNotBlank(parentId)) { + Sys_unit parentUnit = fetch(parentId); + parentPath = parentUnit == null ? "" : parentUnit.getPath(); + } + return getSubPath("sys_unit", "path", parentPath); + } + + /** + * 信息中心一级单位应挂在系统根单位 0 下,其余单位沿用接口返回的 LSDWH。 + */ + private String getNormalizedParentCode(JSONObject raw) { + Integer rawUnitLevel = raw.getInt("DWCC"); + if (Integer.valueOf(1).equals(rawUnitLevel)) { + return "0"; + } + return raw.getStr("LSDWH"); + } + + /** + * 系统内置根单位占用一级层级,信息中心单位层次入库时统一加一。 + */ + private Integer getNormalizedUnitLevel(JSONObject raw) { + Integer rawUnitLevel = raw.getInt("DWCC"); + return rawUnitLevel == null ? null : rawUnitLevel + 1; + } + + /** + * DWCC 为信息中心单位层次,排序时用于尽量先处理父级单位,便于后续子级找到父节点。 + */ + private int getUnitLevel(JSONObject raw) { + return raw.getInt("DWCC", 0); } } diff --git a/src/main/java/com/budwk/app/sys/services/impl/SysDataUserPullServiceImpl.java b/src/main/java/com/budwk/app/sys/services/impl/SysDataUserPullServiceImpl.java index f2d38c82..660e15c3 100644 --- a/src/main/java/com/budwk/app/sys/services/impl/SysDataUserPullServiceImpl.java +++ b/src/main/java/com/budwk/app/sys/services/impl/SysDataUserPullServiceImpl.java @@ -50,6 +50,10 @@ import java.util.stream.Collectors; @Slf4j public class SysDataUserPullServiceImpl extends BaseServiceImpl implements SysDataUserPullService { + private static final String DMP_TEACHER_KEY = "teacher"; + private static final String DMP_DISPATCH_KEY = "dispatch"; + private static final int DMP_PAGE_SIZE = 1000; + @Inject private SysDictService sysDictService; @Inject @@ -115,48 +119,14 @@ public class SysDataUserPullServiceImpl extends BaseServiceImpl // 字典码表Map,key为父级编码,value为子级字典列表 Map> dataDictMap = dataDictList.stream().collect(Collectors.groupingBy(Sys_data_dict::getParentCode)); - // token url参数 - Map tokenParams = Map.of( - "grant_type", "password", - "scope", "read", - "username", "3848A3C738EFBB2C2E76E0D59B419DACA16A80F3AB0354CD", - "password", "3848A3C738EFBB2C2E76E0D59B419DACA16A80F3AB0354CD" - ); - - String params = URLUtil.buildQuery(tokenParams, StandardCharsets.UTF_8); - String tokenUrl = "https://bd.whcp.edu.cn/sjjkfwpt/api/bd-api/oauth/token" + (StrUtil.isBlank(params) ? "" : "?" + params); - HttpRequest tokenHttpRequest = HttpUtil.createPost(tokenUrl); - tokenHttpRequest.header("Content-Type", "application/json"); - tokenHttpRequest.header("Authorization", "Basic MTE0OjU5M2UwNGI4YTE4NWRlMzkyNjY4ZmY0ZDJhZGMyZTEw"); - - log.info("请求token: {}", tokenHttpRequest); - String tokenResBody = tokenHttpRequest.execute().body(); - log.info("请求token结果: {}", tokenResBody); - - JSONObject tokenJsonBody = JSONUtil.parseObj(tokenResBody); - - // 请求数据 - HttpRequest httpRequest = HttpUtil.createGet("https://bd.whcp.edu.cn/sjjkfwpt/api/bd-api/get/JZGJCSJZLB"); - httpRequest.form(Map.of("access_token", tokenJsonBody.getStr("access_token"), "pageSize", 5000, "page", 1)); - - log.info("请求人员数据: {}", httpRequest); - String resBody = httpRequest.execute().body(); - log.info("请求人员数据结果: {}", resBody); - JSONObject jsonBody = JSONUtil.parseObj(resBody); - - if (jsonBody.getInt("code") != 200) { - throw new BaseException("获取人员数据失败,错误码: " + jsonBody.getInt("code") + ";错误原因:" + jsonBody.getStr("msg")); - } - + String accessToken = getDmpAccessToken(); List rawDataList = new ArrayList<>(); - JSONArray data = jsonBody.getJSONArray("data"); + rawDataList.addAll(pullDmpUsers(getDmpUrl(DMP_TEACHER_KEY), accessToken, "在岗教职工")); + rawDataList.addAll(pullDmpUsers(getDmpUrl(DMP_DISPATCH_KEY), accessToken, "在岗劳务派遣")); + rawDataList = distinctDmpUsers(rawDataList); - if (!data.isEmpty()) { - // 先收集所有原始数据 - rawDataList = data.stream().map(v -> (JSONObject) v).toList(); - log.info("数据拉取进度: {}/{}", rawDataList.size(), jsonBody.getInt("total")); - } else { - log.warn("当前未获取到数据"); + if (rawDataList.isEmpty()) { + log.warn("当前未获取到人员数据"); } Date nowDate = new Date(); @@ -172,14 +142,16 @@ public class SysDataUserPullServiceImpl extends BaseServiceImpl if (mapping.field.getType() == String.class) { if (StrUtil.isNotBlank(mapping.dict)) { - Sys_data_dict sysDataDict = dataDictMap.get(mapping.dict).stream() - .filter(v -> raw.getStr(mapping.key) - .equals(v.getCode())).findFirst().orElse(new Sys_data_dict()); - mapping.field.set(sysUser, sysDataDict.getName()); + List sysDataDicts = dataDictMap.getOrDefault(mapping.dict, Collections.emptyList()); + Sys_data_dict sysDataDict = sysDataDicts.stream() + .filter(v -> raw.getStr(mapping.key, "").equals(v.getCode())) + .findFirst().orElse(new Sys_data_dict()); + mapping.field.set(sysUser, StrUtil.blankToDefault(sysDataDict.getName(), raw.getStr(mapping.key))); } else { // 特殊处理 根据身份证号获取性别和出生年月 - if ("SFZJH".equals(mapping.field.getName())) { + if ("SFZJH".equals(mapping.key)) { String idCard = raw.getStr(mapping.key); + mapping.field.set(sysUser, idCard); // 设置出生年月 try { sysUser.setBirthday(IdcardUtil.getBirthDate(idCard)); @@ -206,10 +178,12 @@ public class SysDataUserPullServiceImpl extends BaseServiceImpl log.info("用户数据初始化完成,等待插入,当前数据条数{}", latestSourceList.size()); // 插入到数据库 - dao().insert(latestSourceList); + if (CollUtil.isNotEmpty(latestSourceList)) { + dao().insert(latestSourceList); + } // 人员的单位数据和数据库的单位数据比较,如果人员里面有单位不存在,去更新单位数据 - List sourceUnitIds = latestSourceList.stream().map(Sys_user_source::getUnitId).distinct().toList(); + List sourceUnitIds = latestSourceList.stream().map(Sys_user_source::getUnitId).filter(StrUtil::isNotBlank).distinct().toList(); Sql sql = Sqls.create("select id from sys_unit group by id"); sql.setCallback(Sqls.callback.strList()); dao().execute(sql); @@ -225,6 +199,122 @@ public class SysDataUserPullServiceImpl extends BaseServiceImpl } } + /** + * 获取信息中心开放平台访问令牌。 + * 返回值为接口后续调用使用的 access_token 字符串。 + */ + private String getDmpAccessToken() { + String tokenUrl = dataCenterProperties.getTokenUrl(); + if (StrUtil.isBlank(tokenUrl)) { + throw new BaseException("未配置信息中心token地址: data-center.token-url"); + } + String key = dataCenterProperties.getKey(); + if (StrUtil.isBlank(key)) { + throw new BaseException("未配置信息中心token key: data-center.key"); + } + String secret = dataCenterProperties.getSecret(); + if (StrUtil.isBlank(secret)) { + throw new BaseException("未配置信息中心token secret: data-center.secret"); + } + // 信息中心token接口要求key和secret通过URL参数传入,配置中分开维护,调用时统一组装。 + String params = URLUtil.buildQuery(Map.of("key", key, "secret", secret), StandardCharsets.UTF_8); + HttpRequest tokenHttpRequest = HttpUtil.createGet(tokenUrl + "?" + params); + log.info("请求信息中心token: {}", tokenHttpRequest); + String tokenResBody = tokenHttpRequest.execute().body(); + log.info("请求信息中心token结果: {}", tokenResBody); + + JSONObject tokenJsonBody = JSONUtil.parseObj(tokenResBody); + String accessToken = tokenJsonBody.getStr("access_token"); + if (StrUtil.isBlank(accessToken) && tokenJsonBody.getJSONObject("result") != null) { + accessToken = tokenJsonBody.getJSONObject("result").getStr("access_token"); + } + if (StrUtil.isBlank(accessToken) && tokenJsonBody.getJSONObject("data") != null) { + accessToken = tokenJsonBody.getJSONObject("data").getStr("access_token"); + } + if (StrUtil.isBlank(accessToken)) { + accessToken = tokenJsonBody.getStr("token"); + } + if (StrUtil.isBlank(accessToken)) { + throw new BaseException("获取信息中心token失败"); + } + return accessToken; + } + + /** + * 读取信息中心人员接口地址。 + * 入参 key 对应 data-center.urls 下的配置项,返回值为具体接口URL。 + */ + private String getDmpUrl(String key) { + String url = dataCenterProperties.getUrls().get(key); + if (StrUtil.isBlank(url)) { + throw new BaseException("未配置信息中心人员接口地址: data-center.urls." + key); + } + return url; + } + + /** + * 分页拉取单个人员接口数据。 + * 入参 url 为人员接口地址,accessToken 为信息中心令牌,sourceName 用于日志区分接口来源。 + * 返回值为该接口所有分页合并后的原始人员 JSON 列表。 + */ + private List pullDmpUsers(String url, String accessToken, String sourceName) { + List rawDataList = new ArrayList<>(); + int page = 1; + int maxPage = 1; + int total = 0; + + do { + // 信息中心接口单页最多返回1000条,按page循环拉完当前接口全部数据。 + HttpRequest httpRequest = HttpUtil.createPost(url); + httpRequest.header(Header.CONTENT_TYPE, "application/json"); + httpRequest.body(JSONUtil.toJsonStr(Map.of( + "access_token", accessToken, + "per_page", String.valueOf(DMP_PAGE_SIZE), + "page", String.valueOf(page) + ))); + + log.info("请求{}人员数据,第{}页: {}", sourceName, page, httpRequest); + String resBody = httpRequest.execute().body(); + log.info("请求{}人员数据第{}页结果: {}", sourceName, page, resBody); + JSONObject jsonBody = JSONUtil.parseObj(resBody); + + if (jsonBody.getInt("code") != 10000) { + throw new BaseException("获取" + sourceName + "人员数据失败,错误码: " + jsonBody.getInt("code") + ";错误原因:" + jsonBody.getStr("message")); + } + + JSONObject result = jsonBody.getJSONObject("result"); + if (result == null) { + break; + } + total = result.getInt("total", total); + maxPage = result.getInt("max_page", maxPage); + JSONArray data = result.getJSONArray("data"); + if (CollUtil.isNotEmpty(data)) { + rawDataList.addAll(data.stream().map(v -> (JSONObject) v).toList()); + } + log.info("{}人员数据拉取进度: {}/{}", sourceName, rawDataList.size(), total); + page++; + } while (page <= maxPage); + + return rawDataList; + } + + /** + * 合并两个接口数据时按职工号去重,避免同一批次出现重复人员。 + * 返回值保持第一次出现的数据,用于后续统一映射入库。 + */ + private List distinctDmpUsers(List rawDataList) { + Map userMap = new LinkedHashMap<>(); + for (JSONObject raw : rawDataList) { + String loginName = raw.getStr("ZGH"); + if (StrUtil.isBlank(loginName)) { + loginName = raw.getStr("ID"); + } + userMap.putIfAbsent(loginName, raw); + } + return new ArrayList<>(userMap.values()); + } + @Async private void updateDict(List userSources) { //判断是否要更新在职状态字典 @@ -388,70 +478,7 @@ public class SysDataUserPullServiceImpl extends BaseServiceImpl @Override public Map pullFinance() { - // 获取配置文件 接口地址 和 token - DataCenterProperties.Credential credential = dataCenterProperties.credential("finance"); - String url = credential.getUrl(); - String token = credential.getToken(); - - List rawDataList = new ArrayList<>(); - String year = String.valueOf(DateUtil.thisYear()); - String month = String.valueOf(DateUtil.thisMonth() + 1); - - int pageNum = 1; - int pageSize = 1000; - int totalPages = 1; - int totalCount = 0; - do { - HttpRequest httpRequest = HttpUtil.createPost(url); - httpRequest.header(Header.CONTENT_TYPE, "application/json"); - httpRequest.header("Accept-Encoding", "gzip, deflate, br"); - httpRequest.header("X-H3C-TOKEN", token); - Map reqBody = Map.of( - "nf", year, - "yf", month, - "pageNum", pageNum, - "pageSize", pageSize - ); - httpRequest.body(JSONUtil.toJsonStr(reqBody)); - - JSONObject resp = JSONUtil.parseObj(httpRequest.execute().body()); - if (resp.getInt("code") != 0) { - throw new BaseException("获取财务数据失败,错误码: " + resp.getInt("code") + ";错误原因:" + resp.getStr("msg")); - } - - JSONObject data = resp.getJSONObject("data"); - if (pageNum == 1) { - totalCount = data.getInt("total", 0); -// totalPages = data.getInt("pages", 0); - totalPages = (int) Math.ceil((double) totalCount / pageSize); - } - - JSONArray records = data.getJSONArray("records"); - if (CollUtil.isNotEmpty(records)) { - rawDataList.addAll(records.stream().map(v -> (JSONObject) v).toList()); - log.info("数据拉取进度: {}/{}", rawDataList.size(), totalCount); - } - pageNum++; // 只加页码 - } while (pageNum <= totalPages); - - // 接口数据输出 - log.info("数据拉取结果: {}", rawDataList); - - // 最后结果数据 - Map result = new HashMap<>(); - - Map api2db = Arrays.stream(Api2FinanceFiledMap.values()) - .collect(Collectors.toMap(e -> e.apiField, e -> e.dbColumn)); - - for (JSONObject row : rawDataList) { - NutMap nutMap = DataCenterUtil.mapJsonToBean(row, NutMap.class, api2db); - result.put(nutMap.getString("loginname"), nutMap); - } - - // 输出结果 - log.info("财务数据拉取转换结果: {}", result); - - log.info("数据拉取完成,共拉取 {} 条数据", result.size()); - return result; + // 旧数据中心财务接口已停用,避免继续调用旧token/code模式接口。 + throw new BaseException("旧数据中心财务拉取接口已停用"); } } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/siteCug/service/impl/SiteCugApplyServiceImpl.java b/src/main/java/com/budwk/app/zhgh/dayofficework/siteCug/service/impl/SiteCugApplyServiceImpl.java index bb248871..4d1c9103 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/siteCug/service/impl/SiteCugApplyServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/siteCug/service/impl/SiteCugApplyServiceImpl.java @@ -80,6 +80,10 @@ public class SiteCugApplyServiceImpl extends BaseServiceImpl imple if (!Boolean.TRUE.equals(siteInfo.getState())) { return Map.of(false, "该场地未开启预约"); } + Map termDateRangeValidate = validateTermDateRange(apply, siteInfo); + if (termDateRangeValidate.containsKey(false)) { + return termDateRangeValidate; + } if (apply.getJoinCount() == null || apply.getJoinCount() <= 0) { return Map.of(false, "预约人数必须大于0"); } @@ -135,6 +139,26 @@ public class SiteCugApplyServiceImpl extends BaseServiceImpl imple return Map.of(true, ""); } + // 分工会预约必须落在后台配置的本学期时间内,批量预约逐条复用该校验防止接口越权提交学期外日期 + private Map validateTermDateRange(SiteCugApply apply, SiteCugInfo siteInfo) { + if (!StrUtil.equals(apply.getReserveType(), "union") + || StrUtil.hasBlank(siteInfo.getTermStartDate(), siteInfo.getTermEndDate())) { + return Map.of(true, ""); + } + try { + DateTime reserveStart = DateUtil.parseDateTime(apply.getReserveStartTime()); + DateTime reserveEnd = DateUtil.parseDateTime(apply.getReserveEndTime()); + Date termStart = DateUtil.beginOfDay(DateUtil.parseDate(siteInfo.getTermStartDate())); + Date termEnd = DateUtil.endOfDay(DateUtil.parseDate(siteInfo.getTermEndDate())); + if (reserveStart.isBefore(termStart) || reserveEnd.isAfter(termEnd)) { + return Map.of(false, "预约时间必须在本学期时间范围内"); + } + return Map.of(true, ""); + } catch (Exception e) { + return Map.of(false, "本学期时间配置有误,请联系管理员处理"); + } + } + @Override public Map occupyRecord(String id, String message, boolean sendMsg) { if (StrUtil.isBlank(message)) { diff --git a/src/main/resources/views/platform/zhgh/dayofficework/siteCug/apply/apply.js b/src/main/resources/views/platform/zhgh/dayofficework/siteCug/apply/apply.js index 3b5d3f33..f72933b3 100644 --- a/src/main/resources/views/platform/zhgh/dayofficework/siteCug/apply/apply.js +++ b/src/main/resources/views/platform/zhgh/dayofficework/siteCug/apply/apply.js @@ -175,9 +175,9 @@ const apply = {
- 预约本年后续每周同一时段 + 预约本月后续每周同一时段 - 例如先选某个周一 09:00-11:00,勾选后会自动预约本年剩余所有周一的这个时段。 + 例如先选某个周一 09:00-11:00,勾选后会自动预约本月剩余所有周一的这个时段。
@@ -344,7 +344,10 @@ const apply = { if (!this.formData.reserveStartTime) { return false } - return this.$moment(time).format('YYYY-MM-DD') < this.formData.reserveStartTime.slice(0, 10) + const targetDay = this.$moment(time).format('YYYY-MM-DD') + const startDay = this.formData.reserveStartTime.slice(0, 10) + const monthEndDay = this.$moment(this.formData.reserveStartTime).endOf('month').format('YYYY-MM-DD') + return targetDay < startDay || targetDay > monthEndDay }, } }, @@ -397,10 +400,10 @@ const apply = { return '' } if (!this.formData.reserveStartTime || !this.formData.reserveEndTime) { - return '请先选择开始和结束时间后,再批量预约本年同星期时段' + return '请先选择开始和结束时间后,再批量预约本月同星期时段' } if (!this.yearlyReserveDates.length) { - return '当前时间段无法生成本年批量预约日期' + return '当前时间段无法生成本月批量预约日期' } return '将从 ' + this.yearlyReserveDates[0] + ' 开始,自动预约到 ' + this.yearlyReserveDates[this.yearlyReserveDates.length - 1] + ',共 ' + this.yearlyReserveDates.length + ' 个时段' }, @@ -498,6 +501,10 @@ const apply = { } if (this.formData.reserveStartTime && this.formData.yearlyReserveEndDate < this.formData.reserveStartTime.slice(0, 10)) { this.$set(this.formData, 'yearlyReserveEndDate', defaultDate) + return + } + if (this.formData.reserveStartTime && this.formData.yearlyReserveEndDate > defaultDate) { + this.$set(this.formData, 'yearlyReserveEndDate', defaultDate) } }, getInitialScheduleDate() { @@ -1020,12 +1027,16 @@ const apply = { this.$message.warning('批量预约截止日期不能早于开始日期') return false } + if (this.formData.reserveStartTime && this.formData.yearlyReserveEndDate > this.$moment(this.formData.reserveStartTime).endOf('month').format('YYYY-MM-DD')) { + this.$message.warning('批量预约截止日期不能超过预约开始时间所在月份') + return false + } if (!this.yearlyReserveDates.length) { this.$message.warning('当前选择的时间段在截止日期内无法生成可批量预约的日期') return false } if (!this.yearlyReserveDates.length) { - this.$message.warning('当前选择的时间段无法生成本年批量预约日期') + this.$message.warning('当前选择的时间段无法生成本月批量预约日期') return false } return true @@ -1038,7 +1049,7 @@ const apply = { if (!this.yearlyReserve) { return baseText + ',是否确定提交预约?' } - return baseText + '。系统将继续预约本年后续 ' + this.yearlyReserveDates.length + ' 个同星期时段,是否确定提交?' + return baseText + '。系统将继续预约本月后续 ' + this.yearlyReserveDates.length + ' 个同星期时段,是否确定提交?' }, validateYearlyReserve() { if (!this.yearlyReserve) { @@ -1052,6 +1063,10 @@ const apply = { this.$message.warning('批量预约截止日期不能早于开始日期') return false } + if (this.formData.reserveStartTime && this.formData.yearlyReserveEndDate > this.$moment(this.formData.reserveStartTime).endOf('month').format('YYYY-MM-DD')) { + this.$message.warning('批量预约截止日期不能超过预约开始时间所在月份') + return false + } if (!this.yearlyReserveDates.length) { this.$message.warning('当前选择的时间段在截止日期内无法生成可批量预约的日期') return false diff --git a/src/main/resources/views/platform/zhgh/dayofficework/siteCug/schoolUnionAudit/index.html b/src/main/resources/views/platform/zhgh/dayofficework/siteCug/schoolUnionAudit/index.html index 0e3bfa88..55d3ba7d 100644 --- a/src/main/resources/views/platform/zhgh/dayofficework/siteCug/schoolUnionAudit/index.html +++ b/src/main/resources/views/platform/zhgh/dayofficework/siteCug/schoolUnionAudit/index.html @@ -288,7 +288,7 @@ layout("/layouts/platform.html"){ return } const message = this.formData.yearlyBatch - ? ('该记录属于“预约本年”批量预约,提交后将一并审核同批次的 ' + (this.formData.batchAuditCount || 0) + ' 条预约记录,是否继续?') + ? ('该记录属于“预约本月”批量预约,提交后将一并审核同批次的 ' + (this.formData.batchAuditCount || 0) + ' 条预约记录,是否继续?') : '您确定要提交吗?' this.$confirm(message, '提示', { confirmButtonText: '确定', @@ -317,7 +317,7 @@ layout("/layouts/platform.html"){ return } const message = this.normalizeBatchFlag(row) - ? ('该记录属于“预约本年”批量预约,撤回后将一并撤回同批次的 ' + (row.batchAuditCount || row._batchCount || 0) + ' 条审核任务,是否继续?') + ? ('该记录属于“预约本月”批量预约,撤回后将一并撤回同批次的 ' + (row.batchAuditCount || row._batchCount || 0) + ' 条审核任务,是否继续?') : '您确定要撤回吗?' this.$confirm(message, '提示', { confirmButtonText: '确定', diff --git a/src/main/resources/views/platform/zhghh5/dayofficework/siteCug/apply/form/apply.js b/src/main/resources/views/platform/zhghh5/dayofficework/siteCug/apply/form/apply.js index 7d9d033b..d2f218f6 100644 --- a/src/main/resources/views/platform/zhghh5/dayofficework/siteCug/apply/form/apply.js +++ b/src/main/resources/views/platform/zhghh5/dayofficework/siteCug/apply/form/apply.js @@ -142,7 +142,10 @@ new Vue({ return base.startOf('day').toDate() }, yearlyReserveMaxDate() { - return this.$moment().add(2, 'year').endOf('year').toDate() + const base = this.formData.reserveStartTime + ? this.$moment(this.formData.reserveStartTime) + : (this.scheduleDate ? this.$moment(this.scheduleDate, 'YYYY-MM-DD') : this.$moment()) + return base.endOf('month').toDate() }, yearlyReserveDates() { if (!this.yearlyReserve || !this.formData.reserveStartTime || !this.formData.reserveEndTime || !this.formData.yearlyReserveEndDate) { @@ -255,6 +258,10 @@ new Vue({ } if (this.formData.reserveStartTime && this.formData.yearlyReserveEndDate < this.formData.reserveStartTime.slice(0, 10)) { this.$set(this.formData, 'yearlyReserveEndDate', defaultDate) + return + } + if (this.formData.reserveStartTime && this.formData.yearlyReserveEndDate > defaultDate) { + this.$set(this.formData, 'yearlyReserveEndDate', defaultDate) } }, openReserveTypePicker() { @@ -488,6 +495,20 @@ new Vue({ result.push('slot-block--closed') return result }, + formatSlotLabel(block) { + const label = block && block.label ? block.label : '' + const match = label.match(/^(.+?)\s+(\d{2}:\d{2}(?::\d{2})?\s*-\s*\d{2}:\d{2}(?::\d{2})?)$/) + if (match) { + return { + name: match[1], + time: match[2], + } + } + return { + name: label, + time: '', + } + }, onAvailabilityBlockClick(block) { if (!block || !block.key) { return @@ -847,6 +868,10 @@ new Vue({ this.$toast('批量预约截止日期不能早于开始日期') return false } + if (this.formData.reserveStartTime && this.formData.yearlyReserveEndDate > this.$moment(this.formData.reserveStartTime).endOf('month').format('YYYY-MM-DD')) { + this.$toast('批量预约截止日期不能超过预约开始时间所在月份') + return false + } if (!this.yearlyReserveDates.length) { this.$toast('当前截止日期内没有可批量预约的同星期时段') return false diff --git a/src/main/resources/views/platform/zhghh5/dayofficework/siteCug/apply/form/index.html b/src/main/resources/views/platform/zhghh5/dayofficework/siteCug/apply/form/index.html index aed48e96..e3f0afb9 100644 --- a/src/main/resources/views/platform/zhghh5/dayofficework/siteCug/apply/form/index.html +++ b/src/main/resources/views/platform/zhghh5/dayofficework/siteCug/apply/form/index.html @@ -165,21 +165,30 @@ layout("/layouts/platform_h5.html"){ .availability-popup__tabs { display: flex; gap: 8px; - padding: 12px 16px 0; + padding: 12px 16px 8px; overflow-x: auto; background: #f8fafc; + scrollbar-width: none; + -ms-overflow-style: none; + } + + .availability-popup__tabs::-webkit-scrollbar { + display: none; } .availability-tab { + flex: 0 0 auto; min-width: 72px; - height: 34px; - padding: 0 14px; + min-height: 34px; + padding: 6px 14px; border: 1px solid #cbd5e1; border-radius: 999px; background: #ffffff; color: #475569; font-size: 13px; font-weight: 500; + line-height: 1.2; + white-space: nowrap; box-sizing: border-box; } @@ -244,12 +253,13 @@ layout("/layouts/platform_h5.html"){ .slot-block { min-height: 48px; - padding: 0 12px; + padding: 8px 10px; border: 1px solid #dbe4ee; border-radius: 14px; background: #ffffff; color: #475569; - display: inline-flex; + display: flex; + flex-direction: column; align-items: center; justify-content: center; text-align: center; @@ -259,6 +269,21 @@ layout("/layouts/platform_h5.html"){ box-sizing: border-box; } + .slot-block__name, + .slot-block__time { + display: block; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .slot-block__time { + margin-top: 2px; + font-size: 12px; + line-height: 1.2; + } + .slot-block--available { border-color: #86efac; background: rgba(34, 197, 94, 0.1); @@ -464,7 +489,7 @@ layout("/layouts/platform_h5.html"){
-
将自动预约截止日期内每周同一时段,默认到本月底。
+
预约本月后续每周同一时段,默认到本月底。
- {{ block.label }} + {{ formatSlotLabel(block).name }} + {{ formatSlotLabel(block).time }}
diff --git a/src/main/resources/views/platform/zhghh5/dayofficework/siteCug/mine/index.html b/src/main/resources/views/platform/zhghh5/dayofficework/siteCug/mine/index.html index 3b8e7ec4..7638bdbd 100644 --- a/src/main/resources/views/platform/zhghh5/dayofficework/siteCug/mine/index.html +++ b/src/main/resources/views/platform/zhghh5/dayofficework/siteCug/mine/index.html @@ -3,7 +3,54 @@ layout("/layouts/platform_h5.html"){ #-->
@@ -18,18 +65,35 @@ layout("/layouts/platform_h5.html"){