OrderFack.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. <?php
  2. /**
  3. * 厦门云联储网络科技有限公司
  4. * https://www.baokuaiyun.com
  5. * Copyright (c) 2023 爆块云 All rights reserved.
  6. */
  7. namespace app\models;
  8. use yii\db\Query;
  9. use yii\db\Expression;
  10. use yii\helpers\Json;
  11. /**
  12. * This is the model class for table "{{%order_fack}}".
  13. *
  14. * @property integer $id
  15. * @property integer $store_id
  16. * @property integer $user_id
  17. * @property string $order_no
  18. * @property string $total_price
  19. * @property string $pay_price
  20. * @property string $name
  21. * @property string $mobile
  22. * @property string $address
  23. * @property string $address_data
  24. * @property integer $is_pay
  25. * @property integer $pay_type
  26. * @property integer $pay_time
  27. * @property integer $send_time
  28. * @property integer $confirm_time
  29. * @property integer $is_comment
  30. * @property integer $created_at
  31. * @property integer $is_delete
  32. * @property integer $goods_id
  33. * @property string $goods_name
  34. * @property string $virtual_user
  35. * @property integer $is_virtual
  36. * @property integer $trade_status
  37. * @property integer $mch_id
  38. * @property integer $updated_at
  39. */
  40. class OrderFack extends \yii\db\ActiveRecord
  41. {
  42. // 从 Order.php 复制常量定义
  43. /**
  44. * 订单流转状态:默认
  45. */
  46. const ORDER_FLOW_DEFAULT = -1;
  47. /**
  48. * 订单流转状态:待发货
  49. */
  50. const ORDER_FLOW_NO_SEND = 0;
  51. /**
  52. * 订单流转状态:已取消
  53. */
  54. const ORDER_FLOW_CANCEL = 1;
  55. /**
  56. * 订单流转状态:已发货
  57. */
  58. const ORDER_FLOW_SEND = 2;
  59. /**
  60. * 订单流转状态:已完成
  61. */
  62. const ORDER_FLOW_CONFIRM = 3;
  63. const TRADE_STATUS_TEXT = [
  64. self::ORDER_FLOW_DEFAULT => '待付款',
  65. self::ORDER_FLOW_NO_SEND => '待发货',
  66. self::ORDER_FLOW_SEND => '已发货',
  67. self::ORDER_FLOW_CANCEL => '已取消',
  68. self::ORDER_FLOW_CONFIRM => '已完成',
  69. ];
  70. /**
  71. * 是否支付:已支付
  72. */
  73. const IS_PAY_TRUE = 1;
  74. /**
  75. * 是否支付:未支付
  76. */
  77. const IS_PAY_FALSE = 0;
  78. /**
  79. * 支付方式:微信支付
  80. */
  81. const PAY_TYPE_WECHAT = 1;
  82. /**
  83. * 支付方式:支付宝支付
  84. */
  85. const PAY_TYPE_ALI = 4;
  86. /**
  87. * 是否取消(手动):已取消
  88. */
  89. const IS_DELETE_TRUE = 1;
  90. /**
  91. * 是否取消(手动):未取消
  92. */
  93. const IS_DELETE_FALSE = 0;
  94. /**
  95. * @inheritdoc
  96. */
  97. public static function tableName()
  98. {
  99. return '{{%order_fack}}';
  100. }
  101. /**
  102. * @inheritdoc
  103. */
  104. public function rules()
  105. {
  106. return [
  107. [['store_id', 'user_id', 'order_no', 'total_price', 'pay_price'], 'required'],
  108. [['store_id', 'user_id', 'is_pay', 'pay_type', 'pay_time', 'send_time', 'confirm_time',
  109. 'is_comment', 'created_at', 'is_delete', 'goods_id', 'is_virtual', 'trade_status', 'mch_id', 'updated_at'], 'integer'],
  110. [['total_price', 'pay_price'], 'number'],
  111. [['order_no', 'name', 'mobile', 'goods_name', 'virtual_user'], 'string', 'max' => 255],
  112. [['address'], 'string', 'max' => 1000],
  113. [['address_data'], 'string'],
  114. ];
  115. }
  116. /**
  117. * @inheritdoc
  118. */
  119. public function attributeLabels()
  120. {
  121. return [
  122. ];
  123. }
  124. /**
  125. * 生成虚拟订单
  126. */
  127. public static function generateVirtualOrders($goodsId, $storeId, $count = null)
  128. {
  129. if ($count === null) {
  130. $count = rand(5, 15); // 随机5-15条虚拟订单
  131. }
  132. $orders = [];
  133. $currentTime = time();
  134. for ($i = 0; $i < $count; $i++) {
  135. $address = self::getAddress();
  136. // 根据订单位置设置不同的时间范围
  137. if ($i > 20) {
  138. // 前10条订单:最近30分钟内
  139. $orderTime = $currentTime - rand(0, 1800); // 0-30分钟
  140. } else {
  141. // 其他订单:24小时内(30分钟-24小时)
  142. $orderTime = $currentTime - rand(1800, 86400); // 30分钟-24小时
  143. }
  144. // 根据订单时间计算后续时间点(保持合理的时间间隔)
  145. $payTime = $orderTime + rand(60, 1800); // 1-30分钟内支付(快速支付)
  146. $sendTime = $payTime + rand(3600, 21600); // 1-6小时内发货(快速发货)
  147. $confirmTime = $sendTime + rand(86400, 172800); // 1-2天内确认收货
  148. $order = new self();
  149. $order->store_id = 1;
  150. $order->user_id = $address->user_id;
  151. $order->order_no = 'VF' . date('YmdHis', $orderTime) . rand(1000, 9999); // 使用订单时间生成订单号
  152. $order->total_price = rand(100, 500) + rand(0, 99) / 100;
  153. $order->pay_price = $order->total_price;
  154. $order->name = $address->name;
  155. $order->mobile = $address->mobile;
  156. $order->address = trim($address->province).trim($address->city).trim($address->district).trim($address->detail);
  157. $order->is_pay = 1;
  158. $order->pay_type = 1;
  159. $order->pay_time = $payTime;
  160. $order->send_time = $sendTime;
  161. $order->confirm_time = $confirmTime;
  162. $order->is_comment = rand(0, 1); // 随机是否已评价
  163. $order->created_at = $orderTime;
  164. $order->is_delete = 0;
  165. $order->goods_id = $goodsId;
  166. $order->goods_name = '虚拟商品';
  167. $order->virtual_user = '';
  168. $order->is_virtual = 1;
  169. $order->trade_status = 3; // 已确认收货
  170. $order->mch_id = 0;
  171. $order->updated_at = $currentTime;
  172. $order->address_data = json_encode([
  173. 'province' => trim($address->province),
  174. 'city' => trim($address->city),
  175. 'district' => trim($address->district),
  176. 'detail' => trim($address->detail),
  177. 'latitude' => $address->latitude,
  178. 'longitude' => $address->longitude,
  179. ], JSON_UNESCAPED_UNICODE);
  180. $orders[] = $order;
  181. }
  182. return $orders;
  183. }
  184. private static function getAddress()
  185. {
  186. for ($i = 0; $i < 10; $i++){
  187. $address = Address::find()->where(['is_delete' => 0])->orderBy('RAND()')->one();
  188. $fack = OrderFack::find()->where(['user_id'=>$address->user_id])->one();
  189. if(!$fack){
  190. return $address;
  191. }
  192. }
  193. return $address;
  194. }
  195. /**
  196. * 获取商品的虚拟订单
  197. */
  198. public static function getGoodsVirtualOrders($goodsId, $storeId)
  199. {
  200. try {// 先查询已存在的虚拟订单
  201. $fiveMinutesAgo = time() - 300; // 当前时间戳 - 300秒(5分钟)
  202. $existingOrders = self::find()
  203. ->where(['goods_id' => $goodsId, 'is_delete' => 0])
  204. ->andWhere(['>=', 'created_at', $fiveMinutesAgo])
  205. ->orderBy(['created_at' => SORT_DESC])
  206. ->all();// 如果没有数据,生成新的虚拟订单
  207. if (empty($existingOrders)) {
  208. // 生成随机数量(20-100条)
  209. $randomCount = rand(1, 3);
  210. $newOrders = self::generateVirtualOrders($goodsId, $storeId, $randomCount);
  211. $successCount = 0;
  212. foreach ($newOrders as $index => $order) {
  213. // 设置创建时间:前10条在0-30分钟内,其余在24小时内随机分布
  214. /*if ($index < 10) {
  215. // 0-30分钟内的随机时间
  216. $minutes = rand(0, 30);
  217. } else {
  218. // 其他订单在24小时内随机分布
  219. $minutes = rand(30, 24 * 60 - 1); // 30-1439分钟(确保在24小时内但超过30分钟)
  220. }
  221. $order->created_at = date('Y-m-d H:i:s', strtotime("-$minutes minutes"));*/
  222. if (!$order->save()) {
  223. throw new \Exception('保存虚拟订单失败:' . json_encode($order->getErrorSummary(false)[0]));
  224. // 同时生成订单详情
  225. //OrderDetailFack::generateVirtualOrderDetail($order);
  226. }
  227. $successCount++;
  228. }
  229. return true;
  230. }// 如果有数据,检查最新10条数据的时间分布
  231. $recentOrders = array_slice($existingOrders, 0, 10);
  232. $currentTime = time();
  233. $thirtyMinutesAgo = $currentTime - (30 * 60);
  234. $needsTimeAdjustment = false;// 检查前10条订单是否都在30分钟内
  235. foreach ($recentOrders as $order) {
  236. $orderTime = strtotime($order->created_at);
  237. if ($orderTime < $thirtyMinutesAgo) {
  238. $needsTimeAdjustment = true;
  239. break;
  240. }
  241. }// 如果需要调整时间,更新前10条订单的时间
  242. if ($needsTimeAdjustment) {
  243. self::adjustRecentOrdersTime($goodsId, 10);
  244. }
  245. return true;
  246. } catch (\Exception $e) {
  247. ActionLog::addLog(1,'OrderFackJob',$e->getMessage());
  248. }catch (\Throwable $e) {
  249. ActionLog::addLog(1,'OrderFackJob',$e->getMessage());
  250. }
  251. }
  252. private static function adjustRecentOrdersTime($goodsId, $count)
  253. {
  254. // 获取需要调整的订单(按创建时间倒序,取最新的几个)
  255. $ordersToAdjust = self::find()
  256. ->where(['goods_id' => $goodsId, 'is_delete' => 0])
  257. ->orderBy(['created_at' => SORT_DESC])
  258. ->limit($count)
  259. ->all();
  260. foreach ($ordersToAdjust as $index => $order) {
  261. // 修改为0-30分钟内的随机时间
  262. $minutes = rand(0, 30);
  263. $order->created_at = date('Y-m-d H:i:s', strtotime("-$minutes minutes"));
  264. $order->save();
  265. }
  266. }
  267. /**
  268. * 获取混合订单(真实+虚拟)
  269. */
  270. public static function getMixedOrders($goodsId, $limit = 20, $page = 1)
  271. {
  272. // 构建联合查询
  273. $query = new Query();
  274. $offset = ($page - 1) * $limit;
  275. // 真实订单查询 - 添加 avatar_url
  276. $realQuery = (new Query())
  277. ->select([
  278. 'o.id', 'o.user_id', 'o.name','o.address_data', 'o.created_at',
  279. 'u.avatar_url',
  280. //new \yii\db\Expression("'real' as order_type")
  281. ])
  282. ->from(['o' => Order::tableName()])
  283. ->innerJoin(['od' => OrderDetail::tableName()], 'o.id = od.order_id')
  284. ->leftJoin(['u' => User::tableName()], 'o.user_id = u.id') // 左连接用户表
  285. ->where([
  286. 'od.goods_id' => $goodsId,
  287. 'o.is_delete' => 0
  288. ]);
  289. // 虚拟订单查询 - 添加 avatar_url
  290. $virtualQuery = (new Query())
  291. ->select([
  292. 'of.id', 'of.user_id', 'of.name','of.address_data', 'of.created_at',
  293. 'u.avatar_url',
  294. //new \yii\db\Expression("'virtual' as order_type")
  295. ])
  296. ->from(['of' => OrderFack::tableName()])
  297. ->leftJoin(['u' => User::tableName()], 'of.user_id = u.id') // 左连接用户表
  298. ->where([
  299. //'of.goods_id' => $goodsId,
  300. 'of.is_delete' => 0
  301. ]);
  302. // 合并查询
  303. $unionQuery = (new Query())
  304. ->from(['union_data' => $realQuery->union($virtualQuery, true)]);
  305. $countQuery = clone $unionQuery;
  306. // 先获取基础订单数据
  307. $baseOrders = $unionQuery
  308. ->orderBy('created_at DESC')
  309. ->offset($offset)
  310. ->limit($limit)
  311. ->all();
  312. if (empty($baseOrders)) {
  313. return [];
  314. }
  315. // 提取所有用户ID
  316. $userIds = array_unique(array_column($baseOrders, 'user_id'));
  317. // 分别查询真实订单和虚拟订单的用户购买次数
  318. $realPurchaseCounts = (new Query())
  319. ->select(['o.user_id', 'COUNT(*) as purchase_count'])
  320. ->from(['o' => Order::tableName()])
  321. ->innerJoin(['od' => OrderDetail::tableName()], 'o.id = od.order_id')
  322. ->where([
  323. 'od.goods_id' => $goodsId,
  324. 'o.is_delete' => 0,
  325. 'o.user_id' => $userIds
  326. ])
  327. ->groupBy('o.user_id')
  328. ->indexBy('user_id')
  329. ->all();
  330. $virtualPurchaseCounts = (new Query())
  331. ->select(['user_id', 'COUNT(*) as purchase_count'])
  332. ->from(OrderFack::tableName())
  333. ->where([
  334. //'goods_id' => $goodsId,
  335. 'is_delete' => 0,
  336. 'user_id' => $userIds
  337. ])
  338. ->groupBy('user_id')
  339. ->indexBy('user_id')
  340. ->all();
  341. // 合并购买次数(真实订单 + 虚拟订单)
  342. $purchaseCounts = [];
  343. foreach ($userIds as $userId) {
  344. $realCount = isset($realPurchaseCounts[$userId]) ? $realPurchaseCounts[$userId]['purchase_count'] : 0;
  345. $virtualCount = isset($virtualPurchaseCounts[$userId]) ? $virtualPurchaseCounts[$userId]['purchase_count'] : 0;
  346. $purchaseCounts[$userId] = $realCount + $virtualCount;
  347. }
  348. // 将购买次数添加到订单数据中
  349. foreach ($baseOrders as &$order) {
  350. $order['purchase_count'] = $purchaseCounts[$order['user_id']] ?? 0;
  351. $order['formatted_time'] = self::formatOrderTime($order['created_at']);
  352. $address = Json::decode($order['address_data']);
  353. if (!empty($address['city'])) $order['city'] = $address['city'];
  354. unset($order['address_data']);
  355. //处理数据将名字后面*处理
  356. $order['name'] = self::maskName($order['name']);
  357. }
  358. $data['list'] = $baseOrders;
  359. $data['pageNo'] = $page;
  360. $data['pageSize'] = $limit;
  361. $data['totalCount'] = $countQuery->count();
  362. return $data;
  363. }
  364. //对姓名进行脱敏处理
  365. private static function maskName($name) {
  366. if (empty($name)) {
  367. return '';
  368. }
  369. // 获取第一个字符
  370. $firstChar = mb_substr($name, 0, 1, 'UTF-8');
  371. // 获取姓名长度
  372. $length = mb_strlen($name, 'UTF-8');
  373. // 如果只有一个字,直接返回
  374. if ($length <= 1) {
  375. return $firstChar;
  376. }
  377. // 生成星号
  378. $stars = str_repeat('*', $length - 1);
  379. return $firstChar . $stars;
  380. }
  381. private static function formatOrderTime($orderTime)
  382. {
  383. if (empty($orderTime)) {
  384. return '';
  385. }
  386. $orderTimestamp = is_numeric($orderTime) ? $orderTime : strtotime($orderTime);
  387. $currentTimestamp = time();
  388. $diff = $currentTimestamp - $orderTimestamp;
  389. // 1分钟内显示"刚刚"
  390. if ($diff < 60) {
  391. return '刚刚';
  392. }
  393. // 1-10分钟内显示"一分钟前"
  394. if ($diff < 600) { // 10分钟 = 600秒
  395. return '一分钟前';
  396. }
  397. // 10-30分钟内显示"10分钟前"
  398. if ($diff < 1800) { // 30分钟 = 1800秒
  399. return '10分钟前';
  400. }
  401. // 检查是否是当天
  402. $orderDate = date('Y-m-d', $orderTimestamp);
  403. $currentDate = date('Y-m-d', $currentTimestamp);
  404. // 30分钟-当天23:59:59显示"半小时前"
  405. if ($orderDate === $currentDate) {
  406. return '半小时前';
  407. }
  408. // 不是当天则显示MM-DD
  409. return date('m-d', $orderTimestamp);
  410. }
  411. }