sync from local backup

This commit is contained in:
chunyu
2026-08-05 23:59:15 +08:00
commit d48ec1747c
358 changed files with 34384 additions and 0 deletions
View File
+23
View File
@@ -0,0 +1,23 @@
from django.contrib import admin
from .models import ApiCategory, ApiItem, ApiFavorite
@admin.register(ApiCategory)
class ApiCategoryAdmin(admin.ModelAdmin):
list_display = ['name', 'icon', 'sort_order', 'created_at']
list_editable = ['sort_order']
search_fields = ['name']
@admin.register(ApiItem)
class ApiItemAdmin(admin.ModelAdmin):
list_display = ['name', 'method', 'category', 'is_enabled', 'is_new', 'is_coming_soon', 'views_count', 'sort_order']
list_filter = ['method', 'is_enabled', 'is_new', 'is_coming_soon', 'category']
list_editable = ['sort_order', 'is_enabled']
search_fields = ['name', 'description', 'url_path']
@admin.register(ApiFavorite)
class ApiFavoriteAdmin(admin.ModelAdmin):
list_display = ['user', 'api_item', 'created_at']
list_filter = ['created_at']
+6
View File
@@ -0,0 +1,6 @@
from django.apps import AppConfig
class ApidirectoryConfig(AppConfig):
name = 'apidirectory'
verbose_name = 'API接口大全'
View File
@@ -0,0 +1,60 @@
from django.core.management.base import BaseCommand
from apidirectory.models import ApiCategory, ApiItem
class Command(BaseCommand):
help = '填充API接口大全的初始数据'
def handle(self, *args, **options):
# 清空现有数据
ApiItem.objects.all().delete()
ApiCategory.objects.all().delete()
# 创建分类
categories_data = [
{'name': '基础工具', 'icon': 'ToolOutlined', 'sort_order': 1},
{'name': '数据查询', 'icon': 'SearchOutlined', 'sort_order': 2},
{'name': '内容生成', 'icon': 'FileTextOutlined', 'sort_order': 3},
{'name': '生活服务', 'icon': 'HomeOutlined', 'sort_order': 4},
{'name': '开发工具', 'icon': 'CodeOutlined', 'sort_order': 5},
]
categories = {}
for cat_data in categories_data:
cat = ApiCategory.objects.create(**cat_data)
categories[cat_data['name']] = cat
self.stdout.write(f'创建分类: {cat.name}')
# 创建API项目
items_data = [
# 基础工具
{'name': 'IP地址查询', 'description': '根据IP地址获取地理位置信息', 'url_path': '/api/getIpData/', 'method': 'GET', 'category': '基础工具', 'icon': 'GlobalOutlined', 'color': '#667eea'},
{'name': '二维码生成', 'description': '将文本或URL转换为二维码图片', 'url_path': '/api/qrcode-generator/', 'method': 'POST', 'category': '基础工具', 'icon': 'QrcodeOutlined', 'color': '#10b981'},
{'name': '短链接生成', 'description': '将长URL转换为短链接', 'url_path': '/api/shorturl/shorten/', 'method': 'POST', 'category': '基础工具', 'icon': 'LinkOutlined', 'color': '#f59e0b'},
{'name': '密码生成器', 'description': '生成随机安全密码', 'url_path': '/api/password-generator/', 'method': 'GET', 'category': '基础工具', 'icon': 'LockOutlined', 'is_coming_soon': True},
# 数据查询
{'name': '天气查询', 'description': '查询全球各地实时天气信息', 'url_path': '/api/weather', 'method': 'GET', 'category': '数据查询', 'icon': 'CloudOutlined', 'color': '#3b82f6'},
{'name': '空气质量', 'description': '查询城市空气质量指数AQI', 'url_path': '/api/air-quality/', 'method': 'GET', 'category': '数据查询', 'icon': 'DashboardOutlined', 'color': '#10b981'},
# 内容生成
{'name': '百度翻译', 'description': '支持多种语言的互译服务', 'url_path': '/api/baiduFanyi/', 'method': 'POST', 'category': '内容生成', 'icon': 'TranslationOutlined', 'color': '#667eea', 'is_new': True},
{'name': 'OCR文字识别', 'description': '从图片中识别文字内容', 'url_path': '/api/baiduFanyi/pictureRecognize/', 'method': 'POST', 'category': '内容生成', 'icon': 'ScanOutlined', 'color': '#8b5cf6', 'is_coming_soon': True},
{'name': '语音识别', 'description': '将语音转换为文字', 'url_path': '/api/baiduFanyi/speechRecognition/', 'method': 'POST', 'category': '内容生成', 'icon': 'AudioOutlined', 'color': '#ec4899', 'is_coming_soon': True},
# 生活服务
{'name': '滑块验证码', 'description': '滑块验证码生成与验证', 'url_path': '/api/captcha/', 'method': 'GET', 'category': '生活服务', 'icon': 'SafetyOutlined', 'color': '#10b981'},
{'name': '图片验证码', 'description': '图片验证码生成与识别', 'url_path': '/api/image-captcha/', 'method': 'GET', 'category': '生活服务', 'icon': 'SafetyOutlined', 'color': '#3b82f6'},
{'name': 'TODO待办', 'description': '管理个人待办事项', 'url_path': '/api/todo/', 'method': 'GET', 'category': '生活服务', 'icon': 'CheckSquareOutlined', 'is_coming_soon': True},
]
for item_data in items_data:
cat_name = item_data.pop('category')
item_data['category'] = categories[cat_name]
item = ApiItem.objects.create(**item_data)
self.stdout.write(f'创建API: {item.name} ({item.method} {item.url_path})')
self.stdout.write(self.style.SUCCESS(
f'\n成功创建 {ApiCategory.objects.count()} 个分类, '
f'{ApiItem.objects.count()} 个API接口'
))
@@ -0,0 +1,119 @@
from django.core.management.base import BaseCommand
from apidirectory.models import ApiCategory, ApiItem
class Command(BaseCommand):
help = 'Seed API directory with initial data'
def handle(self, *args, **options):
self.stdout.write('Seeding API directory...')
categories_data = [
{'name': '数据查询', 'icon': 'SearchOutlined', 'sort_order': 1},
{'name': '位置服务', 'icon': 'EnvironmentOutlined', 'sort_order': 2},
{'name': '金融货币', 'icon': 'DollarOutlined', 'sort_order': 3},
{'name': '工具服务', 'icon': 'ToolOutlined', 'sort_order': 4},
{'name': '翻译服务', 'icon': 'TranslationOutlined', 'sort_order': 5},
]
categories = {}
for cat_data in categories_data:
cat, created = ApiCategory.objects.get_or_create(
name=cat_data['name'],
defaults=cat_data
)
categories[cat_data['name']] = cat
if created:
self.stdout.write(f' Created category: {cat.name}')
items_data = [
{
'name': '天气查询',
'description': '根据城市名称查询实时天气信息,支持全球城市和多种温度单位',
'url_path': '/api/weather',
'method': 'GET',
'category': '数据查询',
'is_new': True,
'color': '#10b981',
'image_url': 'https://images.unsplash.com/photo-1504608524841-42fe6f032b4b?w=800&h=600&fit=crop',
},
{
'name': '空气质量指数',
'description': '查询指定城市的空气质量指数AQI、PM2.5、PM10等环境数据',
'url_path': '/api/air-quality/',
'method': 'GET',
'category': '数据查询',
'is_new': True,
'color': '#3b82f6',
'image_url': 'https://images.unsplash.com/photo-1441974231531-c6227db76b6e?w=800&h=600&fit=crop',
},
{
'name': 'IP地址定位',
'description': '根据IP地址获取地理位置信息,支持自动识别客户端IP',
'url_path': '/api/getIpData/',
'method': 'GET',
'category': '位置服务',
'color': '#8b5cf6',
'image_url': 'https://images.unsplash.com/photo-1451187580459-43490279c0fa?w=800&h=600&fit=crop',
},
{
'name': '二维码生成',
'description': '根据URL或文本生成二维码图片',
'url_path': '/api/qrcode-generator/',
'method': 'POST',
'category': '工具服务',
'color': '#ec4899',
'image_url': 'https://images.unsplash.com/photo-1558591710-4b4a1ae0f04d?w=800&h=600&fit=crop',
},
{
'name': '百度翻译',
'description': '支持200+语言互译,自动检测源语言',
'url_path': '/api/baiduFanyi/',
'method': 'POST',
'category': '翻译服务',
'color': '#06b6d4',
'is_enabled': True,
'image_url': 'https://images.unsplash.com/photo-1456513080510-7bf3a84b82f8?w=800&h=600&fit=crop',
},
{
'name': '文本比较',
'description': '比较两段文本的差异,支持多种比较模式',
'url_path': '/api/tool/text-diff/',
'method': 'POST',
'category': '工具服务',
'color': '#14b8a6',
'image_url': 'https://images.unsplash.com/photo-1518770660439-4636190af475?w=800&h=600&fit=crop',
},
{
'name': '图片压缩',
'description': '在线压缩图片,支持批量处理和质量调节',
'url_path': '/api/tool/image-compressor',
'method': 'POST',
'category': '工具服务',
'color': '#84cc16',
'image_url': 'https://images.unsplash.com/photo-1542038784456-1ea8e935640e?w=800&h=600&fit=crop',
},
{
'name': '短链接生成',
'description': '将长链接转换为短链接,支持自定义后缀',
'url_path': '/api/shorturl/shorten/',
'method': 'POST',
'category': '工具服务',
'color': '#f97316',
'image_url': 'https://images.unsplash.com/photo-1557682172-298e090bd0f1?w=800&h=600&fit=crop',
},
]
for item_data in items_data:
cat_name = item_data.pop('category')
item_data['category'] = categories[cat_name]
item, created = ApiItem.objects.update_or_create(
url_path=item_data['url_path'],
defaults=item_data
)
if created:
self.stdout.write(f' Created item: {item.name} ({item.url_path})')
else:
self.stdout.write(f' Updated item: {item.name} ({item.url_path})')
self.stdout.write(self.style.SUCCESS('API directory seeded successfully!'))
+72
View File
@@ -0,0 +1,72 @@
# Generated by Django 5.2.16 on 2026-07-28 00:43
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='ApiCategory',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=50, unique=True, verbose_name='分类名称')),
('icon', models.CharField(default='AppstoreOutlined', max_length=100, verbose_name='图标名称')),
('sort_order', models.IntegerField(default=0, verbose_name='排序')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
],
options={
'verbose_name': 'API分类',
'verbose_name_plural': 'API分类',
'ordering': ['sort_order', '-created_at'],
},
),
migrations.CreateModel(
name='ApiItem',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100, verbose_name='API名称')),
('description', models.TextField(blank=True, default='', verbose_name='API描述')),
('icon', models.CharField(default='ApiOutlined', max_length=100, verbose_name='图标名称')),
('url_path', models.CharField(max_length=200, verbose_name='API路径')),
('method', models.CharField(choices=[('GET', 'GET'), ('POST', 'POST'), ('PUT', 'PUT'), ('DELETE', 'DELETE')], default='GET', max_length=10, verbose_name='请求方法')),
('is_enabled', models.BooleanField(default=True, verbose_name='是否启用')),
('is_new', models.BooleanField(default=False, verbose_name='是否为新API')),
('is_coming_soon', models.BooleanField(default=False, verbose_name='是否即将到来')),
('views_count', models.IntegerField(default=0, verbose_name='浏览次数')),
('color', models.CharField(blank=True, default='#667eea', max_length=20, verbose_name='主题颜色')),
('sort_order', models.IntegerField(default=0, verbose_name='排序')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
('category', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='items', to='apidirectory.apicategory', verbose_name='所属分类')),
],
options={
'verbose_name': 'API接口',
'verbose_name_plural': 'API接口',
'ordering': ['sort_order', '-created_at'],
},
),
migrations.CreateModel(
name='ApiFavorite',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='api_favorites', to=settings.AUTH_USER_MODEL, verbose_name='用户')),
('api_item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='favorites', to='apidirectory.apiitem', verbose_name='API接口')),
],
options={
'verbose_name': 'API收藏',
'verbose_name_plural': 'API收藏',
'ordering': ['-created_at'],
'unique_together': {('user', 'api_item')},
},
),
]
@@ -0,0 +1,16 @@
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('apidirectory', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='apiitem',
name='image_url',
field=models.URLField(blank=True, default='', max_length=500, verbose_name='图片URL'),
),
]
View File
+65
View File
@@ -0,0 +1,65 @@
from django.conf import settings
from django.db import models
class ApiCategory(models.Model):
name = models.CharField(max_length=50, unique=True, verbose_name='分类名称')
icon = models.CharField(max_length=100, default='AppstoreOutlined', verbose_name='图标名称')
sort_order = models.IntegerField(default=0, verbose_name='排序')
created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
class Meta:
ordering = ['sort_order', '-created_at']
verbose_name = 'API分类'
verbose_name_plural = 'API分类'
def __str__(self):
return self.name
class ApiItem(models.Model):
HTTP_METHOD_CHOICES = [
('GET', 'GET'),
('POST', 'POST'),
('PUT', 'PUT'),
('DELETE', 'DELETE'),
]
name = models.CharField(max_length=100, verbose_name='API名称')
description = models.TextField(default='', blank=True, verbose_name='API描述')
icon = models.CharField(max_length=100, default='ApiOutlined', verbose_name='图标名称')
url_path = models.CharField(max_length=200, verbose_name='API路径')
method = models.CharField(max_length=10, choices=HTTP_METHOD_CHOICES, default='GET', verbose_name='请求方法')
category = models.ForeignKey(ApiCategory, on_delete=models.SET_NULL, null=True, blank=True, related_name='items', verbose_name='所属分类')
is_enabled = models.BooleanField(default=True, verbose_name='是否启用')
is_new = models.BooleanField(default=False, verbose_name='是否为新API')
is_coming_soon = models.BooleanField(default=False, verbose_name='是否即将到来')
views_count = models.IntegerField(default=0, verbose_name='浏览次数')
color = models.CharField(max_length=20, default='#667eea', blank=True, verbose_name='主题颜色')
image_url = models.URLField(max_length=500, blank=True, default='', verbose_name='图片URL')
sort_order = models.IntegerField(default=0, verbose_name='排序')
created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
updated_at = models.DateTimeField(auto_now=True, verbose_name='更新时间')
class Meta:
ordering = ['sort_order', '-created_at']
verbose_name = 'API接口'
verbose_name_plural = 'API接口'
def __str__(self):
return self.name
class ApiFavorite(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='api_favorites', verbose_name='用户')
api_item = models.ForeignKey(ApiItem, on_delete=models.CASCADE, related_name='favorites', verbose_name='API接口')
created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
class Meta:
verbose_name = 'API收藏'
verbose_name_plural = 'API收藏'
unique_together = ['user', 'api_item']
ordering = ['-created_at']
def __str__(self):
return f'{self.user} favorites {self.api_item}'
+49
View File
@@ -0,0 +1,49 @@
from rest_framework import serializers
from .models import ApiCategory, ApiItem, ApiFavorite
class ApiCategorySerializer(serializers.ModelSerializer):
item_count = serializers.SerializerMethodField()
class Meta:
model = ApiCategory
fields = ['id', 'name', 'icon', 'sort_order', 'item_count', 'created_at']
read_only_fields = ['id', 'created_at']
def get_item_count(self, obj):
return obj.items.filter(is_enabled=True).count()
class ApiItemSerializer(serializers.ModelSerializer):
category_name = serializers.CharField(source='category.name', read_only=True, default='')
favorites_count = serializers.SerializerMethodField()
is_favorited = serializers.SerializerMethodField()
class Meta:
model = ApiItem
fields = [
'id', 'name', 'description', 'icon', 'url_path', 'method',
'category', 'category_name', 'is_enabled', 'is_new', 'is_coming_soon',
'views_count', 'color', 'image_url', 'sort_order', 'created_at', 'updated_at',
'favorites_count', 'is_favorited'
]
read_only_fields = ['id', 'created_at', 'updated_at']
def get_favorites_count(self, obj):
return obj.favorites.count()
def get_is_favorited(self, obj):
request = self.context.get('request')
if request and request.user.is_authenticated:
return obj.favorites.filter(user=request.user).exists()
return False
class ApiFavoriteSerializer(serializers.ModelSerializer):
api_item_name = serializers.CharField(source='api_item.name', read_only=True)
api_item_url = serializers.CharField(source='api_item.url_path', read_only=True)
class Meta:
model = ApiFavorite
fields = ['id', 'user', 'api_item', 'api_item_name', 'api_item_url', 'created_at']
read_only_fields = ['id', 'created_at']
+11
View File
@@ -0,0 +1,11 @@
from django.urls import path
from . import views
app_name = 'apidirectory'
urlpatterns = [
path('categories/', views.CategoryListView.as_view()),
path('items/', views.ApiItemListView.as_view()),
path('items/<int:pk>/', views.ApiItemDetailView.as_view()),
path('items/<int:pk>/favorite/', views.FavoriteToggleView.as_view()),
]
+141
View File
@@ -0,0 +1,141 @@
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.permissions import AllowAny, IsAuthenticated
from django.http import JsonResponse
from django.db.models import Count
from drf_yasg.utils import swagger_auto_schema
from drf_yasg import openapi
from .models import ApiCategory, ApiItem, ApiFavorite
from .serializers import ApiCategorySerializer, ApiItemSerializer
class CategoryListView(APIView):
permission_classes = [AllowAny]
@swagger_auto_schema(
operation_summary='获取API分类列表',
operation_description='获取所有API分类及其关联的API数量',
tags=['API接口大全'],
responses={200: '成功'}
)
def get(self, request):
categories = ApiCategory.objects.prefetch_related('items').all()
serializer = ApiCategorySerializer(categories, many=True)
return JsonResponse({
'code': 200,
'message': 'success',
'data': serializer.data
})
class ApiItemListView(APIView):
permission_classes = [AllowAny]
@swagger_auto_schema(
operation_summary='获取API列表',
operation_description='获取API列表,支持分类筛选、搜索、排序',
tags=['API接口大全'],
manual_parameters=[
openapi.Parameter('category_id', openapi.IN_QUERY, description='分类ID', type=openapi.TYPE_INTEGER),
openapi.Parameter('search', openapi.IN_QUERY, description='搜索关键词', type=openapi.TYPE_STRING),
openapi.Parameter('ordering', openapi.IN_QUERY, description='排序字段', type=openapi.TYPE_STRING),
],
responses={200: '成功'}
)
def get(self, request):
category_id = request.query_params.get('category_id')
search = request.query_params.get('search', '')
ordering = request.query_params.get('ordering', '')
items = ApiItem.objects.select_related('category').annotate(
annotated_favorites_count=Count('favorites')
).filter(is_enabled=True)
if category_id:
items = items.filter(category_id=category_id)
if search:
items = items.filter(name__icontains=search) | items.filter(description__icontains=search)
if ordering:
if ordering == '-favorites_count':
items = items.order_by('-annotated_favorites_count')
elif ordering == 'favorites_count':
items = items.order_by('annotated_favorites_count')
else:
items = items.order_by(ordering)
else:
items = items.order_by('sort_order', '-created_at')
serializer = ApiItemSerializer(items, many=True, context={'request': request})
return JsonResponse({
'code': 200,
'message': 'success',
'data': serializer.data
})
class ApiItemDetailView(APIView):
permission_classes = [AllowAny]
@swagger_auto_schema(
operation_summary='获取API详情',
operation_description='获取单个API的详细信息',
tags=['API接口大全'],
responses={200: '成功'}
)
def get(self, request, pk):
try:
item = ApiItem.objects.select_related('category').get(pk=pk, is_enabled=True)
serializer = ApiItemSerializer(item, context={'request': request})
return JsonResponse({
'code': 200,
'message': 'success',
'data': serializer.data
})
except ApiItem.DoesNotExist:
return JsonResponse({
'code': 404,
'message': 'API不存在',
'data': None
}, status=status.HTTP_404_NOT_FOUND)
class FavoriteToggleView(APIView):
permission_classes = [IsAuthenticated]
@swagger_auto_schema(
operation_summary='切换API收藏状态',
operation_description='收藏或取消收藏API接口',
tags=['API接口大全'],
responses={200: '成功'}
)
def post(self, request, pk):
try:
api_item = ApiItem.objects.get(pk=pk, is_enabled=True)
except ApiItem.DoesNotExist:
return JsonResponse({
'code': 404,
'message': 'API不存在',
'data': None
}, status=status.HTTP_404_NOT_FOUND)
fav, created = ApiFavorite.objects.get_or_create(user=request.user, api_item=api_item)
if not created:
fav.delete()
is_favorited = False
else:
is_favorited = True
favorites_count = ApiFavorite.objects.filter(api_item=api_item).count()
return JsonResponse({
'code': 200,
'message': 'success',
'data': {
'id': api_item.id,
'is_favorited': is_favorited,
'favorites_count': favorites_count
}
})