@ -1,19 +1,22 @@
package cn.iocoder.yudao.module.statistics.service.member ;
import cn.hutool.core.util.NumberUtil ;
import cn.hutool.core.date.LocalDateTimeUtil ;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum ;
import cn.iocoder.yudao.framework.ip.core.Area ;
import cn.iocoder.yudao.framework.ip.core.enums.AreaTypeEnum ;
import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils ;
import cn.iocoder.yudao.module.statistics.controller.admin.common.vo.DataComparisonRespVO ;
import cn.iocoder.yudao.module.statistics.controller.admin.member.vo.* ;
import cn.iocoder.yudao.module.statistics.con troller.admin.trade.vo.TradeStatisticsComparisonRespVO ;
import cn.iocoder.yudao.module.statistics.con vert.member.MemberStatisticsConvert ;
import cn.iocoder.yudao.module.statistics.dal.mysql.member.MemberStatisticsMapper ;
import cn.iocoder.yudao.module.statistics.service.infra.ApiAccessLogStatisticsService ;
import cn.iocoder.yudao.module.statistics.service.member.bo.MemberAreaStatisticsRespBO ;
import cn.iocoder.yudao.module.statistics.service.pay.PayWalletStatisticsService ;
import cn.iocoder.yudao.module.statistics.service.pay.bo.RechargeSummaryRespBO ;
import cn.iocoder.yudao.module.statistics.service.trade.TradeOrderStatisticsService ;
import cn.iocoder.yudao.module.statistics.service.trade.TradeStatisticsService ;
import org.springframework.stereotype.Service ;
import org.springframework.validation.annotation.Validated ;
import cn.iocoder.yudao.module.statistics.convert.member.MemberStatisticsConvert ;
import javax.annotation.Resource ;
import java.time.Duration ;
@ -57,63 +60,77 @@ public class MemberStatisticsServiceImpl implements MemberStatisticsService {
@Override
public List < MemberAreaStatisticsRespVO > getMemberAreaStatisticsList ( ) {
// 统计用户
// TODO @疯狂:要处理下,未知省份;就是没填写省份的情况;
// TODO @疯狂:可能得把每个省的用户,都查询出来,然后去 order 那边 in; 因为要按照这些人为基础来计算; ; 用户规模量大可能不太好, 但是暂时就先这样搞吧 = =
Map < Integer , Integer > userCountMap = convertMap ( memberStatisticsMapper . selectSummaryListByAreaId ( ) ,
vo - > AreaUtils . getParentIdByType ( vo . getAreaId ( ) , AreaTypeEnum . PROVINCE ) ,
MemberAreaStatisticsResp V O: : getUserCount , Integer : : sum ) ;
MemberAreaStatisticsResp B O: : getUserCount , Integer : : sum ) ;
// 统计订单
Map < Integer , MemberAreaStatisticsRespVO > orderMap = convertMap ( tradeOrderStatisticsService . getSummaryListByAreaId ( ) ,
vo - > AreaUtils . getParentIdByType ( vo . getAreaId ( ) , AreaTypeEnum . PROVINCE ) ,
vo - > vo ,
( a , b ) - > new MemberAreaStatisticsRespVO ( )
. setOrderCreate Count( a . getOrderCreate Count( ) + b . getOrderCreate Count( ) )
. setOrderPay Count( a . getOrderPay Count( ) + b . getOrderPay Count( ) )
. setOrderCreate User Count( a . getOrderCreate User Count( ) + b . getOrderCreate User Count( ) )
. setOrderPay User Count( a . getOrderPay User Count( ) + b . getOrderPay User Count( ) )
. setOrderPayPrice ( a . getOrderPayPrice ( ) + b . getOrderPayPrice ( ) ) ) ;
// 拼接数据
return MemberStatisticsConvert . INSTANCE . convertList ( AreaUtils . getByType ( AreaTypeEnum . PROVINCE , area - > area ) , userCountMap , orderMap ) ;
List < Area > areaList = AreaUtils . getByType ( AreaTypeEnum . PROVINCE , area - > area ) ;
areaList . add ( new Area ( ) . setId ( null ) . setName ( "未知" ) ) ;
return MemberStatisticsConvert . INSTANCE . convertList ( areaList , userCountMap , orderMap ) ;
}
// TODO @疯狂: 这个方法, 要不拆成: 1) controller 调用 getMemberAnalyseComparisonData; 2) tradeOrderStatisticsService.getPayUserCount; 3) tradeOrderStatisticsService.getOrderPayPrice; 4) 。。。
// TODO 就是说:分析交给 controller 去组合;
@Override
public MemberAnalyseRespVO getMemberAnalyse ( LocalDateTime beginTime , LocalDateTime endTime ) {
public DataComparisonRespVO < MemberAnalyseDataRespVO > getMemberAnalyseComparisonData ( LocalDateTime beginTime , LocalDateTime endTime ) {
// 当前数据
MemberAnalyseDataRespVO vo = getMemberAnalyseData ( beginTime , endTime ) ;
// 对照数据
MemberAnalyseComparisonRespVO vo = getMemberAnalyseComparisonData ( beginTime , endTime ) ;
// TODO @疯狂:如果时间段这么处理,会不会 beginTime 重叠了。因为是 <= 一个时间;如果数据库插入的是 , xxxx-yy-zz 00:00:00 的话,它既满足 >= ? 也满足 <= ;(如果不好理解,微信聊)
LocalDateTime referenceBeginTime = beginTime . minus ( Duration . between ( beginTime , endTime ) ) ;
MemberAnalyseComparisonRespVO reference = getMemberAnalyseComparisonData ( referenceBeginTime , beginTime ) ;
// 计算客单价
// TODO @疯狂:这个可能有点特殊,要按照 create_time 来查询;不然它的漏斗就不统一;因为是访问数量 > 今日下单人 > 今日支付人;是一个统一的维度;
Integer payUserCount = tradeOrderStatisticsService . getPayUserCount ( beginTime , endTime ) ;
int atv = 0 ;
if ( payUserCount ! = null & & payUserCount > 0 ) {
// TODO @疯狂:类似上面的 payUserCount
Integer payPrice = tradeOrderStatisticsService . getOrderPayPrice ( beginTime , endTime ) ;
atv = NumberUtil . div ( payPrice , payUserCount ) . intValue ( ) ;
}
return new MemberAnalyseRespVO ( )
. setVisitorCount ( apiAccessLogStatisticsService . getVisitorUserCount ( beginTime , endTime ) )
. setOrderUserCount ( tradeOrderStatisticsService . getOrderUserCount ( beginTime , endTime ) )
. setPayUserCount ( payUserCount )
. setAtv ( atv )
. setComparison ( new TradeStatisticsComparisonRespVO < > ( vo , reference ) ) ;
LocalDateTime referenceEndDate = beginTime . minusDays ( 1 ) ; // 减少1天, 防止出现时间重叠
LocalDateTime referenceBeginDate = referenceEndDate . minus ( Duration . between ( beginTime , endTime ) ) ;
MemberAnalyseDataRespVO reference = getMemberAnalyseData (
LocalDateTimeUtil . beginOfDay ( referenceBeginDate ) , LocalDateTimeUtil . endOfDay ( referenceEndDate ) ) ;
return new DataComparisonRespVO < > ( vo , reference ) ;
}
private MemberAnalyse ComparisonRespVO getMemberAnalyseComparison Data( LocalDateTime beginTime , LocalDateTime endTime ) {
private MemberAnalyseDataRespVO getMemberAnalyseData ( LocalDateTime beginTime , LocalDateTime endTime ) {
Integer rechargeUserCount = Optional . ofNullable ( payWalletStatisticsService . getUserRechargeSummary ( beginTime , endTime ) )
. map ( RechargeSummaryRespBO : : getRechargeUserCount ) . orElse ( 0 ) ;
return new MemberAnalyse Comparison RespVO( )
. set UserCount( memberStatisticsMapper . selectUserCount ( beginTime , endTime ) )
. set Active UserCount( apiAccessLogStatisticsService . get Active UserCount( beginTime , endTime ) )
return new MemberAnalyseDataRespVO ( )
. setRegisterUserCount ( memberStatisticsMapper . selectUserCount ( beginTime , endTime ) )
. setVisitUserCount ( apiAccessLogStatisticsService . getUserCount ( UserTypeEnum . MEMBER . getValue ( ) , beginTime , endTime ) )
. setRechargeUserCount ( rechargeUserCount ) ;
}
@Override
public List < MemberSexStatisticsRespVO > getMemberSexStatisticsList ( ) {
// TODO @疯狂:需要考虑,用户性别为空,则是“未知”
return memberStatisticsMapper . selectSummaryListBySex ( ) ;
}
@Override
public List < MemberTerminalStatisticsRespVO > getRegisterTerminalStatisticsList ( ) {
return memberStatisticsMapper . selectSummaryListByRegisterTerminal ( ) ;
}
@Override
public List < MemberRegisterCountRespVO > getMemberRegisterCountList ( LocalDateTime beginTime , LocalDateTime endTime ) {
return memberStatisticsMapper . selectListByCreateTimeBetween ( beginTime , endTime ) ;
}
@Override
public DataComparisonRespVO < MemberCountRespVO > getUserCountComparison ( ) {
// 今日时间范围
LocalDateTime beginOfToday = LocalDateTimeUtil . beginOfDay ( LocalDateTime . now ( ) ) ;
LocalDateTime endOfToday = LocalDateTimeUtil . endOfDay ( beginOfToday ) ;
// 昨日时间范围
LocalDateTime beginOfYesterday = LocalDateTimeUtil . beginOfDay ( beginOfToday . minusDays ( 1 ) ) ;
LocalDateTime endOfYesterday = LocalDateTimeUtil . endOfDay ( beginOfYesterday ) ;
return new DataComparisonRespVO < MemberCountRespVO > ( )
. setValue ( getUserCount ( beginOfToday , endOfToday ) )
. setReference ( getUserCount ( beginOfYesterday , endOfYesterday ) ) ;
}
private MemberCountRespVO getUserCount ( LocalDateTime beginTime , LocalDateTime endTime ) {
return new MemberCountRespVO ( )
. setCreateUserCount ( memberStatisticsMapper . selectUserCount ( beginTime , endTime ) )
. setVisitUserCount ( apiAccessLogStatisticsService . getIpCount ( UserTypeEnum . MEMBER . getValue ( ) , beginTime , endTime ) ) ;
}
}