瀏覽代碼

New: 新增营销配置、营销数据统计功能

lwh 3 周之前
父節點
當前提交
43d2f9fe18
共有 23 個文件被更改,包括 1513 次插入0 次删除
  1. 96 0
      pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/entity/marketing/MarketingConfigDetail.java
  2. 74 0
      pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/entity/marketing/MarketingConfigDomain.java
  3. 158 0
      pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/entity/marketing/MarketingReport.java
  4. 37 0
      pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/vo/marketing/MarketingAppVO.java
  5. 51 0
      pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/vo/marketing/MarketingConfigDetailVO.java
  6. 105 0
      pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/vo/marketing/MarketingReportVO.java
  7. 32 0
      pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/vo/marketing/PageMarketingConfigVO.java
  8. 44 0
      pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/vo/marketing/PageMarketingReportReqVO.java
  9. 56 0
      pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/vo/marketing/PageMarketingReportRspVO.java
  10. 34 0
      pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/vo/marketing/SaveMarketingConfigVO.java
  11. 127 0
      pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/controller/MarketingConfigController.java
  12. 15 0
      pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/mapper/MarketingConfigDetailMapper.java
  13. 15 0
      pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/mapper/MarketingConfigDomainMapper.java
  14. 30 0
      pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/mapper/MarketingReportMapper.java
  15. 14 0
      pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/service/MarketingConfigDetailService.java
  16. 13 0
      pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/service/MarketingConfigDomainService.java
  17. 74 0
      pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/service/MarketingConfigService.java
  18. 15 0
      pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/service/MarketingReportService.java
  19. 19 0
      pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/service/impl/MarketingConfigDetailServiceImpl.java
  20. 19 0
      pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/service/impl/MarketingConfigDomainServiceImpl.java
  21. 400 0
      pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/service/impl/MarketingConfigServiceImpl.java
  22. 20 0
      pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/service/impl/MarketingReportServiceImpl.java
  23. 65 0
      pig-upms/pig-upms-biz/src/main/resources/mapper/MarketingReportMapper.xml

+ 96 - 0
pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/entity/marketing/MarketingConfigDetail.java

@@ -0,0 +1,96 @@
+package com.pig4cloud.pig.admin.api.entity.marketing;
+
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.time.LocalDateTime;
+
+/**
+ * @author: lwh
+ * @date: 2025-07-16
+ * @description: 营销方案配置详情
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class MarketingConfigDetail  extends Model<MarketingConfigDetail> {
+
+	private static final long serialVersionUID = 1L;
+
+
+	/**
+	 * id
+	 */
+	@TableId(value = "id")
+	@Schema(description = "id")
+	private Long id;
+
+	/**
+	 * 域名ID
+	 */
+	@Schema(description = "域名ID")
+	private Long domainId;
+
+	/**
+	 * ip
+	 */
+	@Schema(description = "ip")
+	private String ip;
+
+	/**
+	 * 应用ID
+	 */
+	@Schema(description = "应用ID")
+	private String appId;
+
+	/**
+	 * 应用名称
+	 */
+	@Schema(description = "应用名称")
+	private String appName;
+
+	/**
+	 * 应用链接
+	 */
+	@Schema(description = "应用链接")
+	private String appUrl;
+
+	/**
+	 * 删除标记
+	 */
+	@TableLogic
+	@TableField(fill = FieldFill.INSERT)
+	@Schema(description = "删除标记,1:已删除,0:正常")
+	private String delFlag;
+
+	/**
+	 * 创建人
+	 */
+	@TableField(fill = FieldFill.INSERT)
+	@Schema(description = "创建人")
+	private String createBy;
+
+	/**
+	 * 修改人
+	 */
+	@TableField(fill = FieldFill.UPDATE)
+	@Schema(description = "修改人")
+	private String updateBy;
+
+	/**
+	 * 创建时间
+	 */
+	@TableField(fill = FieldFill.INSERT)
+	@Schema(description = "创建时间")
+	private LocalDateTime createTime;
+
+	/**
+	 * 更新时间
+	 */
+	@TableField(fill = FieldFill.UPDATE)
+	@Schema(description = "更新时间")
+	private LocalDateTime updateTime;
+}

+ 74 - 0
pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/entity/marketing/MarketingConfigDomain.java

@@ -0,0 +1,74 @@
+package com.pig4cloud.pig.admin.api.entity.marketing;
+
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.time.LocalDateTime;
+
+/**
+ * @author: lwh
+ * @date: 2025-07-16
+ * @description: 营销方案配置域名
+ */
+
+@Data
+@Schema(description = "营销方案配置域名")
+@EqualsAndHashCode(callSuper = true)
+public class MarketingConfigDomain extends Model<MarketingConfigDomain> {
+
+	/**
+	 * id
+	 */
+	@TableId(value = "id")
+	@Schema(description = "id")
+	private Long id;
+
+	/**
+	 * 域名
+	 */
+	@Schema(description = "域名")
+	private String domain;
+
+	/**
+	 * 删除标记
+	 */
+	@TableLogic
+	@TableField(fill = FieldFill.INSERT)
+	@Schema(description = "删除标记,1:已删除,0:正常")
+	private String delFlag;
+
+	/**
+	 * 创建人
+	 */
+	@TableField(fill = FieldFill.INSERT)
+	@Schema(description = "创建人")
+	private String createBy;
+
+	/**
+	 * 修改人
+	 */
+	@TableField(fill = FieldFill.UPDATE)
+	@Schema(description = "修改人")
+	private String updateBy;
+
+	/**
+	 * 创建时间
+	 */
+	@TableField(fill = FieldFill.INSERT)
+	@Schema(description = "创建时间")
+	private LocalDateTime createTime;
+
+	/**
+	 * 更新时间
+	 */
+	@TableField(fill = FieldFill.UPDATE)
+	@Schema(description = "更新时间")
+	private LocalDateTime updateTime;
+}

+ 158 - 0
pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/entity/marketing/MarketingReport.java

@@ -0,0 +1,158 @@
+package com.pig4cloud.pig.admin.api.entity.marketing;
+
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.time.LocalDateTime;
+
+/**
+ * @author: lwh
+ * @date: 2025-07-16
+ * @description: 营销上报数据
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class MarketingReport extends Model<MarketingReport> {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * id
+	 */
+	@TableId(value = "id")
+	@Schema(description = "id")
+	private Long id;
+
+	/**
+	 * ip
+	 */
+	@Schema(description = "ip")
+	private String ip;
+
+	/**
+	 * 域名
+	 */
+	@Schema(description = "域名")
+	private String domain;
+
+	/**
+	 * 流量的原始来源
+	 */
+	@Schema(description = "流量的原始来源")
+	private String utmSource;
+
+	/**
+	 * 推广渠道类型
+	 */
+	@Schema(description = "推广渠道类型")
+	private String utmMedium;
+
+	/**
+	 * 活动名称
+	 */
+	@Schema(description = "活动名称")
+	private String utmCampaign;
+
+	/**
+	 * 关键词
+	 */
+	@Schema(description = "关键词")
+	private String utmTerm;
+
+	/**
+	 * 活动内容
+	 */
+	@Schema(description = "活动内容")
+	private String utmContent;
+
+	/**
+	 * 跳转来源
+	 */
+	@Schema(description = "跳转来源")
+	private String referrer;
+
+	/**
+	 * 时间戳
+	 */
+	@Schema(description = "时间戳")
+	private LocalDateTime timestamp;
+
+	/**
+	 * 埋点服务url地址
+	 */
+	@Schema(description = "埋点服务url地址")
+	private String url;
+
+	/**
+	 * 是否移动端
+	 */
+	@Schema(description = "是否移动端")
+	private Boolean isMobile;
+
+	/**
+	 * 浏览器
+	 */
+	@Schema(description = "浏览器")
+	private String browser;
+
+	/**
+	 * 用户代理信息
+	 */
+	@Schema(description = "用户代理信息")
+	private String userAgent;
+
+	/**
+	 * 操作系统类型
+	 */
+	@Schema(description = "操作系统类型")
+	private String osType;
+
+	/**
+	 * 操作系统版本
+	 */
+	@Schema(description = "操作系统版本")
+	private String osVersion;
+
+	/**
+	 * 删除标记
+	 */
+	@TableLogic
+	@TableField(fill = FieldFill.INSERT)
+	@Schema(description = "删除标记,1:已删除,0:正常")
+	private String delFlag;
+
+	/**
+	 * 创建人
+	 */
+	@TableField(fill = FieldFill.INSERT)
+	@Schema(description = "创建人")
+	private String createBy;
+
+	/**
+	 * 修改人
+	 */
+	@TableField(fill = FieldFill.UPDATE)
+	@Schema(description = "修改人")
+	private String updateBy;
+
+	/**
+	 * 创建时间
+	 */
+	@TableField(fill = FieldFill.INSERT)
+	@Schema(description = "创建时间")
+	private LocalDateTime createTime;
+
+	/**
+	 * 更新时间
+	 */
+	@TableField(fill = FieldFill.UPDATE)
+	@Schema(description = "更新时间")
+	private LocalDateTime updateTime;
+}

+ 37 - 0
pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/vo/marketing/MarketingAppVO.java

@@ -0,0 +1,37 @@
+package com.pig4cloud.pig.admin.api.vo.marketing;
+
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * @author: lwh
+ * @date: 2025-07-17
+ * @description: 营销应用
+ */
+@Data
+public class MarketingAppVO {
+	/**
+	 * 应用ID
+	 */
+	@Schema(description = "应用ID")
+	String appId;
+
+	/**
+	 * 应用名称
+	 */
+	@Schema(description = "应用名称")
+	String appName;
+
+	/**
+	 * 应用下载链接
+	 */
+	@Schema(description = "应用下载链接")
+	String appUrl;
+
+	/**
+	 * 应用下载链接
+	 */
+	@Schema(description = "应用备用下载链接")
+	String backUpUrl;
+}

+ 51 - 0
pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/vo/marketing/MarketingConfigDetailVO.java

@@ -0,0 +1,51 @@
+package com.pig4cloud.pig.admin.api.vo.marketing;
+
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+/**
+ * @author: lwh
+ * @date: 2025-07-16
+ * @description: 营销方案配置详情VO
+ */
+
+@Data
+public class MarketingConfigDetailVO {
+
+	/**
+	 * id
+	 */
+	@Schema(description = "id")
+	private Long id;
+
+	/**
+	 * ip
+	 */
+	@Schema(description = "ip")
+	@NotBlank(message = "ip不能为空")
+	private String ip;
+
+	/**
+	 * 应用ID
+	 */
+	@Schema(description = "应用ID")
+	@NotBlank(message = "应用ID不能为空")
+	private String appId;
+
+	/**
+	 * 应用名称
+	 */
+	@Schema(description = "应用名称")
+	@NotBlank(message = "应用名称不能为空")
+	private String appName;
+
+	/**
+	 * 应用链接
+	 */
+	@Schema(description = "应用链接")
+	@NotBlank(message = "应用链接不能为空")
+	private String appUrl;
+
+}

+ 105 - 0
pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/vo/marketing/MarketingReportVO.java

@@ -0,0 +1,105 @@
+package com.pig4cloud.pig.admin.api.vo.marketing;
+
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+
+/**
+ * @author: lwh
+ * @date: 2025-07-16
+ * @description: 营销数据上报VO
+ */
+@Data
+public class MarketingReportVO {
+
+	/**
+	 * id
+	 */
+	@Schema(description = "id")
+	private Long id;
+
+	/**
+	 * 域名
+	 */
+	@Schema(description = "域名")
+	private String domain;
+
+	/**
+	 * 流量的原始来源
+	 */
+	@Schema(description = "流量的原始来源")
+	private String utmSource;
+
+	/**
+	 * 推广渠道类型
+	 */
+	@Schema(description = "推广渠道类型")
+	private String utmMedium;
+
+	/**
+	 * 活动名称
+	 */
+	@Schema(description = "活动名称")
+	private String utmCampaign;
+
+	/**
+	 * 关键词
+	 */
+	@Schema(description = "关键词")
+	private String utmTerm;
+
+	/**
+	 * 活动内容
+	 */
+	@Schema(description = "活动内容")
+	private String utmContent;
+
+	/**
+	 * 跳转来源
+	 */
+	@Schema(description = "跳转来源")
+	private String referrer;
+
+	/**
+	 * 时间戳
+	 */
+	@Schema(description = "时间戳")
+	private String timestamp;
+
+	/**
+	 * 埋点服务url地址
+	 */
+	@Schema(description = "埋点服务url地址")
+	private String url;
+
+	/**
+	 * 是否移动端
+	 */
+	@Schema(description = "是否移动端")
+	private Boolean isMobile;
+
+	/**
+	 * 浏览器
+	 */
+	@Schema(description = "浏览器")
+	private String browser;
+
+	/**
+	 * 用户代理信息
+	 */
+	@Schema(description = "用户代理信息")
+	private String userAgent;
+
+	/**
+	 * 操作系统类型
+	 */
+	@Schema(description = "操作系统类型")
+	private String osType;
+
+	/**
+	 * 操作系统版本
+	 */
+	@Schema(description = "操作系统版本")
+	private String osVersion;
+}

+ 32 - 0
pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/vo/marketing/PageMarketingConfigVO.java

@@ -0,0 +1,32 @@
+package com.pig4cloud.pig.admin.api.vo.marketing;
+
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * @author: lwh
+ * @date: 2025-07-16
+ * @description: 分页查询营销配置VO
+ */
+@Data
+public class PageMarketingConfigVO {
+
+	/**
+	 * 每页显示条数,默认 10
+	 */
+	@Schema(description = "每页显示条数")
+	private long size = 10;
+
+	/**
+	 * 当前页
+	 */
+	@Schema(description = "当前页")
+	private long current = 1;
+
+	/**
+	 * 域名
+	 */
+	@Schema(description = "域名")
+	private String domain;
+}

+ 44 - 0
pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/vo/marketing/PageMarketingReportReqVO.java

@@ -0,0 +1,44 @@
+package com.pig4cloud.pig.admin.api.vo.marketing;
+
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * @author: lwh
+ * @date: 2025-07-16
+ * @description: 分页查询营销上报数据请求VO
+ */
+@Data
+public class PageMarketingReportReqVO {
+
+	/**
+	 * 每页显示条数,默认 10
+	 */
+	@Schema(description = "每页显示条数")
+	private long size = 10;
+
+	/**
+	 * 当前页
+	 */
+	@Schema(description = "当前页")
+	private long current = 1;
+
+	/**
+	 * 域名
+	 */
+	@Schema(description = "域名")
+	private String domain;
+
+	/**
+	 * ip
+	 */
+	@Schema(description = "ip")
+	private String ip;
+
+	/**
+	 * 跳转来源
+	 */
+	@Schema(description = "跳转来源")
+	private String referrer;
+}

+ 56 - 0
pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/vo/marketing/PageMarketingReportRspVO.java

@@ -0,0 +1,56 @@
+package com.pig4cloud.pig.admin.api.vo.marketing;
+
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * @author: lwh
+ * @date: 2025-07-16
+ * @description: 分页查询营销上报数据响应VO
+ */
+@Data
+public class PageMarketingReportRspVO {
+
+	/**
+	 * ip
+	 */
+	@Schema(description = "ip")
+	private String ip;
+
+	/**
+	 * 域名
+	 */
+	@Schema(description = "域名")
+	private String domain;
+
+	/**
+	 * 跳转来源
+	 */
+	@Schema(description = "跳转来源")
+	private String referrer;
+
+	/**
+	 * 总访问量
+	 */
+	@Schema(description = "总访问量")
+	private Long total;
+
+	/**
+	 * 日活
+	 */
+	@Schema(description = "日活")
+	private Long daily;
+
+	/**
+	 * 时活
+	 */
+	@Schema(description = "时活")
+	private Long hourly;
+
+	/**
+	 * 15分钟在线人数
+	 */
+	@Schema(description = "15分钟在线人数")
+	private Long online;
+}

+ 34 - 0
pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/vo/marketing/SaveMarketingConfigVO.java

@@ -0,0 +1,34 @@
+package com.pig4cloud.pig.admin.api.vo.marketing;
+
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @author: lwh
+ * @date: 2025-07-16
+ * @description: 新增/更新营销方案配置
+ */
+@Data
+@Schema(description = "新增/更新营销方案配置")
+public class SaveMarketingConfigVO implements Serializable {
+
+	private static final long serialVersionUID = 1L;
+
+	@Schema(description = "id")
+	private Long id;
+
+	@Schema(description = "域名")
+	@NotBlank(message = "域名不能为空")
+	private String domain;
+
+	@Valid
+	@Schema(description = "营销配置详情")
+	private List<MarketingConfigDetailVO> configs;
+
+}

+ 127 - 0
pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/controller/MarketingConfigController.java

@@ -0,0 +1,127 @@
+package com.pig4cloud.pig.admin.controller;
+
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.pig4cloud.pig.admin.api.vo.marketing.*;
+import com.pig4cloud.pig.admin.service.MarketingConfigService;
+import com.pig4cloud.pig.common.core.util.R;
+import com.pig4cloud.pig.common.security.annotation.Inner;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.security.SecurityRequirement;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.validation.Valid;
+import lombok.AllArgsConstructor;
+import org.springdoc.core.annotations.ParameterObject;
+import org.springframework.http.HttpHeaders;
+import org.springframework.web.bind.annotation.*;
+
+
+
+/**
+ * @author: lwh
+ * @date: 2025-07-15
+ * @description: 营销方案配置
+ */
+@RestController
+@AllArgsConstructor
+@RequestMapping("/marketing/config")
+@Tag(description = "marketingConfig", name = "营销方案配置")
+@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
+public class MarketingConfigController {
+
+	private final MarketingConfigService configService;
+
+	/**
+	 * 分页查询营销配置列表
+	 * @param reqVo 分页参数对象
+	 * @return 分页查询结果
+	 */
+	@GetMapping("/page")
+	@Operation(summary = "分页查询营销配置列表")
+	public R pageMarketingConfig(@ParameterObject PageMarketingConfigVO reqVo) {
+
+		Page page = new Page(reqVo.getCurrent(), reqVo.getSize());
+		return R.ok(configService.pageMarketingConfig(page,reqVo.getDomain()));
+	}
+
+	/**
+	 * 通过ID查询营销方案配置详情
+	 * @param id 域名ID
+	 * @return 营销方案配置详情
+	 */
+	@GetMapping("/{id}")
+	@Operation(summary = "通过ID查询营销方案配置详情")
+	public R getById(@PathVariable String id) {
+		if (id == null || id.isEmpty()){
+			return R.failed("ID不能为空");
+		}
+		SaveMarketingConfigVO resVo = configService.getById(id);
+		return R.ok(resVo);
+	}
+
+	/**
+	 * 保存/更新营销配置
+	 * @param reqVo 营销配置VO
+	 * @return 保存结果
+	 */
+	@PostMapping("/save")
+	@Operation(summary = "保存/更新营销配置")
+	public R saveMarketingConfig(@Valid @RequestBody SaveMarketingConfigVO reqVo) {
+		if (reqVo.getId() == null){
+			// 新增
+			Long id = configService.addMarketingConfig(reqVo);
+			return R.ok(id);
+		}else{
+			// 更新
+			Long id = configService.updateMarketingConfig(reqVo);
+			return id == null ? R.failed():R.ok(id);
+		}
+	}
+
+	/**
+	 * 删除营销配置
+	 * @param ids ids
+	 * @return 删除结果
+	 */
+	@PostMapping("/remove")
+	@Operation(summary = "删除营销配置")
+	public R removeById(@RequestBody Long[] ids) {
+		if (ids == null || ids.length == 0){
+			return R.failed("ID列表不能为空");
+		}
+		Boolean res = configService.delMarketingConfig(ids);
+		return res?R.ok():R.failed();
+	}
+
+	/**
+	 * 获取应用列表
+	 * @return 应用列表
+	 */
+	@GetMapping("/apps")
+	@Operation(summary = "获取应用列表")
+	public R getAppList(Integer current, Integer size) {
+		if(current == null || current <= 0){
+			current	= 1;
+		}
+		if (size == null || size <= 0){
+			size = 10;
+		}
+		return R.ok(configService.pageAppList(current, size));
+	}
+
+
+	@Inner(value = false)
+	@PostMapping("/report")
+	@Operation(summary = "上报营销数据")
+	public R saveMarketingReport(@RequestBody MarketingReportVO reqVo, HttpServletRequest request) {
+		return R.ok(configService.saveMarketingReport(reqVo, request));
+	}
+
+	@GetMapping("/pageData")
+	@Operation(summary = "分页统计营销数据")
+	public R pageMarketingData(@ParameterObject PageMarketingReportReqVO reqVo) {
+		return R.ok(configService.pageMarketingData(reqVo));
+	}
+
+}

+ 15 - 0
pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/mapper/MarketingConfigDetailMapper.java

@@ -0,0 +1,15 @@
+package com.pig4cloud.pig.admin.mapper;
+
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.pig4cloud.pig.admin.api.entity.marketing.MarketingConfigDetail;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * @author: lwh
+ * @date: 2025-07-16
+ * @description: 营销方案配置详情Mapper
+ */
+@Mapper
+public interface MarketingConfigDetailMapper extends BaseMapper<MarketingConfigDetail> {
+}

+ 15 - 0
pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/mapper/MarketingConfigDomainMapper.java

@@ -0,0 +1,15 @@
+package com.pig4cloud.pig.admin.mapper;
+
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.pig4cloud.pig.admin.api.entity.marketing.MarketingConfigDomain;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * @author: lwh
+ * @date: 2025-07-16
+ * @description: 营销方案配置域名Mapper
+ */
+@Mapper
+public interface MarketingConfigDomainMapper extends BaseMapper<MarketingConfigDomain> {
+}

+ 30 - 0
pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/mapper/MarketingReportMapper.java

@@ -0,0 +1,30 @@
+package com.pig4cloud.pig.admin.mapper;
+
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.pig4cloud.pig.admin.api.entity.marketing.MarketingReport;
+import com.pig4cloud.pig.admin.api.vo.marketing.PageMarketingReportRspVO;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * @author: lwh
+ * @date: 2025-07-16
+ * @description: 营销上报Mapper
+ */
+@Mapper
+public interface MarketingReportMapper extends BaseMapper<MarketingReport> {
+
+	List<PageMarketingReportRspVO> selectMarketingReportGroupBy(
+			@Param("domain") String domain,
+			@Param("ip") String ip,
+			@Param("referrer") String referrer,
+			@Param("lastHourTime") String lastHourTime,
+			@Param("todayStartTime") String todayStartTime,
+			@Param("offset") Long offset,
+			@Param("pageSize") Long pageSize);
+
+	Long selectMarketingReportGroupByCount(String domain, String ip, String referrer);
+}

+ 14 - 0
pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/service/MarketingConfigDetailService.java

@@ -0,0 +1,14 @@
+package com.pig4cloud.pig.admin.service;
+
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.pig4cloud.pig.admin.api.entity.marketing.MarketingConfigDetail;
+
+/**
+ * @author: lwh
+ * @date: 2025-07-16
+ * @description: 营销配置详情Service
+ */
+
+public interface MarketingConfigDetailService extends IService<MarketingConfigDetail> {
+}

+ 13 - 0
pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/service/MarketingConfigDomainService.java

@@ -0,0 +1,13 @@
+package com.pig4cloud.pig.admin.service;
+
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.pig4cloud.pig.admin.api.entity.marketing.MarketingConfigDomain;
+
+/**
+ * @author: lwh
+ * @date: 2025-07-16
+ * @description: 营销域名配置service
+ */
+public interface MarketingConfigDomainService extends IService<MarketingConfigDomain> {
+}

+ 74 - 0
pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/service/MarketingConfigService.java

@@ -0,0 +1,74 @@
+package com.pig4cloud.pig.admin.service;
+
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.pig4cloud.pig.admin.api.vo.marketing.MarketingReportVO;
+import com.pig4cloud.pig.admin.api.vo.marketing.PageMarketingReportReqVO;
+import com.pig4cloud.pig.admin.api.vo.marketing.SaveMarketingConfigVO;
+import jakarta.servlet.http.HttpServletRequest;
+
+import java.util.HashMap;
+
+
+/**
+ * @author: lwh
+ * @date: 2025-07-16
+ * @description: TODO
+ */
+
+public interface MarketingConfigService {
+	/**
+	 * 添加营销配置
+	 * @param reqVo 营销配置
+	 * @return ID
+	 */
+	Long addMarketingConfig(SaveMarketingConfigVO reqVo);
+
+	/**
+	 * 修改营销配置
+	 * @param reqVo 营销配置
+	 * @return ID
+	 */
+	Long updateMarketingConfig(SaveMarketingConfigVO reqVo);
+
+	/**
+	 * 删除营销配置
+	 * @param ids ID
+	 * @return Boolean
+	 */
+	Boolean delMarketingConfig(Long[] ids);
+
+	/**
+	 * 分页查询营销配置列表
+	 * @param page 分页参数
+	 * @param domain 域名
+	 * @return 营销配置列表
+	 */
+	Page pageMarketingConfig(Page page, String domain);
+
+	/**
+	 * 通过ID查询营销配置详情
+	 * @param id ID
+	 * @return 营销配置详情
+	 */
+	SaveMarketingConfigVO getById(String id);
+
+	/**
+	 * 上报营销数据
+	 * @param reqVo 营销数据
+	 * @return ID
+	 */
+	HashMap<String,Object> saveMarketingReport(MarketingReportVO reqVo, HttpServletRequest request);
+
+	/**
+	 * 分页查询营销上报数据
+	 * @return 分页数据
+	 */
+	Page pageMarketingData(PageMarketingReportReqVO reqVo);
+
+	/**
+	 * 获取应用列表
+	 * @return 应用列表
+	 */
+	Page pageAppList(int pageNum, int pageSize);
+}

+ 15 - 0
pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/service/MarketingReportService.java

@@ -0,0 +1,15 @@
+package com.pig4cloud.pig.admin.service;
+
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.pig4cloud.pig.admin.api.entity.marketing.MarketingReport;
+
+/**
+ * @author: lwh
+ * @date: 2025-07-16
+ * @description: 营销上报Service
+ */
+
+public interface MarketingReportService extends IService<MarketingReport> {
+
+}

+ 19 - 0
pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/service/impl/MarketingConfigDetailServiceImpl.java

@@ -0,0 +1,19 @@
+package com.pig4cloud.pig.admin.service.impl;
+
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.pig4cloud.pig.admin.api.entity.marketing.MarketingConfigDetail;
+import com.pig4cloud.pig.admin.mapper.MarketingConfigDetailMapper;
+import com.pig4cloud.pig.admin.service.MarketingConfigDetailService;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author: lwh
+ * @date: 2025-07-16
+ * @description: 营销配置详情Service实现类
+ */
+@Service
+@AllArgsConstructor
+public class MarketingConfigDetailServiceImpl extends ServiceImpl<MarketingConfigDetailMapper, MarketingConfigDetail> implements MarketingConfigDetailService {
+}

+ 19 - 0
pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/service/impl/MarketingConfigDomainServiceImpl.java

@@ -0,0 +1,19 @@
+package com.pig4cloud.pig.admin.service.impl;
+
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.pig4cloud.pig.admin.api.entity.marketing.MarketingConfigDomain;
+import com.pig4cloud.pig.admin.mapper.MarketingConfigDomainMapper;
+import com.pig4cloud.pig.admin.service.MarketingConfigDomainService;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author: lwh
+ * @date: 2025-07-16
+ * @description: 营销域名配置service实现类
+ */
+@Service
+@AllArgsConstructor
+public class MarketingConfigDomainServiceImpl extends ServiceImpl<MarketingConfigDomainMapper, MarketingConfigDomain> implements MarketingConfigDomainService {
+}

+ 400 - 0
pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/service/impl/MarketingConfigServiceImpl.java

@@ -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;
+	}
+}

+ 20 - 0
pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/service/impl/MarketingReportServiceImpl.java

@@ -0,0 +1,20 @@
+package com.pig4cloud.pig.admin.service.impl;
+
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.pig4cloud.pig.admin.api.entity.marketing.MarketingReport;
+import com.pig4cloud.pig.admin.mapper.MarketingReportMapper;
+import com.pig4cloud.pig.admin.service.MarketingReportService;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Service;
+
+
+/**
+ * @author: lwh
+ * @date: 2025-07-16
+ * @description: 营销上报Service实现类
+ */
+@Service
+@AllArgsConstructor
+public class MarketingReportServiceImpl extends ServiceImpl<MarketingReportMapper, MarketingReport> implements MarketingReportService {
+}

+ 65 - 0
pig-upms/pig-upms-biz/src/main/resources/mapper/MarketingReportMapper.xml

@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~
+  ~      Copyright (c) 2018-2025, lengleng All rights reserved.
+  ~
+  ~  Redistribution and use in source and binary forms, with or without
+  ~  modification, are permitted provided that the following conditions are met:
+  ~
+  ~ Redistributions of source code must retain the above copyright notice,
+  ~  this list of conditions and the following disclaimer.
+  ~  Redistributions in binary form must reproduce the above copyright
+  ~  notice, this list of conditions and the following disclaimer in the
+  ~  documentation and/or other materials provided with the distribution.
+  ~  Neither the name of the pig4cloud.com developer nor the names of its
+  ~  contributors may be used to endorse or promote products derived from
+  ~  this software without specific prior written permission.
+  ~  Author: lengleng ([email protected])
+  ~
+  -->
+
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.pig4cloud.pig.admin.mapper.MarketingReportMapper">
+
+	<select id="selectMarketingReportGroupBy" resultType="com.pig4cloud.pig.admin.api.vo.marketing.PageMarketingReportRspVO">
+		SELECT
+			domain,ip,referrer,
+			COUNT(*) AS total,
+			SUM(CASE WHEN create_time >= #{lastHourTime} THEN 1 ELSE 0 END) AS hourly,
+			SUM(CASE WHEN create_time >= #{todayStartTime} THEN 1 ELSE 0 END) AS daily
+		FROM
+		    marketing_report
+		<where>
+			<if test="domain != null and domain != ''">
+				AND domain LIKE CONCAT('%', #{domain}, '%')
+			</if>
+			<if test="ip != null and ip != ''">
+				AND ip LIKE CONCAT('%', #{ip}, '%')
+			</if>
+			<if test="referrer != null and referrer != ''">
+				AND referrer LIKE CONCAT('%', #{referrer}, '%')
+			</if>
+		</where>
+		GROUP BY
+		    domain, ip, referrer
+		LIMIT #{offset}, #{pageSize}
+	</select>
+
+	<select id="selectMarketingReportGroupByCount" resultType="java.lang.Long">
+		SELECT
+		    COUNT(DISTINCT domain, ip, referrer)
+		FROM
+		    marketing_report
+		<where>
+			<if test="domain != null and domain != ''">
+				AND domain LIKE CONCAT('%', #{domain}, '%')
+			</if>
+			<if test="ip != null and ip != ''">
+				AND ip LIKE CONCAT('%', #{ip}, '%')
+			</if>
+			<if test="referrer != null and referrer != ''">
+				AND referrer LIKE CONCAT('%', #{referrer}, '%')
+			</if>
+		</where>
+	</select>
+</mapper>