xgb 1 неделя назад
Родитель
Сommit
8f8e64838a

+ 59 - 0
fs-admin/src/main/java/com/fs/app/controller/statistic/MemberReportController.java

@@ -0,0 +1,59 @@
+package com.fs.app.controller.statistic;
+
+import com.fs.app.params.MemberReportQuery;
+import com.fs.app.service.IMemberReportService;
+import com.fs.app.vo.MemberReportVO;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.utils.poi.ExcelUtil;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * APP会员统计报表Controller
+ *
+ * @author ylrz
+ * @date 2026-04-22
+ */
+@Api(tags = "APP会员统计报表")
+@RestController
+@RequestMapping("/app/statistics")
+public class MemberReportController extends BaseController
+{
+    @Autowired
+    private IMemberReportService memberReportService;
+
+    /**
+     * 查询会员统计报表列表(按销售公司)
+     */
+    @ApiOperation("查询会员统计报表列表")
+    @PreAuthorize("@ss.hasPermi('app:statistics:memberReport:list')")
+    @GetMapping("/memberReport")
+    public TableDataInfo memberReport(MemberReportQuery query)
+    {
+        List<MemberReportVO> list = memberReportService.selectMemberReportList(query);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出会员统计报表
+     */
+    @ApiOperation("导出会员统计报表")
+    @PreAuthorize("@ss.hasPermi('app:statistics:memberReport:export')")
+    @GetMapping("/exportMemberReport")
+    public AjaxResult exportMemberReport(MemberReportQuery query)
+    {
+        List<MemberReportVO> list = memberReportService.selectMemberReportList(query);
+        ExcelUtil<MemberReportVO> util = new ExcelUtil<MemberReportVO>(MemberReportVO.class);
+        return util.exportExcel(list, "会员统计报表");
+    }
+}
+

+ 3 - 8
fs-company/src/main/java/com/fs/app/controller/im/FsImMsgSendLogController.java

@@ -1,17 +1,14 @@
 package com.fs.app.controller.im;
 
-import com.fs.app.service.param.BatchSendImTextRequest;
-import com.fs.app.service.param.FsImMsgSendLogRequest;
-import com.fs.app.service.param.FsImMsgSendLogResponse;
+import com.fs.app.params.BatchSendImTextRequest;
+import com.fs.app.params.FsImMsgSendLogRequest;
+import com.fs.app.params.FsImMsgSendLogResponse;
 import com.fs.common.core.controller.BaseController;
-import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.utils.ServletUtils;
-import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.framework.security.LoginUser;
 import com.fs.framework.service.TokenService;
-import com.fs.im.domain.FsImMsgSendLog;
 import com.fs.im.dto.OpenImResponseDTO;
 import com.fs.im.service.IFsImMsgSendLogService;
 import com.fs.im.service.OpenIMService;
@@ -20,9 +17,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
-import javax.servlet.http.HttpServletResponse;
 import java.util.List;
-import java.util.Map;
 
 /**
  * OpenIM 消息发送记录主表 Controller

+ 22 - 0
fs-service/src/main/java/com/fs/app/mapper/MemberReportMapper.java

@@ -0,0 +1,22 @@
+package com.fs.app.mapper;
+
+import com.fs.app.params.MemberReportQuery;
+import com.fs.app.vo.MemberReportVO;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 会员统计报表Mapper接口
+ *
+ * @author ylrz
+ * @date 2026-04-22
+ */
+public interface MemberReportMapper
+{
+
+    List<MemberReportVO> selectMemberReportListNoTime(@Param("params") MemberReportQuery params);
+
+    List<MemberReportVO> selectMemberReportAddCountList(@Param("params") MemberReportQuery query);
+}
+

+ 1 - 1
fs-service/src/main/java/com/fs/app/service/param/BatchSendImTextRequest.java → fs-service/src/main/java/com/fs/app/params/BatchSendImTextRequest.java

@@ -1,4 +1,4 @@
-package com.fs.app.service.param;
+package com.fs.app.params;
 
 import lombok.Data;
 import javax.validation.constraints.NotBlank;

+ 1 - 2
fs-service/src/main/java/com/fs/app/service/param/FsImMsgSendLogRequest.java → fs-service/src/main/java/com/fs/app/params/FsImMsgSendLogRequest.java

@@ -1,6 +1,5 @@
-package com.fs.app.service.param;
+package com.fs.app.params;
 
-import com.fs.common.core.domain.BaseEntity;
 import com.fs.common.core.page.PageDomain;
 import lombok.Data;
 import lombok.EqualsAndHashCode;

+ 1 - 1
fs-service/src/main/java/com/fs/app/service/param/FsImMsgSendLogResponse.java → fs-service/src/main/java/com/fs/app/params/FsImMsgSendLogResponse.java

@@ -1,4 +1,4 @@
-package com.fs.app.service.param;
+package com.fs.app.params;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.Data;

+ 1 - 4
fs-service/src/main/java/com/fs/app/service/param/FsImMsgSendLogStatisticsResponse.java → fs-service/src/main/java/com/fs/app/params/FsImMsgSendLogStatisticsResponse.java

@@ -1,10 +1,7 @@
-package com.fs.app.service.param;
+package com.fs.app.params;
 
-import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.Data;
 
-import java.util.Date;
-
 /**
  * OpenIM 消息发送记录响应参数
  */

+ 37 - 0
fs-service/src/main/java/com/fs/app/params/MemberReportQuery.java

@@ -0,0 +1,37 @@
+package com.fs.app.params;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 会员统计报表查询对象
+ *
+ * @author ylrz
+ * @date 2026-04-22
+ */
+@Data
+@ApiModel("会员统计报表查询对象")
+public class MemberReportQuery
+{
+    /** 公司ID */
+    @ApiModelProperty("公司ID")
+    private String companyId;
+
+    /** 开始时间 */
+    @ApiModelProperty("开始时间")
+    private String sTime;
+
+    /** 结束时间 */
+    @ApiModelProperty("结束时间")
+    private String eTime;
+
+    /** 页码 */
+    @ApiModelProperty("页码")
+    private Integer pageNum;
+
+    /** 每页条数 */
+    @ApiModelProperty("每页条数")
+    private Integer pageSize;
+}
+

+ 24 - 0
fs-service/src/main/java/com/fs/app/service/IMemberReportService.java

@@ -0,0 +1,24 @@
+package com.fs.app.service;
+
+import com.fs.app.params.MemberReportQuery;
+import com.fs.app.vo.MemberReportVO;
+
+import java.util.List;
+
+/**
+ * 会员统计报表Service接口
+ *
+ * @author ylrz
+ * @date 2026-04-22
+ */
+public interface IMemberReportService
+{
+    /**
+     * 查询会员统计报表列表
+     *
+     * @param query 查询条件
+     * @return 会员统计报表集合
+     */
+    public List<MemberReportVO> selectMemberReportList(MemberReportQuery query);
+}
+

+ 79 - 0
fs-service/src/main/java/com/fs/app/service/impl/MemberReportServiceImpl.java

@@ -0,0 +1,79 @@
+package com.fs.app.service.impl;
+
+import com.fs.app.service.IMemberReportService;
+import com.fs.common.core.redis.RedisCache;
+import org.apache.commons.lang.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.fs.app.params.MemberReportQuery;
+import com.fs.app.vo.MemberReportVO;
+import com.fs.app.mapper.MemberReportMapper;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+/**
+ * 会员统计报表Service业务层处理
+ *
+ * @author ylrz
+ * @date 2026-04-22
+ */
+@Service
+public class MemberReportServiceImpl implements IMemberReportService
+{
+
+    @Autowired
+    private MemberReportMapper memberReportMapper;
+
+    @Autowired
+    private RedisCache redisCache;
+
+    /**
+     * 查询会员统计报表列表
+     *
+     * @param query 查询条件
+     * @return 会员统计报表
+     */
+    @Override
+    public List<MemberReportVO> selectMemberReportList(MemberReportQuery query)
+    {
+        String memberReportKey="memberReport";
+        List<MemberReportVO> memberReportVOList =redisCache.getCacheObject(memberReportKey);
+        if(memberReportVOList==null || memberReportVOList.isEmpty()){
+            memberReportVOList=memberReportMapper.selectMemberReportListNoTime(query);
+            redisCache.setCacheObject(memberReportKey,memberReportVOList,1, TimeUnit.HOURS);
+        }
+        // 时间为空不查询新增app用户数
+        if(StringUtils.isBlank(query.getSTime())||StringUtils.isBlank(query.getETime())){
+            return memberReportVOList;
+        }
+        // 查询新增用户数
+        List<MemberReportVO> memberReportAddCountList =memberReportMapper.selectMemberReportAddCountList(query);
+        if (memberReportAddCountList != null && !memberReportAddCountList.isEmpty()) {
+            Map<Long, MemberReportVO> reportMap = memberReportVOList.stream()
+                    .filter(report -> report.getCompanyId() != null)
+                    .collect(Collectors.toMap(
+                            MemberReportVO::getCompanyId,
+                            report -> report,
+                            (existing, replacement) -> existing
+                    ));
+
+            for (MemberReportVO addCountReport : memberReportAddCountList) {
+                if (addCountReport.getCompanyId() != null) {
+                    MemberReportVO existingReport = reportMap.get(addCountReport.getCompanyId());
+
+                    if (existingReport != null) {
+                        existingReport.setNewMemberCount(
+                                addCountReport.getNewMemberCount() != null ?
+                                        addCountReport.getNewMemberCount() : 0L
+                        );
+                    }
+                }
+            }
+        }
+
+        return memberReportVOList;
+    }
+}

+ 43 - 0
fs-service/src/main/java/com/fs/app/vo/MemberReportVO.java

@@ -0,0 +1,43 @@
+package com.fs.app.vo;
+
+import com.fs.common.annotation.Excel;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 会员统计报表视图对象
+ *
+ * @author ylrz
+ * @date 2026-04-22
+ */
+@Data
+@ApiModel("会员统计报表视图对象")
+public class MemberReportVO implements java.io.Serializable
+{
+
+    private static final long serialVersionUID = 1L;
+
+    private Long companyId;
+
+    /** 销售公司名称 */
+    @Excel(name = "销售公司")
+    @ApiModelProperty("销售公司名称")
+    private String companyName;
+
+    /** 会员总数 */
+    @Excel(name = "会员总数")
+    @ApiModelProperty("会员总数")
+    private Long totalMemberCount;
+
+    /** APP会员数 */
+    @Excel(name = "APP会员数")
+    @ApiModelProperty("APP会员数")
+    private Long appMemberCount;
+
+    /** 新增会员数 */
+    @Excel(name = "APP新增会员数")
+    @ApiModelProperty("新增会员数")
+    private Long newMemberCount;
+}
+

+ 3 - 3
fs-service/src/main/java/com/fs/im/mapper/FsImMsgSendLogMapper.java

@@ -4,9 +4,9 @@ import java.util.List;
 import java.util.Map;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.fs.app.service.param.FsImMsgSendLogRequest;
-import com.fs.app.service.param.FsImMsgSendLogResponse;
-import com.fs.app.service.param.FsImMsgSendLogStatisticsResponse;
+import com.fs.app.params.FsImMsgSendLogRequest;
+import com.fs.app.params.FsImMsgSendLogResponse;
+import com.fs.app.params.FsImMsgSendLogStatisticsResponse;
 import com.fs.course.vo.newfs.FsImSendLogVO;
 import com.fs.im.domain.FsImMsgSendLog;
 import org.apache.ibatis.annotations.Param;

+ 2 - 2
fs-service/src/main/java/com/fs/im/service/IFsImMsgSendLogService.java

@@ -4,8 +4,8 @@ import java.util.List;
 import java.util.Map;
 
 import com.baomidou.mybatisplus.extension.service.IService;
-import com.fs.app.service.param.FsImMsgSendLogRequest;
-import com.fs.app.service.param.FsImMsgSendLogResponse;
+import com.fs.app.params.FsImMsgSendLogRequest;
+import com.fs.app.params.FsImMsgSendLogResponse;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.ResponseResult;
 import com.fs.course.vo.newfs.FsImSendLogVO;

+ 3 - 4
fs-service/src/main/java/com/fs/im/service/impl/FsImMsgSendLogServiceImpl.java

@@ -1,14 +1,13 @@
 package com.fs.im.service.impl;
 
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
-import com.fs.app.service.param.FsImMsgSendLogRequest;
-import com.fs.app.service.param.FsImMsgSendLogResponse;
-import com.fs.app.service.param.FsImMsgSendLogStatisticsResponse;
+import com.fs.app.params.FsImMsgSendLogRequest;
+import com.fs.app.params.FsImMsgSendLogResponse;
+import com.fs.app.params.FsImMsgSendLogStatisticsResponse;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.ResponseResult;
 import com.fs.common.core.redis.RedisCache;

+ 85 - 0
fs-service/src/main/resources/mapper/app/MemberReportMapper.xml

@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+        <!DOCTYPE mapper
+                PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+                "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.app.mapper.MemberReportMapper">
+
+    <resultMap type="com.fs.app.vo.MemberReportVO" id="MemberReportResult">
+        <result property="companyName"    column="company_name"    />
+        <result property="totalMemberCount"    column="total_member_count"    />
+        <result property="appMemberCount"    column="app_member_count"    />
+        <result property="newMemberCount"    column="new_member_count"    />
+    </resultMap>
+
+    <select id="selectMemberReportList" resultMap="MemberReportResult">
+        SELECT
+        c.company_name,
+        COUNT(DISTINCT m.user_id) AS total_member_count,
+        COUNT(DISTINCT CASE WHEN m.is_app_user = 1 THEN m.user_id END) AS app_member_count,
+        COUNT(DISTINCT CASE
+        WHEN DATE(m.create_time) BETWEEN DATE(#{query.sTime}) AND DATE(#{query.eTime})
+        THEN m.user_id
+        END) AS new_member_count
+        FROM his_member m
+        LEFT JOIN sys_company c ON m.company_id = c.company_id
+        <where>
+            <if test="query.companyId != null and query.companyId != ''">
+                AND m.company_id = #{query.companyId}
+            </if>
+            <if test="query.sTime != null and query.sTime != ''">
+                AND DATE(m.create_time) &gt;= DATE(#{query.sTime})
+            </if>
+            <if test="query.eTime != null and query.eTime != ''">
+                AND DATE(m.create_time) &lt;= DATE(#{query.eTime})
+            </if>
+        </where>
+        GROUP BY c.company_id, c.company_name
+        ORDER BY c.company_name
+    </select>
+
+    <select id="selectMemberReportListNoTime" resultType="com.fs.app.vo.MemberReportVO">
+        SELECT
+            c.company_id,
+            c.company_name,
+            COALESCE(stats.total_count, 0) AS total_member_count,
+            COALESCE(stats.app_count, 0) AS app_member_count
+        FROM company c
+                 LEFT JOIN (
+            SELECT
+                uc.company_id,
+                COUNT(DISTINCT uc.user_id) AS total_count,
+                COUNT(DISTINCT CASE WHEN u.app_open_id IS NOT NULL THEN u.user_id END) AS app_count
+            FROM fs_user_company_user uc
+                     INNER JOIN fs_user u ON uc.user_id = u.user_id
+            where u.is_del=0 and uc.status =1
+              <if test="params.companyId != null and params.companyId != ''">
+                and uc.company_id = #{params.companyId}
+              </if>
+            GROUP BY uc.company_id
+        ) stats ON c.company_id = stats.company_id
+
+
+
+    </select>
+    <select id="selectMemberReportAddCountList" resultType="com.fs.app.vo.MemberReportVO">
+        SELECT
+            c.company_id,
+            c.company_name,
+            COALESCE(stats.new_member_count, 0) AS newMemberCount
+        FROM company c
+                 LEFT JOIN (
+            SELECT
+                uc.company_id,
+                COUNT(DISTINCT uc.user_id) AS new_member_count
+            FROM fs_user_company_user uc
+                     INNER JOIN fs_user u ON uc.user_id = u.user_id
+            where u.is_del=0 and uc.status =1
+              and u.app_create_time &gt;= #{params.sTime} and u.app_create_time &lt;= #{params.eTime}
+            <if test="params.companyId != null and params.companyId != ''">
+                and uc.company_id = #{params.companyId}
+            </if>
+            GROUP BY uc.company_id
+        ) stats ON c.company_id = stats.company_id
+    </select>
+
+</mapper>

+ 2 - 2
fs-service/src/main/resources/mapper/im/FsImMsgSendLogMapper.xml

@@ -210,7 +210,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                 #{logDetailId}
             </foreach>
     </select>
-    <select id="selectFsImMsgSendLogInfoList" resultType="com.fs.app.service.param.FsImMsgSendLogResponse">
+    <select id="selectFsImMsgSendLogInfoList" resultType="com.fs.app.params.FsImMsgSendLogResponse">
         SELECT
         d.log_id AS logId,
         l.company_user_id AS companyUserId,
@@ -258,7 +258,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         ORDER BY l.create_time DESC
     </select>
 
-    <select id="getFsImMsgSendStatistics" resultType="com.fs.app.service.param.FsImMsgSendLogStatisticsResponse">
+    <select id="getFsImMsgSendStatistics" resultType="com.fs.app.params.FsImMsgSendLogStatisticsResponse">
         SELECT
             count(d.log_detail_id) as total,
             sum(case when d.status = 0 then 1 else 0 end) as sent,