Files
djangoadmin-wiki/DjangoAdmin_Flask_EleVue/zh/volume4/business/dept.md
T
2026-09-12 23:12:15 +08:00

406 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
:::tip 部门/职级/岗位
部门、职级、岗位是组织架构管理的三个核心模块。部门采用树形结构,职级和岗位为简单 CRUD 模块,均继承 `BaseService`。用户表通过 `dept_id`、`position_id`、`level_id` 三个外键关联这三个模块。
:::
## 部门管理
### 部门模型
```python
# src/modules/system/dept/models.py
# ============================================================
# 部门模型
# ============================================================
class Dept(BaseModel, BaseDB):
"""部门模型类"""
# ============================================================
# 表名配置
# ============================================================
__tablename__ = DB_PREFIX + "dept"
__table_comment__ = "部门表"
# ============================================================
# 字段定义
# ============================================================
# 部门名称
name = Column(String(150), nullable=False, index=True, comment="部门名称")
# 部门类型:1-公司 2-子公司 3-部门 4-小组
type = Column(Integer, default=0, server_default=text('0'), comment="部门类型:1-公司 2-子公司 3-部门 4-小组")
# 上级部门ID
parent_id = Column(Integer, default=0, server_default=text('0'), index=True, comment="上级部门ID")
# 部门排序
sort = Column(Integer, default=0, server_default=text('0'), comment="部门排序")
# 备注
note = Column(String(255), nullable=True, comment="备注")
# ============================================================
# 内置方法
# ============================================================
def __str__(self):
"""返回部门名称作为字符串表示"""
return '部门{}'.format(self.name)
```
### 树形结构
部门通过 `parent_id` 字段形成树形结构,与菜单类似:
```
公司总部 (parent_id=0)
├── 技术部 (parent_id=1)
│ ├── 前端组 (parent_id=2)
│ └── 后端组 (parent_id=2)
└── 市场部 (parent_id=1)
```
### 删除前校验
部门删除时需检查是否存在下级部门或用户引用,避免组织架构悬空:
```python
# src/modules/system/dept/service.py
from src.common.base_service import BaseService
from src.core.exceptions import BizError
from src.modules.system.dept.repository import DeptRepository
from src.modules.system.user.repository import UserRepository
class DeptService(BaseService):
"""部门服务类"""
repository = DeptRepository
form = DeptForm
model_name = "部门"
@classmethod
def before_delete(cls, ids):
"""
删除前校验:检查下级部门和用户引用
Args:
ids: 待删除的部门ID列表
Raises:
BizError: 存在下级部门或用户引用时抛出
"""
for did in ids:
# 检查是否存在下级部门
if DeptRepository.count_children(did) > 0:
raise BizError(f"部门ID {did} 存在下级部门,请先删除下级部门")
# 检查是否存在用户引用
if UserRepository.count_by({'dept_id': did}) > 0:
raise BizError(f"部门ID {did} 已被用户引用,请先调整用户部门")
```
## 职级管理
### 职级模型
```python
# src/modules/system/level/models.py
# ============================================================
# 职级模型
# ============================================================
class Level(BaseModel, BaseDB):
"""职级模型类"""
# ============================================================
# 表名配置
# ============================================================
__tablename__ = DB_PREFIX + "level"
__table_comment__ = "职级表"
# ============================================================
# 字段定义
# ============================================================
# 职级名称
name = Column(String(255), nullable=False, index=True, comment="职级名称")
# 职级状态:1-在用 2-停用
status = Column(Integer, default=0, server_default=text('0'), index=True, comment="职级状态:1-在用 2-停用")
# 职级排序
sort = Column(Integer, default=0, server_default=text('0'), comment="职级排序")
# ============================================================
# 内置方法
# ============================================================
def __repr__(self):
"""返回职级名称作为字符串表示"""
return '职级:{}'.format(self.name)
```
### 表单验证
```python
# src/modules/system/level/schema.py
from wtforms import StringField, IntegerField
from wtforms.validators import DataRequired, Length, NumberRange, Optional
from src.common.forms.base_form import BaseForm
from src.modules.system.level.repository import LevelRepository
class LevelForm(BaseForm):
"""职级创建/编辑表单"""
name = StringField('职级名称', validators=[
DataRequired(message='职级名称不能为空'),
Length(max=150, message='职级名称长度不得超过150个字符')
])
status = IntegerField('职级状态', validators=[
DataRequired(message='职级状态不能为空'),
NumberRange(min=1, max=2, message='职级状态值在1~2之间')
])
sort = IntegerField('职级排序', validators=[
Optional(),
NumberRange(min=0, max=99999, message='职级排序值在0~99999之间')
])
def validate_name(self, field):
"""职级名称唯一性校验"""
if not field.data:
return
current_id = self.id.data if self.id.data else None
if LevelRepository.exists_by({'name': field.data}, exclude_id=current_id):
raise ValidationError("职级名称已存在")
class LevelStatusForm(BaseForm):
"""职级状态更新表单"""
id = IntegerField('职级ID', validators=[
DataRequired(message='职级ID不能为空'),
NumberRange(min=1, message='职级ID必须大于0')
])
status = IntegerField('职级状态', validators=[
DataRequired(message='职级状态不能为空'),
NumberRange(min=1, max=2, message='职级状态值在1~2之间')
])
```
### 业务服务
职级模块继承 `BaseService`,声明差异点即可获得完整 CRUD 能力。删除前校验用户引用,支持 Excel 导入导出:
```python
# src/modules/system/level/service.py
from src.common.base_service import BaseService
from src.core.exceptions import BizError
from src.modules.system.level.schema import LevelForm, LevelStatusForm
from src.modules.system.level.repository import LevelRepository
from src.modules.system.user.repository import UserRepository
class LevelService(BaseService):
"""职级服务类,继承 BaseService 获得标准 CRUD 能力"""
repository = LevelRepository
form = LevelForm
status_form = LevelStatusForm
model_name = "职级"
@classmethod
def before_delete(cls, ids):
"""删除前校验:存在用户引用该职级时禁止删除"""
for lid in ids:
if UserRepository.count_by({'level_id': lid}) > 0:
raise BizError(f"职级ID {lid} 已被用户引用,无法删除")
# 模块级代理函数
get_level_page = LevelService.get_page
get_level_detail = LevelService.get_detail
add_level = LevelService.add
update_level = LevelService.update
delete_levels = LevelService.remove
update_level_status = LevelService.update_status
get_level_list = LevelService.get_list
```
## 岗位管理
### 岗位模型
```python
# src/modules/system/position/models.py
# ============================================================
# 岗位模型
# ============================================================
class Position(BaseModel, BaseDB):
"""岗位模型类"""
# ============================================================
# 表名配置
# ============================================================
__tablename__ = DB_PREFIX + "position"
__table_comment__ = "岗位表"
# ============================================================
# 字段定义
# ============================================================
# 岗位名称
name = Column(String(255), nullable=False, index=True, comment="岗位名称")
# 岗位状态:1-在用 2-停用
status = Column(Integer, default=0, server_default=text('0'), index=True, comment="岗位状态:1-在用 2-停用")
# 岗位排序
sort = Column(Integer, default=0, server_default=text('0'), comment="岗位排序")
# ============================================================
# 内置方法
# ============================================================
def __repr__(self):
"""返回岗位名称作为字符串表示"""
return '岗位:{}'.format(self.name)
```
### 表单验证
```python
# src/modules/system/position/schema.py
from wtforms import StringField, IntegerField
from wtforms.validators import DataRequired, Length, NumberRange, Optional
from src.common.forms.base_form import BaseForm
from src.modules.system.position.repository import PositionRepository
class PositionForm(BaseForm):
"""岗位创建/编辑表单"""
name = StringField('岗位名称', validators=[
DataRequired(message='岗位名称不能为空'),
Length(max=150, message='岗位名称长度不得超过150个字符')
])
status = IntegerField('岗位状态', validators=[
DataRequired(message='岗位状态不能为空'),
NumberRange(min=1, max=2, message='岗位状态值在1~2之间')
])
sort = IntegerField('岗位排序', validators=[
Optional(),
NumberRange(min=0, max=99999, message='岗位排序值在0~99999之间')
])
def validate_name(self, field):
"""岗位名称唯一性校验"""
if not field.data:
return
current_id = self.id.data if self.id.data else None
if PositionRepository.exists_by({'name': field.data}, exclude_id=current_id):
raise ValidationError("岗位名称已存在")
class PositionStatusForm(BaseForm):
"""岗位状态更新表单"""
id = IntegerField('岗位ID', validators=[
DataRequired(message='岗位ID不能为空'),
NumberRange(min=1, message='岗位ID必须大于0')
])
status = IntegerField('岗位状态', validators=[
DataRequired(message='岗位状态不能为空'),
NumberRange(min=1, max=2, message='岗位状态值在1~2之间')
])
```
### 数据访问层
```python
# src/modules/system/position/repository.py
from src.common.base_repository import BaseRepository
from src.common.constants.common import PAGE_SIZE
from src.modules.system.position.models import Position
class PositionRepository(BaseRepository):
"""岗位数据仓储,继承 BaseRepository 获得通用 CRUD 能力"""
model = Position
@classmethod
def page(cls, params: dict):
"""分页查询"""
return cls.find_page(
page=int(params.get('pageNo', 1)),
page_size=int(params.get('pageSize', PAGE_SIZE)),
filters={'status': params.get('status')},
like_filters={'name': params.get('name')},
order_by='sort',
)
@classmethod
def list_enabled(cls, params=None):
"""查询启用的岗位列表"""
params = params or {}
return cls.find_list(
filters={'status': 1},
like_filters={'name': params.get('name')},
order_by='sort',
)
```
### 业务服务
```python
# src/modules/system/position/service.py
from src.common.base_service import BaseService
from src.core.exceptions import BizError
from src.modules.system.position.schema import PositionForm, PositionStatusForm
from src.modules.system.position.repository import PositionRepository
from src.modules.system.user.repository import UserRepository
class PositionService(BaseService):
"""岗位服务类,继承 BaseService 获得标准 CRUD 能力"""
repository = PositionRepository
form = PositionForm
status_form = PositionStatusForm
model_name = "岗位"
@classmethod
def before_delete(cls, ids):
"""删除前校验:存在用户引用该岗位时禁止删除"""
for pid in ids:
if UserRepository.count_by({'position_id': pid}) > 0:
raise BizError(f"岗位ID {pid} 已被用户引用,无法删除")
# 模块级代理函数
get_position_page = PositionService.get_page
get_position_detail = PositionService.get_detail
add_position = PositionService.add
update_position = PositionService.update
delete_positions = PositionService.remove
update_position_status = PositionService.update_status
get_position_list = PositionService.get_list
```
## 通用 CRUD 接口
职级和岗位模块继承 BaseService,自动生成标准接口:
| 接口 | 方法 | 权限节点 | 说明 |
|------|------|---------|------|
| `/api/v1/level/page` | GET | `sys:level:page` | 职级分页列表 |
| `/api/v1/level/list` | GET | `sys:level:list` | 职级列表(无分页) |
| `/api/v1/level/detail/{id}` | GET | `sys:level:detail` | 职级详情 |
| `/api/v1/level/add` | POST | `sys:level:add` | 新增职级 |
| `/api/v1/level/update` | PUT | `sys:level:update` | 编辑职级 |
| `/api/v1/level/delete/{id}` | DELETE | `sys:level:delete` | 删除职级 |
| `/api/v1/level/batchDelete` | DELETE | `sys:level:batchDelete` | 批量删除 |
| `/api/v1/level/status` | PUT | `sys:level:status` | 设置状态 |
| `/api/v1/level/import` | POST | `sys:level:import` | 导入 Excel |
| `/api/v1/level/export` | GET | `sys:level:export` | 导出 Excel |
| `/api/v1/position/page` | GET | `sys:position:page` | 岗位分页列表 |
| `/api/v1/position/list` | GET | `sys:position:list` | 岗位列表(无分页) |
| `/api/v1/position/detail/{id}` | GET | `sys:position:detail` | 岗位详情 |
| `/api/v1/position/add` | POST | `sys:position:add` | 新增岗位 |
| `/api/v1/position/update` | PUT | `sys:position:update` | 编辑岗位 |
| `/api/v1/position/delete/{id}` | DELETE | `sys:position:delete` | 删除岗位 |
| `/api/v1/position/batchDelete` | DELETE | `sys:position:batchDelete` | 批量删除 |
| `/api/v1/position/status` | PUT | `sys:position:status` | 设置状态 |
:::tip Excel 导入导出
职级模块支持 Excel 导入导出(`import_level` / `export_level`),导入时逐行校验并返回成功/失败条数。岗位模块结构相同,可按需添加导入导出功能。
:::
## 总结
组织架构模块具备以下特点:
```
1. 部门树形:parent_id 形成多级部门结构,删除前校验下级部门和用户引用
2. 职级/岗位:简单 CRUD,继承 BaseService,声明差异点即可
3. 表单验证:WTForms Field 约束(DataRequired/Length/NumberRange)
4. 数据仓库:BaseRepository 提供通用 CRUD 能力,模块级代理函数
5. 用户关联:用户表通过 dept_id、position_id、level_id 关联
6. 删除保护:before_delete 钩子防止引用悬空
```