feat: add community post categories and channels

This commit is contained in:
hajimi
2026-08-02 17:39:52 +08:00
parent 95f03af60e
commit b0b278d11f
18 changed files with 1132 additions and 12 deletions
@@ -0,0 +1,54 @@
<?php
namespace app\adminapi\controller\community;
use app\adminapi\controller\BaseAdminController;
use app\adminapi\lists\community\CommunityCategoryLists;
use app\adminapi\logic\community\CommunityCategoryLogic;
use app\adminapi\validate\community\CommunityCategoryValidate;
class CommunityCategoryController extends BaseAdminController
{
public function lists()
{
return $this->dataLists(new CommunityCategoryLists());
}
public function all()
{
return $this->data(CommunityCategoryLogic::all());
}
public function detail()
{
$params = (new CommunityCategoryValidate())->goCheck('detail');
return $this->data(CommunityCategoryLogic::detail($params));
}
public function add()
{
$params = (new CommunityCategoryValidate())->post()->goCheck('add');
if (CommunityCategoryLogic::add($params)) {
return $this->success('添加成功', [], 1, 1);
}
return $this->fail(CommunityCategoryLogic::getError());
}
public function edit()
{
$params = (new CommunityCategoryValidate())->post()->goCheck('edit');
if (CommunityCategoryLogic::edit($params)) {
return $this->success('编辑成功', [], 1, 1);
}
return $this->fail(CommunityCategoryLogic::getError());
}
public function delete()
{
$params = (new CommunityCategoryValidate())->post()->goCheck('delete');
if (CommunityCategoryLogic::delete($params)) {
return $this->success('删除成功', [], 1, 1);
}
return $this->fail(CommunityCategoryLogic::getError());
}
}
@@ -61,6 +61,26 @@ class CommunityPostController extends BaseAdminController
return $this->fail(CommunityPostLogic::getError());
}
public function setTopic()
{
$params = (new CommunityPostValidate())->post()->goCheck('topic');
$result = CommunityPostLogic::setTopic($params);
if (true === $result) {
return $this->success('设置成功', [], 1, 1);
}
return $this->fail(CommunityPostLogic::getError());
}
public function setCategory()
{
$params = (new CommunityPostValidate())->post()->goCheck('category');
$result = CommunityPostLogic::setCategory($params);
if (true === $result) {
return $this->success('设置成功', [], 1, 1);
}
return $this->fail(CommunityPostLogic::getError());
}
public function delete()
{
$params = (new CommunityPostValidate())->post()->goCheck('delete');
@@ -0,0 +1,33 @@
<?php
namespace app\adminapi\lists\community;
use app\adminapi\lists\BaseAdminDataLists;
use app\common\lists\ListsSearchInterface;
use app\common\model\community\CommunityCategory;
class CommunityCategoryLists extends BaseAdminDataLists implements ListsSearchInterface
{
public function setSearch(): array
{
return [
'%like%' => ['name'],
'=' => ['status'],
];
}
public function lists(): array
{
return CommunityCategory::where($this->searchWhere)
->limit($this->limitOffset, $this->limitLength)
->order('sort', 'desc')
->order('id', 'desc')
->select()
->toArray();
}
public function count(): int
{
return CommunityCategory::where($this->searchWhere)->count();
}
}
@@ -4,6 +4,7 @@ namespace app\adminapi\lists\community;
use app\adminapi\lists\BaseAdminDataLists;
use app\common\lists\ListsSearchInterface;
use app\common\model\community\CommunityCategory;
use app\common\model\community\CommunityPost;
use app\common\model\user\User;
@@ -12,7 +13,7 @@ class CommunityPostLists extends BaseAdminDataLists implements ListsSearchInterf
public function setSearch(): array
{
return [
'=' => ['status', 'post_type', 'is_top', 'is_hot', 'is_recommend', 'user_id'],
'=' => ['status', 'post_type', 'category_id', 'is_top', 'is_hot', 'is_recommend', 'is_topic', 'user_id'],
];
}
@@ -26,11 +27,16 @@ class CommunityPostLists extends BaseAdminDataLists implements ListsSearchInterf
$userIds = array_unique(array_column($lists, 'user_id'));
$users = User::whereIn('id', $userIds)->column('nickname,avatar', 'id');
$categoryIds = array_values(array_filter(array_unique(array_column($lists, 'category_id'))));
$categories = $categoryIds
? CommunityCategory::whereIn('id', $categoryIds)->column('name', 'id')
: [];
foreach ($lists as &$item) {
$user = $users[$item['user_id']] ?? [];
$item['nickname'] = $user['nickname'] ?? '-';
$item['avatar'] = $user['avatar'] ?? '';
$item['category_name'] = $categories[$item['category_id']] ?? '';
$item['create_time'] = is_numeric($item['create_time']) ? date('Y-m-d H:i:s', $item['create_time']) : $item['create_time'];
}
@@ -0,0 +1,72 @@
<?php
namespace app\adminapi\logic\community;
use app\common\logic\BaseLogic;
use app\common\model\community\CommunityCategory;
use app\common\model\community\CommunityPost;
class CommunityCategoryLogic extends BaseLogic
{
public static function add(array $params): bool
{
try {
CommunityCategory::create([
'name' => trim($params['name']),
'icon' => $params['icon'] ?? '',
'sort' => $params['sort'] ?? 0,
'status' => $params['status'] ?? 1,
]);
return true;
} catch (\Exception $e) {
self::setError($e->getMessage());
return false;
}
}
public static function edit(array $params): bool
{
try {
CommunityCategory::update([
'id' => $params['id'],
'name' => trim($params['name']),
'icon' => $params['icon'] ?? '',
'sort' => $params['sort'] ?? 0,
'status' => $params['status'] ?? 1,
]);
return true;
} catch (\Exception $e) {
self::setError($e->getMessage());
return false;
}
}
public static function delete(array $params): bool
{
if (CommunityPost::where('category_id', $params['id'])->count() > 0) {
self::setError('该分类下存在帖子,不能删除');
return false;
}
try {
CommunityCategory::destroy($params['id']);
return true;
} catch (\Exception $e) {
self::setError($e->getMessage());
return false;
}
}
public static function detail(array $params): array
{
return CommunityCategory::findOrEmpty($params['id'])->toArray();
}
public static function all(): array
{
return CommunityCategory::order('sort', 'desc')
->order('id', 'desc')
->select()
->toArray();
}
}
@@ -3,9 +3,11 @@
namespace app\adminapi\logic\community;
use app\common\logic\BaseLogic;
use app\common\model\community\CommunityCategory;
use app\common\model\community\CommunityPost;
use app\common\service\ai\KbSyncService;
use app\common\model\user\User;
use app\common\service\ai\KbSyncService;
use think\facade\Db;
class CommunityPostLogic extends BaseLogic
{
@@ -21,6 +23,9 @@ class CommunityPostLogic extends BaseLogic
$user = User::field('nickname,avatar')->findOrEmpty($post['user_id'] ?? 0)->toArray();
$post['nickname'] = $user['nickname'] ?? '-';
$post['avatar'] = $user['avatar'] ?? '';
$post['category_name'] = (int) ($post['category_id'] ?? 0) > 0
? (string) CommunityCategory::where('id', $post['category_id'])->value('name')
: '';
return $post;
}
@@ -87,9 +92,65 @@ class CommunityPostLogic extends BaseLogic
}
}
public static function setTopic(array $params): bool
{
try {
CommunityPost::update([
'id' => $params['id'],
'is_topic' => $params['is_topic'],
]);
return true;
} catch (\Exception $e) {
self::setError($e->getMessage());
return false;
}
}
public static function setCategory(array $params): bool
{
$post = CommunityPost::findOrEmpty($params['id']);
if ($post->isEmpty()) {
self::setError('帖子不存在');
return false;
}
$oldCategoryId = (int) $post->category_id;
$newCategoryId = (int) $params['category_id'];
if ($oldCategoryId === $newCategoryId) {
return true;
}
Db::startTrans();
try {
$post->category_id = $newCategoryId;
$post->save();
self::refreshCategoryCount($oldCategoryId);
self::refreshCategoryCount($newCategoryId);
Db::commit();
return true;
} catch (\Exception $e) {
Db::rollback();
self::setError($e->getMessage());
return false;
}
}
public static function delete(array $params)
{
$categoryId = (int) CommunityPost::where('id', $params['id'])->value('category_id');
CommunityPost::destroy($params['id']);
self::refreshCategoryCount($categoryId);
KbSyncService::enqueue('post', 'post', (int) $params['id'], 'delete', 20);
}
private static function refreshCategoryCount(int $categoryId): void
{
if ($categoryId <= 0) {
return;
}
CommunityCategory::where('id', $categoryId)->update([
'post_count' => CommunityPost::where('category_id', $categoryId)->count(),
'update_time' => time(),
]);
}
}
@@ -0,0 +1,64 @@
<?php
namespace app\adminapi\validate\community;
use app\common\model\community\CommunityCategory;
use app\common\validate\BaseValidate;
class CommunityCategoryValidate extends BaseValidate
{
protected $rule = [
'id' => 'require|checkCategory',
'name' => 'require|length:1,50|checkNameUnique',
'icon' => 'max:500',
'sort' => 'integer|egt:0',
'status' => 'in:0,1',
];
protected $message = [
'id.require' => '分类id不能为空',
'name.require' => '分类名称不能为空',
'name.length' => '分类名称长度须在1-50位字符',
'icon.max' => '分类图标地址不能超过500个字符',
'sort.integer' => '排序值必须为整数',
'sort.egt' => '排序值不能小于0',
'status.in' => '分类状态值错误',
];
public function sceneAdd()
{
return $this->remove('id', 'require|checkCategory');
}
public function sceneEdit()
{
}
public function sceneDetail()
{
return $this->only(['id']);
}
public function sceneDelete()
{
return $this->only(['id']);
}
public function checkCategory($value)
{
$category = CommunityCategory::findOrEmpty($value);
if ($category->isEmpty()) {
return '分类不存在';
}
return true;
}
public function checkNameUnique($value, $rule, $data)
{
$query = CommunityCategory::where('name', trim((string) $value));
if (!empty($data['id'])) {
$query->where('id', '<>', (int) $data['id']);
}
return $query->count() > 0 ? '分类名称已存在' : true;
}
}
@@ -3,6 +3,7 @@
namespace app\adminapi\validate\community;
use app\common\validate\BaseValidate;
use app\common\model\community\CommunityCategory;
use app\common\model\community\CommunityPost;
class CommunityPostValidate extends BaseValidate
@@ -13,6 +14,8 @@ class CommunityPostValidate extends BaseValidate
'is_top' => 'require|in:0,1',
'is_hot' => 'require|in:0,1',
'is_recommend' => 'require|in:0,1',
'is_topic' => 'require|in:0,1',
'category_id' => 'require|integer|egt:0|checkCategory',
];
protected $message = [
@@ -50,6 +53,16 @@ class CommunityPostValidate extends BaseValidate
return $this->only(['id', 'is_recommend']);
}
public function sceneTopic()
{
return $this->only(['id', 'is_topic']);
}
public function sceneCategory()
{
return $this->only(['id', 'category_id']);
}
public function checkPost($value)
{
$post = CommunityPost::findOrEmpty($value);
@@ -58,4 +71,16 @@ class CommunityPostValidate extends BaseValidate
}
return true;
}
public function checkCategory($value)
{
if ((int) $value === 0) {
return true;
}
$category = CommunityCategory::where('id', $value)->where('status', 1)->findOrEmpty();
if ($category->isEmpty()) {
return '帖子分类不存在或已禁用';
}
return true;
}
}
@@ -3,6 +3,7 @@
namespace app\api\lists\community;
use app\api\lists\BaseApiDataLists;
use app\common\model\community\CommunityCategory;
use app\common\model\community\CommunityPost;
use app\common\model\community\CommunityFollow;
use app\common\model\community\CommunityTag;
@@ -61,6 +62,11 @@ class CommunityPostLists extends BaseApiDataLists
$where[] = ['id', '=', 0];
}
}
foreach (['is_recommend', 'is_hot', 'is_topic', 'category_id'] as $field) {
if (isset($this->params[$field]) && $this->params[$field] !== '') {
$where[] = [$field, '=', (int) $this->params[$field]];
}
}
return $where;
}
@@ -88,7 +94,7 @@ class CommunityPostLists extends BaseApiDataLists
public function lists(): array
{
$list = $this->buildQuery()
->field('id,origin_id,user_id,content,images,ext,post_type,match_id,is_paid,price_points,paid_count,view_count,like_count,comment_count,share_count,is_top,is_hot,is_recommend,create_time')
->field('id,origin_id,user_id,content,images,ext,post_type,category_id,match_id,is_paid,price_points,paid_count,view_count,like_count,comment_count,share_count,is_top,is_hot,is_recommend,is_topic,create_time')
->orderRaw('is_top DESC, is_hot DESC, create_time DESC')
->limit($this->limitOffset, $this->limitLength)
->select()
@@ -107,6 +113,11 @@ class CommunityPostLists extends BaseApiDataLists
}
}
$categoryIds = array_values(array_filter(array_unique(array_column($list, 'category_id'))));
$categoryNames = $categoryIds
? CommunityCategory::whereIn('id', $categoryIds)->column('name', 'id')
: [];
// 批量查询帖子标签 → post_id => [tag_name, ...]
$tagMap = [];
if ($postIds) {
@@ -166,6 +177,7 @@ class CommunityPostLists extends BaseApiDataLists
$item['user'] = $userMap[$item['user_id']] ?? null;
$item['tags'] = $tagMap[$item['id']] ?? [];
$item['category_name'] = $categoryNames[$item['category_id']] ?? '';
$item['source_url'] = $ext['url'] ?? '';
$item['is_liked'] = false;
if ($this->userId) {
@@ -0,0 +1,21 @@
<?php
namespace app\common\model\community;
use app\common\model\BaseModel;
use app\common\service\FileService;
class CommunityCategory extends BaseModel
{
protected $name = 'community_post_category';
public function getIconAttr($value): string
{
return trim((string) $value) ? FileService::getFileUrl((string) $value) : '';
}
public function setIconAttr($value): string
{
return trim((string) $value) ? FileService::setFileUrl($value) : '';
}
}
@@ -26,4 +26,9 @@ class CommunityPost extends BaseModel
{
return $this->belongsToMany(CommunityTag::class, 'la_community_post_tag', 'tag_id', 'post_id');
}
public function category()
{
return $this->belongsTo(CommunityCategory::class, 'category_id');
}
}