diff --git a/application/service/controller/Fans.php b/application/service/controller/Fans.php
new file mode 100644
index 000000000..004e2ba7a
--- /dev/null
+++ b/application/service/controller/Fans.php
@@ -0,0 +1,163 @@
+appid = input('appid', session('current_appid'));
+ if (empty($this->appid)) {
+ $this->appid = Db::name('WechatServiceConfig')->value('authorizer_appid');
+ }
+ if (empty($this->appid)) {
+ $this->fetch('/not-auth');
+ } else {
+ session('current_appid', $this->appid);
+ }
+ if ($this->request->isGet()) {
+ $this->wechats = Db::name('WechatServiceConfig')->where(['status' => '1'])->column('authorizer_appid,nick_name');
+ }
+ }
+
+ /**
+ * 微信粉丝管理
+ * @auth true
+ * @menu true
+ * @throws \think\Exception
+ * @throws \think\db\exception\DataNotFoundException
+ * @throws \think\db\exception\ModelNotFoundException
+ * @throws \think\exception\DbException
+ * @throws \think\exception\PDOException
+ */
+ public function index()
+ {
+ $this->title = '微信粉丝管理';
+ $query = $this->_query($this->table)->like('nickname')->equal('subscribe,is_black');
+ $query->dateBetween('subscribe_at')->where(['appid' => $this->appid])->order('subscribe_time desc')->page();
+ }
+
+ /**
+ * 列表数据处理
+ * @param array $data
+ */
+ protected function _index_page_filter(array &$data)
+ {
+ $tags = Db::name('WechatFansTags')->column('id,name');
+ foreach ($data as &$user) {
+ $user['tags'] = [];
+ foreach (explode(',', $user['tagid_list']) as $tagid) {
+ if (isset($tags[$tagid])) $user['tags'][] = $tags[$tagid];
+ }
+ }
+ }
+
+ /**
+ * 批量拉黑粉丝
+ * @auth true
+ */
+ public function setBlack()
+ {
+ $this->applyCsrfToken();
+ try {
+ foreach (array_chunk(explode(',', $this->request->post('openid')), 20) as $openids) {
+ WechatService::WeChatUser($this->appid)->batchBlackList($openids);
+ Db::name('WechatFans')->whereIn('openid', $openids)->update(['is_black' => '1']);
+ }
+ $this->success('拉黑粉丝信息成功!');
+ } catch (HttpResponseException $exception) {
+ throw $exception;
+ } catch (\Exception $e) {
+ $this->error("拉黑粉丝信息失败,请稍候再试!{$e->getMessage()}");
+ }
+ }
+
+ /**
+ * 取消拉黑粉丝
+ * @auth true
+ */
+ public function delBlack()
+ {
+ try {
+ $this->applyCsrfToken();
+ foreach (array_chunk(explode(',', $this->request->post('openid')), 20) as $openids) {
+ WechatService::WeChatUser($this->appid)->batchUnblackList($openids);
+ Db::name('WechatFans')->whereIn('openid', $openids)->update(['is_black' => '0']);
+ }
+ $this->success('取消拉黑粉丝信息成功!');
+ } catch (HttpResponseException $exception) {
+ throw $exception;
+ } catch (\Exception $e) {
+ $this->error("取消拉黑粉丝信息失败,请稍候再试!{$e->getMessage()}");
+ }
+ }
+
+ /**
+ * 同步粉丝列表
+ * @auth true
+ */
+ public function sync()
+ {
+ try {
+ sysoplog('微信管理', "创建微信{$this->appid}粉丝同步任务");
+ QueueService::add("同步{$this->appid}粉丝列表", WechatQueue::URI, 0, ['appid' => $this->appid], 0);
+ $this->success('创建同步粉丝任务成功,需要时间来完成。
请到系统任务管理查看进度!');
+ } catch (HttpResponseException $exception) {
+ throw $exception;
+ } catch (\Exception $e) {
+ $this->error("创建同步粉丝任务失败,请稍候再试!
{$e->getMessage()}");
+ }
+ }
+
+ /**
+ * 删除粉丝信息
+ * @auth true
+ * @throws \think\Exception
+ * @throws \think\exception\PDOException
+ */
+ public function remove()
+ {
+ $this->applyCsrfToken();
+ $this->_delete($this->table);
+ }
+
+}
diff --git a/application/service/queue/WechatQueue.php b/application/service/queue/WechatQueue.php
new file mode 100644
index 000000000..c3b8aea83
--- /dev/null
+++ b/application/service/queue/WechatQueue.php
@@ -0,0 +1,82 @@
+appid = $this->data['appid'];
+ $wechat = WechatService::WeChatUser($this->appid);
+ $next = ''; // 获取远程粉丝
+ $this->output->writeln('Start synchronizing fans from the Wechat server');
+ while (is_array($result = $wechat->getUserList($next)) && !empty($result['data']['openid'])) {
+ foreach (array_chunk($result['data']['openid'], 100) as $chunk)
+ if (is_array($list = $wechat->getBatchUserInfo($chunk)) && !empty($list['user_info_list']))
+ foreach ($list['user_info_list'] as $user) FansService::set($user, $this->appid);
+ if (in_array($result['next_openid'], $result['data']['openid'])) break;
+ $next = $result['next_openid'];
+ }
+ $next = ''; // 同步粉丝黑名单
+ $this->output->writeln('Start synchronizing black from the Wechat server');
+ while (is_array($result = $wechat->getBlackList($next)) && !empty($result['data']['openid'])) {
+ foreach (array_chunk($result['data']['openid'], 100) as $chunk) {
+ $where = [['is_black', 'eq', '0'], ['openid', 'in', $chunk]];
+ Db::name('WechatFans')->where($where)->update(['is_black' => '1']);
+ }
+ if (in_array($result['next_openid'], $result['data']['openid'])) break;
+ $next = $result['next_openid'];
+ }
+ // 同步粉丝标签列表
+ $this->output->writeln('Start synchronizing tags from the Wechat server');
+ if (is_array($list = WechatService::WeChatTags($this->appid)->getTags()) && !empty($list['tags'])) {
+ foreach ($list['tags'] as &$tag) $tag['appid'] = $this->appid;
+ Db::name('WechatFansTags')->where('1=1')->delete();
+ Db::name('WechatFansTags')->insertAll($list['tags']);
+ }
+ return true;
+ } catch (\Exception $e) {
+ $this->statusDesc = $e->getMessage();
+ return false;
+ }
+ }
+
+}
diff --git a/application/service/view/fans/index.html b/application/service/view/fans/index.html
new file mode 100644
index 000000000..1697a4df0
--- /dev/null
+++ b/application/service/view/fans/index.html
@@ -0,0 +1,98 @@
+{extend name='admin@main'}
+
+{block name="button"}
+
+{if auth("service/fans/setblack")}
+
+{/if}
+
+{if auth("service/fans/delblack")}
+
+{/if}
+
+{if auth("service/fans/sync")}
+
+{/if}
+
+{/block}
+
+{block name="content"}
+
+ | 微信昵称 | +粉丝标签 | +性别语言 | +关注时间 | ++ | + |
---|---|---|---|---|---|---|
+ + | +
+
+ 昵称:{$vo.nickname|default='--'}
+
+ + 区域:{$vo.country|default='--'} {$vo.province} {$vo.city} + |
+
+ {foreach $vo.tags as $t}
+ {$t|default='--'} {/foreach} |
+
+ 性别:{switch name='vo.sex'}{case value='1'}男{/case}{case value='2'}女{/case}{default}未知{/switch}
+ + 语言:{$vo.language|raw} + |
+
+ 日期:{$vo.subscribe_at|format_datetime|str_replace=' ',' 时间:',###|raw} + |
+
+ {eq name='vo.subscribe' value='0'}
+ 未关注
+ {else}
+ 已关注
+ {/eq}
+ + {eq name='vo.is_black' value='0'} + 未拉黑 + {else} + 已拉黑 + {/eq} + |
+ + + {eq name='vo.is_black' value='0'} + + 拉 黑 + + {else} + + 拉 白 + + {/eq} + + {if auth("service/fans/remove")} + 删 除 + {/if} + | +