返回博客
2026年4月15日1 阅读4 分钟阅读开发者

体系架构问题

ERP 的组织模型现状已经非常接近你的设计了!现在有了完整信息,来给出最终评估。 ✅ 好消息:架构主体已完成 80% 扫描完现有模型,发现你的设计基本已经落地,只是有几处细节需要补齐: code复制a...

ERP 的组织模型现状已经非常接近你的设计了!现在有了完整信息,来给出最终评估。 ✅ 好消息:架构主体已完成 80% 扫描完现有模型,发现你的设计基本已经落地,只是有几处细节需要补齐: code复制accounts.Tenant └── organization.Company ✅ 已有,company FK → Company │ └── accounts.Store ✅ 已有,company FK(可空)→ Company └── accounts.Store ✅ 直属租户(company=NULL)→ 直接归属租户 └── organization.Department ⚠️ 缺失 store FK(Department 现在挂在 Company 下) 现状对比你要的设计

模型 你的设计 ERP 现状 状态

Tenant(租户) ✅ ✅ accounts.Tenant 完成

Company(公司) ✅ ✅ organization.Company(继承 TenantBaseModel) 完成

Store.company FK 可空 ✅ ✅ accounts.Store.company(null=True, blank=True) 完成

Store.is_company_owned() ✅ ✅ 已有实现 完成

Store.is_tenant_owned() ✅ ✅ 已有实现 完成

Department ✅ ⚠️ 有,但挂在 Company 下,无 store FK 需补

User → Company/Store/Department ✅ ⚠️ 只有 default_tenant,无 store_id 需补

UserRole 绑定租户/公司 ✅ ✅ 已有 完成

🔴 需要补齐的设计

  1. Department → 新增 store FK(最关键的改动) 当前 Department 的归属只有 Company 层级,缺少 Store 层级: python复制# organization/models/department.py

class Department(TenantBaseModel): # … 现有字段 …

# 归属层级:二选一
company = models.ForeignKey(
    'organization.Company',
    on_delete=models.CASCADE,
    null=True, blank=True,
    related_name='departments',
    verbose_name='所属公司'
)

# ← 新增:门店级部门(门店是公司的子级,所以 Dept 的 store 必须属于 company)
store = models.ForeignKey(
    'accounts.Store',
    on_delete=models.CASCADE,
    null=True, blank=True,
    related_name='departments',
    verbose_name='所属门店'
)

def clean(self):
    super().clean()
    # 验证:若选了 store,则 store 必须属于同一个 company
    if self.store and self.store.company:
        if self.company and self.store.company_id != self.company_id:
            raise ValidationError("门店必须属于所选公司")
        # 自动用 store 的 company
        self.company = self.store.company
    # 若选了 company 但 store 为空 → 公司级部门
    # 若 store 不为空 → 门店级部门
  1. User 模型 → 新增 store FK 和 default_company python复制# users/models/user.py

class User(AbstractBaseUser): # … 现有字段 …

# 归属字段
default_tenant = models.ForeignKey(...)  # 已有
default_company = models.ForeignKey(...)  # ← 新增
default_store = models.ForeignKey(...)    # ← 新增(关键)

# 当前上下文(切换时用)
current_company_id = models.IntegerField(null=True)  # ← 新增
# data_scope 已有
  1. UserRole 绑定扩展 当前 UserRole 只绑定 tenant_id,需要扩展支持 company_id / store_id: python复制# users/models/user_role.py

class UserRole(models.Model): user = ForeignKey(User) role = ForeignKey(Role)

# 绑定层级:三选一
tenant_id = ForeignKey('accounts.Tenant', ...)   # 已有
company_id = ForeignKey('organization.Company', null=True, ...)  # ← 新增
store_id = ForeignKey('accounts.Store', null=True, ...)           # ← 新增

class Meta:
    constraints = [
        # 确保只有一个层级被填充
        models.CheckConstraint(
            check=(
                Q(tenant_id__isnull=False, company_id__isnull=True, store_id__isnull=True) |
                Q(tenant_id__isnull=True, company_id__isnull=False, store_id__isnull=True) |
                Q(tenant_id__isnull=True, company_id__isnull=True, store_id__isnull=False)
            ),
            name='userrole_single_level'
        )
    ]

⚠️ 一个需要决策的设计点 Department 的双层归属如何保证一致性? 你希望 Department 有两个归属路径:

公司级部门(company=某公司, store=NULL) 门店级部门(store=某门店,company 自动推导)

这在业务上是合理的,但在数据模型上有歧义风险: code复制门店A 属于 公司B 门店A 下有"前台组"(department)

→ 前台组的 company 字段是填"公司B"还是空?

方案 做法 优点 缺点

A(推荐) store FK 非空时 company 自动推导(写 clean()),前台不展示 company 字段 数据一致,无歧义 需前端配合,store 级部门不能脱离 store 存在

B 两者都填,显式维护 查询快 容易不一致

C 去掉 company FK,只保留 store FK 最简洁 无法表达"公司级部门"

建议采用方案A,但需要确认:你们是否有"公司级部门"这个概念(比如"集团行政部"不属于任何门店)?如果有,则需要额外考虑。 ✅ 架构整体评价 你的设计与 ERP 现状匹配度:★★★★☆(4/5)

评估维度 评分 说明

多层级完整性 ⭐⭐⭐⭐ 四级(系统/租户/公司/门店)已有,部门归属需补

可空设计合理性 ⭐⭐⭐⭐⭐ Store.company 可空已完美实现

权限粒度 ⭐⭐⭐⭐ UserRole 已有租户绑定,补公司/门店绑定即可

模型一致性 ⭐⭐⭐⭐ 主要模型已继承 TenantBaseModel

业务贴合度 ⭐⭐⭐⭐⭐ 完全贴合"集团连锁 ERP"场景

核心结论:这个架构设计是 Best Practice,ERP 现状已经落地了大部分。 剩余的主要工作:

给 Department 加 store FK(核心改动) 给 User 加 store_id + default_company 扩展 UserRole 支持公司/门店级绑定