This commit is contained in:
Jane Doe
2024-09-25 12:18:07 +08:00
parent 3f369584fb
commit 9992293b9f
124 changed files with 2770 additions and 975 deletions

8
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,8 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

32
.idea/Ai_Admin.iml generated Normal file
View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="FacetManager">
<facet type="django" name="Django">
<configuration>
<option name="rootFolder" value="$MODULE_DIR$" />
<option name="settingsModule" value="WebSite/settings.py" />
<option name="manageScript" value="$MODULE_DIR$/manage.py" />
<option name="environment" value="&lt;map/&gt;" />
<option name="doNotUseTestRunner" value="false" />
<option name="trackFilePattern" value="migrations" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="PyDocumentationSettings">
<option name="format" value="PLAIN" />
<option name="myDocStringFormat" value="Plain" />
</component>
<component name="TemplatesService">
<option name="TEMPLATE_CONFIGURATION" value="Django" />
<option name="TEMPLATE_FOLDERS">
<list>
<option value="$MODULE_DIR$/templates" />
</list>
</option>
</component>
</module>

30
.idea/WebSite.iml generated Normal file
View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="FacetManager">
<facet type="django" name="Django">
<configuration>
<option name="rootFolder" value="$MODULE_DIR$" />
<option name="settingsModule" value="WebSite/settings.py" />
<option name="manageScript" value="$MODULE_DIR$/manage.py" />
<option name="environment" value="&lt;map/&gt;" />
<option name="doNotUseTestRunner" value="false" />
<option name="trackFilePattern" value="migrations" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.venv" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="TemplatesService">
<option name="TEMPLATE_CONFIGURATION" value="Django" />
<option name="TEMPLATE_FOLDERS">
<list>
<option value="$MODULE_DIR$/../WebSite\templates" />
</list>
</option>
</component>
</module>

23
.idea/deployment.xml generated Normal file
View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PublishConfigData" autoUpload="Always" remoteFilesAllowedToDisappearOnAutoupload="false" confirmBeforeUploading="false">
<option name="confirmBeforeUploading" value="false" />
<serverData>
<paths name="光映">
<serverdata>
<mappings>
<mapping deploy="/" local="$PROJECT_DIR$" web="/" />
</mappings>
</serverdata>
</paths>
<paths name="阿里云">
<serverdata>
<mappings>
<mapping deploy="/www/wwwroot/Ai_Admin" local="$PROJECT_DIR$" web="http://www.typeframes.cc/" />
</mappings>
</serverdata>
</paths>
</serverData>
<option name="myAutoUpload" value="ALWAYS" />
</component>
</project>

View File

@@ -0,0 +1,19 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="DuplicatedCode" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<Languages>
<language minSize="822" name="Python" />
</Languages>
</inspection_tool>
<inspection_tool class="PyPep8NamingInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="ignoredErrors">
<list>
<option value="N802" />
<option value="N806" />
<option value="N803" />
</list>
</option>
</inspection_tool>
</profile>
</component>

View File

@@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

7
.idea/misc.xml generated Normal file
View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Black">
<option name="sdkName" value="Python 3.10 (WebSite)" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (WebSite)" project-jdk-type="Python SDK" />
</project>

8
.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/Ai_Admin.iml" filepath="$PROJECT_DIR$/.idea/Ai_Admin.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

21
.idea/webServers.xml generated Normal file
View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="WebServers">
<option name="servers">
<webServer id="095cae3e-e6f2-4072-b09f-4593cdd5fc50" name="阿里云" url="http://www.typeframes.cc/">
<fileTransfer accessType="SFTP" host="107.155.87.31" port="22" sshConfigId="fb91fe58-7f4f-40e7-8533-bb4699b648c0" sshConfig="root@107.155.87.31:22 password">
<advancedOptions>
<advancedOptions dataProtectionLevel="Private" passiveMode="true" shareSSLContext="true" isUseSudo="true" />
</advancedOptions>
</fileTransfer>
</webServer>
<webServer id="5c18341b-89da-4b9e-90c7-51206d19b89f" name="光映">
<fileTransfer rootFolder="/www/wwwroot/Ai_Admin" accessType="SFTP" host="107.155.87.31" port="22" sshConfigId="fb91fe58-7f4f-40e7-8533-bb4699b648c0" sshConfig="root@107.155.87.31:22 password">
<advancedOptions>
<advancedOptions dataProtectionLevel="Private" passiveMode="true" shareSSLContext="true" />
</advancedOptions>
</fileTransfer>
</webServer>
</option>
</component>
</project>

View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,12 +1,36 @@
import json import json
import os
import re import re
from django.contrib.auth import authenticate, login as auth_login import uuid
from datetime import datetime
from django.contrib.auth import logout
from django.contrib.auth import authenticate, login as auth_login, update_session_auth_hash
from django.core.exceptions import ValidationError
from django.core.serializers.json import DjangoJSONEncoder
from django.db import IntegrityError
from django.db.models import Count, Sum
from django.http import JsonResponse from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from django.shortcuts import render, redirect from django.shortcuts import render, redirect,get_object_or_404
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from allauth.account.auth_backends import AuthenticationBackend from allauth.account.auth_backends import AuthenticationBackend
from .models import User from django.views.decorators.http import require_http_methods
from django.views.generic import View
from django.core.cache import cache
from captcha.image import ImageCaptcha
import random
from WebSite import settings
from .models import User, WebsiteInfo, Article, Order, VideoGeneration
from django.http import JsonResponse, HttpResponse
import string
def generate_captcha(request):
image = ImageCaptcha()
captcha_text = ''.join(random.choices(string.ascii_uppercase + string.digits, k=4)) # 生成随机验证码
cache.set('captcha', captcha_text, timeout=300) # 存储验证码
image_data = image.generate(captcha_text)
return HttpResponse(image_data, content_type='image/png')
@csrf_exempt @csrf_exempt
def admin_login_view(request): def admin_login_view(request):
@@ -18,9 +42,14 @@ def admin_login_view(request):
body = json.loads(request.body) body = json.loads(request.body)
username = body.get('username') username = body.get('username')
password = body.get('password') password = body.get('password')
captcha_input = body.get('captcha')
captcha_stored = cache.get('captcha')
if captcha_input != captcha_stored:
return JsonResponse({'code': 400, 'message': '验证码错误'})
user = authenticate(request, username=username, password=password) user = authenticate(request, username=username, password=password)
if user is not None: if user is not None:
user.backend = 'allauth.account.auth_backends.AuthenticationBackend'
auth_login(request, user) auth_login(request, user)
return JsonResponse({'code': 200, 'message': '登录成功'}) return JsonResponse({'code': 200, 'message': '登录成功'})
else: else:
@@ -35,3 +64,469 @@ def admin_login_view(request):
@login_required @login_required
def admin_home_view(request): def admin_home_view(request):
return render(request, 'admin/home.html') return render(request, 'admin/home.html')
@login_required
@require_http_methods(["GET", "POST"])
@csrf_exempt # 根据需要选择是否使用
def admin_article_list(request):
articles = list(Article.objects.all().values('id', 'chinese_title', 'english_title', 'published_at', 'status'))
if request.method == 'GET':
# GET 请求返回页面
return render(request, 'admin/article-list.html', {"articles":articles})
elif request.method == 'POST':
# 获取文章列表并转换为 JSON 格式
articles = list(Article.objects.all().values('id', 'chinese_title', 'english_title', 'published_at', 'status'))
# 手动格式化 published_at 字段为字符串
for article in articles:
if article['published_at']:
article['published_at'] = article['published_at'].strftime('%Y-%m-%d %H:%M:%S')
else:
article['published_at'] = '未发布'
# 返回适配 layui 表格的数据格式
data = {
"code": 200,
"msg": "成功",
"count": len(articles), # 返回文章数量
"data": articles # 直接返回文章数据
}
return JsonResponse(data) # 返回 JSON 响应
@login_required
def ArticleDetailView(request, pk, lang):
# 确认用户登录
if not request.user.is_authenticated:
return redirect('admin_login')
# 获取指定的文章
article = get_object_or_404(Article, pk=pk)
# 根据 URL 中的 lang 参数选择显示的语言
if lang == 'en':
title = article.english_title
keywords = article.english_keywords
content = article.english_content
else:
title = article.chinese_title
keywords = article.chinese_keywords
content = article.chinese_content
# 渲染模板并传递数据
context = {
'title': title,
'keywords': keywords,
'content': content,
'published_at': article.published_at,
}
return render(request, 'admin/article_detail.html', context)
@login_required
@csrf_exempt # 根据需要选择是否使用
def admin_site_info_view(request):
website_info = WebsiteInfo.objects.first()
if request.method == 'POST':
try:
# 获取 JSON 数据
data = json.loads(request.body)
# 从数据中提取信息
domain_en = data.get('domain_en', '')
title_en = data.get('title_en', '')
keywords_en = data.get('keywords_en', '')
description_en = data.get('description_en', '')
domain_zh = data.get('domain_zh', '')
title_zh = data.get('title_zh', '')
keywords_zh = data.get('keywords_zh', '')
description_zh = data.get('description_zh', '')
# 数据验证(示例)
if not domain_en or not domain_zh:
return JsonResponse({
'code': 400,
'message': '域名不能为空',
'data': {}
})
# 更新网站配置信息
website_info.domain_en = domain_en
website_info.title_en = title_en
website_info.keywords_en = keywords_en
website_info.description_en = description_en
website_info.domain_zh = domain_zh
website_info.title_zh = title_zh
website_info.keywords_zh = keywords_zh
website_info.description_zh = description_zh
website_info.save()
# 返回统一的响应格式
return JsonResponse({
'code': 200,
'message': '保存成功',
'data': {}
})
except json.JSONDecodeError:
return JsonResponse({
'code': 400,
'message': '无效的 JSON 格式',
'data': {}
})
except ValidationError as ve:
return JsonResponse({
'code': 400,
'message': str(ve),
'data': {}
})
except IntegrityError:
return JsonResponse({
'code': 500,
'message': '数据库错误,请重试',
'data': {}
})
except Exception as e:
return JsonResponse({
'code': 500,
'message': '服务器错误,请重试',
'data': {'error': str(e)}
})
# 返回网站配置信息
# data = {
# 'domain_en': website_info.domain_en,
# 'title_en': website_info.title_en,
# 'keywords_en': website_info.keywords_en,
# 'description_en': website_info.description_en,
# 'domain_zh': website_info.domain_zh,
# 'title_zh': website_info.title_zh,
# 'keywords_zh': website_info.keywords_zh,
# 'description_zh': website_info.description_zh,
# }
else:
return render(request, 'admin/site-info.html', {"website_info": website_info})
@login_required
@csrf_exempt
def save_article(request, pk=None):
if request.method == "GET":
# 如果传递了文章 ID则为编辑模式否则为添加模式
article = get_object_or_404(Article, pk=pk) if pk else None
return render(request, 'admin/add-article.html', {
'article': article, # 传递文章对象给模板
'page_title': '修改文章' if article else '添加文章',
})
elif request.method == "POST":
try:
data = json.loads(request.body)
chinese_title = data.get('chinese_title', '')
english_title = data.get('english_title', '')
chinese_content = data.get('chinese_content', '')
english_content = data.get('english_content', '')
image_url = data.get('image_url', '')
status = data.get('status', 'pending')
# 检查是否传递了文章 ID 以判断是编辑还是添加
if pk:
article = get_object_or_404(Article, pk=pk)
else:
article = Article()
# 设置文章内容
article.chinese_title = chinese_title
article.english_title = english_title
article.chinese_content = chinese_content
article.english_content = english_content
article.image_url = image_url
article.status = status
article.published_at = datetime.now() if status == 'published' else None
# 保存文章
article.save()
return JsonResponse({'code': 200, 'message': '文章保存成功', 'article_id': article.id})
except Exception as e:
return JsonResponse({'code': 500, 'message': str(e)})
# 图片上传接口(专用于编辑器)
@login_required
@csrf_exempt
@require_http_methods(["POST"])
def upload_editor_image(request):
"""
上传图片并返回图片URL供富文本编辑器使用
"""
try:
# 验证用户是否已登录
if not request.user.is_authenticated:
return JsonResponse({'code': 401, 'message': '用户未登录'})
# 检查是否包含上传的图片
if 'file' not in request.FILES:
return JsonResponse({'code': 400, 'message': '没有上传图片'})
# 获取上传的图片文件
image_file = request.FILES['file']
# 使用 UUID 为文件生成唯一名称,并保存到服务器指定目录
file_extension = image_file.name.split('.')[-1]
filename = f"{uuid.uuid4()}.{file_extension}"
file_path = os.path.join(settings.MEDIA_ROOT, 'editor_images', filename)
# 将文件写入服务器
with open(file_path, 'wb') as f:
for chunk in image_file.chunks():
f.write(chunk)
# 生成图片的访问 URL
image_url = f"{settings.DOMAIN}{settings.MEDIA_URL}editor_images/{filename}"
data_json={
"code": 0
,"msg": "图片上传成功"
,"data": {
"src": image_url
,"title": None
}
}
return JsonResponse(data_json)
except Exception as e:
return JsonResponse({'code': 500, 'message': str(e)})
class ArticleManagementView(View):
def delete(self, request, article_id):
if not request.user.is_authenticated or not request.user.is_staff:
return JsonResponse({'code': 403, 'message': '没有权限执行此操作'})
try:
article = Article.objects.get(id=article_id)
article.delete()
return JsonResponse({'code': 200, 'message': '删除成功'})
except Article.DoesNotExist:
return JsonResponse({'code': 404, 'message': '文章未找到'})
def post(self, request, article_id):
if not request.user.is_authenticated or not request.user.is_staff:
return JsonResponse({'code': 403, 'message': '没有权限执行此操作'})
try:
article = Article.objects.get(id=article_id)
# 审核文章逻辑
article.status = 'published' # 假设审核通过状态设为 published
article.published_at = datetime.now() # 设置当前时间为发布时间
article.save()
return JsonResponse({'code': 200, 'message': '审核成功'})
except Article.DoesNotExist:
return JsonResponse({'code': 404, 'message': '文章未找到'})
from django.core.paginator import Paginator, EmptyPage, InvalidPage
@login_required
@require_http_methods(["GET", "POST"])
@csrf_exempt
def admin_user_list(request):
if request.method == 'GET':
# GET 请求返回页面
return render(request, 'admin/user-list.html')
elif request.method == 'POST':
# 获取分页参数
page = request.POST.get('page', 1)
limit = request.POST.get('limit', 10)
# 获取用户列表并转换为 JSON 格式,按照注册时间最新排序
users_queryset = User.objects.all().order_by('-created_at').values(
'id', 'username', 'email', 'phone', 'is_member', 'points', 'created_at',
'login_count', 'last_login_ip', 'source'
)
paginator = Paginator(users_queryset, limit)
current_page = paginator.page(page)
# 格式化创建时间字段
users = list(current_page.object_list)
for user in users:
if user['created_at']:
user['created_at'] = user['created_at'].strftime('%Y-%m-%d %H:%M:%S')
# 返回适配 layui 表格的数据格式
data = {
"code": 0,
"msg": "成功",
"count": paginator.count, # 总记录数
"data": users
}
return JsonResponse(data)
@login_required
@require_http_methods(["GET", "POST"])
@csrf_exempt
def admin_task_list(request):
if request.method == 'GET':
# GET 请求返回页面
return render(request, 'admin/task-list.html')
elif request.method == 'POST':
# 获取分页参数
try:
page = int(request.POST.get('page', 1))
limit = int(request.POST.get('limit', 10))
except ValueError:
page = 1
limit = 10
# 获取所有任务按时间排序
tasks_queryset = VideoGeneration.objects.all().order_by('-created_at')
# 统计不同类型视频数量、状态数量
video_type_count = VideoGeneration.objects.values('slug').annotate(count=Count('slug'))
status_count = {
'completed': tasks_queryset.filter(status='completed').count(),
'in_progress': tasks_queryset.filter(status='in_progress').count(),
'failed': tasks_queryset.filter(status='failed').count(),
}
# 分页
paginator = Paginator(tasks_queryset, limit)
try:
current_page = paginator.page(page)
except (EmptyPage, InvalidPage):
current_page = paginator.page(1)
# 格式化时间字段并构建任务列表
tasks = []
for task in current_page.object_list:
tasks.append({
'id': task.id,
'video_id': task.video_id,
'user_id': task.user_id,
'text': task.text if task.text else '无生成文本',
'slug': task.slug,
'time_duration': task.time_duration if task.time_duration else '无时长',
'video_url': task.video_url,
'created_at': task.created_at.strftime('%Y-%m-%d %H:%M:%S'),
'status': task.status,
})
# 返回适配layui表格的数据格式
data = {
"code": 0,
"msg": "成功",
"count": paginator.count,
"data": tasks,
"video_type_count": list(video_type_count), # 各种类型视频数量
"status_count": status_count # 各个状态数量
}
return JsonResponse(data)
@login_required
@require_http_methods(["GET", "POST"])
@csrf_exempt
def admin_order_list(request):
if request.method == 'GET':
return render(request, 'admin/order-list.html')
elif request.method == 'POST':
# 获取订单列表
order_queryset = Order.objects.all().order_by('-created_at')
# 获取分页参数
page = int(request.POST.get('page', 1))
limit = int(request.POST.get('limit', 10))
# 分页处理
paginator = Paginator(order_queryset, limit)
orders = paginator.get_page(page)
# 转换数据为 JSON 格式
order_data = list(orders.object_list.values('order_id', 'username', 'plan__title', 'amount', 'payment_method', 'status', 'created_at'))
# 手动格式化创建时间字段
for order in order_data:
order['created_at'] = order['created_at'].strftime('%Y-%m-%d %H:%M:%S')
# 翻译支付方式和状态
order['payment_method'] = '支付宝' if order['payment_method'] == 'alipay' else '贝宝'
order['status'] = {
'pending': '待处理',
'completed': '已完成',
'canceled': '已取消',
'failed': '失败'
}.get(order['status'], order['status'])
# 支付方式统计
payment_method_count = Order.objects.values('payment_method').annotate(count=Count('payment_method'))
# 各个状态的订单数量
status_count = {
'completed': Order.objects.filter(status='completed').count(),
'failed': Order.objects.filter(status='failed').count(),
'canceled': Order.objects.filter(status='canceled').count(),
'pending': Order.objects.filter(status='pending').count(),
}
# 支付方式金额统计(仅统计已完成的订单)
total_income_by_payment_method = Order.objects.filter(status='completed').values('payment_method').annotate(
total_income=Sum('amount')
)
# 转换支付方式为中文名称
total_income_by_payment_method = [
{
'payment_method': '支付宝' if method['payment_method'] == 'alipay' else '贝宝',
'total_income': method['total_income']
}
for method in total_income_by_payment_method
]
# 返回数据
data = {
"code": 0,
"msg": "成功",
"count": paginator.count,
"data": order_data,
"payment_method_count": list(payment_method_count),
"status_count": status_count,
"total_income_by_payment_method": total_income_by_payment_method
}
return JsonResponse(data)
@login_required
@require_http_methods(["POST"])
@csrf_exempt
def admin_change_password(request):
user = request.user
data = json.loads(request.body)
old_password = data.get('old_password')
new_password = data.get('new_password')
# 验证旧密码
if not user.check_password(old_password):
return JsonResponse({'code': 400, 'msg': '旧密码错误'})
# 验证新密码是否为空
if not new_password:
return JsonResponse({'code': 400, 'msg': '新密码不能为空'})
# 更新密码
user.set_password(new_password)
user.save()
# 确保更新密码后不会自动退出登录
update_session_auth_hash(request, user)
return JsonResponse({'code': 200, 'msg': '密码修改成功'})
@login_required
@require_http_methods(["POST", "GET"])
def admin_logout(request):
# 退出登录
logout(request)
# 重定向到登录页面
return redirect('admin_login')

View File

@@ -166,7 +166,8 @@ def text_to_video(request):
pid=uuid, pid=uuid,
media_type=f'{model}_text_to_video', media_type=f'{model}_text_to_video',
slug=f'text-to-video', slug=f'text-to-video',
ratio=f"{width}:{height}" ratio=f"{width}:{height}",
time_duration=time_duration
) )
video_generation.save() video_generation.save()
@@ -279,7 +280,8 @@ def image_to_video(request):
pid=uuid, pid=uuid,
media_type=f'{model}_img_to_video', media_type=f'{model}_img_to_video',
slug=f'img-to-video', slug=f'img-to-video',
ratio="auto" # 图片自适应宽高 ratio="auto", # 图片自适应宽高
time_duration=time_duration
) )
video_generation.save() video_generation.save()

View File

@@ -1,16 +1,225 @@
# Generated by Django 5.0.7 on 2024-07-16 15:57 # Generated by Django 5.0.7 on 2024-09-23 22:59
import datetime
import django.contrib.auth.models
import django.contrib.auth.validators
import django.db.models.deletion import django.db.models.deletion
from datetime import datetime import django.utils.timezone
from django.conf import settings
from django.db import migrations, models from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
initial = True initial = True
dependencies = [] dependencies = [
("auth", "0012_alter_user_first_name_max_length"),
]
operations = [ operations = [
migrations.CreateModel(
name="Article",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("chinese_title", models.CharField(max_length=255)),
("english_title", models.CharField(max_length=255)),
("chinese_keywords", models.TextField()),
("english_keywords", models.TextField()),
("chinese_content", models.TextField()),
("english_content", models.TextField()),
("source_url", models.URLField(blank=True, null=True)),
("published_at", models.DateTimeField(blank=True, null=True)),
("image_url", models.URLField(blank=True, null=True)),
(
"status",
models.CharField(
choices=[("published", "已发布"), ("pending", "待审核")],
default="draft",
max_length=10,
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
],
options={
"verbose_name": "Article",
"verbose_name_plural": "Article",
"ordering": ["-created_at"],
},
),
migrations.CreateModel(
name="EmailVerificationCode",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("email", models.EmailField(max_length=254)),
("code", models.CharField(max_length=6)),
("created_at", models.DateTimeField(auto_now_add=True)),
("is_used", models.BooleanField(default=False)),
],
),
migrations.CreateModel(
name="Plan",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("title", models.CharField(max_length=100, unique=True)),
("description", models.TextField()),
("price", models.DecimalField(decimal_places=2, max_digits=10)),
("credits_per_month", models.IntegerField(default=0)),
("created_at", models.DateTimeField(default=datetime.datetime.now)),
("updated_at", models.DateTimeField(auto_now=True)),
("is_promotional", models.BooleanField(default=False)),
("unlimited_exports", models.BooleanField(default=False)),
("smart_music_sync", models.BooleanField(default=False)),
],
),
migrations.CreateModel(
name="SMSVerificationCode",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("phone_number", models.CharField(max_length=15)),
("code", models.CharField(max_length=6)),
("created_at", models.DateTimeField(auto_now_add=True)),
("is_used", models.BooleanField(default=False)),
],
),
migrations.CreateModel(
name="WebsiteInfo",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"domain_en",
models.CharField(default=True, max_length=255, verbose_name="域名"),
),
(
"domain_zh",
models.CharField(default=True, max_length=255, verbose_name="域名"),
),
(
"title_en",
models.CharField(default=True, max_length=255, verbose_name="英文标题"),
),
(
"title_zh",
models.CharField(default=True, max_length=255, verbose_name="中文标题"),
),
("keywords_en", models.TextField(default=True, verbose_name="英文关键词")),
("keywords_zh", models.TextField(default=True, verbose_name="中文关键词")),
("description_en", models.TextField(default=True, verbose_name="英文描述")),
("description_zh", models.TextField(default=True, verbose_name="中文描述")),
(
"created_at",
models.DateTimeField(auto_now_add=True, verbose_name="创建时间"),
),
(
"updated_at",
models.DateTimeField(auto_now=True, verbose_name="更新时间"),
),
],
options={
"verbose_name": "网站信息",
"verbose_name_plural": "网站信息",
"db_table": "website_info",
"ordering": ["-created_at"],
},
),
migrations.CreateModel(
name="WebsiteAccessLog",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("ip_address", models.GenericIPAddressField(verbose_name="访问IP")),
(
"browser_language",
models.CharField(
blank=True, max_length=200, null=True, verbose_name="浏览器语言"
),
),
(
"referrer",
models.TextField(blank=True, null=True, verbose_name="来源URL"),
),
("request_path", models.CharField(max_length=255, verbose_name="请求路径")),
(
"request_method",
models.CharField(max_length=10, verbose_name="请求方法"),
),
(
"user_agent",
models.TextField(blank=True, null=True, verbose_name="用户代理"),
),
(
"device_type",
models.CharField(
blank=True, max_length=50, null=True, verbose_name="设备类型"
),
),
(
"access_time",
models.DateTimeField(auto_now_add=True, verbose_name="访问时间"),
),
(
"access_time_bj",
models.DateTimeField(blank=True, null=True, verbose_name="北京时间"),
),
(
"access_date",
models.DateField(blank=True, null=True, verbose_name="访问日期"),
),
],
options={
"verbose_name": "网站访问日志",
"verbose_name_plural": "网站访问日志",
"db_table": "website_access_log",
"unique_together": {("ip_address", "access_date")},
},
),
migrations.CreateModel( migrations.CreateModel(
name="User", name="User",
fields=[ fields=[
@@ -23,26 +232,84 @@ class Migration(migrations.Migration):
verbose_name="ID", verbose_name="ID",
), ),
), ),
("username", models.CharField(max_length=150, unique=True)), ("password", models.CharField(max_length=128, verbose_name="password")),
(
"last_login",
models.DateTimeField(
blank=True, null=True, verbose_name="last login"
),
),
(
"is_superuser",
models.BooleanField(
default=False,
help_text="Designates that this user has all permissions without explicitly assigning them.",
verbose_name="superuser status",
),
),
(
"username",
models.CharField(
error_messages={
"unique": "A user with that username already exists."
},
help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.",
max_length=150,
unique=True,
validators=[
django.contrib.auth.validators.UnicodeUsernameValidator()
],
verbose_name="username",
),
),
(
"first_name",
models.CharField(
blank=True, max_length=150, verbose_name="first name"
),
),
(
"last_name",
models.CharField(
blank=True, max_length=150, verbose_name="last name"
),
),
( (
"email", "email",
models.EmailField( models.EmailField(
blank=True, max_length=254, null=True, unique=True blank=True, max_length=254, verbose_name="email address"
),
),
(
"is_staff",
models.BooleanField(
default=False,
help_text="Designates whether the user can log into this admin site.",
verbose_name="staff status",
),
),
(
"is_active",
models.BooleanField(
default=True,
help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.",
verbose_name="active",
),
),
(
"date_joined",
models.DateTimeField(
default=django.utils.timezone.now, verbose_name="date joined"
), ),
), ),
( (
"phone", "phone",
models.CharField(blank=True, max_length=20, null=True, unique=True), models.CharField(blank=True, max_length=20, null=True, unique=True),
), ),
(
"password_hash",
models.CharField(blank=True, max_length=128, null=True),
),
( (
"google_id", "google_id",
models.CharField(blank=True, max_length=50, null=True, unique=True), models.CharField(blank=True, max_length=50, null=True, unique=True),
), ),
("is_active", models.BooleanField(default=True)),
("is_member", models.BooleanField(default=False)), ("is_member", models.BooleanField(default=False)),
("points", models.IntegerField(default=0)), ("points", models.IntegerField(default=0)),
("referral_code", models.CharField(max_length=50, unique=True)), ("referral_code", models.CharField(max_length=50, unique=True)),
@@ -50,8 +317,30 @@ class Migration(migrations.Migration):
"commission_rate", "commission_rate",
models.DecimalField(decimal_places=2, default=0.0, max_digits=5), models.DecimalField(decimal_places=2, default=0.0, max_digits=5),
), ),
("created_at", models.DateTimeField(default=datetime.now)), ("created_at", models.DateTimeField(default=datetime.datetime.now)),
("updated_at", models.DateTimeField(auto_now=True)), ("updated_at", models.DateTimeField(auto_now=True)),
("login_count", models.IntegerField(default=0)),
("last_login_ip", models.GenericIPAddressField(blank=True, null=True)),
("membership_start", models.DateTimeField(blank=True, null=True)),
("membership_end", models.DateTimeField(blank=True, null=True)),
(
"openid_used",
models.CharField(
blank=True, max_length=150, null=True, unique=True
),
),
("source", models.CharField(default="web", max_length=50)),
(
"groups",
models.ManyToManyField(
blank=True,
help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.",
related_name="user_set",
related_query_name="user",
to="auth.group",
verbose_name="groups",
),
),
( (
"invited_by", "invited_by",
models.ForeignKey( models.ForeignKey(
@@ -59,13 +348,32 @@ class Migration(migrations.Migration):
null=True, null=True,
on_delete=django.db.models.deletion.SET_NULL, on_delete=django.db.models.deletion.SET_NULL,
related_name="invitees", related_name="invitees",
to="WebAdmin.user", to=settings.AUTH_USER_MODEL,
),
),
(
"user_permissions",
models.ManyToManyField(
blank=True,
help_text="Specific permissions for this user.",
related_name="user_set",
related_query_name="user",
to="auth.permission",
verbose_name="user permissions",
), ),
), ),
], ],
options={
"verbose_name": "user",
"verbose_name_plural": "users",
"abstract": False,
},
managers=[
("objects", django.contrib.auth.models.UserManager()),
],
), ),
migrations.CreateModel( migrations.CreateModel(
name="Referral", name="VideoGeneration",
fields=[ fields=[
( (
"id", "id",
@@ -76,25 +384,42 @@ class Migration(migrations.Migration):
verbose_name="ID", verbose_name="ID",
), ),
), ),
("video_id", models.CharField(default="", max_length=255)),
("text", models.TextField(null=True)),
("voice_name", models.CharField(max_length=50, null=True)),
( (
"commission_amount", "style",
models.DecimalField(decimal_places=2, max_digits=10), models.CharField(default="general", max_length=50, null=True),
), ),
("created_at", models.DateTimeField(default=datetime.now)), ("rate", models.IntegerField(default=0, null=True)),
("media_type", models.CharField(max_length=50, null=True)),
("ratio", models.CharField(max_length=10)),
("audio_url", models.URLField(blank=True, null=True)),
("video_url", models.URLField(blank=True, null=True)),
( (
"referee", "status",
models.ForeignKey( models.CharField(
on_delete=django.db.models.deletion.CASCADE, choices=[
related_name="referrals_received", ("pending", "Pending"),
to="WebAdmin.user", ("in_progress", "In Progress"),
("completed", "Completed"),
("failed", "Failed"),
],
default="pending",
max_length=20,
), ),
), ),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
("slug", models.CharField(max_length=50)),
("pid", models.CharField(default="", max_length=50, null=True)),
("extension_count", models.IntegerField(default=0)),
("time_duration", models.IntegerField(default=0)),
( (
"referrer", "user",
models.ForeignKey( models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, on_delete=django.db.models.deletion.CASCADE,
related_name="referrals_made", to=settings.AUTH_USER_MODEL,
to="WebAdmin.user",
), ),
), ),
], ],
@@ -112,13 +437,170 @@ class Migration(migrations.Migration):
), ),
), ),
("source", models.CharField(max_length=50)), ("source", models.CharField(max_length=50)),
("created_at", models.DateTimeField(default=datetime.now)), ("created_at", models.DateTimeField(default=datetime.datetime.now)),
( (
"user", "user",
models.ForeignKey( models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to="WebAdmin.user" on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL,
), ),
), ),
], ],
), ),
migrations.CreateModel(
name="TransactionHistory",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"feature",
models.CharField(
choices=[
("text-to-video", "AI文生视频"),
("img-to-video", "AI图生视频"),
("create-tiktok-video", "AI长视频生成"),
("music-to-video", "音乐视频生成"),
("create-avatar-video", "虚拟形象视频生成"),
],
max_length=50,
verbose_name="功能类型",
),
),
("points_spent", models.IntegerField(verbose_name="消费积分")),
(
"char_count",
models.IntegerField(blank=True, null=True, verbose_name="字符数量"),
),
(
"description",
models.TextField(blank=True, null=True, verbose_name="操作描述"),
),
(
"transaction_date",
models.DateTimeField(
default=datetime.datetime.now, verbose_name="消费时间"
),
),
("previous_points_balance", models.IntegerField(verbose_name="消费前积分")),
("new_points_balance", models.IntegerField(verbose_name="消费后积分")),
("success", models.BooleanField(default=True, verbose_name="是否成功")),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="transactions",
to=settings.AUTH_USER_MODEL,
verbose_name="用户",
),
),
],
options={
"verbose_name": "消费明细",
"verbose_name_plural": "消费明细",
"db_table": "transaction_history",
"ordering": ["-transaction_date"],
},
),
migrations.CreateModel(
name="Referral",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"commission_amount",
models.DecimalField(decimal_places=2, max_digits=10),
),
("created_at", models.DateTimeField(default=datetime.datetime.now)),
(
"referee",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="referrals_received",
to=settings.AUTH_USER_MODEL,
),
),
(
"referrer",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="referrals_made",
to=settings.AUTH_USER_MODEL,
),
),
],
),
migrations.CreateModel(
name="Order",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("order_id", models.CharField(max_length=100, unique=True)),
("username", models.CharField(max_length=150)),
(
"amount",
models.DecimalField(decimal_places=2, default=0.0, max_digits=10),
),
(
"payment_method",
models.CharField(
choices=[("alipay", "Alipay"), ("paypal", "PayPal")],
max_length=10,
),
),
(
"status",
models.CharField(
choices=[
("pending", "Pending"),
("completed", "Completed"),
("canceled", "Canceled"),
("failed", "Failed"),
],
default="pending",
max_length=10,
),
),
("created_at", models.DateTimeField(default=datetime.datetime.now)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"plan",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to="WebAdmin.plan"
),
),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL,
),
),
],
options={
"verbose_name": "Order",
"verbose_name_plural": "Orders",
"ordering": ["-created_at"],
},
),
] ]

View File

@@ -1,22 +0,0 @@
# Generated by Django 5.0.7 on 2024-07-16 17:33
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("WebAdmin", "0001_initial"),
]
operations = [
migrations.AddField(
model_name="user",
name="last_login_ip",
field=models.GenericIPAddressField(blank=True, null=True),
),
migrations.AddField(
model_name="user",
name="login_count",
field=models.IntegerField(default=0),
),
]

View File

@@ -1,135 +0,0 @@
# Generated by Django 5.0.7 on 2024-07-16 18:04
import django.contrib.auth.models
import django.contrib.auth.validators
from datetime import datetime
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("WebAdmin", "0002_user_last_login_ip_user_login_count"),
("auth", "0012_alter_user_first_name_max_length"),
]
operations = [
migrations.AlterModelOptions(
name="user",
options={"verbose_name": "user", "verbose_name_plural": "users"},
),
migrations.AlterModelManagers(
name="user",
managers=[
("objects", django.contrib.auth.models.UserManager()),
],
),
migrations.RemoveField(
model_name="user",
name="password_hash",
),
migrations.AddField(
model_name="user",
name="date_joined",
field=models.DateTimeField(
default=datetime.now, verbose_name="date joined"
),
),
migrations.AddField(
model_name="user",
name="first_name",
field=models.CharField(
blank=True, max_length=150, verbose_name="first name"
),
),
migrations.AddField(
model_name="user",
name="groups",
field=models.ManyToManyField(
blank=True,
help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.",
related_name="user_set",
related_query_name="user",
to="auth.group",
verbose_name="groups",
),
),
migrations.AddField(
model_name="user",
name="is_staff",
field=models.BooleanField(
default=False,
help_text="Designates whether the user can log into this admin site.",
verbose_name="staff status",
),
),
migrations.AddField(
model_name="user",
name="is_superuser",
field=models.BooleanField(
default=False,
help_text="Designates that this user has all permissions without explicitly assigning them.",
verbose_name="superuser status",
),
),
migrations.AddField(
model_name="user",
name="last_login",
field=models.DateTimeField(
blank=True, null=True, verbose_name="last login"
),
),
migrations.AddField(
model_name="user",
name="last_name",
field=models.CharField(
blank=True, max_length=150, verbose_name="last name"
),
),
migrations.AddField(
model_name="user",
name="password",
field=models.CharField(default=1, max_length=128, verbose_name="password"),
preserve_default=False,
),
migrations.AddField(
model_name="user",
name="user_permissions",
field=models.ManyToManyField(
blank=True,
help_text="Specific permissions for this user.",
related_name="user_set",
related_query_name="user",
to="auth.permission",
verbose_name="user permissions",
),
),
migrations.AlterField(
model_name="user",
name="email",
field=models.EmailField(
blank=True, default=1, max_length=254, verbose_name="email address"
),
preserve_default=False,
),
migrations.AlterField(
model_name="user",
name="is_active",
field=models.BooleanField(
default=True,
help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.",
verbose_name="active",
),
),
migrations.AlterField(
model_name="user",
name="username",
field=models.CharField(
error_messages={"unique": "A user with that username already exists."},
help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.",
max_length=150,
unique=True,
validators=[django.contrib.auth.validators.UnicodeUsernameValidator()],
verbose_name="username",
),
),
]

View File

@@ -1,48 +0,0 @@
# Generated by Django 5.0.7 on 2024-07-20 03:57
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("WebAdmin", "0003_alter_user_options_alter_user_managers_and_more"),
]
operations = [
migrations.CreateModel(
name="EmailVerificationCode",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("email", models.EmailField(max_length=254)),
("code", models.CharField(max_length=6)),
("created_at", models.DateTimeField(auto_now_add=True)),
("is_used", models.BooleanField(default=False)),
],
),
migrations.CreateModel(
name="SMSVerificationCode",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("phone_number", models.CharField(max_length=15)),
("code", models.CharField(max_length=6)),
("created_at", models.DateTimeField(auto_now_add=True)),
("is_used", models.BooleanField(default=False)),
],
),
]

View File

@@ -1,60 +0,0 @@
# Generated by Django 5.0.7 on 2024-08-03 10:13
import django.db.models.deletion
from datetime import datetime
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("WebAdmin", "0004_emailverificationcode_smsverificationcode"),
]
operations = [
migrations.CreateModel(
name="VideoGeneration",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("text", models.TextField()),
("voice_name", models.CharField(max_length=50)),
("style", models.CharField(default="general", max_length=50)),
("rate", models.IntegerField(default=0)),
("media_type", models.CharField(max_length=50)),
("ratio", models.CharField(max_length=10)),
("audio_url", models.URLField(blank=True, null=True)),
("video_url", models.URLField(blank=True, null=True)),
(
"status",
models.CharField(
choices=[
("pending", "Pending"),
("in_progress", "In Progress"),
("completed", "Completed"),
("failed", "Failed"),
],
default="pending",
max_length=20,
),
),
("created_at", models.DateTimeField(default=datetime.now)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL,
),
),
],
),
]

View File

@@ -1,18 +0,0 @@
# Generated by Django 5.0.7 on 2024-08-03 14:09
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("WebAdmin", "0005_videogeneration"),
]
operations = [
migrations.AddField(
model_name="videogeneration",
name="slug",
field=models.CharField(default=1, max_length=50),
preserve_default=False,
),
]

View File

@@ -1,37 +0,0 @@
# Generated by Django 5.0.7 on 2024-08-24 04:16
from datetime import datetime
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("WebAdmin", "0006_videogeneration_slug"),
]
operations = [
migrations.CreateModel(
name="Plan",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("title", models.CharField(max_length=100, unique=True)),
("description", models.TextField()),
("price", models.DecimalField(decimal_places=2, max_digits=10)),
("credits_per_month", models.IntegerField(default=0)),
("created_at", models.DateTimeField(default=datetime.now)),
("updated_at", models.DateTimeField(auto_now=True)),
("is_promotional", models.BooleanField(default=False)),
("unlimited_exports", models.BooleanField(default=False)),
("smart_music_sync", models.BooleanField(default=False)),
],
),
]

View File

@@ -1,62 +0,0 @@
# Generated by Django 5.0.7 on 2024-08-24 12:15
import django.db.models.deletion
from datetime import datetime
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("WebAdmin", "0007_plan"),
]
operations = [
migrations.CreateModel(
name="Order",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("order_id", models.CharField(max_length=100, unique=True)),
("username", models.CharField(max_length=150)),
("amount", models.DecimalField(decimal_places=2, max_digits=10)),
(
"payment_method",
models.CharField(
choices=[("alipay", "Alipay"), ("paypal", "PayPal")],
max_length=10,
),
),
(
"status",
models.CharField(
choices=[
("pending", "Pending"),
("completed", "Completed"),
("canceled", "Canceled"),
("failed", "Failed"),
],
default="pending",
max_length=10,
),
),
("created_at", models.DateTimeField(default=datetime.now)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL,
),
),
],
),
]

View File

@@ -1,22 +0,0 @@
# Generated by Django 5.0.7 on 2024-08-24 13:08
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("WebAdmin", "0008_order"),
]
operations = [
migrations.AddField(
model_name="user",
name="membership_end",
field=models.DateTimeField(blank=True, null=True),
),
migrations.AddField(
model_name="user",
name="membership_start",
field=models.DateTimeField(blank=True, null=True),
),
]

View File

@@ -1,31 +0,0 @@
# Generated by Django 5.0.7 on 2024-08-26 13:02
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("WebAdmin", "0009_user_membership_end_user_membership_start"),
]
operations = [
migrations.AlterModelOptions(
name="order",
options={
"ordering": ["-created_at"],
"verbose_name": "Order",
"verbose_name_plural": "Orders",
},
),
migrations.AddField(
model_name="order",
name="plan",
field=models.ForeignKey(
default=1,
on_delete=django.db.models.deletion.CASCADE,
to="WebAdmin.plan",
),
preserve_default=False,
),
]

View File

@@ -1,17 +0,0 @@
# Generated by Django 5.0.7 on 2024-08-26 13:06
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("WebAdmin", "0010_alter_order_options_order_plan"),
]
operations = [
migrations.AlterField(
model_name="order",
name="amount",
field=models.DecimalField(decimal_places=2, default=0.0, max_digits=10),
),
]

View File

@@ -1,16 +0,0 @@
# Generated by Django 5.0.7 on 2024-08-29 14:16
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("WebAdmin", "0011_alter_order_amount"),
]
operations = [
migrations.AddField(
model_name="videogeneration",
name="video_id",
field=models.AutoField(primary_key=True, serialize=False),
),
]

View File

@@ -1,18 +0,0 @@
# Generated by Django 5.0.7 on 2024-08-29 14:38
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("WebAdmin", "0011_alter_order_amount"),
]
operations = [
# 这里不再有任何操作,因为我们手动删除了重复的字段添加
# migrations.AddField(
# model_name="videogeneration",
# name="video_id",
# field=models.CharField(default="", max_length=255, unique=True),
# ),
]

View File

@@ -1,17 +0,0 @@
# Generated by Django 5.0.7 on 2024-08-29 14:51
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("WebAdmin", "0012_videogeneration_video_id"),
]
operations = [
migrations.AddField(
model_name="videogeneration",
name="video_id",
field=models.CharField(default="", max_length=255, unique=True),
),
]

View File

@@ -1,17 +0,0 @@
# Generated by Django 5.0.7 on 2024-08-30 02:50
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("WebAdmin", "0013_videogeneration_video_id"),
]
operations = [
migrations.AddField(
model_name="videogeneration",
name="pid",
field=models.CharField(default="", max_length=50, null=True),
),
]

View File

@@ -1,42 +0,0 @@
# Generated by Django 5.0.7 on 2024-09-14 10:14
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("WebAdmin", "0014_videogeneration_pid"),
]
operations = [
migrations.AddField(
model_name="videogeneration",
name="extension_count",
field=models.IntegerField(default=0),
),
migrations.AlterField(
model_name="videogeneration",
name="media_type",
field=models.CharField(max_length=50, null=True),
),
migrations.AlterField(
model_name="videogeneration",
name="rate",
field=models.IntegerField(default=0, null=True),
),
migrations.AlterField(
model_name="videogeneration",
name="style",
field=models.CharField(default="general", max_length=50, null=True),
),
migrations.AlterField(
model_name="videogeneration",
name="text",
field=models.TextField(null=True),
),
migrations.AlterField(
model_name="videogeneration",
name="voice_name",
field=models.CharField(max_length=50, null=True),
),
]

View File

@@ -1,17 +0,0 @@
# Generated by Django 5.0.7 on 2024-09-14 14:15
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("WebAdmin", "0015_videogeneration_extension_count_and_more"),
]
operations = [
migrations.AddField(
model_name="videogeneration",
name="time_duration",
field=models.IntegerField(default=0),
),
]

View File

@@ -1,22 +0,0 @@
# Generated by Django 5.0.7 on 2024-09-15 06:08
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("WebAdmin", "0016_videogeneration_time_duration"),
]
operations = [
migrations.AddField(
model_name="user",
name="openid_used",
field=models.CharField(blank=True, max_length=150, null=True, unique=True),
),
migrations.AddField(
model_name="user",
name="source",
field=models.CharField(default="web", max_length=50),
),
]

View File

@@ -1,77 +0,0 @@
# Generated by Django 5.0.7 on 2024-09-15 07:59
import django.db.models.deletion
from datetime import datetime
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("WebAdmin", "0017_user_openid_used_user_source"),
]
operations = [
migrations.CreateModel(
name="TransactionHistory",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"feature",
models.CharField(
choices=[
("text-to-video", "AI文生视频"),
("img-to-video", "AI图生视频"),
("create-tiktok-video", "AI长视频生成"),
("music-to-video", "音乐视频生成"),
("create-avatar-video", "虚拟形象视频生成"),
],
max_length=50,
verbose_name="功能类型",
),
),
("points_spent", models.IntegerField(verbose_name="消费积分")),
(
"char_count",
models.IntegerField(blank=True, null=True, verbose_name="字符数量"),
),
(
"description",
models.TextField(blank=True, null=True, verbose_name="操作描述"),
),
(
"transaction_date",
models.DateTimeField(
default=datetime.now, verbose_name="消费时间"
),
),
("previous_points_balance", models.IntegerField(verbose_name="消费前积分")),
("new_points_balance", models.IntegerField(verbose_name="消费后积分")),
("success", models.BooleanField(default=True, verbose_name="是否成功")),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="transactions",
to=settings.AUTH_USER_MODEL,
verbose_name="用户",
),
),
],
options={
"verbose_name": "消费明细",
"verbose_name_plural": "消费明细",
"db_table": "transaction_history",
"ordering": ["-transaction_date"],
},
),
]

View File

@@ -1,50 +0,0 @@
# Generated by Django 5.0.7 on 2024-09-15 08:05
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("WebAdmin", "0018_transactionhistory"),
]
operations = [
migrations.CreateModel(
name="WebsiteInfo",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"domain",
models.CharField(max_length=255, unique=True, verbose_name="域名"),
),
("title_en", models.CharField(max_length=255, verbose_name="英文标题")),
("title_zh", models.CharField(max_length=255, verbose_name="中文标题")),
("keywords_en", models.TextField(verbose_name="英文关键词")),
("keywords_zh", models.TextField(verbose_name="中文关键词")),
("description_en", models.TextField(verbose_name="英文描述")),
("description_zh", models.TextField(verbose_name="中文描述")),
(
"created_at",
models.DateTimeField(auto_now_add=True, verbose_name="创建时间"),
),
(
"updated_at",
models.DateTimeField(auto_now=True, verbose_name="更新时间"),
),
],
options={
"verbose_name": "网站信息",
"verbose_name_plural": "网站信息",
"db_table": "website_info",
"ordering": ["-created_at"],
},
),
]

View File

@@ -1,56 +0,0 @@
# Generated by Django 5.0.7 on 2024-09-15 08:13
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("WebAdmin", "0019_websiteinfo"),
]
operations = [
migrations.RemoveField(
model_name="websiteinfo",
name="domain",
),
migrations.AddField(
model_name="websiteinfo",
name="domain_en",
field=models.CharField(default=True, max_length=255, verbose_name="域名"),
),
migrations.AddField(
model_name="websiteinfo",
name="domain_zh",
field=models.CharField(default=True, max_length=255, verbose_name="域名"),
),
migrations.AlterField(
model_name="websiteinfo",
name="description_en",
field=models.TextField(default=True, verbose_name="英文描述"),
),
migrations.AlterField(
model_name="websiteinfo",
name="description_zh",
field=models.TextField(default=True, verbose_name="中文描述"),
),
migrations.AlterField(
model_name="websiteinfo",
name="keywords_en",
field=models.TextField(default=True, verbose_name="英文关键词"),
),
migrations.AlterField(
model_name="websiteinfo",
name="keywords_zh",
field=models.TextField(default=True, verbose_name="中文关键词"),
),
migrations.AlterField(
model_name="websiteinfo",
name="title_en",
field=models.CharField(default=True, max_length=255, verbose_name="英文标题"),
),
migrations.AlterField(
model_name="websiteinfo",
name="title_zh",
field=models.CharField(default=True, max_length=255, verbose_name="中文标题"),
),
]

View File

@@ -1,56 +0,0 @@
# Generated by Django 5.0.7 on 2024-09-15 08:28
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("WebAdmin", "0020_remove_websiteinfo_domain_websiteinfo_domain_en_and_more"),
]
operations = [
migrations.CreateModel(
name="WebsiteAccessLog",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("ip_address", models.GenericIPAddressField(verbose_name="访问IP")),
(
"browser_language",
models.CharField(
blank=True, max_length=50, null=True, verbose_name="浏览器语言"
),
),
(
"referrer",
models.URLField(blank=True, null=True, verbose_name="来源URL"),
),
("request_path", models.CharField(max_length=255, verbose_name="请求路径")),
(
"request_method",
models.CharField(max_length=10, verbose_name="请求方法"),
),
(
"access_time",
models.DateTimeField(auto_now_add=True, verbose_name="访问时间"),
),
(
"access_time_bj",
models.DateTimeField(blank=True, null=True, verbose_name="北京时间"),
),
],
options={
"verbose_name": "网站访问日志",
"verbose_name_plural": "网站访问日志",
"db_table": "website_access_log",
"unique_together": {("ip_address", "access_time_bj")},
},
),
]

View File

@@ -1,49 +0,0 @@
# Generated by Django 5.0.7 on 2024-09-15 08:41
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("WebAdmin", "0021_websiteaccesslog"),
]
operations = [
migrations.AlterUniqueTogether(
name="websiteaccesslog",
unique_together=set(),
),
migrations.AddField(
model_name="websiteaccesslog",
name="access_date",
field=models.DateField(blank=True, null=True, verbose_name="访问日期"),
),
migrations.AddField(
model_name="websiteaccesslog",
name="device_type",
field=models.CharField(
blank=True, max_length=50, null=True, verbose_name="设备类型"
),
),
migrations.AddField(
model_name="websiteaccesslog",
name="user_agent",
field=models.TextField(blank=True, null=True, verbose_name="用户代理"),
),
migrations.AlterField(
model_name="websiteaccesslog",
name="browser_language",
field=models.CharField(
blank=True, max_length=200, null=True, verbose_name="浏览器语言"
),
),
migrations.AlterField(
model_name="websiteaccesslog",
name="referrer",
field=models.TextField(blank=True, null=True, verbose_name="来源URL"),
),
migrations.AlterUniqueTogether(
name="websiteaccesslog",
unique_together={("ip_address", "access_date")},
),
]

View File

@@ -85,7 +85,7 @@ class VideoGeneration(models.Model):
('failed', 'Failed'), ('failed', 'Failed'),
] ]
video_id = models.CharField(max_length=255, unique=True, default='') video_id = models.CharField(max_length=255, default='')
user = models.ForeignKey(User, on_delete=models.CASCADE) # 生成请求的用户 user = models.ForeignKey(User, on_delete=models.CASCADE) # 生成请求的用户
text = models.TextField(null=True) # 输入文本 text = models.TextField(null=True) # 输入文本
voice_name = models.CharField(max_length=50, null=True) # 选择的语音 voice_name = models.CharField(max_length=50, null=True) # 选择的语音
@@ -96,7 +96,7 @@ class VideoGeneration(models.Model):
audio_url = models.URLField(null=True, blank=True) # 生成的音频URL audio_url = models.URLField(null=True, blank=True) # 生成的音频URL
video_url = models.URLField(null=True, blank=True) # 生成的视频URL video_url = models.URLField(null=True, blank=True) # 生成的视频URL
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending') # 生成状态 status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending') # 生成状态
created_at = models.DateTimeField(default=datetime.now) # 创建时间 created_at = models.DateTimeField(auto_now_add=True) # 创建时间
updated_at = models.DateTimeField(auto_now=True) # 更新时间 updated_at = models.DateTimeField(auto_now=True) # 更新时间
slug = models.CharField(max_length=50) # 生成视频的类型 slug = models.CharField(max_length=50) # 生成视频的类型
pid = models.CharField(max_length=50, null=True, default='') # 生成视频的ID pid = models.CharField(max_length=50, null=True, default='') # 生成视频的ID
@@ -232,4 +232,30 @@ class WebsiteAccessLog(models.Model):
def __str__(self): def __str__(self):
access_time_str = self.access_time_bj.strftime('%Y-%m-%d %H:%M:%S') if self.access_time_bj else 'N/A' access_time_str = self.access_time_bj.strftime('%Y-%m-%d %H:%M:%S') if self.access_time_bj else 'N/A'
return f"{self.ip_address} - {access_time_str}" return f"{self.ip_address} - {access_time_str}"
class Article(models.Model):
STATUS_CHOICES = [
('published', '已发布'),
('pending', '待审核'),
]
chinese_title = models.CharField(max_length=255)
english_title = models.CharField(max_length=255)
chinese_keywords = models.TextField() # 使用 TextField 允许存储更长的字符串
english_keywords = models.TextField()
chinese_content = models.TextField()
english_content = models.TextField()
source_url = models.URLField(blank=True, null=True)
published_at = models.DateTimeField(blank=True, null=True)
image_url = models.URLField(blank=True, null=True)
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='pending')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
verbose_name = 'Article'
verbose_name_plural = 'Article'
ordering = ['-created_at']
def __str__(self):
return self.chinese_title # 或者返回英文标题,根据需要选择

View File

@@ -10,6 +10,7 @@ from . import ali_pay # 支付宝接口
from . import create_text_img_video # 支付宝接口 from . import create_text_img_video # 支付宝接口
from . import AccessLog # 支付宝接口 from . import AccessLog # 支付宝接口
from . import task_all # 扫描全部任务 from . import task_all # 扫描全部任务
from . import 文章 # 网站首页文章
urlpatterns = [ urlpatterns = [
#网站信息相关 #网站信息相关
path('api/website/', AccessLog.website_info_view, name='website_config'), #获取网站信息 path('api/website/', AccessLog.website_info_view, name='website_config'), #获取网站信息
@@ -70,6 +71,27 @@ urlpatterns = [
path('api/reset-password/', user.reset_password_view, name='reset_password'), # 重置密码API path('api/reset-password/', user.reset_password_view, name='reset_password'), # 重置密码API
# 后台管理相关路由 # 后台管理相关路由
path('custom_admin/login/', admin_views.admin_login_view, name='admin_login'), # 后台登录视图 path('custom_admin/3acf16259def65456fc2a68ab5e10d96/', admin_views.admin_login_view, name='admin_login'), # 后台登录视图
path('custom_admin/home/', admin_views.admin_home_view, name='admin_home'), # 后台主页视图 path('custom_admin/home/', admin_views.admin_home_view, name='admin_home'), # 后台主页视图
path('custom_admin/change-password/', admin_views.admin_change_password, name='admin_change_password'),#修改管理员密码
path('custom_admin/logout/', admin_views.admin_logout, name='admin_logout'),#退出登录
path('custom_admin/home/site-info/', admin_views.admin_site_info_view, name='site-info'), # 基本信息
path('custom_admin/home/article-list/', admin_views.admin_article_list, name='aricle-list'), # 文章列表
path('custom_admin/home/user-list/', admin_views.admin_user_list, name='aricle-list'), # 文章列表
path('custom_admin/home/task-list/', admin_views.admin_task_list, name='task-list'), # 用户列表
path('custom_admin/home/order-list/', admin_views.admin_order_list, name='order-list'), # 订单列表
# 文章保存(添加或编辑)
path('custom_admin/home/save-article/', admin_views.save_article, name='save_article'), # 添加文章
path('custom_admin/home/save-article/<int:pk>/', admin_views.save_article, name='edit_article'), # 编辑文章,带文章 ID
# 图片上传专用于编辑器
path('articles/<int:pk>/<str:lang>/', admin_views.ArticleDetailView, name='article_detail'), #查看文章
path('api/upload_article_image/', admin_views.upload_editor_image, name='upload_editor_image'),
path('api/delete-article/<int:article_id>/', admin_views.ArticleManagementView.as_view(), name='delete_article'),
path('api/audit-article/<int:article_id>/', admin_views.ArticleManagementView.as_view(), name='audit_article'),
path('admin/captcha/', admin_views.generate_captcha, name='captcha_image'),
path('api/article-list/', 文章.article_list, name='admin_article_list'), # 文章列表页
path('api/article/<int:pk>/<str:lang>/', 文章.article_detail, name='admin_article_detail'), # 文章详情页
] ]

Some files were not shown because too many files have changed in this diff Show More