Files
Django/jituan/services/club_shangpin_leixing.py

240 lines
8.1 KiB
Python
Raw 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.
"""俱乐部商品类型上架与图标(全局类型由集团定义)。"""
import logging
import os
import uuid
from django.db import transaction
from jituan.constants import CLUB_ID_DEFAULT, DATA_SCOPE_ALL
from jituan.models import ClubShangpinLeixingConfig
from jituan.services.club_context import (
club_id_for_write,
resolve_club_id_from_request,
resolve_club_scope,
)
from products.models import ShangpinLeixing
from utils.oss_utils import delete_from_oss, upload_to_oss, validate_image
logger = logging.getLogger(__name__)
CLUB_WRITE_SCOPE_MSG = '当前为集团汇总视图,请先在顶栏切换到具体俱乐部后再操作'
CLUB_READ_SCOPE_MSG = '请先在顶栏切换到具体小程序/俱乐部后再操作'
def club_read_blocked(request):
return resolve_club_scope(request) == DATA_SCOPE_ALL
def club_write_blocked(request):
return resolve_club_scope(request) == DATA_SCOPE_ALL
def _config_map(club_id):
return {
r.leixing_id: r
for r in ClubShangpinLeixingConfig.query.filter(club_id=club_id)
}
def _resolve_tupian(leixing, cfg):
if cfg and cfg.tupian_url:
return cfg.tupian_url
return leixing.tupian_url or ''
def _is_visible(club_id, leixing_id, cfg):
if cfg is not None:
return bool(cfg.is_enabled)
return club_id == CLUB_ID_DEFAULT
def build_leixing_item(leixing, cfg=None):
item = {
'id': leixing.id,
'jieshao': leixing.jieshao or '',
'tupian_url': _resolve_tupian(leixing, cfg),
'global_tupian_url': leixing.tupian_url or '',
'yaoqiuleixing': leixing.yaoqiuleixing,
'huiyuan_id': leixing.huiyuan_id,
'yongjin': float(leixing.yongjin) if leixing.yongjin else None,
'paixu': (cfg.paixu if cfg and cfg.paixu else leixing.paixu) or 0,
'shenhezhuangtai': leixing.shenhezhuangtai or 1,
'bankuai_id': leixing.bankuai_id,
'club_enabled': True,
'icon_source': 'club' if cfg and cfg.tupian_url else 'global',
# 新字段旧客户端忽略1/true 时该类型订单强制展示价格
'show_price': bool(getattr(cfg, 'show_price', False)) if cfg else False,
}
return item
def build_club_leixing_list_for_api(club_id):
"""小程序/抢单池:仅返回本俱乐部已上架的类型,图标优先俱乐部配置。"""
cfg_map = _config_map(club_id)
rows = []
for lx in ShangpinLeixing.query.exclude(shenhezhuangtai=2):
cfg = cfg_map.get(lx.id)
if not _is_visible(club_id, lx.id, cfg):
continue
rows.append(build_leixing_item(lx, cfg))
rows.sort(key=lambda x: (-int(x['paixu'] or 0), x['id']))
return rows
def build_club_leixing_admin_list(request):
if club_read_blocked(request):
return {
'club_id': resolve_club_id_from_request(request),
'list': [],
'scope': resolve_club_scope(request),
'scope_error': CLUB_READ_SCOPE_MSG,
}
club_id = resolve_club_id_from_request(request)
cfg_map = _config_map(club_id)
enabled = []
for lx in ShangpinLeixing.query.exclude(shenhezhuangtai=2).order_by('-paixu', 'id'):
cfg = cfg_map.get(lx.id)
if not _is_visible(club_id, lx.id, cfg):
continue
item = build_leixing_item(lx, cfg)
item['club_enabled'] = True
enabled.append(item)
enabled.sort(key=lambda x: (-int(x['paixu'] or 0), x['id']))
return {
'club_id': club_id,
'list': enabled,
'scope': resolve_club_scope(request),
}
def build_club_leixing_catalog(request):
if club_read_blocked(request):
return {
'club_id': resolve_club_id_from_request(request),
'catalog': [],
'scope_error': CLUB_READ_SCOPE_MSG,
}
club_id = resolve_club_id_from_request(request)
cfg_map = _config_map(club_id)
catalog = []
for lx in ShangpinLeixing.query.exclude(shenhezhuangtai=2).order_by('-paixu', 'id'):
cfg = cfg_map.get(lx.id)
if _is_visible(club_id, lx.id, cfg):
continue
catalog.append({
'id': lx.id,
'jieshao': lx.jieshao or '',
'tupian_url': lx.tupian_url or '',
'bankuai_id': lx.bankuai_id,
})
return {'club_id': club_id, 'catalog': catalog}
def enable_leixing_for_club(request, leixing_id, paixu=0):
if club_write_blocked(request):
return None, CLUB_WRITE_SCOPE_MSG
club_id = club_id_for_write(request)
try:
lx = ShangpinLeixing.query.get(id=leixing_id)
except ShangpinLeixing.DoesNotExist:
return None, '商品类型不存在'
if lx.shenhezhuangtai == 2:
return None, '该类型为审核专用,不可上架'
row, _ = ClubShangpinLeixingConfig.query.get_or_create(
club_id=club_id,
leixing_id=leixing_id,
defaults={'is_enabled': True, 'paixu': int(paixu or lx.paixu or 0)},
)
row.is_enabled = True
if paixu:
row.paixu = int(paixu)
row.save(update_fields=['is_enabled', 'paixu', 'UpdateTime'])
return {'leixing_id': leixing_id}, None
def disable_leixing_for_club(request, leixing_id):
if club_write_blocked(request):
return None, CLUB_WRITE_SCOPE_MSG
club_id = club_id_for_write(request)
row, _ = ClubShangpinLeixingConfig.query.get_or_create(
club_id=club_id,
leixing_id=leixing_id,
defaults={'is_enabled': False},
)
row.is_enabled = False
row.save(update_fields=['is_enabled', 'UpdateTime'])
return {'leixing_id': leixing_id}, None
def set_leixing_show_price(request, leixing_id, show_price):
"""按俱乐部商品类型开关:是否强制展示订单价格。"""
if club_write_blocked(request):
return None, CLUB_WRITE_SCOPE_MSG
club_id = club_id_for_write(request)
try:
lx = ShangpinLeixing.query.get(id=leixing_id)
except ShangpinLeixing.DoesNotExist:
return None, '商品类型不存在'
row, _ = ClubShangpinLeixingConfig.query.get_or_create(
club_id=club_id,
leixing_id=int(leixing_id),
defaults={
'is_enabled': club_id == CLUB_ID_DEFAULT,
'paixu': int(lx.paixu or 0),
'show_price': False,
},
)
row.show_price = bool(show_price)
row.save(update_fields=['show_price', 'UpdateTime'])
return {
'leixing_id': int(leixing_id),
'show_price': bool(row.show_price),
}, None
def upload_leixing_icon(request, leixing_id, file_obj):
if club_write_blocked(request):
return None, CLUB_WRITE_SCOPE_MSG
club_id = club_id_for_write(request)
if not ShangpinLeixing.query.filter(id=leixing_id).exists():
return None, '商品类型不存在'
ok, msg = validate_image(file_obj)
if not ok:
return None, msg
ext = os.path.splitext(file_obj.name)[1].lower() or '.png'
if ext not in ('.png', '.jpg', '.jpeg', '.webp'):
ext = '.png'
oss_path = f'club/{club_id}/miniapp/shangpinleixing/{leixing_id}_{uuid.uuid4().hex}{ext}'
row, _ = ClubShangpinLeixingConfig.query.get_or_create(
club_id=club_id,
leixing_id=leixing_id,
defaults={'is_enabled': club_id == CLUB_ID_DEFAULT},
)
if row.tupian_url and row.tupian_url != oss_path:
delete_from_oss(row.tupian_url)
if not upload_to_oss(file_obj, oss_path):
return None, '图片上传失败'
row.tupian_url = oss_path
row.save(update_fields=['tupian_url', 'UpdateTime'])
return {'leixing_id': leixing_id, 'tupian_url': oss_path}, None
def copy_club_shangpin_leixing_config(template_club_id, new_club_id):
for row in ClubShangpinLeixingConfig.query.filter(club_id=template_club_id):
if ClubShangpinLeixingConfig.query.filter(
club_id=new_club_id, leixing_id=row.leixing_id,
).exists():
continue
ClubShangpinLeixingConfig.query.create(
club_id=new_club_id,
leixing_id=row.leixing_id,
is_enabled=row.is_enabled,
tupian_url='', # 新俱乐部需自行上传图标,不复制 OSS 路径
paixu=row.paixu,
show_price=bool(getattr(row, 'show_price', False)),
)