更新联系人别称、快捷回复、进入对话滚到底部
This commit is contained in:
		
							
								
								
									
										42
									
								
								backend/check_db_config.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								backend/check_db_config.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,42 @@ | ||||
| #!/usr/bin/env python | ||||
| import os | ||||
| import sys | ||||
|  | ||||
| # 设置Django环境 | ||||
| os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'application.settings') | ||||
| sys.path.append(os.path.dirname(os.path.abspath(__file__))) | ||||
|  | ||||
| try: | ||||
|     import django | ||||
|     django.setup() | ||||
|      | ||||
|     from django.conf import settings | ||||
|      | ||||
|     print("=" * 50) | ||||
|     print("Django 数据库配置检查") | ||||
|     print("=" * 50) | ||||
|      | ||||
|     db_config = settings.DATABASES['default'] | ||||
|     print(f"ENGINE: {db_config['ENGINE']}") | ||||
|     print(f"NAME: {db_config['NAME']}") | ||||
|     print(f"HOST: {db_config['HOST']}") | ||||
|     print(f"PORT: {db_config['PORT']}") | ||||
|     print(f"USER: {db_config['USER']}") | ||||
|     print(f"PASSWORD: {'*' * len(db_config['PASSWORD'])}") | ||||
|      | ||||
|     print("\n" + "=" * 50) | ||||
|     print("测试数据库连接...") | ||||
|      | ||||
|     from django.db import connection | ||||
|     with connection.cursor() as cursor: | ||||
|         cursor.execute("SELECT 1") | ||||
|         result = cursor.fetchone() | ||||
|         if result: | ||||
|             print("✅ 数据库连接成功!") | ||||
|         else: | ||||
|             print("❌ 数据库连接失败!") | ||||
|              | ||||
| except Exception as e: | ||||
|     print(f"❌ 错误: {e}") | ||||
|     import traceback | ||||
|     traceback.print_exc() | ||||
							
								
								
									
										11056
									
								
								backend/fyapi_prod.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11056
									
								
								backend/fyapi_prod.sql
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										200
									
								
								backend/test_basic_functionality.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										200
									
								
								backend/test_basic_functionality.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,200 @@ | ||||
| #!/usr/bin/env python | ||||
| """ | ||||
| 基本功能测试脚本 | ||||
| """ | ||||
| import os | ||||
| import sys | ||||
| import django | ||||
|  | ||||
| # 添加项目路径 | ||||
| sys.path.append(os.path.dirname(os.path.abspath(__file__))) | ||||
|  | ||||
| # 设置Django环境 | ||||
| os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'application.settings') | ||||
| django.setup() | ||||
|  | ||||
| from translate.models import QuickReplyGroup, QuickReply | ||||
| from dvadmin.system.models import Users | ||||
|  | ||||
| def test_basic_crud(): | ||||
|     """测试基本的增删改查功能""" | ||||
|     print("=== 测试基本CRUD功能 ===") | ||||
|      | ||||
|     # 获取或创建测试用户 | ||||
|     user, created = Users.objects.get_or_create( | ||||
|         username='testuser2', | ||||
|         defaults={'email': 'test2@example.com', 'dept_belong_id': 1} | ||||
|     ) | ||||
|     print(f"测试用户: {user.username} (新建: {created})") | ||||
|      | ||||
|     # 1. 创建分组 | ||||
|     group = QuickReplyGroup.objects.create( | ||||
|         name='CRUD测试分组', | ||||
|         user_id=user.id, | ||||
|         sort_order=0 | ||||
|     ) | ||||
|     print(f"✅ 创建分组成功: {group.name} (ID: {group.id})") | ||||
|      | ||||
|     # 2. 创建快捷回复 | ||||
|     reply = QuickReply.objects.create( | ||||
|         group=group, | ||||
|         user_id=user.id, | ||||
|         remark='CRUD测试回复', | ||||
|         content='这是CRUD测试的快捷回复内容', | ||||
|         type='text', | ||||
|         sort_order=0 | ||||
|     ) | ||||
|     print(f"✅ 创建快捷回复成功: {reply.remark} (ID: {reply.id})") | ||||
|      | ||||
|     # 3. 查询分组和回复 | ||||
|     groups = QuickReplyGroup.objects.filter(user_id=user.id) | ||||
|     print(f"✅ 查询分组成功: 找到 {groups.count()} 个分组") | ||||
|      | ||||
|     for group in groups: | ||||
|         replies = QuickReply.objects.filter(group=group, user_id=user.id) | ||||
|         print(f"  分组 '{group.name}' 包含 {replies.count()} 个快捷回复") | ||||
|         for reply in replies: | ||||
|             print(f"    - {reply.remark}: {reply.content}") | ||||
|      | ||||
|     # 4. 更新快捷回复 | ||||
|     reply.content = '这是更新后的快捷回复内容' | ||||
|     reply.save() | ||||
|     print(f"✅ 更新快捷回复成功: {reply.content}") | ||||
|      | ||||
|     # 5. 删除快捷回复 | ||||
|     reply_id = reply.id | ||||
|     reply.delete() | ||||
|     print(f"✅ 删除快捷回复成功: ID {reply_id}") | ||||
|      | ||||
|     # 6. 删除分组 | ||||
|     group_id = group.id | ||||
|     group.delete() | ||||
|     print(f"✅ 删除分组成功: ID {group_id}") | ||||
|      | ||||
|     print("基本CRUD功能测试完成!") | ||||
|     return True | ||||
|  | ||||
| def test_user_isolation(): | ||||
|     """测试用户数据隔离""" | ||||
|     print("\n=== 测试用户数据隔离 ===") | ||||
|      | ||||
|     # 创建两个测试用户 | ||||
|     user1, _ = Users.objects.get_or_create( | ||||
|         username='user1', | ||||
|         defaults={'email': 'user1@example.com', 'dept_belong_id': 1} | ||||
|     ) | ||||
|     user2, _ = Users.objects.get_or_create( | ||||
|         username='user2', | ||||
|         defaults={'email': 'user2@example.com', 'dept_belong_id': 1} | ||||
|     ) | ||||
|      | ||||
|     # 为用户1创建数据 | ||||
|     group1 = QuickReplyGroup.objects.create( | ||||
|         name='用户1的分组', | ||||
|         user_id=user1.id, | ||||
|         sort_order=0 | ||||
|     ) | ||||
|     reply1 = QuickReply.objects.create( | ||||
|         group=group1, | ||||
|         user_id=user1.id, | ||||
|         remark='用户1的回复', | ||||
|         content='用户1的内容', | ||||
|         type='text', | ||||
|         sort_order=0 | ||||
|     ) | ||||
|      | ||||
|     # 为用户2创建数据 | ||||
|     group2 = QuickReplyGroup.objects.create( | ||||
|         name='用户2的分组', | ||||
|         user_id=user2.id, | ||||
|         sort_order=0 | ||||
|     ) | ||||
|     reply2 = QuickReply.objects.create( | ||||
|         group=group2, | ||||
|         user_id=user2.id, | ||||
|         remark='用户2的回复', | ||||
|         content='用户2的内容', | ||||
|         type='text', | ||||
|         sort_order=0 | ||||
|     ) | ||||
|      | ||||
|     # 验证数据隔离 | ||||
|     user1_groups = QuickReplyGroup.objects.filter(user_id=user1.id) | ||||
|     user2_groups = QuickReplyGroup.objects.filter(user_id=user2.id) | ||||
|      | ||||
|     print(f"用户1的分组数量: {user1_groups.count()}") | ||||
|     print(f"用户2的分组数量: {user2_groups.count()}") | ||||
|      | ||||
|     user1_replies = QuickReply.objects.filter(user_id=user1.id) | ||||
|     user2_replies = QuickReply.objects.filter(user_id=user2.id) | ||||
|      | ||||
|     print(f"用户1的快捷回复数量: {user1_replies.count()}") | ||||
|     print(f"用户2的快捷回复数量: {user2_replies.count()}") | ||||
|      | ||||
|     # 验证用户1看不到用户2的数据 | ||||
|     assert user1_groups.count() == 1 | ||||
|     assert user2_groups.count() == 1 | ||||
|     assert user1_replies.count() == 1 | ||||
|     assert user2_replies.count() == 1 | ||||
|      | ||||
|     # 清理测试数据 | ||||
|     group1.delete() | ||||
|     group2.delete() | ||||
|      | ||||
|     print("✅ 用户数据隔离测试通过!") | ||||
|     return True | ||||
|  | ||||
| def test_cascade_delete(): | ||||
|     """测试级联删除""" | ||||
|     print("\n=== 测试级联删除 ===") | ||||
|      | ||||
|     user, _ = Users.objects.get_or_create( | ||||
|         username='cascade_test_user', | ||||
|         defaults={'email': 'cascade@example.com', 'dept_belong_id': 1} | ||||
|     ) | ||||
|      | ||||
|     # 创建分组和多个快捷回复 | ||||
|     group = QuickReplyGroup.objects.create( | ||||
|         name='级联删除测试分组', | ||||
|         user_id=user.id, | ||||
|         sort_order=0 | ||||
|     ) | ||||
|      | ||||
|     replies = [] | ||||
|     for i in range(3): | ||||
|         reply = QuickReply.objects.create( | ||||
|             group=group, | ||||
|             user_id=user.id, | ||||
|             remark=f'级联测试回复{i+1}', | ||||
|             content=f'级联测试内容{i+1}', | ||||
|             type='text', | ||||
|             sort_order=i | ||||
|         ) | ||||
|         replies.append(reply) | ||||
|      | ||||
|     print(f"创建了1个分组和{len(replies)}个快捷回复") | ||||
|      | ||||
|     # 验证数据存在 | ||||
|     assert QuickReply.objects.filter(group=group).count() == 3 | ||||
|      | ||||
|     # 删除分组,应该级联删除所有快捷回复 | ||||
|     group.delete() | ||||
|      | ||||
|     # 验证快捷回复也被删除了 | ||||
|     remaining_replies = QuickReply.objects.filter(user_id=user.id) | ||||
|     assert remaining_replies.count() == 0 | ||||
|      | ||||
|     print("✅ 级联删除测试通过!") | ||||
|     return True | ||||
|  | ||||
| if __name__ == '__main__': | ||||
|     try: | ||||
|         print("开始测试快捷回复基本功能...") | ||||
|         test_basic_crud() | ||||
|         test_user_isolation() | ||||
|         test_cascade_delete() | ||||
|         print("\n🎉 所有测试通过!快捷回复功能正常工作!") | ||||
|     except Exception as e: | ||||
|         print(f"\n❌ 测试失败: {e}") | ||||
|         import traceback | ||||
|         traceback.print_exc() | ||||
							
								
								
									
										143
									
								
								backend/test_quick_reply_api.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								backend/test_quick_reply_api.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,143 @@ | ||||
| #!/usr/bin/env python | ||||
| """ | ||||
| 快捷回复API测试脚本 | ||||
| """ | ||||
| import requests | ||||
| import json | ||||
|  | ||||
| # API基础URL | ||||
| BASE_URL = 'http://127.0.0.1:8000' | ||||
|  | ||||
| def login_and_get_token(): | ||||
|     """登录并获取token""" | ||||
|     login_data = { | ||||
|         'username': 'admin', | ||||
|         'password': 'admin123' | ||||
|     } | ||||
|  | ||||
|     try: | ||||
|         response = requests.post(f'{BASE_URL}/api/login/', json=login_data) | ||||
|         if response.status_code == 200: | ||||
|             data = response.json() | ||||
|             token = data.get('access_token') or data.get('token') or data.get('access') | ||||
|             print(f"登录成功,获取token: {token[:20]}..." if token else "登录成功但未获取到token") | ||||
|             return token | ||||
|         else: | ||||
|             print(f"登录失败: {response.status_code} - {response.text}") | ||||
|             return None | ||||
|     except Exception as e: | ||||
|         print(f"登录请求失败: {e}") | ||||
|         return None | ||||
|  | ||||
| def test_api_endpoint(method, endpoint, data=None, token=None): | ||||
|     """测试API端点""" | ||||
|     url = f"{BASE_URL}{endpoint}" | ||||
|     headers = {'Content-Type': 'application/json'} | ||||
|      | ||||
|     if token: | ||||
|         headers['Authorization'] = f'Token {token}' | ||||
|      | ||||
|     try: | ||||
|         if method.upper() == 'GET': | ||||
|             response = requests.get(url, headers=headers) | ||||
|         elif method.upper() == 'POST': | ||||
|             response = requests.post(url, headers=headers, json=data) | ||||
|         elif method.upper() == 'PUT': | ||||
|             response = requests.put(url, headers=headers, json=data) | ||||
|         elif method.upper() == 'DELETE': | ||||
|             response = requests.delete(url, headers=headers) | ||||
|         else: | ||||
|             print(f"不支持的HTTP方法: {method}") | ||||
|             return None | ||||
|          | ||||
|         print(f"\n{method.upper()} {endpoint}") | ||||
|         print(f"状态码: {response.status_code}") | ||||
|          | ||||
|         try: | ||||
|             response_data = response.json() | ||||
|             print(f"响应: {json.dumps(response_data, indent=2, ensure_ascii=False)}") | ||||
|         except: | ||||
|             print(f"响应文本: {response.text}") | ||||
|          | ||||
|         return response | ||||
|     except Exception as e: | ||||
|         print(f"请求失败: {e}") | ||||
|         return None | ||||
|  | ||||
| def main(): | ||||
|     """主测试函数""" | ||||
|     print("=== 快捷回复API测试 ===") | ||||
|  | ||||
|     # 获取认证token | ||||
|     token = login_and_get_token() | ||||
|     if not token: | ||||
|         print("无法获取认证token,跳过需要认证的测试") | ||||
|         token = None | ||||
|      | ||||
|     # 测试用例 | ||||
|     print("\n1. 测试获取快捷回复分组列表") | ||||
|     test_api_endpoint('GET', '/api/QuickReplyGroupModelViewSet/', token=token) | ||||
|      | ||||
|     print("\n2. 测试创建快捷回复分组") | ||||
|     group_data = { | ||||
|         'name': '测试分组1', | ||||
|         'sort_order': 1 | ||||
|     } | ||||
|     response = test_api_endpoint('POST', '/api/QuickReplyGroupModelViewSet/', data=group_data, token=token) | ||||
|      | ||||
|     # 如果创建成功,获取分组ID用于后续测试 | ||||
|     group_id = None | ||||
|     if response and response.status_code == 201: | ||||
|         try: | ||||
|             group_id = response.json().get('id') | ||||
|             print(f"创建的分组ID: {group_id}") | ||||
|         except: | ||||
|             pass | ||||
|      | ||||
|     print("\n3. 测试获取快捷回复内容列表") | ||||
|     test_api_endpoint('GET', '/api/QuickReplyModelViewSet/', token=token) | ||||
|      | ||||
|     if group_id: | ||||
|         print("\n4. 测试创建快捷回复内容") | ||||
|         reply_data = { | ||||
|             'group': group_id, | ||||
|             'remark': '测试快捷回复1', | ||||
|             'content': '这是一个测试快捷回复内容', | ||||
|             'type': 'text', | ||||
|             'sort_order': 1 | ||||
|         } | ||||
|         response = test_api_endpoint('POST', '/api/QuickReplyModelViewSet/', data=reply_data, token=token) | ||||
|          | ||||
|         # 获取快捷回复ID用于后续测试 | ||||
|         reply_id = None | ||||
|         if response and response.status_code == 201: | ||||
|             try: | ||||
|                 reply_id = response.json().get('id') | ||||
|                 print(f"创建的快捷回复ID: {reply_id}") | ||||
|             except: | ||||
|                 pass | ||||
|          | ||||
|         print("\n5. 测试获取分组详情(包含快捷回复列表)") | ||||
|         test_api_endpoint('GET', f'/api/QuickReplyGroupModelViewSet/{group_id}/contents/', token=token) | ||||
|          | ||||
|         print("\n6. 测试获取分组及其内容") | ||||
|         test_api_endpoint('GET', '/api/QuickReplyGroupModelViewSet/groups_with_contents/', token=token) | ||||
|          | ||||
|         if reply_id: | ||||
|             print("\n7. 测试更新快捷回复") | ||||
|             update_data = { | ||||
|                 'remark': '更新后的快捷回复', | ||||
|                 'content': '这是更新后的内容' | ||||
|             } | ||||
|             test_api_endpoint('PUT', f'/api/QuickReplyModelViewSet/{reply_id}/', data=update_data, token=token) | ||||
|              | ||||
|             print("\n8. 测试删除快捷回复") | ||||
|             test_api_endpoint('DELETE', f'/api/QuickReplyModelViewSet/{reply_id}/', token=token) | ||||
|          | ||||
|         print("\n9. 测试删除分组") | ||||
|         test_api_endpoint('DELETE', f'/api/QuickReplyGroupModelViewSet/{group_id}/', token=token) | ||||
|      | ||||
|     print("\n=== 测试完成 ===") | ||||
|  | ||||
| if __name__ == '__main__': | ||||
|     main() | ||||
							
								
								
									
										127
									
								
								backend/test_quick_reply_simple.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								backend/test_quick_reply_simple.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,127 @@ | ||||
| #!/usr/bin/env python | ||||
| """ | ||||
| 简单的快捷回复API测试脚本 | ||||
| """ | ||||
| import os | ||||
| import sys | ||||
| import django | ||||
|  | ||||
| # 添加项目路径 | ||||
| sys.path.append(os.path.dirname(os.path.abspath(__file__))) | ||||
|  | ||||
| # 设置Django环境 | ||||
| os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'application.settings') | ||||
| django.setup() | ||||
|  | ||||
| from translate.models import QuickReplyGroup, QuickReply | ||||
| from dvadmin.system.models import Users | ||||
|  | ||||
| def test_models(): | ||||
|     """测试模型是否正常工作""" | ||||
|     print("=== 测试快捷回复模型 ===") | ||||
|      | ||||
|     # 创建测试用户 | ||||
|     user, created = Users.objects.get_or_create( | ||||
|         username='testuser', | ||||
|         defaults={'email': 'test@example.com', 'dept_belong_id': 1} | ||||
|     ) | ||||
|     print(f"测试用户: {user.username} (新建: {created})") | ||||
|      | ||||
|     # 创建测试分组 | ||||
|     group, created = QuickReplyGroup.objects.get_or_create( | ||||
|         name='测试分组', | ||||
|         user_id=user.id, | ||||
|         defaults={'sort_order': 0} | ||||
|     ) | ||||
|     print(f"测试分组: {group.name} (新建: {created})") | ||||
|      | ||||
|     # 创建测试快捷回复 | ||||
|     reply, created = QuickReply.objects.get_or_create( | ||||
|         group=group, | ||||
|         remark='测试回复', | ||||
|         user_id=user.id, | ||||
|         defaults={ | ||||
|             'content': '这是一个测试快捷回复', | ||||
|             'type': 'text', | ||||
|             'sort_order': 0 | ||||
|         } | ||||
|     ) | ||||
|     print(f"测试快捷回复: {reply.remark} (新建: {created})") | ||||
|      | ||||
|     # 查询测试 | ||||
|     groups = QuickReplyGroup.objects.filter(user_id=user.id) | ||||
|     print(f"用户 {user.username} 的分组数量: {groups.count()}") | ||||
|      | ||||
|     for group in groups: | ||||
|         replies = QuickReply.objects.filter(group=group) | ||||
|         print(f"  分组 '{group.name}' 包含 {replies.count()} 个快捷回复") | ||||
|         for reply in replies: | ||||
|             print(f"    - {reply.remark}: {reply.content}") | ||||
|      | ||||
|     print("模型测试完成!") | ||||
|     return True | ||||
|  | ||||
| def test_api_endpoints(): | ||||
|     """测试API端点""" | ||||
|     print("\n=== 测试API端点 ===") | ||||
|      | ||||
|     from django.test import Client | ||||
|  | ||||
|     client = Client() | ||||
|  | ||||
|     # 创建测试用户并登录 | ||||
|     user = Users.objects.get(username='testuser') | ||||
|     client.force_login(user) | ||||
|      | ||||
|     # 测试获取分组列表 | ||||
|     response = client.get('/api/QuickReplyGroupModelViewSet/') | ||||
|     print(f"获取分组列表: {response.status_code}") | ||||
|     if response.status_code == 200: | ||||
|         data = response.json() | ||||
|         print(f"  返回数据: {data}") | ||||
|      | ||||
|     # 测试获取分组详情(包含内容) | ||||
|     response = client.get('/api/QuickReplyGroupModelViewSet/groups_with_contents/') | ||||
|     print(f"获取分组详情: {response.status_code}") | ||||
|     if response.status_code == 200: | ||||
|         data = response.json() | ||||
|         print(f"  返回数据: {data}") | ||||
|      | ||||
|     # 测试创建新分组 | ||||
|     response = client.post('/api/QuickReplyGroupModelViewSet/', { | ||||
|         'name': 'API测试分组', | ||||
|         'sort_order': 1 | ||||
|     }, content_type='application/json') | ||||
|     print(f"创建新分组: {response.status_code}") | ||||
|     if response.status_code == 201: | ||||
|         data = response.json() | ||||
|         print(f"  创建的分组: {data}") | ||||
|          | ||||
|         # 测试在新分组中创建快捷回复 | ||||
|         group_id = data.get('id') | ||||
|         if group_id: | ||||
|             response = client.post('/api/QuickReplyModelViewSet/', { | ||||
|                 'group': group_id, | ||||
|                 'remark': 'API测试回复', | ||||
|                 'content': '这是通过API创建的快捷回复', | ||||
|                 'type': 'text', | ||||
|                 'sort_order': 0 | ||||
|             }, content_type='application/json') | ||||
|             print(f"创建快捷回复: {response.status_code}") | ||||
|             if response.status_code == 201: | ||||
|                 reply_data = response.json() | ||||
|                 print(f"  创建的快捷回复: {reply_data}") | ||||
|      | ||||
|     print("API测试完成!") | ||||
|     return True | ||||
|  | ||||
| if __name__ == '__main__': | ||||
|     try: | ||||
|         print("开始测试快捷回复功能...") | ||||
|         test_models() | ||||
|         test_api_endpoints() | ||||
|         print("\n✅ 所有测试通过!") | ||||
|     except Exception as e: | ||||
|         print(f"\n❌ 测试失败: {e}") | ||||
|         import traceback | ||||
|         traceback.print_exc() | ||||
| @ -299,3 +299,54 @@ class Note(models.Model): | ||||
|         verbose_name_plural = "便签" | ||||
|  | ||||
|  | ||||
| class QuickReplyGroup(CoreModel): | ||||
|     """ | ||||
|     快捷回复分组表 | ||||
|     """ | ||||
|     name = models.CharField(max_length=255, verbose_name='分组名称') | ||||
|     user_id = models.IntegerField(verbose_name='用户ID', help_text='关联用户ID,实现用户维度数据隔离') | ||||
|     sort_order = models.IntegerField(default=0, verbose_name='排序', help_text='数字越小排序越靠前') | ||||
|  | ||||
|     class Meta: | ||||
|         db_table = "quick_reply_group" | ||||
|         verbose_name = "快捷回复分组" | ||||
|         verbose_name_plural = "快捷回复分组" | ||||
|         ordering = ['sort_order', 'create_datetime'] | ||||
|         unique_together = [('name', 'user_id')]  # 同一用户下分组名称不能重复 | ||||
|  | ||||
|  | ||||
| class QuickReply(CoreModel): | ||||
|     """ | ||||
|     快捷回复内容表 | ||||
|     """ | ||||
|     TYPE_CHOICES = ( | ||||
|         ('text', '文本'), | ||||
|         ('image', '图片'), | ||||
|         ('video', '视频'), | ||||
|     ) | ||||
|  | ||||
|     group = models.ForeignKey( | ||||
|         QuickReplyGroup, | ||||
|         on_delete=models.CASCADE, | ||||
|         verbose_name='所属分组', | ||||
|         related_name='quick_replies' | ||||
|     ) | ||||
|     user_id = models.IntegerField(verbose_name='用户ID', help_text='关联用户ID,实现用户维度数据隔离') | ||||
|     remark = models.CharField(max_length=255, verbose_name='备注名称', help_text='快捷回复的显示名称') | ||||
|     content = models.TextField(verbose_name='回复内容', help_text='快捷回复的具体内容') | ||||
|     type = models.CharField( | ||||
|         max_length=10, | ||||
|         choices=TYPE_CHOICES, | ||||
|         default='text', | ||||
|         verbose_name='类型' | ||||
|     ) | ||||
|     url = models.CharField(max_length=500, blank=True, default='', verbose_name='资源链接', help_text='图片或视频的URL链接') | ||||
|     sort_order = models.IntegerField(default=0, verbose_name='排序', help_text='数字越小排序越靠前') | ||||
|  | ||||
|     class Meta: | ||||
|         db_table = "quick_reply" | ||||
|         verbose_name = "快捷回复" | ||||
|         verbose_name_plural = "快捷回复" | ||||
|         ordering = ['sort_order', 'create_datetime'] | ||||
|  | ||||
|  | ||||
|  | ||||
| @ -1,7 +1,8 @@ | ||||
| #backend/crud_demo/serializers.py | ||||
|  | ||||
| from translate.models import TranslationService, StatusCode, TranslationRecord, Device, DeviceCode, BillingRecords | ||||
| from translate.models import TranslationService, StatusCode, TranslationRecord, Device, DeviceCode, BillingRecords, QuickReplyGroup, QuickReply | ||||
| from dvadmin.utils.serializers import CustomModelSerializer | ||||
| from rest_framework import serializers | ||||
|  | ||||
|  | ||||
|  | ||||
| @ -137,3 +138,90 @@ class BillingRecordsModelCreateUpdateSerializer(CustomModelSerializer): | ||||
|     class Meta: | ||||
|         model = BillingRecords | ||||
|         fields = '__all__' | ||||
|  | ||||
|  | ||||
| # 快捷回复分组 | ||||
| class QuickReplyGroupModelSerializer(CustomModelSerializer): | ||||
|     """ | ||||
|     快捷回复分组序列化器 | ||||
|     """ | ||||
|     content_count = serializers.SerializerMethodField(read_only=True) | ||||
|  | ||||
|     class Meta: | ||||
|         model = QuickReplyGroup | ||||
|         fields = "__all__" | ||||
|  | ||||
|     def get_content_count(self, obj): | ||||
|         """获取分组下快捷回复的数量""" | ||||
|         return obj.quick_replies.count() | ||||
|  | ||||
|  | ||||
| class QuickReplyGroupCreateUpdateSerializer(CustomModelSerializer): | ||||
|     """ | ||||
|     快捷回复分组创建/更新序列化器 | ||||
|     """ | ||||
|  | ||||
|     class Meta: | ||||
|         model = QuickReplyGroup | ||||
|         fields = ['name', 'sort_order'] | ||||
|  | ||||
|     def create(self, validated_data): | ||||
|         # 自动设置当前用户ID | ||||
|         request = self.context.get('request') | ||||
|         if request and hasattr(request, 'user'): | ||||
|             validated_data['user_id'] = request.user.id | ||||
|         return super().create(validated_data) | ||||
|  | ||||
|  | ||||
| # 快捷回复内容 | ||||
| class QuickReplyModelSerializer(CustomModelSerializer): | ||||
|     """ | ||||
|     快捷回复内容序列化器 | ||||
|     """ | ||||
|     group_name = serializers.CharField(source='group.name', read_only=True) | ||||
|  | ||||
|     class Meta: | ||||
|         model = QuickReply | ||||
|         fields = "__all__" | ||||
|  | ||||
|  | ||||
| class QuickReplyCreateUpdateSerializer(CustomModelSerializer): | ||||
|     """ | ||||
|     快捷回复内容创建/更新序列化器 | ||||
|     """ | ||||
|  | ||||
|     class Meta: | ||||
|         model = QuickReply | ||||
|         fields = ['group', 'remark', 'content', 'type', 'url', 'sort_order'] | ||||
|  | ||||
|     def create(self, validated_data): | ||||
|         # 自动设置当前用户ID | ||||
|         request = self.context.get('request') | ||||
|         if request and hasattr(request, 'user'): | ||||
|             validated_data['user_id'] = request.user.id | ||||
|         return super().create(validated_data) | ||||
|  | ||||
|     def validate_group(self, value): | ||||
|         """验证分组是否属于当前用户""" | ||||
|         request = self.context.get('request') | ||||
|         if request and hasattr(request, 'user'): | ||||
|             if value.user_id != request.user.id: | ||||
|                 raise serializers.ValidationError("无法在其他用户的分组中创建快捷回复") | ||||
|         return value | ||||
|  | ||||
|  | ||||
| # 快捷回复分组详情(包含内容列表) | ||||
| class QuickReplyGroupDetailSerializer(CustomModelSerializer): | ||||
|     """ | ||||
|     快捷回复分组详情序列化器(包含快捷回复列表) | ||||
|     """ | ||||
|     contents = QuickReplyModelSerializer(source='quick_replies', many=True, read_only=True) | ||||
|     content_count = serializers.SerializerMethodField(read_only=True) | ||||
|  | ||||
|     class Meta: | ||||
|         model = QuickReplyGroup | ||||
|         fields = "__all__" | ||||
|  | ||||
|     def get_content_count(self, obj): | ||||
|         """获取分组下快捷回复的数量""" | ||||
|         return obj.quick_replies.count() | ||||
|  | ||||
| @ -3,7 +3,7 @@ | ||||
| from rest_framework.routers import SimpleRouter | ||||
|  | ||||
| from .views import TranslationServiceModelViewSet, DeviceModelViewSet, DeviceCodeModelViewSet, \ | ||||
|     BillingRecordsModelViewSet | ||||
|     BillingRecordsModelViewSet, QuickReplyGroupModelViewSet, QuickReplyModelViewSet | ||||
| from .views import StatusCodeModelViewSet | ||||
| from .views import TranslationRecordModelViewSet | ||||
| from .api import translate_api, check_login, consumption, renewal, user_login, user_register, get_system_config, detect_language | ||||
| @ -19,6 +19,8 @@ router.register("api/TranslationRecordModelViewSet", TranslationRecordModelViewS | ||||
| router.register("api/DeviceModelViewSet", DeviceModelViewSet) | ||||
| router.register("api/DeviceCodeModelViewSet", DeviceCodeModelViewSet) | ||||
| router.register("api/BillingRecordsModelViewSet", BillingRecordsModelViewSet) | ||||
| router.register("api/QuickReplyGroupModelViewSet", QuickReplyGroupModelViewSet) | ||||
| router.register("api/QuickReplyModelViewSet", QuickReplyModelViewSet) | ||||
| # 自定义api | ||||
|  | ||||
| urlpatterns = [ | ||||
|  | ||||
| @ -1,10 +1,12 @@ | ||||
| # Create your views here. | ||||
| from translate.models import TranslationService, StatusCode, TranslationRecord, Device, DeviceCode, BillingRecords | ||||
| from translate.models import TranslationService, StatusCode, TranslationRecord, Device, DeviceCode, BillingRecords, QuickReplyGroup, QuickReply | ||||
| from translate.serializers import TranslationServiceModelSerializer, TranslationServiceModelCreateUpdateSerializer, \ | ||||
|     DeviceModelSerializer, DeviceModelCreateUpdateSerializer, DeviceCodeModelCreateUpdateSerializer, \ | ||||
|     DeviceCodeModelSerializer, BillingRecordsModelCreateUpdateSerializer, BillingRecordsModelSerializer | ||||
| from translate.serializers import StatusCodeModelSerializer, StatusCodeModelCreateUpdateSerializer | ||||
| from translate.serializers import TranslationRecordModelSerializer, TranslationRecordModelCreateUpdateSerializer | ||||
| from translate.serializers import QuickReplyGroupModelSerializer, QuickReplyGroupCreateUpdateSerializer, \ | ||||
|     QuickReplyModelSerializer, QuickReplyCreateUpdateSerializer, QuickReplyGroupDetailSerializer | ||||
| from dvadmin.utils.viewset import CustomModelViewSet | ||||
| from rest_framework.decorators import action | ||||
| from rest_framework.response import Response | ||||
| @ -94,3 +96,152 @@ class BillingRecordsModelViewSet(CustomModelViewSet): | ||||
|     serializer_class = BillingRecordsModelSerializer | ||||
|     create_serializer_class = BillingRecordsModelCreateUpdateSerializer | ||||
|     update_serializer_class = BillingRecordsModelCreateUpdateSerializer | ||||
|  | ||||
|  | ||||
| # 快捷回复分组 | ||||
| class QuickReplyGroupModelViewSet(CustomModelViewSet): | ||||
|     """ | ||||
|     快捷回复分组管理 | ||||
|     list:查询分组列表 | ||||
|     create:新增分组 | ||||
|     update:修改分组 | ||||
|     retrieve:单个分组详情 | ||||
|     destroy:删除分组 | ||||
|     """ | ||||
|     queryset = QuickReplyGroup.objects.all() | ||||
|     serializer_class = QuickReplyGroupModelSerializer | ||||
|     create_serializer_class = QuickReplyGroupCreateUpdateSerializer | ||||
|     update_serializer_class = QuickReplyGroupCreateUpdateSerializer | ||||
|  | ||||
|     def get_queryset(self): | ||||
|         """只返回当前用户的分组""" | ||||
|         if hasattr(self.request, 'user') and self.request.user.is_authenticated: | ||||
|             return QuickReplyGroup.objects.filter(user_id=self.request.user.id).order_by('sort_order', 'create_datetime') | ||||
|         return QuickReplyGroup.objects.none() | ||||
|  | ||||
|     @action(detail=False, methods=['get']) | ||||
|     def groups_with_contents(self, request): | ||||
|         """获取分组及其快捷回复内容列表""" | ||||
|         queryset = self.get_queryset() | ||||
|         serializer = QuickReplyGroupDetailSerializer(queryset, many=True) | ||||
|         return Response({ | ||||
|             'status': True, | ||||
|             'message': '查询成功', | ||||
|             'data': serializer.data | ||||
|         }) | ||||
|  | ||||
|     @action(detail=True, methods=['get']) | ||||
|     def contents(self, request, pk=None): | ||||
|         """获取指定分组下的快捷回复内容""" | ||||
|         try: | ||||
|             group = self.get_object() | ||||
|             contents = group.quick_replies.all().order_by('sort_order', 'create_datetime') | ||||
|             serializer = QuickReplyModelSerializer(contents, many=True) | ||||
|             return Response({ | ||||
|                 'status': True, | ||||
|                 'message': '查询成功', | ||||
|                 'data': serializer.data | ||||
|             }) | ||||
|         except QuickReplyGroup.DoesNotExist: | ||||
|             return Response({ | ||||
|                 'status': False, | ||||
|                 'message': '分组不存在' | ||||
|             }, status=status.HTTP_404_NOT_FOUND) | ||||
|  | ||||
|     @action(detail=True, methods=['delete']) | ||||
|     def clear_contents(self, request, pk=None): | ||||
|         """清空指定分组下的所有快捷回复""" | ||||
|         try: | ||||
|             group = self.get_object() | ||||
|             deleted_count = group.quick_replies.count() | ||||
|             group.quick_replies.all().delete() | ||||
|             return Response({ | ||||
|                 'status': True, | ||||
|                 'message': f'成功删除{deleted_count}条快捷回复' | ||||
|             }) | ||||
|         except QuickReplyGroup.DoesNotExist: | ||||
|             return Response({ | ||||
|                 'status': False, | ||||
|                 'message': '分组不存在' | ||||
|             }, status=status.HTTP_404_NOT_FOUND) | ||||
|  | ||||
|  | ||||
| # 快捷回复内容 | ||||
| class QuickReplyModelViewSet(CustomModelViewSet): | ||||
|     """ | ||||
|     快捷回复内容管理 | ||||
|     list:查询快捷回复列表 | ||||
|     create:新增快捷回复 | ||||
|     update:修改快捷回复 | ||||
|     retrieve:单个快捷回复详情 | ||||
|     destroy:删除快捷回复 | ||||
|     """ | ||||
|     queryset = QuickReply.objects.all() | ||||
|     serializer_class = QuickReplyModelSerializer | ||||
|     create_serializer_class = QuickReplyCreateUpdateSerializer | ||||
|     update_serializer_class = QuickReplyCreateUpdateSerializer | ||||
|  | ||||
|     def get_queryset(self): | ||||
|         """只返回当前用户的快捷回复""" | ||||
|         if hasattr(self.request, 'user') and self.request.user.is_authenticated: | ||||
|             return QuickReply.objects.filter(user_id=self.request.user.id).order_by('sort_order', 'create_datetime') | ||||
|         return QuickReply.objects.none() | ||||
|  | ||||
|     def list(self, request, *args, **kwargs): | ||||
|         """获取快捷回复列表,支持按分组筛选""" | ||||
|         queryset = self.get_queryset() | ||||
|  | ||||
|         # 支持按分组ID筛选 | ||||
|         group_id = request.query_params.get('group_id') | ||||
|         if group_id: | ||||
|             queryset = queryset.filter(group_id=group_id) | ||||
|  | ||||
|         serializer = self.get_serializer(queryset, many=True) | ||||
|         return Response({ | ||||
|             'status': True, | ||||
|             'message': '查询成功', | ||||
|             'data': serializer.data | ||||
|         }) | ||||
|  | ||||
|     def create(self, request, *args, **kwargs): | ||||
|         """创建快捷回复""" | ||||
|         serializer = self.get_serializer(data=request.data) | ||||
|         if serializer.is_valid(): | ||||
|             serializer.save() | ||||
|             return Response({ | ||||
|                 'status': True, | ||||
|                 'message': '新增成功', | ||||
|                 'data': serializer.data | ||||
|             }, status=status.HTTP_201_CREATED) | ||||
|         return Response({ | ||||
|             'status': False, | ||||
|             'message': '参数错误', | ||||
|             'errors': serializer.errors | ||||
|         }, status=status.HTTP_400_BAD_REQUEST) | ||||
|  | ||||
|     def update(self, request, *args, **kwargs): | ||||
|         """更新快捷回复""" | ||||
|         partial = kwargs.pop('partial', False) | ||||
|         instance = self.get_object() | ||||
|         serializer = self.get_serializer(instance, data=request.data, partial=partial) | ||||
|         if serializer.is_valid(): | ||||
|             serializer.save() | ||||
|             return Response({ | ||||
|                 'status': True, | ||||
|                 'message': '更新成功', | ||||
|                 'data': serializer.data | ||||
|             }) | ||||
|         return Response({ | ||||
|             'status': False, | ||||
|             'message': '参数错误', | ||||
|             'errors': serializer.errors | ||||
|         }, status=status.HTTP_400_BAD_REQUEST) | ||||
|  | ||||
|     def destroy(self, request, *args, **kwargs): | ||||
|         """删除快捷回复""" | ||||
|         instance = self.get_object() | ||||
|         instance.delete() | ||||
|         return Response({ | ||||
|             'status': True, | ||||
|             'message': '删除成功' | ||||
|         }, status=status.HTTP_204_NO_CONTENT) | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 unknown
					unknown