fix(supply-chain): 修复限价管理合并入 mainline 后的 18 处类型错误(type-check 0 错误)
提交自身的错误: - breach-orders/route.ts:filtersSchema 去掉重复的 platform / shopName 键(保留带长度上限的一份) - supply-chain-breach-order.service.ts:BreachOrderListFilters 去掉重复声明的 platform / shopName - BreachOrderTab.tsx:MultiFilterChip 的 field 联合类型补齐店铺款式编码/店铺商品编码/申诉原因/平台/店铺(与 filter-options 接口支持的字段一致) - breach-orders/[id]/route.ts 与 breach-orders/route.ts:多结果分支的处理器统一声明为 handleRequest<unknown>(…): Promise<NextResponse<unknown>>, 与既有 reference-image-estimate 路由同一写法,解决 ctx.user 可选态与返回形状不匹配 补齐缺失的配套改动: - errors.ts:新增 ErrorCode.RESOURCE_NOT_FOUND(含 ERROR_MESSAGES 与 HTTP 404 映射) - ProductArchiveFilters:补 materialLevel / inspectionReport 两个筛选项类型(查询逻辑此前已随功能提交) - bom-costing-multimodal-gateway.ts:BomCostingMultimodalOperation 增加 IMAGE_EXTRACTION(破价申诉凭证识别用), breach-evidence.service 对可空的 providerConfig 加空值保护 - supply-chain-product-archive-select.ts:去掉重复的 sabcRating 选择项 - supply-chain-product-archive.repository.ts:storageType 候选值已在专用分支处理,去掉恒为 false 的重复判断 验证:git 干净检出后 npm run type-check(next typegen + tsc --noEmit)0 错误; breach/supply-chain/permission 相关 230 个测试套件 1650 用例通过,失败 7 个套件均为 mainline 原有问题。
This commit is contained in:
parent
62452b6343
commit
7fc43502fd
@ -1,4 +1,4 @@
|
||||
import { NextRequest } from 'next/server'
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { ErrorCode } from '@/server/core/errors'
|
||||
import { handleRequest, jsonError, jsonSuccess } from '@/server/core/response'
|
||||
@ -46,7 +46,8 @@ export async function GET(request: NextRequest, context: { params: Promise<{ id:
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest, context: { params: Promise<{ id: string }> }) {
|
||||
return handleRequest(request, async (ctx) => {
|
||||
// 申诉与审核返回不同结果形状,统一按 unknown 返回(与既有路由一致)。
|
||||
return handleRequest<unknown>(request, async (ctx): Promise<NextResponse<unknown>> => {
|
||||
const { id } = await context.params
|
||||
const parsed = actionSchema.safeParse(await request.json().catch(() => null))
|
||||
if (!parsed.success) {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { NextRequest } from 'next/server'
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { ErrorCode } from '@/server/core/errors'
|
||||
import { handleRequest, jsonError, jsonSuccess } from '@/server/core/response'
|
||||
@ -13,8 +13,8 @@ const listQuerySchema = z.object({
|
||||
|
||||
const filtersSchema = z.object({
|
||||
status: z.array(z.string()).optional(),
|
||||
platform: z.array(z.string()).optional(),
|
||||
shopName: z.array(z.string()).optional(),
|
||||
platform: z.array(z.string().max(40)).max(50).optional(),
|
||||
shopName: z.array(z.string().max(120)).max(50).optional(),
|
||||
ownerName: z.array(z.string()).optional(),
|
||||
keyword: z.string().optional(),
|
||||
itemCode: z.array(z.string().max(120)).max(50).optional(),
|
||||
@ -23,8 +23,6 @@ const filtersSchema = z.object({
|
||||
orderNo: z.array(z.string().max(80)).max(50).optional(),
|
||||
ownerKeyword: z.array(z.string().max(60)).max(50).optional(),
|
||||
appealType: z.array(z.string().max(60)).max(50).optional(),
|
||||
platform: z.array(z.string().max(40)).max(50).optional(),
|
||||
shopName: z.array(z.string().max(120)).max(50).optional(),
|
||||
listedFrom: z.string().max(10).optional(),
|
||||
listedTo: z.string().max(10).optional(),
|
||||
flowFrom: z.string().max(10).optional(),
|
||||
@ -90,7 +88,9 @@ export async function GET(request: NextRequest) {
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
return handleRequest(request, async (ctx) => {
|
||||
// 一个接口分支返回多种结果形状(生成 / 导入 / 标记离职…),统一按 unknown 返回,
|
||||
// 与 reference-image-estimate 等既有路由保持一致。
|
||||
return handleRequest<unknown>(request, async (ctx): Promise<NextResponse<unknown>> => {
|
||||
const parsed = actionSchema.safeParse(await request.json().catch(() => null))
|
||||
if (!parsed.success) return jsonError(ErrorCode.VALIDATION_INVALID_FORMAT, '请求内容不正确', 400, ctx.requestId)
|
||||
const viewer = await supplyChainBreachOrderService.resolveViewer(ctx.user)
|
||||
|
||||
@ -86,7 +86,8 @@ function MultiFilterChip({
|
||||
}: {
|
||||
label: string
|
||||
placeholder: string
|
||||
field: 'owner' | 'itemCode' | 'orderNo'
|
||||
/** 与 `/api/supply-chain/limit-management/breach-orders/filter-options` 支持的字段保持一致 */
|
||||
field: 'owner' | 'itemCode' | 'orderNo' | 'shopStyleCode' | 'shopItemCode' | 'appealType' | 'platform' | 'shopName'
|
||||
values: string[]
|
||||
onChange: (values: string[]) => void
|
||||
width?: number
|
||||
|
||||
@ -210,6 +210,10 @@ export type ProductArchiveFilters = {
|
||||
productStatus?: ProductArchiveStatusFilter[]
|
||||
productionType?: string[]
|
||||
material?: string[]
|
||||
/** 材质等级(从「材质」文本识别 E0 / E1 / ENF) */
|
||||
materialLevel?: string[]
|
||||
/** 是否有检测报告(外部检测报告接口按 SPU 匹配,「有 / 无」) */
|
||||
inspectionReport?: string[]
|
||||
color?: string[]
|
||||
brand?: string[]
|
||||
supplier?: string[]
|
||||
|
||||
@ -44,6 +44,9 @@ export enum ErrorCode {
|
||||
SUPPLY_CHAIN_CATEGORY_GROUP_TIER_NOT_FOUND = 'SUPPLY_CHAIN_CATEGORY_GROUP_TIER_001',
|
||||
SUPPLY_CHAIN_CATEGORY_GROUP_TIER_EXISTS = 'SUPPLY_CHAIN_CATEGORY_GROUP_TIER_002',
|
||||
|
||||
// 通用资源错误 RESOURCE_xxx(跨模块的「资源不存在」,如破价订单查询)
|
||||
RESOURCE_NOT_FOUND = 'RESOURCE_001',
|
||||
|
||||
// 停产需求管理错误 PRODUCTION_STOP_xxx
|
||||
PRODUCTION_STOP_DEMAND_NOT_FOUND = 'PRODUCTION_STOP_001',
|
||||
PRODUCTION_STOP_ITEM_NOT_FOUND = 'PRODUCTION_STOP_002',
|
||||
@ -148,6 +151,7 @@ export const ERROR_MESSAGES: Record<ErrorCode, string> = {
|
||||
[ErrorCode.PRODUCTION_STOP_REASON_CATEGORY_IN_USE]: '该停产原因分类已被需求使用,不能删除',
|
||||
[ErrorCode.SUPPLY_CHAIN_CATEGORY_GROUP_TIER_NOT_FOUND]: '分类档位不存在',
|
||||
[ErrorCode.SUPPLY_CHAIN_CATEGORY_GROUP_TIER_EXISTS]: '该分类下已存在同名规模标签',
|
||||
[ErrorCode.RESOURCE_NOT_FOUND]: '资源不存在',
|
||||
// 认证错误
|
||||
[ErrorCode.AUTH_INVALID_CREDENTIALS]: '用户名或密码错误',
|
||||
[ErrorCode.AUTH_INVALID_TOKEN]: '无效的认证令牌',
|
||||
@ -262,6 +266,7 @@ export const ERROR_HTTP_STATUS: Record<ErrorCode, number> = {
|
||||
[ErrorCode.SUPPLY_CHAIN_OWNER_CONFIG_NOT_FOUND]: HTTP_STATUS.NOT_FOUND,
|
||||
[ErrorCode.SUPPLY_CHAIN_CATEGORY_GROUP_TIER_NOT_FOUND]: HTTP_STATUS.NOT_FOUND,
|
||||
[ErrorCode.SUPPLY_CHAIN_CATEGORY_GROUP_TIER_EXISTS]: HTTP_STATUS.CONFLICT,
|
||||
[ErrorCode.RESOURCE_NOT_FOUND]: HTTP_STATUS.NOT_FOUND,
|
||||
[ErrorCode.PRODUCTION_STOP_DEMAND_NOT_FOUND]: HTTP_STATUS.NOT_FOUND,
|
||||
[ErrorCode.PRODUCTION_STOP_ITEM_NOT_FOUND]: HTTP_STATUS.NOT_FOUND,
|
||||
[ErrorCode.PRODUCTION_STOP_REASON_CATEGORY_NOT_FOUND]: HTTP_STATUS.NOT_FOUND,
|
||||
|
||||
@ -28,7 +28,6 @@ export const productArchiveSelect = {
|
||||
opsHead: true,
|
||||
productStatus: true,
|
||||
productionType: true,
|
||||
sabcRating: true,
|
||||
material: true,
|
||||
color: true,
|
||||
weight: true,
|
||||
|
||||
@ -137,7 +137,9 @@ class SupplyChainProductArchiveRepositoryClass implements SupplyChainProductArch
|
||||
const query = await preparePeriodArchiveQuery(params.filters, params.field)
|
||||
// 无其他筛选时走 SQL 侧 DISTINCT + 搜索下推,不再把全表取值拉进内存去重。
|
||||
if (query.ids === null && Object.keys(query.where).length === 0) {
|
||||
return listColumnOptionsFromSql(field, params, params.field === 'storageType')
|
||||
// 「入库/代发 / 材质等级 / 是否有检测报告」都在上面的专用分支里算过候选值,
|
||||
// 走到这里的一定是原始档案列,按原值出候选值即可。
|
||||
return listColumnOptionsFromSql(field, params, false)
|
||||
}
|
||||
const records = await readSelectedArchives(query, { [field]: true } as Prisma.SupplyChainProductArchiveSelect)
|
||||
const values = records.map((record) => {
|
||||
|
||||
@ -40,7 +40,13 @@ interface BomCostingChatCompletionResponse {
|
||||
completedFromJsonPayload: boolean
|
||||
}
|
||||
|
||||
export type BomCostingMultimodalOperation = 'IMAGE_FACTS' | 'SYNTHESIS'
|
||||
/**
|
||||
* 多模态调用用途:
|
||||
* - IMAGE_FACTS:图片事实抽取(BOM 识图 / 后台截图识别)
|
||||
* - IMAGE_EXTRACTION:纯图片信息提取(破价申诉凭证识别:优惠明细、链接时间)
|
||||
* - SYNTHESIS:结合图片与文本的复核/汇总
|
||||
*/
|
||||
export type BomCostingMultimodalOperation = 'IMAGE_FACTS' | 'IMAGE_EXTRACTION' | 'SYNTHESIS'
|
||||
|
||||
const BOM_COSTING_REASONING_EFFORTS = new Set([
|
||||
'none',
|
||||
|
||||
@ -38,6 +38,7 @@ export async function recognizeBreachEvidence(image: string): Promise<BreachEvid
|
||||
try {
|
||||
if (!(await isBomCostingMultimodalConfigured())) return null
|
||||
const config = await getBomCostingMultimodalProviderConfig()
|
||||
if (!config) return null
|
||||
const result = await callBomCostingMultimodalModel({
|
||||
config,
|
||||
operation: 'IMAGE_EXTRACTION',
|
||||
@ -116,6 +117,7 @@ export async function recognizeLegacyLinkEvidence(image: string): Promise<Legacy
|
||||
try {
|
||||
if (!(await isBomCostingMultimodalConfigured())) return null
|
||||
const config = await getBomCostingMultimodalProviderConfig()
|
||||
if (!config) return null
|
||||
const result = await callBomCostingMultimodalModel({
|
||||
config,
|
||||
operation: 'IMAGE_FACTS',
|
||||
|
||||
@ -37,8 +37,6 @@ export type BreachOrderListFilters = {
|
||||
orderNo?: string[]
|
||||
ownerKeyword?: string[]
|
||||
appealType?: string[]
|
||||
platform?: string[]
|
||||
shopName?: string[]
|
||||
dateFrom?: string
|
||||
dateTo?: string
|
||||
listedFrom?: string
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user