Your commit message
This commit is contained in:
parent
06dfa7cd9d
commit
ca32eef513
23
MyApi/API_Log.py
Normal file
23
MyApi/API_Log.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import logging
|
||||||
|
from logging.handlers import TimedRotatingFileHandler
|
||||||
|
|
||||||
|
|
||||||
|
# 日志配置
|
||||||
|
def get_logger(name, log_file, when='midnight', backup_count=7):
|
||||||
|
logger = logging.getLogger(name)
|
||||||
|
logger.setLevel(logging.INFO)
|
||||||
|
|
||||||
|
# 创建TimedRotatingFileHandler
|
||||||
|
handler = TimedRotatingFileHandler(
|
||||||
|
log_file,
|
||||||
|
when=when, # 按天切割
|
||||||
|
interval=1, # 每1天切割一次
|
||||||
|
backupCount=backup_count # 保留7个备份
|
||||||
|
)
|
||||||
|
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||||
|
handler.setFormatter(formatter)
|
||||||
|
|
||||||
|
logger.addHandler(handler)
|
||||||
|
return logger
|
||||||
|
|
||||||
|
|
190
MyApi/AiMssage.py
Normal file
190
MyApi/AiMssage.py
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
from django.shortcuts import render
|
||||||
|
import json
|
||||||
|
from http import HTTPStatus
|
||||||
|
from dashscope import Application
|
||||||
|
from django.http import JsonResponse
|
||||||
|
from .models import User, ChatRecord
|
||||||
|
from django.utils.crypto import get_random_string
|
||||||
|
from Crypto.Cipher import AES
|
||||||
|
import base64
|
||||||
|
from .views import update_usage_count
|
||||||
|
|
||||||
|
def decrypt_param(x, t):
|
||||||
|
# 创建AES解密器对象
|
||||||
|
key = b'qw5w6SFE2D1jmxyd'
|
||||||
|
iv = b'345GDFED433223DF'
|
||||||
|
|
||||||
|
# 检查 x 和 t 是否为空
|
||||||
|
if not x or not t:
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
cipher = AES.new(key, AES.MODE_CBC, iv)
|
||||||
|
# 将密文进行Base64解码
|
||||||
|
ciphertext_bytes = base64.b64decode(x)
|
||||||
|
# 使用解密器解密密文
|
||||||
|
plaintext_bytes = cipher.decrypt(ciphertext_bytes)
|
||||||
|
# 删除填充字符
|
||||||
|
plaintext = plaintext_bytes.rstrip(b'\0').decode('utf-8')
|
||||||
|
# 比较解密后的明文和 t
|
||||||
|
if plaintext.rstrip('\x03') == t.rstrip('\x03'):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
print(f"解密过程出现错误: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def call_bailian_app(request):
|
||||||
|
body = request.body.decode("utf-8")
|
||||||
|
data = json.loads(body)
|
||||||
|
user_id = data.get("user_id")
|
||||||
|
openid = data.get("openid")
|
||||||
|
role_name = data.get("role", "AI助手") # 默认角色为“默认AI聊天”
|
||||||
|
prompt = data.get("prompt")
|
||||||
|
x = request.headers.get('X', '')
|
||||||
|
t = request.headers.get('T', '')
|
||||||
|
increment = -1
|
||||||
|
function_type = 'call_bailian_app'
|
||||||
|
result = update_usage_count(openid, increment, function_type)
|
||||||
|
if decrypt_param(x, t):
|
||||||
|
if result['success']:
|
||||||
|
print(f"收到请求: user_id={user_id}, role_name={role_name}, prompt={prompt}")
|
||||||
|
try:
|
||||||
|
user = User.objects.get(nickname=user_id)
|
||||||
|
print(f"找到用户: {user.nickname}")
|
||||||
|
except User.DoesNotExist:
|
||||||
|
print("用户不存在")
|
||||||
|
return JsonResponse({"message": "用户不存在"}, status=404)
|
||||||
|
|
||||||
|
# 检查是否已有该用户和角色的会话
|
||||||
|
conversation = ChatRecord.objects.filter(nickname=user.nickname, role=role_name).first()
|
||||||
|
if not conversation:
|
||||||
|
print(f"新会话ID")
|
||||||
|
response = Application.call(
|
||||||
|
app_id=get_app_id(role_name),
|
||||||
|
prompt=prompt,
|
||||||
|
api_key="sk-9458cae1bed7460c9780e52ca6005cae",
|
||||||
|
)
|
||||||
|
conversation_id = response.output.get('session_id')
|
||||||
|
else:
|
||||||
|
conversation_id = conversation.conversation_id
|
||||||
|
response = Application.call(
|
||||||
|
app_id=get_app_id(role_name),
|
||||||
|
prompt=prompt,
|
||||||
|
api_key="sk-9458cae1bed7460c9780e52ca6005cae",
|
||||||
|
session_id=conversation_id
|
||||||
|
)
|
||||||
|
print(f"已有会话ID: {conversation_id}")
|
||||||
|
|
||||||
|
print(f"API调用响应状态: {response.status_code}")
|
||||||
|
|
||||||
|
if response.status_code != HTTPStatus.OK:
|
||||||
|
print(f"API调用失败: request_id={response.request_id}, message={response.message}")
|
||||||
|
return JsonResponse({
|
||||||
|
"message": "调用应用程序失败",
|
||||||
|
"request_id": response.request_id,
|
||||||
|
"code": response.status_code,
|
||||||
|
"details": response.message
|
||||||
|
}, status=response.status_code)
|
||||||
|
|
||||||
|
ai_response = response.output.get('text') if response.output else "AI无回复"
|
||||||
|
print(f"AI回复: {ai_response}")
|
||||||
|
|
||||||
|
ChatRecord.objects.create(
|
||||||
|
openid=user.openid,
|
||||||
|
nickname=user.nickname,
|
||||||
|
conversation_id=conversation_id,
|
||||||
|
role=role_name,
|
||||||
|
message_content=prompt,
|
||||||
|
is_response=False
|
||||||
|
)
|
||||||
|
|
||||||
|
# 保存AI回复
|
||||||
|
ChatRecord.objects.create(
|
||||||
|
openid=user.openid,
|
||||||
|
nickname=user.nickname,
|
||||||
|
conversation_id=conversation_id,
|
||||||
|
role=role_name,
|
||||||
|
message_content=ai_response,
|
||||||
|
is_response=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# 检索会话的最近10条消息
|
||||||
|
last_messages = ChatRecord.objects.filter(nickname=user.nickname, conversation_id=conversation_id).order_by('-timestamp')[:10]
|
||||||
|
last_messages_list = [
|
||||||
|
{
|
||||||
|
"content": msg.message_content,
|
||||||
|
"is_response": msg.is_response,
|
||||||
|
"role":msg.role,
|
||||||
|
"timestamp": msg.timestamp.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
} for msg in reversed(last_messages)
|
||||||
|
]
|
||||||
|
|
||||||
|
print(f"最近10条消息: {last_messages_list}")
|
||||||
|
|
||||||
|
return JsonResponse({
|
||||||
|
"message": "成功",
|
||||||
|
"ai_response": ai_response,
|
||||||
|
"conversation_id": conversation_id,
|
||||||
|
"last_messages": last_messages_list
|
||||||
|
}, status=HTTPStatus.OK)
|
||||||
|
else:
|
||||||
|
return JsonResponse(result)
|
||||||
|
else:
|
||||||
|
return JsonResponse({'error': '非法参数'}, status=400)
|
||||||
|
|
||||||
|
# 新增接口:获取用户和角色的最近10条聊天记录
|
||||||
|
def get_recent_chat_records(request):
|
||||||
|
body = request.body.decode("utf-8")
|
||||||
|
data = json.loads(body)
|
||||||
|
user_id = data.get("user_id")
|
||||||
|
role_name = data.get("role", "AI助手") # 默认角色为“默认AI聊天”
|
||||||
|
x = request.headers.get('X', '')
|
||||||
|
t = request.headers.get('T', '')
|
||||||
|
if decrypt_param(x, t):
|
||||||
|
print(f"收到请求: user_id={user_id}, role_name={role_name}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
user = User.objects.get(nickname=user_id)
|
||||||
|
print(f"找到用户: {user.nickname}")
|
||||||
|
except User.DoesNotExist:
|
||||||
|
print("用户不存在")
|
||||||
|
return JsonResponse({"message": "用户不存在"}, status=404)
|
||||||
|
|
||||||
|
# 获取该用户和角色的最近10条聊天记录
|
||||||
|
last_messages = ChatRecord.objects.filter(nickname=user.nickname, role=role_name).order_by('-timestamp')[:10]
|
||||||
|
last_messages_list = [
|
||||||
|
{
|
||||||
|
"content": msg.message_content,
|
||||||
|
"is_response": msg.is_response,
|
||||||
|
"role": msg.role,
|
||||||
|
"timestamp": msg.timestamp.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
} for msg in reversed(last_messages)
|
||||||
|
]
|
||||||
|
|
||||||
|
print(f"最近10条消息: {last_messages_list}")
|
||||||
|
|
||||||
|
return JsonResponse({
|
||||||
|
"message": "成功",
|
||||||
|
"last_messages": last_messages_list
|
||||||
|
}, status=HTTPStatus.OK)
|
||||||
|
else:
|
||||||
|
return JsonResponse({'error': '非法参数'}, status=400)
|
||||||
|
def get_app_id(role_name):
|
||||||
|
role_app_id_map = {
|
||||||
|
"法律咨询": "446cde34829e4d6490667b1271dee537",
|
||||||
|
"行业分析师": "bdce7fba11b147ca846a1b4d1f5e7e72",
|
||||||
|
"写工作报告": "f340613da5574815975acdc10bac4e47",
|
||||||
|
"产品顾问": "f9b0612793e94a669fe7ad2fecbda50e",
|
||||||
|
"写爆款文案": "2c8d6675a9794711a886a5e8cf714afe",
|
||||||
|
"写公众号文章": "4fb69f6bd9bb49c9a6329202c6f28c84",
|
||||||
|
"文案改写": "b94734c09c414814bc0505a4e5b636c6",
|
||||||
|
"招聘助理": "c70caf3fd67b44c4947df70f05cf9f10",
|
||||||
|
"AI助手": "92d23cd2c5d64ef5ac6ad02692fc510b",
|
||||||
|
"小红书文案": "c5211023cbf64d548ea55a47c516007a",
|
||||||
|
"写广告文案": "6c9770da90ec47979ce899cec49c396c",
|
||||||
|
}
|
||||||
|
|
||||||
|
return role_app_id_map.get(role_name, "app_id_default")
|
||||||
|
|
22
MyApi/WXBizDataCrypt.py
Normal file
22
MyApi/WXBizDataCrypt.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
from base64 import b64decode
|
||||||
|
from Crypto.Cipher import AES
|
||||||
|
import json
|
||||||
|
|
||||||
|
class WXBizDataCrypt:
|
||||||
|
def __init__(self, appid, session_key):
|
||||||
|
self.appid = appid
|
||||||
|
self.session_key = b64decode(session_key)
|
||||||
|
|
||||||
|
def decrypt(self, encrypted_data, iv):
|
||||||
|
# AES解密
|
||||||
|
try:
|
||||||
|
cipher = AES.new(self.session_key, AES.MODE_CBC, b64decode(iv))
|
||||||
|
decrypted = json.loads(self._unpad(cipher.decrypt(b64decode(encrypted_data))))
|
||||||
|
if decrypted['watermark']['appid'] != self.appid:
|
||||||
|
raise Exception('Invalid Buffer')
|
||||||
|
return decrypted
|
||||||
|
except Exception as e:
|
||||||
|
raise Exception('Failed to decrypt data')
|
||||||
|
|
||||||
|
def _unpad(self, s):
|
||||||
|
return s[:-ord(s[len(s)-1:])]
|
0
MyApi/__init__.py
Normal file
0
MyApi/__init__.py
Normal file
3
MyApi/admin.py
Normal file
3
MyApi/admin.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
6
MyApi/apps.py
Normal file
6
MyApi/apps.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class MyapiConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'MyApi'
|
BIN
MyApi/demo.mp3
Normal file
BIN
MyApi/demo.mp3
Normal file
Binary file not shown.
BIN
MyApi/demo.mp4
Normal file
BIN
MyApi/demo.mp4
Normal file
Binary file not shown.
BIN
MyApi/demo.mp4.mp3
Normal file
BIN
MyApi/demo.mp4.mp3
Normal file
Binary file not shown.
615
MyApi/migrations/0001_initial.py
Normal file
615
MyApi/migrations/0001_initial.py
Normal file
@ -0,0 +1,615 @@
|
|||||||
|
# Generated by Django 5.0.3 on 2024-06-01 04:23
|
||||||
|
|
||||||
|
import django.utils.timezone
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = []
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="Administrator",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.BigAutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("username", models.CharField(max_length=150, unique=True)),
|
||||||
|
("password", models.CharField(max_length=128)),
|
||||||
|
("role", models.CharField(max_length=100)),
|
||||||
|
("is_active", models.BooleanField(default=True)),
|
||||||
|
("last_login", models.DateTimeField(blank=True, null=True)),
|
||||||
|
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"db_table": "Administrator",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="ApiCallLog",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.BigAutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"source",
|
||||||
|
models.CharField(
|
||||||
|
blank=True,
|
||||||
|
choices=[("index", "Index"), ("weixin", "Weixin")],
|
||||||
|
max_length=100,
|
||||||
|
null=True,
|
||||||
|
verbose_name="来源",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"openid",
|
||||||
|
models.CharField(
|
||||||
|
blank=True, max_length=150, null=True, verbose_name="openid"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"nickname",
|
||||||
|
models.CharField(
|
||||||
|
blank=True, max_length=255, null=True, verbose_name="用户标识"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"wxid",
|
||||||
|
models.CharField(
|
||||||
|
blank=True, max_length=255, null=True, verbose_name="微信id"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"wechat_alias",
|
||||||
|
models.CharField(
|
||||||
|
blank=True, max_length=255, null=True, verbose_name="微信号"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("api_name", models.CharField(max_length=255, verbose_name="调用接口")),
|
||||||
|
(
|
||||||
|
"is_successful",
|
||||||
|
models.BooleanField(default=False, verbose_name="是否成功"),
|
||||||
|
),
|
||||||
|
("remarks", models.TextField(blank=True, null=True, verbose_name="备注")),
|
||||||
|
(
|
||||||
|
"call_time",
|
||||||
|
models.DateTimeField(auto_now_add=True, verbose_name="调用时间"),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"verbose_name": "API调用日志",
|
||||||
|
"verbose_name_plural": "API调用日志",
|
||||||
|
"db_table": "api_call_log",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="ChatRecord",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.BigAutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("openid", models.CharField(default=None, max_length=150)),
|
||||||
|
("nickname", models.CharField(max_length=150, verbose_name="用户昵称")),
|
||||||
|
(
|
||||||
|
"conversation_id",
|
||||||
|
models.CharField(max_length=100, verbose_name="会话ID"),
|
||||||
|
),
|
||||||
|
("role", models.CharField(max_length=100, verbose_name="角色")),
|
||||||
|
("message_content", models.TextField(verbose_name="消息内容")),
|
||||||
|
(
|
||||||
|
"is_response",
|
||||||
|
models.BooleanField(default=False, verbose_name="是否为AI回复"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"timestamp",
|
||||||
|
models.DateTimeField(
|
||||||
|
default=django.utils.timezone.now, verbose_name="时间戳"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"verbose_name": "聊天记录",
|
||||||
|
"verbose_name_plural": "聊天记录",
|
||||||
|
"db_table": "chat_record",
|
||||||
|
"ordering": ["-timestamp"],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="Copywriting",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.BigAutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"openid",
|
||||||
|
models.CharField(
|
||||||
|
blank=True, max_length=150, null=True, verbose_name="openid"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"nickname",
|
||||||
|
models.CharField(
|
||||||
|
blank=True, max_length=255, null=True, verbose_name="用户标识"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("text_content", models.TextField(verbose_name="文本内容")),
|
||||||
|
("source", models.CharField(max_length=255, verbose_name="来源")),
|
||||||
|
(
|
||||||
|
"tag",
|
||||||
|
models.CharField(default=None, max_length=255, verbose_name="标签"),
|
||||||
|
),
|
||||||
|
("popularity", models.IntegerField(default=0, verbose_name="热度")),
|
||||||
|
(
|
||||||
|
"added_time",
|
||||||
|
models.DateTimeField(auto_now_add=True, verbose_name="添加时间"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"is_approved",
|
||||||
|
models.BooleanField(default=False, verbose_name="审核通过"),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"verbose_name": "文案库",
|
||||||
|
"verbose_name_plural": "文案库",
|
||||||
|
"db_table": "Copywriting",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="FriendRequest",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.BigAutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("fromusername", models.CharField(max_length=255, verbose_name="微信ID")),
|
||||||
|
(
|
||||||
|
"alias",
|
||||||
|
models.CharField(
|
||||||
|
blank=True, max_length=255, null=True, verbose_name="微信号"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"fromnickname",
|
||||||
|
models.CharField(
|
||||||
|
blank=True, max_length=255, null=True, verbose_name="昵称"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"country",
|
||||||
|
models.CharField(
|
||||||
|
blank=True, max_length=255, null=True, verbose_name="国家"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"province",
|
||||||
|
models.CharField(
|
||||||
|
blank=True, max_length=255, null=True, verbose_name="省份"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"city",
|
||||||
|
models.CharField(
|
||||||
|
blank=True, max_length=255, null=True, verbose_name="城市"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"sex",
|
||||||
|
models.CharField(
|
||||||
|
blank=True, max_length=1, null=True, verbose_name="性别"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"scene",
|
||||||
|
models.CharField(
|
||||||
|
blank=True, max_length=255, null=True, verbose_name="来源"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("time", models.DateTimeField(verbose_name="申请时间")),
|
||||||
|
("towxid", models.CharField(max_length=255, verbose_name="目标微信ID")),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"verbose_name": "添加好友请求",
|
||||||
|
"verbose_name_plural": "添加好友请求",
|
||||||
|
"db_table": "FriendRequest",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="IDCounter",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.BigAutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("count", models.IntegerField(default=10000)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"db_table": "IDCounter",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="MembershipType",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.BigAutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"type",
|
||||||
|
models.CharField(max_length=50, unique=True, verbose_name="卡类型"),
|
||||||
|
),
|
||||||
|
("title", models.CharField(max_length=100, verbose_name="标题")),
|
||||||
|
("description", models.TextField(verbose_name="描述")),
|
||||||
|
(
|
||||||
|
"price",
|
||||||
|
models.DecimalField(
|
||||||
|
decimal_places=2, max_digits=10, verbose_name="价格"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("duration_days", models.IntegerField(verbose_name="有效天数")),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"verbose_name": "会员卡类型",
|
||||||
|
"verbose_name_plural": "会员卡类型",
|
||||||
|
"db_table": "membership_type",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="RedemptionCard",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.BigAutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"code",
|
||||||
|
models.CharField(max_length=100, unique=True, verbose_name="卡密"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"card_type",
|
||||||
|
models.CharField(
|
||||||
|
choices=[("member", "会员卡"), ("quota", "额度卡")],
|
||||||
|
max_length=50,
|
||||||
|
verbose_name="卡类型",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"created_date",
|
||||||
|
models.DateTimeField(auto_now_add=True, verbose_name="生成日期"),
|
||||||
|
),
|
||||||
|
("expiry_date", models.DateTimeField(verbose_name="到期时间")),
|
||||||
|
("is_used", models.BooleanField(default=False, verbose_name="是否已使用")),
|
||||||
|
(
|
||||||
|
"used_by_openid",
|
||||||
|
models.CharField(
|
||||||
|
blank=True, max_length=150, null=True, verbose_name="使用者OpenID"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"used_by_nickname",
|
||||||
|
models.CharField(
|
||||||
|
blank=True, max_length=150, null=True, verbose_name="使用者昵称"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"card_creation_time",
|
||||||
|
models.DateTimeField(auto_now_add=True, verbose_name="卡密创建时间"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"validity_period",
|
||||||
|
models.PositiveIntegerField(default=0, verbose_name="有效期"),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"verbose_name": "兑换卡",
|
||||||
|
"verbose_name_plural": "兑换卡",
|
||||||
|
"db_table": "redemption_card",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="TransactionLog",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.BigAutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"transaction_no",
|
||||||
|
models.CharField(max_length=255, unique=True, verbose_name="交易编号"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"transaction_status",
|
||||||
|
models.CharField(
|
||||||
|
choices=[
|
||||||
|
("pending", "待处理"),
|
||||||
|
("completed", "完成"),
|
||||||
|
("failed", "失败"),
|
||||||
|
],
|
||||||
|
default="pending",
|
||||||
|
max_length=50,
|
||||||
|
verbose_name="交易状态",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"user_openid",
|
||||||
|
models.CharField(max_length=150, verbose_name="用户OpenID"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"transaction_type",
|
||||||
|
models.CharField(
|
||||||
|
choices=[("member", "会员购买"), ("quota", "续费会员")],
|
||||||
|
max_length=50,
|
||||||
|
verbose_name="交易类型",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"transaction_amount",
|
||||||
|
models.DecimalField(
|
||||||
|
decimal_places=2, max_digits=10, verbose_name="交易金额"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"remark",
|
||||||
|
models.TextField(
|
||||||
|
blank=True,
|
||||||
|
choices=[
|
||||||
|
("day", "一天会员充值"),
|
||||||
|
("week", "一周会员充值"),
|
||||||
|
("month", "一月会员充值"),
|
||||||
|
("season", "三月会员充值"),
|
||||||
|
("year", "一年会员充值"),
|
||||||
|
],
|
||||||
|
null=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": "transaction_log",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="User",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.BigAutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("openid", models.CharField(max_length=150, unique=True)),
|
||||||
|
("wxid", models.CharField(blank=True, max_length=150, null=True)),
|
||||||
|
(
|
||||||
|
"wechat_number",
|
||||||
|
models.CharField(
|
||||||
|
blank=True, max_length=150, null=True, verbose_name="微信号"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("nickname", models.CharField(blank=True, max_length=150, null=True)),
|
||||||
|
("gender", models.CharField(blank=True, max_length=10, null=True)),
|
||||||
|
("region", models.CharField(blank=True, max_length=150, null=True)),
|
||||||
|
("email", models.EmailField(blank=True, max_length=254, null=True)),
|
||||||
|
("phone", models.CharField(blank=True, max_length=20, null=True)),
|
||||||
|
("password", models.CharField(blank=True, max_length=128, null=True)),
|
||||||
|
("scene", models.CharField(blank=True, max_length=100, null=True)),
|
||||||
|
("is_member", models.BooleanField(default=False)),
|
||||||
|
("member_start_time", models.BigIntegerField(blank=True, null=True)),
|
||||||
|
("member_end_time", models.BigIntegerField(blank=True, null=True)),
|
||||||
|
("is_active", models.BooleanField(default=True)),
|
||||||
|
("usage_count", models.IntegerField(blank=True, default=0, null=True)),
|
||||||
|
("coins", models.IntegerField(default=100, verbose_name="金币数量")),
|
||||||
|
(
|
||||||
|
"balance",
|
||||||
|
models.DecimalField(
|
||||||
|
decimal_places=2,
|
||||||
|
default=0.0,
|
||||||
|
max_digits=10,
|
||||||
|
verbose_name="用户余额",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"inviter_nickname",
|
||||||
|
models.CharField(
|
||||||
|
blank=True, max_length=150, null=True, verbose_name="邀请人昵称"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"invitees_count",
|
||||||
|
models.IntegerField(default=0, verbose_name="邀请人数量"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"daily_video_quota",
|
||||||
|
models.IntegerField(default=5, verbose_name="每日视频生成额度"),
|
||||||
|
),
|
||||||
|
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"db_table": "User",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="VideoExtractionRecord",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.BigAutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"extraction_link",
|
||||||
|
models.URLField(max_length=2048, verbose_name="提取链接"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"source",
|
||||||
|
models.CharField(
|
||||||
|
choices=[("index", "Index"), ("weixin", "Weixin")],
|
||||||
|
max_length=100,
|
||||||
|
verbose_name="来源",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"openid",
|
||||||
|
models.CharField(
|
||||||
|
blank=True, max_length=150, null=True, verbose_name="openid"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"nickname",
|
||||||
|
models.CharField(
|
||||||
|
blank=True, max_length=255, null=True, verbose_name="用户标识"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"video_title",
|
||||||
|
models.TextField(blank=True, null=True, verbose_name="视频标题"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"wxid",
|
||||||
|
models.CharField(
|
||||||
|
blank=True, max_length=255, null=True, verbose_name="微信id"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"wechat_alias",
|
||||||
|
models.CharField(
|
||||||
|
blank=True, max_length=255, null=True, verbose_name="微信号"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"is_successful",
|
||||||
|
models.CharField(
|
||||||
|
blank=True, max_length=255, null=True, verbose_name="是否提取成功"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"exception_reason",
|
||||||
|
models.TextField(blank=True, null=True, verbose_name="异常原因"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"extraction_time",
|
||||||
|
models.DateTimeField(auto_now_add=True, verbose_name="提取时间"),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"verbose_name": "视频提取记录",
|
||||||
|
"verbose_name_plural": "视频提取记录",
|
||||||
|
"db_table": "VideoExtractionRecord",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="VideoTask",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.BigAutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("openid", models.CharField(max_length=150)),
|
||||||
|
("nickname", models.CharField(max_length=150)),
|
||||||
|
("task_id", models.CharField(max_length=255, unique=True)),
|
||||||
|
("task_type", models.CharField(max_length=50)),
|
||||||
|
(
|
||||||
|
"status",
|
||||||
|
models.CharField(
|
||||||
|
choices=[
|
||||||
|
("PENDING", "待处理"),
|
||||||
|
("IN_PROGRESS", "处理中"),
|
||||||
|
("RUNNING", "运行中"),
|
||||||
|
("SUCCEEDED", "成功"),
|
||||||
|
("FAILED", "失败"),
|
||||||
|
],
|
||||||
|
default="PENDING",
|
||||||
|
max_length=20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||||
|
("updated_at", models.DateTimeField(auto_now=True)),
|
||||||
|
("text_prompt", models.TextField(null=True)),
|
||||||
|
("width", models.IntegerField(null=True)),
|
||||||
|
("height", models.IntegerField(null=True)),
|
||||||
|
("motion_score", models.IntegerField(null=True)),
|
||||||
|
("style", models.CharField(blank=True, max_length=255, null=True)),
|
||||||
|
("seconds", models.IntegerField(null=True)),
|
||||||
|
("image_url", models.TextField(blank=True, null=True)),
|
||||||
|
("result_url", models.TextField(blank=True, null=True)),
|
||||||
|
("qiniu_url", models.TextField(blank=True, null=True)),
|
||||||
|
("progress", models.FloatField(default=0.0)),
|
||||||
|
("error_message", models.TextField(blank=True, null=True)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"db_table": "video_task",
|
||||||
|
"ordering": ["-created_at"],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
87
MyApi/migrations/0002_assetlibrary.py
Normal file
87
MyApi/migrations/0002_assetlibrary.py
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
# Generated by Django 5.0.3 on 2024-06-01 06:06
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
import django.utils.timezone
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("MyApi", "0001_initial"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="AssetLibrary",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.BigAutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("qiniu_url", models.URLField(verbose_name="七牛云视频URL")),
|
||||||
|
("original_url", models.URLField(verbose_name="原始URL")),
|
||||||
|
("duration", models.FloatField(verbose_name="时长")),
|
||||||
|
(
|
||||||
|
"category",
|
||||||
|
models.CharField(
|
||||||
|
choices=[
|
||||||
|
("abandoned", "废弃"),
|
||||||
|
("abstract_sculpture", "抽象"),
|
||||||
|
("advertising", "广告"),
|
||||||
|
("anime", "动漫"),
|
||||||
|
("cine_lens", "电影镜头"),
|
||||||
|
("cinematic", "电影"),
|
||||||
|
("concept_art", "艺术"),
|
||||||
|
("forestpunk", "赛博朋克"),
|
||||||
|
("frost", "雪"),
|
||||||
|
("graphite", "石墨"),
|
||||||
|
("macro_photography", "宏观"),
|
||||||
|
("pixel_art", "像素艺术"),
|
||||||
|
("retro_photography", "复古"),
|
||||||
|
("sci_fi_art", "科幻"),
|
||||||
|
("thriller", "惊悚"),
|
||||||
|
("35mm", "35mm"),
|
||||||
|
("vector", "矢量"),
|
||||||
|
("watercolor", "水彩"),
|
||||||
|
],
|
||||||
|
max_length=50,
|
||||||
|
verbose_name="分类",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"description",
|
||||||
|
models.TextField(blank=True, null=True, verbose_name="视频描述"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"generated_at",
|
||||||
|
models.DateTimeField(
|
||||||
|
default=django.utils.timezone.now, verbose_name="生成时间"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("download_count", models.IntegerField(default=0, verbose_name="下载次数")),
|
||||||
|
(
|
||||||
|
"is_approved",
|
||||||
|
models.BooleanField(default=False, verbose_name="是否审核"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"generated_by",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to="MyApi.user",
|
||||||
|
verbose_name="生成用户",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"verbose_name": "素材库",
|
||||||
|
"verbose_name_plural": "素材库",
|
||||||
|
"db_table": "asset_library",
|
||||||
|
"ordering": ["-generated_at"],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
17
MyApi/migrations/0003_alter_assetlibrary_generated_by.py
Normal file
17
MyApi/migrations/0003_alter_assetlibrary_generated_by.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# Generated by Django 5.0.3 on 2024-06-01 06:25
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("MyApi", "0002_assetlibrary"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="assetlibrary",
|
||||||
|
name="generated_by",
|
||||||
|
field=models.CharField(max_length=50, null=True, verbose_name="生成用户"),
|
||||||
|
),
|
||||||
|
]
|
@ -0,0 +1,22 @@
|
|||||||
|
# Generated by Django 5.0.3 on 2024-06-01 07:03
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("MyApi", "0003_alter_assetlibrary_generated_by"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="assetlibrary",
|
||||||
|
name="original_url",
|
||||||
|
field=models.TextField(verbose_name="原始URL"),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="assetlibrary",
|
||||||
|
name="qiniu_url",
|
||||||
|
field=models.TextField(verbose_name="七牛云视频URL"),
|
||||||
|
),
|
||||||
|
]
|
37
MyApi/migrations/0005_transcriptiontask.py
Normal file
37
MyApi/migrations/0005_transcriptiontask.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# Generated by Django 5.0.3 on 2024-06-01 07:57
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("MyApi", "0004_alter_assetlibrary_original_url_and_more"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="TranscriptionTask",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.BigAutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("uuid", models.CharField(max_length=255)),
|
||||||
|
("video_url", models.URLField()),
|
||||||
|
("task_id", models.CharField(max_length=255)),
|
||||||
|
("result", models.TextField(blank=True, null=True)),
|
||||||
|
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||||
|
("updated_at", models.DateTimeField(auto_now=True)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"verbose_name": "文案提取任务",
|
||||||
|
"verbose_name_plural": "文案提取任务",
|
||||||
|
"db_table": "TranscriptionTask",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
17
MyApi/migrations/0006_alter_transcriptiontask_video_url.py
Normal file
17
MyApi/migrations/0006_alter_transcriptiontask_video_url.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# Generated by Django 5.0.3 on 2024-06-01 08:05
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("MyApi", "0005_transcriptiontask"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="transcriptiontask",
|
||||||
|
name="video_url",
|
||||||
|
field=models.TextField(),
|
||||||
|
),
|
||||||
|
]
|
@ -0,0 +1,22 @@
|
|||||||
|
# Generated by Django 5.0.3 on 2024-06-01 10:49
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("MyApi", "0006_alter_transcriptiontask_video_url"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="assetlibrary",
|
||||||
|
name="description_zh",
|
||||||
|
field=models.TextField(blank=True, null=True, verbose_name="中文描述"),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="videotask",
|
||||||
|
name="description_zh",
|
||||||
|
field=models.TextField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
]
|
17
MyApi/migrations/0008_videotask_is_processing.py
Normal file
17
MyApi/migrations/0008_videotask_is_processing.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# Generated by Django 5.0.3 on 2024-06-01 12:48
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("MyApi", "0007_assetlibrary_description_zh_videotask_description_zh"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="videotask",
|
||||||
|
name="is_processing",
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
]
|
@ -0,0 +1,91 @@
|
|||||||
|
# Generated by Django 5.0.3 on 2024-06-01 20:52
|
||||||
|
|
||||||
|
import django.utils.timezone
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("MyApi", "0008_videotask_is_processing"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="membershiptype",
|
||||||
|
name="daily_video_quota",
|
||||||
|
field=models.IntegerField(default=0, verbose_name="每日生成视频次数"),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="assetlibrary",
|
||||||
|
name="category",
|
||||||
|
field=models.CharField(
|
||||||
|
choices=[
|
||||||
|
("abandoned", "废弃"),
|
||||||
|
("abstract_sculpture", "抽象"),
|
||||||
|
("advertising", "广告"),
|
||||||
|
("anime", "动漫"),
|
||||||
|
("cine_lens", "电影镜头"),
|
||||||
|
("cinematic", "电影"),
|
||||||
|
("concept_art", "艺术"),
|
||||||
|
("forestpunk", "赛博朋克"),
|
||||||
|
("frost", "雪"),
|
||||||
|
("graphite", "石墨"),
|
||||||
|
("macro_photography", "宏观"),
|
||||||
|
("pixel_art", "像素艺术"),
|
||||||
|
("retro_photography", "复古"),
|
||||||
|
("sci_fi_art", "科幻"),
|
||||||
|
("thriller", "惊悚"),
|
||||||
|
("35mm", "35mm"),
|
||||||
|
("vector", "矢量"),
|
||||||
|
("watercolor", "水彩"),
|
||||||
|
],
|
||||||
|
max_length=50,
|
||||||
|
null=True,
|
||||||
|
verbose_name="分类",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="assetlibrary",
|
||||||
|
name="download_count",
|
||||||
|
field=models.IntegerField(default=0, null=True, verbose_name="下载次数"),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="assetlibrary",
|
||||||
|
name="generated_at",
|
||||||
|
field=models.DateTimeField(
|
||||||
|
default=django.utils.timezone.now, null=True, verbose_name="生成时间"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="assetlibrary",
|
||||||
|
name="is_approved",
|
||||||
|
field=models.BooleanField(default=False, null=True, verbose_name="是否审核"),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="assetlibrary",
|
||||||
|
name="original_url",
|
||||||
|
field=models.TextField(null=True, verbose_name="原始URL"),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="assetlibrary",
|
||||||
|
name="qiniu_url",
|
||||||
|
field=models.TextField(null=True, verbose_name="七牛云视频URL"),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="transactionlog",
|
||||||
|
name="remark",
|
||||||
|
field=models.TextField(
|
||||||
|
blank=True,
|
||||||
|
choices=[
|
||||||
|
("day", "一天会员充值"),
|
||||||
|
("week", "一周会员充值"),
|
||||||
|
("month", "一月会员充值"),
|
||||||
|
("season", "三月会员充值"),
|
||||||
|
("year", "一年会员充值"),
|
||||||
|
("lifetime", "永久会员充值"),
|
||||||
|
],
|
||||||
|
null=True,
|
||||||
|
verbose_name="备注",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
24
MyApi/migrations/0010_user_membership_type.py
Normal file
24
MyApi/migrations/0010_user_membership_type.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Generated by Django 5.0.3 on 2024-06-01 23:24
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("MyApi", "0009_membershiptype_daily_video_quota_and_more"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="user",
|
||||||
|
name="membership_type",
|
||||||
|
field=models.ForeignKey(
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.SET_NULL,
|
||||||
|
to="MyApi.membershiptype",
|
||||||
|
verbose_name="会员卡类型",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
@ -0,0 +1,31 @@
|
|||||||
|
# Generated by Django 5.0.3 on 2024-06-04 01:17
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("MyApi", "0010_user_membership_type"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name="membershiptype",
|
||||||
|
name="daily_video_quota",
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="membershiptype",
|
||||||
|
name="coins",
|
||||||
|
field=models.IntegerField(default=0, verbose_name="赠送金币数量"),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="membershiptype",
|
||||||
|
name="is_quota",
|
||||||
|
field=models.BooleanField(default=False, verbose_name="是否为额度充值"),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="user",
|
||||||
|
name="coins",
|
||||||
|
field=models.IntegerField(default=3, verbose_name="金币数量"),
|
||||||
|
),
|
||||||
|
]
|
@ -0,0 +1,30 @@
|
|||||||
|
# Generated by Django 5.0.3 on 2024-06-04 02:42
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("MyApi", "0011_remove_membershiptype_daily_video_quota_and_more"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name="user",
|
||||||
|
name="daily_video_quota",
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name="user",
|
||||||
|
name="membership_type",
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="transactionlog",
|
||||||
|
name="remark",
|
||||||
|
field=models.TextField(blank=True, null=True, verbose_name="备注"),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="transactionlog",
|
||||||
|
name="transaction_type",
|
||||||
|
field=models.CharField(max_length=50, verbose_name="交易类型"),
|
||||||
|
),
|
||||||
|
]
|
17
MyApi/migrations/0013_videotask_gif_url.py
Normal file
17
MyApi/migrations/0013_videotask_gif_url.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# Generated by Django 5.0.3 on 2024-06-04 05:40
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("MyApi", "0012_remove_user_daily_video_quota_and_more"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="videotask",
|
||||||
|
name="gif_url",
|
||||||
|
field=models.TextField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
]
|
@ -0,0 +1,25 @@
|
|||||||
|
# Generated by Django 5.0.3 on 2024-06-04 06:14
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("MyApi", "0013_videotask_gif_url"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name="assetlibrary",
|
||||||
|
name="qiniu_url",
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name="videotask",
|
||||||
|
name="qiniu_url",
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="assetlibrary",
|
||||||
|
name="gif_url",
|
||||||
|
field=models.TextField(blank=True, null=True, verbose_name="GIF图片URL"),
|
||||||
|
),
|
||||||
|
]
|
@ -0,0 +1,22 @@
|
|||||||
|
# Generated by Django 5.0.3 on 2024-06-04 06:18
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("MyApi", "0014_remove_assetlibrary_qiniu_url_and_more"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="assetlibrary",
|
||||||
|
name="qiniu_url",
|
||||||
|
field=models.TextField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="videotask",
|
||||||
|
name="qiniu_url",
|
||||||
|
field=models.TextField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
]
|
26
MyApi/migrations/0016_alter_videotask_status.py
Normal file
26
MyApi/migrations/0016_alter_videotask_status.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# Generated by Django 5.0.3 on 2024-06-04 11:25
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("MyApi", "0015_assetlibrary_qiniu_url_videotask_qiniu_url"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="videotask",
|
||||||
|
name="status",
|
||||||
|
field=models.CharField(
|
||||||
|
choices=[
|
||||||
|
("SUBMITTED", "已提交"),
|
||||||
|
("IN_PROGRESS", "处理中"),
|
||||||
|
("SUCCESS", "成功"),
|
||||||
|
("FAILURE", "失败"),
|
||||||
|
],
|
||||||
|
default="PENDING",
|
||||||
|
max_length=20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
0
MyApi/migrations/__init__.py
Normal file
0
MyApi/migrations/__init__.py
Normal file
351
MyApi/models.py
Normal file
351
MyApi/models.py
Normal file
@ -0,0 +1,351 @@
|
|||||||
|
from django.db import models
|
||||||
|
|
||||||
|
# Create your models here.
|
||||||
|
from datetime import datetime
|
||||||
|
from django.db import models
|
||||||
|
from django.contrib.auth.hashers import make_password,is_password_usable
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
|
# 管理员模型
|
||||||
|
class Administrator(models.Model):
|
||||||
|
username = models.CharField(max_length=150, unique=True) # 用户名,确保唯一性
|
||||||
|
password = models.CharField(max_length=128) # 密码,将会加密存储
|
||||||
|
role = models.CharField(max_length=100) # 角色
|
||||||
|
is_active = models.BooleanField(default=True) # 状态,启用/禁用,默认启用
|
||||||
|
last_login = models.DateTimeField(null=True, blank=True)
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True) # 创建时间,自动设置为当前时间
|
||||||
|
# 保存模型之前对密码进行加密
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
if not is_password_usable(self.password):
|
||||||
|
self.password = make_password(self.password)
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
class Meta:
|
||||||
|
db_table = "Administrator"
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
class MembershipType(models.Model):
|
||||||
|
type = models.CharField(max_length=50, unique=True, verbose_name="卡类型") # 卡类型,如天卡、周卡等 coins 额度卡
|
||||||
|
title = models.CharField(max_length=100, verbose_name="标题") # 卡的标题
|
||||||
|
description = models.TextField(verbose_name="描述") # 卡的描述
|
||||||
|
price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="价格") # 卡的价格
|
||||||
|
duration_days = models.IntegerField(verbose_name="有效天数") # 卡的有效期,以天为单位
|
||||||
|
coins = models.IntegerField(default=0, verbose_name="赠送金币数量") # 赠送金币的数量
|
||||||
|
is_quota = models.BooleanField(default=False, verbose_name="是否为额度充值") # 是否为额度充值
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = "membership_type" # 设置数据库表名
|
||||||
|
verbose_name = "会员卡类型"
|
||||||
|
verbose_name_plural = "会员卡类型"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.title
|
||||||
|
|
||||||
|
|
||||||
|
# 用户模型
|
||||||
|
class User(models.Model):
|
||||||
|
openid = models.CharField(max_length=150, unique=True) # 用户唯一标识
|
||||||
|
wxid = models.CharField(max_length=150, blank=True, null=True) # 微信ID
|
||||||
|
wechat_number = models.CharField(max_length=150, blank=True, null=True, verbose_name='微信号') # 微信号
|
||||||
|
nickname = models.CharField(max_length=150, blank=True, null=True) # 用户标识
|
||||||
|
gender = models.CharField(max_length=10, blank=True, null=True) # 性别
|
||||||
|
region = models.CharField(max_length=150, blank=True, null=True) # 地区
|
||||||
|
email = models.EmailField(blank=True, null=True) # 邮箱
|
||||||
|
phone = models.CharField(max_length=20, blank=True, null=True) # 号码
|
||||||
|
password = models.CharField(max_length=128, blank=True, null=True) # 密码
|
||||||
|
scene = models.CharField(max_length=100, blank=True, null=True) # 用户来源
|
||||||
|
is_member = models.BooleanField(default=False) # 是否为会员
|
||||||
|
member_start_time = models.BigIntegerField(blank=True, null=True) # 存储会员开始时间的时间戳
|
||||||
|
member_end_time = models.BigIntegerField(blank=True, null=True) # 存储会员到期时间的时间戳
|
||||||
|
is_active = models.BooleanField(default=True) # 状态,启用/禁用,默认启用
|
||||||
|
usage_count = models.IntegerField(default=0, blank=True, null=True) # 用户使用次数
|
||||||
|
coins = models.IntegerField(default=3, verbose_name='金币数量') # 用户金币数量
|
||||||
|
balance = models.DecimalField(max_digits=10, decimal_places=2, default=0.00, verbose_name='用户余额') # 用户余额
|
||||||
|
inviter_nickname = models.CharField(max_length=150, blank=True, null=True, verbose_name='邀请人昵称') # 用户邀请人昵称
|
||||||
|
invitees_count = models.IntegerField(default=0, verbose_name='邀请人数量') # 邀请人数量
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True) # 用户创建时间
|
||||||
|
class Meta:
|
||||||
|
db_table = "User"
|
||||||
|
|
||||||
|
#添加好友表
|
||||||
|
class FriendRequest(models.Model):
|
||||||
|
fromusername = models.CharField(max_length=255, verbose_name='微信ID') # 发起请求的微信ID
|
||||||
|
alias = models.CharField(max_length=255, blank=True, null=True, verbose_name='微信号') # 微信号
|
||||||
|
fromnickname = models.CharField(max_length=255, blank=True, null=True, verbose_name='昵称') # 昵称
|
||||||
|
country = models.CharField(max_length=255, blank=True, null=True, verbose_name='国家') # 国家
|
||||||
|
province = models.CharField(max_length=255, blank=True, null=True, verbose_name='省份') # 省份
|
||||||
|
city = models.CharField(max_length=255, blank=True, null=True, verbose_name='城市') # 城市
|
||||||
|
sex = models.CharField(max_length=1, blank=True, null=True, verbose_name='性别') # 性别
|
||||||
|
scene = models.CharField(max_length=255, blank=True, null=True, verbose_name='来源') # 来源
|
||||||
|
time = models.DateTimeField(verbose_name='申请时间') # 申请时间
|
||||||
|
towxid = models.CharField(max_length=255, verbose_name='目标微信ID') # 接收好友请求的微信ID
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = "FriendRequest"
|
||||||
|
verbose_name = '添加好友请求'
|
||||||
|
verbose_name_plural = '添加好友请求'
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.fromnickname or self.fromusername
|
||||||
|
|
||||||
|
#文案库
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
class Copywriting(models.Model):
|
||||||
|
openid = models.CharField(max_length=150, blank=True, null=True, verbose_name="openid")
|
||||||
|
nickname = models.CharField(max_length=255, blank=True, null=True, verbose_name="用户标识")
|
||||||
|
text_content = models.TextField(verbose_name="文本内容")
|
||||||
|
source = models.CharField(max_length=255, verbose_name="来源")
|
||||||
|
tag = models.CharField(max_length=255, default=None, verbose_name="标签")
|
||||||
|
popularity = models.IntegerField(default=0, verbose_name="热度")
|
||||||
|
added_time = models.DateTimeField(auto_now_add=True, verbose_name="添加时间")
|
||||||
|
is_approved = models.BooleanField(default=False, verbose_name="审核通过") # 默认未审核
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = "Copywriting"
|
||||||
|
verbose_name = "文案库"
|
||||||
|
verbose_name_plural = "文案库"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.text_content[:50] # 返回文案的前50个字符作为字符串表示
|
||||||
|
|
||||||
|
|
||||||
|
#视频提取记录
|
||||||
|
class VideoExtractionRecord(models.Model):
|
||||||
|
extraction_link = models.URLField(verbose_name="提取链接", max_length=2048)
|
||||||
|
source = models.CharField(max_length=100, choices=[('index', 'Index'), ('weixin', 'Weixin')], verbose_name="来源")
|
||||||
|
openid = models.CharField(max_length=150, blank=True, null=True, verbose_name="openid")
|
||||||
|
nickname = models.CharField(max_length=255, blank=True, null=True, verbose_name="用户标识")
|
||||||
|
video_title = models.TextField(blank=True, null=True, verbose_name="视频标题")
|
||||||
|
wxid = models.CharField(max_length=255, blank=True, null=True, verbose_name="微信id")
|
||||||
|
wechat_alias = models.CharField(max_length=255, blank=True, null=True, verbose_name="微信号")
|
||||||
|
is_successful = models.CharField(max_length=255,blank=True, null=True, verbose_name="是否提取成功")
|
||||||
|
exception_reason = models.TextField(blank=True, null=True, verbose_name="异常原因")
|
||||||
|
extraction_time = models.DateTimeField(auto_now_add=True, verbose_name="提取时间")
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = "VideoExtractionRecord"
|
||||||
|
verbose_name = "视频提取记录"
|
||||||
|
verbose_name_plural = "视频提取记录"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.user}'s extraction record"
|
||||||
|
|
||||||
|
#文案提取任务
|
||||||
|
class TranscriptionTask(models.Model):
|
||||||
|
uuid = models.CharField(max_length=255)
|
||||||
|
video_url = models.TextField()
|
||||||
|
task_id = models.CharField(max_length=255)
|
||||||
|
result = models.TextField(null=True, blank=True)
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
|
class Meta:
|
||||||
|
db_table = "TranscriptionTask"
|
||||||
|
verbose_name = "文案提取任务"
|
||||||
|
verbose_name_plural = "文案提取任务"
|
||||||
|
def __str__(self):
|
||||||
|
return self.task_id
|
||||||
|
|
||||||
|
#api调用日志
|
||||||
|
class ApiCallLog(models.Model):
|
||||||
|
# 用户来源,如'index', 'weixin'等
|
||||||
|
source = models.CharField(max_length=100, blank=True, null=True, choices=[('index', 'Index'), ('weixin', 'Weixin')], verbose_name="来源")
|
||||||
|
|
||||||
|
# 用户的OpenID
|
||||||
|
openid = models.CharField(max_length=150, blank=True, null=True, verbose_name="openid")
|
||||||
|
|
||||||
|
# 用户昵称
|
||||||
|
nickname = models.CharField(max_length=255, blank=True, null=True, verbose_name="用户标识")
|
||||||
|
|
||||||
|
# 微信ID
|
||||||
|
wxid = models.CharField(max_length=255, blank=True, null=True, verbose_name="微信id")
|
||||||
|
|
||||||
|
# 微信号
|
||||||
|
wechat_alias = models.CharField(max_length=255, blank=True, null=True, verbose_name="微信号")
|
||||||
|
|
||||||
|
# 调用的API接口名称或路径
|
||||||
|
api_name = models.CharField(max_length=255, verbose_name="调用接口")
|
||||||
|
|
||||||
|
# 调用结果,使用BooleanField表示成功或失败
|
||||||
|
is_successful = models.BooleanField(default=False, verbose_name="是否成功")
|
||||||
|
|
||||||
|
# 备注信息,可以用来记录失败原因或其他注释信息
|
||||||
|
remarks = models.TextField(blank=True, null=True, verbose_name="备注")
|
||||||
|
|
||||||
|
# 调用时间,使用自动设置为当前时间的DateTimeField
|
||||||
|
call_time = models.DateTimeField(auto_now_add=True, verbose_name="调用时间")
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = "api_call_log"
|
||||||
|
verbose_name = "API调用日志"
|
||||||
|
verbose_name_plural = "API调用日志"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.nickname} - {self.api_name} at {self.call_time}"
|
||||||
|
|
||||||
|
#卡密记录
|
||||||
|
class RedemptionCard(models.Model):
|
||||||
|
code = models.CharField(max_length=100, unique=True, verbose_name="卡密") # 卡密
|
||||||
|
card_type = models.CharField(max_length=50, choices=(('member', '会员卡'), ('quota', '额度卡')), verbose_name="卡类型") # 卡类型
|
||||||
|
created_date = models.DateTimeField(auto_now_add=True, verbose_name="生成日期") # 卡密生成日期
|
||||||
|
expiry_date = models.DateTimeField(verbose_name="到期时间") # 卡密到期时间
|
||||||
|
is_used = models.BooleanField(default=False, verbose_name="是否已使用") # 是否已被使用
|
||||||
|
used_by_openid = models.CharField(max_length=150, blank=True, null=True, verbose_name="使用者OpenID") # 使用者的OpenID
|
||||||
|
used_by_nickname = models.CharField(max_length=150, blank=True, null=True, verbose_name="使用者昵称") # 使用者的昵称
|
||||||
|
card_creation_time = models.DateTimeField(auto_now_add=True, verbose_name="卡密创建时间") # 卡密创建时间
|
||||||
|
validity_period = models.PositiveIntegerField(default=0, verbose_name="有效期") # 有效期天数或额度数量
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = "redemption_card" # 设置数据库表名
|
||||||
|
verbose_name = "兑换卡"
|
||||||
|
verbose_name_plural = "兑换卡"
|
||||||
|
|
||||||
|
|
||||||
|
# 充值列表
|
||||||
|
|
||||||
|
|
||||||
|
# 订单记录
|
||||||
|
class TransactionLog(models.Model):
|
||||||
|
TRANSACTION_STATUS_CHOICES = [
|
||||||
|
('pending', '待处理'),
|
||||||
|
('completed', '完成'),
|
||||||
|
('failed', '失败')
|
||||||
|
]
|
||||||
|
|
||||||
|
TRANSACTION_TYPE_CHOICES = [
|
||||||
|
('member', '会员购买'),
|
||||||
|
('quota', '续费会员')
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
transaction_no = models.CharField(max_length=255, unique=True, verbose_name="交易编号")
|
||||||
|
transaction_status = models.CharField(max_length=50, default='pending', choices=TRANSACTION_STATUS_CHOICES, verbose_name="交易状态")
|
||||||
|
user_openid = models.CharField(max_length=150, verbose_name="用户OpenID")
|
||||||
|
transaction_type = models.CharField(max_length=50, verbose_name="交易类型")
|
||||||
|
transaction_amount = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="交易金额")
|
||||||
|
remark = models.TextField(blank=True, null=True, verbose_name="备注")
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
|
||||||
|
updated_at = models.DateTimeField(auto_now=True, verbose_name="更新时间")
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = "transaction_log"
|
||||||
|
verbose_name = "交易记录"
|
||||||
|
verbose_name_plural = "交易记录"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.transaction_no} - {self.transaction_status}"
|
||||||
|
|
||||||
|
|
||||||
|
class IDCounter(models.Model):
|
||||||
|
count = models.IntegerField(default=10000) # 初始化为0或其他你想开始的数字
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_next_id(cls):
|
||||||
|
# 这个方法获取下一个ID,并且自增存储的计数
|
||||||
|
counter, _ = cls.objects.get_or_create(pk=1) # 假设我们只用一个计数器,其ID为1
|
||||||
|
counter.count += 1
|
||||||
|
counter.save()
|
||||||
|
return counter.count
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = "IDCounter"
|
||||||
|
|
||||||
|
class ChatRecord(models.Model):
|
||||||
|
openid = models.CharField(max_length=150,default=None) # 用户唯一标识
|
||||||
|
nickname = models.CharField(max_length=150, verbose_name='用户昵称') # 用户昵称
|
||||||
|
conversation_id = models.CharField(max_length=100, verbose_name='会话ID') # 会话ID
|
||||||
|
role = models.CharField(max_length=100, verbose_name='角色') # 角色
|
||||||
|
message_content = models.TextField(verbose_name='消息内容') # 消息内容
|
||||||
|
is_response = models.BooleanField(default=False, verbose_name='是否为AI回复') # 是否为AI回复
|
||||||
|
timestamp = models.DateTimeField(default=timezone.now, verbose_name='时间戳') # 时间戳
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = "chat_record" # 数据库表名
|
||||||
|
ordering = ['-timestamp'] # 默认排序
|
||||||
|
verbose_name = '聊天记录'
|
||||||
|
verbose_name_plural = '聊天记录'
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.nickname}: {self.message_content[:20]}"
|
||||||
|
|
||||||
|
class VideoTask(models.Model):
|
||||||
|
TASK_STATUS_CHOICES = [
|
||||||
|
('pending','待处理'),
|
||||||
|
('failed', '已提交'),
|
||||||
|
('running','运行中'),
|
||||||
|
('success', '成功'),
|
||||||
|
('failed', '失败'),
|
||||||
|
]
|
||||||
|
|
||||||
|
openid = models.CharField(max_length=150) # 用户唯一标识
|
||||||
|
nickname = models.CharField(max_length=150) # 用户昵称
|
||||||
|
task_id = models.CharField(max_length=255, unique=True) # 任务ID
|
||||||
|
task_type = models.CharField(max_length=50) # 任务类型,例如 'gen2'
|
||||||
|
status = models.CharField(max_length=20, choices=TASK_STATUS_CHOICES, default='pending') # 任务状态
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True) # 任务创建时间
|
||||||
|
updated_at = models.DateTimeField(auto_now=True) # 任务更新时间
|
||||||
|
text_prompt = models.TextField(null=True) # 文本提示
|
||||||
|
width = models.IntegerField(null=True) # 视频宽度
|
||||||
|
height = models.IntegerField(null=True) # 视频高度
|
||||||
|
motion_score = models.IntegerField(null=True) # 动态评分
|
||||||
|
style = models.CharField(max_length=255, blank=True, null=True) # 风格
|
||||||
|
seconds = models.IntegerField(null=True) # 视频时长
|
||||||
|
image_url = models.TextField(blank=True, null=True) # 图生视频的图像URL
|
||||||
|
result_url = models.TextField(blank=True, null=True) # 视频生成结果URL
|
||||||
|
qiniu_url = models.TextField(blank=True, null=True) # 七牛云储存url
|
||||||
|
gif_url = models.TextField(blank=True, null=True) # GIF图片链接
|
||||||
|
progress = models.FloatField(default=0.0) # 进度百分比
|
||||||
|
error_message = models.TextField(blank=True, null=True) # 错误信息
|
||||||
|
description_zh = models.TextField(blank=True, null=True) # 中文描述
|
||||||
|
is_processing = models.BooleanField(default=False) # 正在处理标志
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = "video_task"
|
||||||
|
ordering = ['-created_at']
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.task_id
|
||||||
|
|
||||||
|
|
||||||
|
class AssetLibrary(models.Model):
|
||||||
|
STYLE_CHOICES = [
|
||||||
|
('abandoned', '废弃'),
|
||||||
|
('abstract_sculpture', '抽象'),
|
||||||
|
('advertising', '广告'),
|
||||||
|
('anime', '动漫'),
|
||||||
|
('cine_lens', '电影镜头'),
|
||||||
|
('cinematic', '电影'),
|
||||||
|
('concept_art', '艺术'),
|
||||||
|
('forestpunk', '赛博朋克'),
|
||||||
|
('frost', '雪'),
|
||||||
|
('graphite', '石墨'),
|
||||||
|
('macro_photography', '宏观'),
|
||||||
|
('pixel_art', '像素艺术'),
|
||||||
|
('retro_photography', '复古'),
|
||||||
|
('sci_fi_art', '科幻'),
|
||||||
|
('thriller', '惊悚'),
|
||||||
|
('35mm', '35mm'),
|
||||||
|
('vector', '矢量'),
|
||||||
|
('watercolor', '水彩'),
|
||||||
|
]
|
||||||
|
|
||||||
|
original_url = models.TextField(verbose_name='原始URL', null=True) # 原始URL
|
||||||
|
qiniu_url = models.TextField(blank=True, null=True) # 七牛云储存url
|
||||||
|
duration = models.FloatField(verbose_name='时长') # 视频时长,以秒为单位
|
||||||
|
category = models.CharField(max_length=50, choices=STYLE_CHOICES, verbose_name='分类', null=True) # 视频分类
|
||||||
|
description = models.TextField(verbose_name='视频描述', blank=True, null=True) # 视频描述
|
||||||
|
description_zh = models.TextField(verbose_name='中文描述', blank=True, null=True) # 中文描述
|
||||||
|
generated_by = models.CharField(null=True, verbose_name='生成用户', max_length=50) # 生成用户
|
||||||
|
generated_at = models.DateTimeField(default=timezone.now, verbose_name='生成时间', null=True) # 生成时间
|
||||||
|
download_count = models.IntegerField(default=0, verbose_name='下载次数', null=True) # 下载次数
|
||||||
|
is_approved = models.BooleanField(default=False, verbose_name='是否审核', null=True) # 是否审核
|
||||||
|
gif_url = models.TextField(verbose_name='GIF图片URL', blank=True, null=True) # GIF图片URL
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = "asset_library" # 数据库表名
|
||||||
|
verbose_name = '素材库'
|
||||||
|
verbose_name_plural = '素材库'
|
||||||
|
ordering = ['-generated_at'] # 默认排序
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.original_url
|
170
MyApi/sparkAPI.py
Normal file
170
MyApi/sparkAPI.py
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
import _thread as thread
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import base64
|
||||||
|
|
||||||
|
import base64
|
||||||
|
import datetime
|
||||||
|
import hashlib
|
||||||
|
import hmac
|
||||||
|
import json
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
import ssl
|
||||||
|
from datetime import datetime
|
||||||
|
from time import mktime
|
||||||
|
from urllib.parse import urlencode
|
||||||
|
from wsgiref.handlers import format_date_time
|
||||||
|
|
||||||
|
import websocket
|
||||||
|
|
||||||
|
class Ws_Param(object):
|
||||||
|
# 初始化
|
||||||
|
def __init__(self, APPID, APIKey, APISecret, gpt_url):
|
||||||
|
self.APPID = APPID
|
||||||
|
self.APIKey = APIKey
|
||||||
|
self.APISecret = APISecret
|
||||||
|
self.host = urlparse(gpt_url).netloc
|
||||||
|
self.path = urlparse(gpt_url).path
|
||||||
|
self.gpt_url = gpt_url
|
||||||
|
|
||||||
|
# 生成url
|
||||||
|
def create_url(self):
|
||||||
|
# 生成RFC1123格式的时间戳
|
||||||
|
now = datetime.now()
|
||||||
|
date = format_date_time(mktime(now.timetuple()))
|
||||||
|
|
||||||
|
# 拼接字符串
|
||||||
|
signature_origin = "host: " + self.host + "\n"
|
||||||
|
signature_origin += "date: " + date + "\n"
|
||||||
|
signature_origin += "GET " + self.path + " HTTP/1.1"
|
||||||
|
|
||||||
|
# 进行hmac-sha256进行加密
|
||||||
|
signature_sha = hmac.new(self.APISecret.encode('utf-8'), signature_origin.encode('utf-8'),
|
||||||
|
digestmod=hashlib.sha256).digest()
|
||||||
|
|
||||||
|
signature_sha_base64 = base64.b64encode(signature_sha).decode(encoding='utf-8')
|
||||||
|
|
||||||
|
authorization_origin = f'api_key="{self.APIKey}", algorithm="hmac-sha256", headers="host date request-line", signature="{signature_sha_base64}"'
|
||||||
|
|
||||||
|
authorization = base64.b64encode(authorization_origin.encode('utf-8')).decode(encoding='utf-8')
|
||||||
|
|
||||||
|
# 将请求的鉴权参数组合为字典
|
||||||
|
v = {
|
||||||
|
"authorization": authorization,
|
||||||
|
"date": date,
|
||||||
|
"host": self.host
|
||||||
|
}
|
||||||
|
# 拼接鉴权参数,生成url
|
||||||
|
url = self.gpt_url + '?' + urlencode(v)
|
||||||
|
# 此处打印出建立连接时候的url,参考本demo的时候可取消上方打印的注释,比对相同参数时生成的url与自己代码生成的url是否一致
|
||||||
|
return url
|
||||||
|
|
||||||
|
# import openpyxl
|
||||||
|
# from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||||
|
# import os
|
||||||
|
|
||||||
|
|
||||||
|
class SparkUtil(object):
|
||||||
|
# 初始化
|
||||||
|
def __init__(self):
|
||||||
|
self.isClose = False
|
||||||
|
self.data = ''
|
||||||
|
|
||||||
|
appid = "cb0bb4de"
|
||||||
|
api_secret = "MDI5MGNhOThlNTQxNTBhY2ZjODNkM2Fh"
|
||||||
|
api_key = "dfd17a540cf52db34c98aac216a0473c"
|
||||||
|
gpt_url = "wss://spark-api.xf-yun.com/v3.5/chat"
|
||||||
|
domain = "generalv3.5"
|
||||||
|
|
||||||
|
wsParam = Ws_Param(appid, api_key, api_secret, gpt_url)
|
||||||
|
websocket.enableTrace(False)
|
||||||
|
wsUrl = wsParam.create_url()
|
||||||
|
|
||||||
|
self.ws = websocket.WebSocketApp(wsUrl, on_message=self.on_message, on_error=self.on_error,
|
||||||
|
on_close=self.on_close,
|
||||||
|
on_open=self.on_open)
|
||||||
|
self.ws.appid = appid
|
||||||
|
self.ws.domain = domain
|
||||||
|
|
||||||
|
# 收到websocket错误的处理
|
||||||
|
def on_error(self, ws, error):
|
||||||
|
self.isClose = True
|
||||||
|
print("###--- error:", error)
|
||||||
|
|
||||||
|
# 收到websocket关闭的处理
|
||||||
|
def on_close(self, ws,close_status_code, close_msg):
|
||||||
|
self.isClose = True
|
||||||
|
print("### closed ###------")
|
||||||
|
|
||||||
|
# 获取数据
|
||||||
|
def query_and_get_data(self, query):
|
||||||
|
self.ws.query = query
|
||||||
|
self.ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})
|
||||||
|
while True:
|
||||||
|
if self.isClose:
|
||||||
|
self.isClose = False
|
||||||
|
result = self.data
|
||||||
|
self.data = ''
|
||||||
|
return result
|
||||||
|
|
||||||
|
# 收到websocket连接建立的处理
|
||||||
|
def on_open(self, ws):
|
||||||
|
thread.start_new_thread(self.run, (ws,))
|
||||||
|
|
||||||
|
def run(self, ws, *args):
|
||||||
|
data = json.dumps(self.gen_params(appid=ws.appid, query=ws.query, domain=ws.domain))
|
||||||
|
ws.send(data)
|
||||||
|
|
||||||
|
# 收到websocket消息的处理
|
||||||
|
def on_message(self, ws, message):
|
||||||
|
# print(message)
|
||||||
|
data = json.loads(message)
|
||||||
|
code = data['header']['code']
|
||||||
|
if code != 0:
|
||||||
|
print(f'请求错误: {code}, {data}')
|
||||||
|
self.isClose = True
|
||||||
|
ws.close()
|
||||||
|
else:
|
||||||
|
choices = data["payload"]["choices"]
|
||||||
|
status = choices["status"]
|
||||||
|
content = choices["text"][0]["content"]
|
||||||
|
self.data += content
|
||||||
|
print(content, end='')
|
||||||
|
if status == 2:
|
||||||
|
print("#### 关闭会话")
|
||||||
|
self.isClose = True
|
||||||
|
ws.close()
|
||||||
|
|
||||||
|
|
||||||
|
def gen_params(self, appid, query, domain):
|
||||||
|
"""
|
||||||
|
通过appid和用户的提问来生成请参数
|
||||||
|
"""
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"header": {
|
||||||
|
"app_id": appid,
|
||||||
|
"uid": "文案助手",
|
||||||
|
# "patch_id": [] #接入微调模型,对应服务发布后的resourceid
|
||||||
|
},
|
||||||
|
"parameter": {
|
||||||
|
"chat": {
|
||||||
|
"domain": domain,
|
||||||
|
"temperature": 0.5,
|
||||||
|
"max_tokens": 4096,
|
||||||
|
"auditing": "default",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"payload": {
|
||||||
|
"message": {
|
||||||
|
"text": [{"role": "user", "content": query}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
data = SparkUtil().query_and_get_data("帮我写一篇一百字的作文")
|
||||||
|
print(data)
|
3
MyApi/tests.py
Normal file
3
MyApi/tests.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
40
MyApi/urls.py
Normal file
40
MyApi/urls.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# myapp/urls.py
|
||||||
|
from django.urls import path
|
||||||
|
from . import views
|
||||||
|
from . import video_text
|
||||||
|
from . import wxpay
|
||||||
|
from . import AiMssage
|
||||||
|
urlpatterns = [
|
||||||
|
path('login/', views.miniapp_login, name='miniapp_login'),
|
||||||
|
path('video/', views.video_extraction, name='video_extraction'),
|
||||||
|
path('add_friend/', views.add_friend_request, name='add_friend_request'),
|
||||||
|
path('total/', views.total, name='total'),
|
||||||
|
# path('record_extraction', views.record_extraction, name='record_extraction'),
|
||||||
|
# path('video_transcribe/', views.video_transcribe, name='video_transcribe'),
|
||||||
|
path('rewrite_text/', views.rewrite_text, name='rewrite_text'),
|
||||||
|
path('get_copywriting_list_with_filters/', views.get_copywriting_list_with_filters,name='get_copywriting_list_with_filters'),
|
||||||
|
# path('create_copywriting/', views.create_copywriting_entry, name='create_copywriting'),
|
||||||
|
path('userinfo/', views.userinfo, name='userinfo'),
|
||||||
|
path('redeem_card/', views.redeem_card, name='redeem_card'),
|
||||||
|
path('reward_invitation', views.reward_invitation, name='reward_invitation'),
|
||||||
|
path('video_to_text/', video_text.transcribe_audio, name='video_to_text'),
|
||||||
|
path('query_task/', video_text.query_task_status, name='query_task'),
|
||||||
|
path('wx_pay/', wxpay.wx_pay, name='wx_pay'),
|
||||||
|
path('wx_pay_notify/', wxpay.wx_pay_notify, name='wx_pay_notify'),
|
||||||
|
path('upload_image/', views.UploadImageView.as_view(), name='upload_image'),
|
||||||
|
path('generate_video/', views.GenerateVideoView.as_view(), name='generate_video'),
|
||||||
|
path('task_status/<str:task_id>/', views.TaskStatusView.as_view(), name='task_status'),
|
||||||
|
path('extend_video/', views.ExtendVideoView.as_view(), name='extend_video'),
|
||||||
|
path('generate_image_video/', views.GenerateImageVideoView.as_view(), name='generate_image_video'),
|
||||||
|
path('user/<int:user_id>/video_tasks/', views.UserVideoTaskListView.as_view(), name='user_video_tasks'),
|
||||||
|
path('user/<int:user_id>/delete_task/<str:task_id>/', views.DeleteVideoTaskView.as_view(), name='delete_task'),
|
||||||
|
path('call_bailian_app/', AiMssage.call_bailian_app, name='call_bailian_app'),
|
||||||
|
path('get_recent_chat_records/', AiMssage.get_recent_chat_records, name='get_recent_chat_records'),
|
||||||
|
path('asset_library/', views.AssetLibraryView.as_view(), name='asset_library'),
|
||||||
|
path('update_pending_tasks/',views.UpdatePendingTasksView.as_view(), name='update_pending_tasks'),
|
||||||
|
path('increment-download-count/', views.increment_download_count, name='increment_download_count'),
|
||||||
|
path('reset-daily-quota/', views.DailyCoinsBonusView.as_view(), name='reset_daily_quota'),
|
||||||
|
path('video-generation-callback/', views.VideoGenerationCallbackView.as_view(), name='VideoGenerationCallbackView'),
|
||||||
|
|
||||||
|
]
|
||||||
|
|
177
MyApi/video_text.py
Normal file
177
MyApi/video_text.py
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
import base64
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
from tencentcloud.common import credential
|
||||||
|
from tencentcloud.common.profile.client_profile import ClientProfile
|
||||||
|
from tencentcloud.common.profile.http_profile import HttpProfile
|
||||||
|
|
||||||
|
from tencentcloud.asr.v20190614 import asr_client, models
|
||||||
|
from django.http import JsonResponse
|
||||||
|
from Crypto.Cipher import AES
|
||||||
|
import base64
|
||||||
|
|
||||||
|
from .API_Log import get_logger
|
||||||
|
from .models import TranscriptionTask
|
||||||
|
|
||||||
|
from .views import update_usage_count
|
||||||
|
# 使用示例
|
||||||
|
log_file = '文案提取日志.log'
|
||||||
|
logger = get_logger('文案提取日志', log_file, when='midnight', backup_count=7)
|
||||||
|
|
||||||
|
def decrypt_param(x, t):
|
||||||
|
# 创建AES解密器对象
|
||||||
|
key = b'qw5w6SFE2D1jmxyd'
|
||||||
|
iv = b'345GDFED433223DF'
|
||||||
|
|
||||||
|
# 检查 x 和 t 是否为空
|
||||||
|
if not x or not t:
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
cipher = AES.new(key, AES.MODE_CBC, iv)
|
||||||
|
# 将密文进行Base64解码
|
||||||
|
ciphertext_bytes = base64.b64decode(x)
|
||||||
|
# 使用解密器解密密文
|
||||||
|
plaintext_bytes = cipher.decrypt(ciphertext_bytes)
|
||||||
|
# 删除填充字符
|
||||||
|
plaintext = plaintext_bytes.rstrip(b'\0').decode('utf-8')
|
||||||
|
# 比较解密后的明文和 t
|
||||||
|
if plaintext.rstrip('\x03') == t.rstrip('\x03'):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
print(f"解密过程出现错误: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
# 数据库操作函数
|
||||||
|
def get_task_from_db(video_url):
|
||||||
|
try:
|
||||||
|
return TranscriptionTask.objects.get(video_url=video_url)
|
||||||
|
except TranscriptionTask.DoesNotExist:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def save_task_to_db(uuid, video_url, task_id, result=None):
|
||||||
|
task = TranscriptionTask(uuid=uuid, video_url=video_url, task_id=task_id, result=result)
|
||||||
|
task.save()
|
||||||
|
|
||||||
|
|
||||||
|
def get_task_result_from_db(task_id):
|
||||||
|
try:
|
||||||
|
task = TranscriptionTask.objects.get(task_id=task_id)
|
||||||
|
return task.result
|
||||||
|
except TranscriptionTask.DoesNotExist:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def update_task_result_in_db(task_id, result):
|
||||||
|
try:
|
||||||
|
task = TranscriptionTask.objects.get(task_id=task_id)
|
||||||
|
task.result = result
|
||||||
|
task.save()
|
||||||
|
except TranscriptionTask.DoesNotExist:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def transcribe_audio(request):
|
||||||
|
data = json.loads(request.body)
|
||||||
|
video_url = data.get('url')
|
||||||
|
uuid = data.get('uuid')
|
||||||
|
openid = data.get('openid')
|
||||||
|
x = request.headers.get('X', '')
|
||||||
|
t = request.headers.get('T', '')
|
||||||
|
|
||||||
|
if not uuid or not video_url:
|
||||||
|
logger.error(
|
||||||
|
f'API 文案提取: 【缺少必要参数】\n headers={request.headers}\n data={data} \n ---------------------------------')
|
||||||
|
return JsonResponse({'error': '缺少必要参数'})
|
||||||
|
|
||||||
|
if decrypt_param(x, t):
|
||||||
|
existing_task = get_task_from_db(video_url)
|
||||||
|
if existing_task:
|
||||||
|
if existing_task.result:
|
||||||
|
return JsonResponse({'result': existing_task.result})
|
||||||
|
else:
|
||||||
|
return JsonResponse({'task_id': existing_task.task_id})
|
||||||
|
else:
|
||||||
|
increment = -1 # 假设每次生成视频需要消耗一次使用次数
|
||||||
|
function_type = 'transcribe_audio'
|
||||||
|
result = update_usage_count(openid, increment, function_type)
|
||||||
|
if not result['success']:
|
||||||
|
return JsonResponse(result)
|
||||||
|
|
||||||
|
cred = credential.Credential("AKIDb6YsAMQL4hBjVGybN6gzLeu7FUegcins", "MpYQQbp8UxQ2pbIKXrCX5d2hjx9Th2Or")
|
||||||
|
http_profile = HttpProfile()
|
||||||
|
http_profile.endpoint = "asr.tencentcloudapi.com"
|
||||||
|
client_profile = ClientProfile()
|
||||||
|
client_profile.httpProfile = http_profile
|
||||||
|
client = asr_client.AsrClient(cred, "ap-guangzhou", client_profile)
|
||||||
|
req = models.CreateRecTaskRequest()
|
||||||
|
req.EngineModelType = "16k_zh"
|
||||||
|
req.ChannelNum = 1
|
||||||
|
req.ResTextFormat = 0
|
||||||
|
req.SourceType = 0
|
||||||
|
req.Url = video_url
|
||||||
|
resp = client.CreateRecTask(req)
|
||||||
|
resp_dict = json.loads(resp.to_json_string())
|
||||||
|
|
||||||
|
task_id = resp_dict['Data']['TaskId']
|
||||||
|
save_task_to_db(uuid, video_url, task_id)
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
f'API 文案提取: 【{uuid}--{video_url} 提交任务成功】\n 返回结果={resp}\n---------------------------------')
|
||||||
|
return JsonResponse(resp_dict, safe=False)
|
||||||
|
else:
|
||||||
|
logger.error(
|
||||||
|
f'API 文案提取: 【非法参数】\n headers={request.headers}\n data={data} \n ---------------------------------')
|
||||||
|
return JsonResponse({'error': '非法参数'})
|
||||||
|
|
||||||
|
def query_task_status(request):
|
||||||
|
data = json.loads(request.body)
|
||||||
|
logger.info(
|
||||||
|
f'API 文案提取任务查询: {data} \n ---------------------------------')
|
||||||
|
task_id = data.get('task_id')
|
||||||
|
uuid = data.get('uuid')
|
||||||
|
x = request.headers.get('X', '')
|
||||||
|
t = request.headers.get('T', '')
|
||||||
|
|
||||||
|
if not uuid or not task_id:
|
||||||
|
logger.error(
|
||||||
|
f'API 文案提取任务查询: 【缺少必要参数】\n headers={request.headers}\n data={data} \n ---------------------------------')
|
||||||
|
return JsonResponse({'error': '缺少必要参数'})
|
||||||
|
|
||||||
|
if decrypt_param(x, t):
|
||||||
|
cached_result = get_task_result_from_db(task_id)
|
||||||
|
if cached_result:
|
||||||
|
return JsonResponse({'result': cached_result})
|
||||||
|
|
||||||
|
cred = credential.Credential("AKIDb6YsAMQL4hBjVGybN6gzLeu7FUegcins", "MpYQQbp8UxQ2pbIKXrCX5d2hjx9Th2Or")
|
||||||
|
http_profile = HttpProfile()
|
||||||
|
http_profile.endpoint = "asr.tencentcloudapi.com"
|
||||||
|
clientProfile = ClientProfile()
|
||||||
|
clientProfile.httpProfile = http_profile
|
||||||
|
client = asr_client.AsrClient(cred, "ap-guangzhou", clientProfile)
|
||||||
|
req = models.DescribeTaskStatusRequest()
|
||||||
|
params = {
|
||||||
|
"TaskId": int(task_id)
|
||||||
|
}
|
||||||
|
req.from_json_string(json.dumps(params))
|
||||||
|
|
||||||
|
resp = client.DescribeTaskStatus(req)
|
||||||
|
resp_dict = json.loads(resp.to_json_string())
|
||||||
|
|
||||||
|
if resp_dict['Data']['Status'] == 2:
|
||||||
|
result = resp_dict['Data']['Result']
|
||||||
|
update_task_result_in_db(task_id, result)
|
||||||
|
return JsonResponse({'result': result})
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
f'API 文案提取任务查询: 【{uuid}--{task_id} 进度查询】\n 返回结果={resp}\n---------------------------------')
|
||||||
|
return JsonResponse(resp_dict, safe=False)
|
||||||
|
else:
|
||||||
|
logger.error(
|
||||||
|
f'API 文案提取任务查询: 【非法参数】\n headers={request.headers}\n data={data} \n ---------------------------------')
|
||||||
|
return JsonResponse({'error': '非法参数'})
|
1260
MyApi/views.py
Normal file
1260
MyApi/views.py
Normal file
File diff suppressed because it is too large
Load Diff
310
MyApi/wxpay.py
Normal file
310
MyApi/wxpay.py
Normal file
@ -0,0 +1,310 @@
|
|||||||
|
import base64
|
||||||
|
import json
|
||||||
|
import string
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
|
import requests
|
||||||
|
import time
|
||||||
|
import random
|
||||||
|
from xml.etree import ElementTree as ET
|
||||||
|
from Crypto.Hash import SHA256
|
||||||
|
from Crypto.PublicKey import RSA
|
||||||
|
from Crypto.Signature import pkcs1_15
|
||||||
|
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
||||||
|
from django.http import JsonResponse, HttpResponse
|
||||||
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
|
from django.conf import settings
|
||||||
|
from .models import TransactionLog, MembershipType,User
|
||||||
|
from .API_Log import get_logger
|
||||||
|
log_file = '支付日志.log'
|
||||||
|
logger = get_logger('支付日志', log_file, when='midnight', backup_count=7)
|
||||||
|
|
||||||
|
|
||||||
|
def wx_pay(request):
|
||||||
|
request_data = json.loads(request.body)
|
||||||
|
type = request_data.get('type', 'default')
|
||||||
|
wx_price = request_data['total_fee'] # 从请求获取订单金额
|
||||||
|
openid = request_data['openid'] # 从请求获取用户openid
|
||||||
|
transaction_type = request_data['transaction_type'] # 续费或者开通
|
||||||
|
up_time = datetime.now()
|
||||||
|
|
||||||
|
# 从数据库中动态获取 body_map
|
||||||
|
membership_types = MembershipType.objects.all()
|
||||||
|
body_map = {membership_type.type: membership_type.title for membership_type in membership_types}
|
||||||
|
|
||||||
|
body_description = body_map.get(type)
|
||||||
|
|
||||||
|
# 以交易日期生成交易号
|
||||||
|
transactionNo = str(up_time).replace('.', '').replace('-', '').replace(':', '').replace(' ', '')
|
||||||
|
|
||||||
|
membership_type = MembershipType.objects.get(type=type)
|
||||||
|
price = membership_type.price
|
||||||
|
newTransaction = TransactionLog.objects.create(
|
||||||
|
transaction_no=transactionNo,
|
||||||
|
transaction_status='pending',
|
||||||
|
user_openid=openid,
|
||||||
|
transaction_type=transaction_type,
|
||||||
|
transaction_amount=price,
|
||||||
|
remark=type,
|
||||||
|
created_at=up_time
|
||||||
|
)
|
||||||
|
|
||||||
|
# 打印和记录日志
|
||||||
|
logger.info("生成订单:")
|
||||||
|
logger.info(f"商户订单号:{transactionNo}")
|
||||||
|
logger.info(f"用户OpenID:{openid}")
|
||||||
|
logger.info(f"type:{type}")
|
||||||
|
logger.info(f"transaction_type:{transaction_type}")
|
||||||
|
logger.info(f"成功时间:{up_time}")
|
||||||
|
logger.info(f"金额(前端):{wx_price}")
|
||||||
|
logger.info(f"金额(后端):{price}")
|
||||||
|
|
||||||
|
# 生成统一下单的报文body
|
||||||
|
url = 'https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi'
|
||||||
|
body = {
|
||||||
|
"appid": settings.WX_APP_ID,
|
||||||
|
"mchid": settings.WX_MCH_ID,
|
||||||
|
"description": body_description,
|
||||||
|
"out_trade_no": transactionNo,
|
||||||
|
"notify_url": settings.WX_NOTIFY_URL, # 后端接收回调通知的接口
|
||||||
|
"amount": {"total": int(float(price) * 100), "currency": "CNY"}, # 微信金额单位为分
|
||||||
|
"payer": {"openid": openid},
|
||||||
|
"attach": json.dumps({"type": type, "transaction_type": transaction_type})
|
||||||
|
}
|
||||||
|
data = json.dumps(body)
|
||||||
|
|
||||||
|
# 定义生成签名的函数
|
||||||
|
def get_sign(sign_str):
|
||||||
|
rsa_key = RSA.importKey(open(settings.WX_KEY_PATH).read())
|
||||||
|
signer = pkcs1_15.new(rsa_key)
|
||||||
|
digest = SHA256.new(sign_str.encode('utf8'))
|
||||||
|
sign = base64.b64encode(signer.sign(digest)).decode('utf-8')
|
||||||
|
return sign
|
||||||
|
|
||||||
|
# 生成请求随机串
|
||||||
|
random_str = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(32))
|
||||||
|
|
||||||
|
# 生成请求时间戳
|
||||||
|
time_stamps = str(int(time.time()))
|
||||||
|
|
||||||
|
# 生成签名串
|
||||||
|
sign_str = f"POST\n/v3/pay/transactions/jsapi\n{time_stamps}\n{random_str}\n{data}\n"
|
||||||
|
|
||||||
|
# 生成签名
|
||||||
|
sign = get_sign(sign_str)
|
||||||
|
|
||||||
|
# 生成HTTP请求头
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'User-Agent': '*/*',
|
||||||
|
'Authorization': 'WECHATPAY2-SHA256-RSA2048 ' + f'mchid="{settings.WX_MCH_ID}",nonce_str="{random_str}",signature="{sign}",timestamp="{time_stamps}",serial_no="{settings.WX_SERIAL_NO}"',
|
||||||
|
'Wechatpay-Serial': '66DB35F836EFD4CBEC66F3815D283A2892310324'
|
||||||
|
}
|
||||||
|
|
||||||
|
# 发送请求获得prepay_id
|
||||||
|
response = requests.post(url, data=data, headers=headers) # 获取预支付交易会话标识(prepay_id)
|
||||||
|
|
||||||
|
# 应答签名验证
|
||||||
|
wechatpaySerial = response.headers['Wechatpay-Serial'] # 获取HTTP头部中包括回调报文的证书序列号
|
||||||
|
wechatpaySignature = response.headers['Wechatpay-Signature'] # 获取HTTP头部中包括回调报文的签名
|
||||||
|
wechatpayTimestamp = response.headers['Wechatpay-Timestamp'] # 获取HTTP头部中包括回调报文的时间戳
|
||||||
|
wechatpayNonce = response.headers['Wechatpay-Nonce'] # 获取HTTP头部中包括回调报文的随机串
|
||||||
|
|
||||||
|
# 获取微信平台证书
|
||||||
|
url2 = "https://api.mch.weixin.qq.com/v3/certificates"
|
||||||
|
|
||||||
|
# 生成证书请求随机串
|
||||||
|
random_str2 = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(32))
|
||||||
|
|
||||||
|
# 生成证书请求时间戳
|
||||||
|
time_stamps2 = str(int(time.time()))
|
||||||
|
|
||||||
|
# 生成请求证书的签名串
|
||||||
|
data2 = ""
|
||||||
|
sign_str2 = f"GET\n/v3/certificates\n{time_stamps2}\n{random_str2}\n{data2}\n"
|
||||||
|
|
||||||
|
# 生成签名
|
||||||
|
sign2 = get_sign(sign_str2)
|
||||||
|
|
||||||
|
# 生成HTTP请求头
|
||||||
|
headers2 = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Authorization': 'WECHATPAY2-SHA256-RSA2048 ' + f'mchid="{settings.WX_MCH_ID}",nonce_str="{random_str2}",signature="{sign2}",timestamp="{time_stamps2}",serial_no="{settings.WX_SERIAL_NO}"'
|
||||||
|
}
|
||||||
|
|
||||||
|
# 发送请求获得证书
|
||||||
|
response2 = requests.get(url2, headers=headers2) # 只需要请求头
|
||||||
|
cert = response2.json()
|
||||||
|
|
||||||
|
# 证书解密
|
||||||
|
def decrypt(nonce, ciphertext, associated_data):
|
||||||
|
key = settings.WX_API_KEY
|
||||||
|
key_bytes = str.encode(key)
|
||||||
|
nonce_bytes = str.encode(nonce)
|
||||||
|
ad_bytes = str.encode(associated_data)
|
||||||
|
data = base64.b64decode(ciphertext)
|
||||||
|
aesgcm = AESGCM(key_bytes)
|
||||||
|
return aesgcm.decrypt(nonce_bytes, data, ad_bytes)
|
||||||
|
|
||||||
|
nonce = cert["data"][0]['encrypt_certificate']['nonce']
|
||||||
|
ciphertext = cert["data"][0]['encrypt_certificate']['ciphertext']
|
||||||
|
associated_data = cert["data"][0]['encrypt_certificate']['associated_data']
|
||||||
|
serial_no = cert["data"][0]['serial_no']
|
||||||
|
certificate = decrypt(nonce, ciphertext, associated_data)
|
||||||
|
|
||||||
|
# 签名验证
|
||||||
|
if wechatpaySerial == serial_no: # 应答签名中的序列号同证书序列号应相同
|
||||||
|
print('serial_no match')
|
||||||
|
|
||||||
|
def verify(data, signature): # 验签函数
|
||||||
|
key = RSA.importKey(certificate) # 直接使用解密后的证书
|
||||||
|
verifier = pkcs1_15.new(key)
|
||||||
|
hash_obj = SHA256.new(data.encode('utf8'))
|
||||||
|
return verifier.verify(hash_obj, base64.b64decode(signature))
|
||||||
|
|
||||||
|
data3 = f"{wechatpayTimestamp}\n{wechatpayNonce}\n{response.text}\n"
|
||||||
|
try:
|
||||||
|
verify(data3, wechatpaySignature)
|
||||||
|
|
||||||
|
# 生成调起支付API需要的参数并返回前端
|
||||||
|
res = {
|
||||||
|
'timeStamp': time_stamps,
|
||||||
|
'nonceStr': random_str,
|
||||||
|
'package': 'prepay_id=' + response.json()['prepay_id'],
|
||||||
|
'paySign': get_sign(
|
||||||
|
f"{settings.WX_APP_ID}\n{time_stamps}\n{random_str}\n{'prepay_id=' + response.json()['prepay_id']}\n"),
|
||||||
|
}
|
||||||
|
print(f'签名有效: {res}')
|
||||||
|
logger.info(f'签名有效: {res}')
|
||||||
|
return HttpResponse(json.dumps(res), content_type='application/json')
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
logger.info(f'签名无效')
|
||||||
|
print('签名无效')
|
||||||
|
return HttpResponse(json.dumps({'msg': "支付失败"}), content_type='application/json')
|
||||||
|
|
||||||
|
def wx_resource(nonce, ciphertext, associated_data):
|
||||||
|
key_bytes = str.encode(settings.WX_API_KEY) # APIv3_key(商户平台设置)
|
||||||
|
nonce_bytes = str.encode(nonce)
|
||||||
|
ad_bytes = str.encode(associated_data)
|
||||||
|
data = base64.b64decode(ciphertext)
|
||||||
|
aesgcm = AESGCM(key_bytes)
|
||||||
|
plaintext = aesgcm.decrypt(nonce_bytes, data, ad_bytes)
|
||||||
|
plaintext_str = bytes.decode(plaintext)
|
||||||
|
|
||||||
|
return eval(plaintext_str)
|
||||||
|
|
||||||
|
def update_user_membership_or_quota(openid, membership_type):
|
||||||
|
current_time = int(time.time()) # 获取当前时间戳
|
||||||
|
|
||||||
|
try:
|
||||||
|
user = User.objects.get(openid=openid)
|
||||||
|
|
||||||
|
if membership_type.is_quota:
|
||||||
|
# 如果是额度充值,更新用户的金币数量
|
||||||
|
user.coins += membership_type.coins
|
||||||
|
logger.info(f'用户 {user.id} (openid: {openid}) 充值额度卡,增加金币 {membership_type.coins}')
|
||||||
|
else:
|
||||||
|
# 如果是会员充值,更新会员状态和时间
|
||||||
|
user.is_member = True
|
||||||
|
|
||||||
|
if user.member_start_time is None: # 如果用户是第一次开会员
|
||||||
|
user.member_start_time = current_time
|
||||||
|
logger.info(f'第一次开会员:{current_time} -- {current_time + membership_type.duration_days * 24 * 3600}')
|
||||||
|
elif user.member_end_time is None or user.member_end_time < current_time: # 如果会员已经过期
|
||||||
|
logger.info(f'过期:{current_time} -- {current_time + membership_type.duration_days * 24 * 3600}')
|
||||||
|
user.member_start_time = current_time
|
||||||
|
|
||||||
|
# 计算会员到期时间
|
||||||
|
if user.member_end_time is None or user.member_end_time < current_time:
|
||||||
|
logger.info(f'第一次开会员2:{user.member_end_time} -- {current_time + membership_type.duration_days * 24 * 3600}')
|
||||||
|
user.member_end_time = current_time + membership_type.duration_days * 24 * 3600 # 将天数转换为秒
|
||||||
|
else: # 如果会员尚未过期,则在现有会员结束时间上添加续费的天数
|
||||||
|
logger.info(f'续费:{user.member_end_time} -- {user.member_end_time + membership_type.duration_days * 24 * 3600}')
|
||||||
|
user.member_end_time += membership_type.duration_days * 24 * 3600
|
||||||
|
|
||||||
|
# 会员充值赠送金币
|
||||||
|
user.coins += membership_type.coins
|
||||||
|
|
||||||
|
user.save()
|
||||||
|
return True
|
||||||
|
except User.DoesNotExist:
|
||||||
|
# 处理用户不存在的情况
|
||||||
|
logger.error(f'用户 {openid} 不存在')
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f'更新会员或额度充值时出错: {str(e)}')
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
@csrf_exempt
|
||||||
|
def wx_pay_notify(request):
|
||||||
|
webData = json.loads(request.body)
|
||||||
|
logger.info(f'回调返回信息:{webData}')
|
||||||
|
ciphertext = webData['resource']['ciphertext']
|
||||||
|
nonce = webData['resource']['nonce']
|
||||||
|
associated_data = webData['resource']['associated_data']
|
||||||
|
try:
|
||||||
|
callback_data = wx_resource(nonce, ciphertext, associated_data)
|
||||||
|
logger.info(f'回调返回信息:{callback_data}')
|
||||||
|
mchid = callback_data.get('mchid')
|
||||||
|
appid = callback_data.get('appid')
|
||||||
|
out_trade_no = callback_data.get('out_trade_no')
|
||||||
|
transaction_id = callback_data.get('transaction_id')
|
||||||
|
trade_state = callback_data.get('trade_state')
|
||||||
|
openid = callback_data.get('payer').get('openid')
|
||||||
|
attach = callback_data.get('attach')
|
||||||
|
attach = attach.replace("'", "\"")
|
||||||
|
attach = json.loads(attach)
|
||||||
|
type = attach.get('type')
|
||||||
|
transaction_type = attach.get('transaction_type')
|
||||||
|
success_time_str = callback_data.get('success_time')
|
||||||
|
amount_total = callback_data.get('amount').get('total')
|
||||||
|
success_time = datetime.strptime(success_time_str, "%Y-%m-%dT%H:%M:%S%z")
|
||||||
|
logger.info("交易结果:")
|
||||||
|
logger.info(f"商户号:{mchid}")
|
||||||
|
logger.info(f"AppID:{appid}")
|
||||||
|
logger.info(f"商户订单号:{out_trade_no}")
|
||||||
|
logger.info(f"微信订单号:{transaction_id}")
|
||||||
|
logger.info(f"交易状态:{trade_state}")
|
||||||
|
logger.info(f"用户OpenID:{openid}")
|
||||||
|
logger.info(f"type:{type}")
|
||||||
|
logger.info(f"transaction_type:{transaction_type}")
|
||||||
|
logger.info(f"成功时间:{success_time}")
|
||||||
|
logger.info(f"金额(分):{amount_total}")
|
||||||
|
transaction_log = TransactionLog.objects.get(transaction_no=out_trade_no)
|
||||||
|
membership_type = MembershipType.objects.get(type=type) # 获取会员类型对象
|
||||||
|
|
||||||
|
# 验证订单信息一致性
|
||||||
|
if (transaction_log.user_openid == openid) and (transaction_log.transaction_amount * 100 == amount_total) and (
|
||||||
|
membership_type.price * 100 == amount_total):
|
||||||
|
# 更新交易状态为完成
|
||||||
|
transaction_log.transaction_status = 'completed'
|
||||||
|
transaction_log.save()
|
||||||
|
logger.info(f'交易记录更新成功:{transaction_log}')
|
||||||
|
|
||||||
|
# 更新用户的会员或额度
|
||||||
|
success = update_user_membership_or_quota(openid, membership_type)
|
||||||
|
|
||||||
|
# 给邀请人返利
|
||||||
|
user = User.objects.get(openid=openid)
|
||||||
|
if user.inviter_nickname:
|
||||||
|
inviter = User.objects.filter(nickname=user.inviter_nickname).first()
|
||||||
|
if inviter:
|
||||||
|
rebate_amount = round(Decimal(amount_total) * Decimal(0.35) / Decimal(100), 2) # 保留两位小数,单位为元
|
||||||
|
inviter.balance += rebate_amount
|
||||||
|
inviter.save()
|
||||||
|
logger.info(f'邀请人{inviter.nickname}返利{rebate_amount}元成功')
|
||||||
|
return HttpResponse(json.dumps({"code": "SUCCESS", "message": "成功"}))
|
||||||
|
else:
|
||||||
|
logger.warning('回调数据与订单记录不一致')
|
||||||
|
return HttpResponse(json.dumps({"code": "FAIL", "message": "回调数据与订单记录不一致"}))
|
||||||
|
except TransactionLog.DoesNotExist:
|
||||||
|
logger.error(f'交易记录不存在')
|
||||||
|
return HttpResponse(json.dumps({"code": "FAIL", "message": "交易记录不存在"}))
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f'处理回调时出错:{str(e)}')
|
||||||
|
return HttpResponse(json.dumps({"code": "FAIL", "message": "处理回调时出错"}))
|
||||||
|
|
25
MyApi/证书/apiclient_cert.pem
Normal file
25
MyApi/证书/apiclient_cert.pem
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIENzCCAx+gAwIBAgIUZts1+Dbv1MvsZvOBXSg6KJIxAyQwDQYJKoZIhvcNAQEL
|
||||||
|
BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT
|
||||||
|
FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg
|
||||||
|
Q0EwHhcNMjQwNDIwMDk0OTU5WhcNMjkwNDE5MDk0OTU5WjCBkDETMBEGA1UEAwwK
|
||||||
|
MTY3NDAxMjkzMjEbMBkGA1UECgwS5b6u5L+h5ZWG5oi357O757ufMTwwOgYDVQQL
|
||||||
|
DDPnpo/lt57luILpvJPmpbzljLrlpKnov5zkuablupfvvIjkuKrkvZPlt6XllYbm
|
||||||
|
iLfvvIkxCzAJBgNVBAYTAkNOMREwDwYDVQQHDAhTaGVuWmhlbjCCASIwDQYJKoZI
|
||||||
|
hvcNAQEBBQADggEPADCCAQoCggEBAK3ueF4X+IVGCSajTvZycizthPIoxspbCTHu
|
||||||
|
5XzLRDAiNL92w6sJIb488MKIhDJtdxw4RJjtf6W9qjv1D2wI2EldehTJfJkyTFCW
|
||||||
|
xQlPh615dMDNayg/L4w6cIZLcjvbIl/RGAoZ7ldr5Z6+O+tFpejg9Sh4V7pU0Vhl
|
||||||
|
ZlHolaCqVbuqcRIlybF9BpPpWJRu0jPFrMG5rDpzE6HnbNiAKpdc+8mZriHgeJI7
|
||||||
|
unfd3zQj/Qy7j3cuASjhlXZQdA/IZvy/El7n46VDk4hZCfNHu5h1CJ/S89h+BSlS
|
||||||
|
WBvCiZZAiKZS7ELjlHsA7bZf50LYokPyvDgx628dVszsVy0+GsECAwEAAaOBuTCB
|
||||||
|
tjAJBgNVHRMEAjAAMAsGA1UdDwQEAwID+DCBmwYDVR0fBIGTMIGQMIGNoIGKoIGH
|
||||||
|
hoGEaHR0cDovL2V2Y2EuaXRydXMuY29tLmNuL3B1YmxpYy9pdHJ1c2NybD9DQT0x
|
||||||
|
QkQ0MjIwRTUwREJDMDRCMDZBRDM5NzU0OTg0NkMwMUMzRThFQkQyJnNnPUhBQ0M0
|
||||||
|
NzFCNjU0MjJFMTJCMjdBOUQzM0E4N0FEMUNERjU5MjZFMTQwMzcxMA0GCSqGSIb3
|
||||||
|
DQEBCwUAA4IBAQCvG+nV62rfX5kVxO8Uc8AL9UbpXIWi+6qL0+tBfR5YQWsYDY9n
|
||||||
|
wMLoYYAwcQ+xTurQcsUA4WOFrhgkrelcyWu6jhBKhVBLEed2jL5pKLwt+CvCvTYk
|
||||||
|
4Q1MW6f7P1Gs+/OMAleu55YGMCe1PqvtTypljT+mx8rHXtgLSNDtCO65jXRbbehs
|
||||||
|
kxQalLrzGEeUaLk4UknDZJInALNZHeqT9k8NXBZrlIlM9V4oPb+Ai/FCXkO/kqHS
|
||||||
|
b0OORRBgi74sLogXyQSma4uHhyNDGrmGSTBHu3If6l9QhEHSZjTQeXIK7wbBGgAw
|
||||||
|
LDd8xm4cEWxi6xyssl0s/7qs+pG/eG47MP9J
|
||||||
|
-----END CERTIFICATE-----
|
28
MyApi/证书/apiclient_key.pem
Normal file
28
MyApi/证书/apiclient_key.pem
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCt7nheF/iFRgkm
|
||||||
|
o072cnIs7YTyKMbKWwkx7uV8y0QwIjS/dsOrCSG+PPDCiIQybXccOESY7X+lvao7
|
||||||
|
9Q9sCNhJXXoUyXyZMkxQlsUJT4eteXTAzWsoPy+MOnCGS3I72yJf0RgKGe5Xa+We
|
||||||
|
vjvrRaXo4PUoeFe6VNFYZWZR6JWgqlW7qnESJcmxfQaT6ViUbtIzxazBuaw6cxOh
|
||||||
|
52zYgCqXXPvJma4h4HiSO7p33d80I/0Mu493LgEo4ZV2UHQPyGb8vxJe5+OlQ5OI
|
||||||
|
WQnzR7uYdQif0vPYfgUpUlgbwomWQIimUuxC45R7AO22X+dC2KJD8rw4MetvHVbM
|
||||||
|
7FctPhrBAgMBAAECggEAOgEtYzPbTZbttlUAIHBKY3FSxO+UXCfACUcCgXvIYcUG
|
||||||
|
klOpLYD+H9Ny921PqQGYl3Csb9PEniGChDxVyFGqz8y8yfHn+68qhDXDwDclqFS4
|
||||||
|
+xOGiQWJddqHbEH89rk93XZ97eB3+++fxDDtCqlPizp6h+SaXSmsJy6p0Ocf/1RC
|
||||||
|
IFXUz3J3DsKAHySklq4GyovSOWd6o/KzJiKfRDwn85Jc5uCXyM7rzVfXUWx1HOsr
|
||||||
|
tPwXTAxE8opUsIP7BnAErgrGRZJc6gQunfbye5JQYxEeg1Hxkc+HbJHhbjueWMDW
|
||||||
|
cMzappDQH4NL0QeYDRfhkeV4/glQHJhDegVmyAPlgQKBgQDcttEMVlNgWzKTY7Wv
|
||||||
|
BoqUxH6ZMpvNDRehCqpIqoXYNodCKT66vYBXEfAGxIdIyDE+bEWKpSwSNX4l9Hay
|
||||||
|
d52eKv73f7Go0Uo54SwMlsNBKA3xZ1terskJlsDwtvT7N387GUyZpv6Ej/qJLIIk
|
||||||
|
WsuNgLKH8PQklaN8afnIIy2OvwKBgQDJvPqBKG8CP4CfxbKue063tkZdVjffyv+4
|
||||||
|
w/Y81lMCu5tvfebF7drUVSYqaLSXD71ramViDe++PQxksqmk/H0dLQZVJuNdunk/
|
||||||
|
w/iyjk/dI+wh2dILL7DwX6vaFiA+S6YjXWJkv148tHL0+tVotUOh9ETzBN8oO5R2
|
||||||
|
2Ik5zHg2fwKBgGD7X74ZDIfRatbCwGmI6UnXUX2FdFpUf2Z+5jYJ38gSpbpXnz20
|
||||||
|
2PjpY9vFbWB4vtKcBq6WR1g08xpAYgunbbW7VM9x56GzXPczzwQ1FtTBgebqMIbZ
|
||||||
|
LHMvL9ZVvi3Iw/o28qatbzbWgRLXYNQSpVCnFUuQf+a3bd/UPTJI6tLHAoGBAKbi
|
||||||
|
I8AQeLQ9X+PLoWJhMt8ac97mYdZJkX2A7gOpsIXEOHLkjbEScEHSJn5Sm4GTTbi8
|
||||||
|
DR4uNTMEQWpTua54B+/IvUJXpyJ0DbVIIkVjIIHpI39lNzJ1w0M1gW4pe4bNAaSj
|
||||||
|
ra919zj0dVxe24eA22/wJ9F5KDmZ/9jIi8w6bv91AoGAefCgs6tA9cA3dag9lWP+
|
||||||
|
8+S4rpXSR7qCTmSfxCvWkq13vh4jlIFhChBpu+re91n9vyIUWgrlCcJUwft2WBqF
|
||||||
|
60Huafsy3hQTuclm15sYtEkeuh9OKuzUKJ3mfTXYef6F8RKOOseuFtXnH5xmZEJl
|
||||||
|
AifcseiIEMcIlBIlIjRZlSs=
|
||||||
|
-----END PRIVATE KEY-----
|
Loading…
Reference in New Issue
Block a user