| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867 |
- <?php
- // +----------------------------------------------------------------------
- // | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
- // +----------------------------------------------------------------------
- // | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
- // +----------------------------------------------------------------------
- // | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
- // +----------------------------------------------------------------------
- // | Author: CRMEB Team <admin@crmeb.com>
- // +----------------------------------------------------------------------
- namespace app\common\repositories\store\broadcast;
- use app\common\dao\store\broadcast\BroadcastRoomDao;
- use app\common\model\store\broadcast\BroadcastRoom;
- use app\common\repositories\BaseRepository;
- use crmeb\jobs\SendSmsJob;
- use crmeb\services\DownloadImageService;
- use crmeb\services\MiniProgramService;
- use crmeb\services\SwooleTaskService;
- use EasyWeChat\Core\Exceptions\HttpException;
- use Exception;
- use FormBuilder\Exception\FormBuilderException;
- use FormBuilder\Factory\Elm;
- use FormBuilder\Form;
- use think\db\exception\DataNotFoundException;
- use think\db\exception\DbException;
- use think\db\exception\ModelNotFoundException;
- use think\exception\ValidateException;
- use think\facade\Db;
- use think\facade\Queue;
- use think\facade\Route;
- /**
- * 直播间
- */
- class BroadcastRoomRepository extends BaseRepository
- {
- /**
- * @var BroadcastRoomDao
- */
- protected $dao;
- /**
- * BroadcastRoomRepository constructor.
- * @param BroadcastRoomDao $dao
- */
- public function __construct(BroadcastRoomDao $dao)
- {
- $this->dao = $dao;
- }
- /**
- * 根据条件获取指定商户的数据列表
- *
- * 本函数用于查询数据库中与指定商户相关的数据列表。它支持分页查询,并返回符合条件的数据总数和具体数据列表。
- * 主要用于后台管理或其他需要查询特定商户数据的场景。
- *
- * @param int $merId 商户ID,用于指定查询的商户
- * @param array $where 查询条件数组,用于进一步筛选数据
- * @param int $page 当前页码,用于分页查询
- * @param int $limit 每页数据条数,用于分页查询
- * @return array 返回包含数据总数(count)和数据列表(list)的数组
- */
- public function getList($merId, array $where, $page, $limit)
- {
- // 将指定的商户ID添加到查询条件中
- $where['mer_id'] = $merId;
- // 构建查询语句,根据创建时间降序排序
- $query = $this->dao->search($where)->order('create_time DESC');
- // 计算符合条件的数据总数
- $count = $query->count();
- // 执行分页查询,并获取具体的数据列表
- $list = $query->page($page, $limit)->select();
- // 将数据总数和数据列表打包成数组返回
- return compact('count', 'list');
- }
- /**
- * 获取用户列表
- *
- * 根据给定的条件和分页信息,查询用户列表,包括满足条件的用户数量和用户详细信息。
- * 特别地,关注了直播状态为非关闭的状态,以及对直播时间进行了格式化处理。
- *
- * @param array $where 查询条件,用于筛选用户。
- * @param int $page 当前页码,用于分页查询。
- * @param int $limit 每页显示的用户数量,用于分页查询。
- * @return array 返回包含用户数量和用户列表的数组。
- */
- public function userList(array $where, $page, $limit)
- {
- // 设置显示标签为1的条件,表示只查询显示中的用户
- $where['show_tag'] = 1;
- // 构建查询用户的信息,包括用户自身信息和关联的广播信息
- $query = $this->dao->search($where)->with([
- 'broadcast' => function ($query) {
- // 筛选正在销售的广播,并包含关联的商品信息
- $query->where('on_sale', 1);
- $query->with('goods');
- }
- ])
- // 筛选出房间ID大于0,且直播状态不为107(表示直播关闭)的用户
- ->where('room_id', '>', 0)
- ->whereNotIn('live_status', [107])
- // 按照星级、排序和创建时间倒序排列
- ->order('star DESC, sort DESC, create_time DESC');
- // 计算满足条件的用户总数
- $count = $query->count();
- // 分页查询用户信息
- $list = $query->page($page, $limit)->select();
- // 对查询结果中的每个用户,格式化其直播开始时间
- foreach ($list as $item) {
- $item->show_time = date('m/d H:i', strtotime($item->start_time));
- }
- // 返回用户总数和用户列表
- return compact('count', 'list');
- }
- /**
- * 获取管理员列表
- *
- * 根据给定的条件查询管理员信息,并包含关联的商户信息。支持分页查询。
- *
- * @param array $where 查询条件,用于筛选管理员。
- * @param int $page 当前页码,用于分页查询。
- * @param int $limit 每页显示的数量,用于分页查询。
- * @return array 返回包含管理员数量和管理员列表的数组。
- */
- public function adminList(array $where, $page, $limit)
- {
- // 构建查询语句,根据给定条件搜索管理员,并包含关联的商户信息,按特定字段排序
- $query = $this->dao->search($where)
- ->with(['merchant' => function ($query) {
- // 关联查询商户信息,只包含mer_name, mer_id, is_trader字段
- $query->field('mer_name,mer_id,is_trader');
- }])
- ->order('BroadcastRoom.star DESC, BroadcastRoom.sort DESC, BroadcastRoom.create_time DESC');
- // 计算满足条件的管理员总数
- $count = $query->count();
- // 分页查询管理员列表
- $list = $query->page($page, $limit)->select();
- // 返回管理员总数和管理员列表
- return compact('count', 'list');
- }
- /**
- * 创建直播间
- * @return Form
- * @throws FormBuilderException
- * @author xaboy
- * @day 2020/7/29
- */
- public function createForm()
- {
- return Elm::createForm(Route::buildUrl('merchantBroadcastRoomCreate')->build(), [
- Elm::input('name', '直播间名字:')->placeholder('请输入直播间名字')->required(),
- Elm::frameImage('cover_img', '背景图:', '/' . config('admin.merchant_prefix') . '/setting/uploadPicture?field=cover_img&type=1')
- ->info('建议像素1080*1920,大小不超过2M')->icon('el-icon-camera')->modal(['modal' => false])->width('1000px')->height('600px')->props(['footer' => false])->required(),
- Elm::frameImage('share_img', '分享图:', '/' . config('admin.merchant_prefix') . '/setting/uploadPicture?field=share_img&type=1')
- ->info('建议像素800*640,大小不超过1M')->icon('el-icon-camera')->modal(['modal' => false])->width('1000px')->height('600px')->props(['footer' => false])->required(),
- Elm::frameImage('feeds_img', '封面图:', '/' . config('admin.merchant_prefix') . '/setting/uploadPicture?field=feeds_img&type=1')
- ->info('建议像素800*800,大小不超过1M')->icon('el-icon-camera')->modal(['modal' => false])->width('1000px')->height('600px')->props(['footer' => false])->required(),
- Elm::input('anchor_name', '主播昵称:')->required()->placeholder('请输入主播昵称,主播需通过小程序直播认证,否则会提交失败。'),
- Elm::input('anchor_wechat', '主播微信号:')->required()->placeholder('请输入主播微信号,主播需通过小程序直播认证,否则会提交失败。'),
- Elm::input('phone', '联系电话')->placeholder('请输入联系电话')->required(),
- Elm::dateTimeRange('start_time', '直播时间:')->value([])->required(),
- Elm::radio('type', '直播间类型:', 0)->options([['value' => 0, 'label' => '手机直播'], ['value' => 1, 'label' => '推流']]),
- Elm::radio('screen_type', '显示样式:', 0)->options([['value' => 0, 'label' => '竖屏'], ['value' => 1, 'label' => '横屏']]),
- Elm::switches('close_like', '是否开启点赞:', 0)
- ->activeValue(0)->inactiveValue(1)
- ->activeText('开')->inactiveText('关'),
- Elm::switches('close_goods', '是否开启货架:', 0)
- ->activeValue(0)->inactiveValue(1)
- ->activeText('开')->inactiveText('关'),
- Elm::switches('close_comment', '是否开启评论:', 0)
- ->activeValue(0)->inactiveValue(1)
- ->activeText('开')->inactiveText('关'),
- Elm::switches('replay_status', '是否开启回放:', 0)
- ->activeValue(1)->inactiveValue(0)
- ->activeText('开')->inactiveText('关'),
- Elm::switches('close_share', '是否开启分享:', 0)
- ->activeValue(0)->inactiveValue(1)
- ->activeText('开')->inactiveText('关'),
- Elm::switches('close_kf', '是否开启客服:', 0)
- ->activeValue(0)->inactiveValue(1)
- ->activeText('开')->inactiveText('关'),
- Elm::switches('is_feeds_public', '是否开启官方收录:', 1)
- ->activeValue(1)->inactiveValue(0)
- ->activeText('开')->inactiveText('关'),
- ])->setTitle('创建直播间');
- }
- /**
- * 创建编辑直播间的表单
- *
- * 本函数用于生成一个用于编辑直播间的表单。它首先通过$id$从数据层获取直播间的信息,
- * 然后将开始时间和结束时间组合为一个数组,以便在表单中以特定的方式显示。
- * 最后,它构建并返回一个填充了直播间数据的表单实例,表单的动作是更新直播间信息的路由。
- *
- * @param int $id 直播间的唯一标识符,用于获取直播间的信息。
- * @return Form 生成的表单实例,包含了直播间的信息和编辑操作的动作。
- */
- public function updateForm($id)
- {
- // 通过$id$获取直播间的信息,并转换为数组格式
- $data = $this->dao->get($id)->toArray();
- // 将开始时间和结束时间组合为一个数组,方便在表单中处理
- $data['start_time'] = [$data['start_time'], $data['end_time']];
- // 创建表单,设置表单的动作为更新直播间信息的路由,并填充直播间数据
- // 设置表单标题为“编辑直播间”
- return $this->createForm()->setAction(Route::buildUrl('merchantBroadcastRoomUpdate', compact('id'))->build())->formData($data)->setTitle('编辑直播间');
- }
- /**
- * 创建直播房间
- *
- * @param string $merId 商户ID
- * @param array $data 房间相关数据
- * @return Room|bool 创建的房间对象或操作结果
- *
- * 本函数负责根据提供的商户ID和房间数据创建直播房间。它会根据商户是否是主播房间
- * 来设定房间的状态,并在创建房间后根据房间状态执行不同的后续操作,如设置房间ID
- * 和发送管理员通知。
- */
- public function create($merId, array $data)
- {
- // 根据商户是否是主播房间,设置房间状态
- $data['status'] = request()->merchant()->is_bro_room == 1 ? 0 : 1;
- $data['mer_id'] = $merId;
- // 使用事务处理来确保数据的一致性
- return Db::transaction(function () use ($data) {
- $room = $this->dao->create($data);
- // 如果房间是待审核状态,则进行房间ID的设置和状态更新
- if ($data['status'] == 1) {
- $room->room_id = $this->wxCreate($room);
- $room->status = 2;
- $room->save();
- } else {
- // 如果房间是待审核状态以外的状态,则发送管理员通知
- SwooleTaskService::admin('notice', [
- 'type' => 'new_broadcast',
- 'data' => [
- 'title' => '新直播间申请',
- 'message' => '您有1个新的直播间审核,请及时处理!',
- 'id' => $room->broadcast_room_id
- ]
- ]);
- }
- return $room;
- });
- }
- /**
- * 更新直播间信息并发送通知。
- *
- * 本函数用于处理直播间信息的更新,并在更新完成后发送管理员通知。它首先通过$merId和$id查询到对应的直播间,
- * 然后更新直播间的状态和其他信息。最后,通过SwooleTaskService发送一条新直播间申请的通知给管理员。
- *
- * @param string $merId 商户ID,用于查询直播间所属的商户。
- * @param int $id 直播间ID,用于查询具体的直播间。
- * @param array $data 包含需要更新的直播间信息的数据数组。
- */
- public function updateRoom($merId, $id, array $data)
- {
- // 设置直播间状态为待审核
- $data['status'] = 0;
- // 根据商户ID和直播间ID查询直播间信息
- $room = $this->dao->getWhere(['mer_id' => $merId, 'broadcast_room_id' => $id]);
- // 更新直播间的信息
- $room->save($data);
- // 发送管理员通知,告知有新的直播间申请待处理
- SwooleTaskService::admin('notice', [
- 'type' => 'new_broadcast',
- 'data' => [
- 'title' => '新直播间申请',
- 'message' => '您有1个新的直播间审核,请及时处理!',
- 'id' => $room->broadcast_room_id
- ]
- ]);
- }
- /**
- * 创建直播间申请表单
- *
- * 本函数用于生成一个包含直播间申请审核状态的表单。表单中包含一个单选按钮组,
- * 用于选择审核状态(未通过或通过),如果选择未通过,还需要提供未通过的原因。
- *
- * @param int $id 直播间申请的ID,用于构建表单的提交URL。
- * @return Elm|Form
- */
- public function applyForm($id)
- {
- // 构建表单提交的URL,使用紧凑模式传递ID参数
- $url = Route::buildUrl('systemBroadcastRoomApply', compact('id'))->build();
- // 创建表单,设置表单标题为“审核直播间”
- return Elm::createForm($url, [
- // 添加单选按钮组,用于选择审核状态,初始值为通过(1)
- Elm::radio('status', '审核状态:', '1')
- ->options([['value' => '-1', 'label' => '未通过'], ['value' => '1', 'label' => '通过']])
- ->control([
- // 当选择未通过时,显示文本区域,用于输入未通过的原因
- ['value' => '-1', 'rule' => [
- Elm::textarea('msg', '未通过原因:', '信息有误,请完善')
- ->placeholder('请输入未通过原因')
- ->required()
- ]]
- ])
- ])
- ->setTitle('审核直播间');
- }
- /**
- * 处理直播间的申请操作。
- *
- * 根据传入的状态对直播间进行相应的处理,包括修改直播间状态、记录错误信息、创建直播间、发送通知等。
- * 当状态为-1时,表示申请未通过,会记录失败原因;其他状态表示申请通过并进行相应的直播间创建和通知发送操作。
- *
- * @param int $id 直播间ID。
- * @param int $status 直播间状态,用于确定具体的处理流程。
- * @param string $msg 当状态为-1时,用于记录申请未通过的原因。
- */
- public function apply($id, $status, $msg = '')
- {
- // 根据ID获取直播间信息
- $room = $this->dao->get($id);
- // 开启数据库事务,确保一系列操作的原子性
- Db::transaction(function () use ($msg, $status, $room) {
- // 更新直播间状态
- $room->status = $status;
- // 当状态为-1时,记录未通过的原因
- if ($status == -1) {
- $room->error_msg = $msg;
- } else {
- // 通过微信接口创建直播间,并更新直播间ID和状态
- $room_id = $this->wxCreate($room);
- $room->room_id = $room_id;
- $room->status = 2;
- // 如果直播间类型需要,生成并记录推流地址
- if ($room->type) {
- $path = MiniProgramService::create()->miniBroadcast()->getPushUrl($room_id);
- $room->push_url = $path->pushAddr;
- }
- }
- // 保存更新后的直播间信息
- $room->save();
- // 发送审核状态通知给商家
- SwooleTaskService::merchant('notice', [
- 'type' => 'broadcast_status_' . ($status == -1 ? 'fail' : 'success'),
- 'data' => [
- 'title' => '直播间审核通知',
- 'message' => $status == -1 ? '您的直播间审核未通过!' : '您的直播间审核已通过',
- 'id' => $room->broadcast_room_id
- ]
- ], $room->mer_id);
- // 当状态为-1时,将未通过审核的信息推送到短信队列
- if ($status == -1) {
- Queue::push(SendSmsJob::class, [
- 'tempId' => 'BROADCAST_ROOM_FAIL',
- 'id' => $room['broadcast_room_id']
- ]);
- }
- });
- }
- /**
- * 创建微信直播房间
- *
- * @param BroadcastRoom $room 直播房间信息对象
- * @return string 创建的直播房间ID
- * @throws ValidateException 如果房间已经存在,则抛出验证异常
- */
- public function wxCreate(BroadcastRoom $room)
- {
- // 检查房间ID是否存在,如果存在则表示房间已创建,抛出异常
- if ($room['room_id']) {
- throw new ValidateException('直播间已创建');
- }
- // 将BroadcastRoom对象转换为数组
- $room = $room->toArray();
- // 创建小程序服务实例
- $miniProgramService = MiniProgramService::create();
- // 创建图片下载服务实例
- $DownloadImageService = app()->make(DownloadImageService::class);
- // 下载并获取封面图片路径
- $coverImg = './public' . $DownloadImageService->downloadImage($room['cover_img'], 'def', '', 1)['path'];
- // 下载并获取分享图片路径
- $shareImg = './public' . $DownloadImageService->downloadImage($room['share_img'], 'def', '', 1)['path'];
- // 下载并获取Feed流图片路径
- $feedsImg = './public' . $DownloadImageService->downloadImage($room['feeds_img'], 'def', '', 1)['path'];
- // 准备直播房间相关信息
- $data = [
- 'name' => $room['name'], // 直播间名称
- 'coverImg' => $miniProgramService->material()->uploadImage($coverImg)->media_id, // 封面图片ID
- 'startTime' => strtotime($room['start_time']), // 直播开始时间戳
- 'endTime' => strtotime($room['end_time']), // 直播结束时间戳
- 'anchorName' => $room['anchor_name'], // 主播姓名
- 'anchorWechat' => $room['anchor_wechat'], // 主播微信号
- 'shareImg' => $miniProgramService->material()->uploadImage($shareImg)->media_id, // 分享图片ID
- 'feedsImg' => $miniProgramService->material()->uploadImage($feedsImg)->media_id, // Feed流图片ID
- 'type' => $room['type'], // 直播类型
- 'closeLike' => $room['close_like'], // 是否关闭点赞
- 'closeGoods' => $room['close_goods'], // 是否关闭商品
- 'closeComment' => $room['close_comment'], // 是否关闭评论
- 'screenType' => $room['screen_type'], // 屏幕类型
- 'closeShare' => $room['close_share'], // 是否关闭分享
- 'closeKf' => $room['close_kf'], // 是否关闭客服
- 'closeReplay' => $room['replay_status'] == 1 ? 0 : 1, // 是否关闭回放
- 'isFeedsPublic' => $room['is_feeds_public'] == 1 ? 0 : 1, // 是否关闭Feed流公开
- ];
- // 删除本地临时图片文件
- @unlink($coverImg);
- @unlink($shareImg);
- @unlink($feedsImg);
- try {
- // 创建直播房间,并获取房间ID
- $roomId = $miniProgramService->miniBroadcast()->createLiveRoom($data)->roomId;
- } catch (Exception $e) {
- // 如果创建失败,抛出验证异常,异常信息为微信返回的错误信息
- throw new ValidateException($e->getMessage());
- }
- // 将发送短信的任务推入队列
- Queue::push(SendSmsJob::class, [
- 'tempId' => 'BROADCAST_ROOM_CODE',
- 'id' => $room['broadcast_room_id']
- ]);
- // 返回创建的直播房间ID
- return $roomId;
- }
- /**
- * 更新展示状态
- * 根据管理员身份决定更新的是全局展示状态还是商家展示状态
- *
- * @param int $id 数据标识符
- * @param int $isShow 展示状态值,通常为0(不展示)或1(展示)
- * @param bool $admin 管理员身份标记,默认为false,表示非管理员
- * @return bool 更新操作的结果,true表示成功,false表示失败
- */
- public function isShow($id, $isShow, bool $admin = false)
- {
- // 根据管理员身份选择更新的字段,管理员更新is_show字段,非管理员更新is_mer_show字段
- return $this->dao->update($id, [($admin ? 'is_show' : 'is_mer_show') => $isShow]);
- }
- /**
- * 更新记录的标记。
- *
- * 本函数通过调用DAO层的update方法,更新指定ID的记录的mark字段。
- * 主要用于在系统中对特定资源进行标记或状态更新,例如标记一项任务为完成。
- *
- * @param int $id 需要更新的记录的ID。这是一个主键标识,用于精确定位到要更新的数据。
- * @param string $mark 新的标记值。这个值将替换原有记录的mark字段,用于表示资源的最新状态或标记。
- * @return bool 返回更新操作的结果。成功更新时返回true,更新失败则返回false。
- */
- public function mark($id, $mark)
- {
- // 调用DAO层的update方法,传入ID和新的标记值,尝试更新记录。
- return $this->dao->update($id, compact('mark'));
- }
- /**
- * 导出商品到直播间
- * 该方法用于将指定的商品关联到指定的直播间。它首先验证所选商品的有效性,然后检查直播间的状态,
- * 最后将商品与直播间建立关联。
- *
- * @param int $merId 商家ID,用于权限验证和商品查询。
- * @param array $ids 商品ID列表,表示需要导出到直播间的商品ID。
- * @param int $roomId 直播间ID,表示商品将被导出到的直播间。
- * @throws ValidateException 如果验证失败,抛出异常提示用户。
- */
- public function exportGoods($merId, array $ids, $roomId)
- {
- // 实例化直播商品仓库,用于查询商品信息。
- $broadcastGoodsRepository = app()->make(BroadcastGoodsRepository::class);
- // 验证所选商品是否存在并属于该商家。
- if (count($ids) != count($goods = $broadcastGoodsRepository->goodsList($merId, $ids)))
- throw new ValidateException('请选择正确的直播商品');
- // 验证直播间是否存在并处于有效状态。
- if (!$room = $this->dao->validRoom($roomId, $merId))
- throw new ValidateException('直播间状态有误');
- // 实例化直播房间商品仓库,用于查询直播间商品关联信息。
- $broadcastRoomGoodsRepository = app()->make(BroadcastRoomGoodsRepository::class);
- // 获取当前直播间已关联的商品ID列表。
- $goodsId = $broadcastRoomGoodsRepository->goodsId($room->broadcast_room_id);
- $ids = [];
- $data = [];
- // 遍历商品列表,找出未关联到当前直播间的商品,准备建立关联。
- foreach ($goods as $item) {
- if (!in_array($item->broadcast_goods_id, $goodsId)) {
- $data[] = [
- 'broadcast_room_id' => $room->broadcast_room_id,
- 'broadcast_goods_id' => $item->broadcast_goods_id
- ];
- $ids[] = $item->goods_id;
- }
- }
- // 如果没有需要新增关联的商品,则直接返回。
- if (!count($ids)) return;
- // 使用事务确保数据操作的完整性。
- Db::transaction(function () use ($ids, $broadcastRoomGoodsRepository, $goods, $room, $data) {
- // 批量插入新的商品与直播间关联记录。
- $broadcastRoomGoodsRepository->insertAll($data);
- // 调用小程序服务,将商品添加到直播间(注意:这里的代码可能需要根据实际服务位置进行调整)。
- MiniProgramService::create()->miniBroadcast()->addGoods(['roomId' => $room->room_id, 'ids' => $ids]);
- });
- }
- /**
- * 删除导出的商品
- * 该方法用于从直播房间中移除指定的商品。它首先验证商家和直播房间的存在性,然后通过商品ID和房间ID删除相关商品。
- * 如果商家或直播房间不存在,则抛出一个验证异常。
- *
- * @param int $merId 商家ID 用于验证商家是否存在
- * @param int $roomId 直播房间ID 用于验证直播房间是否存在
- * @param int $id 商品ID 用于从直播房间中删除指定商品
- * @throws ValidateException 如果商家或直播房间不存在,则抛出此异常
- */
- public function rmExportGoods($merId, $roomId, $id)
- {
- // 验证商家和直播房间是否存在
- if (!$this->dao->merExists($roomId, $merId))
- throw new ValidateException('直播间不存在');
- // 删除指定房间中的商品
- app()->make(BroadcastRoomGoodsRepository::class)->rmGoods($id, $roomId);
- }
- /**
- * 同步房间状态
- * 该方法用于从小程序广播接口获取房间状态,并更新本地数据库中对应房间的状态。考虑到性能和接口限制,采用分页方式批量获取和更新。
- */
- public function syncRoomStatus()
- {
- // 初始化起始位置和每批处理的数量
- $start = 0;
- $limit = 50;
- // 创建小程序广播客户端
- $client = MiniProgramService::create()->miniBroadcast();
- do {
- // 分批获取房间信息
- $data = $client->getRooms($start, $limit)->room_info;
- $start += 50; // 更新起始位置,准备下一批处理
- // 根据获取的房间ID,从本地数据库中批量获取房间详情
- $rooms = $this->getRooms(array_column($data, 'roomid'));
- // 遍历获取的房间信息,对比并更新本地数据库中的房间状态
- foreach ($data as $room) {
- // 如果本地有该房间记录且状态不同,则更新房间状态
- if (isset($rooms[$room['roomid']]) && $room['live_status'] != $rooms[$room['roomid']]['live_status']) {
- $this->dao->update($rooms[$room['roomid']]['broadcast_room_id'], ['live_status' => $room['live_status']]);
- }
- }
- } while (count($data) >= $limit); // 如果当前批次的数据量达到或超过限制,继续处理下一批
- }
- /**
- * 商家删除操作
- *
- * 本函数用于执行商家的删除操作。删除操作由商家ID标识。
- * 注意:此处的删除操作可能是逻辑删除,即标记为删除,而不是物理删除。
- *
- * @param int $id 商家ID,用于指定需要删除的商家。
- * @return bool 返回删除操作的结果,通常是操作是否成功的布尔值。
- *
- * @throws ValidateException 如果商家状态不正确,则抛出验证异常。
- */
- public function merDelete($id)
- {
- // 通过商家ID执行删除操作
- // 此处注释掉的代码块原本用于检查商家的状态是否允许删除
- // 如果商家状态不正确,则不应该执行删除操作,并抛出异常
- // 在实际执行中,这些检查可能被实现为更复杂的业务逻辑,以确保数据的安全性和一致性
- // 调用DAO层的方法执行商家删除操作
- return $this->dao->merDelete($id);
- }
- /**
- * 关闭直播相关信息的函数
- *
- * 本函数用于处理直播间的关闭操作,包括关闭客服、评论、公开性以及商品上架状态的变更。
- * 在执行操作前,会检查直播间的审核状态及权限,确保只有在允许的情况下才能进行变更。
- * 使用事务确保数据库操作的一致性。
- *
- * @param int $id 直播间ID
- * @param string $type 关闭类型,包括关闭客服('close_kf')、关闭评论('close_comment')、设置feed是否公开('is_feeds_public')和商品上架状态('on_sale')
- * @param int $status 新的状态值,用于关闭或开启相关功能
- * @param bool $check 是否检查平台状态,默认为true。如果平台已关闭,则不允许进行修改。
- * @param array $data 当类型为'on_sale'时,需要提供商品相关信息,包括商品ID。
- * @throws ValidateException 当直播间状态不正确、数据不存在或操作权限受限时抛出异常。
- */
- public function closeInfo($id, string $type, int $status, $check = true, $data = [])
- {
- // 根据ID获取直播间信息
- $room = $this->dao->get($id);
- // 检查直播间是否已通过审核
- if ($room->status !== 2) throw new ValidateException('直播间还未审核通过,无法修改');
- // 检查直播间是否存在
- if (!$room) throw new ValidateException('数据不存在');
- // 如果需要检查平台状态且直播间对应类型已关闭,则抛出异常
- if ($check && $room[$type] == -1) {
- throw new ValidateException('平台已关闭,您无法修改');
- }
- // 使用事务处理数据库操作
- Db::transaction(function () use ($room, $id, $type, $status, $data) {
- // 根据类型创建对应的操作客户端
- $client = MiniProgramService::create()->miniBroadcast();
- // 根据类型执行相应的关闭操作
- switch ($type) {
- case 'close_kf':
- // 关闭客服
- $client->closeKf($room->room_id, $status);
- $room->close_kf = $status;
- break;
- case 'close_comment':
- // 关闭评论
- $client->banComment($room->room_id, $status);
- $room->close_comment = $status;
- break;
- case 'is_feeds_public':
- // 设置feed公开性
- $client->updateFeedPublic($room->room_id, $status);
- $room->is_feeds_public = $status;
- break;
- case 'on_sale':
- // 商品上架状态变更
- $ret = app()->make(BroadcastRoomGoodsRepository::class)->getWhere([
- 'broadcast_room_id' => $id,
- 'broadcast_goods_id' => $data['goods_id'],
- ], '*', ['goods']);
- // 检查商品是否存在
- if (!isset($ret['goods']['goods_id'])) throw new ValidateException('数据不存在');
- // 更新商品上架状态
- $ret->on_sale = $status;
- $ret->save();
- // 商品上架或下架
- $client->goodsOnsale($room->room_id, $ret['goods']['goods_id'], $status);
- break;
- }
- // 更新直播间信息
- $room->save();
- });
- }
- /**
- * 创建商家助手编辑表单
- *
- * 该方法用于生成一个用于编辑商家助手的表单。商家助手是在直播间中协助商家进行互动的工具。
- * 表单中包含一个选择字段,用于选择可用的小助手。
- *
- * @param int $id 商家直播间的ID
- * @param int $merId 商家的ID
- * @return \EasyWeChat\Kernel\Messages\Miniprogram|Form
- *
- * @throws ValidateException 如果直播间未通过审核,则抛出异常
- */
- public function assistantForm(int $id, int $merId)
- {
- // 实例化广播助手仓库
- $make = app()->make(BroadcastAssistantRepository::class);
- // 根据ID获取直播间信息
- $get = $this->dao->get($id);
- // 如果直播间的状态不是2(未审核通过),则抛出异常
- if ($get->status !== 2) throw new ValidateException('直播间还未审核通过,无法操作');
- // 获取可选的助手列表
- $data = $make->options($merId);
- // 检查当前商家是否已添加过助手
- $has = $make->intersection($get->assistant_id, $merId);
- // 创建表单,设置表单的URL和标题,并添加选择助手的字段
- return Elm::createForm(Route::buildUrl('merchantBroadcastAddAssistant', compact('id'))->build(),
- [
- Elm::selectMultiple('assistant_id', '小助手:')->options(function () use ($data) {
- $options = [];
- // 如果有可用的助手数据,遍历生成选项
- if ($data) {
- foreach ($data as $value => $label) {
- $options[] = compact('value', 'label');
- }
- }
- return $options;
- })
- ])->setTitle('修改小助手');
- }
- /**
- * 编辑助手信息
- *
- * 该方法用于更新指定房间ID下的助手列表。它首先检查新提供的助手ID列表是否与现有列表有差异,
- * 然后根据差异添加或移除助手,并最终更新数据库中的助手ID组合。
- *
- * @param int $id 房间ID
- * @param int $merId 商家ID,用于权限控制或记录操作来源
- * @param array $data 新的助手ID列表
- */
- public function editAssistant(int $id, int $merId, array $data)
- {
- // 实例化广播助手仓库,用于后续的操作
- $make = app()->make(BroadcastAssistantRepository::class);
- // 检查传入的数据是否在数据库中全部存在,用于防止非法数据操作
- $make->existsAll($data, $merId);
- // 使用数据库事务来确保操作的原子性
- Db::transaction(function () use ($id, $data) {
- // 获取当前房间的信息
- $get = $this->dao->get($id);
- // 将现有的助手ID字符串转换为数组
- $old = explode(',', $get->assistant_id);
- // 计算需要移除的助手ID和需要添加的助手ID
- $remove = array_diff($old, $data);
- $add = array_diff($data, $old);
- // 分别调用方法来添加和移除助手
- $this->addAssistant($get->room_id, $add);
- $this->removeAssistant($get->room_id, $remove);
- // 更新房间的助手ID列表,并保存到数据库
- $get->assistant_id = implode(',', $data);
- $get->save();
- });
- }
- /**
- * 从指定的房间中移除助手。
- *
- * 本函数用于处理从特定房间中移除多个助手的操作。它首先通过提供的助手ID查询到相关助手的信息,
- * 然后依次调用接口将这些助手从指定的房间中移除。
- *
- * @param int $roomId 房间ID,指定要从哪个房间移除助手。
- * @param array $ids 助手ID数组,指定要移除的助手的ID列表。
- */
- public function removeAssistant($roomId, array $ids)
- {
- // 实例化广播助手仓库,用于后续查询助手信息。
- $make = app()->make(BroadcastAssistantRepository::class);
- // 根据提供的助手ID查询助手信息。
- $data = $make->getSearch(['assistant_ids' => $ids])->select();
- // 遍历查询到的助手信息,逐个从房间中移除助手。
- foreach ($data as $datum) {
- // 创建小程序服务实例,并调用其广播相关方法,实现在指定房间中移除助手的功能。
- MiniProgramService::create()->miniBroadcast()->removeAssistant($roomId, $datum->username);
- }
- }
- /**
- * 添加助手到直播间
- *
- * 本函数用于将指定的助手用户添加到指定的直播间中。它首先通过助手ID查询用户信息,
- * 然后将这些信息发送给小程序服务端,以在直播间中添加助手。
- *
- * @param int $roomId 直播间ID
- * @param array $ids 助手用户的ID列表
- */
- public function addAssistant($roomId, array $ids)
- {
- // 通过依赖注入获取广播助手仓库实例
- $make = app()->make(BroadcastAssistantRepository::class);
- // 根据助手ID查询用户用户名和昵称
- $data = $make->getSearch(['assistant_ids' => $ids])->column('username,nickname');
- // 构建添加助手的参数
- $params = [
- 'roomId' => $roomId,
- 'users' => $data
- ];
- // 调用小程序服务,添加助手到直播间
- MiniProgramService::create()->miniBroadcast()->addAssistant($params);
- }
- /**
- * 向特定房间的用户推送消息。
- *
- * 本函数旨在向小程序中特定房间的用户推送消息。它首先通过房间ID获取房间信息,
- * 然后分页获取该房间的所有关注者列表,并将消息推送给这些用户。
- *
- * @param int $id 房间的ID,用于定位要推送消息的特定房间。
- * @throws ValidateException 如果获取关注者列表或推送消息时发生错误,则抛出异常。
- */
- public function pushMessage(int $id)
- {
- // 通过房间ID获取房间信息
- $get = $this->dao->get($id);
- // 创建小程序服务实例,并初始化广播功能
- $make = MiniProgramService::create()->miniBroadcast();
- // 初始化分页标识
- $page_break = '';
- do {
- // 分页获取关注者列表
- $data = $make->getFollowers($page_break);
- $restult = [];
- // 检查是否有错误发生,如果有则抛出异常
- if ($data['errcode'] !== 0) throw new ValidateException($data['errmsg']);
- // 遍历关注者列表,筛选出属于指定房间的用户
- foreach ($data['followers'] as $datum) {
- if ($datum['room_id'] == $get->room_id) {
- $restult[] = $datum['openid'];
- }
- }
- // 如果有符合的用户,则向他们推送消息
- if ($restult) {
- $make->pushMessage($get->room_id, $restult);
- }
- // 更新分页标识,准备获取下一页数据
- $page_break = $data['page_break'] ?? '';
- } while ($page_break);
- }
- }
|