GlobalStatistics.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. <?php
  2. namespace common\models\statistics;
  3. use common\models\base\BaseActiveRecord;
  4. use common\enums\StatusEnum;
  5. use common\enums\RedisKeyEnum;
  6. use common\models\user\UserLevel;
  7. use Yii;
  8. use yii\redis\Connection;
  9. /**
  10. *
  11. * @property int $mall_id
  12. * @property int $browse_num
  13. * @property int $follow_num
  14. * @property json $upgrade_level
  15. * @property int $date
  16. */
  17. class GlobalStatistics extends BaseActiveRecord
  18. {
  19. private static $fields = ['browse_num' => 0, 'follow_num' => 0, 'visitor_num' => 0, 'register_num' => 0,'upgrade_level' => '{}'];
  20. /**
  21. * {@inheritdoc}
  22. */
  23. public static function tableName()
  24. {
  25. return '{{%global_statistics}}';
  26. }
  27. /**
  28. * {@inheritdoc}
  29. */
  30. public function rules()
  31. {
  32. return [
  33. [['mall_id', 'browse_num', 'visitor_num', 'follow_num', 'register_num', 'date'], 'required'],
  34. [['mall_id', 'browse_num', 'visitor_num', 'follow_num', 'register_num', 'goods_num', 'date'], 'integer'],
  35. [['upgrade_level'], 'safe'],
  36. ];
  37. }
  38. /**
  39. * {@inheritdoc}
  40. */
  41. public function attributeLabels()
  42. {
  43. return [
  44. 'id' => 'ID',
  45. 'mall_id' => '商城id',
  46. 'browse_num' => '浏览量',
  47. 'follow_num' => '关注数',
  48. 'visitor_num' => '访客量',
  49. 'register_num' => '注册数',
  50. 'goods_num' => '商品数量',
  51. 'upgrade_level' => '用户等级',
  52. 'date' => '日期',
  53. ];
  54. }
  55. public function behaviors() {
  56. return [];
  57. }
  58. /**
  59. * 读取数据
  60. *
  61. * @param int $mall_id
  62. * @author hpei
  63. * @return array
  64. */
  65. public static function getData($mall_id) {
  66. try {
  67. $redis = \Yii::$app->redis;
  68. $cacheKey = RedisKeyEnum::suffix(RedisKeyEnum::GLOBAL_STATISTICS, 'mall_id_'. $mall_id);
  69. $globalStatistics = $redis->hgetall($cacheKey);
  70. $levels = UserLevel::getListIndexByLevelColumn($mall_id);
  71. if (empty($globalStatistics)) {
  72. $globalStatistics = self::createCacheData($mall_id, $levels);
  73. self::setCache($mall_id, $globalStatistics);
  74. } else {
  75. $num = 0;
  76. $fields = [];
  77. $values = [];
  78. foreach ($globalStatistics as $val) {
  79. if ($num % 2 === 0) {
  80. $fields[] = $val;
  81. } else {
  82. $values[] = $val;
  83. }
  84. $num++;
  85. }
  86. $globalStatistics = array_combine($fields, $values);
  87. $globalStatistics['yesterday_upgrade_level'] = json_decode($globalStatistics['yesterday_upgrade_level'], true);
  88. $globalStatistics['today_upgrade_level'] = json_decode($globalStatistics['today_upgrade_level'], true);
  89. }
  90. return self::getShowData($globalStatistics, $levels);
  91. } catch (\Exception $e) {
  92. Yii::error('读取全局统计缓存异常:' . $e->getMessage());
  93. if (isset($defaultData) && isset($values)) {
  94. return self::getShowData($defaultData, $levels);
  95. }
  96. throw new \Exception($e->getMessage() . ',行数:' . $e->getLine());
  97. }
  98. }
  99. /**
  100. * 保存数据
  101. *
  102. * @param int $mall_id
  103. * @param array $data [browse_num=>browse_num|follow_num=>follow_num|register_num=>register_num|upgrade_level=>[level=>num]]
  104. * @author hpei
  105. * @return bool
  106. */
  107. public static function saveData($mall_id, $data) {
  108. try {
  109. $levels = UserLevel::getListIndexByLevelColumn($mall_id);
  110. $defaultData = self::getDefaultStatistics($levels);
  111. $date = date('Ymd');
  112. $globalMdl = self::find()->where(['mall_id' => $mall_id, 'date' => $date])->one();
  113. if (empty($globalMdl)) {
  114. $globalMdl = new self();
  115. $globalMdl->mall_id = $mall_id;
  116. $globalMdl->date = $date;
  117. $globalMdl->load($defaultData, '');
  118. }
  119. //循环赋值
  120. foreach ($data as $key => $val) {
  121. if ($key == 'upgrade_level' && isset($data['upgrade_level']) && !empty($data['upgrade_level'])) {
  122. //循环默认等级,即使当天新增了等级,也会设置数量为0
  123. foreach ($defaultData['upgrade_level'] as $level => $num) {
  124. if (isset($data['upgrade_level'][$level])) {
  125. if (!isset($globalMdl->upgrade_level[$level])) {
  126. $defaultData['upgrade_level'][$level] = $data['upgrade_level'][$level];
  127. } else {
  128. $defaultData['upgrade_level'][$level] = $globalMdl->upgrade_level[$level] + $data['upgrade_level'][$level];
  129. }
  130. }
  131. }
  132. $globalMdl->upgrade_level = $defaultData['upgrade_level'];
  133. } else {
  134. $globalMdl->{$key} += $val;
  135. }
  136. }
  137. if (!$globalMdl->save()) {
  138. self::$static_error = $globalMdl->getErrorMessage();
  139. return false;
  140. }
  141. $cacheKey = RedisKeyEnum::suffix(RedisKeyEnum::GLOBAL_STATISTICS, 'mall_id_'. $mall_id);
  142. $config = \Yii::$app->getComponents()['redis'];
  143. unset($config['class']);
  144. $redis = new Connection($config);
  145. //是否要设置昨日数据缓存
  146. $isSetCache = true;
  147. if (!$redis->hget($cacheKey, 'yesterday_register_num') || !$redis->hget($cacheKey, 'upgrade_level') || !$redis->hget($cacheKey, 'follow_num')) {
  148. $isSetCache = false;
  149. }
  150. $isSetCache = $redis->hget($cacheKey, 'yesterday_register_num') ? true : false;
  151. //组装缓存数据
  152. $globalStatistics = [];
  153. foreach ($globalMdl as $field => $value) {
  154. if (isset(self::$fields[$field])) {
  155. $globalStatistics['today_' . $field] = $value;
  156. if ($isSetCache === false) {
  157. $globalStatistics['yesterday_' . $field] = $defaultData[$field];
  158. }
  159. }
  160. }
  161. $user_num = $redis->hget($cacheKey, 'user_num');
  162. $globalStatistics['user_num'] = $user_num ?? 0;
  163. if (isset($globalMdl->register_num) && $globalMdl->register_num != 0) {
  164. if ($user_num) {
  165. $globalStatistics['user_num'] += 1;
  166. } else {
  167. $globalStatistics['user_num'] = self::find()->where(['mall_id' => $mall_id])->sum('register_num') ?? 0;
  168. }
  169. }
  170. self::setCache($mall_id, $globalStatistics);
  171. return true;
  172. } catch (\Exception $e) {
  173. self::$static_error = '设置全局统计错误,' . $e->getMessage() . ',行数' . $e->getLine();
  174. return false;
  175. }
  176. }
  177. /**
  178. * 设置缓存
  179. *
  180. * @param int $mall_id
  181. * @param array $globalStatistics 统计数据
  182. * @author hpei
  183. * @return void
  184. */
  185. public static function setCache($mall_id, $globalStatistics) {
  186. $config = \Yii::$app->getComponents()['redis'];
  187. unset($config['class']);
  188. $redis = new Connection($config);
  189. $cacheKey = RedisKeyEnum::suffix(RedisKeyEnum::GLOBAL_STATISTICS, 'mall_id_'. $mall_id);
  190. try {
  191. //hmset
  192. foreach ($globalStatistics as $field => $value) {
  193. $value = is_array($value) ? json_encode($value, JSON_UNESCAPED_UNICODE) : $value;
  194. $redis->hset($cacheKey, $field, $value);
  195. }
  196. $redis->expire($cacheKey, strtotime("+1 day 0:0:0") - time());
  197. $redis->close();
  198. } catch (\Exception $e) {
  199. Yii::error('设置全局统计缓存异常:'.$e->getMessage());
  200. $redis->close();
  201. }
  202. }
  203. /**
  204. * 生成缓存数据 如果数据库没有数据则入库
  205. *
  206. * @param int $mall_id
  207. * @param array $level common\models\user\UserLevel
  208. * @return array
  209. */
  210. public static function createCacheData($mall_id, $levels) {
  211. try {
  212. $defaultData = self::getDefaultStatistics($levels);
  213. $yesterday = date('Ymd', strtotime("-1 day"));
  214. $today = date('Ymd');
  215. $globalMdl = self::find()->select(['id', 'browse_num', 'visitor_num', 'follow_num', 'register_num', 'goods_num', 'upgrade_level', 'date'])
  216. ->where(['mall_id' => $mall_id])->andWhere(['in', 'date', [$yesterday, $today]])->indexBy('date')->limit(2)->all();
  217. $globalStatistics = [];
  218. $insertData = $defaultData;
  219. if (!isset($globalMdl[$yesterday])) {
  220. $yesterdayModel = new self();
  221. $yesterdayModel->loadDefaultValues();
  222. $insertData['date'] = $yesterday;
  223. $insertData['mall_id'] = $mall_id;
  224. $yesterdayModel->load($insertData, '');
  225. $yesterdayModel->save();
  226. $yesterdayStatistics = $defaultData;
  227. } else {
  228. $yesterdayStatistics = $globalMdl[$yesterday];
  229. }
  230. if (!isset($globalMdl[$today])) {
  231. $todayModel = new self();
  232. $todayModel->loadDefaultValues();
  233. $insertData['date'] = $today;
  234. $insertData['mall_id'] = $mall_id;
  235. $todayModel->load($insertData, '');
  236. $todayModel->save();
  237. $todayStatistics = $defaultData;
  238. } else {
  239. $todayStatistics = $globalMdl[$today];
  240. }
  241. array_walk($defaultData, function($defaultValue, $field) use($levels, $todayStatistics, $yesterdayStatistics, &$globalStatistics) {
  242. if ($field == 'upgrade_level') {
  243. $globalStatistics['today_upgrade_level'] = self::removeLevel($todayStatistics['upgrade_level'], $levels);
  244. $globalStatistics['yesterday_upgrade_level'] = self::removeLevel($yesterdayStatistics['upgrade_level'], $levels);
  245. } else {
  246. $globalStatistics['today_' . $field] = $todayStatistics[$field];
  247. $globalStatistics['yesterday_' . $field] = $yesterdayStatistics[$field];
  248. }
  249. });
  250. $user_num = self::find()->where(['mall_id' => $mall_id])->sum('register_num');
  251. $globalStatistics['user_num'] = $user_num;
  252. $totalData = self::find()->where(['mall_id' => $mall_id])
  253. ->select(['sum(browse_num) total_browse_num', 'sum(visitor_num) total_visitor_num', 'sum(follow_num) total_follow_num'])->asArray()->one();
  254. if (empty($totalData)) {
  255. $globalStatistics['total_browse_num'] = 0;
  256. $globalStatistics['total_visitor_num'] = 0;
  257. $globalStatistics['total_follow_num'] = 0;
  258. } else {
  259. $globalStatistics['total_browse_num'] = $totalData['total_browse_num'];
  260. $globalStatistics['total_visitor_num'] = $totalData['total_visitor_num'];
  261. $globalStatistics['total_follow_num'] = $totalData['total_follow_num'];
  262. }
  263. return $globalStatistics;
  264. } catch (\Exception $e) {
  265. throw new \Exception('全局缓存。生成缓存数据' . $e->getMessage() . ',行数:' . $e->getLine());
  266. }
  267. }
  268. /**
  269. * 默认数据
  270. *
  271. * @param int mall_id
  272. * @param array $levels common\models\user\UserLevel
  273. * @param bool $day_data 是否分别设置昨天,今天数据
  274. * @author hpei
  275. * @return array
  276. */
  277. public static function getDefaultStatistics($levels, $day_data = false) {
  278. $upgrade_level = [];
  279. foreach ($levels as $level => $levelName) {
  280. $upgrade_level[$level] = 0;
  281. }
  282. $defaultData = self::$fields;
  283. $defaultData['upgrade_level'] = $upgrade_level;
  284. return $defaultData;
  285. }
  286. /**
  287. * 排除写入统计表后删除的等级
  288. *
  289. * @param array $upgradeLevel 要筛选的等级 [level=>level_num]
  290. * @param array $levels common\models\user\UserLevel
  291. * @author hpei
  292. * @return array
  293. */
  294. public static function removeLevel($upgradeLevel, $levels) {
  295. $newUpgradeLevel = [];
  296. foreach ($levels as $level => $levelName) {
  297. if (isset($upgradeLevel[$level])) {
  298. $newUpgradeLevel[$level] = $upgradeLevel[$level];
  299. } else {
  300. $newUpgradeLevel[$level] = 0;
  301. }
  302. }
  303. ksort($newUpgradeLevel);
  304. return $newUpgradeLevel;
  305. }
  306. /**
  307. * 添加注册用户数量
  308. *
  309. * @param int $mall_id
  310. */
  311. public static function increaseRegisterNum($mall_id) {
  312. try {
  313. $date = date('Ymd');
  314. $globalMdl = self::find()->where(['mall_id' => $mall_id, 'date' => $date])->one();
  315. if (empty($globalMdl)) {
  316. $globalMdl = new self();
  317. $levels = UserLevel::getListIndexByLevelColumn($mall_id);
  318. $defaultData = self::getDefaultStatistics($levels);
  319. $defaultData['mall_id'] = $mall_id;
  320. $defaultData['date'] = $date;
  321. $globalMdl->load($defaultData, '');
  322. $globalMdl->register_num = 1;
  323. } else {
  324. $globalMdl->register_num += 1;
  325. }
  326. $globalMdl->save();
  327. return true;
  328. } catch (\Exception $e) {
  329. self::$static_error = $e->getMessage();
  330. return false;
  331. }
  332. }
  333. /**
  334. * 发布商品
  335. */
  336. public static function publishGoods($mall_id) {
  337. try {
  338. $date = date('Ymd');
  339. $globalMdl = self::find()->select(['goods_num'])->where(['mall_id' => $mall_id, 'date' => $date])->one();
  340. if (empty($globalMdl)) {
  341. $globalMdl = new self();
  342. $levels = UserLevel::getListIndexByLevelColumn($mall_id);
  343. $defaultData = self::getDefaultStatistics($levels);
  344. $defaultData['mall_id'] = $mall_id;
  345. $defaultData['date'] = $date;
  346. $globalMdl->load($defaultData, '');
  347. $globalMdl->goods_num = 1;
  348. } else{
  349. $globalMdl->goods_num += 1;
  350. }
  351. if (!$globalMdl->save()) {
  352. self::$static_error = $globalMdl->getErrorMessage();
  353. return false;
  354. }
  355. return true;
  356. } catch (\Exception $e) {
  357. self::$static_error = $e->getMessage();
  358. return true;
  359. }
  360. }
  361. /**
  362. * 返回首页最终展示的数据
  363. */
  364. public static function getShowData($globalStatistics, $levels) {
  365. $list = [];
  366. foreach ($levels as $level => $levelName) {
  367. $proportion = 100;
  368. if(!empty($globalStatistics['yesterday_upgrade_level'][$level]) && !empty($globalStatistics['today_upgrade_level'][$level])){
  369. $proportion = ($globalStatistics['today_upgrade_level'][$level] - $globalStatistics['yesterday_upgrade_level'][$level])/$globalStatistics['yesterday_upgrade_level'][$level] * 100;
  370. }else{
  371. if(empty($globalStatistics['today_upgrade_level'][$level])) $proportion = 0;
  372. if(empty($globalStatistics['yesterday_upgrade_level'][$level])) $proportion = 0;
  373. }
  374. $list[] = ['id' => $level,
  375. 'name' => $levelName,
  376. 'today_num' => $globalStatistics['today_upgrade_level'][$level],
  377. 'yesterday_num' => $globalStatistics['yesterday_upgrade_level'][$level],
  378. 'proportion' =>$proportion
  379. ];
  380. }
  381. unset($globalStatistics['yesterday_upgrade_level'],$globalStatistics['today_upgrade_level']);
  382. $globalStatistics['level_list'] = $list;
  383. return $globalStatistics;
  384. }
  385. /**
  386. * 重新生成缓存
  387. */
  388. public static function resetCache($mall_id) {
  389. try {
  390. $levels = UserLevel::getListIndexByLevelColumn($mall_id);
  391. $globalStatistics = self::createCacheData($mall_id, $levels);
  392. self::setCache($mall_id, $globalStatistics);
  393. } catch (\Exception $e) {
  394. throw new \Exception($e->getMessage());
  395. }
  396. }
  397. }