/** * R2 设计清单接口(api-contract-v1 §5)。 * * 映射约定(阶段0 决策,见 docs/r2-workflow.md): * - 一条前端 DesignItem = 一条后端 DesignList 记录,items 固定 1 个元素; * - title 由 productName 承担(后端必填); * - productIcon 不入库(后端 forbidNonWhitelisted 会拒绝),页面按 productId 兜底推导; * - 状态映射:DRAFT↔undesigned、SUBMITTED↔designing、PROCESSING↔processing、DONE↔ordered; * - 前端只允许提交 designing(→SUBMITTED),PROCESSING/DONE 由 R3 驱动。 */ import http from '../request' import type { DesignDataV1, DesignItem } from '../../types' /** 后端 DesignListStatus → 前端状态码(契约 §5 映射表) */ const SERVER_STATUS_MAP: Record = { DRAFT: 'undesigned', SUBMITTED: 'designing', PROCESSING: 'processing', DONE: 'ordered', } /** 清单条目(后端 items[] 固定 1 个元素,契约 §5) */ export interface DesignListEntryPayload { productId: string productName: string unitPrice: number count: number designData?: DesignDataV1 } /** 后端 DesignList 记录结构 */ interface ServerDesignList { id: string userId?: string title: string items: (Partial & Record)[] status: string createdAt: string updatedAt?: string } /** ServerDesignList → 前端 DesignItem */ function toDesignItem(rec: ServerDesignList): DesignItem { const entry = rec.items && rec.items[0] return { id: rec.id, productId: entry?.productId ?? '', productName: entry?.productName || rec.title, // productIcon 有意不返回:服务端不存储,页面按 PRODUCT_ICON_MAP[productId] 兜底 unitPrice: entry?.unitPrice ?? 0, count: entry?.count ?? 1, status: SERVER_STATUS_MAP[rec.status] ?? 'undesigned', designData: entry?.designData, createdAt: (rec.createdAt || '').slice(0, 10), } } /** 我的设计清单(createdAt 倒序) */ export async function fetchDesignList(): Promise { const list = await http.get('/api/design-list') return (list || []).map(toDesignItem) } /** 创建清单(一条设计一条清单;后端初始状态 DRAFT/undesigned) */ export async function createDesign(entry: DesignListEntryPayload): Promise { const rec = await http.post('/api/design-list', { title: entry.productName, items: [ { productId: entry.productId, productName: entry.productName, unitPrice: entry.unitPrice, count: entry.count, ...(entry.designData !== undefined ? { designData: entry.designData } : {}), }, ], }) return toDesignItem(rec) } export interface UpdateDesignPayload { /** 新标题(前端一般不传) */ title?: string /** 状态迁移:前端只允许 designing(→SUBMITTED,DIY 保存设计时) */ status?: 'designing' /** 条目整体替换(items 是全量覆盖,修改 designData 时必须带全 4 个基本字段) */ item?: DesignListEntryPayload } /** 更新本人清单(部分更新:只传显式给出的字段) */ export async function updateDesign(id: string, payload: UpdateDesignPayload): Promise { const body: Record = {} if (payload.title !== undefined) body.title = payload.title if (payload.status !== undefined) body.status = 'SUBMITTED' if (payload.item !== undefined) { body.items = [ { productId: payload.item.productId, productName: payload.item.productName, unitPrice: payload.item.unitPrice, count: payload.item.count, ...(payload.item.designData !== undefined ? { designData: payload.item.designData } : {}), }, ] } const rec = await http.patch(`/api/design-list/${id}`, body) return toDesignItem(rec) } /** 删除本人清单 */ export async function deleteDesign(id: string): Promise { await http.del(`/api/design-list/${id}`) } /** 批量删除(后端只删本人条目,返回实际删除数;部分 id 无效不报错) */ export async function deleteDesigns(ids: string[]): Promise { const res = await http.post<{ deleted: number }>('/api/design-list/batch-delete', { ids }) return res?.deleted ?? 0 }