ProductController.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. <?php
  2. /**
  3. * 商品控制器 (控制台脚本)
  4. *
  5. * 用途:用于定时任务、命令行脚本处理商品数据,如复制花材、预售到期自动取消等。
  6. * 谁用:系统定时任务 (Cron Job) 自动调用。
  7. * 解决什么问题:解决商品后台定时数据维护问题,如预售到期自动取消与部分日期到期更新。
  8. */
  9. namespace console\controllers;
  10. use biz\item\classes\PtItemClass;
  11. use biz\product\classes\ProductClass;
  12. use biz\product\models\Product;
  13. use biz\shop\classes\ShopClass;
  14. use bizGhs\item\classes\ItemClass;
  15. use bizGhs\item\models\Item;
  16. use common\components\stringUtil;
  17. use common\components\util;
  18. use yii\console\Controller;
  19. use Yii;
  20. class ProductController extends Controller
  21. {
  22. //复制花材 ssh 20240422
  23. public function actionClone()
  24. {
  25. ini_set('memory_limit', '5045M');
  26. set_time_limit(0);
  27. if (getenv('YII_ENV') == 'production') {
  28. $shopId = 23580;
  29. } else {
  30. $shopId = 36523;
  31. }
  32. $shop = ShopClass::getById($shopId, true);
  33. if (empty($shop)) {
  34. util::stop();
  35. }
  36. $mainId = $shop->mainId ?? 0;
  37. $productList = ProductClass::getAllByCondition(['mainId' => $mainId, 'classId' => ['in', [64848, 64849, 64850, 64851, 64852, 64853, 64854, 64855, 64856, 64857, 64858, 64859, 64860, 64861, 64862, 64863, 64865, 64866]]], null, '*');
  38. if (!empty($productList)) {
  39. foreach ($productList as $post) {
  40. $post['copy'] = 1;
  41. $post['auth'] = 0;
  42. $post['skPrice'] = $post['price'];
  43. $post['hjPrice'] = $post['price'];
  44. $post['sjId'] = $shop->sjId ?? 0;
  45. unset($post['viewNum']);
  46. unset($post['actualSold']);
  47. unset($post['itemId']);
  48. unset($post['id']);
  49. unset($post['delStatus']);
  50. $a = $post;
  51. $a['name'] = $a['name'] . ' A级';
  52. $a['py'] = stringUtil::py($a['name']);
  53. \bizGhs\product\classes\ProductClass::addProduct($a, $shop);
  54. $b = $post;
  55. $b['name'] = $b['name'] . ' B级';
  56. $b['py'] = stringUtil::py($b['name']);
  57. \bizGhs\product\classes\ProductClass::addProduct($b, $shop);
  58. $ab = $post;
  59. $ab['name'] = $ab['name'] . ' AB级';
  60. $ab['py'] = stringUtil::py($ab['name']);
  61. \bizGhs\product\classes\ProductClass::addProduct($ab, $shop);
  62. // $c = $post;
  63. // $c['name'] = $c['name'] . ' C级';
  64. // $c['py'] = stringUtil::py($c['name']);
  65. // \bizGhs\product\classes\ProductClass::addProduct($c, $shop);
  66. //
  67. // $d = $post;
  68. // $d['name'] = $d['name'] . ' D级';
  69. // $d['py'] = stringUtil::py($d['name']);
  70. // \bizGhs\product\classes\ProductClass::addProduct($d, $shop);
  71. // $e = $post;
  72. // $e['name'] = $e['name'] . ' E级';
  73. // $e['py'] = stringUtil::py($e['name']);
  74. // \bizGhs\product\classes\ProductClass::addProduct($e, $shop);
  75. }
  76. }
  77. }
  78. public function actionChangeSk()
  79. {
  80. ini_set('memory_limit', '2045M');
  81. set_time_limit(0);
  82. //$id = 36523;
  83. $id = 520;
  84. $shop = ShopClass::getById($id, true);
  85. $mainId = $shop->mainId ?? 0;
  86. $sjId = $shop->sjId ?? 0;
  87. $chainShopList = ShopClass::getAllByCondition(['sjId' => $sjId, 'join' => 0], null, '*', null, true);
  88. $default = $shop->default ?? 0;
  89. $productList = ProductClass::getAllByCondition(['mainId' => $mainId], null, '*', 'id', true);
  90. if (!empty($productList)) {
  91. foreach ($productList as $product) {
  92. $price = $product->price ?? 0;
  93. $skPrice = bcadd(floatval($price), 3, 0);
  94. $product->skPrice = $skPrice;
  95. $product->save();
  96. $ptItemId = $product->itemId ?? 0;
  97. if ($default == 1) {
  98. foreach ($chainShopList as $chain) {
  99. if ($chain->id == $shop->id) {
  100. continue;
  101. }
  102. $chainMainId = $chain->mainId ?? 0;
  103. $chainProduct = ProductClass::getByCondition(['mainId' => $chainMainId, 'itemId' => $ptItemId], true);
  104. if (!empty($chainProduct)) {
  105. $chainProduct->skPrice = $skPrice;
  106. $chainProduct->save();
  107. }
  108. }
  109. }
  110. }
  111. }
  112. }
  113. //修改散客比批发价格贵 ssh 20220609
  114. public function actionSkMore()
  115. {
  116. $query = new \yii\db\Query();
  117. $query->from(Item::tableName());
  118. $query->where(['>', 'id', 0]);
  119. foreach ($query->batch() as $batchItem) {
  120. foreach ($batchItem as $item) {
  121. $addPrice = $item['addPrice'] ?? 0;
  122. $id = $item['id'] ?? 0;
  123. $skAddPrice = $item['skAddPrice'] ?? 0;
  124. if ($skAddPrice > $addPrice) {
  125. $add = bcsub($skAddPrice, $addPrice, 2);
  126. ItemClass::updateById($id, ['skMore' => $add]);
  127. } else {
  128. echo "零售加价小于批发加价 {$id}\n";
  129. }
  130. }
  131. }
  132. }
  133. //更新纯彩花艺的价格 ./yii product/update-price ssh 20211020
  134. public function actionUpdatePrice()
  135. {
  136. $list = ProductClass::getAllByCondition(['shopId' => 10], null, '*', null, true);
  137. if (!empty($list)) {
  138. foreach ($list as $key => $item) {
  139. $price = $item->price ?? 0;
  140. $addPrice = $item->addPrice ?? 0;
  141. $skAddPrice = $item->skAddPrice ?? 0;
  142. $hjAddPrice = $item->hjAddPrice ?? 0;
  143. $zsAddPrice = $item->zsAddPrice ?? 0;
  144. $skDiff = bcsub($addPrice, $skAddPrice, 2);
  145. $skPrice = bcsub($price, $skDiff, 2);
  146. $hjDiff = bcsub($addPrice, $hjAddPrice, 2);
  147. $hkPrice = bcsub($price, $hjDiff, 2);
  148. $zsDiff = bcsub($addPrice, $zsAddPrice, 2);
  149. $zsPrice = bcsub($price, $zsDiff, 2);
  150. $item->skPrice = $skPrice;
  151. $item->hjPrice = $hkPrice;
  152. $item->zsPrice = $zsPrice;
  153. $item->save();
  154. }
  155. }
  156. }
  157. // ./yii product/reset-cover
  158. public function actionResetCover()
  159. {
  160. $sql = "SELECT * FROM `" . Product::tableName() . "` WHERE cover not like '%.%'";
  161. $command = Yii::$app->db->createCommand($sql);
  162. $data = $command->queryAll();
  163. if (empty($data)) {
  164. return false;
  165. }
  166. foreach ($data as $item) {
  167. $id = $item['id'] ?? 0;
  168. $ptItemId = $item['itemId'] ?? 0;
  169. $preCover = $item['cover'] ?? '';
  170. $ptItem = PtItemClass::getById($ptItemId, true);
  171. $cover = $ptItem->cover ?? '';
  172. if (!empty($cover)) {
  173. ProductClass::updateById($id, ['cover' => $cover]);
  174. echo "productId:" . $id . "原图:{$preCover}, 恢复图片:" . $cover . "\n";
  175. } else {
  176. echo "productId:" . $id . " 恢复图片没有成功-----------------\n";
  177. }
  178. }
  179. }
  180. // ./yii product/sk-price
  181. public function actionSkPrice()
  182. {
  183. ini_set('memory_limit', '2045M');
  184. set_time_limit(0);
  185. $list = ProductClass::getAllByCondition([], null, '*', null, true);
  186. if (empty($list)) {
  187. return false;
  188. }
  189. foreach ($list as $key => $item) {
  190. $price = $item->price ?? 0;
  191. $skAddPrice = $item->skAddPrice ?? 0;
  192. $addPrice = $item->addPrice ?? 0;
  193. $diff = bcsub($skAddPrice, $addPrice, 2);
  194. $currentPrice = $price;
  195. if ($diff > 0) {
  196. $currentPrice = bcadd($price, $diff, 2);
  197. }
  198. $item->skPrice = $currentPrice;
  199. $item->save();
  200. }
  201. }
  202. //二个空间问题
  203. public function actionSame()
  204. {
  205. ini_set('memory_limit', '5045M');
  206. set_time_limit(0);
  207. if (getenv('YII_ENV') == 'production') {
  208. $mainId = 8164;
  209. } else {
  210. $mainId = 644;
  211. }
  212. $list = ProductClass::getAllByCondition(['mainId' => $mainId], null, '*', null);
  213. if (!empty($list)) {
  214. $ids = array_column($list, 'itemId');
  215. $ptInfo = PtItemClass::getByIds($ids, null, 'id');
  216. foreach ($list as $item) {
  217. $id = $item['id'] ?? 0;
  218. $name = $item['name'] ?? '';
  219. $ptItemId = $item['itemId'] ?? 0;
  220. $pt = $ptInfo[$ptItemId];
  221. $ptName = $pt['name'] ?? '';
  222. if ($name != $ptName) {
  223. echo $id . ' ' . $name . ' ' . $ptName . "\n";
  224. }
  225. }
  226. }
  227. }
  228. //二个空间问题
  229. public function actionEmpty()
  230. {
  231. ini_set('memory_limit', '5045M');
  232. set_time_limit(0);
  233. $list = PtItemClass::getAllByCondition(['delStatus' => 0], null, '*', null, true);
  234. if (!empty($list)) {
  235. foreach ($list as $item) {
  236. $name = $item->name ?? '';
  237. $name = trim($name);
  238. $name = str_replace(" ", " ", $name);
  239. $name = str_replace(" ", " ", $name);
  240. $item->name = $name;
  241. $item->save();
  242. }
  243. }
  244. }
  245. //恢复花材初始名称 ssh 20250213
  246. public function actionRecover()
  247. {
  248. ini_set('memory_limit', '5045M');
  249. set_time_limit(0);
  250. if (getenv('YII_ENV') == 'production') {
  251. $mainId = 26374;
  252. } else {
  253. $mainId = 644;
  254. }
  255. $list = ProductClass::getAllByCondition(['mainId' => $mainId], null, '*', null);
  256. if (!empty($list)) {
  257. $ids = array_column($list, 'itemId');
  258. $ptInfo = PtItemClass::getByIds($ids, null, 'id');
  259. foreach ($list as $item) {
  260. $id = $item['id'] ?? 0;
  261. $name = $item['name'] ?? '';
  262. $ptItemId = $item['itemId'] ?? 0;
  263. $pt = $ptInfo[$ptItemId];
  264. $ptName = $pt['name'] ?? '';
  265. if (!empty($ptName)) {
  266. $ptPy = stringUtil::py($ptName);
  267. ProductClass::updateById($id, ['name' => $ptName, 'py' => $ptPy]);
  268. }
  269. }
  270. }
  271. }
  272. /**
  273. * 预售到期自动取消与更新脚本
  274. *
  275. * 职责:每天凌晨执行,扫描所有预售商品,若预售日期已到则进行清除或更新。
  276. * 入参/返回:无(定时脚本,无入参,无返回值)
  277. * 副作用:会修改商品表(product)的 presell 和 presellDate 字段,并修改 Redis 缓存。
  278. * 关键边界:
  279. * 1. 如果设置的全部预售日期都到了,则彻底清除预售状态(presell = 0,presellDate = ''),并删除 Redis 缓存。
  280. * 2. 如果只到了其中部分日期,则只清除已到期的日期,保留未到期的日期,预售继续(presell = 1,更新 presellDate),并更新 Redis 缓存为剩余最早日期。
  281. */
  282. public function actionCancelPresell()
  283. {
  284. try{
  285. $now = time(); //注意脚本执行时间要在凌晨0点之后,否则会出问题
  286. $redis = Yii::$app->redis;
  287. $cursor = '0';
  288. do {
  289. $scanResult = $redis->executeCommand('SCAN', [$cursor, 'MATCH', 'product_presell:*', 'COUNT', 500]);
  290. $cursor = $scanResult[0] ?? '0';
  291. $keys = $scanResult[1] ?? [];
  292. if (empty($keys)) {
  293. continue;
  294. }
  295. foreach ($keys as $key) {
  296. $firstDate = (int)$redis->get($key);
  297. if ($firstDate >= $now) {
  298. continue;
  299. }
  300. $id = (int)str_replace('product_presell:', '', $key);
  301. if ($id <= 0) {
  302. $redis->del($key);
  303. continue;
  304. }
  305. $item = ProductClass::getById($id, true);
  306. if (empty($item)) {
  307. $redis->del($key);
  308. continue;
  309. }
  310. if ((int)$item->presell !== 1) {
  311. $redis->del($key);
  312. continue;
  313. }
  314. // 解析商品当前的预售日期列表,干什么:获取所有设置的预售日期时间戳,为什么:需要逐一比对是否到期
  315. $presellDateStr = trim($item->presellDate ?? '');
  316. if (empty($presellDateStr)) {
  317. // 预售日期为空,干什么:直接清除预售状态,为什么:没有日期的预售是无效数据,必须清除并删缓存
  318. $item->presell = 0;
  319. $item->presellDate = '';
  320. $item->save();
  321. $redis->del($key);
  322. continue;
  323. }
  324. $presellDates = explode(',', $presellDateStr);
  325. $presellDates = array_unique(array_filter(array_map('intval', $presellDates)));
  326. // 过滤掉所有已经到期(小于当前时间)的预售日期,干什么:筛选出未来还未到期的预售日期,为什么:只保留未到期的预售日期
  327. $remainingDates = [];
  328. foreach ($presellDates as $date) {
  329. if ($date >= $now) {
  330. $remainingDates[] = $date;
  331. }
  332. }
  333. // 根据剩余日期情况,决定是完全清除预售还是只清除部分日期
  334. if (empty($remainingDates)) {
  335. // 全部预售日期都到了,干什么:清除预售状态,删除 Redis 缓存,为什么:所有预售日期都已过期,商品不再是预售商品
  336. $item->presell = 0;
  337. $item->presellDate = '';
  338. $item->save();
  339. $redis->del($key);
  340. } else {
  341. // 只到了其中部分日期,干什么:清除已到期的日期,预售继续,并更新 Redis 缓存为剩余的最早日期,为什么:部分日期未到,预售仍需继续
  342. sort($remainingDates);
  343. $item->presellDate = implode(',', $remainingDates);
  344. $item->save();
  345. $redis->set($key, $remainingDates[0]);
  346. }
  347. }
  348. } while ($cursor !== '0');
  349. Yii::info('预售到期自动取消脚本成功执行');
  350. }catch(\Exception $e){
  351. Yii::error('预售到期自动取消脚本执行失败:' . $e->getMessage());
  352. }
  353. }
  354. }