Files
YwxAppThink/addon/forum/controller/Index.php
T

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');
}
}