From 15c200aa7e0ced518364a68769503472b542c9f4 Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Tue, 08 Jul 2025 14:03:57 +0800
Subject: [PATCH] feat(mall): 添加会员数据统计功能

---
 src/main/java/cc/mrbird/febs/mall/controller/member/AdminMallMemberController.java |   12 ++
 src/main/resources/templates/febs/views/modules/mallMember/dataInfo.html           |  222 +++++++++++++++++++++++++++++++++++++
 src/main/java/cc/mrbird/febs/mall/controller/member/ViewMallMemberController.java  |   10 +
 src/main/java/cc/mrbird/febs/mall/vo/AdminDataInfoVo.java                          |   20 +++
 src/main/java/cc/mrbird/febs/mall/service/IAdminMallMemberService.java             |    2 
 src/main/java/cc/mrbird/febs/mall/service/impl/AdminMallMemberServiceImpl.java     |   41 ++++++
 src/main/java/cc/mrbird/febs/mall/service/impl/ApiMallMemberServiceImpl.java       |    9 +
 7 files changed, 316 insertions(+), 0 deletions(-)

diff --git a/src/main/java/cc/mrbird/febs/mall/controller/member/AdminMallMemberController.java b/src/main/java/cc/mrbird/febs/mall/controller/member/AdminMallMemberController.java
index d6d33f8..6c75d69 100644
--- a/src/main/java/cc/mrbird/febs/mall/controller/member/AdminMallMemberController.java
+++ b/src/main/java/cc/mrbird/febs/mall/controller/member/AdminMallMemberController.java
@@ -4,25 +4,31 @@
 import cc.mrbird.febs.common.controller.BaseController;
 import cc.mrbird.febs.common.entity.FebsResponse;
 import cc.mrbird.febs.common.entity.QueryRequest;
+import cc.mrbird.febs.common.enumerates.OrderPayMethodEnum;
+import cc.mrbird.febs.common.enumerates.OrderStatusEnum;
 import cc.mrbird.febs.mall.dto.*;
 import cc.mrbird.febs.mall.entity.*;
 import cc.mrbird.febs.mall.service.IAdminMallMemberService;
 import cc.mrbird.febs.mall.service.IApiMallMemberService;
 import cc.mrbird.febs.mall.vo.*;
+import cc.mrbird.febs.system.entity.User;
 import cc.mrbird.febs.vip.entity.MallVipBenefitsRecord;
 import cc.mrbird.febs.vip.mapper.MallVipBenefitsRecordMapper;
 import cc.mrbird.febs.vip.service.IMallVipBenefitsService;
 import cn.hutool.core.date.DateTime;
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
 import javax.validation.Valid;
+import javax.validation.constraints.NotBlank;
 import javax.validation.constraints.NotNull;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -38,6 +44,12 @@
     private final IMallVipBenefitsService mallVipBenefitsService;
     private final MallVipBenefitsRecordMapper mallVipBenefitsRecordMapper;
 
+    @GetMapping("dataInfo")
+    public FebsResponse dataInfo() {
+
+        return mallMemberService.dataInfo();
+    }
+
     /**
      * 平台账单
      *
diff --git a/src/main/java/cc/mrbird/febs/mall/controller/member/ViewMallMemberController.java b/src/main/java/cc/mrbird/febs/mall/controller/member/ViewMallMemberController.java
index 345f802..614e08d 100644
--- a/src/main/java/cc/mrbird/febs/mall/controller/member/ViewMallMemberController.java
+++ b/src/main/java/cc/mrbird/febs/mall/controller/member/ViewMallMemberController.java
@@ -40,6 +40,16 @@
      * 平台账单
      * @return
      */
+    @GetMapping("dataInfo")
+    @RequiresPermissions("dataInfo:view")
+    public String dataInfo() {
+        return FebsUtil.view("modules/mallMember/dataInfo");
+    }
+
+    /**
+     * 平台账单
+     * @return
+     */
     @GetMapping("mallDataList")
     @RequiresPermissions("mallDataList:view")
     public String mallDataList() {
diff --git a/src/main/java/cc/mrbird/febs/mall/service/IAdminMallMemberService.java b/src/main/java/cc/mrbird/febs/mall/service/IAdminMallMemberService.java
index c762a22..89b6b37 100644
--- a/src/main/java/cc/mrbird/febs/mall/service/IAdminMallMemberService.java
+++ b/src/main/java/cc/mrbird/febs/mall/service/IAdminMallMemberService.java
@@ -145,4 +145,6 @@
     FebsResponse checkOrder(Long id);
 
     FebsResponse checkLeader(Long id);
+
+    FebsResponse dataInfo();
 }
diff --git a/src/main/java/cc/mrbird/febs/mall/service/impl/AdminMallMemberServiceImpl.java b/src/main/java/cc/mrbird/febs/mall/service/impl/AdminMallMemberServiceImpl.java
index 254e019..fee552f 100644
--- a/src/main/java/cc/mrbird/febs/mall/service/impl/AdminMallMemberServiceImpl.java
+++ b/src/main/java/cc/mrbird/febs/mall/service/impl/AdminMallMemberServiceImpl.java
@@ -18,6 +18,7 @@
 import cc.mrbird.febs.pay.model.MemberWithdrawalDto;
 import cc.mrbird.febs.pay.service.IXcxPayService;
 import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.date.DateUnit;
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.IdUtil;
 import cn.hutool.core.util.ObjectUtil;
@@ -27,6 +28,7 @@
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import lombok.RequiredArgsConstructor;
@@ -35,6 +37,7 @@
 import org.springframework.transaction.annotation.Transactional;
 
 import java.math.BigDecimal;
+import java.util.Arrays;
 import java.util.Date;
 import java.util.List;
 
@@ -995,4 +998,42 @@
 
         return new FebsResponse().success().message("操作成功");
     }
+
+    @Override
+    public FebsResponse dataInfo() {
+        AdminDataInfoVo adminDataInfoVo = new AdminDataInfoVo();
+
+        List<MallMember> mallMembers = this.baseMapper.selectList(null);
+        if(CollUtil.isNotEmpty(mallMembers)){
+            adminDataInfoVo.setTotalMember(mallMembers.size());
+
+            adminDataInfoVo.setTotalDayMember(
+                    Math.toIntExact(mallMembers.stream().filter(mallMember -> DateUtil.compare(mallMember.getCreatedTime(), DateUtil.beginOfDay(new Date())) >= 0).count()));
+            adminDataInfoVo.setTotalWeekMember(
+                    Math.toIntExact(mallMembers.stream().filter(mallMember -> DateUtil.compare(mallMember.getCreatedTime(), DateUtil.beginOfWeek(new Date())) >= 0).count()));
+            adminDataInfoVo.setTotalMonthMember(
+                    Math.toIntExact(mallMembers.stream().filter(mallMember -> DateUtil.compare(mallMember.getCreatedTime(), DateUtil.beginOfMonth(new Date())) >= 0).count()));
+        }
+
+        List<Integer> typeList = Arrays.asList(ScoreFlowTypeEnum.WECHAT_PAY.getValue(), ScoreFlowTypeEnum.PAY_BALANCE.getValue());
+        List<MallMoneyFlow> mallMoneyFlows = mallMoneyFlowMapper.selectList(
+                Wrappers.lambdaQuery(MallMoneyFlow.class)
+                        .in(MallMoneyFlow::getType, typeList)
+        );
+        if(CollUtil.isNotEmpty(mallMoneyFlows)){
+            adminDataInfoVo.setTotalAmount(mallMoneyFlows.stream().map(MallMoneyFlow::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add).negate());
+            adminDataInfoVo.setTotalDayAmount(
+                    mallMoneyFlows.stream().filter(mallMoneyFlow -> DateUtil.compare(mallMoneyFlow.getCreatedTime(), DateUtil.beginOfDay(new Date())) >= 0)
+                            .map(MallMoneyFlow::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add).negate());
+            adminDataInfoVo.setTotalWeekAmount(
+                    mallMoneyFlows.stream().filter(mallMoneyFlow -> DateUtil.compare(mallMoneyFlow.getCreatedTime(), DateUtil.beginOfWeek(new Date())) >= 0)
+                            .map(MallMoneyFlow::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add).negate());
+            adminDataInfoVo.setTotalMonthAmount(
+                    mallMoneyFlows.stream().filter(mallMoneyFlow -> DateUtil.compare(mallMoneyFlow.getCreatedTime(), DateUtil.beginOfMonth(new Date())) >= 0)
+                            .map(MallMoneyFlow::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add).negate());
+
+        }
+
+        return new FebsResponse().success().data(adminDataInfoVo);
+    }
 }
diff --git a/src/main/java/cc/mrbird/febs/mall/service/impl/ApiMallMemberServiceImpl.java b/src/main/java/cc/mrbird/febs/mall/service/impl/ApiMallMemberServiceImpl.java
index f127194..2f722a6 100644
--- a/src/main/java/cc/mrbird/febs/mall/service/impl/ApiMallMemberServiceImpl.java
+++ b/src/main/java/cc/mrbird/febs/mall/service/impl/ApiMallMemberServiceImpl.java
@@ -808,6 +808,15 @@
         }
         String phone = apiXcxSaveInfoDto.getPhone();
         if(StrUtil.isNotEmpty(phone)){
+
+            MallMember entity = this.baseMapper.selectOne(
+                    Wrappers.lambdaQuery(MallMember.class)
+                            .eq(MallMember::getPhone, phone)
+                    .last("LIMIT 1")
+            );
+            if(ObjectUtil.isNotNull( entity) && !entity.getId().equals(memberId)){
+                throw new FebsException("手机号码已使用");
+            }
             mallMember.setPhone(phone);
         }
         String avatarUrl = apiXcxSaveInfoDto.getAvatarUrl();
diff --git a/src/main/java/cc/mrbird/febs/mall/vo/AdminDataInfoVo.java b/src/main/java/cc/mrbird/febs/mall/vo/AdminDataInfoVo.java
new file mode 100644
index 0000000..0eb84b5
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/mall/vo/AdminDataInfoVo.java
@@ -0,0 +1,20 @@
+package cc.mrbird.febs.mall.vo;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+public class AdminDataInfoVo {
+
+    private Integer totalMember = 0;//总数
+    private Integer totalDayMember = 0;//昨日新增
+    private Integer totalWeekMember = 0;//七日内新增
+    private Integer totalMonthMember = 0;//30天内新增
+
+    private BigDecimal totalAmount = BigDecimal.ZERO;//总数
+    private BigDecimal totalDayAmount = BigDecimal.ZERO;//新增
+    private BigDecimal totalWeekAmount = BigDecimal.ZERO;//本周新增
+    private BigDecimal totalMonthAmount = BigDecimal.ZERO;//本月新增
+
+}
diff --git a/src/main/resources/templates/febs/views/modules/mallMember/dataInfo.html b/src/main/resources/templates/febs/views/modules/mallMember/dataInfo.html
new file mode 100644
index 0000000..cf36f4b
--- /dev/null
+++ b/src/main/resources/templates/febs/views/modules/mallMember/dataInfo.html
@@ -0,0 +1,222 @@
+<style>
+    #data-info .welcome-info {
+        border: 1px solid #f1f1f1;
+        margin-bottom: .5rem;
+        padding: .5rem;
+        background: #FFFFFF;
+    }
+
+    #data-info .welcome-info-wrapper {
+        padding: .2rem;
+        display: inline-block
+    }
+
+    #data-info .welcome-info-wrapper .user-header {
+        display: inline-block;
+        vertical-align: middle
+    }
+
+    #data-info .welcome-info-wrapper .user-header img {
+        width: 5rem;
+        margin: .5rem 1rem;
+        border-radius: 50%
+    }
+
+    #data-info .welcome-info-wrapper .user-info {
+        display: inline-block;
+        vertical-align: middle
+    }
+
+    #data-info .welcome-info-wrapper .user-info .random-message {
+        font-size: 1rem;
+        margin-bottom: .2rem;
+        max-width: 21rem
+    }
+
+    #data-info .welcome-info-wrapper .user-info .user-dept, #data-info .welcome-info-wrapper .user-info .user-login-info {
+        color: rgba(0, 0, 0, 0.45);
+    }
+
+    #data-info .login-count-table {
+        width: 100%;
+        margin: 1rem;
+    }
+
+    #data-info .login-count-table .count {
+        padding-top: .8rem;
+        font-size: 1rem;
+        font-weight: 600;
+        color: #1890ff
+    }
+
+    #data-info .project-table {
+        padding: .5rem;
+        border: 1px solid #f1f1f1;
+        width: 100%
+    }
+
+    #data-info .project-table-td {
+        padding: .5rem 0.7rem;
+        border: 1px solid #f1f1f1;
+    }
+
+    #data-info .project-table-td a {
+        color: #42b983;
+        font-size: .9rem;
+        font-weight: 600;
+    }
+
+    #data-info .project-desc {
+        color: rgba(0, 0, 0, 0.45);
+    }
+    /*.layui-card:last-child{*/
+    /*    background: #F8F8F8;*/
+    /*}*/
+    .box{
+        background: #FFFFFF;
+        padding: 30px 30px;
+        border-radius: 4px;
+    }
+    .box .name{
+        font-size: 14px;
+        color: #333333;
+    }
+    .box .num{
+        font-size: 24px;
+        color: #1890ff;
+        font-weight: bold;
+        margin-top: 10px;
+    }
+    .m-title{
+        position: relative;
+        font-size: 16px;
+        font-weight: bold;
+        color: #000000;
+        padding-left: 20px;
+        margin: 0 0 10px;
+    }
+    .m-title::before{
+        content: '';
+        position: absolute;
+        left: 0;
+        width: 4px;
+        height: 20px;
+        background: #1890ff;
+    }
+</style>
+<div class="layui-fluid layui-anim febs-anim-up" id="data-info" lay-title="数据看板">
+    <div class="layui-row layui-col-space8 febs-container">
+        <div class="layui-col-md12 layui-col-sm12 layui-col-xs12">
+            <div class="layui-card" style="background: #F8F8F8;">
+                <div class="layui-card-body layui-anim layui-anim-fadein">
+                </div>
+
+                <div class="layui-card-body layui-anim layui-anim-fadein">
+                    <div class="layui-row">
+                        <div class="m-title">会员注册</div>
+                        <div class="layui-row layui-col-space15">
+                            <div class="layui-col-md3">
+                                <div class="layui-panel">
+                                    <div class="box">
+                                        <p class="name">总计</p>
+                                        <p class="num"><span id="totalMember"></span></p>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="layui-col-md3">
+                                <div class="layui-panel">
+                                    <div class="box">
+                                        <p class="name">今日新增</p>
+                                        <p class="num"><span id="totalDayMember"></span></p>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="layui-col-md3">
+                                <div class="layui-panel">
+                                    <div class="box">
+                                        <p class="name">本周新增</p>
+                                        <p class="num"><span id="totalWeekMember"></span></p>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="layui-col-md3">
+                                <div class="layui-panel">
+                                    <div class="box">
+                                        <p class="name">本月新增</p>
+                                        <p class="num"><span id="totalMonthMember"></span></p>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+
+                <div class="layui-card-body layui-anim layui-anim-fadein">
+                    <div class="layui-row">
+                        <div class="m-title">交易数据</div>
+                        <div class="layui-row layui-col-space15">
+                            <div class="layui-col-md3">
+                                <div class="layui-panel">
+                                    <div class="box">
+                                        <p class="name">累计</p>
+                                        <p class="num"><span id="totalAmount"></span></p>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="layui-col-md3">
+                                <div class="layui-panel">
+                                    <div class="box">
+                                        <p class="name">今日新增</p>
+                                        <p class="num"><span id="totalDayAmount"></span></p>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="layui-col-md3">
+                                <div class="layui-panel">
+                                    <div class="box">
+                                        <p class="name">本周新增</p>
+                                        <p class="num"><span id="totalWeekAmount"></span></p>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="layui-col-md3">
+                                <div class="layui-panel">
+                                    <div class="box">
+                                        <p class="name">本月新增</p>
+                                        <p class="num"><span id="totalMonthAmount"></span></p>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+
+
+
+            </div>
+        </div>
+    </div>
+</div>
+<script data-th-inline="javascript" type="text/javascript">
+    layui.use(['apexcharts', 'febs', 'jquery', 'util'], function () {
+        var $ = layui.jquery,
+            util = layui.util,
+            $view = $('#data-info'),
+            febs = layui.febs;
+
+        febs.get(ctx + 'admin/mallMember/dataInfo', null, function (r) {
+            handleSuccess(r.data);
+        });
+
+        function handleSuccess(data) {
+            $view.find('#totalMember').text(data.totalMember).end()
+                .find('#totalDayMember').text(data.totalDayMember).end()
+                .find('#totalWeekMember').text(data.totalWeekMember).end()
+                .find('#totalMonthMember').text(data.totalMonthMember).end()
+                .find('#totalAmount').text(data.totalAmount).end()
+                .find('#totalDayAmount').text(data.totalDayAmount).end()
+                .find('#totalWeekAmount').text(data.totalWeekAmount).end()
+                .find('#totalMonthAmount').text(data.totalMonthAmount).end();
+        }
+    });
+</script>

--
Gitblit v1.9.1