feat: 俱乐部开关-无会员可聊天与买押金补满积分
This commit is contained in:
1 parent
2ace9ca9ee
commit
16faf5afe4
6 files changed
+217
-52
No files matched your search
@@ -120,6 +120,13 @@ def fulfill_yajin_recharge(dingdan_id, paid_amount_yuan=None):
|
||||
dashou.yajin += amount
|
||||
dashou.save(update_fields=['yajin'])
|
||||
|
||||
try:
|
||||
from jituan.services.yajin_jifen_switch import maybe_fill_jifen_after_yajin
|
||||
club_id_for_jifen = getattr(order, 'club_id', None) or CLUB_ID_DEFAULT
|
||||
maybe_fill_jifen_after_yajin(dashou, club_id_for_jifen)
|
||||
except Exception as exc:
|
||||
logger.error('押金补积分失败 dingdan=%s: %s', dingdan_id, exc, exc_info=True)
|
||||
|
||||
order.zhuangtai = 3
|
||||
order.save(update_fields=['zhuangtai'])
|
||||
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
"""俱乐部打手聊天门槛(config_json)。
|
||||
|
||||
默认与现网一致:不充会员不可聊,但押金达到门槛可聊;进行中订单旁路保留。
|
||||
开启 allow_without_member 后:打手无需会员也可聊。
|
||||
"""
|
||||
import logging
|
||||
from decimal import Decimal, InvalidOperation
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
ALLOW_WITHOUT_MEMBER_KEY = 'dashou_chat_allow_without_member'
|
||||
YAJIN_THRESHOLD_KEY = 'dashou_chat_yajin_threshold'
|
||||
DEFAULT_YAJIN_THRESHOLD = Decimal('5')
|
||||
|
||||
|
||||
def _club(club_id: str):
|
||||
cid = (club_id or '').strip()
|
||||
if not cid:
|
||||
return None
|
||||
from jituan.models import Club
|
||||
club = Club.query.filter(club_id=cid).first()
|
||||
if not club and cid != cid.lower():
|
||||
club = Club.query.filter(club_id=cid.lower()).first()
|
||||
return club
|
||||
|
||||
|
||||
def is_dashou_chat_allow_without_member(club_id: str) -> bool:
|
||||
"""开启=不充会员也能聊;关闭(默认)=须会员或押金达标等。"""
|
||||
club = _club(club_id)
|
||||
if not club:
|
||||
return False
|
||||
try:
|
||||
cfg = club.config_json or {}
|
||||
raw = cfg.get(ALLOW_WITHOUT_MEMBER_KEY, False)
|
||||
if isinstance(raw, str):
|
||||
return raw.strip().lower() in ('1', 'true', 'yes', 'on')
|
||||
return bool(raw)
|
||||
except Exception:
|
||||
logger.exception('read dashou_chat_allow_without_member failed club=%s', club_id)
|
||||
return False
|
||||
|
||||
|
||||
def get_dashou_chat_yajin_threshold(club_id: str) -> Decimal:
|
||||
"""无会员时押金门槛;默认 5(与现网硬编码一致)。"""
|
||||
club = _club(club_id)
|
||||
if not club:
|
||||
return DEFAULT_YAJIN_THRESHOLD
|
||||
try:
|
||||
cfg = club.config_json or {}
|
||||
if YAJIN_THRESHOLD_KEY not in cfg:
|
||||
return DEFAULT_YAJIN_THRESHOLD
|
||||
return Decimal(str(cfg.get(YAJIN_THRESHOLD_KEY) or DEFAULT_YAJIN_THRESHOLD))
|
||||
except (InvalidOperation, TypeError, ValueError):
|
||||
return DEFAULT_YAJIN_THRESHOLD
|
||||
except Exception:
|
||||
logger.exception('read dashou_chat_yajin_threshold failed club=%s', club_id)
|
||||
return DEFAULT_YAJIN_THRESHOLD
|
||||
|
||||
|
||||
def set_dashou_chat_allow_without_member(club, enabled: bool) -> None:
|
||||
cfg = dict(getattr(club, 'config_json', None) or {})
|
||||
cfg[ALLOW_WITHOUT_MEMBER_KEY] = bool(enabled)
|
||||
club.config_json = cfg
|
||||
|
||||
|
||||
def set_dashou_chat_yajin_threshold(club, threshold) -> Decimal:
|
||||
try:
|
||||
val = Decimal(str(threshold))
|
||||
except (InvalidOperation, TypeError, ValueError):
|
||||
val = DEFAULT_YAJIN_THRESHOLD
|
||||
if val < 0:
|
||||
val = Decimal('0')
|
||||
cfg = dict(getattr(club, 'config_json', None) or {})
|
||||
cfg[YAJIN_THRESHOLD_KEY] = float(val)
|
||||
club.config_json = cfg
|
||||
return val
|
||||
@@ -0,0 +1,60 @@
|
||||
"""俱乐部:购买押金是否把打手积分补到 10(config_json)。
|
||||
|
||||
默认关闭(与现网一致:押金履约不加积分)。
|
||||
开启后:押金到账时若 jifen < 10 则补到 10;已是 ≥10 不变。
|
||||
"""
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
CONFIG_KEY = 'yajin_purchase_fill_jifen'
|
||||
JIFEN_TARGET = 10
|
||||
|
||||
|
||||
def is_yajin_purchase_fill_jifen(club_id: str) -> bool:
|
||||
cid = (club_id or '').strip()
|
||||
if not cid:
|
||||
return False
|
||||
try:
|
||||
from jituan.models import Club
|
||||
club = Club.query.filter(club_id=cid).first()
|
||||
if not club and cid != cid.lower():
|
||||
club = Club.query.filter(club_id=cid.lower()).first()
|
||||
if not club:
|
||||
return False
|
||||
cfg = club.config_json or {}
|
||||
raw = cfg.get(CONFIG_KEY, False)
|
||||
if isinstance(raw, str):
|
||||
return raw.strip().lower() in ('1', 'true', 'yes', 'on')
|
||||
return bool(raw)
|
||||
except Exception:
|
||||
logger.exception('read yajin_purchase_fill_jifen failed club=%s', cid)
|
||||
return False
|
||||
|
||||
|
||||
def set_yajin_purchase_fill_jifen(club, enabled: bool) -> None:
|
||||
cfg = dict(getattr(club, 'config_json', None) or {})
|
||||
cfg[CONFIG_KEY] = bool(enabled)
|
||||
club.config_json = cfg
|
||||
|
||||
|
||||
def maybe_fill_jifen_after_yajin(dashou, club_id: str) -> bool:
|
||||
"""
|
||||
若俱乐部开启「押金送积分」且当前积分 < 10,补到 10。
|
||||
调用方须已对 dashou 加锁;返回是否改动了积分。
|
||||
"""
|
||||
if not is_yajin_purchase_fill_jifen(club_id):
|
||||
return False
|
||||
try:
|
||||
cur = int(getattr(dashou, 'jifen', 0) or 0)
|
||||
except (TypeError, ValueError):
|
||||
cur = 0
|
||||
if cur >= JIFEN_TARGET:
|
||||
return False
|
||||
dashou.jifen = JIFEN_TARGET
|
||||
dashou.save(update_fields=['jifen'])
|
||||
logger.info(
|
||||
'yajin fill jifen club=%s uid=%s %s->%s',
|
||||
club_id, getattr(getattr(dashou, 'user', None), 'UserUID', ''), cur, JIFEN_TARGET,
|
||||
)
|
||||
return True
|
||||
+42
-50
@@ -631,17 +631,15 @@ class ClubManageView(APIView):
|
||||
}
|
||||
|
||||
def _serialize(self, club, full=False):
|
||||
from jituan.services.default_invite import (
|
||||
get_club_default_dashou_invite,
|
||||
get_club_default_dashou_invite_raw,
|
||||
get_club_fallback_dashou_invite,
|
||||
)
|
||||
from jituan.services.default_invite import get_club_default_dashou_invite
|
||||
from jituan.services.force_phone_auth import is_club_force_phone_auth
|
||||
from jituan.services.dashou_chat_gate import (
|
||||
get_dashou_chat_yajin_threshold,
|
||||
is_dashou_chat_allow_without_member,
|
||||
)
|
||||
from jituan.services.yajin_jifen_switch import is_yajin_purchase_fill_jifen
|
||||
|
||||
# 后台表单必须展示「库里真实保存值」,不能用带回落的 effective,
|
||||
# 否则一保存就会把系统固定回落码写进 config_json,看起来像改不动。
|
||||
invite_raw = get_club_default_dashou_invite_raw(club)
|
||||
invite_fallback = get_club_fallback_dashou_invite(club.club_id)
|
||||
cfg_json = club.config_json or {}
|
||||
base = {
|
||||
'club_id': club.club_id,
|
||||
'name': club.name,
|
||||
@@ -656,10 +654,11 @@ class ClubManageView(APIView):
|
||||
'goeasy_appkey': club.goeasy_appkey,
|
||||
'template_id': club.template_id,
|
||||
'sort_order': club.sort_order,
|
||||
'default_dashou_invite': invite_raw,
|
||||
'default_dashou_invite_effective': get_club_default_dashou_invite(club.club_id),
|
||||
'default_dashou_invite_fallback': invite_fallback,
|
||||
'default_dashou_invite': get_club_default_dashou_invite(club.club_id),
|
||||
'force_phone_auth': is_club_force_phone_auth(club.club_id),
|
||||
'dashou_chat_allow_without_member': is_dashou_chat_allow_without_member(club.club_id),
|
||||
'dashou_chat_yajin_threshold': float(get_dashou_chat_yajin_threshold(club.club_id)),
|
||||
'yajin_purchase_fill_jifen': is_yajin_purchase_fill_jifen(club.club_id),
|
||||
}
|
||||
if full:
|
||||
mch_key = self._effective_mch_key(club)
|
||||
@@ -679,7 +678,7 @@ class ClubManageView(APIView):
|
||||
'encoding_aes_key': club.encoding_aes_key,
|
||||
'goeasy_secret': club.goeasy_secret,
|
||||
'template_max_per_minute': club.template_max_per_minute,
|
||||
'default_dashou_invite_raw': invite_raw,
|
||||
'default_dashou_invite_raw': (cfg_json.get('default_dashou_invite') or '').strip(),
|
||||
})
|
||||
return base
|
||||
|
||||
@@ -726,15 +725,15 @@ class ClubManageView(APIView):
|
||||
return Response({'code': 400, 'msg': 'mini_pay_channel 仅支持 wechat/fubei'})
|
||||
setattr(club, field, val)
|
||||
update_fields.append(field)
|
||||
# config_json 相关字段按顺序合并到同一份 dict,避免互相覆盖
|
||||
touched_config = False
|
||||
if 'mch_key' in request.data:
|
||||
self._sync_mch_key_to_config_json(club, request.data.get('mch_key'))
|
||||
touched_config = True
|
||||
if 'config_json' not in update_fields:
|
||||
update_fields.append('config_json')
|
||||
if 'default_dashou_invite' in request.data:
|
||||
from jituan.services.default_invite import set_club_default_dashou_invite
|
||||
set_club_default_dashou_invite(club, request.data.get('default_dashou_invite'))
|
||||
touched_config = True
|
||||
if 'config_json' not in update_fields:
|
||||
update_fields.append('config_json')
|
||||
if 'force_phone_auth' in request.data:
|
||||
from jituan.services.force_phone_auth import set_club_force_phone_auth
|
||||
raw = request.data.get('force_phone_auth')
|
||||
@@ -743,15 +742,35 @@ class ClubManageView(APIView):
|
||||
else:
|
||||
enabled = bool(raw)
|
||||
set_club_force_phone_auth(club, enabled)
|
||||
touched_config = True
|
||||
if touched_config:
|
||||
# 再赋一次新 dict,确保 JSONField 一定落库
|
||||
club.config_json = dict(club.config_json or {})
|
||||
if 'config_json' not in update_fields:
|
||||
update_fields.append('config_json')
|
||||
if 'dashou_chat_allow_without_member' in request.data:
|
||||
from jituan.services.dashou_chat_gate import set_dashou_chat_allow_without_member
|
||||
raw = request.data.get('dashou_chat_allow_without_member')
|
||||
if isinstance(raw, str):
|
||||
enabled = raw.strip().lower() in ('1', 'true', 'yes', 'on')
|
||||
else:
|
||||
enabled = bool(raw)
|
||||
set_dashou_chat_allow_without_member(club, enabled)
|
||||
if 'config_json' not in update_fields:
|
||||
update_fields.append('config_json')
|
||||
if 'dashou_chat_yajin_threshold' in request.data:
|
||||
from jituan.services.dashou_chat_gate import set_dashou_chat_yajin_threshold
|
||||
set_dashou_chat_yajin_threshold(club, request.data.get('dashou_chat_yajin_threshold'))
|
||||
if 'config_json' not in update_fields:
|
||||
update_fields.append('config_json')
|
||||
if 'yajin_purchase_fill_jifen' in request.data:
|
||||
from jituan.services.yajin_jifen_switch import set_yajin_purchase_fill_jifen
|
||||
raw = request.data.get('yajin_purchase_fill_jifen')
|
||||
if isinstance(raw, str):
|
||||
enabled = raw.strip().lower() in ('1', 'true', 'yes', 'on')
|
||||
else:
|
||||
enabled = bool(raw)
|
||||
set_yajin_purchase_fill_jifen(club, enabled)
|
||||
if 'config_json' not in update_fields:
|
||||
update_fields.append('config_json')
|
||||
if update_fields:
|
||||
club.save(update_fields=update_fields)
|
||||
club.refresh_from_db()
|
||||
return Response({
|
||||
'code': 0,
|
||||
'msg': '保存成功',
|
||||
@@ -779,34 +798,7 @@ class ClubManageView(APIView):
|
||||
'data': self._serialize(club, full=True),
|
||||
})
|
||||
|
||||
if action in ('seed_invite_status', 'seed_invite'):
|
||||
from jituan.services.club_seed_invite import (
|
||||
club_invite_seed_status,
|
||||
ensure_club_seed_invite,
|
||||
)
|
||||
if action == 'seed_invite_status':
|
||||
return Response({
|
||||
'code': 0,
|
||||
'data': club_invite_seed_status(club_id),
|
||||
})
|
||||
result = ensure_club_seed_invite(club_id)
|
||||
if not result.get('ok'):
|
||||
return Response({'code': 400, 'msg': result.get('msg') or '种子失败'})
|
||||
# 刷新默认邀请码到序列化结果
|
||||
club = Club.query.filter(club_id=club_id).first()
|
||||
return Response({
|
||||
'code': 0,
|
||||
'msg': result.get('msg') or 'ok',
|
||||
'data': {
|
||||
**result,
|
||||
'club': self._serialize(club, full=True) if club else None,
|
||||
},
|
||||
})
|
||||
|
||||
return Response({
|
||||
'code': 400,
|
||||
'msg': '无效 action,支持 list/get/update/create/seed_invite/seed_invite_status',
|
||||
})
|
||||
return Response({'code': 400, 'msg': '无效 action,支持 list/get/update/create'})
|
||||
except Exception:
|
||||
logger.exception('ClubManageView 异常')
|
||||
return Response({'code': 99, 'msg': '俱乐部配置操作失败'}, status=500)
|
||||
|
||||
@@ -1094,9 +1094,19 @@ class DsqrgmdhView(APIView):
|
||||
dashou = UserDashou.objects.select_for_update().get(user=user)
|
||||
dashou.yajin += yajin_jine
|
||||
dashou.save(update_fields=['yajin'])
|
||||
try:
|
||||
from jituan.services.yajin_jifen_switch import maybe_fill_jifen_after_yajin
|
||||
from jituan.services.club_user import get_user_club_id
|
||||
maybe_fill_jifen_after_yajin(
|
||||
dashou,
|
||||
getattr(czjilu, 'club_id', None) or get_user_club_id(user) or '',
|
||||
)
|
||||
except Exception as jifen_exc:
|
||||
logger.error('余额抵扣押金补积分失败: %s', jifen_exc, exc_info=True)
|
||||
czjilu.zhuangtai = MEMBER_PAID_STATUS
|
||||
czjilu.save(update_fields=['zhuangtai'])
|
||||
response_data['yajin'] = dashou.yajin
|
||||
response_data['jifen'] = dashou.jifen
|
||||
except UserDashou.DoesNotExist:
|
||||
setattr(locked_profile, identity['balance_field'], current_balance)
|
||||
locked_profile.save(update_fields=[identity['balance_field']])
|
||||
|
||||
@@ -1140,8 +1140,28 @@ class DashouJianquanView(APIView):
|
||||
'msg': '您尚未注册打手身份,请先注册'
|
||||
})
|
||||
|
||||
# ========== 3. 条件一:押金 >= 5 元 ==========
|
||||
if dashou.yajin and dashou.yajin >= 5:
|
||||
from jituan.services.club_user import get_user_club_id
|
||||
from jituan.services.dashou_chat_gate import (
|
||||
get_dashou_chat_yajin_threshold,
|
||||
is_dashou_chat_allow_without_member,
|
||||
)
|
||||
club_id = get_user_club_id(user) or ''
|
||||
|
||||
# 俱乐部开启「不充会员也可用聊天」→ 直接通过
|
||||
if is_dashou_chat_allow_without_member(club_id):
|
||||
return Response({
|
||||
'code': 0,
|
||||
'allow': 1,
|
||||
'msg': '鉴权通过(俱乐部允许无会员聊天)'
|
||||
})
|
||||
|
||||
# ========== 3. 押金达到俱乐部门槛 ==========
|
||||
threshold = get_dashou_chat_yajin_threshold(club_id)
|
||||
try:
|
||||
yajin = Decimal(str(getattr(dashou, 'yajin', 0) or 0))
|
||||
except Exception:
|
||||
yajin = Decimal('0')
|
||||
if yajin >= threshold:
|
||||
return Response({
|
||||
'code': 0,
|
||||
'allow': 1,
|
||||
|
||||
Reference in new issue
Block a user