| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258 |
- package com.kingdee.eas.custom.perfweb.utils;
- import com.kingdee.eas.custom.integration.base.config.AppConfig;
- import com.kingdee.eas.custom.integration.base.config.SpringContextHolder;
- import com.kingdee.eas.custom.integration.base.service.AppConfigService;
- import com.kingdee.eas.custom.integration.base.service.StateValidator;
- import org.apache.commons.lang3.StringUtils;
- import org.apache.log4j.Logger;
- import org.springframework.web.util.UriComponentsBuilder;
- import java.io.UnsupportedEncodingException;
- import java.net.URLEncoder;
- import java.util.HashMap;
- import java.util.Map;
- /**
- *类名称: UrlParamUtil
- *功能描述: 飞书待办跳转地址工具类
- *负责拼接并编码 pcurl / appurl 参数,集中管理域名、入口路径和各业务页面路径
- *创建日期: 2026-06-12
- *作 者: 青梧
- *版 本: 1.0
- */
- public class UrlParamUtil {
- private static final Logger logger = Logger.getLogger(UrlParamUtil.class);
- /** integration 飞书单点入口(相对 SHR 域名) */
- private static final String FEISHU_AUTH_PATH = "/qy/auth/feishu";
- //===== 基础域名(切换环境时改 BASE_HOST / SHR_HOST 等当前生效变量)=====
- /** OA 跳转网关前缀 - 正式环境(oAToSHR) */
- public static String BASE_HOST_PROD = "http://ehr.shichuangyi.cn:8080/shr/api/oAToSHR?redirect=";
- /** OA 跳转网关前缀 - 测试环境(oAToSHR) */
- public static String BASE_HOST_TEST = "http://10.0.3.24:8080/shr/api/oAToSHR?redirect=";
- /** 当前生效的 OA 待办网关 */
- public static String BASE_HOST = BASE_HOST_PROD;
- /** SHR 系统地址 - 正式环境 */
- public static String SHR_HOST_PROD = "http://ehr.shichuangyi.cn:8080";
- /** SHR 系统地址 - 测试环境 */
- public static String SHR_HOST_TEST = "http://10.0.3.24:8080";
- /** 当前生效的 SHR 地址(dynamic.do 等业务页) */
- public static String SHR_HOST = SHR_HOST_PROD;
- //正式环境
- public static String PRODURL = "https://freport.shichuangyi.cn/oa-ofs/prod-api/approve/rest/ofs/ReceiveRequestInfoByJson";
- //测试环境
- public static String TESTURL = "https://mtest.shichuangyi.cn/prod-approval-api/approve/rest/ofs/ReceiveRequestInfoByJson";
- //绩效工作台 - 正式环境
- public static String JXGZT_PC_PROD = "http://ehr.shichuangyi.cn:8080/shr/dynamic.do?inFrame=true&fromHeader=true&uipk=com.kingdee.shr.perfweb.app.perfportal.core&serviceId=fUjSqQK1RouZaEJUI2jH5vI9KRA%3D";
- public static String JXGZT_BOS_PROD = "http://ehr.shichuangyi.cn:8080/shr/dynamic.do?inFrame=true&fromHeader=true&uipk=com.kingdee.shr.perfweb.app.perfportal.core&serviceId=fUjSqQK1RouZaEJUI2jH5vI9KRA%3D";
- //绩效工作台 - 测试环境
- public static String JXGZT_PC_TEST = "http://10.0.3.24:8080/shr/dynamic.do?inFrame=true&fromHeader=true&uipk=com.kingdee.shr.perfweb.app.perfportal.core&serviceId=fUjSqQK1RouZaEJUI2jH5vI9KRA%3D";
- public static String JXGZT_BOS_TEST = "http://10.0.3.24:8080/shr/dynamic.do?inFrame=true&fromHeader=true&uipk=com.kingdee.shr.perfweb.app.perfportal.core&serviceId=fUjSqQK1RouZaEJUI2jH5vI9KRA%3D";
- public static String JXGZT_PC = JXGZT_PC_PROD;
- public static String JXGZT_BOS = JXGZT_BOS_PROD;
- //默认组织ID(考核结果汇总列表 hrOrgId)
- public static String DEFAULT_HR_ORG_ID = "00000000-0000-0000-0000-000000000000CCE7AED4";
- /**
- *考核结果汇总列表页 query 模板。
- *动态参数使用 {@code {period}}、{@code {perfPlanId}}、{@code {hrOrgId}} 占位,拼接时需对值做 URL 编码。
- */
- private static final String EVA_RESULT_MANAGER_LIST_QUERY =
- "uipk=com.kingdee.shr.perfweb.app.base.evalplan.evaResultManager.list"
- + "&serviceId=nQafn+gKQ+CngP3xudsk5PI9KRA="
- + "&isOrg=0"
- + "&period={period}"
- + "&perfPlanId={perfPlanId}"
- + "&hrOrgId={hrOrgId}";
- /**
- * 绩效考核结果报表列表页 query 模板(飞书待办跳转)。
- * periodId 为对应待办的考核周期 ID。
- */
- private static final String CENTER_PERF_STAT_REPORT_LIST_QUERY =
- "uipk=com.kingdee.eas.custom.perfweb.handler.CenterPerfStatReport.list"
- + "&serviceId=eF6u2TdvSbScepkXUeRU1vI9KRA%3D"
- + "&inFrame=true"
- + "&periodId={periodId}";
- private static final String DEFAULT_CHARSET = "UTF-8";
- /**
- *对字符串进行 URL 编码(UTF-8),用于 query 参数值。
- *例如:{@code urlEncode("a=b")} 返回 {@code "a%3Db"}
- *
- *@param value 待编码字符串,null 或空串返回空串
- *@return 编码后的字符串
- */
- public static String urlEncode(String value) {
- if (value == null || value.isEmpty()) {
- return "";
- }
- try {
- return URLEncoder.encode(value, DEFAULT_CHARSET);
- } catch (UnsupportedEncodingException e) {
- throw new RuntimeException("URL encode failed: " + value, e);
- }
- }
- /**
- *替换模板中的 {@code {key}} 占位符。
- *
- *@param template 含占位符的模板,如 {@code "...&period={period}&perfPlanId={perfPlanId}"}
- *@param params key 对应占位符名称(不含花括号),value 为替换值(调用方自行决定是否已编码)
- *@return 替换后的字符串
- */
- public static String fillUrlTemplate(String template, Map<String, String> params) {
- if (template == null) {
- return null;
- }
- if (params == null || params.isEmpty()) {
- return template;
- }
- String result = template;
- for (Map.Entry<String, String> entry : params.entrySet()) {
- String replacement = entry.getValue() != null ? entry.getValue() : "";
- result = result.replace("{" + entry.getKey() + "}", replacement);
- }
- return result;
- }
- /**
- *替换模板占位符,并对每个参数值自动做 URL 编码(传入 BOS 原始 ID 即可)。
- */
- public static String fillUrlTemplateEncoded(String template, Map<String, String> rawParams) {
- Map<String, String> encodedParams = new HashMap<>();
- if (rawParams != null) {
- for (Map.Entry<String, String> entry : rawParams.entrySet()) {
- encodedParams.put(entry.getKey(), urlEncode(entry.getValue()));
- }
- }
- return fillUrlTemplate(template, encodedParams);
- }
- /**
- *构建考核结果汇总列表页地址(dynamic.do)。
- *
- *@param periodId 考核周期 ID(summaryInfo.getPeriod().getId())
- *@param perfPlanId 考核方案 ID(summaryInfo.getEvaPlan().getId())
- *@param hrOrgId 组织 ID,为空时使用 {@link #DEFAULT_HR_ORG_ID}
- *@return 完整 SHR 页面 URL,未套 OA redirect
- */
- public static String buildEvaResultManagerListUrl(String periodId, String perfPlanId, String hrOrgId) {
- Map<String, String> params = new HashMap<>();
- params.put("period", periodId);
- params.put("perfPlanId", perfPlanId);
- params.put("hrOrgId", resolveHrOrgId(hrOrgId));
- String query = fillUrlTemplateEncoded(EVA_RESULT_MANAGER_LIST_QUERY, params);
- return SHR_HOST + "/shr/dynamic.do?" + query;
- }
- /**
- *构建考核结果汇总列表页地址,hrOrgId 使用 {@link #DEFAULT_HR_ORG_ID}。
- */
- public static String buildEvaResultManagerListUrl(String periodId, String perfPlanId) {
- return buildEvaResultManagerListUrl(periodId, perfPlanId, DEFAULT_HR_ORG_ID);
- }
- /**
- * 构建考核结果汇总列表页的 OA 待办跳转地址(oAToSHR,非 Bot 消息)。
- */
- public static String buildEvaResultManagerListOaUrl(String periodId, String perfPlanId) {
- return buildOaRedirectUrl(buildEvaResultManagerListUrl(periodId, perfPlanId));
- }
- /**
- * 构建考核结果汇总列表页的 OA 待办跳转地址(oAToSHR,非 Bot 消息)。
- */
- public static String buildEvaResultManagerListOaUrl(String periodId, String perfPlanId, String hrOrgId) {
- return buildOaRedirectUrl(buildEvaResultManagerListUrl(periodId, perfPlanId, hrOrgId));
- }
- /**
- * 构建绩效考核结果报表列表页地址(dynamic.do)。
- *
- * @param periodId 考核周期 ID
- */
- public static String buildCenterPerfStatReportListUrl(String periodId) {
- Map<String, String> params = new HashMap<>();
- params.put("periodId", periodId != null ? periodId : "");
- String query = fillUrlTemplateEncoded(CENTER_PERF_STAT_REPORT_LIST_QUERY, params);
- return SHR_HOST + "/shr/dynamic.do?" + query;
- }
- /**
- * 构建绩效考核结果报表列表页的 OA 待办跳转地址(oAToSHR,非 Bot 消息)。
- */
- public static String buildCenterPerfStatReportListOaUrl(String periodId) {
- return buildOaRedirectUrl(buildCenterPerfStatReportListUrl(periodId));
- }
- /**
- * 绩效工作台 Bot 消息跳转地址(integration 单点 + 目标页)。
- * 用于:启动考核、单人/批量评分、考核结果公示。
- */
- public static String buildPerfPortalFeishuMessageUrl() {
- return buildFeishuMessageUrl(JXGZT_PC);
- }
- /**
- * @deprecated 待办请用 {@link #buildOaRedirectUrl(String)};Bot 消息请用 {@link #buildPerfPortalFeishuMessageUrl()}
- */
- public static String buildPerfPortalOaUrl() {
- return buildPerfPortalFeishuMessageUrl();
- }
- /**
- * Bot 消息跳转地址:integration {@code /qy/auth/feishu?state=Base64(redirect|appType)}。
- * 仅用于启动考核、单人/批量评分、考核结果公示;OA 待办请用 {@link #buildOaRedirectUrl(String)}。
- *
- * @param targetUrl 登录成功后打开的 HR 业务页完整 URL(未编码,写入 state)
- */
- public static String buildFeishuMessageUrl(String targetUrl) {
- if (StringUtils.isBlank(targetUrl)) {
- return "";
- }
- try {
- StateValidator stateValidator = SpringContextHolder.getBean(StateValidator.class);
- AppConfigService appConfigService = SpringContextHolder.getBean(AppConfigService.class);
- AppConfig appConfig = appConfigService.getPrimaryApp("feishu");
- if (appConfig == null || StringUtils.isBlank(appConfig.getAppType())) {
- throw new IllegalStateException("oauth-config 未配置 feishu 主应用 appType");
- }
- String state = stateValidator.generate(targetUrl, appConfig.getAppType());
- String url = UriComponentsBuilder.fromUriString(appConfigService.getShrServerName() + FEISHU_AUTH_PATH)
- .queryParam("state", state)
- .build(true)
- .toUriString();
- logger.error("integration 单点地址:" +url);
- return url;
- } catch (Exception e) {
- logger.error("integration 单点地址构建失败,回退 oAToSHR,targetUrl=" + targetUrl, e);
- return buildOaRedirectUrl(targetUrl);
- }
- }
- /**
- * OA 待办跳转地址:{@code BASE_HOST + urlEncode(targetUrl)}(oAToSHR)。
- */
- public static String buildOaRedirectUrl(String targetUrl) {
- if (StringUtils.isBlank(targetUrl)) {
- return "";
- }
- return BASE_HOST + urlEncode(targetUrl);
- }
- private static String resolveHrOrgId(String hrOrgId) {
- if (hrOrgId == null || hrOrgId.trim().isEmpty()) {
- return DEFAULT_HR_ORG_ID;
- }
- return hrOrgId.trim();
- }
- }
|