From cc5a944b4703eddefe4061caa5dbe63488905151 Mon Sep 17 00:00:00 2001 From: zhouhefeng Date: Tue, 7 Jul 2026 15:15:26 +0800 Subject: [PATCH] commit --- .../app/base/config/DataCenterProperties.java | 75 +---- .../com/budwk/app/sys/models/Sys_user.java | 18 +- .../impl/SysDataDictPullServiceImpl.java | 78 +---- .../impl/SysDataUnitPullServiceImpl.java | 266 ++++++++++++++---- .../impl/SysDataUserPullServiceImpl.java | 251 +++++++++-------- 5 files changed, 358 insertions(+), 330 deletions(-) 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..4445a995 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,211 @@ 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(v -> v.getStr("LSDWH")) + .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, raw.getStr("LSDWH")); + 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 表示单位层次;当前项目用 unitTypeCode=1 识别业务可选单位。 + */ + 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 unitLevel = raw.getInt("DWCC"); + boolean availableBusinessUnit = "是".equals(raw.getStr("DWYXBS")) && Integer.valueOf(2).equals(unitLevel); + + 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 为空、等于自身或本地不存在该父级时按根级处理,避免错误父级影响单位树保存。 + */ + private String getParentId(String unitCode, String parentCode) { + if (StrUtil.isBlank(parentCode) || unitCode.equals(parentCode)) { + return ""; + } + 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); + } + + /** + * 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("旧数据中心财务拉取接口已停用"); } }