348 lines
12 KiB
PHP
348 lines
12 KiB
PHP
<?php
|
|
// +----------------------------------------------------------------------
|
|
// | YwxApp [ WE CAN DO IT JUST THINK ]
|
|
// +----------------------------------------------------------------------
|
|
declare(strict_types=1);
|
|
|
|
namespace addon\forum\controller;
|
|
|
|
use addon\forum\model\ForumBoard;
|
|
use addon\forum\model\ForumTopic;
|
|
use addon\forum\model\ForumReply;
|
|
use addon\forum\model\ForumMessage;
|
|
use ywxapp\model\BaseModel;
|
|
use think\facade\Db;
|
|
|
|
class Index extends ForumFrontend
|
|
{
|
|
protected $noNeedLogin = [];
|
|
protected $noNeedVerify = ['*'];
|
|
|
|
/**
|
|
* 社区首页:版块 + 帖子流
|
|
*/
|
|
public function index()
|
|
{
|
|
$boards = ForumBoard::enabledList();
|
|
$topics = ForumTopic::listFront(0, '', 'new', 1, 20);
|
|
$hot = ForumTopic::listFront(0, '', 'hot', 1, 10);
|
|
$this->attachAuthors($topics);
|
|
$this->attachAuthors($hot);
|
|
|
|
// 当前会员未读消息
|
|
$unread = 0;
|
|
if ($member = $this->member()) {
|
|
$unread = ForumMessage::unreadCount((int) $member['uid']);
|
|
}
|
|
|
|
$this->assign([
|
|
'boards' => $boards,
|
|
'topics' => $topics,
|
|
'hot' => $hot,
|
|
'unread' => $unread,
|
|
'typeMap'=> ForumTopic::$typeMap,
|
|
]);
|
|
return $this->fetch('index/index');
|
|
}
|
|
|
|
/**
|
|
* 版块帖子列表
|
|
*/
|
|
public function board($slug = '')
|
|
{
|
|
$board = ForumBoard::findBySlug($slug);
|
|
if (!$board) {
|
|
$this->assign('msg', '版块不存在');
|
|
return $this->fetch('index/notfound');
|
|
}
|
|
$type = $this->request->param('type', '');
|
|
$order = $this->request->param('order', 'new');
|
|
$page = (int) $this->request->param('page', 1);
|
|
$topics = ForumTopic::listFront((int) $board->id, $type, $order, $page, 20);
|
|
$this->attachAuthors($topics);
|
|
|
|
$this->assign([
|
|
'board' => $board,
|
|
'topics' => $topics,
|
|
'type' => $type,
|
|
'order' => $order,
|
|
'typeMap'=> ForumTopic::$typeMap,
|
|
]);
|
|
return $this->fetch('index/board');
|
|
}
|
|
|
|
/**
|
|
* 帖子详情 + 回复列表
|
|
*/
|
|
public function topic($id = 0)
|
|
{
|
|
$topic = ForumTopic::findById((int) $id);
|
|
if (!$topic || (int) $topic->status !== 1) {
|
|
$this->assign('msg', '帖子不存在、待审核或已下架');
|
|
return $this->fetch('index/notfound');
|
|
}
|
|
ForumTopic::addViews((int) $topic->id);
|
|
|
|
$replies = ForumReply::listByTopic((int) $topic->id);
|
|
$board = ForumBoard::find((int) $topic->board_id);
|
|
|
|
// 作者信息(复用会员表),带默认值避免模板内联查库
|
|
$author = Db::name('member')->where('id', $topic->user_id)
|
|
->field('id,nickname,avatar,intro')->find();
|
|
if (!$author) {
|
|
$author = ['id' => $topic->user_id, 'nickname' => '匿名', 'avatar' => '', 'intro' => ''];
|
|
}
|
|
|
|
// 每条回复附带用户信息
|
|
$uids = array_column($replies->toArray(), 'user_id');
|
|
$users = [];
|
|
if (!empty($uids)) {
|
|
$users = Db::name('member')->where('id', 'in', array_unique($uids))
|
|
->column('nickname,avatar', 'id');
|
|
}
|
|
$replyList = [];
|
|
foreach ($replies as $rp) {
|
|
$u = $users[$rp['user_id']] ?? ['nickname' => '匿名', 'avatar' => ''];
|
|
$replyList[] = [
|
|
'id' => $rp['id'],
|
|
'user_id' => $rp['user_id'],
|
|
'floor' => $rp['floor'],
|
|
'content' => $rp['content'],
|
|
'create_at' => $rp['create_at'],
|
|
'nickname' => $u['nickname'] ?: '匿名',
|
|
'avatar' => $u['avatar'] ?: '',
|
|
];
|
|
}
|
|
|
|
$this->assign([
|
|
'topic' => $topic,
|
|
'author' => $author,
|
|
'replyList' => $replyList,
|
|
'board' => $board,
|
|
'related' => \addon\forum\service\ForumSearchService::related((int) $topic->id, 6),
|
|
'typeName' => ForumTopic::$typeMap[$topic->type] ?? '',
|
|
]);
|
|
return $this->fetch('index/topic');
|
|
}
|
|
|
|
/**
|
|
* 发帖页
|
|
*/
|
|
public function post()
|
|
{
|
|
if (!$this->requireLogin()) {
|
|
return redirect('/user/login');
|
|
}
|
|
$boards = ForumBoard::enabledList();
|
|
$this->assign('boards', $boards);
|
|
$this->assign('typeMap', ForumTopic::$typeMap);
|
|
$cfg = \ywxapp\service\AddonService::config('forum');
|
|
$this->assign('editorHeight', (int) ($cfg['editor_height'] ?? 360));
|
|
return $this->fetch('index/post');
|
|
}
|
|
|
|
/**
|
|
* 提交发帖
|
|
*/
|
|
public function doPost()
|
|
{
|
|
if (!$this->requireLogin()) {
|
|
return $this->error('请先登录');
|
|
}
|
|
$member = $this->member();
|
|
$data = $this->request->post();
|
|
|
|
$title = trim((string) ($data['title'] ?? ''));
|
|
$content = trim((string) ($data['content'] ?? ''));
|
|
$boardId = (int) ($data['board_id'] ?? 0);
|
|
$type = (int) ($data['type'] ?? 0);
|
|
$lat = (float) ($data['lat'] ?? 0);
|
|
$lng = (float) ($data['lng'] ?? 0);
|
|
|
|
if ($title === '' || $content === '' || $boardId < 1) {
|
|
return $this->error('请填写完整信息');
|
|
}
|
|
if (!isset(ForumTopic::$typeMap[$type])) {
|
|
$type = ForumTopic::TYPE_ASK;
|
|
}
|
|
|
|
$needAudit = (int) config('forum.need_audit');
|
|
$status = $needAudit ? 0 : 1; // 0待审 1正常
|
|
$auditReason = '';
|
|
|
|
// 机审(云审):开启 need_audit 且无 AI 审核服务时,按策略直接通过/转人工
|
|
if ($needAudit) {
|
|
$ai = new \addon\wxchat\service\AiAuditService();
|
|
$res = $ai->audit($title . "\n" . $content);
|
|
if ($res['code'] === 0) {
|
|
if (!empty($res['data']['blocked'])) {
|
|
// 机审命中违规:直接驳回,不进入人工队列
|
|
$status = -1;
|
|
$auditReason = '机审不通过:' . ($res['data']['reason'] ?? '内容违规');
|
|
}
|
|
// 机审通过:保持待审(status=0),等待人工复核
|
|
}
|
|
// AI 服务不可用(code!=0 且非 blocked):维持待审,转人工队列
|
|
}
|
|
|
|
\addon\forum\library\ForumSchema::ensure();
|
|
$topicId = ForumTopic::insertGetId([
|
|
'board_id' => $boardId,
|
|
'user_id' => $member['id'],
|
|
'title' => $title,
|
|
'content' => $content,
|
|
'type' => $type,
|
|
'status' => $status,
|
|
'lat' => $lat,
|
|
'lng' => $lng,
|
|
'audit_reason' => $auditReason,
|
|
'create_at' => time(),
|
|
'update_at' => time(),
|
|
]);
|
|
|
|
ForumBoard::incr($boardId, 'topic_count', 1);
|
|
\addon\forum\service\ForumSearchService::sync($topicId);
|
|
|
|
if ($status === -1) {
|
|
return $this->success(['id' => $topicId], $auditReason);
|
|
}
|
|
return $this->success(['id' => $topicId], $needAudit ? '发布成功,等待审核' : '发布成功');
|
|
}
|
|
|
|
/**
|
|
* 提交回复
|
|
*/
|
|
public function doReply()
|
|
{
|
|
if (!$this->requireLogin()) {
|
|
return $this->error('请先登录');
|
|
}
|
|
$member = $this->member();
|
|
$data = $this->request->post();
|
|
$topicId = (int) ($data['topic_id'] ?? 0);
|
|
$content = trim((string) ($data['content'] ?? ''));
|
|
|
|
if ($topicId < 1 || $content === '') {
|
|
return $this->error('回复内容不能为空');
|
|
}
|
|
$topic = ForumTopic::findById($topicId);
|
|
if (!$topic) {
|
|
return $this->error('帖子不存在');
|
|
}
|
|
|
|
$floor = ForumReply::countByTopic($topicId) + 1;
|
|
$replyId = ForumReply::insertGetId([
|
|
'topic_id' => $topicId,
|
|
'user_id' => $member['id'],
|
|
'content' => $content,
|
|
'floor' => $floor,
|
|
'status' => 1,
|
|
'create_at' => time(),
|
|
'update_at' => time(),
|
|
]);
|
|
|
|
// 更新帖子回复计数 + 最后回复
|
|
ForumTopic::where('id', $topicId)->inc('reply_count', 1)->update([
|
|
'last_reply_id' => $replyId,
|
|
'last_reply_uid' => $member['id'],
|
|
'last_reply_time' => time(),
|
|
]);
|
|
ForumBoard::incr((int) $topic->board_id, 'reply_count', 1);
|
|
|
|
// 给帖子作者发消息(非自己)
|
|
if ($topic->user_id != $member['id']) {
|
|
ForumMessage::insert([
|
|
'user_id' => $topic->user_id,
|
|
'from_uid' => $member['id'],
|
|
'type' => 'reply',
|
|
'topic_id' => $topicId,
|
|
'reply_id' => $replyId,
|
|
'content' => mb_substr(strip_tags($content), 0, 120),
|
|
'is_read' => 0,
|
|
'create_at' => time(),
|
|
]);
|
|
}
|
|
|
|
return $this->success(['floor' => $floor], '回复成功');
|
|
}
|
|
|
|
/**
|
|
* 搜索
|
|
*/
|
|
public function search()
|
|
{
|
|
$kw = trim((string) $this->request->param('q', ''));
|
|
$list = [];
|
|
if ($kw !== '') {
|
|
$list = \addon\forum\service\ForumSearchService::search($kw, (int) $this->request->param('page', 1), 20);
|
|
}
|
|
$this->assign('kw', $kw);
|
|
$this->assign('list', $list);
|
|
return $this->fetch('index/search');
|
|
}
|
|
|
|
/**
|
|
* 附近帖子(GEO/LBS):由前端定位传入 lat/lng,展示半径内帖子
|
|
*/
|
|
public function nearby()
|
|
{
|
|
$lat = (float) $this->request->param('lat', 0);
|
|
$lng = (float) $this->request->param('lng', 0);
|
|
$radius = (float) $this->request->param('radius', 10);
|
|
$list = \addon\forum\service\ForumSearchService::nearby($lat, $lng, $radius, 30);
|
|
$this->assign('list', $list);
|
|
$this->assign('lat', $lat);
|
|
$this->assign('lng', $lng);
|
|
$this->assign('radius', $radius);
|
|
return $this->fetch('index/nearby');
|
|
}
|
|
|
|
/**
|
|
* 批量给帖子列表(Paginator)注入 author 字段,避免模板读取不存在的关联属性
|
|
* @param \think\Paginator $paginator
|
|
*/
|
|
private function attachAuthors($collection)
|
|
{
|
|
if (!is_iterable($collection)) {
|
|
return;
|
|
}
|
|
$uids = [];
|
|
foreach ($collection as $t) {
|
|
$uids[] = $t['user_id'] ?? ($t->user_id ?? 0);
|
|
}
|
|
$uids = array_filter(array_unique($uids));
|
|
$users = [];
|
|
if (!empty($uids)) {
|
|
$users = Db::name('member')->where('id', 'in', $uids)
|
|
->column('nickname,avatar', 'id');
|
|
}
|
|
foreach ($collection as $t) {
|
|
$uid = $t['user_id'] ?? ($t->user_id ?? 0);
|
|
$u = $users[$uid] ?? ['nickname' => '匿名', 'avatar' => ''];
|
|
$t->author = [
|
|
'nickname' => $u['nickname'] ?: '匿名',
|
|
'avatar' => $u['avatar'] ?: '',
|
|
];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 用户主页(展示其论坛数据)
|
|
*/
|
|
public function user($id = 0)
|
|
{
|
|
$user = Db::name('member')->where('id', (int) $id)->field('id,nickname,avatar,intro')->find();
|
|
if (!$user) {
|
|
$this->assign('msg', '用户不存在');
|
|
return $this->fetch('index/notfound');
|
|
}
|
|
$topics = ForumTopic::where('user_id', (int) $id)
|
|
->where('status', 1)
|
|
->order('id desc')
|
|
->limit(20)->select();
|
|
$this->assign('member', $user);
|
|
$this->assign('topics', $topics);
|
|
return $this->fetch('index/user');
|
|
}
|
|
}
|