|
@@ -0,0 +1,463 @@
|
|
|
+package com.slodon.b2b2c.investment.model;
|
|
|
+
|
|
|
+import cn.hutool.json.JSONArray;
|
|
|
+import cn.hutool.json.JSONObject;
|
|
|
+import cn.hutool.json.JSONUtil;
|
|
|
+import com.slodon.b2b2c.core.util.HttpClientUtil;
|
|
|
+import com.slodon.b2b2c.investment.bean.bo.*;
|
|
|
+import com.slodon.b2b2c.investment.bean.dto.RivalDTO;
|
|
|
+import com.slodon.b2b2c.investment.bean.vo.KeyWordPartVO;
|
|
|
+import com.slodon.b2b2c.investment.bean.vo.RivalPartVO;
|
|
|
+import com.slodon.b2b2c.investment.bean.vo.SuggestionVO;
|
|
|
+import com.slodon.b2b2c.investment.config.AnalysisConfig;
|
|
|
+import com.slodon.b2b2c.investment.constant.AnalysisConst;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
+
|
|
|
+import java.time.LocalDate;
|
|
|
+import java.time.format.DateTimeFormatter;
|
|
|
+import java.util.*;
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
+import javax.annotation.Resource;
|
|
|
+import java.util.concurrent.*;
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * @author sunshihao
|
|
|
+ * @version 1.0
|
|
|
+ * @description: 定量分析模型
|
|
|
+ * @date 2025/8/5 11:24
|
|
|
+ */
|
|
|
+@Component
|
|
|
+@Slf4j
|
|
|
+public class AnalysisModel {
|
|
|
+
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private AnalysisConfig analysisConfig;
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/***
|
|
|
+ * @description:翻译产品名称
|
|
|
+ * @param originalText 各语言名称
|
|
|
+ * @return: java.lang.String
|
|
|
+ * @author sunshihao
|
|
|
+ * @date: 2025/8/5 13:52
|
|
|
+ */
|
|
|
+public List<String> translateProduct(String originalText) throws Exception {
|
|
|
+ HashMap<String, String> headers = new HashMap<>();
|
|
|
+ headers.put(AnalysisConst.CONTENT_TYPE, AnalysisConst.APPLICATION_JSON);
|
|
|
+ JSONObject param = new JSONObject();
|
|
|
+ param.set(AnalysisConst.TEXT, originalText);
|
|
|
+ param.set(AnalysisConst.MODEL_NAME, analysisConfig.getModelName());
|
|
|
+ String result;
|
|
|
+ try {
|
|
|
+ result = HttpClientUtil.httpPost(analysisConfig.getTranslateAddress() + "/api/translate", param.toString(), headers);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error(e.getMessage(), "产品翻译接口出错");
|
|
|
+ throw new Exception("产品翻译接口出错", e);
|
|
|
+ }
|
|
|
+ JSONObject jsonObject = JSONUtil.parseObj(result);
|
|
|
+ String text = jsonObject.getByPath("translated_text", String.class);
|
|
|
+ if (StringUtils.isEmpty(text)) {
|
|
|
+ throw new Exception("翻译结果为空");
|
|
|
+ }
|
|
|
+ int start = text.indexOf("[");
|
|
|
+ int end = text.lastIndexOf("]") + AnalysisConst.NUM_ONE;
|
|
|
+ String jsonString = "";
|
|
|
+ if (start != AnalysisConst.NEGATIVE_ONE && end != AnalysisConst.NEGATIVE_ONE && start < end) {
|
|
|
+ jsonString = text.substring(start, end);
|
|
|
+ } else {
|
|
|
+ throw new Exception("未找到翻译结果");
|
|
|
+ }
|
|
|
+ return JSONUtil.parseArray(jsonString).toList(String.class);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+public HashMap<String,String> getCredential(){
|
|
|
+ String credentials = analysisConfig.getLoginUser() + ":" + analysisConfig.getPassword();
|
|
|
+ // 进行Base64编码
|
|
|
+ String base64Creds = Base64.getEncoder()
|
|
|
+ .encodeToString(credentials.getBytes(StandardCharsets.UTF_8));
|
|
|
+ // 创建请求头Map
|
|
|
+ HashMap<String, String> headers = new HashMap<>();
|
|
|
+ headers.put(AnalysisConst.CONTENT_TYPE, AnalysisConst.APPLICATION_JSON);
|
|
|
+ headers.put(AnalysisConst.AUTHORIZATION, "Basic " + base64Creds);
|
|
|
+ return headers;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+private ThreadPoolExecutor createThreadPool() {
|
|
|
+ return new ThreadPoolExecutor(
|
|
|
+ AnalysisConst.DEFAULT_RUN_THREAD_NUM,
|
|
|
+ AnalysisConst.DEFAULT_RUN_THREAD_NUM,
|
|
|
+ AnalysisConst.KEEP_ALIVE_TIME,
|
|
|
+ TimeUnit.MILLISECONDS,
|
|
|
+ new LinkedBlockingQueue<>(AnalysisConst.WORK_QUEUE),
|
|
|
+ new ThreadFactory() {
|
|
|
+ private int count = AnalysisConst.NUM_ZERO;
|
|
|
+ @Override
|
|
|
+ public Thread newThread(Runnable r) {
|
|
|
+ Thread thread = new Thread(r);
|
|
|
+ thread.setName("dataForSeo-api-call-thread-" + count++);
|
|
|
+ thread.setDaemon(true);
|
|
|
+ return thread;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ new ThreadPoolExecutor.CallerRunsPolicy()
|
|
|
+ );
|
|
|
+}
|
|
|
+ private void shutdownThreadPool(ThreadPoolExecutor executor) {
|
|
|
+ executor.shutdown();
|
|
|
+ try {
|
|
|
+ if (!executor.awaitTermination(AnalysisConst.NUM_TEN, TimeUnit.SECONDS)) {
|
|
|
+ executor.shutdownNow();
|
|
|
+ }
|
|
|
+ } catch (InterruptedException e) {
|
|
|
+ executor.shutdownNow();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/***
|
|
|
+ * @description: 接口1
|
|
|
+ * @param header 请求头
|
|
|
+ * @param keywords 关键词
|
|
|
+ * @param locationName 目标市场
|
|
|
+ * @return: com.slodon.b2b2c.investment.bean.vo.KeyWordPartVO
|
|
|
+ * @author sunshihao
|
|
|
+ * @date: 2025/8/7 11:54
|
|
|
+ */
|
|
|
+public KeyWordPartVO searchVolume(HashMap<String,String> header,String keywords,String locationName) throws Exception {
|
|
|
+ KeyWordPartVO keyWordPartVO = new KeyWordPartVO();
|
|
|
+ JSONArray payload = new JSONArray();
|
|
|
+ JSONObject payloadItem = new JSONObject();
|
|
|
+ JSONArray keywordsArray = new JSONArray();
|
|
|
+ keywordsArray.add(keywords);
|
|
|
+ payloadItem.set(AnalysisConst.KEYWORDS, keywordsArray);
|
|
|
+ payloadItem.set(AnalysisConst.LANGUAGE_NAME, AnalysisConst.ENGLISH);
|
|
|
+ payloadItem.set(AnalysisConst.LOCATION_NAME, locationName);
|
|
|
+ payload.put(payloadItem);
|
|
|
+ String post = HttpClientUtil.httpPost(analysisConfig.getSearchVolume(), payload.toString(),header);
|
|
|
+ JSONObject postObject = JSONUtil.parseObj(post);
|
|
|
+ Integer tasksError = postObject.getByPath("tasks_error", Integer.class);
|
|
|
+ if(tasksError==null||tasksError> AnalysisConst.NUM_ZERO){
|
|
|
+ throw new RuntimeException("接口1查询关键词 按月返回搜索量/CPC search_volume 任务失败");
|
|
|
+ }
|
|
|
+ MonthlySearchesBO bo = postObject.getByPath("tasks[0].result[0].monthly_searches[0]", MonthlySearchesBO.class);
|
|
|
+ Double competition = postObject.getByPath("tasks[0].result[0].competition",Double.class);
|
|
|
+ Integer cp = competition==null?null:(int)Math.round(competition * AnalysisConst.NUM_ONE_HUNDRED);
|
|
|
+ keyWordPartVO.setMonthlySearchesBO(bo);
|
|
|
+ keyWordPartVO.setCompetition(cp);
|
|
|
+ return keyWordPartVO;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/***
|
|
|
+ * @description: 上方右侧推荐数据
|
|
|
+ * @param:
|
|
|
+ * @param header 请求头
|
|
|
+ * @param keyword 关键词
|
|
|
+ * @param locationName 地区
|
|
|
+ * @return: com.slodon.b2b2c.investment.bean.vo.SuggestionVO
|
|
|
+ * @author sunshihao
|
|
|
+ * @date: 2025/8/7 15:49
|
|
|
+ */
|
|
|
+public SuggestionVO KeywordForSuggestions(HashMap<String,String> header, String keyword, String locationName) throws Exception {
|
|
|
+ SuggestionVO suggestionVO = new SuggestionVO();
|
|
|
+ JSONArray payload = new JSONArray();
|
|
|
+ JSONObject payloadItem = new JSONObject();
|
|
|
+ payloadItem.set(AnalysisConst.KEYWORD, keyword);
|
|
|
+ payloadItem.set(AnalysisConst.LANGUAGE_NAME, AnalysisConst.ENGLISH);
|
|
|
+ payloadItem.set(AnalysisConst.LOCATION_NAME, locationName);
|
|
|
+ payloadItem.set("include_serp_info", true);
|
|
|
+ payloadItem.set("include_seed_keyword", true);
|
|
|
+ payloadItem.set("include_seed_keyword", true);
|
|
|
+ payloadItem.set(AnalysisConst.LIMIT, AnalysisConst.NUM_TEN);
|
|
|
+ payload.put(payloadItem);
|
|
|
+ String post = HttpClientUtil.httpPost(analysisConfig.getKeywordForSuggestions(), payload.toString(),header);
|
|
|
+ JSONObject postObject = JSONUtil.parseObj(post);
|
|
|
+ Integer tasksError = postObject.getByPath("tasks_error", Integer.class);
|
|
|
+ if(tasksError==null||tasksError> AnalysisConst.NUM_ZERO){
|
|
|
+ throw new RuntimeException("接口5查询推荐数据 任务失败");
|
|
|
+ }
|
|
|
+ JSONArray jsonArray = postObject.getByPath("tasks[0].result[0].items", JSONArray.class);
|
|
|
+ if(jsonArray==null|| jsonArray.isEmpty()){
|
|
|
+ return suggestionVO;
|
|
|
+ }
|
|
|
+ List<RelatedInfoBO> relatedInfoBOList = new ArrayList<>();
|
|
|
+ for (Object o : jsonArray) {
|
|
|
+ RelatedInfoBO bo = new RelatedInfoBO();
|
|
|
+ JSONObject obj = (JSONObject) o;
|
|
|
+ bo.setKeyword(obj.getByPath(AnalysisConst.KEYWORD, String.class));
|
|
|
+ bo.setSearchVolume(obj.getByPath("keyword_info.search_volume", Long.class));
|
|
|
+ Double competition = obj.getByPath("keyword_info.competition", Double.class);
|
|
|
+ bo.setCompetition(competition==null?null:(int)Math.round(competition * AnalysisConst.NUM_ONE_HUNDRED));
|
|
|
+ relatedInfoBOList.add(bo);
|
|
|
+ }
|
|
|
+ suggestionVO.setRelatedInfoBOList(relatedInfoBOList);
|
|
|
+ return suggestionVO;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ public RivalPartVO rival(HashMap<String, String> header, RivalDTO dto) throws Exception {
|
|
|
+ String websites = dto.getCompetitorWebsite();
|
|
|
+ String[] competitorWebsites = websites.split(",");
|
|
|
+ RivalPartVO vo = new RivalPartVO();
|
|
|
+ if(competitorWebsites.length== AnalysisConst.NUM_ZERO){
|
|
|
+ throw new Exception("竞品网站地址不正确");
|
|
|
+ }
|
|
|
+ List<CompetitorBO> competitorBOS = new ArrayList<>();
|
|
|
+ ThreadPoolExecutor executor = createThreadPool();
|
|
|
+ try {
|
|
|
+ for (String competitorWebsite : competitorWebsites) {
|
|
|
+ CompetitorBO bo = new CompetitorBO();
|
|
|
+ bo.setWebsite(competitorWebsite);
|
|
|
+ // 推荐投放关键词表格(接口3)
|
|
|
+ CompletableFuture<List<RecommendationBO>> recommendationBOListFuture = CompletableFuture.supplyAsync(
|
|
|
+ () -> {
|
|
|
+ try {
|
|
|
+ return keywordsForSite(header, competitorWebsite, dto.getLocationName());
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ executor
|
|
|
+ );
|
|
|
+ // 折线图表格(接口8)
|
|
|
+ CompletableFuture<List<TrafficBO>> trafficBOListFuture = CompletableFuture.supplyAsync(
|
|
|
+ () -> {
|
|
|
+ try {
|
|
|
+ return historicalTraffic(header, competitorWebsite, dto.getLocationName());
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ executor
|
|
|
+ );
|
|
|
+ // 排名表格(接口7)
|
|
|
+ CompletableFuture<List<RankBO>> rankBOListFuture = CompletableFuture.supplyAsync(
|
|
|
+ () -> {
|
|
|
+ try {
|
|
|
+ return rankedKeywords(header, competitorWebsite, dto.getLocationName());
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ executor
|
|
|
+ );
|
|
|
+ CompletableFuture.allOf(recommendationBOListFuture, trafficBOListFuture,rankBOListFuture).join();
|
|
|
+ bo.setRecommendationBOList(recommendationBOListFuture.get());
|
|
|
+ bo.setTrafficBOList(trafficBOListFuture.join());
|
|
|
+ bo.setRankBOList(rankBOListFuture.get());
|
|
|
+ competitorBOS.add(bo);
|
|
|
+ }
|
|
|
+ }finally {
|
|
|
+ shutdownThreadPool(executor);
|
|
|
+ }
|
|
|
+ vo.setCompetitorBOS(competitorBOS);
|
|
|
+ return vo;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ // 接口7
|
|
|
+ public List<RankBO> rankedKeywords(HashMap<String,String> header,String website,String locationName) throws Exception {
|
|
|
+ JSONArray payload = new JSONArray();
|
|
|
+ JSONObject payloadItem = new JSONObject();
|
|
|
+ payloadItem.set(AnalysisConst.TARGET, website);
|
|
|
+ payloadItem.set(AnalysisConst.LANGUAGE_NAME, AnalysisConst.ENGLISH);
|
|
|
+ payloadItem.set(AnalysisConst.LOCATION_NAME, locationName);
|
|
|
+ payload.put(payloadItem);
|
|
|
+ String post = HttpClientUtil.httpPost(analysisConfig.getRankedKeywords(), payload.toString(),header);
|
|
|
+ JSONObject postObject = JSONUtil.parseObj(post);
|
|
|
+ Integer tasksError = postObject.getByPath("tasks_error", Integer.class);
|
|
|
+ if(tasksError==null||tasksError> AnalysisConst.NUM_ZERO){
|
|
|
+ throw new RuntimeException("接口7查询 关键词排名 任务失败");
|
|
|
+ }
|
|
|
+ JSONArray items = postObject.getByPath("tasks[0].result[0].items", JSONArray.class);
|
|
|
+ List<RankBO> list = new ArrayList<>();
|
|
|
+ if(items==null|| items.isEmpty()){
|
|
|
+ return list;
|
|
|
+ }
|
|
|
+ for (Object item : items) {
|
|
|
+ JSONObject obj = (JSONObject) item;
|
|
|
+ String keyword = obj.getByPath("keyword_data.keyword", String.class);
|
|
|
+ Long searchVolume = obj.getByPath("keyword_data.keyword_info.search_volume", Long.class);
|
|
|
+ Double competition = obj.getByPath("keyword_data.keyword_info.competition", Double.class);
|
|
|
+ Integer dp = competition==null?null:(int)Math.round(competition * AnalysisConst.NUM_ONE_HUNDRED);
|
|
|
+ Double cpc = obj.getByPath("keyword_data.keyword_info.cpc", Double.class);
|
|
|
+ Double cpcFormat=cpc==null?null:Math.round(cpc * AnalysisConst.NUM_ONE_HUNDRED) / AnalysisConst.ONE_HUNDRED_DOUBLE;
|
|
|
+ Long monthly = obj.getByPath("keyword_data.keyword_info.search_volume_trend.monthly", Long.class);
|
|
|
+ Long quarterly = obj.getByPath("keyword_data.keyword_info.search_volume_trend.quarterly", Long.class);
|
|
|
+ Long yearly = obj.getByPath("keyword_data.keyword_info.search_volume_trend.yearly", Long.class);
|
|
|
+ RankBO bo = RankBO.builder()
|
|
|
+ .keyword(keyword)
|
|
|
+ .searchVolume(searchVolume)
|
|
|
+ .monthly(monthly)
|
|
|
+ .quarterly(quarterly)
|
|
|
+ .yearly(yearly)
|
|
|
+ .dp(dp)
|
|
|
+ .cpc(cpcFormat)
|
|
|
+ .build();
|
|
|
+ list.add(bo);
|
|
|
+ }
|
|
|
+ return list;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 接口8
|
|
|
+ public List<TrafficBO> historicalTraffic(HashMap<String,String> header,String website,String locationName) throws Exception {
|
|
|
+ JSONArray payload = new JSONArray();
|
|
|
+ JSONObject payloadItem = new JSONObject();
|
|
|
+ JSONArray targets = new JSONArray();
|
|
|
+ String keyword = StringUtils.removeStart(
|
|
|
+ StringUtils.removeStart(
|
|
|
+ StringUtils.removeStart(website, AnalysisConst.HTTPS),
|
|
|
+ AnalysisConst.HTTP),
|
|
|
+ AnalysisConst.WWW);
|
|
|
+ int comIndex = keyword.indexOf(AnalysisConst.COM);
|
|
|
+ if (comIndex != -AnalysisConst.NEGATIVE_ONE) {
|
|
|
+ keyword = keyword.substring(AnalysisConst.NUM_ZERO, comIndex+ AnalysisConst.NUM_FOUR);
|
|
|
+ }
|
|
|
+ targets.add(keyword);
|
|
|
+ payloadItem.set(AnalysisConst.TARGETS,targets);
|
|
|
+ payloadItem.set(AnalysisConst.LANGUAGE_NAME, AnalysisConst.ENGLISH);
|
|
|
+ payloadItem.set(AnalysisConst.LOCATION_NAME, locationName);
|
|
|
+ payload.put(payloadItem);
|
|
|
+ LocalDate currentDate = LocalDate.now();
|
|
|
+ LocalDate currentMonthFirstDay = currentDate.withDayOfMonth(AnalysisConst.NUM_ONE);
|
|
|
+ LocalDate lastYearSameMonthFirstDay = currentDate.minusYears(AnalysisConst.NUM_ONE).plusMonths(AnalysisConst.NUM_ONE).withDayOfMonth(AnalysisConst.NUM_ONE);
|
|
|
+ DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE;
|
|
|
+ payloadItem.set("date_from", lastYearSameMonthFirstDay.format(formatter));
|
|
|
+ payloadItem.set("date_to", currentMonthFirstDay.format(formatter));
|
|
|
+ JSONArray itemTypes = new JSONArray();
|
|
|
+ itemTypes.put("organic");
|
|
|
+ itemTypes.put("paid");
|
|
|
+ payloadItem.set("item_types", itemTypes);
|
|
|
+ JSONObject postData = new JSONObject();
|
|
|
+ postData.set("0", payloadItem);
|
|
|
+
|
|
|
+ String post = HttpClientUtil.httpPost(analysisConfig.getHistoricalTraffic(), postData.toString(),header);
|
|
|
+ JSONObject postObject = JSONUtil.parseObj(post);
|
|
|
+ Integer tasksError = postObject.getByPath("tasks_error", Integer.class);
|
|
|
+ if(tasksError==null||tasksError> AnalysisConst.NUM_ZERO){
|
|
|
+ throw new RuntimeException("接口8查询 历史流量 任务失败");
|
|
|
+ }
|
|
|
+ List<TrafficBO> list = new ArrayList<>();
|
|
|
+ JSONArray organics = postObject.getByPath("tasks[0].result[0].items[0].metrics.organic", JSONArray.class);
|
|
|
+ JSONArray paids = postObject.getByPath("tasks[0].result[0].items[0].metrics.paid", JSONArray.class);
|
|
|
+ Map<Integer, Long> temp = new HashMap<>();
|
|
|
+ if(organics!=null&&!organics.isEmpty()){
|
|
|
+ for (Object organic : organics) {
|
|
|
+ JSONObject obj = (JSONObject) organic;
|
|
|
+ int month = obj.getByPath("month", Integer.class);
|
|
|
+ long organicCount = obj.getByPath("count", Long.class);
|
|
|
+ temp.put(month,organicCount);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if(paids!=null&&!paids.isEmpty()){
|
|
|
+ for (Object paid : paids) {
|
|
|
+ TrafficBO bo = new TrafficBO();
|
|
|
+ JSONObject obj = (JSONObject) paid;
|
|
|
+ int month = obj.getByPath("month", Integer.class);
|
|
|
+ long paidCount = obj.getByPath("count", Long.class);
|
|
|
+ long organicCount = temp.get(month);
|
|
|
+ bo.setX_axis(month);
|
|
|
+ bo.setPaid(paidCount);
|
|
|
+ bo.setOrganic(organicCount);
|
|
|
+ list.add(bo);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ Collections.reverse(list);
|
|
|
+ return list;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 接口3
|
|
|
+ public List<RecommendationBO> keywordsForSite(HashMap<String, String> header, String website, String locationName) throws Exception {
|
|
|
+ JSONArray payload = new JSONArray();
|
|
|
+ JSONObject payloadItem = new JSONObject();
|
|
|
+ payloadItem.set(AnalysisConst.TARGET, website);
|
|
|
+ payloadItem.set(AnalysisConst.LANGUAGE_NAME, AnalysisConst.ENGLISH);
|
|
|
+ payloadItem.set(AnalysisConst.LOCATION_NAME, locationName);
|
|
|
+ payloadItem.set(AnalysisConst.LIMIT, AnalysisConst.NUM_FIFTY);
|
|
|
+ payload.put(payloadItem);
|
|
|
+ String post = HttpClientUtil.httpPost(analysisConfig.getKeywordsForSite(), payload.toString(),header);
|
|
|
+ JSONObject postObject = JSONUtil.parseObj(post);
|
|
|
+ Integer tasksError = postObject.getByPath("tasks_error", Integer.class);
|
|
|
+ if(tasksError==null||tasksError> AnalysisConst.NUM_ZERO){
|
|
|
+ throw new RuntimeException("接口3查询 keywordsForSite 任务失败");
|
|
|
+ }
|
|
|
+ List<RecommendationBO> list = new ArrayList<>();
|
|
|
+ JSONArray results = postObject.getByPath("tasks[0].result", JSONArray.class);
|
|
|
+ if(results==null){
|
|
|
+ return list;
|
|
|
+ }
|
|
|
+ int total = Math.min(results.size(), AnalysisConst.NUM_FIFTY);
|
|
|
+ for(int i = AnalysisConst.NUM_ZERO; i<total; i++){
|
|
|
+ JSONObject result = (JSONObject) results.get(i);
|
|
|
+ RecommendationBO bo = new RecommendationBO();
|
|
|
+ bo.setKeywords(result.getByPath(AnalysisConst.KEYWORD,String.class));
|
|
|
+ bo.setSearchVolume(result.getByPath("search_volume",Long.class));
|
|
|
+ Double cpc = result.getByPath("cpc",Double.class);
|
|
|
+ Double cpcFormat=cpc==null?null:Math.round(cpc * AnalysisConst.NUM_ONE_HUNDRED) / AnalysisConst.ONE_HUNDRED_DOUBLE;
|
|
|
+ bo.setPrice(cpcFormat);
|
|
|
+ list.add(bo);
|
|
|
+ }
|
|
|
+ return list;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String qualitative(String keyword) throws Exception {
|
|
|
+ HashMap<String, String> headers = new HashMap<>();
|
|
|
+ headers.put(AnalysisConst.CONTENT_TYPE, AnalysisConst.APPLICATION_JSON);
|
|
|
+ JSONObject param = new JSONObject();
|
|
|
+ param.set("question", keyword);
|
|
|
+ param.set("initial_search_query_count", AnalysisConst.NUM_THREE);
|
|
|
+ param.set("max_research_loops", AnalysisConst.NUM_THREE);
|
|
|
+ param.set("reasoning_model", analysisConfig.getModelName());
|
|
|
+ String result;
|
|
|
+ try {
|
|
|
+ result = HttpClientUtil.httpPost(analysisConfig.getTranslateAddress() + "/api/research", param.toString(), headers);
|
|
|
+ JSONObject jsonObject = JSONUtil.parseObj(result);
|
|
|
+ String answer = jsonObject.getByPath("answer",String.class);
|
|
|
+ if(StringUtils.isEmpty(answer)){
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+ return dealAnswer(answer);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error(e.getMessage(), "定性分析接口出错");
|
|
|
+ throw new Exception("定性分析接口出错", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private String dealAnswer(String answer) {
|
|
|
+ answer = answer.replace(AnalysisConst.CHINESE, "");
|
|
|
+ String lastPart = getLastPart(answer);
|
|
|
+ String firstPart = getFirstPart(answer);
|
|
|
+ return firstPart + lastPart;
|
|
|
+ }
|
|
|
+
|
|
|
+ private String getLastPart(String answer) {
|
|
|
+ int startIndex = answer.indexOf (AnalysisConst.LAST_PART_MARKER);
|
|
|
+ if (startIndex != AnalysisConst.NEGATIVE_ONE) {
|
|
|
+ return answer.substring (startIndex);
|
|
|
+ } else {
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getFirstPart (String input) {
|
|
|
+ int markerIndex = input.indexOf (AnalysisConst.FIRST_MARKER);
|
|
|
+ if (markerIndex != AnalysisConst.NEGATIVE_ONE) {
|
|
|
+ return input.substring (AnalysisConst.NUM_ZERO, markerIndex);
|
|
|
+ } else {
|
|
|
+ return input;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|