merge: 同步 main 最新到 dev(接单平台店铺筛选)
This commit is contained in:
@@ -453,6 +453,12 @@ export type ListInput = {
|
||||
/** 后台待办跳转时按工单主键精确定位 */
|
||||
workOrderId?: number
|
||||
keyword?: string
|
||||
/**
|
||||
* 后台按店铺筛选(精确匹配,可多选)。
|
||||
* 来源是 `orders.shop_name`:工单只存 `order_id`,`material.source.shopName` 只是货源快照,
|
||||
* 两者并不等价,所以这里统一按订单归属店铺过滤。
|
||||
*/
|
||||
shopNames?: string[]
|
||||
/** 打手类型筛选:internal 内部打手 / external 外部打手 */
|
||||
workerType?: string
|
||||
/** 后台打手列表按主键精确定位。 */
|
||||
|
||||
@@ -347,6 +347,7 @@ export async function listWorkOrders({
|
||||
status = '',
|
||||
workOrderId = 0,
|
||||
keyword = '',
|
||||
shopNames = [],
|
||||
workerId = 0,
|
||||
workerLevelIds = [],
|
||||
categoryId = 0,
|
||||
@@ -370,6 +371,7 @@ export async function listWorkOrders({
|
||||
statuses: effectiveStatuses,
|
||||
workOrderId,
|
||||
keyword,
|
||||
shopNames,
|
||||
workerId,
|
||||
workerLevelIds,
|
||||
...(resolvedWorkerIds ? { workerIds: resolvedWorkerIds } : {}),
|
||||
@@ -455,6 +457,26 @@ export async function listWorkOrders({
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 后台「店铺」筛选的候选值。
|
||||
*
|
||||
* 口径是「订单归属店铺」(orders.shop_name),只列真正挂在工单上的店铺——
|
||||
* 订单里存在但没同步出工单的店铺如果出现在下拉里,选了必然是空列表。
|
||||
* 固定全量去重、不随其它筛选变化,避免用户选完店铺后候选自己缩水。
|
||||
*/
|
||||
export async function listWorkOrderShopNames(): Promise<string[]> {
|
||||
const result = await query<{ shop_name: string }>(
|
||||
`SELECT DISTINCT BTRIM(o.shop_name) AS shop_name
|
||||
FROM work_orders wo
|
||||
INNER JOIN orders o ON o.id = wo.order_id
|
||||
WHERE BTRIM(o.shop_name) <> ''
|
||||
ORDER BY shop_name ASC`,
|
||||
)
|
||||
return result.rows
|
||||
.map((row) => String(row.shop_name || '').trim())
|
||||
.filter((shopName) => shopName.length > 0)
|
||||
}
|
||||
|
||||
/** 只返回筛选范围总数,不加载工单 JSON、取消事件和当前页关联字段。 */
|
||||
export async function countWorkOrders(input: ListInput = {}): Promise<number> {
|
||||
const resolvedWorkerIds = await resolveWorkerLevelFilterIds(input.workerLevelIds)
|
||||
@@ -1077,6 +1099,7 @@ export function buildWorkOrderWhere({
|
||||
statuses = [],
|
||||
workOrderId = 0,
|
||||
keyword = '',
|
||||
shopNames = [],
|
||||
workerId = 0,
|
||||
workerLevelIds = [],
|
||||
workerIds,
|
||||
@@ -1275,6 +1298,22 @@ export function buildWorkOrderWhere({
|
||||
filters.push(`${workOrderSearchExpression()} LIKE LOWER($${params.length})`)
|
||||
}
|
||||
}
|
||||
|
||||
const normalizedShopNames = [
|
||||
...new Set(shopNames.map((item) => String(item || '').trim()).filter(Boolean)),
|
||||
]
|
||||
if (normalizedShopNames.length > 0) {
|
||||
// 店铺口径是「订单归属店铺」:工单只存 order_id,必须回查 orders.shop_name。
|
||||
// 下拉选项本身就是真实值,这里做精确匹配,并用 BTRIM 兼容历史脏数据的前后空格
|
||||
// (表达式索引见 103_work_order_shop_filter_index.sql)。
|
||||
params.push(normalizedShopNames)
|
||||
filters.push(`wo.order_id IN (
|
||||
SELECT shop_order.id
|
||||
FROM orders shop_order
|
||||
WHERE BTRIM(shop_order.shop_name) = ANY($${params.length}::text[])
|
||||
)`)
|
||||
}
|
||||
|
||||
if (workerId) {
|
||||
params.push(workerId)
|
||||
filters.push(`(
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
getAdminWorkOrderEvents,
|
||||
getAdminWorkOrderSharing,
|
||||
listAdminMockCustomShops,
|
||||
listAdminWorkOrderShopNames,
|
||||
listAdminWorkOrders,
|
||||
pinAdminWorkOrder,
|
||||
publishAdminWorkOrder,
|
||||
@@ -47,6 +48,17 @@ router.get(
|
||||
}),
|
||||
)
|
||||
|
||||
// 「店铺」筛选下拉:固定全量候选,不随列表筛选变化(前端 Select 自带搜索过滤)。
|
||||
router.get(
|
||||
'/worker-platform/orders/shop-options',
|
||||
requireAdminPermission('worker_order.view'),
|
||||
createJsonHandler(() => listAdminWorkOrderShopNames(), {
|
||||
successMessage: 'ok',
|
||||
errorMessage: '读取店铺筛选选项失败',
|
||||
scope: '[admin/worker-platform/orders/shop-options]',
|
||||
}),
|
||||
)
|
||||
|
||||
router.get(
|
||||
'/worker-platform/orders/mock/custom-shops',
|
||||
requireAdminRoles(['admin', 'operator', 'support']),
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
listConsignmentInventoriesByIds,
|
||||
listWorkOrderEventsByOrderId,
|
||||
listWorkOrderSharesByOrderIds,
|
||||
listWorkOrderShopNames,
|
||||
listWorkOrders,
|
||||
sumPendingUnfreezeByOrderIds,
|
||||
type ConsignmentInventoryRow,
|
||||
@@ -49,6 +50,7 @@ function buildWorkOrderStatisticsCacheKey(filters: JsonObject): string {
|
||||
filters.status || '',
|
||||
filters.workOrderId || 0,
|
||||
filters.keyword || '',
|
||||
filters.shopNames || [],
|
||||
filters.workerSharingId || 0,
|
||||
filters.workerId || 0,
|
||||
filters.workerLevelIds || [],
|
||||
@@ -92,6 +94,7 @@ export function resolveAdminWorkOrderListFilters(query: JsonObject = {}): ListIn
|
||||
workOrderId: 0,
|
||||
categoryId: 0,
|
||||
keyword,
|
||||
shopNames: [],
|
||||
workerSharingId: 0,
|
||||
workerLevelIds: [],
|
||||
vipEvidencePending: false,
|
||||
@@ -106,6 +109,8 @@ export function resolveAdminWorkOrderListFilters(query: JsonObject = {}): ListIn
|
||||
.map(normalizeOptionalId)
|
||||
.filter((levelId): levelId is number => levelId !== null)
|
||||
const statuses = normalizeStatuses(query.status)
|
||||
// 店铺来自 orders.shop_name,前端按逗号拼串传参,复用同一套数组解析(去重 + trim)。
|
||||
const shopNames = normalizeStatuses(query.shopNames ?? query.shop_names)
|
||||
const vipEvidencePending = normalizeBoolean(query.vipEvidencePending, false)
|
||||
const createdFrom = normalizeDateString(query.createdFrom)
|
||||
const createdTo = normalizeDateString(query.createdTo)
|
||||
@@ -116,6 +121,7 @@ export function resolveAdminWorkOrderListFilters(query: JsonObject = {}): ListIn
|
||||
workOrderId,
|
||||
categoryId,
|
||||
keyword,
|
||||
shopNames,
|
||||
workerSharingId: workerId,
|
||||
workerLevelIds,
|
||||
vipEvidencePending,
|
||||
@@ -182,3 +188,13 @@ export async function listAdminWorkOrders(query: JsonObject = {}) {
|
||||
statistics,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 后台工单「店铺」筛选的下拉候选。
|
||||
*
|
||||
* 固定全量去重(不随状态/日期等筛选变化),下拉里带搜索框由前端过滤即可;
|
||||
* 数据口径见 repository 的 listWorkOrderShopNames(orders.shop_name,精确匹配)。
|
||||
*/
|
||||
export async function listAdminWorkOrderShopNames(): Promise<{ items: string[] }> {
|
||||
return { items: await listWorkOrderShopNames() }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user