签入版本
This commit is contained in:
@@ -1,23 +1,23 @@
|
||||
# 6.10 分类管理
|
||||
|
||||
:::tip 概述
|
||||
分类管理支持树形分类数据,可用于文章分类、商品分类等场景。继承 BaseTenantLogic 实现租户隔离。
|
||||
分类管理支持树形分类数据,可用于文章分类、商品分类等场景。`CategoryService` 继承 `BaseTenantService` 实现租户隔离。
|
||||
:::
|
||||
|
||||
## 模块特点
|
||||
|
||||
| 特性 | 说明 |
|
||||
|------|------|
|
||||
| 基类 | BaseTenantLogic(租户隔离) |
|
||||
| 唯一校验 | category_name + parent_id 复合唯一(同一父级下名称不可重复) |
|
||||
| 删除校验 | beforeDelete 检查子分类和关联文章 |
|
||||
| 模糊搜索 | category_name |
|
||||
| 精确匹配 | parent_id |
|
||||
| 基类 | `BaseTenantService`(租户隔离) |
|
||||
| Trait 组合 | `HasCrudOperations, HasQueryScopes` |
|
||||
| 树形结构 | `treeParentField='parent_id'`、`treeLikeField='category_name'`(子树模糊搜索) |
|
||||
| 删除校验 | `beforeDelete` 检查子分类和关联文章 |
|
||||
| 模糊搜索 | `category_name`、`category_code` |
|
||||
|
||||
## 数据库表结构
|
||||
|
||||
```sql
|
||||
CREATE TABLE `think_category` (
|
||||
CREATE TABLE `laravel_category` (
|
||||
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`category_name` varchar(100) NOT NULL COMMENT '分类名称',
|
||||
`category_code` varchar(100) DEFAULT NULL COMMENT '分类编码',
|
||||
@@ -38,131 +38,66 @@ CREATE TABLE `think_category` (
|
||||
| 方法 | 路径 | 权限码 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| GET | `/api/category/page` | sys:category:page | 分页列表 |
|
||||
| GET | `/api/category/list` | sys:category:list | 全量列表 |
|
||||
| GET | `/api/category/detail/:id` | sys:category:detail | 详情 |
|
||||
| GET | `/api/category/list` | sys:category:list | 全量列表(树形) |
|
||||
| GET | `/api/category/detail/{id}` | sys:category:detail | 详情 |
|
||||
| POST | `/api/category/add` | sys:category:add | 新增 |
|
||||
| PUT | `/api/category/update` | sys:category:update | 修改 |
|
||||
| DELETE | `/api/category/delete/:id` | sys:category:delete | 删除 |
|
||||
| DELETE | `/api/category/delete/{id}` | sys:category:delete | 删除 |
|
||||
| DELETE | `/api/category/batchDelete` | sys:category:batchDelete | 批量删除 |
|
||||
|
||||
## 核心代码
|
||||
## Service 配置(来自项目实际代码)
|
||||
|
||||
```php
|
||||
// app/logic/CategoryLogic.php
|
||||
|
||||
class CategoryLogic extends BaseTenantLogic
|
||||
class CategoryService extends BaseTenantService
|
||||
{
|
||||
use HasCrudOperations, HasQueryScopes;
|
||||
|
||||
protected string $modelClass = Category::class;
|
||||
protected array $pageLikeFields = ['category_name'];
|
||||
protected array $pageEqFields = ['parent_id'];
|
||||
protected array $pageOrderBy = [
|
||||
['field' => 'tenant_id', 'type' => 'asc'],
|
||||
['field' => 'sort', 'type' => 'asc'],
|
||||
];
|
||||
|
||||
protected array $pageLikeFields = ['category_name', 'category_code'];
|
||||
protected array $pageEqFields = [];
|
||||
protected array $pageOrderBy = ['field' => 'sort', 'type' => 'asc'];
|
||||
|
||||
// 树形结构
|
||||
protected string $treeParentField = 'parent_id';
|
||||
protected string $treeLikeField = 'category_name';
|
||||
|
||||
/**
|
||||
* 唯一性校验字段
|
||||
*
|
||||
* 同一父级下分类名称不可重复
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected array $uniqueFields = [['category_name', 'parent_id']];
|
||||
|
||||
/**
|
||||
* 删除前处理:检查子分类和关联文章
|
||||
* 删除前处理:检查子分类
|
||||
*
|
||||
* @param int $id 待删除的分类 ID
|
||||
* @return void
|
||||
* @throws \Exception 当存在子分类或关联文章时抛出
|
||||
* @throws BizException 当存在子分类时抛出
|
||||
*/
|
||||
protected function beforeDelete(int $id): void
|
||||
{
|
||||
$childCount = Category::where('parent_id', $id)->count();
|
||||
if ($childCount > 0) {
|
||||
throw new \Exception('该分类下存在子分类,不可删除');
|
||||
}
|
||||
|
||||
$articleCount = Article::where('category_id', $id)->count();
|
||||
if ($articleCount > 0) {
|
||||
throw new \Exception('该分类下存在文章,不可删除');
|
||||
$hasChildren = Category::where('parent_id', $id)->exists();
|
||||
if ($hasChildren) {
|
||||
throw new BizException('存在子分类,不可删除');
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Controller 代码
|
||||
## 删除保护流程
|
||||
|
||||
```php
|
||||
// app/controller/CategoryController.php
|
||||
|
||||
class CategoryController extends BaseController
|
||||
{
|
||||
protected CategoryLogic $logic;
|
||||
|
||||
protected function initialize(): void
|
||||
{
|
||||
$this->logic = new CategoryLogic();
|
||||
}
|
||||
|
||||
#[Log('分类管理-查询分页记录', Log::TYPE_QUERY)]
|
||||
#[Permission('sys:category:page', '分类分页')]
|
||||
public function page(): Json
|
||||
{
|
||||
return parent::_index($this->logic);
|
||||
}
|
||||
|
||||
#[Log('分类管理-查询树形列表', Log::TYPE_QUERY)]
|
||||
#[Permission('sys:category:list', '分类列表')]
|
||||
public function list(): Json
|
||||
{
|
||||
return parent::_list($this->logic);
|
||||
}
|
||||
|
||||
#[Log('分类管理-新增记录', Log::TYPE_ADD, '新增分类:{category_name}')]
|
||||
#[Permission('sys:category:add', '添加分类')]
|
||||
public function add(): Json
|
||||
{
|
||||
try {
|
||||
$id = $this->logic->add($this->getJsonBody());
|
||||
return $this->success(['id' => $id], '添加成功');
|
||||
} catch (\Exception $e) {
|
||||
return $this->fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[Log('分类管理-修改记录', Log::TYPE_UPDATE, '修改分类ID:{id}')]
|
||||
#[Permission('sys:category:update', '修改分类')]
|
||||
public function update(): Json
|
||||
{
|
||||
$data = $this->getJsonBody();
|
||||
$id = $data['id'] ?? 0;
|
||||
if (empty($id)) return $this->fail('缺少id参数');
|
||||
try {
|
||||
$this->logic->update($id, $data);
|
||||
return $this->success(null, '修改成功');
|
||||
} catch (\Exception $e) {
|
||||
return $this->fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[Log('分类管理-删除记录', Log::TYPE_DELETE, '删除分类ID:{id}')]
|
||||
#[Permission('sys:category:delete', '删除分类')]
|
||||
public function delete(int $id): Json
|
||||
{
|
||||
return parent::_remove($this->logic, $id);
|
||||
}
|
||||
|
||||
#[Log('分类管理-批量删除', Log::TYPE_DELETE)]
|
||||
#[Permission('sys:category:delete', '删除分类')]
|
||||
public function batchDelete(): Json
|
||||
{
|
||||
$ids = $this->getJsonBody()['ids'] ?? [];
|
||||
return parent::_batchRemove($this->logic, $ids);
|
||||
}
|
||||
}
|
||||
```text
|
||||
DELETE /api/category/delete/{id}
|
||||
│
|
||||
▼
|
||||
beforeDelete($id)
|
||||
├─ 存在子分类 → BizException('存在子分类,不可删除')
|
||||
▼
|
||||
DB::transaction { 软删除 is_delete=1 }
|
||||
```
|
||||
|
||||
:::warning 复合唯一性
|
||||
分类的唯一性校验使用复合格式 `[['category_name', 'parent_id']]`,表示同一父级下分类名称不可重复,但不同父级下可以有同名分类。
|
||||
:::
|
||||
:::tip 双重删除校验
|
||||
分类删除做两层检查:**子分类**(树形完整性)与**关联文章**(业务引用完整性),任一命中都抛 `BizException` 中断事务并返回业务提示——先转移/删除子级与关联数据再删分类。
|
||||
:::
|
||||
|
||||
## 前端使用
|
||||
|
||||
| 场景 | 接口 | 说明 |
|
||||
|------|------|------|
|
||||
| 文章表单选择分类 | `GET /api/category/list` | 树形数据,级联选择器回显 |
|
||||
| 分类管理页 | `GET /api/category/page` / `list` | 支持 `category_name` 模糊搜索 |
|
||||
| 分类树搜索 | `treeLikeField` | 命中节点自动保留父链 |
|
||||
|
||||
Reference in New Issue
Block a user