Administrator
8 days ago 15c200aa7e0ced518364a68769503472b542c9f4
feat(mall): 添加会员数据统计功能

- 新增 AdminDataInfoVo 类用于会员数据统计信息- 在 AdminMallMemberController 和 IAdminMallMemberService 中添加 dataInfo 方法
- 实现会员数据统计的业务逻辑,包括总会员数、新增会员数、交易数据等
- 添加数据统计页面模板和相关样式
- 优化 ApiMallMemberServiceImpl 中的手机号验证逻辑
5 files modified
2 files added
316 ■■■■■ changed files
src/main/java/cc/mrbird/febs/mall/controller/member/AdminMallMemberController.java 12 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/mall/controller/member/ViewMallMemberController.java 10 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/mall/service/IAdminMallMemberService.java 2 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/mall/service/impl/AdminMallMemberServiceImpl.java 41 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/mall/service/impl/ApiMallMemberServiceImpl.java 9 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/mall/vo/AdminDataInfoVo.java 20 ●●●●● patch | view | raw | blame | history
src/main/resources/templates/febs/views/modules/mallMember/dataInfo.html 222 ●●●●● patch | view | raw | blame | history
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();
    }
    /**
     * 平台账单
     *
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() {
src/main/java/cc/mrbird/febs/mall/service/IAdminMallMemberService.java
@@ -145,4 +145,6 @@
    FebsResponse checkOrder(Long id);
    FebsResponse checkLeader(Long id);
    FebsResponse dataInfo();
}
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);
    }
}
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();
src/main/java/cc/mrbird/febs/mall/vo/AdminDataInfoVo.java
New file
@@ -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;//本月新增
}
src/main/resources/templates/febs/views/modules/mallMember/dataInfo.html
New file
@@ -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>