Your commit message

This commit is contained in:
Jane Doe 2024-06-05 05:25:27 +08:00
parent 06dfa7cd9d
commit ca32eef513
35 changed files with 3708 additions and 0 deletions

23
MyApi/API_Log.py Normal file
View 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
View 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
View 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
View File

3
MyApi/admin.py Normal file
View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
MyApi/apps.py Normal file
View 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

Binary file not shown.

BIN
MyApi/demo.mp4 Normal file

Binary file not shown.

BIN
MyApi/demo.mp4.mp3 Normal file

Binary file not shown.

View 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"],
},
),
]

View 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"],
},
),
]

View 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="生成用户"),
),
]

View File

@ -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"),
),
]

View 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",
},
),
]

View 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(),
),
]

View File

@ -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),
),
]

View 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),
),
]

View File

@ -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="备注",
),
),
]

View 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="会员卡类型",
),
),
]

View File

@ -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="金币数量"),
),
]

View File

@ -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="交易类型"),
),
]

View 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),
),
]

View File

@ -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"),
),
]

View File

@ -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),
),
]

View 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,
),
),
]

View File

351
MyApi/models.py Normal file
View 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
View 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
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

40
MyApi/urls.py Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff

310
MyApi/wxpay.py Normal file
View 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": "处理回调时出错"}))

View 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-----

View 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-----