|
|
@@ -11,7 +11,11 @@ import com.fs.common.utils.DateUtils;
|
|
|
import com.fs.common.utils.PubFun;
|
|
|
import com.fs.common.utils.StringUtils;
|
|
|
import com.fs.common.utils.date.DateUtil;
|
|
|
+import com.fs.company.domain.Company;
|
|
|
+import com.fs.company.domain.CompanyMiniapp;
|
|
|
import com.fs.company.mapper.CompanyUserMapper;
|
|
|
+import com.fs.company.service.ICompanyMiniappService;
|
|
|
+import com.fs.company.service.ICompanyService;
|
|
|
import com.fs.config.cloud.CloudHostProper;
|
|
|
import com.fs.core.config.WxMaConfig;
|
|
|
import com.fs.core.config.WxMaConfiguration;
|
|
|
@@ -70,6 +74,8 @@ import java.nio.charset.StandardCharsets;
|
|
|
import java.time.LocalDate;
|
|
|
import java.time.LocalDateTime;
|
|
|
import java.time.ZoneId;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.Collections;
|
|
|
import java.util.*;
|
|
|
|
|
|
import static com.fs.course.utils.LinkUtil.generateRandomStringWithLock;
|
|
|
@@ -125,6 +131,12 @@ public class FsCourseLinkServiceImpl implements IFsCourseLinkService
|
|
|
@Autowired
|
|
|
private SysConfigMapper sysConfigMapper;
|
|
|
|
|
|
+ @Autowired
|
|
|
+ private ICompanyMiniappService companyMiniappService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private ICompanyService companyService;
|
|
|
+
|
|
|
private static String TOKEN_VALID_CODE = "40001";
|
|
|
|
|
|
private volatile Integer version = 0;
|
|
|
@@ -765,18 +777,129 @@ public class FsCourseLinkServiceImpl implements IFsCourseLinkService
|
|
|
|
|
|
/**
|
|
|
* 获取跳转微信小程序的链接地址
|
|
|
- * @param linkStr
|
|
|
- * @return
|
|
|
+ * 逻辑:
|
|
|
+ * 1. 从linkStr解析companyId
|
|
|
+ * 2. 通过companyId获取公司绑定的小程序appId列表
|
|
|
+ * 3. 如果微信API调用失败,自动切换到下一个appId重试
|
|
|
+ * @param linkStr 链接字符串
|
|
|
+ * @param appId 小程序appId(起始appId,可为空)
|
|
|
+ * @return 小程序链接
|
|
|
*/
|
|
|
@Override
|
|
|
- public String getGotoWxAppLink(String linkStr,String appId) {
|
|
|
+ public String getGotoWxAppLink(String linkStr, String appId) {
|
|
|
+ Long companyId = parseCompanyIdFromLink(linkStr);
|
|
|
+
|
|
|
+ List<String> appIdList = getAppIdListByCompanyId(companyId);
|
|
|
+
|
|
|
+ if (appIdList == null || appIdList.isEmpty()) {
|
|
|
+ return "未配置小程序,获取失败";
|
|
|
+ }
|
|
|
+
|
|
|
+ int currentIndex = 0;
|
|
|
+ if (StringUtils.isNotBlank(appId) && appIdList.contains(appId)) {
|
|
|
+ currentIndex = appIdList.indexOf(appId);
|
|
|
+ }
|
|
|
+
|
|
|
+ return generateWxAppLinkWithRetry(linkStr, appIdList, currentIndex);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从链接字符串中解析companyId
|
|
|
+ * @param linkStr 链接字符串,格式如:/pages_course/videovip?course={"companyId":123,...}
|
|
|
+ * @return companyId
|
|
|
+ */
|
|
|
+ private Long parseCompanyIdFromLink(String linkStr) {
|
|
|
+ try {
|
|
|
+ String decodedStr = URLDecoder.decode(linkStr, StandardCharsets.UTF_8.toString());
|
|
|
+ log.info("解析linkStr,解码后:{}", decodedStr);
|
|
|
+
|
|
|
+ int companyIdIndex = decodedStr.indexOf("companyId");
|
|
|
+ if (companyIdIndex == -1) {
|
|
|
+ log.warn("linkStr中未找到companyId参数");
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ int valueStart = decodedStr.indexOf(":", companyIdIndex);
|
|
|
+ if (valueStart == -1) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ valueStart++;
|
|
|
+
|
|
|
+ while (valueStart < decodedStr.length() &&
|
|
|
+ (decodedStr.charAt(valueStart) == ' ' || decodedStr.charAt(valueStart) == '"')) {
|
|
|
+ valueStart++;
|
|
|
+ }
|
|
|
+
|
|
|
+ StringBuilder numStr = new StringBuilder();
|
|
|
+ while (valueStart < decodedStr.length() && Character.isDigit(decodedStr.charAt(valueStart))) {
|
|
|
+ numStr.append(decodedStr.charAt(valueStart));
|
|
|
+ valueStart++;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (numStr.length() > 0) {
|
|
|
+ return Long.parseLong(numStr.toString());
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("解析companyId失败", e);
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据companyId获取小程序appId列表
|
|
|
+ * @param companyId 公司ID
|
|
|
+ * @return appId列表(从company_miniapp表的appId字段解析,逗号分隔)
|
|
|
+ */
|
|
|
+ private List<String> getAppIdListByCompanyId(Long companyId) {
|
|
|
+ if (companyId == null) {
|
|
|
+ return Collections.emptyList();
|
|
|
+ }
|
|
|
+
|
|
|
+ List<CompanyMiniapp> miniappList = companyMiniappService.list(
|
|
|
+ new QueryWrapper<CompanyMiniapp>().eq("company_id", companyId)
|
|
|
+ );
|
|
|
+
|
|
|
+ if (miniappList == null || miniappList.isEmpty()) {
|
|
|
+ log.warn("公司{}未配置小程序appId", companyId);
|
|
|
+ return Collections.emptyList();
|
|
|
+ }
|
|
|
+
|
|
|
+ String appIdStr = miniappList.get(0).getAppId();
|
|
|
+ if (StringUtils.isEmpty(appIdStr)) {
|
|
|
+ log.warn("公司{}未配置小程序appId", companyId);
|
|
|
+ return Collections.emptyList();
|
|
|
+ }
|
|
|
+
|
|
|
+ String[] appIdArray = appIdStr.split(",");
|
|
|
+ List<String> appIdList = new ArrayList<>();
|
|
|
+ for (String appId : appIdArray) {
|
|
|
+ String trimmed = appId.trim();
|
|
|
+ if (StringUtils.isNotBlank(trimmed)) {
|
|
|
+ appIdList.add(trimmed);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("公司{}绑定的小程序列表:{}", companyId, appIdList);
|
|
|
+ return appIdList;
|
|
|
+ }
|
|
|
+
|
|
|
+ private String generateWxAppLinkWithRetry(String linkStr, List<String> appIdList, int currentIndex) {
|
|
|
+ if (currentIndex >= appIdList.size()) {
|
|
|
+ log.error("所有appId都已尝试失败");
|
|
|
+ return "所有小程序均调用失败,获取失败";
|
|
|
+ }
|
|
|
+
|
|
|
+ String currentAppId = appIdList.get(currentIndex);
|
|
|
+ log.info("尝试使用appId[{}]:{}", currentIndex, currentAppId);
|
|
|
+
|
|
|
CloseableHttpClient client = null;
|
|
|
try {
|
|
|
client = HttpClients.createDefault();
|
|
|
int queryIndex = linkStr.indexOf("?");
|
|
|
if (queryIndex == -1 || queryIndex == linkStr.length() - 1) {
|
|
|
- return "链接格式错误,缺少参数";
|
|
|
+ return "页面链接错误,获取失败";
|
|
|
}
|
|
|
+
|
|
|
String pageUrl = linkStr.substring(0, queryIndex);
|
|
|
String queryString = linkStr.substring(queryIndex + 1);
|
|
|
if (pageUrl.startsWith("/")) {
|
|
|
@@ -802,54 +925,78 @@ public class FsCourseLinkServiceImpl implements IFsCourseLinkService
|
|
|
}
|
|
|
}
|
|
|
String query = encodedQuery.toString();
|
|
|
-// String json = configService.selectConfigByKey("course.config");
|
|
|
-// CourseConfig config = JSON.parseObject(json, CourseConfig.class);
|
|
|
-// String miniprogramAppid = config.getMiniprogramAppid();
|
|
|
-// if (StringUtils.isBlank(miniprogramAppid)) {
|
|
|
-// return "未配置点播小程序id";
|
|
|
-// }
|
|
|
- //获取微信token
|
|
|
- final WxMaService wxService = WxMaConfiguration.getMaService(appId);
|
|
|
- String token = wxService.getAccessToken();
|
|
|
- log.info("小程序TOKEN值-------->刷新前TOKEN:{}", token);
|
|
|
- HttpPost httpPost = new HttpPost("https://api.weixin.qq.com/wxa/generate_urllink?access_token=" + token);
|
|
|
- JSONObject bodyObj = new JSONObject();
|
|
|
- bodyObj.put("path", pageUrl);
|
|
|
- bodyObj.put("query", query);
|
|
|
- log.info("微信小程序请求参数打印:{}", bodyObj.toJSONString());
|
|
|
- StringEntity entity = new StringEntity(bodyObj.toJSONString(),"UTF-8");
|
|
|
- httpPost.setEntity(entity);
|
|
|
- httpPost.setHeader("Content-type", "application/json");
|
|
|
- httpPost.setHeader("cache-control","max-age=0");
|
|
|
- HttpEntity response = client.execute(httpPost).getEntity();
|
|
|
- String responseString = EntityUtils.toString(response);
|
|
|
- log.info("微信小程序接口响应数据:{}", responseString);
|
|
|
- JSONObject jsonObject = JSONObject.parseObject(responseString);
|
|
|
-
|
|
|
- if(TOKEN_VALID_CODE.equals(jsonObject.getString("errcode"))){
|
|
|
- Integer curVersion = Integer.valueOf(version);
|
|
|
- synchronized (TOKEN_VALID_CODE){
|
|
|
- if(curVersion.equals(version)){
|
|
|
- log.info("小程序TOKEN:40001进入强制刷新-------->刷新前TOKEN:{}", token);
|
|
|
- wxService.getAccessToken(true);
|
|
|
- version = version.equals(Integer.MAX_VALUE) ? 0 : curVersion + 1;
|
|
|
- log.info("小程序TOKEN:40001进入强制刷新-------->刷新后TOKEN:{}", wxService.getAccessToken());
|
|
|
- }
|
|
|
- return getGotoWxAppLink(linkStr,appId);
|
|
|
+
|
|
|
+ final WxMaService wxService = WxMaConfiguration.getMaService(currentAppId);
|
|
|
+ String token = wxService.getAccessToken();
|
|
|
+ log.info("小程序TOKEN值-------->刷新前TOKEN:{}", token);
|
|
|
+ HttpPost httpPost = new HttpPost("https://api.weixin.qq.com/wxa/generate_urllink?access_token=" + token);
|
|
|
+ JSONObject bodyObj = new JSONObject();
|
|
|
+ bodyObj.put("path", pageUrl);
|
|
|
+ bodyObj.put("query", query);
|
|
|
+ log.info("微信小程序请求参数打印:{}", bodyObj.toJSONString());
|
|
|
+ StringEntity entity = new StringEntity(bodyObj.toJSONString(),"UTF-8");
|
|
|
+ httpPost.setEntity(entity);
|
|
|
+ httpPost.setHeader("Content-type", "application/json");
|
|
|
+ httpPost.setHeader("cache-control","max-age=0");
|
|
|
+ HttpEntity response = client.execute(httpPost).getEntity();
|
|
|
+ String responseString = EntityUtils.toString(response);
|
|
|
+ log.info("微信小程序接口响应数据:{}", responseString);
|
|
|
+ JSONObject jsonObject = JSONObject.parseObject(responseString);
|
|
|
+
|
|
|
+ if(TOKEN_VALID_CODE.equals(jsonObject.getString("errcode"))){
|
|
|
+ Integer curVersion = Integer.valueOf(version);
|
|
|
+ synchronized (TOKEN_VALID_CODE){
|
|
|
+ if(curVersion.equals(version)){
|
|
|
+ log.info("小程序TOKEN:40001进入强制刷新-------->刷新前TOKEN:{}", token);
|
|
|
+ wxService.getAccessToken(true);
|
|
|
+ version = version.equals(Integer.MAX_VALUE) ? 0 : curVersion + 1;
|
|
|
+ log.info("小程序TOKEN:40001进入强制刷新-------->刷新后TOKEN:{}", wxService.getAccessToken());
|
|
|
}
|
|
|
+ return generateWxAppLinkWithRetry(linkStr, appIdList, currentIndex);
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- if(null != jsonObject && !jsonObject.isEmpty() && jsonObject.containsKey("url_link")){
|
|
|
- return jsonObject.getString("url_link");
|
|
|
+ if(null != jsonObject && !jsonObject.isEmpty() && jsonObject.containsKey("url_link")){
|
|
|
+ return jsonObject.getString("url_link");
|
|
|
+ }
|
|
|
+
|
|
|
+ if(jsonObject != null && jsonObject.containsKey("errcode")) {
|
|
|
+ Integer errcode = jsonObject.getInteger("errcode");
|
|
|
+ String errmsg = jsonObject.getString("errmsg");
|
|
|
+ log.error("微信小程序生成链接失败,appId:{},错误码:{},错误信息:{}", currentAppId, errcode, errmsg);
|
|
|
+
|
|
|
+ if (currentIndex + 1 < appIdList.size()) {
|
|
|
+ log.info("切换到下一个appId重试,下一个索引:{}", currentIndex + 1);
|
|
|
+ return generateWxAppLinkWithRetry(linkStr, appIdList, currentIndex + 1);
|
|
|
}
|
|
|
- return "页面链接错误,获取失败";
|
|
|
+
|
|
|
+ return "微信错误:" + errcode + "|" + errmsg;
|
|
|
+ }
|
|
|
+
|
|
|
+ log.error("微信小程序响应格式异常");
|
|
|
+ return "微信小程序响应格式异常";
|
|
|
|
|
|
} catch (WxErrorException e) {
|
|
|
- throw new RuntimeException(e);
|
|
|
+ log.error("微信API调用异常,appId:{}", currentAppId, e);
|
|
|
+ if (currentIndex + 1 < appIdList.size()) {
|
|
|
+ log.info("异常切换到下一个appId重试,下一个索引:{}", currentIndex + 1);
|
|
|
+ return generateWxAppLinkWithRetry(linkStr, appIdList, currentIndex + 1);
|
|
|
+ }
|
|
|
+ return "微信API异常";
|
|
|
} catch (ClientProtocolException e) {
|
|
|
- throw new RuntimeException(e);
|
|
|
+ log.error("HTTP协议错误", e);
|
|
|
+ return "网络请求失败";
|
|
|
} catch (IOException e) {
|
|
|
- throw new RuntimeException(e);
|
|
|
+ log.error("IO异常", e);
|
|
|
+ return "网络连接失败";
|
|
|
+ } finally {
|
|
|
+ if (client != null) {
|
|
|
+ try {
|
|
|
+ client.close();
|
|
|
+ } catch (IOException e) {
|
|
|
+ log.error("关闭HttpClient失败", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|