Files
Django/jituan/services/miniapp_assets.py
2026-07-25 22:49:11 +08:00

330 lines
16 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""小程序频道与可配置图标(按俱乐部)。"""
from jituan.services.display_config import _display_club_id
ICON_FOLDER = 'beijing/tubiao/chengxutubiao'
# icon_key -> 后台展示名(文件名固定为 {icon_key}.png
MINIAPP_ICON_META = [
('merchant_gold_banner', '商家金牌横幅(抢单/订单列表顶部)'),
# 商家首页大图(与「我的」页小图标分开配置)
('merchant_home_regular', '商家首页-常规派单大图'),
('merchant_home_custom', '商家首页-自定义派单大图'),
('merchant_home_link', '商家首页-链接派单大图'),
('merchant_home_kefu_key', '商家首页-客服密钥'),
('merchant_home_kefu_list', '商家首页-客服列表'),
('merchant_home_notice', '商家首页-公告图标'),
('merchant_home_stat_bg', '商家首页-统计卡背景'),
# 商家「我的」页功能小图标
('merchant_regular_dispatch', '我的页-常规派单'),
('merchant_custom_dispatch', '我的页-自定义派单'),
('merchant_pending_settle', '我的页-待清算'),
('merchant_kefu_key', '我的页-客服密钥'),
('merchant_kefu_list', '我的页-客服列表'),
('merchant_link_dispatch', '我的页-链接派单'),
('merchant_punish', '我的页-处罚记录'),
('merchant_rank', '我的页-商家排行'),
('merchant_refresh', '我的页-刷新'),
('fighter_recharge_member', '打手中心-会员充值横幅(左侧)'),
('fighter_recharge_deposit', '打手中心-保证金充值横幅(右侧)'),
('fighter_recharge_vip_bg', '打手会员充值页-顶部背景图'),
('fighter_mine_vip_bg', '打手「我的」-VIP条背景图'),
('fighter_mine_vip_icon', '打手「我的」-VIP条左侧图标'),
('fighter_deposit_bg', '打手保证金页-顶部背景图'),
('fighter_deposit_hero', '打手保证金页-卡片右侧装饰图'),
('fighter_deposit_benefit_1', '保证金页-权益图标1'),
('fighter_deposit_benefit_2', '保证金页-权益图标2'),
('fighter_deposit_benefit_3', '保证金页-权益图标3'),
('fighter_deposit_benefit_4', '保证金页-权益图标4'),
('fighter_recharge_benefit_1', '会员充值页-权益图标1'),
('fighter_recharge_benefit_2', '会员充值页-权益图标2'),
('fighter_recharge_benefit_3', '会员充值页-权益图标3'),
('fighter_recharge_advantage_1', '会员充值页-平台优势1'),
('fighter_recharge_advantage_2', '会员充值页-平台优势2'),
('fighter_recharge_advantage_3', '会员充值页-平台优势3'),
('fighter_recharge_advantage_4', '会员充值页-平台优势4'),
('fighter_order_pending', '打手中心-订单-待完成'),
('fighter_order_settling', '打手中心-订单-待结算'),
('fighter_order_done', '打手中心-订单-已完成'),
('fighter_order_closed', '打手中心-订单-已关闭'),
('fighter_trade_commission', '打手中心-佣金记录'),
('fighter_trade_withdraw', '打手中心-提现记录'),
('fighter_trade_settle', '打手中心-结算记录'),
('fighter_trade_deduct', '打手中心-扣款记录'),
('fighter_trade_recharge', '打手中心-充值记录'),
('fighter_invite_subordinate', '打手中心-我的下级'),
('fighter_invite_poster', '打手中心-推广海报'),
('fighter_invite_code', '打手中心-邀请码'),
('fighter_kuaishou_cash', '打手中心-快手提现'),
('fighter_follow_kuaishou', '打手中心-关注快手'),
('fighter_mine_copy', '打手中心-复制ID'),
('fighter_mine_edit', '打手中心-编辑资料'),
('fighter_mine_qiepian', '打手中心-切片活动横幅'),
('fighter_mine_penalty_tip', '打手中心-罚单提示图标'),
('fighter_mine_kefu', '打手中心-在线客服'),
('fighter_mine_shangjia', '打手中心-商家认证'),
('fighter_complaint_record', '打手中心-投诉记录'),
('fighter_mine_punish', '打手中心-我的处罚'),
('fighter_mine_medal', '打手中心-考核金牌'),
('fighter_mine_book', '打手中心-接单考试/规则'),
('fighter_mine_rank', '打手中心-总排行榜'),
('fighter_mine_invite', '打手中心-联系邀请人'),
('fighter_mine_contact', '打手中心-联系组长'),
('fighter_mine_record', '打手中心-会员/分红记录'),
('fighter_mine_kaohe_dafen', '打手中心-考核打分'),
('fighter_mine_kaohe_jilu', '打手中心-考核记录'),
('fighter_mine_kaohe_zhongxin', '打手中心-考核中心'),
('fighter_mine_dashou_auth', '打手中心-接单员认证'),
('fighter_mine_clear', '打手中心-清除缓存'),
('fighter_mine_switch', '打手中心-返回点单端'),
('mine_pindao', '频道入口'),
('icon_refresh', '通用刷新'),
# 点单端「我的」服务入口(客服 / 邀请须分开可配)
('cuser_mine_kefu', '点单端我的-在线客服'),
('cuser_mine_invite', '点单端我的-邀请点单'),
('cuser_mine_invitees', '点单端我的-我推荐的人'),
('guanshi_poster_bg', '管事推广海报背景'),
('zuzhang_poster_bg', '组长推广海报背景'),
('cuser_poster_bg', '点单端推荐官海报背景'),
# 打手接单池订单卡背景(未上传不下发,前端保留原样式)
('grab_card_pingtai_bg', '接单池-平台订单卡背景'),
('grab_card_youzhi_bg', '接单池-优质商家卡背景'),
('grab_card_shangjia_bg', '接单池-普通商家卡背景'),
]
MINIAPP_ICON_KEYS = [k for k, _ in MINIAPP_ICON_META]
MINIAPP_ICON_LABELS = dict(MINIAPP_ICON_META)
# 无 DB 记录时的默认相对路径(与种子脚本一致)
MINIAPP_ICON_DEFAULTS = {
key: f'{ICON_FOLDER}/{key}.png' for key in MINIAPP_ICON_KEYS
}
# 部分历史资源扩展名不同
MINIAPP_ICON_DEFAULTS['icon_refresh'] = 'beijing/guanshiduan/icon-refresh.png'
MINIAPP_ICON_DEFAULTS['merchant_refresh'] = 'beijing/guanshiduan/icon-refresh.png'
MINIAPP_ICON_DEFAULTS['merchant_gold_banner'] = 'beijing/tubiao/chengxutubiao/merchant_gold_banner.png'
MINIAPP_ICON_DEFAULTS['merchant_home_regular'] = 'beijing/shangjiaduan/home/regular_dispatch.png'
MINIAPP_ICON_DEFAULTS['merchant_home_custom'] = 'beijing/shangjiaduan/home/custom_dispatch.png'
MINIAPP_ICON_DEFAULTS['merchant_home_link'] = 'beijing/shangjiaduan/home/link_dispatch.png'
MINIAPP_ICON_DEFAULTS['merchant_home_kefu_key'] = 'beijing/shangjiaduan/home/kefu_key.png'
MINIAPP_ICON_DEFAULTS['merchant_home_kefu_list'] = 'beijing/shangjiaduan/home/kefu_list.png'
MINIAPP_ICON_DEFAULTS['merchant_home_notice'] = 'beijing/shangjiaduan/home/notice.png'
MINIAPP_ICON_DEFAULTS['merchant_home_stat_bg'] = 'beijing/shangjiaduan/stat_card_bg.png'
MINIAPP_ICON_DEFAULTS['fighter_recharge_member'] = f'{ICON_FOLDER}/fighter_recharge_member.png'
MINIAPP_ICON_DEFAULTS['fighter_recharge_deposit'] = f'{ICON_FOLDER}/fighter_recharge_deposit.png'
MINIAPP_ICON_DEFAULTS['fighter_recharge_vip_bg'] = f'{ICON_FOLDER}/fighter_recharge_vip_bg.png'
MINIAPP_ICON_DEFAULTS['fighter_mine_vip_bg'] = f'{ICON_FOLDER}/fighter_mine_vip_bg.png'
MINIAPP_ICON_DEFAULTS['fighter_mine_vip_icon'] = f'{ICON_FOLDER}/fighter_mine_vip_icon.png'
MINIAPP_ICON_DEFAULTS['fighter_deposit_bg'] = f'{ICON_FOLDER}/fighter_deposit_bg.png'
MINIAPP_ICON_DEFAULTS['fighter_deposit_hero'] = f'{ICON_FOLDER}/fighter_deposit_hero.png'
MINIAPP_ICON_DEFAULTS['fighter_order_pending'] = f'{ICON_FOLDER}/fighter_order_pending.png'
MINIAPP_ICON_DEFAULTS['fighter_order_settling'] = f'{ICON_FOLDER}/fighter_order_settling.png'
MINIAPP_ICON_DEFAULTS['fighter_order_done'] = f'{ICON_FOLDER}/fighter_order_done.png'
MINIAPP_ICON_DEFAULTS['fighter_order_closed'] = f'{ICON_FOLDER}/fighter_order_closed.png'
MINIAPP_ICON_DEFAULTS['fighter_trade_commission'] = f'{ICON_FOLDER}/fighter_trade_commission.png'
MINIAPP_ICON_DEFAULTS['fighter_trade_withdraw'] = f'{ICON_FOLDER}/fighter_trade_withdraw.png'
MINIAPP_ICON_DEFAULTS['fighter_trade_settle'] = f'{ICON_FOLDER}/fighter_trade_settle.png'
MINIAPP_ICON_DEFAULTS['fighter_trade_deduct'] = f'{ICON_FOLDER}/fighter_trade_deduct.png'
MINIAPP_ICON_DEFAULTS['fighter_trade_recharge'] = f'{ICON_FOLDER}/fighter_trade_recharge.png'
MINIAPP_ICON_DEFAULTS['fighter_invite_subordinate'] = f'{ICON_FOLDER}/fighter_invite_subordinate.png'
MINIAPP_ICON_DEFAULTS['fighter_invite_poster'] = f'{ICON_FOLDER}/fighter_invite_poster.png'
MINIAPP_ICON_DEFAULTS['fighter_invite_code'] = f'{ICON_FOLDER}/fighter_invite_code.png'
MINIAPP_ICON_DEFAULTS['fighter_kuaishou_cash'] = f'{ICON_FOLDER}/fighter_kuaishou_cash.png'
MINIAPP_ICON_DEFAULTS['fighter_follow_kuaishou'] = 'beijing/tubiao/grzx_guanzhualong.jpg'
MINIAPP_ICON_DEFAULTS['fighter_mine_copy'] = 'beijing/guanshiduan/icon-copy.png'
MINIAPP_ICON_DEFAULTS['fighter_mine_edit'] = 'beijing/dashouduan/dashoutubiaobeijing/icon-edit.jpg'
MINIAPP_ICON_DEFAULTS['fighter_mine_qiepian'] = f'{ICON_FOLDER}/fighter_mine_qiepian.png'
MINIAPP_ICON_DEFAULTS['fighter_mine_penalty_tip'] = f'{ICON_FOLDER}/fighter_mine_penalty_tip.png'
MINIAPP_ICON_DEFAULTS['fighter_mine_kefu'] = 'beijing/tubiao/grzx_kefu.jpg'
MINIAPP_ICON_DEFAULTS['fighter_mine_shangjia'] = 'beijing/tubiao/grzx_shangjia.jpg'
MINIAPP_ICON_DEFAULTS['fighter_complaint_record'] = 'beijing/guanshiduan/icon-contact.png'
MINIAPP_ICON_DEFAULTS['fighter_mine_punish'] = 'beijing/dashouduan/dashoutubiaobeijing/icon-punish.jpg'
MINIAPP_ICON_DEFAULTS['fighter_mine_medal'] = 'beijing/dashouduan/dashoutubiaobeijing/icon-medal.jpg'
MINIAPP_ICON_DEFAULTS['fighter_mine_book'] = 'beijing/dashouduan/dashoutubiaobeijing/icon-book.jpg'
MINIAPP_ICON_DEFAULTS['fighter_mine_rank'] = 'beijing/dashouduan/dashoutubiaobeijing/icon-rank.jpg'
MINIAPP_ICON_DEFAULTS['fighter_mine_invite'] = 'beijing/guanshiduan/icon-invite.png'
MINIAPP_ICON_DEFAULTS['fighter_mine_contact'] = 'beijing/guanshiduan/icon-contact.png'
MINIAPP_ICON_DEFAULTS['fighter_mine_record'] = 'beijing/guanshiduan/icon-record.png'
MINIAPP_ICON_DEFAULTS['fighter_mine_kaohe_dafen'] = 'beijing/kaohe/daofen.png'
MINIAPP_ICON_DEFAULTS['fighter_mine_kaohe_jilu'] = 'beijing/kaohe/jilu.png'
MINIAPP_ICON_DEFAULTS['fighter_mine_kaohe_zhongxin'] = 'beijing/kaohe/zhongxin.png'
MINIAPP_ICON_DEFAULTS['fighter_mine_dashou_auth'] = 'beijing/tubiao/grzx_dashou.jpg'
MINIAPP_ICON_DEFAULTS['fighter_mine_clear'] = 'beijing/tubiao/grzx_qingchu.jpg'
MINIAPP_ICON_DEFAULTS['fighter_mine_switch'] = f'{ICON_FOLDER}/fighter_mine_switch.png'
MINIAPP_ICON_DEFAULTS['mine_pindao'] = 'beijing/tubiao/grzx_pindao.jpg'
MINIAPP_ICON_DEFAULTS['cuser_mine_kefu'] = 'beijing/tubiao/grzx_kefu.jpg'
# 邀请默认用邀请图标,避免与客服同图
MINIAPP_ICON_DEFAULTS['cuser_mine_invite'] = 'beijing/guanshiduan/icon-invite.png'
# 推荐的人:用下级/人员图标,勿与关注快手同图;勿用可能不存在的 chengxutubiao 占位 png
MINIAPP_ICON_DEFAULTS['cuser_mine_invitees'] = 'beijing/guanshiduan/icon-sub.png'
MINIAPP_ICON_DEFAULTS['guanshi_poster_bg'] = 'beijing/haibaobeijing.jpg'
MINIAPP_ICON_DEFAULTS['zuzhang_poster_bg'] = 'beijing/zuzhangbeijing.jpg'
# 点单端推荐官海报:独立路径,禁止复用管事/打手背景
MINIAPP_ICON_DEFAULTS['cuser_poster_bg'] = 'beijing/haibao/cuser_poster_bg.jpg'
POSTER_BG_FIELDS = {
'guanshi_bg': 'guanshi_poster_bg',
'zuzhang_bg': 'zuzhang_poster_bg',
'cuser_bg': 'cuser_poster_bg',
}
POSTER_BG_DEFAULTS = {
'guanshi_bg': 'beijing/haibaobeijing.jpg',
'zuzhang_bg': 'beijing/zuzhangbeijing.jpg',
'cuser_bg': 'beijing/haibao/cuser_poster_bg.jpg',
}
POSTER_FOLDER = 'beijing/haibao'
POSTER_ICON_KEYS = frozenset(POSTER_BG_FIELDS.values())
# 仅后台上传后才下发给小程序(页面大背景等)
CONFIG_ONLY_DELIVERY_KEYS = frozenset([
'fighter_recharge_vip_bg',
'fighter_mine_vip_bg',
'fighter_mine_vip_icon',
'fighter_deposit_bg',
'fighter_deposit_hero',
'grab_card_pingtai_bg',
'grab_card_youzhi_bg',
'grab_card_shangjia_bg',
])
def default_icon_path(icon_key: str) -> str:
return MINIAPP_ICON_DEFAULTS.get(icon_key) or f'{ICON_FOLDER}/{icon_key}.png'
def get_miniapp_icons_dict(club_id: str) -> dict:
from config.models import ClubMiniappIcon
rows = {
r.icon_key: r
for r in ClubMiniappIcon.query.filter(club_id=club_id, is_active=True)
}
result = {}
for key, _label in MINIAPP_ICON_META:
row = rows.get(key)
path = (row.image_path if row else '') or ''
path = path.strip()
# 已上传:必须下发(含接单池卡背景 / 充值页大图)
if row and path and row.is_uploaded:
result[key] = path
continue
if key in CONFIG_ONLY_DELIVERY_KEYS:
# 兼容:有非默认路径但 is_uploaded 标志异常时仍下发
default_path = default_icon_path(key)
if row and path and path != default_path:
result[key] = path
continue
if row and path:
result[key] = path
else:
result[key] = default_icon_path(key)
return result
def get_pindao_payload(club_id: str) -> dict:
from config.models import ClubPindao, ClubPindaoImage
row = ClubPindao.query.filter(club_id=club_id, is_active=True).first()
if not row:
return {'channel_no': '', 'title': '频道', 'images': []}
images = [
img.image_path
for img in ClubPindaoImage.query.filter(club_id=club_id).order_by('sort_order', 'id')
if img.image_path
]
return {
'channel_no': row.channel_no or '',
'title': row.title or '频道',
'images': images,
}
def get_poster_payload(club_id: str) -> dict:
"""各俱乐部推广海报背景相对路径(后台上传后覆盖默认)。"""
from config.models import ClubMiniappIcon
rows = {
r.icon_key: r
for r in ClubMiniappIcon.query.filter(club_id=club_id, is_active=True)
}
result = {}
for field, icon_key in POSTER_BG_FIELDS.items():
default = POSTER_BG_DEFAULTS[field]
row = rows.get(icon_key)
if row and row.is_uploaded and (row.image_path or '').strip():
result[field] = row.image_path.strip()
elif field == 'cuser_bg':
# 点单端未上传时不回落到管事背景,避免错图
result[field] = (row.image_path.strip() if row and (row.image_path or '').strip() else '') or default
else:
result[field] = default
return result
def build_miniapp_assets_payload(request) -> dict:
club_id = _display_club_id(request)
return {
'club_id': club_id,
'icons': get_miniapp_icons_dict(club_id),
'pindao': get_pindao_payload(club_id),
'poster': get_poster_payload(club_id),
}
def seed_miniapp_icons_for_club(club_id: str, overwrite: bool = False) -> int:
from config.models import ClubMiniappIcon
created = 0
for key, label in MINIAPP_ICON_META:
path = default_icon_path(key)
row = ClubMiniappIcon.query.filter(club_id=club_id, icon_key=key).first()
if row:
if overwrite:
row.label = label
row.image_path = path
row.is_active = True
row.save(update_fields=['label', 'image_path', 'is_active', 'UpdateTime'])
continue
ClubMiniappIcon.query.create(
club_id=club_id,
icon_key=key,
label=label,
image_path=path,
is_active=True,
)
created += 1
return created
def copy_miniapp_assets(template_club_id: str, new_club_id: str):
from config.models import ClubMiniappIcon, ClubPindao, ClubPindaoImage
for row in ClubMiniappIcon.query.filter(club_id=template_club_id):
if ClubMiniappIcon.query.filter(club_id=new_club_id, icon_key=row.icon_key).exists():
continue
ClubMiniappIcon.query.create(
club_id=new_club_id,
icon_key=row.icon_key,
label=row.label,
image_path=row.image_path,
description=row.description,
is_active=row.is_active,
is_uploaded=row.is_uploaded,
)
src_pindao = ClubPindao.query.filter(club_id=template_club_id).first()
if not src_pindao or ClubPindao.query.filter(club_id=new_club_id).exists():
return
new_pindao = ClubPindao.query.create(
club_id=new_club_id,
channel_no=src_pindao.channel_no,
title=src_pindao.title,
is_active=src_pindao.is_active,
)
for img in ClubPindaoImage.query.filter(club_id=template_club_id).order_by('sort_order', 'id'):
ClubPindaoImage.query.create(
club_id=new_club_id,
pindao=new_pindao,
image_path=img.image_path,
sort_order=img.sort_order,
)