diff --git a/jituan/services/default_invite.py b/jituan/services/default_invite.py index 43936ac..ba7d017 100644 --- a/jituan/services/default_invite.py +++ b/jituan/services/default_invite.py @@ -12,24 +12,21 @@ _FALLBACK_DEFAULT_INVITE = { } -def get_club_default_dashou_invite(club_id: str) -> str: - """ - 优先读 Club.config_json.default_dashou_invite, - 否则回落内置各店默认管事邀请码。 - """ +def get_club_default_dashou_invite_raw(club) -> str: + """只读 config_json 里真正保存的值(后台表单用,不含回落)。""" + if not club: + return '' + cfg = getattr(club, 'config_json', None) or {} + if not isinstance(cfg, dict): + return '' + return (cfg.get(CONFIG_KEY) or '').strip() + + +def get_club_fallback_dashou_invite(club_id: str) -> str: + """系统内置回落(仅小程序运行时;后台不应把它当已保存值展示)。""" cid = (club_id or '').strip() if not cid: return '' - try: - from jituan.models import Club - club = Club.query.filter(club_id=cid).first() - if club: - cfg = club.config_json or {} - code = (cfg.get(CONFIG_KEY) or '').strip() - if code: - return code - except Exception: - logger.exception('read default_dashou_invite failed club=%s', cid) try: from jituan.services.club_migrate import CLUB_MIGRATE_DEFAULT_GUANSHI_INVITE code = (CLUB_MIGRATE_DEFAULT_GUANSHI_INVITE.get(cid) or '').strip() @@ -40,8 +37,33 @@ def get_club_default_dashou_invite(club_id: str) -> str: return (_FALLBACK_DEFAULT_INVITE.get(cid) or '').strip() +def get_club_default_dashou_invite(club_id: str) -> str: + """ + 小程序用:优先 config_json.default_dashou_invite,否则回落内置码。 + """ + cid = (club_id or '').strip() + if not cid: + return '' + try: + from jituan.models import Club + club = Club.query.filter(club_id=cid).first() + if club: + code = get_club_default_dashou_invite_raw(club) + if code: + return code + except Exception: + logger.exception('read default_dashou_invite failed club=%s', cid) + return get_club_fallback_dashou_invite(cid) + + def set_club_default_dashou_invite(club, invite_code: str) -> None: - """写入 club.config_json,调用方负责 save。""" + """写入 club.config_json,调用方负责 save。空字符串表示清空已保存值。""" cfg = dict(getattr(club, 'config_json', None) or {}) - cfg[CONFIG_KEY] = (invite_code or '').strip() - club.config_json = cfg + code = (invite_code or '').strip() + if code: + cfg[CONFIG_KEY] = code + else: + # 清空:删掉键,避免再次读到旧固定值 + cfg.pop(CONFIG_KEY, None) + # 强制新 dict,避免 JSONField 变更检测失败 + club.config_json = dict(cfg) diff --git a/jituan/views.py b/jituan/views.py index 72794be..03ba0a4 100644 --- a/jituan/views.py +++ b/jituan/views.py @@ -631,10 +631,17 @@ class ClubManageView(APIView): } def _serialize(self, club, full=False): - from jituan.services.default_invite import get_club_default_dashou_invite + 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.force_phone_auth import is_club_force_phone_auth - cfg_json = club.config_json or {} + # 后台表单必须展示「库里真实保存值」,不能用带回落的 effective, + # 否则一保存就会把系统固定回落码写进 config_json,看起来像改不动。 + invite_raw = get_club_default_dashou_invite_raw(club) + invite_fallback = get_club_fallback_dashou_invite(club.club_id) base = { 'club_id': club.club_id, 'name': club.name, @@ -649,7 +656,9 @@ class ClubManageView(APIView): 'goeasy_appkey': club.goeasy_appkey, 'template_id': club.template_id, 'sort_order': club.sort_order, - 'default_dashou_invite': get_club_default_dashou_invite(club.club_id), + 'default_dashou_invite': invite_raw, + 'default_dashou_invite_effective': get_club_default_dashou_invite(club.club_id), + 'default_dashou_invite_fallback': invite_fallback, 'force_phone_auth': is_club_force_phone_auth(club.club_id), } if full: @@ -670,7 +679,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': (cfg_json.get('default_dashou_invite') or '').strip(), + 'default_dashou_invite_raw': invite_raw, }) return base @@ -717,15 +726,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')) - if 'config_json' not in update_fields: - update_fields.append('config_json') + touched_config = True 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')) - if 'config_json' not in update_fields: - update_fields.append('config_json') + touched_config = True 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') @@ -734,10 +743,15 @@ 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 update_fields: club.save(update_fields=update_fields) + club.refresh_from_db() return Response({ 'code': 0, 'msg': '保存成功',