BaseUserTeamStatistics.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. <?php
  2. namespace common\models\statistics;
  3. use common\helpers\FormatHelper;
  4. use common\models\base\BaseActiveRecord;
  5. use common\models\user\UserPartitionRelationship;
  6. use Exception;
  7. use Yii;
  8. use yii\db\ActiveQuery;
  9. /**
  10. * 团队统计数据
  11. */
  12. abstract class BaseUserTeamStatistics extends BaseActiveRecord
  13. {
  14. /**
  15. * 是否是根据日期统计
  16. *
  17. * @var bool
  18. */
  19. const IS_BY_DATE = true;
  20. /**
  21. * 允许自增的字段
  22. *
  23. * @var string[]
  24. */
  25. const ALLOW_INCR_FIELD = [
  26. 'user_direct_order_total',
  27. 'user_indirect_order_total',
  28. 'user_direct_order_price_total',
  29. 'user_indirect_order_price_total',
  30. 'team_finish_order_total',
  31. 'team_finish_order_price_total',
  32. 'user_direct_pay_selling_price',
  33. 'user_direct_finish_selling_price',
  34. 'user_indirect_pay_selling_price',
  35. 'user_indirect_finish_selling_price',
  36. 'team_finish_selling_price',
  37. 'team_pay_selling_price',
  38. 'team_goods_price_total',
  39. 'team_order_num',
  40. ];
  41. /**
  42. * 允许自减的字段
  43. *
  44. * @var string[]
  45. */
  46. const ALLOW_DECR_FIELD = [
  47. 'user_direct_order_total',
  48. 'user_indirect_order_total',
  49. 'user_direct_order_price_total',
  50. 'user_indirect_order_price_total',
  51. 'user_direct_pay_selling_price',
  52. 'user_indirect_pay_selling_price',
  53. 'team_pay_selling_price',
  54. 'team_goods_price_total',
  55. 'team_order_num',
  56. ];
  57. /**
  58. * 分了几张表
  59. */
  60. const TABLE_NUM = 10;
  61. /**
  62. * 当前表
  63. *
  64. * @var integer $currentTableNum
  65. */
  66. protected static $currentTableNum;
  67. /**
  68. * 切割规则
  69. *
  70. * @param integer $user_id
  71. *
  72. * @return int
  73. */
  74. public static function sliceRule(int $user_id): int
  75. {
  76. return $user_id % 10 ?: 10;
  77. }
  78. /**
  79. * @param int $tableNum
  80. * @param bool $isSliceRule
  81. *
  82. * @return ActiveQuery
  83. */
  84. public static function setTableNumAndReturnSelf(int $tableNum, bool $isSliceRule = true)
  85. {
  86. static::$currentTableNum = $isSliceRule ? $tableNum : self::sliceRule($tableNum);
  87. return static::find();
  88. }
  89. /**
  90. * 保存统计数据(整个上级都需要加)(只支持分表之后的表)
  91. *
  92. * @param array $baseData 基础数据(不需要保存的数据)
  93. * @param array $teamData 团队需要保存的数据
  94. * @param bool $isIncr 是否是自增
  95. * @param array $directData 直推需要保存的数据
  96. * @param array $indirectData 间推需要保存的数据
  97. *
  98. * @return bool
  99. */
  100. public static function saveStatistics(
  101. array $baseData,
  102. array $teamData,
  103. bool $isIncr = true,
  104. array $directData = [],
  105. array $indirectData = []
  106. ): bool
  107. {
  108. // 找出整个群组的人
  109. $parentUserIds = $baseData['parents_user_ids'] ?? UserPartitionRelationship::getParentIdArr($baseData['user_id']);
  110. if (empty($parentUserIds)) {
  111. return true;
  112. }
  113. // 直推ID
  114. $parentId = end($parentUserIds);
  115. // 间推ID
  116. $secondParentId = prev($parentUserIds);
  117. if (static::IS_BY_DATE) {
  118. $baseDateWhere = ['date' => $baseData['date']];
  119. } else {
  120. $baseDateWhere = [];
  121. }
  122. // 切割规则
  123. $sliceRuleUserIds = [];
  124. foreach ($parentUserIds as $userId) {
  125. $sliceRuleUserIds[static::sliceRule($userId)][] = $userId;
  126. }
  127. // 检查上级不存在的数据
  128. $findParentUserIds = [];
  129. foreach ($sliceRuleUserIds as $tableNum => $sliceRuleUserIdsItem) {
  130. $baseDateWhere['user_id'] = $sliceRuleUserIdsItem;
  131. $userIdsColumn = static::setTableNumAndReturnSelf($tableNum)
  132. ->where($baseDateWhere)
  133. ->select('user_id')
  134. ->column() ?: [];
  135. $findParentUserIds[] = $userIdsColumn;
  136. }
  137. $findParentUserIds = array_merge(...$findParentUserIds);
  138. $now = time();
  139. // 找差集
  140. $noExistUserIds = array_diff($parentUserIds, $findParentUserIds);
  141. if ($noExistUserIds) {
  142. $batchInsertData = [];
  143. $batchInsertField = ['mall_id', 'user_id', 'created_at', 'updated_at'];
  144. if (static::IS_BY_DATE) {
  145. $batchInsertField[] = 'date';
  146. }
  147. foreach ($noExistUserIds as $key => $noExistUserId) {
  148. $batchInsertData[self::sliceRule($noExistUserId)][$key] = [
  149. 'mall_id' => $baseData['mall_id'],
  150. 'user_id' => $noExistUserId,
  151. 'created_at' => $now,
  152. 'updated_at' => $now,
  153. ];
  154. if (static::IS_BY_DATE) {
  155. $batchInsertData[self::sliceRule($noExistUserId)][$key]['date'] = $baseData['date'];
  156. }
  157. }
  158. try {
  159. foreach ($batchInsertData as $tableNum => $values) {
  160. static::$currentTableNum = $tableNum;
  161. Yii::$app->db->createCommand()->batchInsert(
  162. static::tableName(),
  163. $batchInsertField,
  164. array_values($values)
  165. )->execute();
  166. }
  167. } catch (Exception $e) {
  168. $desc = sprintf(
  169. '团队统计,插入基础统计数据失败,data: %s',
  170. json_encode([
  171. 'class' => static::class,
  172. 'agrs' => func_get_args(),
  173. 'saveData' => array_merge(['tableName' => static::tableName()], compact('batchInsertField', 'batchInsertData'))
  174. ])
  175. );
  176. Yii::error(FormatHelper::exception($e, $desc));
  177. }
  178. }
  179. [
  180. 'allowSaveTeamData' => $allowSaveTeamData,
  181. 'allowSaveDirectData' => $allowSaveDirectData,
  182. 'allowSaveIndirectData' => $allowSaveIndirectData
  183. ] = self::getAllowSaveData($isIncr, $teamData, $directData, $indirectData);
  184. try {
  185. // 群组总业绩、群组产品总额、群组订单总数。(不包含自己)
  186. if ($allowSaveTeamData) {
  187. foreach ($sliceRuleUserIds as $tableNum => $sliceRuleUserIdsItem) {
  188. $baseDateWhere['user_id'] = $sliceRuleUserIdsItem;
  189. static::$currentTableNum = $tableNum;
  190. static::updateAllCounters($allowSaveTeamData, $baseDateWhere);
  191. }
  192. }
  193. // 保存直推数据
  194. if ($parentId && $allowSaveDirectData) {
  195. $baseDateWhere['user_id'] = $parentId;
  196. static::$currentTableNum = self::sliceRule($parentId);
  197. static::updateAllCounters($allowSaveDirectData, $baseDateWhere);
  198. }
  199. // 保存间推数据
  200. if ($secondParentId && $allowSaveIndirectData) {
  201. $baseDateWhere['user_id'] = $secondParentId;
  202. static::$currentTableNum = self::sliceRule($secondParentId);
  203. static::updateAllCounters($allowSaveIndirectData, $baseDateWhere);
  204. }
  205. } catch (Exception $e) {
  206. $desc = sprintf(
  207. '保存团队统计数据失败,data: %s',
  208. json_encode([
  209. 'class' => static::class,
  210. 'agrs' => func_get_args(),
  211. 'saveData' => compact('allowSaveTeamData', 'allowSaveDirectData', 'allowSaveIndirectData')
  212. ])
  213. );
  214. Yii::error(FormatHelper::exception($e, $desc));
  215. return false;
  216. }
  217. return true;
  218. }
  219. /**
  220. * 保存统计数据(整个上级都需要加)(只支持分表之后的表)
  221. *
  222. * @param array $baseData 基础数据(不需要保存的数据)
  223. * @param array $teamData 团队需要保存的数据
  224. * @param bool $isIncr 是否是自增
  225. * @param array $directData 直推需要保存的数据
  226. * @param array $indirectData 间推需要保存的数据
  227. *
  228. * @return bool
  229. */
  230. public static function saveStatisticsTotal(
  231. array $baseData,
  232. array $teamData,
  233. bool $isIncr = true,
  234. array $directData = [],
  235. array $indirectData = []
  236. ): bool
  237. {
  238. // 找出整个群组的人
  239. $parentUserIds = $baseData['parents_user_ids'] ?? UserPartitionRelationship::getParentIdArr($baseData['user_id']);
  240. if (empty($parentUserIds)) {
  241. return true;
  242. }
  243. // 直推ID
  244. $parentId = end($parentUserIds);
  245. // 间推ID
  246. $secondParentId = prev($parentUserIds);
  247. if (static::IS_BY_DATE) {
  248. $baseDateWhere = ['date' => $baseData['date']];
  249. } else {
  250. $baseDateWhere = [];
  251. }
  252. // 检查上级不存在的数据
  253. $baseDateWhere['user_id'] = $parentUserIds;
  254. $findParentUserIds = static::find()
  255. ->where($baseDateWhere)
  256. ->select('user_id')
  257. ->column() ?: [];
  258. $now = time();
  259. // 找差集
  260. $noExistUserIds = array_diff($parentUserIds, $findParentUserIds);
  261. if ($noExistUserIds) {
  262. $batchInsertData = [];
  263. $batchInsertField = ['mall_id', 'user_id', 'created_at', 'updated_at'];
  264. if (static::IS_BY_DATE) {
  265. $batchInsertField[] = 'date';
  266. }
  267. foreach ($noExistUserIds as $key => $noExistUserId) {
  268. $batchInsertData[$key] = [
  269. 'mall_id' => $baseData['mall_id'],
  270. 'user_id' => $noExistUserId,
  271. 'created_at' => $now,
  272. 'updated_at' => $now,
  273. ];
  274. if (static::IS_BY_DATE) {
  275. $batchInsertData[$key]['date'] = $baseData['date'];
  276. }
  277. }
  278. try {
  279. Yii::$app->db->createCommand()->batchInsert(
  280. static::tableName(),
  281. $batchInsertField,
  282. array_values($batchInsertData)
  283. )->execute();
  284. } catch (Exception $e) {
  285. $desc = sprintf(
  286. '团队统计,插入基础统计数据失败,data: %s',
  287. json_encode([
  288. 'class' => static::class,
  289. 'agrs' => func_get_args(),
  290. 'saveData' => array_merge(['tableName' => static::tableName()], compact('batchInsertField', 'batchInsertData'))
  291. ])
  292. );
  293. Yii::error(FormatHelper::exception($e, $desc));
  294. }
  295. }
  296. [
  297. 'allowSaveTeamData' => $allowSaveTeamData,
  298. 'allowSaveDirectData' => $allowSaveDirectData,
  299. 'allowSaveIndirectData' => $allowSaveIndirectData
  300. ] = self::getAllowSaveData($isIncr, $teamData, $directData, $indirectData);
  301. try {
  302. // 群组总业绩、群组产品总额、群组订单总数。(不包含自己)
  303. if ($allowSaveTeamData) {
  304. $baseDateWhere['user_id'] = $parentUserIds;
  305. static::updateAllCounters($allowSaveTeamData, $baseDateWhere);
  306. }
  307. // 保存直推数据
  308. if ($parentId && $allowSaveDirectData) {
  309. $baseDateWhere['user_id'] = $parentId;
  310. static::updateAllCounters($allowSaveDirectData, $baseDateWhere);
  311. }
  312. // 保存间推数据
  313. if ($secondParentId && $allowSaveIndirectData) {
  314. $baseDateWhere['user_id'] = $secondParentId;
  315. static::updateAllCounters($allowSaveIndirectData, $baseDateWhere);
  316. }
  317. } catch (Exception $e) {
  318. $desc = sprintf(
  319. '保存团队统计数据失败,data: %s',
  320. json_encode([
  321. 'class' => static::class,
  322. 'agrs' => func_get_args(),
  323. 'saveData' => compact('allowSaveTeamData', 'allowSaveDirectData', 'allowSaveIndirectData')
  324. ])
  325. );
  326. Yii::error(FormatHelper::exception($e, $desc));
  327. return false;
  328. }
  329. return true;
  330. }
  331. /**
  332. * 获取允许操作的字段
  333. *
  334. * @param bool $isIncr
  335. * @param array $teamData
  336. * @param array $directData
  337. * @param array $indirectData
  338. *
  339. * @return array
  340. */
  341. protected static function getAllowSaveData(bool $isIncr, array $teamData, array $directData, array $indirectData): array
  342. {
  343. $allowSaveTeamData = [];
  344. // 直推数据
  345. $allowSaveDirectData = [];
  346. // 间推数据
  347. $allowSaveIndirectData = [];
  348. // 允许操作的字段
  349. $allowUpdateField = $isIncr ? static::ALLOW_INCR_FIELD : static::ALLOW_DECR_FIELD;
  350. $model = new static;
  351. foreach ($model as $field => $_) {
  352. !empty($teamData[$field]) && in_array($field, $allowUpdateField, true) && $allowSaveTeamData[$field] = $teamData[$field];
  353. !empty($directData[$field]) && in_array($field, $allowUpdateField, true) && $allowSaveDirectData[$field] = $directData[$field];
  354. !empty($indirectData[$field]) && in_array($field, $allowUpdateField, true) && $allowSaveIndirectData[$field] = $indirectData[$field];
  355. }
  356. return compact('allowSaveTeamData', 'allowSaveDirectData', 'allowSaveIndirectData');
  357. }
  358. }