feat: add Organization model with tree structure
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
c3466f4866
commit
7e089bd5ec
|
|
@ -0,0 +1,32 @@
|
|||
# Generated by Django 4.2.20 on 2026-03-24 09:34
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Organization',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=100, verbose_name='公司名称')),
|
||||
('logo', models.ImageField(blank=True, null=True, upload_to='org_logos/')),
|
||||
('description', models.TextField(blank=True, verbose_name='公司简介')),
|
||||
('email', models.EmailField(blank=True, max_length=254, verbose_name='联系邮箱')),
|
||||
('is_active', models.BooleanField(default=True)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='children', to='organizations.organization', verbose_name='上级公司')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '组织架构',
|
||||
'verbose_name_plural': '组织架构',
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
@ -2,12 +2,23 @@ from django.db import models
|
|||
|
||||
|
||||
class Organization(models.Model):
|
||||
name = models.CharField(max_length=255)
|
||||
name = models.CharField(max_length=100, verbose_name='公司名称')
|
||||
parent = models.ForeignKey(
|
||||
'self', null=True, blank=True,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='children',
|
||||
verbose_name='上级公司'
|
||||
)
|
||||
logo = models.ImageField(upload_to='org_logos/', null=True, blank=True)
|
||||
description = models.TextField(blank=True, verbose_name='公司简介')
|
||||
email = models.EmailField(blank=True, verbose_name='联系邮箱')
|
||||
is_active = models.BooleanField(default=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
class Meta:
|
||||
app_label = 'organizations'
|
||||
verbose_name = '组织'
|
||||
verbose_name_plural = '组织'
|
||||
verbose_name = '组织架构'
|
||||
verbose_name_plural = '组织架构'
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
from rest_framework import serializers
|
||||
from .models import Organization
|
||||
|
||||
|
||||
class OrganizationSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Organization
|
||||
fields = ['id', 'name', 'parent', 'logo', 'description', 'email', 'is_active']
|
||||
|
||||
|
||||
class OrganizationTreeSerializer(serializers.ModelSerializer):
|
||||
"""带子公司列表,用于门户展示"""
|
||||
children = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = Organization
|
||||
fields = ['id', 'name', 'logo', 'description', 'email', 'children']
|
||||
|
||||
def get_children(self, obj):
|
||||
return OrganizationSerializer(
|
||||
obj.children.filter(is_active=True), many=True
|
||||
).data
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
import pytest
|
||||
from apps.organizations.models import Organization
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestOrganizationModel:
|
||||
def test_create_group(self):
|
||||
org = Organization.objects.create(name='示例集团', email='group@example.com')
|
||||
assert org.parent is None
|
||||
assert org.is_active is True
|
||||
|
||||
def test_create_subsidiary(self):
|
||||
parent = Organization.objects.create(name='示例集团', email='group@example.com')
|
||||
child = Organization.objects.create(
|
||||
name='子公司A', email='a@example.com', parent=parent
|
||||
)
|
||||
assert child.parent == parent
|
||||
|
||||
def test_list_subsidiaries(self):
|
||||
parent = Organization.objects.create(name='集团', email='g@example.com')
|
||||
Organization.objects.create(name='子A', email='a@example.com', parent=parent)
|
||||
Organization.objects.create(name='子B', email='b@example.com', parent=parent)
|
||||
assert parent.children.count() == 2
|
||||
|
|
@ -1,3 +1,9 @@
|
|||
from django.urls import path
|
||||
from django.urls import path, include
|
||||
from rest_framework.routers import DefaultRouter
|
||||
from .views import OrganizationPublicViewSet, OrganizationManageViewSet
|
||||
|
||||
urlpatterns = []
|
||||
router = DefaultRouter()
|
||||
router.register('public', OrganizationPublicViewSet, basename='org-public')
|
||||
router.register('manage', OrganizationManageViewSet, basename='org-manage')
|
||||
|
||||
urlpatterns = [path('', include(router.urls))]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
from rest_framework import viewsets
|
||||
from rest_framework.permissions import AllowAny
|
||||
from .models import Organization
|
||||
from .serializers import OrganizationSerializer, OrganizationTreeSerializer
|
||||
from apps.accounts.permissions import IsSuperAdmin
|
||||
|
||||
|
||||
class OrganizationPublicViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
"""公开只读:门户展示用"""
|
||||
queryset = Organization.objects.filter(is_active=True, parent__isnull=False)
|
||||
serializer_class = OrganizationSerializer
|
||||
permission_classes = [AllowAny]
|
||||
|
||||
|
||||
class OrganizationManageViewSet(viewsets.ModelViewSet):
|
||||
"""超管:完整增删改查"""
|
||||
queryset = Organization.objects.all()
|
||||
serializer_class = OrganizationSerializer
|
||||
permission_classes = [IsSuperAdmin]
|
||||
Loading…
Reference in New Issue