mirror of
https://github.com/primedigitaltech/market-assistant.git
synced 2026-07-22 08:01:34 +08:00
feat(dataset): 店铺改为下拉选择,接口 shop 精确匹配
摘要增加 shop_options;筛选参数由 shop_q 改为 shop;合并表匹配 shop_name 或 detail_shop_name。 Made-with: Cursor
This commit is contained in:
parent
5ea55233b1
commit
f21bfbe02b
@ -1,4 +1,4 @@
|
||||
"""库内数据浏览 API:排序、价格、类目(§5 矩阵)与店铺名筛选。"""
|
||||
"""库内数据浏览 API:排序、价格、类目(§5 矩阵)与店铺(精确)筛选。"""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
@ -61,9 +61,9 @@ def report_group_from_request(request: Request) -> str:
|
||||
return (request.query_params.get("report_group") or "").strip()
|
||||
|
||||
|
||||
def shop_q_from_request(request: Request) -> str:
|
||||
"""店铺名模糊匹配;查询参数 ``shop_q``。"""
|
||||
return (request.query_params.get("shop_q") or "").strip()
|
||||
def shop_from_request(request: Request) -> str:
|
||||
"""店铺名精确匹配(与摘要 ``shop_options`` 中某项一致);查询参数 ``shop``。"""
|
||||
return (request.query_params.get("shop") or "").strip()
|
||||
|
||||
|
||||
def detail_category_q_from_request(request: Request) -> str:
|
||||
@ -73,7 +73,7 @@ def detail_category_q_from_request(request: Request) -> str:
|
||||
def filter_echo(
|
||||
*,
|
||||
report_group: str,
|
||||
shop_q: str,
|
||||
shop: str,
|
||||
price_min: float | None,
|
||||
price_max: float | None,
|
||||
detail_category_q: str,
|
||||
@ -82,7 +82,7 @@ def filter_echo(
|
||||
) -> dict[str, Any]:
|
||||
return {
|
||||
"report_group": report_group or None,
|
||||
"shop_q": shop_q or None,
|
||||
"shop": shop or None,
|
||||
"price_min": price_min,
|
||||
"price_max": price_max,
|
||||
"detail_category_q": detail_category_q or None,
|
||||
@ -95,9 +95,9 @@ def apply_search_filters(qs: QuerySet, request: Request) -> QuerySet:
|
||||
rg = report_group_from_request(request)
|
||||
if rg:
|
||||
qs = qs.filter(Q(matrix_group_label=rg) | Q(leaf_category=rg))
|
||||
sq = shop_q_from_request(request)
|
||||
if sq:
|
||||
qs = qs.filter(shop_name__icontains=sq)
|
||||
sp = shop_from_request(request)
|
||||
if sp:
|
||||
qs = qs.filter(shop_name=sp)
|
||||
pmin, pmax = price_bounds_from_request(request)
|
||||
if pmin is not None:
|
||||
qs = qs.filter(price_value__gte=pmin)
|
||||
@ -131,9 +131,9 @@ def apply_detail_filters(qs: QuerySet, request: Request) -> QuerySet:
|
||||
rg = report_group_from_request(request)
|
||||
if rg:
|
||||
qs = qs.filter(matrix_group_label=rg)
|
||||
sq = shop_q_from_request(request)
|
||||
if sq:
|
||||
qs = qs.filter(detail_shop_name__icontains=sq)
|
||||
sp = shop_from_request(request)
|
||||
if sp:
|
||||
qs = qs.filter(detail_shop_name=sp)
|
||||
q = detail_category_q_from_request(request)
|
||||
if q:
|
||||
qs = qs.filter(detail_category_path__icontains=q)
|
||||
@ -170,11 +170,9 @@ def apply_merged_filters(qs: QuerySet, request: Request) -> QuerySet:
|
||||
rg = report_group_from_request(request)
|
||||
if rg:
|
||||
qs = qs.filter(matrix_group_label=rg)
|
||||
sq = shop_q_from_request(request)
|
||||
if sq:
|
||||
qs = qs.filter(
|
||||
Q(shop_name__icontains=sq) | Q(detail_shop_name__icontains=sq)
|
||||
)
|
||||
sp = shop_from_request(request)
|
||||
if sp:
|
||||
qs = qs.filter(Q(shop_name=sp) | Q(detail_shop_name=sp))
|
||||
q = detail_category_q_from_request(request)
|
||||
if q:
|
||||
qs = qs.filter(detail_category_path__icontains=q)
|
||||
|
||||
@ -32,7 +32,7 @@ from .dataset_api import (
|
||||
parse_sort_meta,
|
||||
price_bounds_from_request,
|
||||
report_group_from_request,
|
||||
shop_q_from_request,
|
||||
shop_from_request,
|
||||
)
|
||||
from .dataset_nonempty import (
|
||||
comment_columns_for_api,
|
||||
@ -777,6 +777,48 @@ def _detail_category_path_options(job: PipelineJob) -> list[str]:
|
||||
)
|
||||
|
||||
|
||||
def _shop_options_for_job(job: PipelineJob) -> list[str]:
|
||||
"""任务内各表出现的店铺名去重排序(搜索 shop_name、商详/宽表店铺列)。"""
|
||||
names: set[str] = set()
|
||||
for v in (
|
||||
JdJobSearchRow.objects.filter(job=job)
|
||||
.exclude(shop_name="")
|
||||
.values_list("shop_name", flat=True)
|
||||
.distinct()
|
||||
):
|
||||
t = str(v).strip()
|
||||
if t:
|
||||
names.add(t)
|
||||
for v in (
|
||||
JdJobDetailRow.objects.filter(job=job)
|
||||
.exclude(detail_shop_name="")
|
||||
.values_list("detail_shop_name", flat=True)
|
||||
.distinct()
|
||||
):
|
||||
t = str(v).strip()
|
||||
if t:
|
||||
names.add(t)
|
||||
for v in (
|
||||
JdJobMergedRow.objects.filter(job=job)
|
||||
.exclude(shop_name="")
|
||||
.values_list("shop_name", flat=True)
|
||||
.distinct()
|
||||
):
|
||||
t = str(v).strip()
|
||||
if t:
|
||||
names.add(t)
|
||||
for v in (
|
||||
JdJobMergedRow.objects.filter(job=job)
|
||||
.exclude(detail_shop_name="")
|
||||
.values_list("detail_shop_name", flat=True)
|
||||
.distinct()
|
||||
):
|
||||
t = str(v).strip()
|
||||
if t:
|
||||
names.add(t)
|
||||
return sorted(names)
|
||||
|
||||
|
||||
class JobDatasetSummaryView(APIView):
|
||||
"""任务在库中的搜索/详情/评价行数(入库后可用)。"""
|
||||
|
||||
@ -796,6 +838,7 @@ class JobDatasetSummaryView(APIView):
|
||||
"comment_columns": comment_columns_for_api(job),
|
||||
"merged_columns": merged_columns_for_api(job),
|
||||
"category_options": _report_group_options_for_job(job),
|
||||
"shop_options": _shop_options_for_job(job),
|
||||
"detail_category_path_options": _detail_category_path_options(job),
|
||||
"dataset_sort_help": {
|
||||
"search": sorted(SEARCH_SORT_FIELDS),
|
||||
@ -814,7 +857,7 @@ class JobDatasetSearchView(APIView):
|
||||
sort, desc = parse_sort_meta(request)
|
||||
sort_eff = sort if sort in SEARCH_SORT_FIELDS else "row_index"
|
||||
rg = report_group_from_request(request)
|
||||
sq = shop_q_from_request(request)
|
||||
sp = shop_from_request(request)
|
||||
pmin, pmax = price_bounds_from_request(request)
|
||||
dcq = detail_category_q_from_request(request)
|
||||
qs = JdJobSearchRow.objects.filter(job=job)
|
||||
@ -830,7 +873,7 @@ class JobDatasetSearchView(APIView):
|
||||
"page_size": page_size,
|
||||
"filters": filter_echo(
|
||||
report_group=rg,
|
||||
shop_q=sq,
|
||||
shop=sp,
|
||||
price_min=pmin,
|
||||
price_max=pmax,
|
||||
detail_category_q=dcq,
|
||||
@ -849,7 +892,7 @@ class JobDatasetDetailView(APIView):
|
||||
sort, desc = parse_sort_meta(request)
|
||||
sort_eff = sort if sort in DETAIL_SORT_FIELDS else "row_index"
|
||||
rg = report_group_from_request(request)
|
||||
sq = shop_q_from_request(request)
|
||||
sp = shop_from_request(request)
|
||||
pmin, pmax = price_bounds_from_request(request)
|
||||
dcq = detail_category_q_from_request(request)
|
||||
qs = JdJobDetailRow.objects.filter(job=job)
|
||||
@ -865,7 +908,7 @@ class JobDatasetDetailView(APIView):
|
||||
"page_size": page_size,
|
||||
"filters": filter_echo(
|
||||
report_group=rg,
|
||||
shop_q=sq,
|
||||
shop=sp,
|
||||
price_min=pmin,
|
||||
price_max=pmax,
|
||||
detail_category_q=dcq,
|
||||
@ -906,7 +949,7 @@ class JobDatasetMergedView(APIView):
|
||||
sort, desc = parse_sort_meta(request)
|
||||
sort_eff = sort if sort in MERGED_SORT_FIELDS else "row_index"
|
||||
rg = report_group_from_request(request)
|
||||
sq = shop_q_from_request(request)
|
||||
sp = shop_from_request(request)
|
||||
pmin, pmax = price_bounds_from_request(request)
|
||||
dcq = detail_category_q_from_request(request)
|
||||
qs = JdJobMergedRow.objects.filter(job=job)
|
||||
@ -922,7 +965,7 @@ class JobDatasetMergedView(APIView):
|
||||
"page_size": page_size,
|
||||
"filters": filter_echo(
|
||||
report_group=rg,
|
||||
shop_q=sq,
|
||||
shop=sp,
|
||||
price_min=pmin,
|
||||
price_max=pmax,
|
||||
detail_category_q=dcq,
|
||||
|
||||
@ -42,7 +42,8 @@ const sortField = ref('row_index')
|
||||
const sortOrder = ref('asc')
|
||||
/** 类目(§5 矩阵),对应接口参数 report_group */
|
||||
const reportGroup = ref('')
|
||||
const shopQ = ref('')
|
||||
/** 店铺名精确筛选,对应接口参数 shop;选项来自摘要 shop_options */
|
||||
const selectedShop = ref('')
|
||||
const priceMin = ref('')
|
||||
const priceMax = ref('')
|
||||
const detailCategoryQ = ref('')
|
||||
@ -72,6 +73,7 @@ const sortOptions = computed(() => {
|
||||
})
|
||||
|
||||
const categoryOptions = computed(() => summary.value?.category_options || [])
|
||||
const shopOptions = computed(() => summary.value?.shop_options || [])
|
||||
|
||||
const displayColumns = computed(() => {
|
||||
const s = summary.value
|
||||
@ -163,7 +165,7 @@ async function refreshList() {
|
||||
sort: sortField.value,
|
||||
order: sortOrder.value,
|
||||
reportGroup: reportGroup.value.trim(),
|
||||
shopQ: shopQ.value.trim(),
|
||||
shop: selectedShop.value.trim(),
|
||||
priceMin: priceMin.value,
|
||||
priceMax: priceMax.value,
|
||||
detailCategoryQ: detailCategoryQ.value.trim(),
|
||||
@ -198,7 +200,7 @@ watch(
|
||||
sortField.value = 'row_index'
|
||||
sortOrder.value = 'asc'
|
||||
reportGroup.value = ''
|
||||
shopQ.value = ''
|
||||
selectedShop.value = ''
|
||||
priceMin.value = ''
|
||||
priceMax.value = ''
|
||||
detailCategoryQ.value = ''
|
||||
@ -217,7 +219,7 @@ watch(tab, () => {
|
||||
sortField.value = 'row_index'
|
||||
sortOrder.value = 'asc'
|
||||
reportGroup.value = ''
|
||||
shopQ.value = ''
|
||||
selectedShop.value = ''
|
||||
priceMin.value = ''
|
||||
priceMax.value = ''
|
||||
detailCategoryQ.value = ''
|
||||
@ -228,7 +230,7 @@ watch(
|
||||
sortField,
|
||||
sortOrder,
|
||||
reportGroup,
|
||||
shopQ,
|
||||
selectedShop,
|
||||
priceMin,
|
||||
priceMax,
|
||||
detailCategoryQ,
|
||||
@ -248,7 +250,7 @@ watch(
|
||||
sortField,
|
||||
sortOrder,
|
||||
reportGroup,
|
||||
shopQ,
|
||||
selectedShop,
|
||||
priceMin,
|
||||
priceMax,
|
||||
detailCategoryQ,
|
||||
@ -434,20 +436,18 @@ async function runExport(format) {
|
||||
</select>
|
||||
</label>
|
||||
<label class="filter-item">
|
||||
类目(§5 矩阵)
|
||||
类目
|
||||
<select v-model="reportGroup" class="filter-select wide">
|
||||
<option value="">全部</option>
|
||||
<option v-for="g in categoryOptions" :key="g" :value="g">{{ g }}</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="filter-item">
|
||||
店铺名包含
|
||||
<input
|
||||
v-model="shopQ"
|
||||
type="search"
|
||||
class="filter-input wide"
|
||||
placeholder="模糊匹配店铺名"
|
||||
/>
|
||||
店铺
|
||||
<select v-model="selectedShop" class="filter-select wide">
|
||||
<option value="">全部</option>
|
||||
<option v-for="s in shopOptions" :key="s" :value="s">{{ s }}</option>
|
||||
</select>
|
||||
</label>
|
||||
<template v-if="tab === 'detail' || tab === 'merged'">
|
||||
<label class="filter-item">
|
||||
|
||||
@ -209,9 +209,9 @@ export function jobDatasetPageUrl(jobId, kind, page = 1, pageSize = 50, opts = {
|
||||
o.reportGroup ?? o.report_group ?? o.categoryNormId ?? o.category_norm_id
|
||||
if (rg !== undefined && rg !== null && String(rg).trim() !== '')
|
||||
p.set('report_group', String(rg).trim())
|
||||
const shopQ = o.shopQ ?? o.shop_q
|
||||
if (shopQ !== undefined && shopQ !== null && String(shopQ).trim() !== '')
|
||||
p.set('shop_q', String(shopQ).trim())
|
||||
const shop = o.shop ?? o.shop_name ?? o.shopQ ?? o.shop_q
|
||||
if (shop !== undefined && shop !== null && String(shop).trim() !== '')
|
||||
p.set('shop', String(shop).trim())
|
||||
const pmin = o.priceMin ?? o.price_min
|
||||
if (pmin !== undefined && pmin !== null && String(pmin).trim() !== '')
|
||||
p.set('price_min', String(pmin).trim())
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user