This commit is contained in:
doudou201703
2026-08-02 14:07:04 +08:00
commit 046fac6dc1
339 changed files with 19435 additions and 0 deletions
+141
View File
@@ -0,0 +1,141 @@
import os
import django
from sixGuoDjango.settings import BASE_DIR
os.environ.setdefault(key="DJANGO_SETTINGS_MODULE", value=f"{BASE_DIR.name}.settings")
django.setup()
# timer_manager.py
import threading
import secrets
from datetime import datetime, timedelta
from zoneinfo import ZoneInfo
from apscheduler.jobstores.base import JobLookupError
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.jobstores.memory import MemoryJobStore
from apscheduler.executors.pool import ThreadPoolExecutor
from django_定时器.幸运拼团数据库 import lucky拼团
from wechat_数据库.雪花算法.雪花算法工具 import generator
class TwoHourWindowManager:
"""
团购任务
"""
_instance = None
_lock = threading.Lock()
def __new__(cls):
if cls._instance is None:
with cls._lock:
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance._initialized = False
return cls._instance
def __init__(self):
if self._initialized:
return
self._initialized = True
self.generate_str_id = None
# 共享状态
self.items = []
self.current_cleanup_job_id: str | None = None
self.state_lock = threading.RLock() # 保护 items 和 job_id
# 在这里,需要对数据库的数据进行处理
self.init初始化数据库()
# 初始化调度器(单例)
jobstores = {'default': MemoryJobStore()}
executors = {'default': ThreadPoolExecutor(max_workers=2)}
self.scheduler = BackgroundScheduler(
jobstores=jobstores, executors=executors, timezone='Asia/Shanghai', # 替换为你的 settings.TIME_ZONE
)
self.scheduler.start()
# 启动初始清理任务(2小时后)
self._schedule_cleanup()
def 开团重置(self):
"""APScheduler 调用的清理函数"""
with self.state_lock:
print("开团重置任务触发了啦")
print(f"[{datetime.now()}] ⏰⏰⏰⏰⏰⏰⏰⏰⏰⏰⏰ 定时器到期,清空列表(当前 {len(self.items)} 项)")
self.items.clear()
self.current_cleanup_job_id = None # 任务已执行,ID 失效
self.init初始化数据库()
self._schedule_cleanup() # 开启新的任务
print("开启新的任务")
def init初始化数据库(self):
print("初始化数据库执行中")
generate_str = generator.generate_str()
创建对象 = lucky拼团.objects.create(idKey=generate_str, lucky拼团详细=[])
self.generate_str_id = 创建对象.idKey
def _schedule_cleanup(self):
"""安排一个新的 2 小时后清理任务"""
with self.state_lock:
# 取消旧任务(如果存在且未执行)
if self.current_cleanup_job_id is not None:
print("存在旧的任务的吗?")
try:
self.scheduler.remove_job(self.current_cleanup_job_id)
print("存在旧的任务的吗?")
pass
except JobLookupError:
pass # 可能已被执行或不存在
print("打印报错", JobLookupError)
# 添加新任务
job = self.scheduler.add_job(
func=self.开团重置,
trigger='date',
run_date=datetime.now(tz=ZoneInfo("Asia/Shanghai")) + timedelta(seconds=7200),
id=f"cleanup_{secrets.token_hex(8)}",
name="Auto Cleanup",
)
self.current_cleanup_job_id = job.id
print(f"[{datetime.now()}] 🕒 新建清理任务,2小时后执行,ID: {job.id}")
def add_item(self, item):
"""外部调用:安全地添加元素"""
with self.state_lock:
self.items.append(item)
print(f"[{datetime.now()}] ➕ 添加: {item}, 当前列表: {self.items}")
if len(self.items) >= 4:
print(f"[{datetime.now()}] 🧹 列表已满,立即清空并重置2小时计时器")
self.items.clear()
self._schedule_cleanup() # 重置计时器
def get_items(self):
"""
:return:
:rtype:
"""
with self.state_lock:
return self.items.copy()
def shutdown(self):
"""
"""
self.scheduler.shutdown()
if __name__ == '__main__':
pass
# main.py 或 views.py
# from timer_manager import TwoHourWindowManager
# 获取单例实例
window = TwoHourWindowManager()
# 添加元素(可在多线程/Web请求中调用)
window.add_item("user_123")
window.add_item("order_456")
# 查看当前状态
print("当前列表:", window.get_items())
+58
View File
@@ -0,0 +1,58 @@
import logging
import secrets
from apscheduler.executors.pool import ThreadPoolExecutor
from apscheduler.schedulers.blocking import BlockingScheduler
from apscheduler.triggers.interval import IntervalTrigger
from django.conf import settings
# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class DingShiQi:
def __init__(self):
# ✅ 实例属性:每个实例独立
self.ding_id = secrets.token_hex(12)
# 注册 executors
executors = {
'my_executor': ThreadPoolExecutor(max_workers=1, ),
'you_executor': ThreadPoolExecutor(max_workers=1),
}
# 使用 BlockingScheduler(会阻塞)
self.scheduler = BlockingScheduler(timezone=settings.TIME_ZONE, executors=executors)
def task任务(self):
print("任务执行了啦") # 或 logger.info("任务执行了啦")
def triggerIntervalTrigger触发器(self) -> IntervalTrigger:
return IntervalTrigger(seconds=3)
def 添加任务(self):
self.scheduler.add_job(
func=self.task任务,
id=self.ding_id,
trigger=self.triggerIntervalTrigger触发器(),
name="xiaozizi",
max_instances=1,
misfire_grace_time=1,
coalesce=True,
executor="my_executor", # 在这里可以选择线程池的名称
)
def startTask(self):
logger.info("Starting scheduler...")
try:
self.scheduler.start() # ⚠️ 这里会阻塞!
except KeyboardInterrupt:
logger.info("Stopping scheduler...")
self.scheduler.shutdown()
logger.info("Scheduler shut down successfully!")
if __name__ == '__main__':
print("TIME_ZONE:", settings.TIME_ZONE)
ding = DingShiQi()
ding.添加任务()
ding.startTask() # ← 会阻塞在这里,每3秒打印一次 "任务执行了啦"
@@ -0,0 +1,192 @@
import datetime
import threading
from datetime import timedelta
from typing import cast
from zoneinfo import ZoneInfo
import jmespath
import orjson
import pydash
import requests
from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer, BaseChannelLayer
from django.forms import model_to_dict
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.routers import DefaultRouter
from rest_framework.viewsets import ViewSet
from django_celery.message.tasks import my_task
from django_定时器.团购任务 import TwoHourWindowManager
from django_定时器.幸运拼团数据库 import lucky拼团对象类型, lucky拼团
from sixGuoDjango import ResData, BaseResponse
from wechat_数据库.支付订单数据库.商品订单数据 import order订单
from wechat_数据库.洗衣液商品数据库.六果洗衣液商品 import goods商品, goods商品Choices
from wechat_数据库.用户数据库.小程序登陆用户 import weiuser
from wechat_数据库.雪花算法.雪花算法工具 import generator
from wechat_项目工具包.微信支付工具包.微信支付类 import paymentCategory, wechatPay
# window = 0
window = TwoHourWindowManager()
default_channel_layer = cast(BaseChannelLayer, get_channel_layer(alias="default"))
class 拼团业务接口ViewSets(ViewSet):
lock = threading.Lock()
def 处理积分方法(self, attach):
pass
# 将字符串转为换字典
attachData = orjson.loads(attach)
# 拿到数据
print("\n🍎 🍏 🍊 🍎 🍏 🍊 🍎 🍏 🍊 🍎 🍏 🍊 🍎 🍏 \n", attachData)
share订单分享者ID = pydash.get(obj=attachData, path="share订单分享者ID")
out_trade_no = pydash.get(obj=attachData, path="out_trade_no")
if share订单分享者ID == "六果":
# 说明没有分享都,啥也不需要干
pass
else:
print("\n🍎 🍏 🍊 有分享者,需要调用添加积分的接口 \n", )
uri = "https://xiao.sixgou.com/integral/pay_add/"
requests.post(url=uri, json={"attachData": attachData})
pass
@action(detail=False, methods=["post"], url_path="current")
def 查询当前的拼团的数据(self, request: Request):
""""""
self_user_unionid = request.data.get("self_user_unionid", None)
self_mini_openid = request.data.get("self_mini_openid", None)
拼团商品 = goods商品.objects.filter(goods商品类型=goods商品Choices.幸运拼团.value).values()
print("\n🍎 🍏 🍊 🍎 🍏 🍊 🍎 🍏 🍊 🍎 🍏 🍊 🍎 🍏 \n", 拼团商品)
查询对象 = lucky拼团.objects.get(idKey=window.generate_str_id)
时间戳 = (查询对象.dateAndTime.astimezone(tz=ZoneInfo("Asia/Shanghai")) + timedelta(seconds=7200)).timestamp()
最终时间戳 = int(时间戳) * 1000 - int(datetime.datetime.now(tz=ZoneInfo("Asia/Shanghai")).timestamp()) * 1000
resData = {"idKey": 查询对象.idKey, "lucky拼团详细": 查询对象.lucky拼团详细, "dateAndTime": 最终时间戳}
httpResData = BaseResponse(resData=ResData(listData=拼团商品, dictData=resData), ).model_dump()
return Response(data=httpResData, status=status.HTTP_200_OK)
@action(detail=False, methods=["post"], url_path="join")
def 加入拼团支付(self, request: Request):
""""""
url_pay = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi" # 发起请求的链接
self_mini_openid = request.data.get("self_mini_openid", None)
self_mini_unionid = request.data.get("self_mini_unionid", None)
order商品数据 = request.data.get("order商品数据", None)
order商品数量 = request.data.get("order商品数量", None)
order商品总价 = request.data.get("order商品总价", None)
order商品ID = request.data.get("order商品ID", None)
share订单分享者ID = request.data.get("share订单分享者ID", None) # 当前是否有上级对象
order订单类型 = request.data.get("order订单类型", None)
superior_unionid = request.data.get("superior_unionid", None) # 当前是否有上级对象
order用户配送地址 = request.data.get("order用户配送地址", None)
print("\n🍎 🍏 🍊 🍎 🍏 🍊 🍎 🍏 🍊 🍎 🍏 🍊 🍎 🍏 \n", share订单分享者ID, "\n🍎 🍏 🍊 🍎 🍏 🍊 🍎 🍏 🍊 🍎 🍏 🍊 🍎 🍏 \n")
out_trade_no = generator.generate_str()
body = {
"mchid": paymentCategory.mchid, # 【直连商户号】 直连商户号
"out_trade_no": out_trade_no, # 【商户订单号】 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一。
"appid": paymentCategory.appid, # 【公众号 或者 小程序ID】
"description": "幸运拼团", # 【商品描述】 商品描述要
"notify_url": "https://xiao.sixgou.com/lucky/update/", # 回调地址要
"amount": {
"total": 1, # 【总金额】 订单总金额,单位为分。
"currency": "CNY", # 【货币类型】 CNY:人民币,境内商户号仅支持人民币。
}, # 【支付者】 支付者信息。
"payer": {
"openid": self_mini_openid, # 【用户标识】 用户在普通商户AppID下的唯一标识。 下单前需获取到用户的OpenID,详见OpenID获取
# "openid": OPENID测试, # 【用户标识】 用户在普通商户AppID下的唯一标识。 下单前需获取到用户的OpenID,详见OpenID获取
},
"attach": orjson.dumps({"share订单分享者ID": share订单分享者ID}, option=orjson.OPT_SORT_KEYS, ).decode(
encoding="utf-8")
} # 请求的数据
wechat_data = wechatPay(payment=paymentCategory).getpay_jsapi(uri=url_pay, body_data=body, method="POST")
order订单.objects.create(order支付商家侧订单号=out_trade_no,
order商品总价=order商品总价, order商品数据=order商品数据, order商品数量=order商品数量,
order分享者unionid=share订单分享者ID,
order商品ID=order商品ID, order支付数据=body,
wai外键lucky拼团表_id=window.generate_str_id, order支付状态="支付中",
order支付者openid=self_mini_openid, order用户配送地址=order用户配送地址,
order订单类型=order订单类型
)
payData = pydash.get(obj=wechat_data, path="response")
httpResData = BaseResponse(resData=ResData(listData=[""], dictData=payData), ).model_dump()
return Response(data=httpResData, status=status.HTTP_200_OK)
@action(detail=False, methods=["post"], url_path="update")
def 更新幸运拼团的支付回调接口(self, request: Request):
""" """
with self.lock:
wechat_data支付回调数据 = wechatPay(payment=paymentCategory).callBack回调解密(headers=request.headers,
response_body=request.data)
print("\n🍎 🍏 🍊 🍎 🍏 🍊 🍎 🍏 🍊 🍎 🍏 🍊 🍎 🍏 \n", wechat_data支付回调数据, "返回的结果支付返回",
type(wechat_data支付回调数据))
out_trade_no = jmespath.search(data=wechat_data支付回调数据, expression="out_trade_no")
trade_state_desc = pydash.get(obj=wechat_data支付回调数据, path="trade_state_desc")
self_mini_openid = pydash.get(obj=wechat_data支付回调数据, path="payer.openid")
attach = pydash.get(obj=wechat_data支付回调数据, path="attach")
self.处理积分方法(attach=attach)
# 调用异步方法 : 作用 (发起订单录入接口调用)
share订单分享者ID = pydash.get(obj=orjson.loads(pydash.get(obj=wechat_data支付回调数据, path="attach")),
path="share订单分享者ID")
my_task.apply_async(kwargs={"self_mini_openid": self_mini_openid, "out_trade_no": out_trade_no,
"share订单分享者ID": share订单分享者ID}, countdown=30)
# 更新 拼团订单的 支付状态
生成物流的订单号 = generator.generate_str()
order订单.objects.filter(order支付商家侧订单号=out_trade_no).update(order支付数据=wechat_data支付回调数据,
order支付状态=trade_state_desc,
order物流状态=order订单.order订单choosies物流状态.待发货.value,
order物流订单号=生成物流的订单号
)
当前用户对象 = weiuser.objects.get(mini_openid=self_mini_openid)
参与拼团的对象 = lucky拼团对象类型(wei_openid=self_mini_openid, pay订单号=window.generate_str_id,
avatar=当前用户对象.avatar, nickName=当前用户对象.nickName)
# 查询当前的对象,然后进行数据的更新
当前拼团的对象 = lucky拼团.objects.get(idKey=window.generate_str_id)
print("🍎 🍏🍎 🍏🍎 🍏🍎 🍏🍎 长度是多少了呢", len(当前拼团的对象.lucky拼团详细))
# 需要判断当前列表的长度,如果大于等于5的话,就需要调用清团重开
if len(当前拼团的对象.lucky拼团详细) >= 4:
# 需要清掉重新
print("🍎 🍏 🍊 🍎 🍏 🍊 🍎 🍏 🍊 🍎 🍏 🍊 🍎 🍏 新开团的ID是否变更了", window.generate_str_id)
当前拼团的对象.lucky拼团详细.append(参与拼团的对象)
当前拼团的对象.lucky开团状态 = lucky拼团.choosies.已开团.value
当前拼团的对象.save()
对象 = model_to_dict(instance=当前拼团的对象, fields=None)
async_to_sync(awaitable=default_channel_layer.group_send)(group="default_group_name",
message={"lucktyData": 对象,
"type": "chat_Lucky拼团"}, )
window.开团重置()
else:
当前拼团的对象.lucky拼团详细.append(参与拼团的对象)
当前拼团的对象.save()
对象 = model_to_dict(instance=当前拼团的对象, fields=None)
async_to_sync(awaitable=default_channel_layer.group_send)(group="default_group_name",
message={"lucktyData": 对象,
"type": "chat_Lucky拼团"}, )
httpResData = BaseResponse(resData=ResData(listData=[""], dictData={}), ).model_dump()
return Response(data=httpResData, status=status.HTTP_200_OK)
@action(detail=False, methods=["get", "post", "put", "delete"], url_path="group_opening_order")
def 查询当前开团的最后一个团数据(self, request: Request):
""""""
self_user_unionid = request.data.get("self_user_unionid", None)
self_mini_openid = request.data.get("self_mini_openid", None)
拼团订单号 = window.generate_str_id
# 查询结果 = lucky拼团.objects.filter(lucky开团状态=lucky拼团.choosies.已开团.value, lucky拼团订单__order支付都ID=self_mini_openid,
# lucky拼团订单__order支付状态="支付成功"
# ).order_by("-updateTime")
查询结果 = lucky拼团.objects.filter(lucky开团状态=lucky拼团.choosies.已开团.value,
lucky拼团详细__contains=[{"wei_openid": self_mini_openid}]).order_by(
"-updateTime").first()
print(查询结果, "🍎 🍏 🍊 🍎 🍏 🍊 🍎 🍏 🍊 🍎 🍏 🍊 🍎 🍏 ")
if 查询结果 is None:
httpResData = BaseResponse(resData=ResData(listData=[""], dictData={}), ).model_dump()
return Response(data=httpResData, status=status.HTTP_200_OK)
else:
返回结果数据 = model_to_dict(instance=查询结果, fields=None)
print(返回结果数据, "🍎 🍏 🍊 🍎 🍏 🍊 🍎 🍏 🍊 🍎 🍏 🍊 🍎 🍏 ")
httpResData = BaseResponse(resData=ResData(listData=[], dictData=返回结果数据), ).model_dump()
return Response(data=httpResData, status=status.HTTP_200_OK)
# 拼团业务接口ViewSetsRouter = DefaultRouter()
# 拼团业务接口ViewSetsRouter.register(prefix="", viewset=拼团业务接口ViewSets, basename="")
+68
View File
@@ -0,0 +1,68 @@
from typing import TypedDict
from django.db import models
from django.utils.translation import gettext_lazy
from wechat_数据库.用户数据库.小程序登陆用户 import models_app_name
from wechat_数据库.雪花算法.雪花算法工具 import generator
# 定义 一 个字典的类型结构
class lucky拼团对象类型(TypedDict):
wei_openid: str
pay订单号: str
avatar: str
nickName: str
class lucky拼团(models.Model):
"""城市信息表"""
class choosies(models.TextChoices):
未开团 = "未开团", gettext_lazy("未开团")
已开团 = "已开团", gettext_lazy("已开团")
objects = models.Manager() #
idKey = models.CharField(primary_key=True, default=generator.generate_str, ) # 拼团的团订单号
lucky拼团详细 = models.JSONField(default=list)
lucky开团状态 = models.CharField(max_length=128, choices=choosies, default=choosies.未开团.value)
# lucky创建时间 = models.DateTimeField()
dateAndTime = models.DateTimeField(auto_now_add=True, db_comment="创建时间") # 数据创建时间
updateTime = models.DateTimeField(auto_now=True, db_comment="更新时间") # 更新时间(需要用 save() )
class Meta:
db_table = "lucky拼团" # 给数据表自定义一个名称;默认的会是在 应用名+类名
app_label = models_app_name
ordering = ["dateAndTime", "updateTime"]
class lucky拼团订单(models.Model):
"""城市信息表"""
objects = models.Manager() #
class choosies物流状态(models.TextChoices):
待发货 = "待发货", gettext_lazy("待发货")
已发货 = "已发货", gettext_lazy("已发货")
已签收 = "已签收", gettext_lazy("已签收")
待评价 = "待评价", gettext_lazy("待评价")
idKey = models.CharField(primary_key=True, default=generator.generate_str, )
order拼团支付订单号 = models.CharField(max_length=128, )
order支付都ID = models.CharField(max_length=128, )
wai外键lucky拼团表 = models.ForeignKey(to=lucky拼团, on_delete=models.CASCADE, )
order商品ID = models.CharField(max_length=128, )
order商品数据 = models.JSONField()
order商品总价 = models.IntegerField()
order商品数量 = models.IntegerField()
order支付数据 = models.JSONField(default=dict)
order支付状态 = models.CharField(max_length=128, default="支付中")
order用户配送地址 = models.JSONField(default=dict)
order物流状态 = models.CharField(max_length=128, blank=True, null=True, )
order物流订单号 = models.CharField(max_length=128, blank=True, null=True, )
order物流数据 = models.JSONField(default=dict)
dateAndTime = models.DateTimeField(auto_now_add=True, db_comment="创建时间") # 数据创建时间
updateTime = models.DateTimeField(auto_now=True, db_comment="更新时间") # 更新时间(需要用 save() )
class Meta:
db_table = "lucky拼团订单" # 给数据表自定义一个名称;默认的会是在 应用名+类名
app_label = models_app_name
ordering = ["dateAndTime", "updateTime"]