1
0
forked from erp-dev/erp

feat: added frontend_pages models(basic_info) and api(v2), support employees query pages that they can visit by themself token

This commit is contained in:
2026-01-07 14:28:17 +08:00
parent 941f797e35
commit 4566c2a2c8
14 changed files with 1079 additions and 61 deletions

View File

@@ -1,8 +1,13 @@
from io import StringIO
from django.core.management import call_command
from django.test import TestCase
from django.core.exceptions import ValidationError
from .models import (
Merchant, MerchantTypeEnum, WareHouse, WarehouseTypeEnum,
WareHouseModeEnum, EmployeeType, Employee, EmployeeStatusEnum
WareHouseModeEnum, EmployeeType, Employee, EmployeeStatusEnum,
FrontendPage, FrontendPageTypeEnum
)
@@ -164,3 +169,211 @@ class EmployeeTypeTestCase(TestCase):
self.assertEqual(emp1.job_type, '打纸工')
self.assertEqual(emp2.job_type, '打纸工')
self.assertEqual(emp_type.employees.count(), 2)
class FrontendPageTestCase(TestCase):
"""测试前端页面模型"""
def test_create_main_menu(self):
"""测试创建主菜单"""
page = FrontendPage.objects.create(
key='plate-opening',
label='开版管理',
page_type=FrontendPageTypeEnum.MAIN,
sort_order=0
)
self.assertEqual(page.key, 'plate-opening')
self.assertEqual(page.label, '开版管理')
self.assertEqual(page.page_type, FrontendPageTypeEnum.MAIN)
self.assertIsNone(page.parent)
self.assertTrue(page.is_active)
self.assertEqual(str(page), '开版管理 (plate-opening)')
def test_create_sub_menu_with_parent(self):
"""测试创建子菜单并关联父级"""
parent = FrontendPage.objects.create(
key='plate-opening',
label='开版管理',
page_type=FrontendPageTypeEnum.MAIN
)
child = FrontendPage.objects.create(
key='plate-order',
label='开版订单',
page_type=FrontendPageTypeEnum.SUB,
parent=parent
)
self.assertEqual(child.parent, parent)
self.assertEqual(parent.children.count(), 1)
self.assertEqual(parent.children.first(), child)
def test_key_unique_constraint(self):
"""测试 key 字段唯一性约束"""
FrontendPage.objects.create(key='test-page', label='测试页面')
with self.assertRaises(Exception):
FrontendPage.objects.create(key='test-page', label='重复页面')
def test_cascade_delete(self):
"""测试删除父级时子级也被删除"""
parent = FrontendPage.objects.create(
key='parent',
label='父级',
page_type=FrontendPageTypeEnum.MAIN
)
FrontendPage.objects.create(
key='child1',
label='子级1',
parent=parent
)
FrontendPage.objects.create(
key='child2',
label='子级2',
parent=parent
)
self.assertEqual(FrontendPage.objects.count(), 3)
parent.delete()
self.assertEqual(FrontendPage.objects.count(), 0)
class EmployeeTypeVisiblePagesTestCase(TestCase):
"""测试员工职位类型的可见页面功能"""
def setUp(self):
"""设置测试数据"""
self.merchant = Merchant.objects.create(
name='测试商户',
type=MerchantTypeEnum.STORE
)
# 创建页面
self.main_menu = FrontendPage.objects.create(
key='production',
label='生产管理',
page_type=FrontendPageTypeEnum.MAIN
)
self.sub_menu1 = FrontendPage.objects.create(
key='production-kanban',
label='生产看板',
page_type=FrontendPageTypeEnum.SUB,
parent=self.main_menu
)
self.sub_menu2 = FrontendPage.objects.create(
key='printing-order-list',
label='生产订单',
page_type=FrontendPageTypeEnum.SUB,
parent=self.main_menu
)
# 创建岗位
self.emp_type = EmployeeType.objects.create(
merchant=self.merchant,
title='生产主管'
)
def test_assign_visible_pages(self):
"""测试为岗位分配可见页面"""
self.emp_type.visible_pages.add(self.main_menu)
self.assertEqual(self.emp_type.visible_pages.count(), 1)
self.assertIn(self.main_menu, self.emp_type.visible_pages.all())
def test_get_visible_page_keys_with_main_menu(self):
"""测试授予主菜单时获取所有可见页面(含子菜单)"""
self.emp_type.visible_pages.add(self.main_menu)
# 模拟查询逻辑
visible_keys = set()
for page in self.emp_type.visible_pages.filter(is_active=True):
visible_keys.add(page.key)
if page.page_type == FrontendPageTypeEnum.MAIN:
children_keys = page.children.filter(is_active=True).values_list('key', flat=True)
visible_keys.update(children_keys)
self.assertIn('production', visible_keys)
self.assertIn('production-kanban', visible_keys)
self.assertIn('printing-order-list', visible_keys)
self.assertEqual(len(visible_keys), 3)
def test_get_visible_page_keys_with_sub_menu_only(self):
"""测试仅授予子菜单时只能看到该子菜单"""
self.emp_type.visible_pages.add(self.sub_menu1)
visible_keys = set()
for page in self.emp_type.visible_pages.filter(is_active=True):
visible_keys.add(page.key)
if page.page_type == FrontendPageTypeEnum.MAIN:
children_keys = page.children.filter(is_active=True).values_list('key', flat=True)
visible_keys.update(children_keys)
self.assertIn('production-kanban', visible_keys)
self.assertNotIn('production', visible_keys)
self.assertNotIn('printing-order-list', visible_keys)
self.assertEqual(len(visible_keys), 1)
def test_employee_visible_pages_through_position(self):
"""测试通过员工岗位查询可见页面"""
self.emp_type.visible_pages.add(self.main_menu)
employee = Employee.objects.create(
merchant=self.merchant,
name='测试员工',
position=self.emp_type
)
# 通过员工 -> 岗位 -> 可见页面
visible_pages = employee.position.visible_pages.all()
self.assertEqual(visible_pages.count(), 1)
self.assertIn(self.main_menu, visible_pages)
class SyncFrontendPagesCommandTestCase(TestCase):
"""测试 sync_frontend_pages 管理命令"""
def test_sync_command_dry_run(self):
"""测试 dry-run 模式不写入数据"""
out = StringIO()
call_command('sync_frontend_pages', '--dry-run', stdout=out)
# 验证没有实际写入
self.assertEqual(FrontendPage.objects.count(), 0)
self.assertIn('DRY-RUN', out.getvalue())
def test_sync_command_creates_pages(self):
"""测试命令创建页面数据"""
out = StringIO()
call_command('sync_frontend_pages', stdout=out)
# 验证创建了页面
self.assertGreater(FrontendPage.objects.count(), 0)
# 验证主菜单
main_menus = FrontendPage.objects.filter(page_type=FrontendPageTypeEnum.MAIN)
self.assertEqual(main_menus.count(), 9) # 9 个主菜单
# 验证子菜单
sub_menus = FrontendPage.objects.filter(page_type=FrontendPageTypeEnum.SUB)
self.assertEqual(sub_menus.count(), 43) # 43 个子菜单
# 验证父子关系
plate_opening = FrontendPage.objects.get(key='plate-opening')
self.assertEqual(plate_opening.children.count(), 5)
def test_sync_command_idempotent(self):
"""测试命令幂等性(多次运行结果一致)"""
call_command('sync_frontend_pages', stdout=StringIO())
first_count = FrontendPage.objects.count()
call_command('sync_frontend_pages', stdout=StringIO())
second_count = FrontendPage.objects.count()
self.assertEqual(first_count, second_count)
def test_sync_command_clear_option(self):
"""测试 --clear 选项"""
# 先创建数据
call_command('sync_frontend_pages', stdout=StringIO())
self.assertGreater(FrontendPage.objects.count(), 0)
# 使用 --clear 重新同步
out = StringIO()
call_command('sync_frontend_pages', '--clear', stdout=out)
self.assertIn('已清空', out.getvalue())
# 数据应该重新创建
self.assertEqual(FrontendPage.objects.count(), 52) # 9 主菜单 + 43 子菜单