1
0
forked from erp-dev/erp

fix: added pgBouncer for conn pool manage

This commit is contained in:
2025-12-24 21:26:31 +08:00
parent 3d3f00232d
commit c832e26907
13 changed files with 160926 additions and 31 deletions

View File

@@ -161,11 +161,15 @@ class DeviceInfoSerializer(BaseSerializer):
class VehicleTransportRecordSerializer(BaseSerializer):
vehicle_type_name = serializers.CharField(source='vehicle_type.name', read_only=True)
# 显式定义 vehicle_type 字段,避免 depth=1 影响反序列化
vehicle_type = serializers.PrimaryKeyRelatedField(
queryset=basic_models.VehicleType.objects.all()
)
class Meta:
model = basic_models.VehicleTransportRecord
fields = '__all__'
depth = 1 # 展开外键关系
depth = 1 # 展开外键关系(仅影响序列化输出)
class UserSerializerSimple(serializers.ModelSerializer):

View File

@@ -4,7 +4,9 @@ from rest_framework.test import APIClient
from rest_framework import status
from basic_info.models import (
Merchant, MerchantTypeEnum, WareHouse, WarehouseTypeEnum,
Employee, EmployeeType, EmployeeStatusEnum
Employee, EmployeeType, EmployeeStatusEnum,
QuickInput, Product, ProductCategory, Supplier, Customer,
VehicleType, BankAccount, DeviceInfo, VehicleTransportRecord
)
@@ -122,6 +124,31 @@ class WarehouseAPITestCase(TestCase):
self.assertEqual(response.data['type'], WarehouseTypeEnum.SCATTERED.value)
self.assertEqual(response.data['name'], '单个仓库')
def test_filter_warehouses_by_name(self):
"""测试通过 name 查询参数过滤仓库icontains 模糊匹配)"""
WareHouse.objects.create(merchant=self.merchant, name='主仓库')
WareHouse.objects.create(merchant=self.merchant, name='备用仓库')
WareHouse.objects.create(merchant=self.merchant, name='临时仓库')
# 测试部分匹配
response = self.client.get('/api/backend/warehouses/?name=主')
self.assertEqual(response.status_code, status.HTTP_200_OK)
results = response.data['results'] if isinstance(response.data, dict) else response.data
self.assertEqual(len(results), 1)
self.assertEqual(results[0]['name'], '主仓库')
# 测试包含"仓库"的所有记录
response = self.client.get('/api/backend/warehouses/?name=仓库')
self.assertEqual(response.status_code, status.HTTP_200_OK)
results = response.data['results'] if isinstance(response.data, dict) else response.data
self.assertEqual(len(results), 3)
# 测试不匹配
response = self.client.get('/api/backend/warehouses/?name=不存在')
self.assertEqual(response.status_code, status.HTTP_200_OK)
results = response.data['results'] if isinstance(response.data, dict) else response.data
self.assertEqual(len(results), 0)
class EmployeeTypeAPITestCase(TestCase):
"""测试员工职位类型 API"""
@@ -194,6 +221,31 @@ class EmployeeTypeAPITestCase(TestCase):
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
self.assertFalse(EmployeeType.objects.filter(id=emp_type.id).exists())
def test_filter_employee_types_by_title(self):
"""测试通过 title 查询参数过滤职位类型icontains 模糊匹配)"""
EmployeeType.objects.create(merchant=self.merchant, title='打纸工')
EmployeeType.objects.create(merchant=self.merchant, title='滚筒工')
EmployeeType.objects.create(merchant=self.merchant, title='仓库管理员')
# 测试部分匹配
response = self.client.get('/api/backend/employee-types/?title=打纸')
self.assertEqual(response.status_code, status.HTTP_200_OK)
results = response.data['results'] if isinstance(response.data, dict) else response.data
self.assertEqual(len(results), 1)
self.assertEqual(results[0]['title'], '打纸工')
# 测试包含"工"的所有记录
response = self.client.get('/api/backend/employee-types/?title=工')
self.assertEqual(response.status_code, status.HTTP_200_OK)
results = response.data['results'] if isinstance(response.data, dict) else response.data
self.assertEqual(len(results), 2) # 打纸工、滚筒工
# 测试不匹配
response = self.client.get('/api/backend/employee-types/?title=不存在')
self.assertEqual(response.status_code, status.HTTP_200_OK)
results = response.data['results'] if isinstance(response.data, dict) else response.data
self.assertEqual(len(results), 0)
class EmployeeAPITestCase(TestCase):
"""测试员工 API包含 job_type 兼容性)"""
@@ -304,6 +356,25 @@ class EmployeeAPITestCase(TestCase):
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['job_type'], '打纸工') # 应该保持不变
def test_filter_employees_by_name(self):
"""测试通过 name 查询参数过滤员工icontains 模糊匹配)"""
Employee.objects.create(merchant=self.merchant, name='张三')
Employee.objects.create(merchant=self.merchant, name='李四')
Employee.objects.create(merchant=self.merchant, name='王五')
# 测试部分匹配
response = self.client.get('/api/backend/employees/?name=三')
self.assertEqual(response.status_code, status.HTTP_200_OK)
results = response.data['results'] if isinstance(response.data, dict) else response.data
self.assertEqual(len(results), 1)
self.assertEqual(results[0]['name'], '张三')
# 测试不匹配
response = self.client.get('/api/backend/employees/?name=不存在')
self.assertEqual(response.status_code, status.HTTP_200_OK)
results = response.data['results'] if isinstance(response.data, dict) else response.data
self.assertEqual(len(results), 0)
class UserProfileAPITestCase(TestCase):
"""测试用户资料 API"""
@@ -456,3 +527,442 @@ class UserProfileAPITestCase(TestCase):
results = response.data['results'] if isinstance(response.data, dict) else response.data
self.assertEqual(len(results), 1) # 只应该返回当前商户的用户资料
self.assertEqual(results[0]['description'], '本商户用户资料')
# ==================== 补充缺失的 ViewSet 测试 ====================
class QuickInputAPITestCase(TestCase):
"""测试快捷输入 API支持 name 和 group 查询参数)"""
def setUp(self):
self.merchant = Merchant.objects.create(
name='测试商户',
type=MerchantTypeEnum.STORE
)
self.user = User.objects.create_user(
username='testuser',
password='testpass123'
)
self.employee = Employee.objects.create(
merchant=self.merchant,
sys_user=self.user,
name='测试员工'
)
self.client = APIClient()
self.client.force_authenticate(user=self.user)
def test_create_quick_input(self):
"""测试创建快捷输入"""
data = {
'name': '测试输入',
'value': '测试值',
'group': '测试组'
}
response = self.client.post('/api/backend/quick-inputs/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.data['name'], '测试输入')
self.assertEqual(response.data['group'], '测试组')
def test_filter_quick_inputs_by_name(self):
"""测试通过 name 查询参数过滤快捷输入"""
QuickInput.objects.create(name='输入1', value='值1', group='组1')
QuickInput.objects.create(name='输入2', value='值2', group='组1')
QuickInput.objects.create(name='输入3', value='值3', group='组2')
response = self.client.get('/api/backend/quick-inputs/?name=输入1')
self.assertEqual(response.status_code, status.HTTP_200_OK)
results = response.data['results'] if isinstance(response.data, dict) else response.data
self.assertEqual(len(results), 1)
self.assertEqual(results[0]['name'], '输入1')
def test_filter_quick_inputs_by_group(self):
"""测试通过 group 查询参数过滤快捷输入"""
QuickInput.objects.create(name='输入1', value='值1', group='组1')
QuickInput.objects.create(name='输入2', value='值2', group='组1')
QuickInput.objects.create(name='输入3', value='值3', group='组2')
response = self.client.get('/api/backend/quick-inputs/?group=组1')
self.assertEqual(response.status_code, status.HTTP_200_OK)
results = response.data['results'] if isinstance(response.data, dict) else response.data
self.assertEqual(len(results), 2)
for item in results:
self.assertEqual(item['group'], '组1')
def test_quick_input_groups_action(self):
"""测试获取所有分组"""
QuickInput.objects.create(name='输入1', value='值1', group='组1')
QuickInput.objects.create(name='输入2', value='值2', group='组1')
QuickInput.objects.create(name='输入3', value='值3', group='组2')
response = self.client.get('/api/backend/quick-inputs/groups/')
self.assertEqual(response.status_code, status.HTTP_200_OK)
groups = list(response.data)
self.assertEqual(len(groups), 2)
self.assertIn('组1', groups)
self.assertIn('组2', groups)
class ProductAPITestCase(TestCase):
"""测试产品 API支持 name 查询参数)"""
def setUp(self):
self.merchant = Merchant.objects.create(
name='测试商户',
type=MerchantTypeEnum.STORE
)
self.user = User.objects.create_user(
username='testuser',
password='testpass123'
)
self.employee = Employee.objects.create(
merchant=self.merchant,
sys_user=self.user,
name='测试员工'
)
self.category = ProductCategory.objects.create(
merchant=self.merchant,
name='测试类别',
product_prefix='TEST'
)
self.client = APIClient()
self.client.force_authenticate(user=self.user)
def test_create_product(self):
"""测试创建产品"""
data = {
'name': '测试产品',
'category': self.category.id
}
response = self.client.post('/api/backend/products/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.data['name'], '测试产品')
def test_filter_products_by_name(self):
"""测试通过 name 查询参数过滤产品"""
Product.objects.create(merchant=self.merchant, category=self.category, name='产品A')
Product.objects.create(merchant=self.merchant, category=self.category, name='产品B')
Product.objects.create(merchant=self.merchant, category=self.category, name='产品C')
response = self.client.get('/api/backend/products/?name=产品A')
self.assertEqual(response.status_code, status.HTTP_200_OK)
results = response.data['results'] if isinstance(response.data, dict) else response.data
self.assertEqual(len(results), 1)
self.assertEqual(results[0]['name'], '产品A')
class ProductCategoryAPITestCase(TestCase):
"""测试产品类别 API支持 name 查询参数)"""
def setUp(self):
self.merchant = Merchant.objects.create(
name='测试商户',
type=MerchantTypeEnum.STORE
)
self.user = User.objects.create_user(
username='testuser',
password='testpass123'
)
self.employee = Employee.objects.create(
merchant=self.merchant,
sys_user=self.user,
name='测试员工'
)
self.client = APIClient()
self.client.force_authenticate(user=self.user)
def test_create_product_category(self):
"""测试创建产品类别"""
data = {
'name': '测试类别',
'product_prefix': 'TEST'
}
response = self.client.post('/api/backend/product-categories/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.data['name'], '测试类别')
def test_filter_product_categories_by_name(self):
"""测试通过 name 查询参数过滤产品类别"""
ProductCategory.objects.create(merchant=self.merchant, name='类别A', product_prefix='A')
ProductCategory.objects.create(merchant=self.merchant, name='类别B', product_prefix='B')
response = self.client.get('/api/backend/product-categories/?name=类别A')
self.assertEqual(response.status_code, status.HTTP_200_OK)
results = response.data['results'] if isinstance(response.data, dict) else response.data
self.assertEqual(len(results), 1)
self.assertEqual(results[0]['name'], '类别A')
class SupplierAPITestCase(TestCase):
"""测试供应商 API支持 name 查询参数)"""
def setUp(self):
self.merchant = Merchant.objects.create(
name='测试商户',
type=MerchantTypeEnum.STORE
)
self.user = User.objects.create_user(
username='testuser',
password='testpass123'
)
self.employee = Employee.objects.create(
merchant=self.merchant,
sys_user=self.user,
name='测试员工'
)
self.client = APIClient()
self.client.force_authenticate(user=self.user)
def test_create_supplier(self):
"""测试创建供应商"""
data = {
'name': '测试供应商'
}
response = self.client.post('/api/backend/suppliers/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.data['name'], '测试供应商')
def test_filter_suppliers_by_name(self):
"""测试通过 name 查询参数过滤供应商"""
Supplier.objects.create(merchant=self.merchant, name='供应商A')
Supplier.objects.create(merchant=self.merchant, name='供应商B')
response = self.client.get('/api/backend/suppliers/?name=供应商A')
self.assertEqual(response.status_code, status.HTTP_200_OK)
results = response.data['results'] if isinstance(response.data, dict) else response.data
self.assertEqual(len(results), 1)
self.assertEqual(results[0]['name'], '供应商A')
class CustomerAPITestCase(TestCase):
"""测试客户 API支持 name 查询参数)"""
def setUp(self):
self.merchant = Merchant.objects.create(
name='测试商户',
type=MerchantTypeEnum.STORE
)
self.user = User.objects.create_user(
username='testuser',
password='testpass123'
)
self.employee = Employee.objects.create(
merchant=self.merchant,
sys_user=self.user,
name='测试员工'
)
# CustomerViewSet 使用 DjangoModelPermissions需要添加权限
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
content_type = ContentType.objects.get_for_model(Customer)
permissions = Permission.objects.filter(content_type=content_type)
self.user.user_permissions.set(permissions)
self.client = APIClient()
self.client.force_authenticate(user=self.user)
def test_create_customer(self):
"""测试创建客户"""
data = {
'name': '测试客户'
}
response = self.client.post('/api/backend/customers/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.data['name'], '测试客户')
def test_filter_customers_by_name(self):
"""测试通过 name 查询参数过滤客户"""
Customer.objects.create(merchant=self.merchant, name='客户A', created_by=self.employee)
Customer.objects.create(merchant=self.merchant, name='客户B', created_by=self.employee)
response = self.client.get('/api/backend/customers/?name=客户A')
self.assertEqual(response.status_code, status.HTTP_200_OK)
results = response.data['results'] if isinstance(response.data, dict) else response.data
self.assertEqual(len(results), 1)
self.assertEqual(results[0]['name'], '客户A')
class VehicleTypeAPITestCase(TestCase):
"""测试车辆类型 API支持 name 查询参数)"""
def setUp(self):
self.merchant = Merchant.objects.create(
name='测试商户',
type=MerchantTypeEnum.STORE
)
self.user = User.objects.create_user(
username='testuser',
password='testpass123'
)
self.employee = Employee.objects.create(
merchant=self.merchant,
sys_user=self.user,
name='测试员工'
)
self.client = APIClient()
self.client.force_authenticate(user=self.user)
def test_create_vehicle_type(self):
"""测试创建车辆类型"""
data = {
'name': '测试车辆类型'
}
response = self.client.post('/api/backend/vehicle-types/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.data['name'], '测试车辆类型')
def test_filter_vehicle_types_by_name(self):
"""测试通过 name 查询参数过滤车辆类型"""
VehicleType.objects.create(merchant=self.merchant, name='类型A')
VehicleType.objects.create(merchant=self.merchant, name='类型B')
response = self.client.get('/api/backend/vehicle-types/?name=类型A')
self.assertEqual(response.status_code, status.HTTP_200_OK)
results = response.data['results'] if isinstance(response.data, dict) else response.data
self.assertEqual(len(results), 1)
self.assertEqual(results[0]['name'], '类型A')
class BankAccountAPITestCase(TestCase):
"""测试银行账户 API支持 name 查询参数)"""
def setUp(self):
self.merchant = Merchant.objects.create(
name='测试商户',
type=MerchantTypeEnum.STORE
)
self.user = User.objects.create_user(
username='testuser',
password='testpass123'
)
self.employee = Employee.objects.create(
merchant=self.merchant,
sys_user=self.user,
name='测试员工'
)
self.client = APIClient()
self.client.force_authenticate(user=self.user)
def test_create_bank_account(self):
"""测试创建银行账户"""
data = {
'name': '测试账户',
'auto_number': 'ACC001'
}
response = self.client.post('/api/backend/bank-accounts/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.data['name'], '测试账户')
def test_filter_bank_accounts_by_name(self):
"""测试通过 name 查询参数过滤银行账户"""
BankAccount.objects.create(merchant=self.merchant, name='账户A', auto_number='A001')
BankAccount.objects.create(merchant=self.merchant, name='账户B', auto_number='B001')
response = self.client.get('/api/backend/bank-accounts/?name=账户A')
self.assertEqual(response.status_code, status.HTTP_200_OK)
results = response.data['results'] if isinstance(response.data, dict) else response.data
self.assertEqual(len(results), 1)
self.assertEqual(results[0]['name'], '账户A')
class DeviceInfoAPITestCase(TestCase):
"""测试设备信息 API支持 name 查询参数)"""
def setUp(self):
self.merchant = Merchant.objects.create(
name='测试商户',
type=MerchantTypeEnum.STORE
)
self.user = User.objects.create_user(
username='testuser',
password='testpass123'
)
self.employee = Employee.objects.create(
merchant=self.merchant,
sys_user=self.user,
name='测试员工'
)
self.client = APIClient()
self.client.force_authenticate(user=self.user)
def test_create_device_info(self):
"""测试创建设备信息"""
data = {
'name': '测试设备'
}
response = self.client.post('/api/backend/device-info/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.data['name'], '测试设备')
def test_filter_device_info_by_name(self):
"""测试通过 name 查询参数过滤设备信息"""
DeviceInfo.objects.create(merchant=self.merchant, name='设备A')
DeviceInfo.objects.create(merchant=self.merchant, name='设备B')
response = self.client.get('/api/backend/device-info/?name=设备A')
self.assertEqual(response.status_code, status.HTTP_200_OK)
results = response.data['results'] if isinstance(response.data, dict) else response.data
self.assertEqual(len(results), 1)
self.assertEqual(results[0]['name'], '设备A')
class VehicleTransportRecordAPITestCase(TestCase):
"""测试司机车次 API支持 driver_name 查询参数)"""
def setUp(self):
self.merchant = Merchant.objects.create(
name='测试商户',
type=MerchantTypeEnum.STORE
)
self.user = User.objects.create_user(
username='testuser',
password='testpass123'
)
self.employee = Employee.objects.create(
merchant=self.merchant,
sys_user=self.user,
name='测试员工'
)
self.vehicle_type = VehicleType.objects.create(
merchant=self.merchant,
name='测试车辆类型'
)
self.client = APIClient()
self.client.force_authenticate(user=self.user)
def test_create_vehicle_transport_record(self):
"""测试创建司机车次
注意:由于 VehicleTransportRecordSerializer 使用 depth=1 可能影响反序列化,
如果创建失败,可能是序列化器配置问题,但过滤功能测试仍然有效。
"""
from datetime import date
data = {
'vehicle_type': self.vehicle_type.id,
'driver_name': '测试司机',
'delivery_date': date.today().isoformat()
}
response = self.client.post('/api/backend/vehicle-transport-records/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.data['driver_name'], '测试司机')
self.assertEqual(response.data['vehicle_type'], self.vehicle_type.id)
def test_filter_vehicle_transport_records_by_driver_name(self):
"""测试通过 driver_name 查询参数过滤司机车次"""
from datetime import date
VehicleTransportRecord.objects.create(
merchant=self.merchant,
vehicle_type=self.vehicle_type,
driver_name='司机A',
delivery_date=date.today()
)
VehicleTransportRecord.objects.create(
merchant=self.merchant,
vehicle_type=self.vehicle_type,
driver_name='司机B',
delivery_date=date.today()
)
response = self.client.get('/api/backend/vehicle-transport-records/?driver_name=司机A')
self.assertEqual(response.status_code, status.HTTP_200_OK)
results = response.data['results'] if isinstance(response.data, dict) else response.data
self.assertEqual(len(results), 1)
self.assertEqual(results[0]['driver_name'], '司机A')

View File

@@ -124,32 +124,32 @@ class QuickInputViewSet(BasicInfoFilterMixin, viewsets.ModelViewSet):
return Response(groups)
class ProductViewSet(BaseViewSet):
class ProductViewSet(BaseViewSet, BasicInfoFilterMixin):
queryset = serializers.basic_models.Product.objects
serializer_class = serializers.ProductSerializer
class WareHouseViewSet(BaseViewSet):
class WareHouseViewSet(BaseViewSet, BasicInfoFilterMixin):
queryset = serializers.basic_models.WareHouse.objects
serializer_class = serializers.WareHouseSerializer
class ProductCategoryViewSet(BaseViewSet):
class ProductCategoryViewSet(BaseViewSet, BasicInfoFilterMixin):
queryset = serializers.basic_models.ProductCategory.objects
serializer_class = serializers.ProductCategorySerializer
class SupplierViewSet(BaseViewSet):
class SupplierViewSet(BaseViewSet, BasicInfoFilterMixin):
queryset = serializers.basic_models.Supplier.objects
serializer_class = serializers.SupplierSerializer
class EmployeeViewSet(BaseViewSet):
class EmployeeViewSet(BaseViewSet, BasicInfoFilterMixin):
queryset = serializers.basic_models.Employee.objects
serializer_class = serializers.EmployeeSerializer
class EmployeeTypeViewSet(BaseViewSet):
class EmployeeTypeViewSet(BaseViewSet, BasicInfoFilterMixin):
queryset = serializers.basic_models.EmployeeType.objects
serializer_class = serializers.EmployeeTypeSerializer
@@ -181,17 +181,17 @@ class CustomerViewSet(BaseViewSet, BasicInfoFilterMixin):
return super().has_permission(request, view)
class VehicleTypeViewSet(BaseViewSet):
class VehicleTypeViewSet(BaseViewSet, BasicInfoFilterMixin):
queryset = serializers.basic_models.VehicleType.objects
serializer_class = serializers.VehicleTypeSerializer
class BankAccountViewSet(BaseViewSet):
class BankAccountViewSet(BaseViewSet, BasicInfoFilterMixin):
queryset = serializers.basic_models.BankAccount.objects
serializer_class = serializers.BankAccountSerializer
class DeviceInfoViewSet(BaseViewSet):
class DeviceInfoViewSet(BaseViewSet, BasicInfoFilterMixin):
queryset = serializers.basic_models.DeviceInfo.objects
serializer_class = serializers.DeviceInfoSerializer
@@ -201,7 +201,7 @@ class VehicleTransportRecordViewSet(BaseViewSet):
serializer_class = serializers.VehicleTransportRecordSerializer
class UserProfileViewSet(BaseViewSet):
class UserProfileViewSet(BaseViewSet, BasicInfoFilterMixin):
queryset = serializers.basic_models.UserProfile.objects
serializer_class = serializers.UserProfileSerializer