|
@@ -0,0 +1,400 @@
|
|
|
+package com.pig4cloud.pig.admin.service.impl;
|
|
|
+
|
|
|
+
|
|
|
+import cn.hutool.core.collection.CollUtil;
|
|
|
+import cn.hutool.core.util.StrUtil;
|
|
|
+import com.alibaba.fastjson.JSONArray;
|
|
|
+import com.alibaba.fastjson.JSONObject;
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
|
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|
|
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
+import com.pig4cloud.pig.admin.api.entity.marketing.MarketingConfigDetail;
|
|
|
+import com.pig4cloud.pig.admin.api.entity.marketing.MarketingConfigDomain;
|
|
|
+import com.pig4cloud.pig.admin.api.entity.marketing.MarketingReport;
|
|
|
+import com.pig4cloud.pig.admin.api.vo.marketing.*;
|
|
|
+import com.pig4cloud.pig.admin.mapper.MarketingReportMapper;
|
|
|
+import com.pig4cloud.pig.admin.service.MarketingConfigDetailService;
|
|
|
+import com.pig4cloud.pig.admin.service.MarketingConfigDomainService;
|
|
|
+import com.pig4cloud.pig.admin.service.MarketingConfigService;
|
|
|
+import com.pig4cloud.pig.admin.service.MarketingReportService;
|
|
|
+import jakarta.servlet.http.HttpServletRequest;
|
|
|
+import lombok.AllArgsConstructor;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import okhttp3.*;
|
|
|
+import org.springframework.beans.BeanUtils;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
+
|
|
|
+import java.io.IOException;
|
|
|
+import java.net.InetAddress;
|
|
|
+import java.net.UnknownHostException;
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.time.OffsetDateTime;
|
|
|
+import java.time.format.DateTimeFormatter;
|
|
|
+import java.util.*;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * @author: lwh
|
|
|
+ * @date: 2025-07-16
|
|
|
+ * @description: TODO
|
|
|
+ */
|
|
|
+
|
|
|
+@Service
|
|
|
+@Slf4j
|
|
|
+@AllArgsConstructor
|
|
|
+public class MarketingConfigServiceImpl implements MarketingConfigService {
|
|
|
+
|
|
|
+ private final MarketingConfigDomainService domainService;
|
|
|
+
|
|
|
+ private final MarketingConfigDetailService detailService;
|
|
|
+
|
|
|
+ private final MarketingReportService reportService;
|
|
|
+
|
|
|
+ private final MarketingReportMapper reportMapper;
|
|
|
+
|
|
|
+
|
|
|
+ // 从配置文件读取URL
|
|
|
+// @Value("${marketing.app.url}")
|
|
|
+ private static final String getAppListUrl = "http://192.168.80.130:8000/ipa/getApps";
|
|
|
+
|
|
|
+ // 从配置文件读取accessKey(更安全的做法)
|
|
|
+// @Value("${marketing.app.access-key}")
|
|
|
+ private static final String accessKey = "4ea5ba93-d222-45dc-862a-1c7fc7789d11";
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 分页查询营销配置列表
|
|
|
+ * @param page 分页参数
|
|
|
+ * @param domain 域名
|
|
|
+ * @return 营销配置列表
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public Page pageMarketingConfig(Page page, String domain) {
|
|
|
+ LambdaQueryWrapper<MarketingConfigDomain> wrapper = Wrappers.<MarketingConfigDomain>lambdaQuery()
|
|
|
+ .like(StrUtil.isNotBlank(domain), MarketingConfigDomain::getDomain,domain);
|
|
|
+
|
|
|
+ Page domainPage = domainService.page(page, wrapper);
|
|
|
+
|
|
|
+ ArrayList<SaveMarketingConfigVO> res = new ArrayList<>();
|
|
|
+
|
|
|
+ List<MarketingConfigDomain> records = domainPage.getRecords();
|
|
|
+ records.forEach(config -> {
|
|
|
+ SaveMarketingConfigVO saveVo = new SaveMarketingConfigVO();
|
|
|
+ saveVo.setId(config.getId());
|
|
|
+ saveVo.setDomain(config.getDomain());
|
|
|
+ List<MarketingConfigDetail> detailList = detailService.list(new QueryWrapper<MarketingConfigDetail>().eq("domain_id", config.getId()));
|
|
|
+
|
|
|
+
|
|
|
+ // 集合转换
|
|
|
+ List<MarketingConfigDetailVO> detailVOS = detailList.stream()
|
|
|
+ .map(detail -> {
|
|
|
+ MarketingConfigDetailVO detailVO = new MarketingConfigDetailVO();
|
|
|
+ BeanUtils.copyProperties(detail, detailVO);
|
|
|
+ return detailVO;
|
|
|
+ })
|
|
|
+ .collect(Collectors.toList());
|
|
|
+
|
|
|
+ saveVo.setConfigs(detailVOS);
|
|
|
+ res.add(saveVo);
|
|
|
+ });
|
|
|
+
|
|
|
+ domainPage.setRecords(res);
|
|
|
+ return domainPage;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取营销配置详情
|
|
|
+ * @param id ID
|
|
|
+ * @return 营销配置详情
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public SaveMarketingConfigVO getById(String id) {
|
|
|
+ MarketingConfigDomain domain = domainService.getById(id);
|
|
|
+ if (domain != null){
|
|
|
+ SaveMarketingConfigVO saveVo = new SaveMarketingConfigVO();
|
|
|
+ saveVo.setId(domain.getId());
|
|
|
+ saveVo.setDomain(domain.getDomain());
|
|
|
+ List<MarketingConfigDetail> detailList = detailService.list(new QueryWrapper<MarketingConfigDetail>().eq("domain_id", domain.getId()));
|
|
|
+ // 集合转换
|
|
|
+ List<MarketingConfigDetailVO> detailVOS = detailList.stream()
|
|
|
+ .map(detail -> {
|
|
|
+ MarketingConfigDetailVO detailVO = new MarketingConfigDetailVO();
|
|
|
+ BeanUtils.copyProperties(detail, detailVO);
|
|
|
+ return detailVO;
|
|
|
+ })
|
|
|
+ .collect(Collectors.toList());
|
|
|
+ saveVo.setConfigs(detailVOS);
|
|
|
+ return saveVo;
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 添加营销配置
|
|
|
+ * @param reqVo 营销配置
|
|
|
+ * @return ID
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ @Transactional
|
|
|
+ public Long addMarketingConfig(SaveMarketingConfigVO reqVo) {
|
|
|
+ MarketingConfigDomain configDomain = new MarketingConfigDomain();
|
|
|
+ configDomain.setDomain(reqVo.getDomain());
|
|
|
+ domainService.save(configDomain);
|
|
|
+
|
|
|
+ reqVo.getConfigs().forEach(detailVO -> {
|
|
|
+ MarketingConfigDetail detail = new MarketingConfigDetail();
|
|
|
+ detail.setDomainId(configDomain.getId());
|
|
|
+ detail.setIp(detailVO.getIp());
|
|
|
+ detail.setAppId(detailVO.getAppId());
|
|
|
+ detail.setAppName(detailVO.getAppName());
|
|
|
+ detail.setAppUrl(detailVO.getAppUrl());
|
|
|
+ detailService.save(detail);
|
|
|
+ });
|
|
|
+ return configDomain.getId();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 修改营销配置
|
|
|
+ * @param reqVo 营销配置
|
|
|
+ * @return ID
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ @Transactional
|
|
|
+ public Long updateMarketingConfig(SaveMarketingConfigVO reqVo) {
|
|
|
+ MarketingConfigDomain configDomain = domainService.getById(reqVo.getId());
|
|
|
+ if (configDomain == null){
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ // 修改域名
|
|
|
+ configDomain.setDomain(reqVo.getDomain());
|
|
|
+ domainService.updateById(configDomain);
|
|
|
+
|
|
|
+ // 修改配置项
|
|
|
+ Long id = configDomain.getId();
|
|
|
+ List<MarketingConfigDetail> list = detailService.list(new QueryWrapper<MarketingConfigDetail>().eq("domain_id", id));
|
|
|
+
|
|
|
+ List<MarketingConfigDetailVO> configs = reqVo.getConfigs();
|
|
|
+ Map<Long, MarketingConfigDetailVO> idMap = configs.stream()
|
|
|
+ .collect(Collectors.toMap(
|
|
|
+ MarketingConfigDetailVO::getId, // key: 取对象的 id
|
|
|
+ detail -> detail, // value: 对象本身
|
|
|
+ (existing, replacement) -> existing // 处理 key 冲突:保留先出现的元素
|
|
|
+ ));
|
|
|
+
|
|
|
+ if (!list.isEmpty()) {
|
|
|
+ // 先清除没有的配置项
|
|
|
+ list.forEach(detail -> {
|
|
|
+ if (!idMap.containsKey(detail.getId())) {
|
|
|
+ detailService.removeById(detail);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ // 添加/新增的配置项
|
|
|
+ configs.forEach(detailVO -> {
|
|
|
+ if (detailVO.getId() == null) {
|
|
|
+ // 新增
|
|
|
+ MarketingConfigDetail detail = new MarketingConfigDetail();
|
|
|
+ detail.setDomainId(id);
|
|
|
+ detail.setIp(detailVO.getIp());
|
|
|
+ detail.setAppName(detailVO.getAppName());
|
|
|
+ detail.setAppUrl(detailVO.getAppUrl());
|
|
|
+ detailService.save(detail);
|
|
|
+ } else {
|
|
|
+ // 修改
|
|
|
+ MarketingConfigDetail detail = detailService.getById(detailVO.getId());
|
|
|
+ detail.setIp(detailVO.getIp());
|
|
|
+ detail.setAppName(detailVO.getAppName());
|
|
|
+ detail.setAppUrl(detailVO.getAppUrl());
|
|
|
+ detailService.updateById(detail);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }else {
|
|
|
+ configs.forEach(detailVO -> {
|
|
|
+ MarketingConfigDetail detail = new MarketingConfigDetail();
|
|
|
+ detail.setDomainId(id);
|
|
|
+ detail.setIp(detailVO.getIp());
|
|
|
+ detail.setAppName(detailVO.getAppName());
|
|
|
+ detail.setAppUrl(detailVO.getAppUrl());
|
|
|
+ detailService.save(detail);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ return id;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 删除营销配置
|
|
|
+ * @param ids ID
|
|
|
+ * @return Boolean
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ @Transactional
|
|
|
+ public Boolean delMarketingConfig(Long[] ids) {
|
|
|
+ // 删除域名
|
|
|
+ boolean delDomain = domainService.removeBatchByIds(CollUtil.toList(ids));
|
|
|
+ // 删除配置项
|
|
|
+ boolean delDetail = detailService.remove(new QueryWrapper<MarketingConfigDetail>().in("domain_id", ids));
|
|
|
+ return (delDomain && delDetail);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 上报营销数据
|
|
|
+ * @param reqVo 营销数据
|
|
|
+ * @return ID
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public HashMap<String,Object> saveMarketingReport(MarketingReportVO reqVo, HttpServletRequest request) {
|
|
|
+
|
|
|
+ // 获取客户端IP地址
|
|
|
+ String ip = null;
|
|
|
+ try {
|
|
|
+ ip = getIp(request);
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
+
|
|
|
+ MarketingReport marketingReport = new MarketingReport();
|
|
|
+ BeanUtils.copyProperties(reqVo, marketingReport);
|
|
|
+ marketingReport.setTimestamp(OffsetDateTime.parse(reqVo.getTimestamp()).toLocalDateTime());
|
|
|
+ marketingReport.setIp(ip);
|
|
|
+ reportService.save(marketingReport);
|
|
|
+
|
|
|
+ HashMap<String, Object> result = new HashMap<>();
|
|
|
+ result.put("type","link");
|
|
|
+ result.put("downloadUrl", "");
|
|
|
+ MarketingConfigDomain domain = domainService.getOne(new QueryWrapper<MarketingConfigDomain>().eq("domain", reqVo.getDomain()));
|
|
|
+ if (domain == null) {
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ MarketingConfigDetail detail = detailService.getOne(new QueryWrapper<MarketingConfigDetail>().eq("domain_id", domain.getId()).eq("ip", ip));
|
|
|
+ if (detail == null) {
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ result.put("downloadUrl", detail.getAppUrl());
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 分页查询营销上报数据
|
|
|
+ * @return 分页数据
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public Page pageMarketingData(PageMarketingReportReqVO reqVo) {
|
|
|
+ String domain = reqVo.getDomain();
|
|
|
+ String ip = reqVo.getIp();
|
|
|
+ String referrer = reqVo.getReferrer();
|
|
|
+
|
|
|
+ // 计算时间条件
|
|
|
+ LocalDateTime now = LocalDateTime.now();
|
|
|
+ LocalDateTime lastHourTime = now.minusHours(1);
|
|
|
+ LocalDateTime todayStartTime = now.withHour(0).withMinute(0).withSecond(0).withNano(0);
|
|
|
+
|
|
|
+ // 格式化时间
|
|
|
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
|
|
+ String lastHourTimeStr = lastHourTime.format(formatter);
|
|
|
+ String todayStartTimeStr = todayStartTime.format(formatter);
|
|
|
+
|
|
|
+ // 计算偏移量
|
|
|
+ long offset = (reqVo.getCurrent() - 1) * reqVo.getSize();
|
|
|
+
|
|
|
+ // 查询数据
|
|
|
+ Page<PageMarketingReportRspVO> page = new Page<>(reqVo.getCurrent(), reqVo.getSize());
|
|
|
+ page.setRecords(reportMapper.selectMarketingReportGroupBy(domain, ip, referrer, lastHourTimeStr, todayStartTimeStr, offset, reqVo.getSize()));
|
|
|
+ page.setTotal(reportMapper.selectMarketingReportGroupByCount(domain, ip, referrer));
|
|
|
+
|
|
|
+ return page;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取应用列表
|
|
|
+ *
|
|
|
+ * @param pageNum 当前页
|
|
|
+ * @param pageSize 分页大小
|
|
|
+ * @return 应用列表
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public Page pageAppList(int pageNum, int pageSize) {
|
|
|
+ Page page = new Page(pageNum, pageSize);
|
|
|
+
|
|
|
+ // 构建请求参数
|
|
|
+ JSONObject requestParam = new JSONObject();
|
|
|
+ requestParam.put("accessKey", accessKey);
|
|
|
+ requestParam.put("page", pageNum);
|
|
|
+ requestParam.put("size", pageSize);
|
|
|
+
|
|
|
+ // 创建OkHttp客户端
|
|
|
+ OkHttpClient client = new OkHttpClient();
|
|
|
+
|
|
|
+ // 构建POST请求体
|
|
|
+ MediaType mediaType = MediaType.parse("application/json; charset=utf-8");
|
|
|
+ RequestBody body = RequestBody.create(mediaType, requestParam.toJSONString());
|
|
|
+
|
|
|
+ // 构建请求
|
|
|
+ Request request = new Request.Builder()
|
|
|
+ .url(getAppListUrl)
|
|
|
+ .post(body)
|
|
|
+ .build();
|
|
|
+
|
|
|
+ try {
|
|
|
+ ArrayList<MarketingAppVO> res = new ArrayList<>();
|
|
|
+ // 发送请求并获取响应
|
|
|
+ Response response = client.newCall(request).execute();
|
|
|
+ if (response.isSuccessful() && response.body() != null) {
|
|
|
+ JSONObject reponseBody = JSONObject.parseObject(response.body().string());
|
|
|
+ JSONArray content = reponseBody.getJSONArray("content");
|
|
|
+ content.forEach(item -> {
|
|
|
+ JSONObject app = (JSONObject) item;
|
|
|
+ MarketingAppVO marketingAppVO = new MarketingAppVO();
|
|
|
+ marketingAppVO.setAppId(app.getString("id"));
|
|
|
+ marketingAppVO.setAppName(app.getString("name"));
|
|
|
+ marketingAppVO.setAppUrl("https://"+app.getString("download_url")+"/"+marketingAppVO.getAppId());
|
|
|
+ marketingAppVO.setBackUpUrl("https://"+app.getString("backup_url")+"/"+marketingAppVO.getAppId());
|
|
|
+ res.add(marketingAppVO);
|
|
|
+ });
|
|
|
+ page.setTotal(reponseBody.getInteger("count"));
|
|
|
+ } else {
|
|
|
+ // 响应失败
|
|
|
+ log.error("请求失败,响应码: {}", response.code());
|
|
|
+ }
|
|
|
+ page.setRecords(res);
|
|
|
+ return page;
|
|
|
+ } catch (IOException e) {
|
|
|
+ // 网络请求异常
|
|
|
+ log.error("网络请求异常: {}", e.getLocalizedMessage());
|
|
|
+ }
|
|
|
+ // 所有异常情况返回空列表
|
|
|
+ return page;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取ip地址
|
|
|
+ */
|
|
|
+ public static String getIp(HttpServletRequest request) {
|
|
|
+ String ip = request.getHeader("X-Forwarded-For");
|
|
|
+ if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
|
|
|
+ ip = request.getHeader("Proxy-Client-IP");
|
|
|
+ }
|
|
|
+ if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
|
|
|
+ ip = request.getHeader("WL-Proxy-Client-IP");
|
|
|
+ }
|
|
|
+ if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
|
|
|
+ ip = request.getRemoteAddr();
|
|
|
+ }
|
|
|
+ String comma = ",";
|
|
|
+ String localhost = "127.0.0.1";
|
|
|
+ if (ip.contains(comma)) {
|
|
|
+ ip = ip.split(",")[0];
|
|
|
+ }
|
|
|
+ if (localhost.equals(ip)) {
|
|
|
+ // 获取本机真正的ip地址
|
|
|
+ try {
|
|
|
+ ip = InetAddress.getLocalHost().getHostAddress();
|
|
|
+ } catch (UnknownHostException e) {
|
|
|
+ log.error(e.getMessage(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return ip;
|
|
|
+ }
|
|
|
+}
|