LiveForm.php 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218
  1. <?php
  2. /**
  3. * 重庆赤晓店信息科技有限公司
  4. * https://www.chixiaodian.com
  5. * Copyright (c) 2023 赤店商城 All rights reserved.
  6. */
  7. namespace app\modules\admin\models\live;
  8. use app\models\ErpInventory;
  9. use app\models\ErpInventoryProp;
  10. use app\models\ErpInventoryPropLog;
  11. use app\models\ErpInventoryLog;
  12. use app\models\ErpPurchaseinOrder;
  13. use app\models\ErpPurchasein;
  14. use app\models\ErpPurchaseoutOrder;
  15. use app\models\ErpPurchaseout;
  16. use app\models\ErpSupplier;
  17. use app\models\Goods;
  18. use app\models\GoodsCat;
  19. use app\models\Cat;
  20. use app\models\Option;
  21. use app\constants\OptionSetting;
  22. use app\models\Order;
  23. use app\models\OrderDetail;
  24. use app\models\User;
  25. use app\models\SaasUser;
  26. use app\models\Warehouse;
  27. use app\models\WarehouseZone;
  28. use app\models\LiveConfig;
  29. use app\models\LiveAnchor;
  30. use app\models\LiveCat;
  31. use app\models\LiveRoom;
  32. use app\models\LiveComment;
  33. use app\models\LiveFollow;
  34. use app\models\LiveGoods;
  35. use app\models\LiveGoodsExt;
  36. use app\utils\Notice\NoticeSend;
  37. use app\modules\client\models\v1\ShareQrcodeForm;
  38. class LiveForm extends Model {
  39. public $store_id;
  40. public $id;
  41. public $ids;
  42. public $goods_name;
  43. public $supplier_name;
  44. public $goods_id;
  45. public $cat_id;
  46. public $supplier_id;
  47. public $warehouse_id;
  48. public $warehouse_zone_id;
  49. public $is_delete;
  50. public $warning_num;
  51. public $eiId;
  52. public $eipId;
  53. public $nickname;
  54. public $phone;
  55. public $order_no;
  56. public $type;
  57. public $purchase_in_price;
  58. public function init() {
  59. parent::init();
  60. if (empty($this->store_id)) {
  61. $this->store_id = get_store_id();
  62. }
  63. }
  64. public static function roomTimeoutClose($room_id, $addQueue = 1) {
  65. $room = LiveRoom::findOne($room_id);
  66. if($room->is_close == 1){
  67. return;
  68. }
  69. if($room->off_time == 0){
  70. return;
  71. }
  72. $store_id = $room->store_id;
  73. $self = new self(['store_id' => $store_id]);
  74. $timeout_off = $self->storeConf('timeout_off');
  75. if($timeout_off){
  76. if($addQueue){
  77. queue_push(new \app\jobs\live\LivePublishDoneJob([
  78. 'id' => $room_id,
  79. ]), $timeout_off * 60);
  80. }else{
  81. if(time() - $room->off_time >= $timeout_off * 60){
  82. $self->roomSaveKey($room_id, 'is_close', 1);
  83. }
  84. }
  85. }
  86. return [
  87. 'code' => 0,
  88. 'msg' => '操作成功。',
  89. ];
  90. }
  91. public static function roomNoticeBegin($user_id, $room_id, $addQueue = 1) {
  92. $room = LiveRoom::findOne($room_id);
  93. if($room->is_close == 1 || $room->is_delete == 1){
  94. return;
  95. }
  96. if($room->start_time <= 0){
  97. return;
  98. }
  99. $store_id = $room->store_id;
  100. $self = new self(['store_id' => $store_id]);
  101. $delay = $room->start_time - time() > 0 ? $room->start_time - time() : 0;
  102. if($addQueue){
  103. $queueId = queue_push(new \app\jobs\live\LiveBeginJob([
  104. 'room_id' => $room_id,
  105. 'user_id' => $user_id,
  106. ]), $delay);
  107. }else{
  108. $anchor = LiveAnchor::findOne($room->anchor_id);
  109. NoticeSend::LiveBegin($user_id, $anchor, $room);
  110. }
  111. return [
  112. 'code' => 0,
  113. 'msg' => '操作成功。',
  114. 'queueId' => $queueId,
  115. ];
  116. }
  117. public static function roomRecordAdd($room_id, $uri) {
  118. $room = LiveRoom::findOne($room_id);
  119. if ($room) {
  120. if (empty($room->record)) {
  121. $room->record = json_encode([$uri]);
  122. } else {
  123. $record = json_decode($room->record, true);
  124. $record[] = $uri;
  125. $room->record = json_encode($record);
  126. }
  127. $room->save();
  128. }
  129. return [
  130. 'code' => 0,
  131. 'msg' => '操作成功。',
  132. ];
  133. }
  134. public static function encodePath($anchor_id, $anchor_user_id, $room_id) {
  135. $path = '/' . $anchor_id . '_' . $anchor_user_id . '/' . $room_id . '_' . $anchor_id;
  136. return $path;
  137. }
  138. /**
  139. * @return array [room_id,anchor_id,anchor_user_id]
  140. */
  141. public static function decodePath($appname, $stream) {
  142. list($anchor_id, $anchor_user_id) = explode('_', $appname);
  143. list($room_id) = explode('_', $stream);
  144. return [
  145. 'room_id' => $room_id,
  146. 'anchor_id' => $anchor_id,
  147. 'anchor_user_id' => $anchor_user_id,
  148. ];
  149. }
  150. /**
  151. * 播流地址
  152. */
  153. public function getPlayStream($anchor_id, $anchor_user_id, $room_id) {
  154. $config = $this->storeConf();
  155. $filename = self::encodePath($anchor_id, $anchor_user_id, $room_id);
  156. $rtmp = $this->PrivateKeyA(time() + 600, 'rtmp://' . $config['domain_name'], $filename, $config['auth_main_key']);
  157. $m3u8 = $this->PrivateKeyA(time() + 600, 'http://' . $config['domain_name'], $filename . '.m3u8', $config['auth_main_key']);
  158. $flv = $this->PrivateKeyA(time() + 600, 'http://' . $config['domain_name'], $filename, $config['auth_main_key']) . '.flv';
  159. return ['rtmp' => $rtmp, 'm3u8' => $m3u8, 'flv' => $flv];
  160. }
  161. /**
  162. * 推流地址
  163. */
  164. public function getPushStream($anchor, $room) {
  165. $config = $this->storeConf();
  166. $filename = self::encodePath($anchor['id'], $anchor['user_id'], $room['id']);
  167. $pushURl = $this->PrivateKeyA(time() + 600, 'rtmp://' . $config['push_flow'], $filename, $config['auth_main_key'], $config['push_flow_ext']);
  168. return $pushURl;
  169. }
  170. public function PrivateKeyA($time, $domain, $filename, $key, $ext = '') {
  171. $sstring = $filename . "-" . $time . "-0-0-" . $key;
  172. $md5 = md5($sstring);
  173. $auth_key = "auth_key=" . $time . "-0-0-" . $md5;
  174. if ($ext) {
  175. $url = $domain . $filename . "?" . $ext . '&' . $auth_key;
  176. } else {
  177. $url = $domain . $filename . "?" . $auth_key;
  178. }
  179. return $url;
  180. }
  181. public function storeConf($key = null) {
  182. $def = new LiveConfig();
  183. $def->loadDefaultValues();
  184. $attributes = $def->attributes;
  185. unset($attributes['store_id']);
  186. unset($attributes['id']);
  187. $conf = LiveConfig::findOne(['store_id' => $this->store_id]);
  188. return $conf ? ($key ? $conf[$key] : $conf) : $attributes;
  189. }
  190. public function storeConfSave($params = []) {
  191. $conf = LiveConfig::findOne(['store_id' => $this->store_id]);
  192. if (!$conf) {
  193. $conf = new LiveConfig();
  194. }
  195. $conf->setAttributes($params, false);
  196. $conf->store_id = $this->store_id;
  197. if (!$conf->save()) {
  198. return [
  199. 'code' => 1,
  200. 'msg' => '操作失败!' . array_shift($conf->getFirstErrors()),
  201. ];
  202. }
  203. return [
  204. 'code' => 0,
  205. 'msg' => '操作成功。',
  206. 'data' => $conf,
  207. ];
  208. }
  209. public function anchorInfo($id) {
  210. $info = LiveAnchor::findOne($id);
  211. return [
  212. 'code' => 0,
  213. 'data' => $info,
  214. ];
  215. }
  216. public function anchorInfoByUserid($user_id) {
  217. $anchor = LiveAnchor::findOne(['user_id' => $user_id, 'is_delete' => 0, 'status' => 1]);
  218. if(!$anchor){
  219. return [
  220. 'code' => 1,
  221. 'msg' => '不是主播或状态异常',
  222. ];
  223. }
  224. return [
  225. 'code' => 0,
  226. 'data' => $anchor,
  227. ];
  228. }
  229. public function anchorHome($user_id) {
  230. try {
  231. $anchor = $this->anchorInfoByUserid($user_id);
  232. if ($anchor['code']) {
  233. throw new \Exception($anchor['msg']);
  234. }
  235. return [
  236. 'code' => 0,
  237. 'data' => $anchor['data'],
  238. ];
  239. } catch (\Exception $e) {
  240. \Yii::error($e);
  241. return [
  242. 'code' => 1,
  243. 'msg' => $e->getMessage()
  244. ];
  245. }
  246. }
  247. public function anchorList($params = []) {
  248. try {
  249. $query = LiveAnchor::find()->where(['is_delete' => 0, 'store_id' => $this->store_id]);
  250. if (!empty($params['id'])) {
  251. $query->andWhere(['id' => $params['id']]);
  252. }
  253. if (!empty($params['is_push'])) {
  254. $query->andWhere(['is_push' => $params['is_push']]);
  255. }
  256. if (!empty($params['is_open'])) {
  257. $query->andWhere(['is_open' => $params['is_open']]);
  258. }
  259. if (!empty($params['is_recommen'])) {
  260. $query->andWhere(['is_recommen' => $params['is_recommen']]);
  261. }
  262. if (!empty($params['name'])) {
  263. $query->andWhere(['like', 'name', trim($params['name'])]);
  264. }
  265. if (!empty($params['cat_id'])) {
  266. $query->andWhere(['cat_id' => $params['cat_id']]);
  267. }
  268. if ($params['realname']) {
  269. $query->andWhere(['like', 'realname', $params['realname']]);
  270. }
  271. if ($params['mobile']) {
  272. $query->andWhere(['like', 'mobile', $params['mobile']]);
  273. }
  274. if (!empty($params['start_time'])) {
  275. $query->andWhere(['>=', 'created_at', strtotime($params['start_time'])]);
  276. }
  277. if (!empty($params['end_time'])) {
  278. $query->andWhere(['<=', 'created_at', strtotime($params['end_time']) + 86400]);
  279. }
  280. if ($params['user_mobile']) {
  281. $query->andWhere(['user_id' => User::find()->select('id')->where(['like', 'binding', $params['user_mobile']])
  282. ]);
  283. }
  284. if ($params['user_name']) {
  285. $query->andWhere(['user_id' => User::find()->alias('u')
  286. ->leftJoin(['su' => SaasUser::tableName()], 'u.binding = su.mobile')
  287. ->select('u.id')
  288. ->where(['like', 'su.name', $params['user_name']])
  289. ]);
  290. }
  291. $query->addOrderBy('id DESC');
  292. $pagination = pagination_make($query);
  293. $cats = LiveCat::find()->where(['store_id' => $this->store_id])->indexBy('id')->all();
  294. foreach ($pagination['list'] as &$item) {
  295. $item['cat_name'] = $cats[$item['cat_id']] ? $cats[$item['cat_id']]['name'] : '-';
  296. $user = User::findOne($item['user_id']);
  297. $saasUser = SaasUser::findOne(['mobile' => $user['binding']]);
  298. // 做兼容 前端展示的saas_user的信息 将saas_user的头像昵称电话修改为user表的信息
  299. $saasUser['avatar'] = $user['avatar_url'];
  300. $saasUser['name'] = $user['nickname'];
  301. $saasUser['mobile'] = $user['binding'];
  302. $item['user'] = $user;
  303. $item['saasUser'] = $saasUser;
  304. }
  305. return [
  306. 'code' => 0,
  307. 'msg' => '操作成功',
  308. 'data' => $pagination,
  309. 'q' => $query->createCommand()->getRawSql(),
  310. ];
  311. } catch (\Exception $e) {
  312. \Yii::error($e);
  313. return [
  314. 'code' => 1,
  315. 'msg' => $e->getMessage()
  316. ];
  317. }
  318. }
  319. public function anchorSave($params) {
  320. $t = \Yii::$app->db->beginTransaction();
  321. try {
  322. $liveAnchor = $params['id'] ? LiveAnchor::findOne(['id' => $params['id'], 'store_id' => $this->store_id]) : new LiveAnchor();
  323. if (empty($liveAnchor)) {
  324. throw new \Exception('参数错误' . $params['id']);
  325. }
  326. if (isset($this->store_id)) {
  327. $liveAnchor->store_id = $this->store_id;
  328. }
  329. if (isset($params['user_id'])) {
  330. $user = User::findOne($params['user_id']);
  331. if (empty($user)) {
  332. throw new \Exception('用户id错误。' . $params['user_id']);
  333. }
  334. $liveAnchor->user_id = $params['user_id'];
  335. }
  336. if (isset($params['name'])) {
  337. $liveAnchor->name = $params['name'];
  338. }
  339. if (isset($params['desc'])) {
  340. $liveAnchor->desc = $params['desc'];
  341. }
  342. $liveAnchor->status = isset($params['status']) ? int($params['status']) : 1;
  343. if (isset($params['cat_id'])) {
  344. $liveAnchor->cat_id = $params['cat_id'];
  345. }
  346. if (isset($params['realname'])) {
  347. $liveAnchor->realname = $params['realname'];
  348. }
  349. if (isset($params['mobile'])) {
  350. $liveAnchor->mobile = trim($params['mobile']);
  351. }
  352. if (isset($params['status_desc'])) {
  353. $liveAnchor->status_desc = trim($params['status_desc']);
  354. }
  355. if (!$liveAnchor->save()) {
  356. \Yii::error([__METHOD__, $liveAnchor->attributes]);
  357. throw new \Exception('保存失败。' . array_shift($liveAnchor->getFirstErrors()));
  358. }
  359. $t->commit();
  360. return [
  361. 'code' => 0,
  362. 'msg' => '操作成功!',
  363. 'data' => $liveAnchor,
  364. ];
  365. } catch (\Exception $e) {
  366. \Yii::error($e);
  367. $t->rollBack();
  368. return [
  369. 'code' => 1,
  370. 'msg' => $e->getMessage()
  371. ];
  372. }
  373. }
  374. /**
  375. * 修改字段(包括删除is_delete)
  376. * @param type $id
  377. * @param type $key
  378. * @param type $val
  379. * @return type
  380. * @throws \Exception
  381. */
  382. public function anchorSaveKey($id, $key, $val) {
  383. try {
  384. if (!is_array($id)) {
  385. $id = explode(',', $id);
  386. }
  387. foreach ($id as $item) {
  388. $model = LiveAnchor::findOne($item);
  389. if (!$model) {
  390. throw new \Exception('参数错误' . $item);
  391. }
  392. $model->$key = $val;
  393. if (!$model->save()) {
  394. throw new \Exception('操作失败。' . $item . array_shift($model->getFirstErrors()));
  395. }
  396. }
  397. return [
  398. 'code' => 0,
  399. 'msg' => '操作成功',
  400. ];
  401. } catch (\Exception $e) {
  402. \Yii::error($e);
  403. return [
  404. 'code' => 1,
  405. 'msg' => $e->getMessage()
  406. ];
  407. }
  408. }
  409. public function catList($params = []) {
  410. try {
  411. $is_delete = 0;
  412. if ($params['is_delete'] == 1) {
  413. $is_delete = 1;
  414. }
  415. $query = LiveCat::find()->where(['is_delete' => $is_delete, 'store_id' => $this->store_id]);
  416. if (!empty($params['id'])) {
  417. $query->andWhere(['id' => $params['id']]);
  418. }
  419. if (!is_null($params['is_show']) && $params['is_show'] > -1) {
  420. $query->andWhere(['is_show' => $params['is_show']]);
  421. }
  422. if (!empty($params['name'])) {
  423. $query->andWhere(['like', 'name', trim($params['name'])]);
  424. }
  425. $query->orderBy('sort DESC, id DESC');
  426. $pagination = pagination_make($query);
  427. return [
  428. 'code' => 0,
  429. 'msg' => '操作成功',
  430. 'data' => $pagination,
  431. // 'q' => $query->createCommand()->getRawSql(),
  432. ];
  433. } catch (\Exception $e) {
  434. \Yii::error($e);
  435. return [
  436. 'code' => 1,
  437. 'msg' => $e->getMessage()
  438. ];
  439. }
  440. }
  441. public function catSelectList($indexBy = 1) {
  442. $query = LiveCat::find()->where(['store_id' => $this->store_id])
  443. ->select('id,name,is_show');
  444. if ($indexBy) {
  445. $query->indexBy('id');
  446. }
  447. $list = $query->orderBy('sort DESC, id DESC')->asArray()->all();
  448. foreach ($list as &$item) {
  449. if (!$item['is_show']) {
  450. $item['name'] .= '(已隐藏)';
  451. }
  452. }
  453. return [
  454. 'code' => 0,
  455. 'msg' => '操作成功',
  456. 'data' => $list,
  457. ];
  458. }
  459. public function catSave($params = []) {
  460. try {
  461. $model = $params['id'] ? LiveCat::findOne(['id' => $params['id'], 'store_id' => $this->store_id]) : new LiveCat();
  462. if (empty($model)) {
  463. throw new \Exception('参数错误' . $params['id']);
  464. }
  465. $model->name = $params['name'];
  466. $model->sort = $params['sort'] ?: 0;
  467. $model->is_show = $params['is_show'];
  468. $model->store_id = $this->store_id;
  469. isset($params['pic_url']) && $model->pic_url = $params['pic_url'];
  470. if (!$model->save()) {
  471. \Yii::error([__METHOD__, $model->attributes]);
  472. throw new \Exception('保存失败。' . array_shift($model->getFirstErrors()));
  473. }
  474. return [
  475. 'code' => 0,
  476. 'msg' => '操作成功!'
  477. ];
  478. } catch (\Exception $e) {
  479. \Yii::error($e);
  480. return [
  481. 'code' => 1,
  482. 'msg' => $e->getMessage()
  483. ];
  484. }
  485. }
  486. /**
  487. * 修改字段(包括删除is_delete)
  488. * @param type $id
  489. * @param type $key
  490. * @param type $val
  491. * @return type
  492. * @throws \Exception
  493. */
  494. public function catSaveKey($id, $key, $val) {
  495. try {
  496. if (!is_array($id)) {
  497. $id = explode(',', $id);
  498. }
  499. foreach ($id as $item) {
  500. $model = LiveCat::findOne($item);
  501. if (!$model) {
  502. throw new \Exception('参数错误' . $item);
  503. }
  504. $model->$key = $val;
  505. if (!$model->save()) {
  506. throw new \Exception('操作失败。' . $item . array_shift($model->getFirstErrors()));
  507. }
  508. }
  509. return [
  510. 'code' => 0,
  511. 'msg' => '操作成功',
  512. ];
  513. } catch (\Exception $e) {
  514. \Yii::error($e);
  515. return [
  516. 'code' => 1,
  517. 'msg' => $e->getMessage()
  518. ];
  519. }
  520. }
  521. public function goodsExtList($params = []) {
  522. try {
  523. $params['liveGoodsExt'] = 1;
  524. $params['mch'] = -1;
  525. return Goods::getList($params);
  526. } catch (\Exception $e) {
  527. \Yii::error($e);
  528. return [
  529. 'code' => 1,
  530. 'msg' => $e->getMessage()
  531. ];
  532. }
  533. }
  534. public function goodsExtSave($params = []) {
  535. try {
  536. if (!isset($params['id'])) {
  537. if (!empty($params['goods_id'])) {
  538. $goods_id = explode(',', $params['goods_id']);
  539. foreach ($goods_id as $goods_id_item) {
  540. $liveGoodsExtItem = LiveGoodsExt::findOne(['goods_id' => $goods_id_item, 'store_id' => $this->store_id, 'is_delete' => 0]) ?: new LiveGoodsExt();
  541. $liveGoodsExtItem->store_id = $this->store_id;
  542. $liveGoodsExtItem->goods_id = $goods_id_item;
  543. if (!$liveGoodsExtItem->save()) {
  544. throw new \Exception('保存失败。' . array_shift($liveGoodsExtItem->getFirstErrors()));
  545. }
  546. }
  547. }
  548. } else {
  549. $model = LiveGoodsExt::findOne(['id' => $params['id'], 'store_id' => $this->store_id]);
  550. if (empty($model)) {
  551. throw new \Exception('参数错误' . $params['id']);
  552. }
  553. $model->store_id = $this->store_id;
  554. $model->goods_id = $params['goods_id'];
  555. if (!$model->save()) {
  556. \Yii::error([__METHOD__, $model->attributes]);
  557. throw new \Exception('保存失败。' . array_shift($model->getFirstErrors()));
  558. }
  559. }
  560. return [
  561. 'code' => 0,
  562. 'msg' => '操作成功!'
  563. ];
  564. } catch (\Exception $e) {
  565. \Yii::error($e);
  566. return [
  567. 'code' => 1,
  568. 'msg' => $e->getMessage()
  569. ];
  570. }
  571. }
  572. /**
  573. * 修改字段(包括删除is_delete)
  574. * @param type $id
  575. * @param type $key
  576. * @param type $val
  577. * @return type
  578. * @throws \Exception
  579. */
  580. public function goodsExtSaveKey($id, $key, $val) {
  581. try {
  582. if (!is_array($id)) {
  583. $id = explode(',', $id);
  584. }
  585. foreach ($id as $item) {
  586. $model = LiveGoodsExt::findOne($item);
  587. if (!$model) {
  588. throw new \Exception('参数错误' . $item);
  589. }
  590. $model->$key = $val;
  591. if (!$model->save()) {
  592. throw new \Exception('操作失败。' . $item . array_shift($model->getFirstErrors()));
  593. }
  594. }
  595. return [
  596. 'code' => 0,
  597. 'msg' => '操作成功',
  598. ];
  599. } catch (\Exception $e) {
  600. \Yii::error($e);
  601. return [
  602. 'code' => 1,
  603. 'msg' => $e->getMessage()
  604. ];
  605. }
  606. }
  607. public function roomList($params = []) {
  608. try {
  609. $is_delete = 0;
  610. if ($params['is_delete'] == 1) {
  611. $is_delete = 1;
  612. }
  613. $query = LiveRoom::find()->where(['is_delete' => $is_delete, 'store_id' => $this->store_id]);
  614. if ($params['id']) {
  615. $query->andWhere(['id' => $params['id']]);
  616. }
  617. if (isset($params['has_record']) && $params['has_record'] > -1) {
  618. if($params['has_record']){
  619. $query->andWhere(['!=', 'record', '']);
  620. }else{
  621. $query->andWhere(['or', ['record' => null], ['record' => '']]);
  622. }
  623. }
  624. if (!empty($params['name'])) {
  625. $query->andWhere(['like', 'name', trim($params['name'])]);
  626. }
  627. if (isset($params['is_close']) && $params['is_close'] > -1) {
  628. $query->andWhere(['is_close' => $params['is_close']]);
  629. }
  630. if (!empty($params['pre_play_time'])) {
  631. $query->andWhere(['>', 'pre_play_time', 0]);
  632. }
  633. if ($params['anchor_is_recommen']) {
  634. $query->andWhere(['anchor_id' => LiveAnchor::find()->select('id')->where(['is_recommen' => 1])]);
  635. }
  636. if ($params['anchor_is_follow'] && $params['user_id']) {
  637. $query->andWhere(['anchor_id' => LiveFollow::find()->select('anchor_id')->where(['user_id' => $params['user_id']])]);
  638. }
  639. if ($params['anchor_id']) {
  640. $query->andWhere(['anchor_id' => $params['anchor_id']]);
  641. }
  642. if ($params['anchor_name']) {
  643. $query->andWhere(['anchor_id' => LiveAnchor::find()->select('id')->where(['like', 'name', $params['anchor_name']])]);
  644. }
  645. if ($params['anchor_realname']) {
  646. $query->andWhere(['anchor_id' => LiveAnchor::find()->select('id')->where(['like', 'realname', $params['anchor_realname']])]);
  647. }
  648. if ($params['anchor_mobile']) {
  649. $query->andWhere(['anchor_id' => LiveAnchor::find()->select('id')->where(['like', 'mobile', $params['anchor_mobile']])]);
  650. }
  651. if ($params['user_mobile']) {
  652. $query->andWhere(['anchor_id' => LiveAnchor::find()->alias('a')
  653. ->leftJoin(['u' => User::tableName()], 'a.user_id = u.id')
  654. ->select('a.id')
  655. ->where(['like', 'u.binding', $params['user_mobile']])
  656. ]);
  657. }
  658. if ($params['user_name']) {
  659. $query->andWhere(['anchor_id' => LiveAnchor::find()->alias('a')
  660. ->leftJoin(['u' => User::tableName()], 'a.user_id = u.id')
  661. ->leftJoin(['su' => SaasUser::tableName()], 'u.binding = su.mobile')
  662. ->select('a.id')
  663. ->where(['like', 'su.name', $params['user_name']])
  664. ]);
  665. }
  666. if ($params['cat_id']) {
  667. $query->andWhere(['anchor_id' => LiveAnchor::find()->select('id')->where(['cat_id' => $params['cat_id']])]);
  668. }
  669. $query->orderBy('id DESC');
  670. $pagination = pagination_make($query);
  671. $cats = $this->catSelectList()['data'];
  672. $conf = $this->storeConf();
  673. foreach ($pagination['list'] as &$item) {
  674. $item['count_comment'] = (int)LiveComment::find()->where(['room_id' => $item['id'], 'type' => 0])->count();
  675. $item['anchor'] = LiveAnchor::findOne($item['anchor_id']);
  676. $item['user'] = User::findOne($item['anchor']['user_id']);
  677. $item['saasUser'] = SaasUser::findOne(['mobile' => $item['user']['binding']]);
  678. $item['cat_name'] = $cats[$item['anchor']['cat_id']]['name'];
  679. $item['push_url'] = $this->getPushStream($item['anchor'], $item);
  680. $item['play_url'] = $this->getPlayStream($item['anchor']['id'], $item['anchor']['user_id'], $item['id']);
  681. $item['record_url'] = [];
  682. if($item['record']){
  683. $record = json_decode($item['record'], true);
  684. foreach($record as $_rec){
  685. $item['record_url'][] = (stripos($conf['oss_endpoint'], 'http') === false ? 'https://' : '') . implode('/', [$conf['oss_endpoint'], $_rec]);
  686. }
  687. }
  688. }
  689. $template_id = \app\utils\Notice\NoticeAction::getSendTamplateId(['live_begin'], is_h5() ? 'wxaapi' : 'miapp');
  690. return [
  691. 'code' => 0,
  692. 'msg' => '操作成功',
  693. 'data' => $pagination,
  694. 'template_id' => $template_id,
  695. 'q' => $query->createCommand()->getRawSql(),
  696. ];
  697. } catch (\Exception $e) {
  698. \Yii::error($e);
  699. return [
  700. 'code' => 1,
  701. 'msg' => $e->getMessage()
  702. ];
  703. }
  704. }
  705. //主播进入直播间
  706. public function roomAnchorEnter($user_id = 0, $anchor_id = 0, $params = []) {
  707. try {
  708. $anchor = LiveAnchor::findOne($anchor_id);
  709. if($anchor['user_id'] != $user_id){
  710. \Yii::error([__METHOD__, $user_id, $anchor_id]);
  711. throw new \Exception('参数错误。');
  712. }
  713. if(!$anchor['is_open']){
  714. \Yii::error([__METHOD__, $user_id, $anchor_id]);
  715. throw new \Exception('未开通直播。');
  716. }
  717. $room = LiveRoom::find()->where(['anchor_id' => $anchor_id, 'is_close' => 0, 'store_id' => $this->store_id])->orderBy('id DESC')->limit(1)->one();
  718. if (empty($room) && $params['create_new_live']) {
  719. $room = new LiveRoom();
  720. $room->anchor_id = $anchor_id;
  721. $room->store_id = $this->store_id;
  722. $room->start_time = time();
  723. $room->end_time = 0;
  724. isset($params['start_time']) && $room->pre_play_time = time();
  725. isset($params['start_time']) && $room->start_time = $params['start_time'];
  726. isset($params['end_time']) && $room->end_time = $params['end_time'];
  727. isset($params['open_comment']) && $room->open_comment = $params['open_comment'];
  728. isset($params['open_good']) && $room->open_good = $params['open_good'];
  729. isset($params['open_share']) && $room->open_share = $params['open_share'];
  730. $room->name = $params['name'] ?? $anchor->name;
  731. $room->pic_url = $params['pic_url'] ?: \Yii::$app->request->hostInfo . \Yii::$app->request->baseUrl . '/web/v1/statics/clientImg/live/live_bg.png';
  732. isset($params['desc']) && $room->desc = $params['desc'];
  733. isset($params['address']) && $room->address = $params['address'];
  734. if($room->start_time && $room->pre_play_time && $room->start_time - $room->pre_play_time > 86400 * 7){
  735. \Yii::error([__METHOD__, $room->attributes]);
  736. throw new \Exception('开播时间距离预播时间超过7天。');
  737. }
  738. if (!$room->save()) {
  739. \Yii::error([__METHOD__, $room->attributes]);
  740. throw new \Exception('保存失败。' . array_shift($room->getFirstErrors()));
  741. }
  742. // $room->push_url = $this->getPushStream($anchor, $room);
  743. // $room->save();
  744. }
  745. return [
  746. 'code' => 0,
  747. 'msg' => '操作成功!',
  748. 'data' => $room,
  749. 'push_url' => $this->getPushStream($anchor, $room),
  750. 'anchor' => $anchor,
  751. ];
  752. } catch (\Exception $e) {
  753. \Yii::error($e);
  754. return [
  755. 'code' => 1,
  756. 'msg' => $e->getMessage()
  757. ];
  758. }
  759. }
  760. public function roomAnchorClose($room_id) {
  761. // return [
  762. // 'code' => 0,
  763. // 'msg' => '操作成功!',
  764. // ];
  765. return $this->roomSaveKey($room_id, 'is_close', 1);
  766. }
  767. //直播间添加商品
  768. public function roomGoodsAdd($room_id, $goods_id = []) {
  769. if (!is_array($goods_id)) {
  770. $goods_id = explode(',', $goods_id);
  771. }
  772. $goods_id = trim(implode(',', $goods_id), ',');
  773. $model = LiveRoom::findOne($room_id);
  774. $model->goods_id = $goods_id;
  775. if (!$model->save()) {
  776. return [
  777. 'code' => 1,
  778. 'msg' => '操作失败!' . array_shift($model->getFirstErrors()),
  779. ];
  780. }
  781. return [
  782. 'code' => 0,
  783. 'msg' => '操作成功',
  784. ];
  785. }
  786. public function roomInfo($room_id = 0) {
  787. $room = LiveRoom::findOne($room_id);
  788. $anchor = LiveAnchor::findOne($room->anchor_id);
  789. $anchor_user = User::findOne($anchor['user_id']);
  790. $anchor_saasUser = SaasUser::findOne(['mobile' => $anchor_user['binding']]);
  791. $newRoom = null;
  792. if($room->is_close){
  793. $newRoom = LiveRoom::find()->where(['anchor_id' => $anchor->id, 'is_close' => 0])->orderBy('id DESC')->limit(1)->one();
  794. if($newRoom && $newRoom->id == $room->id){
  795. unset($newRoom);
  796. }
  797. }
  798. return [
  799. 'code' => 0,
  800. 'msg' => '操作成功',
  801. 'data' => $room,
  802. 'newRoom' => $newRoom,
  803. 'anchor' => $anchor,
  804. 'anchor_user' => $anchor_user,
  805. 'anchor_saasUser' => $anchor_saasUser,
  806. 'playStream' => $this->getPlayStream($anchor->id, $anchor->user_id, $room['id']),
  807. ];
  808. }
  809. //用户进入直播间
  810. public function roomUserEnter($room_id = 0, $user_id = 0) {
  811. $key = ['roomUserEnter', $room_id, $user_id];
  812. if (!cache_lock($key, 3600)) {
  813. LiveRoom::updateAllCounters(['look_num' => 1], ['id' => $room_id]);
  814. }
  815. $user = User::findOne($user_id);
  816. $saasUser = SaasUser::findOne(['mobile' => $user['binding']]);
  817. $content = $saasUser['name'] . ' 进来了';
  818. $this->commentAdd($room_id, $user_id, $content, LiveComment::TYPE_INFO);
  819. $template_id = \app\utils\Notice\NoticeAction::getSendTamplateId(['live_begin'], is_h5() ? 'wxaapi' : 'miapp');
  820. $roomUserSubscribe = cache()->get(implode('_', ['roomUserSubscribe', $room_id, $user_id])) ? 1 : 0;
  821. return [
  822. 'code' => 0,
  823. 'msg' => '操作成功',
  824. 'template_id' => $template_id,
  825. 'roomUserSubscribe' => $roomUserSubscribe,
  826. ];
  827. }
  828. //点赞
  829. public function roomUserGood($room_id, $user_id = 0) {
  830. $key = ['roomUserGood', $room_id, $user_id];
  831. if (!cache_lock($key, 60)) {
  832. LiveRoom::updateAllCounters(['good_num' => 1], ['id' => $room_id]);
  833. $user = User::findOne($user_id);
  834. $saasUser = SaasUser::findOne(['mobile' => $user['binding']]);
  835. $content = $saasUser['name'] . ' 点赞了';
  836. $this->commentAdd($room_id, $user_id, $content, LiveComment::TYPE_INFO);
  837. return [
  838. 'code' => 0,
  839. 'msg' => '操作成功!',
  840. ];
  841. }
  842. return [
  843. 'code' => 1,
  844. 'msg' => '限制每分钟点赞一次!',
  845. ];
  846. }
  847. //订阅
  848. public function roomUserSubscribe($room_id, $user_id = 0) {
  849. $room = LiveRoom::findOne($room_id);
  850. $time = $room->start_time - time() > 0 ? $room->start_time - time() : 0;
  851. if (!cache_lock(implode('_', ['roomUserSubscribe', $room_id, $user_id]), $time + 3600) || !$time) {
  852. $sub = self::roomNoticeBegin($user_id, $room_id);
  853. \Yii::error([__METHOD__, $sub, $room_id, $user_id]);
  854. }
  855. return [
  856. 'code' => 0,
  857. 'msg' => '操作成功!',
  858. ];
  859. }
  860. /**
  861. * 修改字段(包括删除is_delete)
  862. * @param type $id
  863. * @param type $key
  864. * @param type $val
  865. * @return type
  866. * @throws \Exception
  867. */
  868. public function roomSaveKey($id, $key, $val) {
  869. try {
  870. if (!is_array($id)) {
  871. $id = explode(',', $id);
  872. }
  873. foreach ($id as $item) {
  874. $model = LiveRoom::findOne($item);
  875. if (!$model) {
  876. throw new \Exception('参数错误' . $item);
  877. }
  878. $model->$key = $val;
  879. if (!$model->save()) {
  880. throw new \Exception('操作失败。' . $item . array_shift($model->getFirstErrors()));
  881. }
  882. }
  883. return [
  884. 'code' => 0,
  885. 'msg' => '操作成功',
  886. ];
  887. } catch (\Exception $e) {
  888. \Yii::error($e);
  889. return [
  890. 'code' => 1,
  891. 'msg' => $e->getMessage()
  892. ];
  893. }
  894. }
  895. public function roomDel($id) {
  896. try {
  897. $model = LiveRoom::findOne($id);
  898. if (!$model) {
  899. throw new \Exception('参数错误' . $id);
  900. }
  901. $model->is_close = 1;
  902. $model->is_delete = 1;
  903. if (!$model->save()) {
  904. throw new \Exception('操作失败。' . $id . array_shift($model->getFirstErrors()));
  905. }
  906. return [
  907. 'code' => 0,
  908. 'msg' => '操作成功',
  909. ];
  910. } catch (\Exception $e) {
  911. \Yii::error($e);
  912. return [
  913. 'code' => 1,
  914. 'msg' => $e->getMessage()
  915. ];
  916. }
  917. }
  918. //心跳请求
  919. public function roomUserHeartBeat($room_id = 0, $user = null, $comment_last_id = 0) {
  920. $roomInfo = $this->roomInfo($room_id);
  921. $follow = LiveFollow::findOne(['anchor_id' => $roomInfo['data']['anchor_id'], 'user_id' => $user['id']]);
  922. $roomInfo['is_follow'] = $follow ? 1 : 0;
  923. $query = LiveComment::find()->alias('lc')
  924. ->leftJoin(['u' => User::tableName()], 'u.id = lc.user_id')
  925. ->leftJoin(['su' => SaasUser::tableName()], 'su.mobile = u.binding')
  926. ->where([
  927. 'lc.store_id' => $this->store_id,
  928. 'lc.room_id' => $room_id,
  929. ]);
  930. if ($comment_last_id > 0) {
  931. $query->andWhere(['>', 'lc.id', $comment_last_id]);
  932. }
  933. $query->select('su.name, su.avatar, lc.*');
  934. $query->limit(50);
  935. $query->orderBy('lc.id DESC');
  936. $listComment = $query->asArray()->all();
  937. $roomInfo['comment'] = $listComment;
  938. $roomInfo['tips'] = null;
  939. if($comment_last_id == 0){
  940. $tips = $this->storeConf('tips');
  941. if($tips){
  942. $roomInfo['tips'] = $tips;
  943. }
  944. }
  945. return $roomInfo;
  946. }
  947. public function roomQrcode($room_id = 0, $user_id = 0) {
  948. $user = User::findOne($user_id);
  949. $saasUser = SaasUser::findOne(['mobile' => $user['binding']]);
  950. $content = $saasUser['name'] . ' 分享了直播间';
  951. $this->commentAdd($room_id, $user_id, $content, LiveComment::TYPE_INFO);
  952. $form = new ShareQrcodeForm();
  953. $form->id = $room_id;
  954. $form->store_id = $this->store_id;
  955. $form->type = \app\models\Qrcode::TYPE_LIVE;
  956. $form->user = $user;
  957. $form->user_id = $user['id'];
  958. return $form->search();
  959. }
  960. public function commentList($params = []) {
  961. try {
  962. $query = LiveComment::find()->where(['store_id' => $this->store_id]);
  963. if (isset($params['type']) && $params['type'] > -1) {
  964. $query->andWhere(['type' => $params['type']]);
  965. }
  966. if ($params['room_id']) {
  967. $query->andWhere(['room_id' => $params['room_id']]);
  968. }
  969. if ($params['room_name']) {
  970. $query->andWhere(['room_id' => LiveRoom::find()->select('id')->where(['like', 'name', $params['room_name']])]);
  971. }
  972. if ($params['anchor_id']) {
  973. $query->andWhere(['anchor_id' => $params['anchor_id']]);
  974. }
  975. if ($params['anchor_name']) {
  976. $query->andWhere(['anchor_id' => LiveAnchor::find()->select('id')->where(['like', 'name', $params['anchor_name']])]);
  977. }
  978. if (!empty($params['content'])) {
  979. $query->andWhere(['like', 'content', trim($params['content'])]);
  980. }
  981. $query->orderBy('id DESC');
  982. $pagination = pagination_make($query);
  983. return [
  984. 'code' => 0,
  985. 'msg' => '操作成功',
  986. 'data' => $pagination,
  987. // 'q' => $query->createCommand()->getRawSql(),
  988. ];
  989. } catch (\Exception $e) {
  990. \Yii::error($e);
  991. return [
  992. 'code' => 1,
  993. 'msg' => $e->getMessage()
  994. ];
  995. }
  996. }
  997. public function commentAdd($room_id, $user_id = 0, $content = '', $type = LiveComment::TYPE_DEFAULT) {
  998. $room = LiveRoom::findOne($room_id);
  999. $model = new LiveComment();
  1000. $model->store_id = $room->store_id;
  1001. $model->content = $content;
  1002. $model->room_id = $room_id;
  1003. $model->user_id = $user_id;
  1004. $model->type = $type;
  1005. if (!$model->save()) {
  1006. return [
  1007. 'code' => 1,
  1008. 'msg' => '操作失败!' . array_shift($model->getFirstErrors()),
  1009. ];
  1010. }
  1011. return [
  1012. 'code' => 0,
  1013. 'msg' => '操作成功',
  1014. ];
  1015. }
  1016. public function commentDel($id) {
  1017. LiveComment::deleteAll(['id' => $id]);
  1018. return [
  1019. 'code' => 0,
  1020. 'msg' => '操作成功',
  1021. ];
  1022. }
  1023. public function followAdd($anchor_id, $user_id = 0) {
  1024. $follow = LiveFollow::findOne(['anchor_id' => $anchor_id, 'user_id' => $user_id]);
  1025. if (!$follow) {
  1026. $model = new LiveFollow();
  1027. $model->store_id = $this->store_id;
  1028. $model->anchor_id = $anchor_id;
  1029. $model->user_id = $user_id;
  1030. if (!$model->save()) {
  1031. return [
  1032. 'code' => 1,
  1033. 'msg' => '操作失败!' . array_shift($model->getFirstErrors()),
  1034. ];
  1035. }
  1036. LiveAnchor::updateAllCounters(['follow_num' => 1], ['id' => $anchor_id]);
  1037. }
  1038. return [
  1039. 'code' => 0,
  1040. 'msg' => '操作成功',
  1041. ];
  1042. }
  1043. public function followDel($anchor_id, $user_id = 0) {
  1044. $up = LiveFollow::deleteAll(['anchor_id' => $anchor_id, 'user_id' => $user_id]);
  1045. if ($up) {
  1046. try {
  1047. LiveAnchor::updateAllCounters(['follow_num' => -1], ['id' => $anchor_id]);
  1048. } catch (\Exception $e) {
  1049. }
  1050. }
  1051. return [
  1052. 'code' => 0,
  1053. 'msg' => '操作成功',
  1054. ];
  1055. }
  1056. public function goodsList($params = []) {
  1057. try {
  1058. $is_delete = 0;
  1059. if ($params['is_delete'] == 1) {
  1060. $is_delete = 1;
  1061. }
  1062. $query = LiveGoods::find()->where(['is_delete' => $is_delete, 'store_id' => $this->store_id]);
  1063. if (isset($params['status']) && $params['status'] > -1) {
  1064. $query->andWhere(['status' => $params['status']]);
  1065. }
  1066. if ($params['anchor_id']) {
  1067. $query->andWhere(['anchor_id' => $params['anchor_id']]);
  1068. }
  1069. if ($params['anchor_name']) {
  1070. $query->andWhere(['anchor_id' => LiveAnchor::find()->select('id')->where(['like', 'name', $params['anchor_name']])]);
  1071. }
  1072. if ($params['goods_id']) {
  1073. $query->andWhere(['goods_id' => $params['goods_id']]);
  1074. }
  1075. if ($params['goods_name']) {
  1076. $query->andWhere(['goods_id' => Goods::find()->select('id')->where(['like', 'name', $params['goods_name']])]);
  1077. }
  1078. $query->orderBy('id DESC');
  1079. $pagination = pagination_make($query);
  1080. $goods = [];
  1081. $anchor = [];
  1082. foreach ($pagination['list'] as &$item) {
  1083. $item['goods'] = $goods[$item['goods_id']] = $goods[$item['goods_id']] ?? Goods::findOne($item['goods_id']);
  1084. $item['anchor'] = $anchor[$item['anchor_id']] = $anchor[$item['anchor_id']] ?? LiveAnchor::findOne($item['anchor_id']);
  1085. }
  1086. return [
  1087. 'code' => 0,
  1088. 'msg' => '操作成功',
  1089. 'data' => $pagination,
  1090. // 'q' => $query->createCommand()->getRawSql(),
  1091. ];
  1092. } catch (\Exception $e) {
  1093. \Yii::error($e);
  1094. return [
  1095. 'code' => 1,
  1096. 'msg' => $e->getMessage()
  1097. ];
  1098. }
  1099. }
  1100. public function goodsAdd($anchor_id, $goods_id = []) {
  1101. if (!is_array($goods_id)) {
  1102. $goods_id = explode(',', $goods_id);
  1103. }
  1104. foreach ($goods_id as $item) {
  1105. if ($item <= 0) {
  1106. continue;
  1107. }
  1108. $model = LiveGoods::findOne(['anchor_id' => $anchor_id, 'goods_id' => $item]);
  1109. if (!$model) {
  1110. $model = new LiveGoods();
  1111. $model->store_id = $this->store_id;
  1112. $model->anchor_id = $anchor_id;
  1113. $model->goods_id = $item;
  1114. }
  1115. $model->status = 1;
  1116. $model->is_delete = 0;
  1117. if (!$model->save()) {
  1118. return [
  1119. 'code' => 1,
  1120. 'msg' => '操作失败!' . array_shift($model->getFirstErrors()),
  1121. ];
  1122. }
  1123. }
  1124. return [
  1125. 'code' => 0,
  1126. 'msg' => '操作成功',
  1127. ];
  1128. }
  1129. /**
  1130. * 修改字段(包括删除is_delete)
  1131. * @param type $id
  1132. * @param type $key
  1133. * @param type $val
  1134. * @return type
  1135. * @throws \Exception
  1136. */
  1137. public function goodsSaveKey($id, $key, $val) {
  1138. try {
  1139. if (!is_array($id)) {
  1140. $id = explode(',', $id);
  1141. }
  1142. foreach ($id as $item) {
  1143. $model = LiveGoods::findOne($item);
  1144. if (!$model) {
  1145. throw new \Exception('参数错误' . $item);
  1146. }
  1147. $model->$key = $val;
  1148. if (!$model->save()) {
  1149. throw new \Exception('操作失败。' . $item . array_shift($model->getFirstErrors()));
  1150. }
  1151. }
  1152. return [
  1153. 'code' => 0,
  1154. 'msg' => '操作成功',
  1155. ];
  1156. } catch (\Exception $e) {
  1157. \Yii::error($e);
  1158. return [
  1159. 'code' => 1,
  1160. 'msg' => $e->getMessage()
  1161. ];
  1162. }
  1163. }
  1164. }