feat(jituan): 集团多俱乐部改造 — club 隔离、财务/提现/分红/展示配置
This commit is contained in:
47
config/migrations/0004_daily_stat_club_id.py
Normal file
47
config/migrations/0004_daily_stat_club_id.py
Normal file
@@ -0,0 +1,47 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('config', '0003_remove_shangjialianjie_shangjia_li_yonghu__9f2b25_idx_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='dailyincomestat',
|
||||
name='club_id',
|
||||
field=models.CharField(db_index=True, default='xq', max_length=16, verbose_name='所属俱乐部'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='dailypayoutstat',
|
||||
name='club_id',
|
||||
field=models.CharField(db_index=True, default='xq', max_length=16, verbose_name='所属俱乐部'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='dailyincomestat',
|
||||
name='date',
|
||||
field=models.DateField(db_index=True, verbose_name='统计日期'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='dailypayoutstat',
|
||||
name='date',
|
||||
field=models.DateField(db_index=True, verbose_name='统计日期'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='dailyincomestat',
|
||||
index=models.Index(fields=['club_id', 'date'], name='idx_income_club_date'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='dailypayoutstat',
|
||||
index=models.Index(fields=['club_id', 'date'], name='idx_payout_club_date'),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name='dailyincomestat',
|
||||
constraint=models.UniqueConstraint(fields=('date', 'club_id'), name='uniq_daily_income_club_date'),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name='dailypayoutstat',
|
||||
constraint=models.UniqueConstraint(fields=('date', 'club_id'), name='uniq_daily_payout_club_date'),
|
||||
),
|
||||
]
|
||||
37
config/migrations/0005_szjilu_club_id.py
Normal file
37
config/migrations/0005_szjilu_club_id.py
Normal file
@@ -0,0 +1,37 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
def backfill_szjilu_club_id(apps, schema_editor):
|
||||
Szjilu = apps.get_model('config', 'Szjilu')
|
||||
for row in Szjilu.objects.all():
|
||||
if not getattr(row, 'club_id', None):
|
||||
row.club_id = 'xq'
|
||||
row.save(update_fields=['club_id'])
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('config', '0004_daily_stat_club_id'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='szjilu',
|
||||
name='club_id',
|
||||
field=models.CharField(
|
||||
db_index=True,
|
||||
default='xq',
|
||||
max_length=16,
|
||||
verbose_name='俱乐部ID',
|
||||
),
|
||||
),
|
||||
migrations.RunPython(backfill_szjilu_club_id, migrations.RunPython.noop),
|
||||
migrations.AddConstraint(
|
||||
model_name='szjilu',
|
||||
constraint=models.UniqueConstraint(
|
||||
fields=('club_id',),
|
||||
name='uniq_szjilu_club_id',
|
||||
),
|
||||
),
|
||||
]
|
||||
60
config/migrations/0006_display_club_id.py
Normal file
60
config/migrations/0006_display_club_id.py
Normal file
@@ -0,0 +1,60 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('config', '0005_szjilu_club_id'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='gonggao',
|
||||
name='club_id',
|
||||
field=models.CharField(db_index=True, default='xq', max_length=16, verbose_name='俱乐部ID'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='lunbo',
|
||||
name='club_id',
|
||||
field=models.CharField(db_index=True, default='xq', max_length=16, verbose_name='俱乐部ID'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='lunbo',
|
||||
name='page_key',
|
||||
field=models.CharField(
|
||||
db_index=True, default='order_pool', max_length=32,
|
||||
verbose_name='页面标识(order_pool/accept_order/merchant_home/dashou_center)',
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='popuppage',
|
||||
name='club_id',
|
||||
field=models.CharField(db_index=True, default='xq', max_length=16, verbose_name='俱乐部ID'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='tupianpeizhi',
|
||||
name='club_id',
|
||||
field=models.CharField(db_index=True, default='xq', max_length=16, verbose_name='俱乐部ID'),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='popuppage',
|
||||
unique_together=set(),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='popuppage',
|
||||
name='page_key',
|
||||
field=models.CharField(db_index=True, max_length=50, verbose_name='页面标识'),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='popuppage',
|
||||
unique_together={('club_id', 'page_key')},
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='tupianpeizhi',
|
||||
unique_together={('club_id', 'ImageType')},
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='lunbo',
|
||||
index=models.Index(fields=['club_id', 'page_key', 'ImageType'], name='lunbo_club_page_type_idx'),
|
||||
),
|
||||
]
|
||||
@@ -3,6 +3,11 @@ from gvsdsdk.model_base import QModel
|
||||
|
||||
|
||||
class Lunbo(QModel):
|
||||
club_id = models.CharField(max_length=16, default='xq', db_index=True, verbose_name='俱乐部ID')
|
||||
page_key = models.CharField(
|
||||
max_length=32, default='order_pool', db_index=True,
|
||||
verbose_name='页面标识(order_pool/accept_order/merchant_home/dashou_center)',
|
||||
)
|
||||
ImageURL = models.CharField(max_length=500, null=True, blank=True, verbose_name='轮播图片URL')
|
||||
ImageType = models.IntegerField(null=True, blank=True, verbose_name='图片类型:1:轮播2:个人中心背景')
|
||||
|
||||
@@ -12,10 +17,13 @@ class Lunbo(QModel):
|
||||
db_table = 'lunbo'
|
||||
verbose_name = '轮播'
|
||||
verbose_name_plural = '轮播'
|
||||
# 不要索引!数据量太小
|
||||
indexes = [
|
||||
models.Index(fields=['club_id', 'page_key', 'ImageType']),
|
||||
]
|
||||
|
||||
|
||||
class Gonggao(QModel):
|
||||
club_id = models.CharField(max_length=16, default='xq', db_index=True, verbose_name='俱乐部ID')
|
||||
NoticeType = models.IntegerField(null=True, blank=True, verbose_name='公告类型')
|
||||
CreateTime = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
UpdateTime = models.DateTimeField(auto_now=True, verbose_name='更改时间')
|
||||
@@ -37,6 +45,7 @@ class Qunpeizhi(QModel):
|
||||
db_table = 'qunpeizhi'
|
||||
|
||||
class Tupianpeizhi(QModel):
|
||||
club_id = models.CharField(max_length=16, default='xq', db_index=True, verbose_name='俱乐部ID')
|
||||
ImageURL = models.CharField(max_length=500, null=True, blank=True, verbose_name='配置图片URL')
|
||||
ImageType = models.PositiveIntegerField(null=True, blank=True, verbose_name='提现类型1:打手规则图片2:默认头像图片')
|
||||
CreateTime = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
@@ -46,10 +55,12 @@ class Tupianpeizhi(QModel):
|
||||
db_table = 'tupianpeizhi'
|
||||
verbose_name = '配置图片'
|
||||
verbose_name_plural = '配置图片'
|
||||
unique_together = [['club_id', 'ImageType']]
|
||||
|
||||
|
||||
class Szjilu(QModel):
|
||||
id = models.AutoField(primary_key=True, verbose_name='记录ID')
|
||||
club_id = models.CharField(max_length=16, default='xq', db_index=True, verbose_name='俱乐部ID')
|
||||
TotalIncome = models.DecimalField(max_digits=10, decimal_places=2, default=0.00, verbose_name='总收益')
|
||||
TotalFlow = models.DecimalField(max_digits=10, decimal_places=2, default=0.00, verbose_name='总流水')
|
||||
TotalExpense = models.DecimalField(max_digits=10, decimal_places=2, default=0.00, verbose_name='总支出')
|
||||
@@ -61,6 +72,9 @@ class Szjilu(QModel):
|
||||
db_table = 'szjilu'
|
||||
verbose_name = '收支记录'
|
||||
verbose_name_plural = '收支记录'
|
||||
constraints = [
|
||||
models.UniqueConstraint(fields=['club_id'], name='uniq_szjilu_club_id'),
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -277,7 +291,10 @@ class DailyIncomeStat(QModel):
|
||||
记录平台每天通过微信支付获得的总收入和总笔数
|
||||
"""
|
||||
# 日期相关字段(便于快速筛选和分组)
|
||||
date = models.DateField(verbose_name='统计日期', db_index=True, unique=True)
|
||||
date = models.DateField(verbose_name='统计日期', db_index=True)
|
||||
club_id = models.CharField(
|
||||
max_length=16, default='xq', db_index=True, verbose_name='所属俱乐部',
|
||||
)
|
||||
year = models.PositiveSmallIntegerField(verbose_name='年份')
|
||||
month = models.PositiveSmallIntegerField(verbose_name='月份')
|
||||
day = models.PositiveSmallIntegerField(verbose_name='日期')
|
||||
@@ -297,6 +314,10 @@ class DailyIncomeStat(QModel):
|
||||
indexes = [
|
||||
models.Index(fields=['date']),
|
||||
models.Index(fields=['year', 'month', 'day']),
|
||||
models.Index(fields=['club_id', 'date']),
|
||||
]
|
||||
constraints = [
|
||||
models.UniqueConstraint(fields=['date', 'club_id'], name='uniq_daily_income_club_date'),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
@@ -310,7 +331,10 @@ class DailyPayoutStat(QModel):
|
||||
每日出款统计表
|
||||
记录平台每天通过提现/结算等方式的出款总额和总笔数
|
||||
"""
|
||||
date = models.DateField(verbose_name='统计日期', db_index=True, unique=True)
|
||||
date = models.DateField(verbose_name='统计日期', db_index=True)
|
||||
club_id = models.CharField(
|
||||
max_length=16, default='xq', db_index=True, verbose_name='所属俱乐部',
|
||||
)
|
||||
year = models.PositiveSmallIntegerField(verbose_name='年份')
|
||||
month = models.PositiveSmallIntegerField(verbose_name='月份')
|
||||
day = models.PositiveSmallIntegerField(verbose_name='日期')
|
||||
@@ -328,6 +352,10 @@ class DailyPayoutStat(QModel):
|
||||
indexes = [
|
||||
models.Index(fields=['date']),
|
||||
models.Index(fields=['year', 'month', 'day']),
|
||||
models.Index(fields=['club_id', 'date']),
|
||||
]
|
||||
constraints = [
|
||||
models.UniqueConstraint(fields=['date', 'club_id'], name='uniq_daily_payout_club_date'),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
@@ -337,7 +365,8 @@ class DailyPayoutStat(QModel):
|
||||
|
||||
|
||||
class PopupPage(QModel):
|
||||
page_key = models.CharField(max_length=50, unique=True, db_index=True, verbose_name='页面标识')
|
||||
club_id = models.CharField(max_length=16, default='xq', db_index=True, verbose_name='俱乐部ID')
|
||||
page_key = models.CharField(max_length=50, db_index=True, verbose_name='页面标识')
|
||||
name = models.CharField(max_length=100, verbose_name='页面名称')
|
||||
description = models.TextField(blank=True, verbose_name='描述')
|
||||
is_active = models.BooleanField(default=True, verbose_name='是否启用')
|
||||
@@ -349,6 +378,7 @@ class PopupPage(QModel):
|
||||
db_table = 'popup_page'
|
||||
verbose_name = '弹窗页面'
|
||||
verbose_name_plural = '弹窗页面'
|
||||
unique_together = [['club_id', 'page_key']]
|
||||
|
||||
def __str__(self):
|
||||
return f'{self.name} ({self.page_key})'
|
||||
|
||||
@@ -1,76 +1,34 @@
|
||||
"""
|
||||
阿龙电竞 - 收支记录定时任务
|
||||
处理收支记录表的每日清零
|
||||
固定使用ID=1的记录
|
||||
收支记录定时任务:每日清零各俱乐部今日流水/今日支出。
|
||||
"""
|
||||
from celery import shared_task
|
||||
from django.db import transaction
|
||||
from django.utils import timezone
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
import logging
|
||||
|
||||
# 导入模型
|
||||
from config.models import Szjilu
|
||||
from jituan.services.szjilu_accounting import reset_all_daily_szjilu
|
||||
from utils.celery_utils import log_task_execution, rollback_on_failure
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@shared_task
|
||||
@rollback_on_failure("收支记录每日清零")
|
||||
def daily_sz_reset_task():
|
||||
"""
|
||||
收支记录每日清零 - 每天凌晨0点5分执行
|
||||
清零收支记录表的今日相关字段,固定使用ID=1的记录
|
||||
"""
|
||||
"""收支记录每日清零 - 每天凌晨0点5分执行。"""
|
||||
try:
|
||||
with transaction.atomic():
|
||||
# 获取今天的日期
|
||||
today = datetime.now().strftime('%Y-%m-%d')
|
||||
today = datetime.now().strftime('%Y-%m-%d')
|
||||
logger.info(f"开始执行收支记录清零任务,日期: {today}")
|
||||
|
||||
logger.info(f"开始执行收支记录清零任务,日期: {today}")
|
||||
updated = reset_all_daily_szjilu()
|
||||
result_msg = f"收支记录清零完成 - 日期: {today}, 俱乐部记录数: {updated}"
|
||||
log_task_execution("收支记录每日清零", True, result_msg)
|
||||
|
||||
# 查询ID=1的收支记录,如果不存在则创建
|
||||
try:
|
||||
szjilu = Szjilu.objects.get(id=1)
|
||||
logger.info(f"找到ID=1的收支记录")
|
||||
except Szjilu.DoesNotExist:
|
||||
# 创建ID=1的初始记录
|
||||
szjilu = Szjilu.objects.create(
|
||||
id=1,
|
||||
TotalIncome=Decimal('0.00'),
|
||||
TotalFlow=Decimal('0.00'),
|
||||
TotalExpense=Decimal('0.00'),
|
||||
DailyExpense=Decimal('0.00'),
|
||||
DailyFlow=Decimal('0.00')
|
||||
)
|
||||
logger.info(f"创建ID=1的收支记录")
|
||||
|
||||
# 记录清零前的值(用于日志)
|
||||
old_jrzc = szjilu.DailyExpense
|
||||
old_jrls = szjilu.DailyFlow
|
||||
|
||||
# 清零今日字段
|
||||
szjilu.DailyExpense = Decimal('0.00')
|
||||
szjilu.DailyFlow = Decimal('0.00')
|
||||
szjilu.save(update_fields=['DailyExpense', 'DailyFlow', 'UpdateTime'])
|
||||
|
||||
logger.info(f"收支记录清零完成,今日支出原值: {old_jrzc},今日流水原值: {old_jrls}")
|
||||
|
||||
result_msg = f"收支记录清零完成 - 日期: {today}, 记录ID: {szjilu.id}"
|
||||
|
||||
log_task_execution("收支记录每日清零", True, result_msg)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"date": today,
|
||||
"record_id": szjilu.id,
|
||||
"jrzc_cleared": True,
|
||||
"jrls_cleared": True,
|
||||
"old_jrzc": str(old_jrzc),
|
||||
"old_jrls": str(old_jrls),
|
||||
"message": result_msg
|
||||
}
|
||||
return {
|
||||
"success": True,
|
||||
"date": today,
|
||||
"clubs_updated": updated,
|
||||
"message": result_msg,
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"收支记录每日清零失败: {str(e)}"
|
||||
@@ -81,5 +39,5 @@ def daily_sz_reset_task():
|
||||
"success": False,
|
||||
"error": str(e),
|
||||
"date": datetime.now().strftime('%Y-%m-%d'),
|
||||
"message": error_msg
|
||||
"message": error_msg,
|
||||
}
|
||||
@@ -132,20 +132,17 @@ class ShangpinGonggaoView(APIView):
|
||||
"""
|
||||
try:
|
||||
logger.info("收到商品公告和轮播图请求")
|
||||
# 1. 查询公告(类型为1)
|
||||
gonggao_obj = Gonggao.query.filter(NoticeType=1).first()
|
||||
# 获取公告内容,如果没有则返回空字符串
|
||||
gonggao_content = gonggao_obj.Content if gonggao_obj else ""
|
||||
# 2. 查询轮播图(类型为1,按显示顺序排序)
|
||||
lunbo_queryset = Lunbo.query.filter(ImageType=1)
|
||||
# 提取轮播图URL列表
|
||||
lunbo_urls = [lunbo.ImageURL for lunbo in lunbo_queryset]
|
||||
# 3. 构建返回数据
|
||||
from jituan.services.display_config import get_gonggao_content, get_lunbo_urls, normalize_page_key
|
||||
|
||||
page_key = normalize_page_key(request.data.get('page_key'), image_type=1)
|
||||
gonggao_content = get_gonggao_content(request, notice_type=1)
|
||||
lunbo_urls = get_lunbo_urls(request, page_key=page_key, image_type=1)
|
||||
response_data = {
|
||||
"shangpingonggao": gonggao_content,
|
||||
"shangpinlunbo": lunbo_urls
|
||||
"shangpinlunbo": lunbo_urls,
|
||||
"page_key": page_key,
|
||||
}
|
||||
logger.info(f"返回数据:公告长度{len(gonggao_content)},轮播图数量{len(lunbo_urls)}")
|
||||
logger.info(f"返回数据:公告长度{len(gonggao_content)},轮播图数量{len(lunbo_urls)} page_key={page_key}")
|
||||
# 4. 返回响应
|
||||
return Response(response_data, status=status.HTTP_200_OK)
|
||||
except Exception as e:
|
||||
@@ -3158,9 +3155,13 @@ class PopupConfigView(APIView):
|
||||
'msg': '缺少参数 pageKey'
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# 3. 查询页面配置
|
||||
# 3. 查询页面配置(按俱乐部)
|
||||
from jituan.services.display_config import get_gonggao_content
|
||||
from jituan.services.club_context import resolve_club_id_from_request
|
||||
|
||||
club_id = resolve_club_id_from_request(request)
|
||||
try:
|
||||
popup_page = PopupPage.query.get(page_key=page_key, is_active=True)
|
||||
popup_page = PopupPage.query.get(club_id=club_id, page_key=page_key, is_active=True)
|
||||
except PopupPage.DoesNotExist:
|
||||
# 该页面没有配置弹窗,返回空
|
||||
return Response({
|
||||
@@ -3206,15 +3207,21 @@ class GetWithdrawModeView(APIView):
|
||||
permission_classes = [IsAuthenticated] # JWT 认证
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
# 尝试获取 ID=1 的记录
|
||||
config = WithdrawConfig.query.filter(id=1).first()
|
||||
# 如果存在则取实际值,否则默认 2(手动提现)
|
||||
mode = config.mode if config else 2
|
||||
from jituan.services.club_context import resolve_club_id_from_request
|
||||
from jituan.services.club_user import get_user_club_id
|
||||
from jituan.services.withdraw_config import get_withdraw_mode
|
||||
|
||||
user = getattr(request, 'user', None)
|
||||
club_id = get_user_club_id(user) if user and getattr(user, 'is_authenticated', False) else None
|
||||
if not club_id:
|
||||
club_id = resolve_club_id_from_request(request)
|
||||
mode = get_withdraw_mode(club_id)
|
||||
|
||||
return Response({
|
||||
'code': 0,
|
||||
'data': {
|
||||
'mode': mode
|
||||
'mode': mode,
|
||||
'club_id': club_id,
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user