| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592 |
- <?php
- namespace backend\modules\mall\controllers;
- use backend\controllers\MallController;
- use common\enums\AppEnum;
- use common\enums\RedisKeyEnum;
- use common\enums\StatusEnum;
- use common\helpers\ArrayHelper;
- use common\helpers\DateHelper;
- use common\models\census\CensusMall;
- use common\models\census\CensusMallGoods;
- use common\models\census\CensusMallRegion;
- use common\models\census\CensusMallSingleDay;
- use common\models\common\Region;
- use common\models\goods\Goods;
- class DataCensusController extends MallController
- {
- public $appId = AppEnum::BACKEND;
- public $redis = null;
- public function init()
- {
- parent::init();
- $this->redis = \Yii::$app->redis;
- }
- /**
- * @name:
- * @msg: 组装 redis key
- * @param {string} $key
- * @return {*}
- */
- private function getKey(string $key): string
- {
- $redis_key = RedisKeyEnum::suffix($key, '', true) . ':' . $this->mall_id;
- // $redis_key = RedisKeyEnum::suffix($key, '', true) . ':' . 58;
- return $redis_key;
- }
- /**
- * @name:
- * @msg: 清洗 && 处理 yii2 redis 客户端拿出来的数据,处理成正常数组
- * @param {array} $data
- * @param {string} $type
- * @return {*}
- */
- private function purgeRedisRst(array $data, string $type = 'hash', string $field_name = ''): array
- {
- if (empty($data)) {
- return [];
- }
- $result = [];
- if ('hash' == $type) {
- foreach ($data as $key => $val) {
- if ($key % 2 == 0) {
- $keys[] = $val;
- } else {
- $values[] = $val;
- }
- }
- $result = array_combine($keys, $values);
- } elseif ('zset-good' == $type) {
- $result = [];
- foreach ($data as $dk => $dv) {
- if ($dk % 2 == 0) {
- $keys[] = $dv;
- } else {
- $values[] = $dv;
- }
- }
- $arr = array_combine($keys, $values);
- foreach ($arr as $ak => $av) {
- $a_keys = explode(':', $ak);
- $result[] = [
- 'goods_id' => $a_keys[1],
- $field_name => $av,
- ];
- }
- }
- return $result;
- }
- /**
- * @name:
- * @msg: 总裁驾驶窗
- * @param {*}
- * @return {*}
- */
- public function actionDrivingWindow()
- {
- if (!\Yii::$app->request->isAjax) {
- $is_display = \Yii::$app->request->get('is_display', 1);// 判断跳转是否传参
- return $this->render('/census/driving-window', ['is_display' => $is_display]);
- }
- if (\Yii::$app->request->isGet) {
- // 汇总的数据
- $window_data_default = [
- 'id' => 0,
- 'total_users' => 0,
- 'total_orders' => 0,
- 'total_pay_money' => 0,
- 'total_visitors' => 0,
- ];
- $cache_key_mall = $this->getKey(RedisKeyEnum::CENSUS_MALL);
- $window_data = $this->purgeRedisRst($this->redis->hgetall($cache_key_mall), 'hash');
- if (empty($window_data)) {
- $window_data = CensusMall::find()
- ->select(['id', 'total_users', 'total_orders', 'total_pay_money', 'total_visitors'])
- ->where(['mall_id' => $this->mall_id, 'status' => StatusEnum::ENABLED])
- ->limit(1)
- ->asArray()
- ->one();
- if (empty($window_data)) {
- $window_data = $window_data_default;
- }
- }
- $arr = [];
- foreach ($window_data_default as $key => $val) {
- $arr[$key] = $window_data[$key] ?? 0;
- }
- $window_data = $arr;
- // 今日的数据
- $today_data_default = [
- 'total_views' => 0,
- 'total_users' => 0,
- 'add_users' => 0,
- 'total_pay_money' => 0,
- 'total_visitors' => 0,
- ];
- $date = date('Y-m-d');
- $cache_key_single_day = $this->getKey(RedisKeyEnum::CENSUS_MALL_SINGLE_DATE) . ':' . $date;
- $today_data = $this->purgeRedisRst($this->redis->hgetall($cache_key_single_day), 'hash');
- if (!empty($today_data)) {
- $today_data = array_merge($today_data_default, $today_data);
- $today_data['today_new_users'] = $today_data['add_users'];
- $today_data['today_total_pay_money'] = $today_data['total_pay_money'];
- $today_data['today_total_visitors'] = $today_data['total_visitors'];
- } else {
- $today_data = CensusMallSingleDay::find()
- ->select('total_views,add_users as today_new_users,total_pay_money as today_total_pay_money,total_visitors as today_total_visitors')
- ->where(['mall_id' => $this->mall_id, 'status' => StatusEnum::ENABLED, 'date' => $date])
- ->limit(1)
- ->asArray()
- ->one();
- if (empty($today_data)) {
- $today_data = [
- 'total_views' => 0,
- 'today_new_users' => 0,
- 'today_total_pay_money' => 0,
- 'today_total_visitors' => 0,
- ];
- }
- }
- // dd($window_data);
- $drive_window_datas = array_merge($today_data, $window_data);
- return $this->success('success', $drive_window_datas);
- } else {
- return $this->error('请求方式错误');
- }
- }
- /**
- * @name:
- * @msg: 流量分析页面
- * @param {*}
- * @return {*}
- */
- public function actionFlowanAlyse()
- {
- if (!\Yii::$app->request->isAjax) {
- return $this->render('/census/flow-analyse');
- }
- if (\Yii::$app->request->isGet) {
- /* 流量数据 + 新老客户占比数据 ------ start ---- */
- $flow_datas_default = [
- 'new_customers' => 0,
- 'total_users' => 0,
- 'total_visitors' => 0,
- 'number_of_orders' => 0,
- 'total_pay_people' => 0,
- 'total_pay_orders' => 0,
- 'total_pay_money' => 0,
- 'repurchase_people' => 0,
- 'repurchase_money' => 0,
- 'share_people' => 0,
- 'total_views' => 0,
- ];
- $cache_key_flow = $this->getKey(RedisKeyEnum::CENSUS_MALL);
- $flow_datas = $this->purgeRedisRst($this->redis->hgetall($cache_key_flow), 'hash');
- if (empty($flow_datas)) {
- $flow_datas = CensusMall::find()
- ->select('new_customers,total_users,total_visitors,number_of_orders,total_pay_people,total_pay_orders,total_pay_money,repurchase_people,repurchase_money,share_people,total_views')
- ->where(['mall_id' => $this->mall_id, 'status' => StatusEnum::ENABLED])
- ->asArray()
- ->one();
- if (empty($flow_datas)) {
- $flow_datas = $flow_datas_default;
- }
- }
- $flow_datas = array_merge($flow_datas_default, $flow_datas);
- // dd($flow_datas);
- // 访问-支付转化率
- if ($flow_datas['total_visitors'] > 0) {
- $flow_datas['access_pay_rate'] = bcdiv($flow_datas['total_pay_people'], $flow_datas['total_visitors'], 2);
- } else {
- $flow_datas['access_pay_rate'] = 0;
- }
- // 访问-下单转化率
- if ($flow_datas['total_visitors'] > 0) {
- $flow_datas['access_place_order_rate'] = bcdiv($flow_datas['number_of_orders'], $flow_datas['total_visitors'], 2);
- } else {
- $flow_datas['access_place_order_rate'] = 0;
- }
- // 访客占比
- if ($flow_datas['total_users'] > 0) {
- $flow_datas['visitors_rate'] = bcdiv($flow_datas['total_visitors'], $flow_datas['total_users'], 2);
- } else {
- $flow_datas['visitors_rate'] = 0;
- }
- // 新客户占比
- if ($flow_datas['total_users'] > 0) {
- $flow_datas['new_user_rate'] = bcdiv($flow_datas['new_customers'], $flow_datas['total_users'], 2);
- } else {
- $flow_datas['new_user_rate'] = 0;
- }
- // 老客户占比
- // if ($flow_datas['total_users'] > 0) {
- // $flow_datas['old_user_rate'] = bcdiv($flow_datas['regular_customers'], $flow_datas['total_users'], 2);
- // } else {
- // $flow_datas['old_user_rate'] = 0;
- // }
- $flow_datas['old_user_rate'] = bcsub(1, $flow_datas['new_user_rate'], 2);
- // dd($flow_datas);
- // 客单价
- if ($flow_datas['total_pay_money'] == 0 || $flow_datas['total_pay_people'] == 0) {
- $flow_datas['customer_unit_price'] = 0;
- } else {
- $flow_datas['customer_unit_price'] = bcdiv($flow_datas['total_pay_money'], $flow_datas['total_pay_people'], 2);
- }
- /* 流量数据 + 新老客户占比数据 ------ end ---- */
- /* 地区订单数据 ------ start ---- */
- $cahce_key_region = $this->getKey(RedisKeyEnum::CENSUS_MALL_REGION);
- $region_datas = $this->purgeRedisRst($this->redis->hgetall($cahce_key_region), 'hash');
- // dd($region_datas);
- if (!empty($region_datas)) {
- $arr = [];
- $time = time();
- foreach ($region_datas as $rk => $rv) {
- $total_pay_money = $total_orders = 0;
- $strs = explode('_', $rk);
- $province_id = array_pop($strs);
- if (false !== strpos($rk, 'total_pay_money')) {
- $total_pay_money = $rv;
- }
- if (false !== strpos($rk, 'total_orders')) {
- $total_orders = $rv;
- }
- $arr[$province_id]['id'] = 0;
- $arr[$province_id]['province_id'] = $province_id;
- if (empty($arr[$province_id]['total_orders'])) {
- $arr[$province_id]['total_orders'] = $total_orders;
- }
- if (empty($arr[$province_id]['total_pay_money'])) {
- $arr[$province_id]['total_pay_money'] = $total_pay_money;
- }
- $arr[$province_id]['updated_at'] = $time;
- }
- // dd($region_datas, $arr);
- $region_datas = array_values($arr);
- } else {
- $region_datas = CensusMallRegion::find()
- ->select('id,province_id,total_orders,total_pay_money,updated_at')
- ->where(['mall_id' => $this->mall_id, 'status' => StatusEnum::ENABLED])
- ->asArray()
- ->all();
- }
- // dd($region_datas);
- $province_ids = array_column($region_datas, 'province_id');
- $provinces = Region::find()
- ->select('id,name,short_title,areacode,initial')
- ->where(['id' => $province_ids, 'level' => 1])
- ->asArray()
- ->indexBy('id')
- ->all();
- // 地图数据
- $map_order_datas = [];
- foreach ($region_datas as &$rval) {
- $province = $provinces[$rval['province_id']] ?? [];
- $rval['province_name'] = $province['name'] ?? '未知';
- $rval['province_short_title'] = $province['short_title'] ?? '';
- $rval['areacode'] = $province['areacode'] ?? '';
- $rval['initial'] = $province['initial'] ?? '';
- $map_order_datas[] = [
- 'name' => $province['name'] ?? '',
- 'value' => $rval['total_orders'] ?? 0,
- ];
- }
- /* 地区订单数据 ------ end ---- */
- /* 商品流量排行榜 ------ start ---- */
- $cache_key_good_views_ranking = $this->getKey(RedisKeyEnum::CENSUS_MALL_GOODS_VIEWS);
- $good_rankings = $this->purgeRedisRst($this->redis->zrevrange($cache_key_good_views_ranking, 0, -1, 'WITHSCORES'), 'zset-good', 'total_views');
- // dd($good_rankings);
- if (empty($good_rankings)) {
- $good_rankings = CensusMallGoods::find()
- ->select('id,goods_id,total_views')
- ->where(['mall_id' => $this->mall_id, 'status' => StatusEnum::ENABLED])
- ->orderBy('total_views desc')
- ->limit(6)
- ->asArray()
- ->all();
- }
- if (!empty($good_rankings)) {
- $goods_ids = array_column($good_rankings, 'goods_id');
- $goods = Goods::find()
- ->select('id,goods_name,cover_pic')
- ->where(['mall_id' => $this->mall_id, 'id' => $goods_ids])
- ->asArray()
- ->indexBy('id')
- ->all();
- foreach ($good_rankings as &$ranking) {
- $good = $goods[$ranking['goods_id']] ?? [];
- $ranking['goods_name'] = $good['goods_name'] ?? '';
- $ranking['cover_pic'] = $good['cover_pic'] ?? '';
- }
- }
- /* 商品流量排行榜 ------ end ---- */
- /* 新老客户占比饼图 ------ start ---- */
- $pie_chart_datas = [
- 'total_users' => $flow_datas['total_users'] ?? 0,
- 'names' => ['新客户占比', '老客户占比'],
- 'data' => [
- bcmul($flow_datas['new_user_rate'], 100, 2),
- bcmul($flow_datas['old_user_rate'], 100, 2),
- ],
- ];
- /* 新老客户占比饼图 ------ end ---- */
- // dd($flow_datas, $region_datas, $good_rankings);
- $data = [
- 'flow_datas' => $flow_datas,
- 'region_datas' => $region_datas,
- 'good_rankings' => $good_rankings,
- 'map_order_datas' => $map_order_datas,
- 'pie_chart_datas' => $pie_chart_datas,
- ];
- return $this->success('success', $data);
- } else {
- return $this->error('请求方式错误');
- }
- }
- /**
- * @name:
- * @msg: 商品数据统计
- * @param {*}
- * @return {*}
- */
- public function actionGoodsStatistics()
- {
- if (!\Yii::$app->request->isAjax) {
- return $this->render('/census/goods-statistics');
- }
- /* 顶部汇总数据 ---- start ------ */
- $cache_key_goods_statistics = $this->getKey(RedisKeyEnum::CENSUS_MALL);
- $goods_statistics = $this->purgeRedisRst($this->redis->hgetall($cache_key_goods_statistics), 'hash');
- $goods_statistics_default = [
- 'id' => 0,
- 'goods_total_visitors' => 0,
- 'goods_total_views' => 0,
- 'total_views' => 0,
- 'total_pay_people' => 0,
- 'total_pay_money' => 0,
- 'goods_listings' => 0,
- 'repurchase_people' => 0,
- 'repurchase_money' => 0,
- 'updated_at' => 0,
- ];
- if (!empty($goods_statistics)) {
- $goods_statistics = array_merge($goods_statistics_default, $goods_statistics);
- } else {
- $goods_statistics = CensusMall::find()
- ->select('id,goods_total_visitors,total_views,total_pay_people,total_pay_money,goods_listings,repurchase_people,repurchase_money,updated_at')
- ->where(['mall_id' => $this->mall_id, 'status' => StatusEnum::ENABLED])
- ->asArray()
- ->one();
- if (empty($goods_statistics)) {
- $goods_statistics = [
- 'id' => 0,
- 'goods_total_visitors' => 0,
- 'goods_total_views' => 0,
- 'total_views' => 0,
- 'total_pay_people' => 0,
- 'total_pay_money' => 0,
- 'goods_listings' => 0,
- 'repurchase_people' => 0,
- 'repurchase_money' => 0,
- 'updated_at' => 0,
- ];
- }
- }
- // 客单价
- if ($goods_statistics['total_pay_money'] == 0 || $goods_statistics['total_pay_people'] == 0) {
- $goods_statistics['customer_unit_price'] = 0;
- } else {
- $goods_statistics['customer_unit_price'] = bcdiv($goods_statistics['total_pay_money'], $goods_statistics['total_pay_people'], 2);
- }
- /* 顶部汇总数据 ---- end ------ */
- /* 商品排行榜 --- start --- */
- $goods_ids = [];
- $cache_key_good_views_ranking = $this->getKey(RedisKeyEnum::CENSUS_MALL_GOODS_VIEWS);
- $good_views_ranking = $this->purgeRedisRst($this->redis->zrevrange($cache_key_good_views_ranking, 0, 4, 'WITHSCORES'), 'zset-good', 'total_views');
- if (empty($good_views_ranking)) {
- $good_views_ranking = CensusMallGoods::find()
- ->select('goods_id,total_views')
- ->where(['mall_id' => $this->mall_id, 'status' => StatusEnum::ENABLED])
- ->limit(5)
- ->asArray()
- ->all();
- }
- $goods_ids = array_merge($goods_ids, array_column($good_views_ranking, 'goods_id'));
- $cahce_key_good_sales_ranking = $this->getKey(RedisKeyEnum::CENSUS_MALL_GOODS_SALES_VOLUME);
- $good_sales_ranking = $this->purgeRedisRst($this->redis->zrevrange($cahce_key_good_sales_ranking, 0, 4, 'WITHSCORES'), 'zset-good', 'sales_volumes');
- if (empty($good_sales_ranking)) {
- $good_sales_ranking = CensusMallGoods::find()
- ->select('goods_id,total_sales_volume')
- ->where(['mall_id' => $this->mall_id, 'status' => StatusEnum::ENABLED])
- ->limit(5)
- ->asArray()
- ->all();
- }
- $goods_ids = array_merge($goods_ids, array_column($good_sales_ranking, 'goods_id'));
- $cache_key_paymoney = $this->getKey(RedisKeyEnum::CENSUS_MALL_GOODS_PAY_MONEY);
- $good_paymoney_ranking = $this->purgeRedisRst($this->redis->zrevrange($cache_key_paymoney, 0, 4, 'WITHSCORES'), 'zset-good', 'pay_amounts');
- if (empty($good_paymoney_ranking)) {
- $good_sales_ranking = CensusMallGoods::find()
- ->select('goods_id,total_pay_money')
- ->where(['mall_id' => $this->mall_id, 'status' => StatusEnum::ENABLED])
- ->limit(5)
- ->asArray()
- ->all();
- }
- $goods_ids = array_merge($goods_ids, array_column($good_sales_ranking, 'goods_id'));
- $cache_key_visitors = $this->getKey(RedisKeyEnum::CENSUS_MALL_GOODS_VISITORS);
- $good_visitor_ranking = $this->purgeRedisRst($this->redis->zrevrange($cache_key_visitors, 0, 4, 'WITHSCORES'), 'zset-good', 'access_times');
- if (empty($good_visitor_ranking)) {
- $good_rankings = CensusMallGoods::find()
- ->select('goods_id,total_visitors')
- ->where(['mall_id' => $this->mall_id, 'status' => StatusEnum::ENABLED])
- ->limit(5)
- ->asArray()
- ->all();
- }
- $goods_ids = array_unique(array_merge($goods_ids, array_column($good_sales_ranking, 'goods_id')));
- $goods = Goods::find()
- ->select('id,goods_name,cover_pic')
- ->where(['mall_id' => $this->mall_id, 'id' => $goods_ids])
- ->asArray()
- ->indexBy('id')
- ->all();
- if (!empty($good_sales_ranking)) {
- foreach ($good_sales_ranking as $sk => &$sv) {
- if ($sk >= 5) {
- break;
- }
- $good = $goods[$sv['goods_id']] ?? [];
- $sv['goods_name'] = $good['goods_name'] ?? '';
- $sv['cover_pic'] = $good['cover_pic'] ?? '';
- }
- } else {
- $good_sales_ranking = [];
- }
- if (!empty($good_views_ranking)) {
- foreach ($good_views_ranking as $vk => &$vv) {
- if ($vk >= 5) {
- break;
- }
- $good = $goods[$vv['goods_id']] ?? [];
- $vv['goods_name'] = $good['goods_name'] ?? '';
- $vv['cover_pic'] = $good['cover_pic'] ?? '';
- $list[] = $vv;
- }
- } else {
- $good_views_ranking = [];
- }
- if (!empty($good_paymoney_ranking)) {
- foreach ($good_paymoney_ranking as $pk => &$pv) {
- if ($pk >= 5) {
- break;
- }
- $good = $goods[$pv['goods_id']] ?? [];
- $pv['goods_name'] = $good['goods_name'] ?? '';
- $pv['cover_pic'] = $good['cover_pic'] ?? '';
- $pv['pay_amounts'] = bcadd($pv['pay_amounts'], 0, 2);
- }
- } else {
- $good_paymoney_ranking = [];
- }
- if (!empty($good_visitor_ranking)) {
- foreach ($good_visitor_ranking as $vik => &$viv) {
- if ($vik >= 5) {
- break;
- }
- $good = $goods[$viv['goods_id']] ?? [];
- $viv['goods_name'] = $good['goods_name'] ?? '';
- $viv['cover_pic'] = $good['cover_pic'] ?? '';
- }
- } else {
- $good_visitor_ranking = [];
- }
- /* 商品排行榜 --- end --- */
- /* 中间部分 GMV 的数据 --- start --- */
- $day_numbers = 15;
- $date_time = DateHelper::someDays($day_numbers, 'before');
- $start_time = $date_time['start'];
- $endDate = new \DateTime('yesterday');
- $endDate->setTime(23, 59, 59);
- $end_time = $endDate->getTimestamp();
- $gmv_data = CensusMallSingleDay::find()
- ->select('id,total_order_money,date,updated_at')
- ->where(['mall_id' => $this->mall_id, 'status' => StatusEnum::ENABLED])
- ->andWhere(['and', ['>=', 'created_at', $start_time], ['<', 'created_at', $end_time]])
- ->orderBy('updated_at asc')
- ->asArray()
- ->all();
- $keys = [];
- $values = [];
- for ($j = 0; $j < $day_numbers; $j++) {
- $date_timep = DateHelper::someDays($j + 1, 'before');
- $keys[] = date('m-d', $date_timep['start']);
- }
- $keys = array_reverse($keys);
- if (!empty($gmv_data)) {
- foreach ($gmv_data as $val) {
- $k = date('m-d', strtotime($val['date'] . ' 00:00:00'));
- $date_data_map[$k] = $val['total_order_money'];
- }
- foreach ($keys as $v) {
- $values[] = $date_data_map[$v] ?? 0;
- }
- } else {
- for ($i = 0; $i < $day_numbers; $i++) {
- $values[] = 0;
- }
- }
- $gmvs = [
- 'keys' => $keys,
- 'values' => $values,
- ];
- /* 中间部分 GMV 的数据 --- end --- */
- $data = [
- 'goods_data' => $goods_statistics,
- 'access_rankings' => $good_visitor_ranking,
- 'paymoney_rankings' => $good_paymoney_ranking,
- 'sales_rankings' => $good_sales_ranking,
- 'gmvs' => $gmvs,
- ];
- return $this->success('success', $data);
- }
- }
|