Skip to content

第四章 架构设计 - 分层架构详解

4.1 分层架构概述

项目采用经典的五层架构设计,各层职责明确,依赖关系清晰:

+------------------+
|    View Layer    |  <-- 视图层:接收请求,返回响应
+------------------+
          |
          v
+------------------+
|  Service Layer   |  <-- 服务层:业务逻辑处理
+------------------+
          |
          v
+------------------+
|   Form Layer     |  <-- 表单层:数据验证
+------------------+
          |
          v
+------------------+
|   Model Layer    |  <-- 模型层:数据库操作
+------------------+
          |
          v
+------------------+
|    URL Layer     |  <-- 路由层:URL映射
+------------------+

4.2 模型层 (Model Layer)

4.2.1 BaseModel 抽象基类

所有业务模型继承自 BaseModelapplication/models.py),自动获得 6 个公共字段:

python
class BaseModel(models.Model):
    """基础模型抽象类"""

    # 主键ID字段
    id = models.AutoField(
        auto_created=True,
        primary_key=True,
        serialize=False,
        verbose_name='主键ID',
        db_comment='主键ID'
    )

    # 创建人字段
    create_user = models.CharField(
        null=True,
        max_length=50,
        default=None,
        verbose_name='创建人',
        db_comment='创建人'
    )

    # 创建时间字段
    create_time = models.DateTimeField(
        null=True,
        auto_now_add=True,
        verbose_name="创建时间",
        db_comment='创建时间'
    )

    # 更新人字段
    update_user = models.CharField(
        null=True,
        max_length=50,
        default=None,
        verbose_name='更新人',
        db_comment='更新人'
    )

    # 更新时间字段
    update_time = models.DateTimeField(
        null=True,
        auto_now=True,
        verbose_name="更新时间",
        db_comment='更新时间'
    )

    # 逻辑删除标识
    is_delete = models.BooleanField(
        default=0,
        db_default=0,
        verbose_name="逻辑删除",
        db_comment='逻辑删除:0-正常 1-已删除'
    )

    class Meta:
        abstract = True  # 抽象模型,不创建数据库表

4.2.2 业务模型示例

以案例模型为例(application/example/models.py):

python
from application.models import BaseModel
from utils.common import get_table_name

class Example(BaseModel):
    """案例模型类"""

    name = models.CharField(
        null=False, max_length=100, db_index=True,
        verbose_name="案例名称", db_comment='案例名称'
    )

    avatar = models.CharField(
        null=True, blank=True, max_length=255,
        verbose_name="案例图片", db_comment='案例图片'
    )

    TYPE_CHOICES = ((1, "类型1"), (2, "类型2"), (3, "类型3"), (4, "类型4"))

    type = models.IntegerField(
        null=False, choices=TYPE_CHOICES,
        verbose_name="案例类型", db_comment='案例类型'
    )

    STATUS_CHOICES = ((1, "正常"), (2, "禁用"))

    status = models.IntegerField(
        null=False, choices=STATUS_CHOICES,
        verbose_name="案例状态", db_comment='案例状态:1-正常 2-禁用'
    )

    sort = models.IntegerField(
        null=False, verbose_name="排序", db_comment='排序'
    )

    class Meta:
        db_table = get_table_name('example')  # 自动生成表名
        db_table_comment = "案例表"
        ordering = ("sort",)

4.2.3 字段顺序重排

通过 class_prepared 信号,自动将公共字段移到业务字段之后:

python
BASE_FIELD_NAMES = ['id', 'create_user', 'create_time',
                    'update_user', 'update_time', 'is_delete']

def _reorder_fields(sender, **kwargs):
    """信号处理函数:重排模型字段顺序"""
    if not hasattr(sender, '_meta') or sender._meta.abstract:
        return

    base_fields = []
    business_fields = []

    for field in sender._meta.local_fields:
        if field.name in BASE_FIELD_NAMES:
            base_fields.append(field)
        else:
            business_fields.append(field)

    # 重新排列:业务字段在前,公共字段在后
    sender._meta.local_fields = business_fields + base_fields

class_prepared.connect(_reorder_fields)

4.3 表单层 (Form Layer)

4.3.1 ModelForm 验证

表单层使用 Django 的 ModelForm 进行数据验证(application/example/forms.py):

python
from django import forms
from application.example import models

class ExampleForm(forms.ModelForm):
    """案例表单验证类"""

    name = forms.CharField(
        required=True, max_length=100,
        error_messages={
            'required': '案例名称不能为空',
            'max_length': '案例名称长度不得超过100个字符',
        }
    )

    type = forms.IntegerField(
        required=True, min_value=1, max_value=4,
        error_messages={
            'required': '案例类型不能为空',
            'min_value': '案例类型值不能小于1',
            'max_value': '案例类型值不能大于4',
        }
    )

    status = forms.IntegerField(
        required=True, min_value=1, max_value=2,
        error_messages={
            'required': '案例状态不能为空',
        }
    )

    sort = forms.IntegerField(
        required=True,
        error_messages={'required': '排序不能为空'}
    )

    class Meta:
        model = models.Example
        fields = ['name', 'avatar', 'type', 'status', 'sort']

4.3.2 验证错误处理

python
# 在Service层使用表单验证
form = ExampleForm(data)
if not form.is_valid():
    # 获取第一个错误信息
    return R.failed(msg=regular.get_err(form))

4.4 服务层 (Service Layer)

4.4.1 服务函数命名规范

服务层函数遵循统一的命名规范:

函数类型命名格式示例
分页查询get_<module>_pageget_example_page
列表查询get_<module>_listget_example_list
详情查询get_<module>_detailget_example_detail
添加add_<module>add_example
更新update_<module>update_example
删除delete_<module>sdelete_examples
状态更新update_<module>_statusupdate_example_status

4.4.2 分页查询实现

python
def get_example_page(request):
    """查询案例分页数据"""
    try:
        page_no = int(request.GET.get('pageNo', 1))
        page_size = int(request.GET.get('pageSize', PAGE_SIZE))

        # 构建基础查询(只查询未删除的记录)
        query = Example.objects.filter(is_delete=False)
        query = _apply_filters(query, request)
        query = query.order_by("sort")

        # 执行分页查询
        paginator = Paginator(query, page_size)
        page_list = paginator.page(page_no)

        # 构建并返回分页数据
        page_data = _build_page_data(page_list, paginator, page_no, page_size)
        return R.ok(data=page_data)
    except Exception as e:
        logging.error(f"查询案例分页数据异常: {str(e)}")
        return R.failed(msg="查询失败,请稍后重试")

4.4.3 添加操作实现

python
def add_example(request):
    """添加案例"""
    data, error = parse_request_body(request)
    if error:
        return R.failed(msg=error)

    form = ExampleForm(data)
    if not form.is_valid():
        return R.failed(msg=regular.get_err(form))

    try:
        cleaned_data = form.cleaned_data
        avatar = cleaned_data.get('avatar')
        if avatar:
            avatar = save_file(avatar, "example")

        Example.objects.create(
            name=cleaned_data.get('name'),
            avatar=avatar,
            type=cleaned_data.get('type'),
            status=cleaned_data.get('status'),
            sort=cleaned_data.get('sort'),
            create_user=get_username(request),
            update_user=get_username(request)
        )
        return R.ok(msg="创建成功")
    except Exception as e:
        logging.error(f"添加案例异常: {str(e)}")
        return R.failed(msg="添加失败,请稍后重试")

4.4.4 删除操作实现(软删除)

python
def delete_examples(instance_ids):
    """删除案例(批量删除)"""
    if not instance_ids:
        return R.failed("记录ID不存在")

    try:
        id_list = [int(id_str.strip())
                   for id_str in instance_ids.split(',') if id_str.strip()]

        # 批量更新删除标识(逻辑删除)
        updated_count = Example.objects.filter(
            id__in=id_list, is_delete=False
        ).update(is_delete=True)

        return R.ok(msg=f"本次共删除{updated_count}条数据")
    except ValueError:
        return R.failed("记录ID格式错误")

4.5 视图层 (View Layer)

4.5.1 视图类结构

视图层使用 Django 的类视图,结合装饰器和混入类实现认证和权限控制:

python
from django.utils.decorators import method_decorator
from django.views import View
from middleware.login_middleware import check_login
from middleware.permission_middleware import PermissionRequired

@method_decorator(check_login, name="get")
class ExamplePageView(PermissionRequired, View):
    """案例分页数据查询视图"""

    permission_required = ("sys:example:page",)

    @operation_log(title="案例管理-查询分页列表", log_type=LogType.QUERY)
    def get(self, request):
        result = services.get_example_page(request)
        return result

4.5.2 视图类职责

职责说明
请求接收接收 HTTP 请求,提取参数
认证检查通过 @check_login 装饰器验证登录状态
权限检查通过 PermissionRequired 混入类验证操作权限
参数校验简单的参数格式校验(如 ID 有效性)
服务调用调用 Service 层函数处理业务逻辑
响应返回返回 Service 层返回的响应结果

4.6 路由层 (URL Layer)

4.6.1 模块路由定义

每个模块在 urls.py 中定义自己的路由:

python
# application/example/urls.py
from django.urls import path
from application.example import views

urlpatterns = [
    path('page', views.ExamplePageView.as_view()),
    path('list', views.ExampleListView.as_view()),
    path('detail/<int:id>', views.ExampleDetailView.as_view()),
    path('add', views.ExampleAddView.as_view()),
    path('update', views.ExampleUpdateView.as_view()),
    path('delete/<str:id>', views.ExampleDeleteView.as_view()),
    path('status', views.ExampleStatusView.as_view()),
]

4.6.2 主路由注册

所有模块路由在主路由中注册(application/urls.py):

python
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('application.login.urls')),
    path('index/', include('application.index.urls')),
    path('example/', include('application.example.urls')),
    # ... 其他模块
]

4.7 数据流向

4.7.1 请求数据流

HTTP Request
    |
    v
View.get/post/put/delete
    |
    v
Service函数
    |
    v
parse_request_body(request)  -->  解析JSON请求体
    |
    v
Form验证  -->  数据校验
    |
    v
Model操作  -->  数据库读写
    |
    v
构建返回数据字典
    |
    v
R.ok()/R.failed()  -->  构造响应
    |
    v
JsonResponse  -->  HTTP Response

4.7.2 响应数据格式转换

python
# Model对象 -> 字典
record = {
    'id': item.id,
    'name': item.name,
    'avatar': get_file_url(item.avatar),
    'createUser': item.create_user,      # snake_case -> camelCase
    'createTime': format_datetime(item.create_time),
}

# 字典 -> JSON
return R.ok(data=record)
# {
#     "code": 0,
#     "data": {"id": 1, "name": "...", "createUser": "admin"},
#     "msg": "操作成功",
#     "ok": true
# }

小蚂蚁云团队 · 提供技术支持