feat(jituan): 集团多俱乐部改造 — club 隔离、财务/提现/分红/展示配置

This commit is contained in:
XingQue
2026-06-24 05:02:18 +08:00
parent ce9b09f096
commit dcc4936428
82 changed files with 5748 additions and 924 deletions

View File

@@ -51,9 +51,18 @@ from users.models import (
from users.business_models import User
from orders.models import CommissionRate
from config.models import Szjilu, Gonggao, Lunbo
from config.models import Gonggao, Lunbo
from orders.models import Penalty # 用于罚款类型
from jituan.services.club_config import (
get_commission_rate,
get_commission_rate_object,
get_huiyuan_price,
get_huiyuan_fenchong,
)
from jituan.services.club_context import resolve_club_id_from_request
from jituan.services.club_write import resolve_club_id_for_write
from jituan.services.club_penalty import resolve_gsfenhong_club_id
import traceback
@@ -258,6 +267,7 @@ class DashouHuiyuanList(APIView):
}, status=status.HTTP_400_BAD_REQUEST)
# 3. 查询所有会员数据ORM查询确保性能
club_id = resolve_club_id_from_request(request)
huiyuan_queryset = Huiyuan.query.all().only(
'huiyuan_id', # 会员ID
'jieshao', # 会员名字注意jieshao字段实际存储的是会员名字
@@ -271,7 +281,7 @@ class DashouHuiyuanList(APIView):
huiyuan_list.append({
'id': huiyuan.huiyuan_id, # 会员ID对应前端id
'mingzi': huiyuan.jieshao, # 会员名字对应前端mingzi
'jiage': float(huiyuan.jiage), # 会员价格(转为浮点数)
'jiage': float(get_huiyuan_price(club_id, huiyuan.huiyuan_id, huiyuan)),
'jieshao': huiyuan.jtjieshao # 会员介绍对应前端jieshao
})
@@ -357,7 +367,8 @@ class YajinGoumai(APIView):
yonghuid=request.user.UserUID,
jine=jine,
leixing=2, # 充值类型2=押金
shuoming=settings.YAJIN_PAY_DESCRIPTION
shuoming=settings.YAJIN_PAY_DESCRIPTION,
club_id=resolve_club_id_for_write(request, request.user),
)
logger.info(f"创建押金充值订单: 用户{request.user.UserUID}, 订单{dingdanid}, 金额{jine}")
@@ -397,9 +408,12 @@ class YajinGoumai(APIView):
def generate_wechat_pay_params(self, dingdanid, jine, openid, pay_type):
"""生成微信小程序支付参数"""
# 获取配置
APPID = settings.WEIXIN_APPID
MCHID = settings.WEIXIN_MCHID
KEY = settings.WEIXIN_SHANGHUMIYAO
from jituan.services.club_write import resolve_club_id_for_write
from jituan.services.wechat_pay import get_wechat_v2_config
pay_cfg = get_wechat_v2_config(resolve_club_id_for_write(self.request, request.user))
APPID = pay_cfg['appid']
MCHID = pay_cfg['mch_id']
KEY = pay_cfg['key']
# 根据支付类型获取对应的配置
if pay_type == 'yajin':
@@ -566,13 +580,16 @@ class YajinHuitiao(View):
logger.info(f"回调数据解析: 订单{out_trade_no}, 状态{return_code}/{result_code}, 金额{total_fee}")
from jituan.services.wechat_pay import club_id_from_czjilu, verify_wechat_v2_signature
notify_club = club_id_from_czjilu(out_trade_no)
# 2. 验证基本返回状态
if return_code != 'SUCCESS' or result_code != 'SUCCESS':
logger.warning(f"微信支付回调状态异常: {return_code}/{result_code}")
return self.wechat_response('FAIL', '支付失败')
# 3. 验证签名(确保是微信发来的)
if not self.verify_signature(request_body):
if not verify_wechat_v2_signature(request_body, notify_club):
logger.error(f"签名验证失败: {out_trade_no}")
return self.wechat_response('FAIL', '签名验证失败')
@@ -650,6 +667,11 @@ class YajinHuitiao(View):
avatar=user_main.Avatar,
nicheng=dashou.nicheng or '未知打手',
fenhong_leixing=2,
club_id=resolve_gsfenhong_club_id(
dingdan_id=order.dingdan_id,
dashouid=order.yonghuid,
czjilu=order,
),
)
logger.info(
f"押金分红成功: 订单{order.dingdan_id}, 管事{guanshi_id}, "
@@ -684,25 +706,9 @@ class YajinHuitiao(View):
jine_decimal = Decimal(str(order.jine)) if not isinstance(order.jine, Decimal) else order.jine
# 查询或创建ID=1的收支记录
update_daily_income(jine_decimal)
szjilu, created = Szjilu.query.get_or_create(
id=1,
defaults={
'TotalIncome': Decimal('0.00'),
'TotalFlow': Decimal('0.00'),
'TotalExpense': Decimal('0.00'),
'DailyExpense': Decimal('0.00'),
'DailyFlow': Decimal('0.00')
}
)
# 更新三个字段
szjilu.TotalIncome += jine_decimal # 总收益
szjilu.TotalFlow += jine_decimal # 总流水
szjilu.DailyFlow += jine_decimal # 今日流水
szjilu.save()
update_daily_income(jine_decimal, getattr(order, 'club_id', None))
from jituan.services.szjilu_accounting import apply_szjilu_income
apply_szjilu_income(jine_decimal, getattr(order, 'club_id', None))
logger.info(f"收支记录更新成功: 订单{order.dingdan_id}, 金额{jine_decimal}")
@@ -953,7 +959,8 @@ class JifenBuchong(APIView):
yonghuid=request.user.UserUID,
jine=jine,
leixing=3, # 充值类型3=积分
shuoming=settings.JIFEN_PAY_DESCRIPTION
shuoming=settings.JIFEN_PAY_DESCRIPTION,
club_id=resolve_club_id_for_write(request, request.user),
)
logger.info(f"创建积分充值订单: 用户{request.user.UserUID}, 订单{dingdanid}, 金额{jine}")
@@ -993,9 +1000,12 @@ class JifenBuchong(APIView):
def generate_wechat_pay_params(self, dingdanid, jine, openid, pay_type):
"""生成微信支付参数(完整版,无省略)"""
# 微信支付配置 - 从settings中获取
APPID = settings.WEIXIN_APPID
MCHID = settings.WEIXIN_MCHID
KEY = settings.WEIXIN_SHANGHUMIYAO
from jituan.services.club_write import resolve_club_id_for_write
from jituan.services.wechat_pay import get_wechat_v2_config
pay_cfg = get_wechat_v2_config(resolve_club_id_for_write(self.request, request.user))
APPID = pay_cfg['appid']
MCHID = pay_cfg['mch_id']
KEY = pay_cfg['key']
# 根据支付类型获取对应的配置
if pay_type == 'yajin':
@@ -1166,7 +1176,9 @@ class JifenHuitiao(View):
return self.wechat_response('FAIL', '支付失败')
# 3. 验证签名
if not self.verify_signature(request_body):
from jituan.services.wechat_pay import club_id_from_czjilu, verify_wechat_v2_signature
notify_club = club_id_from_czjilu(out_trade_no)
if not verify_wechat_v2_signature(request_body, notify_club):
logger.error(f"积分签名验证失败: {out_trade_no}")
return self.wechat_response('FAIL', '签名验证失败')
@@ -1198,26 +1210,11 @@ class JifenHuitiao(View):
try:
# 获取订单金额确保是Decimal类型
jine_decimal = Decimal(str(order.jine)) if not isinstance(order.jine, Decimal) else order.jine
update_daily_income(jine_decimal)
update_daily_income(jine_decimal, getattr(order, 'club_id', None))
# 查询或创建ID=1的收支记录
szjilu, created = Szjilu.query.get_or_create(
id=1,
defaults={
'TotalIncome': Decimal('0.00'),
'TotalFlow': Decimal('0.00'),
'TotalExpense': Decimal('0.00'),
'DailyExpense': Decimal('0.00'),
'DailyFlow': Decimal('0.00')
}
)
# 更新三个字段
szjilu.TotalIncome += jine_decimal # 总收益
szjilu.TotalFlow += jine_decimal # 总流水
szjilu.DailyFlow += jine_decimal # 今日流水
szjilu.save()
update_daily_income(jine_decimal, getattr(order, 'club_id', None))
from jituan.services.szjilu_accounting import apply_szjilu_income
apply_szjilu_income(jine_decimal, getattr(order, 'club_id', None))
logger.info(f"收支记录更新成功: 订单{order.dingdan_id}, 金额{jine_decimal}")
@@ -1514,7 +1511,8 @@ class HuiyuanGoumai(APIView):
dingdanid = f"CzHY{timestamp_ms:011d}{timestamp_ns:06d}{random_num:02d}"
# 6. 创建充值记录订单
jine = float(huiyuan.jiage) # 会员价格
club_id = resolve_club_id_from_request(request)
jine = float(get_huiyuan_price(club_id, huiyuanid, huiyuan))
# 组合订单说明:会员介绍 + 配置描述
shuoming = f"{huiyuan.jieshao} - {settings.HUIYUAN_PAY_DESCRIPTION}"
@@ -1525,7 +1523,8 @@ class HuiyuanGoumai(APIView):
jine=jine,
leixing=1, # 充值类型1=会员
shuoming=shuoming,
huiyuan_id=huiyuanid # 存储会员ID
huiyuan_id=huiyuanid, # 存储会员ID
club_id=club_id,
)
logger.info(
@@ -1566,9 +1565,12 @@ class HuiyuanGoumai(APIView):
def generate_wechat_pay_params(self, dingdanid, jine, openid, pay_type):
"""生成微信支付参数(完整版,无省略)"""
# 微信支付配置 - 从settings中获取
APPID = settings.WEIXIN_APPID
MCHID = settings.WEIXIN_MCHID
KEY = settings.WEIXIN_SHANGHUMIYAO
from jituan.services.club_write import resolve_club_id_for_write
from jituan.services.wechat_pay import get_wechat_v2_config
pay_cfg = get_wechat_v2_config(resolve_club_id_for_write(self.request, request.user))
APPID = pay_cfg['appid']
MCHID = pay_cfg['mch_id']
KEY = pay_cfg['key']
# 根据支付类型获取对应的配置
if pay_type == 'yajin':
@@ -1737,7 +1739,9 @@ class HuiyuanHuitiao(View):
logger.warning(f"会员支付回调状态异常: {return_code}/{result_code}")
return self.wechat_response('FAIL', '支付失败')
if not self.verify_signature(request_body):
from jituan.services.wechat_pay import club_id_from_czjilu, verify_wechat_v2_signature
notify_club = club_id_from_czjilu(out_trade_no)
if not verify_wechat_v2_signature(request_body, notify_club):
logger.error(f"会员签名验证失败: {out_trade_no}")
return self.wechat_response('FAIL', '签名验证失败')
@@ -1774,17 +1778,9 @@ class HuiyuanHuitiao(View):
try:
jine_decimal = Decimal(str(order.jine)) if not isinstance(order.jine, Decimal) else order.jine
update_daily_income(jine_decimal)
szjilu, _ = Szjilu.query.get_or_create(
id=1,
defaults={'TotalIncome': Decimal('0.00'), 'TotalFlow': Decimal('0.00'), 'TotalExpense': Decimal('0.00'),
'DailyExpense': Decimal('0.00'), 'DailyFlow': Decimal('0.00')}
)
szjilu.TotalIncome += jine_decimal
szjilu.TotalFlow += jine_decimal
szjilu.DailyFlow += jine_decimal
szjilu.save()
update_daily_income(jine_decimal, getattr(order, 'club_id', None))
from jituan.services.szjilu_accounting import apply_szjilu_income
apply_szjilu_income(jine_decimal, getattr(order, 'club_id', None))
logger.info(f"收支记录更新成功: 订单{order.dingdan_id}, 金额{jine_decimal}")
except Exception as e:
logger.error(f"更新收支记录失败: {str(e)}", exc_info=True)
@@ -1851,7 +1847,8 @@ class HuiyuanHuitiao(View):
huiyuan_id=huiyuan_id,
jieshao=huiyuan.jieshao,
huiyuan_zhuangtai=1,
daoqi_time=timezone.now() + timedelta(days=30)
daoqi_time=timezone.now() + timedelta(days=30),
club_id=get_user_club_id(User.query.filter(UserUID=yonghuid).first()),
)
logger.info(f"新购会员: 用户{yonghuid}, 会员{huiyuan_id}")
return goumai_record, True, is_first_buy_any
@@ -1911,9 +1908,13 @@ class HuiyuanHuitiao(View):
# 获取订单金额(用于限制分红总额)
order_amount = Decimal(str(order.jine)) if not isinstance(order.jine, Decimal) else order.jine
club_id = getattr(order, 'club_id', None) or resolve_club_id_for_write(user=dashou.user)
guanshi_fc, zuzhang_fc = get_huiyuan_fenchong(club_id, huiyuan.huiyuan_id, huiyuan)
# 计算管事分红金额
guanshi_fenhong = self._calc_guanshi_fenhong(guanshi, huiyuan, guanshi_id, cishu)
guanshi_fenhong = self._calc_guanshi_fenhong(
guanshi, huiyuan, guanshi_id, cishu, default_fc=guanshi_fc,
)
if guanshi_fenhong is None:
logger.info(f"管事{guanshi_id}本次无分红")
guanshi_fenhong = Decimal('0.00')
@@ -1928,7 +1929,9 @@ class HuiyuanHuitiao(View):
logger.warning(f"组长信息不存在: {zuzhang_id}")
zuzhang = None
zuzhang_fenhong = self._calc_zuzhang_fenhong(zuzhang, huiyuan, zuzhang_id, cishu)
zuzhang_fenhong = self._calc_zuzhang_fenhong(
zuzhang, huiyuan, zuzhang_id, cishu, default_fc=zuzhang_fc,
)
if zuzhang_fenhong is None:
logger.info(f"组长{zuzhang_id}本次无分红")
zuzhang_fenhong = Decimal('0.00')
@@ -1975,11 +1978,15 @@ class HuiyuanHuitiao(View):
fenhong=guanshi_fenhong,
avatar=avatar,
nicheng=nicheng,
# 新增字段
zuzhang_id=zuzhang_id if zuzhang_fenhong > 0 else None,
zuzhang_fenhong=zuzhang_fenhong if zuzhang_fenhong > 0 else None,
huiyuan_id=huiyuan.huiyuan_id,
fenhong_leixing=1,
club_id=resolve_gsfenhong_club_id(
dingdan_id=order.dingdan_id,
dashouid=order.yonghuid,
czjilu=order,
),
)
@@ -2028,22 +2035,24 @@ class HuiyuanHuitiao(View):
logger.error(f"处理分红失败: {str(e)}", exc_info=True)
# 不抛出异常,避免影响主流程
def _calc_guanshi_fenhong(self, guanshi, huiyuan, guanshi_id, cishu):
def _calc_guanshi_fenhong(self, guanshi, huiyuan, guanshi_id, cishu, default_fc=None):
"""计算管事分红金额"""
# 先查多次分红配置表
try:
cid = resolve_gsfenhong_club_id(dashouid=guanshi_id)
config = DuociFenhong.query.get(
club_id=cid,
huiyuan=huiyuan,
yonghuid=guanshi_id,
cishu=cishu
cishu=cishu,
)
return Decimal(str(config.guanshi_fenhong))
except DuociFenhong.DoesNotExist:
pass
if cishu == 1:
# 第一次分红,取会员表默认金额
return Decimal(str(huiyuan.guanshifc))
base = default_fc if default_fc is not None else huiyuan.guanshifc
return Decimal(str(base))
else:
# 第二次及以上,检查是否开启二次分红
if guanshi.fenghong_erci_enabled:
@@ -2051,24 +2060,26 @@ class HuiyuanHuitiao(View):
else:
return None # 无分红
def _calc_zuzhang_fenhong(self, zuzhang, huiyuan, zuzhang_id, cishu):
def _calc_zuzhang_fenhong(self, zuzhang, huiyuan, zuzhang_id, cishu, default_fc=None):
"""计算组长分红金额"""
if zuzhang is None:
return None
# 查多次分红配置表
try:
cid = resolve_gsfenhong_club_id(dashouid=zuzhang_id)
config = DuociFenhong.query.get(
club_id=cid,
huiyuan=huiyuan,
yonghuid=zuzhang_id,
cishu=cishu
cishu=cishu,
)
return Decimal(str(config.zuzhang_fenhong))
except DuociFenhong.DoesNotExist:
pass
if cishu == 1:
# 第一次分红,取会员表默认组长分成
return Decimal(str(huiyuan.zuzhangfc))
base = default_fc if default_fc is not None else huiyuan.zuzhangfc
return Decimal(str(base))
else:
# 第二次及以上,检查组长是否开启额外分红
if zuzhang.kaioi_ewai_fenhong:
@@ -2358,7 +2369,8 @@ class ShangjiaChongzhi(APIView):
yonghuid=request.user.UserUID,
jine=jine,
leixing=4, # 充值类型4=商家充值
shuoming=settings.SHANGJIA_CZ_PAY_DESCRIPTION
shuoming=settings.SHANGJIA_CZ_PAY_DESCRIPTION,
club_id=resolve_club_id_for_write(request, request.user),
)
logger.info(f"创建商家充值订单: 商家{shangjia.nicheng}, 订单{dingdanid}, 金额{jine}")
except Exception as e:
@@ -2404,9 +2416,12 @@ class ShangjiaChongzhi(APIView):
def generate_wechat_pay_params(self, dingdanid, jine, openid, pay_type):
"""生成微信小程序支付参数"""
# 获取配置
APPID = settings.WEIXIN_APPID
MCHID = settings.WEIXIN_MCHID
KEY = settings.WEIXIN_SHANGHUMIYAO
from jituan.services.club_write import resolve_club_id_for_write
from jituan.services.wechat_pay import get_wechat_v2_config
pay_cfg = get_wechat_v2_config(resolve_club_id_for_write(self.request, request.user))
APPID = pay_cfg['appid']
MCHID = pay_cfg['mch_id']
KEY = pay_cfg['key']
# 根据支付类型获取对应的配置
if pay_type == 'shangjia':
@@ -2573,7 +2588,9 @@ class ShangjiaHuitiao(View):
return self.wechat_response('FAIL', '支付失败')
# 3. 验证签名(确保是微信发来的)
if not self.verify_signature(request_body):
from jituan.services.wechat_pay import club_id_from_czjilu, verify_wechat_v2_signature
notify_club = club_id_from_czjilu(out_trade_no)
if not verify_wechat_v2_signature(request_body, notify_club):
logger.error(f"商家支付签名验证失败: {out_trade_no}")
return self.wechat_response('FAIL', '签名验证失败')
@@ -2606,26 +2623,11 @@ class ShangjiaHuitiao(View):
# 获取订单金额确保是Decimal类型
jine_decimal = Decimal(str(order.jine)) if not isinstance(order.jine, Decimal) else order.jine
update_daily_income(jine_decimal)
update_daily_income(jine_decimal, getattr(order, 'club_id', None))
# 查询或创建ID=1的收支记录
szjilu, created = Szjilu.query.get_or_create(
id=1,
defaults={
'TotalIncome': Decimal('0.00'),
'TotalFlow': Decimal('0.00'),
'TotalExpense': Decimal('0.00'),
'DailyExpense': Decimal('0.00'),
'DailyFlow': Decimal('0.00')
}
)
# 更新三个字段
szjilu.TotalIncome += jine_decimal # 总收益
szjilu.TotalFlow += jine_decimal # 总流水
szjilu.DailyFlow += jine_decimal # 今日流水
szjilu.save()
update_daily_income(jine_decimal, getattr(order, 'club_id', None))
from jituan.services.szjilu_accounting import apply_szjilu_income
apply_szjilu_income(jine_decimal, getattr(order, 'club_id', None))
logger.info(f"收支记录更新成功: 订单{order.dingdan_id}, 金额{jine_decimal}")
@@ -4361,13 +4363,10 @@ class AdShangpinHuQu(APIView):
status=status.HTTP_401_UNAUTHORIZED
)
# 6. 查询公告数据(类型=1的公告
gonggao_obj = Gonggao.query.filter(NoticeType=1).first()
shangpingonggao = gonggao_obj.Content if gonggao_obj else ''
# 7. 查询轮播图数据(类型=1的轮播图
lunbo_objs = Lunbo.query.filter(ImageType=1).values('ImageURL')
shangpinlunbo = [item['ImageURL'] for item in lunbo_objs]
# 6. 查询公告与轮播(按俱乐部,默认抢单池
from jituan.services.display_config import get_gonggao_content, get_lunbo_urls
shangpingonggao = get_gonggao_content(request, notice_type=1)
shangpinlunbo = get_lunbo_urls(request, page_key='order_pool', image_type=1)
# 8. 查询商品类型数据
shangpinleixing_data = ShangpinLeixing.query.all().values(
@@ -6077,7 +6076,8 @@ class CzhqdyView(APIView):
# 会员
try:
huiyuan = Huiyuan.query.get(huiyuan_id=huiyuan_id)
target_price = huiyuan.jiage
club_id = resolve_club_id_from_request(request)
target_price = get_huiyuan_price(club_id, huiyuan_id, huiyuan)
except Huiyuan.DoesNotExist:
return Response({'code': 404, 'msg': '会员不存在'}, status=status.HTTP_404_NOT_FOUND)
elif leixing == 2:
@@ -6096,12 +6096,9 @@ class CzhqdyView(APIView):
# 获取所有费率(打手佣金、管事分红、组长分红、打手押金)
rates = {}
club_id = resolve_club_id_from_request(request)
for key in ['5', '6', '8', '11']:
try:
rate_obj = CommissionRate.query.get(Platform=key)
rates[key] = rate_obj.Rate
except CommissionRate.DoesNotExist:
rates[key] = None # 未配置则跳过
rates[key] = get_commission_rate(club_id, key)
# 构建可用身份列表
available_list = []
@@ -6214,10 +6211,11 @@ class DsqrgmdhView(APIView):
return Response({'code': 400, 'msg': 'yajin_jine 必须为正数'}, status=status.HTTP_400_BAD_REQUEST)
target_price = None
club_id = resolve_club_id_from_request(request)
if leixing == 1:
try:
huiyuan = Huiyuan.query.get(huiyuan_id=huiyuan_id)
target_price = huiyuan.jiage
target_price = get_huiyuan_price(club_id, huiyuan_id, huiyuan)
except Huiyuan.DoesNotExist:
return Response({'code': 404, 'msg': '会员不存在'}, status=status.HTTP_404_NOT_FOUND)
elif leixing == 2:
@@ -6302,6 +6300,7 @@ class DsqrgmdhView(APIView):
jine=required,
leixing=leixing,
shuoming=shuoming,
club_id=club_id,
)
response_data = {}
@@ -6394,7 +6393,8 @@ class DsqrgmdhView(APIView):
huiyuan_id=huiyuan_id,
jieshao=huiyuan_obj.jieshao,
huiyuan_zhuangtai=1,
daoqi_time=timezone.now() + timedelta(days=30)
daoqi_time=timezone.now() + timedelta(days=30),
club_id=get_user_club_id(User.query.filter(UserUID=yonghuid).first()),
)
return record, is_first_buy_any
except Exception as e: