feat(web): 店铺管理页与页头平台选择器接入真实 API
- 页头平台选择器:PlatformProvider 加载平台目录与平台上下文
(X-Atlas-Platform-Id header),平台 id 仅作为非敏感 UI 偏好存
localStorage,默认取目录第一家(Ozon 优先)
- 店铺管理页覆盖 docs/api/stores-and-platforms.md 已实现接口:
游标分页列表、创建(202+operation)、编辑(If-Match 乐观锁、
409 刷新提示)、设为默认、重新验证凭据、仓库同步(422 脱敏
错误展示)、软删除(DEFAULT_STORE_REPLACEMENT_REQUIRED 提示)
- services/api.ts 新增 getList(保留 {data, meta} 分页信封)与
apiErrorCode;services/stores.ts 按契约生成 Idempotency-Key
- @atlas/contracts 补充只读响应类型 Platform / PlatformContext /
CursorPaginationMeta / StoreOperationResponse(无行为变更)
- API Key 仅存在于提交瞬间的内存表单,不落任何存储
- 浏览器实测:注册→平台切换→创建/编辑/设默认/删除保护/同步
错误路径→刷新会话恢复全部通过;typecheck + production build 通过
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
import type {
|
||||
CreateStoreRequest,
|
||||
CursorPaginationMeta,
|
||||
Store,
|
||||
StoreOperationResponse,
|
||||
UpdateStoreRequest,
|
||||
} from '@atlas/contracts';
|
||||
import { api } from '@/services/api';
|
||||
|
||||
export interface StoreListResult {
|
||||
data: Store[];
|
||||
meta: CursorPaginationMeta;
|
||||
}
|
||||
|
||||
export interface UpdateStoreOptions {
|
||||
/** 提交时的乐观锁版本,取自门店资源 revision */
|
||||
revision: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 门店管理接口,契约见 docs/api/stores-and-platforms.md。
|
||||
* 列表必须用 platform_id 查询参数(管理列表禁止携带 X-Atlas-Platform-Id header)。
|
||||
*/
|
||||
export const storesApi = {
|
||||
list: (platformId: string, cursor?: string) =>
|
||||
api.getList<Store, CursorPaginationMeta>('/stores', {
|
||||
params: { platform_id: platformId, limit: 20, ...(cursor ? { cursor } : {}) },
|
||||
}),
|
||||
|
||||
get: (storeId: string) => api.get<Store>(`/stores/${storeId}`),
|
||||
|
||||
/** 创建后店铺处于 verifying 状态,凭据验证由后台异步执行 */
|
||||
create: (payload: CreateStoreRequest) =>
|
||||
api.post<StoreOperationResponse>('/stores', payload, {
|
||||
headers: { 'Idempotency-Key': crypto.randomUUID() },
|
||||
}),
|
||||
|
||||
/**
|
||||
* 编辑:If-Match 乐观锁。未传 api_key 返回更新后的店铺;
|
||||
* 传入新 api_key 返回 202 与验证 operation(响应仍是 { store, operation })。
|
||||
*/
|
||||
update: (
|
||||
storeId: string,
|
||||
payload: UpdateStoreRequest,
|
||||
options: UpdateStoreOptions
|
||||
) =>
|
||||
api.patch<Store | StoreOperationResponse>(`/stores/${storeId}`, payload, {
|
||||
headers: {
|
||||
'If-Match': `"${options.revision}"`,
|
||||
'Idempotency-Key': crypto.randomUUID(),
|
||||
},
|
||||
}),
|
||||
|
||||
setDefault: (storeId: string) =>
|
||||
api.post<Store>(`/stores/${storeId}/set-default`, undefined, {
|
||||
headers: { 'Idempotency-Key': crypto.randomUUID() },
|
||||
}),
|
||||
|
||||
requestCredentialVerification: (storeId: string) =>
|
||||
api.post<StoreOperationResponse>(`/stores/${storeId}/credential-verifications`, undefined, {
|
||||
headers: { 'Idempotency-Key': crypto.randomUUID() },
|
||||
}),
|
||||
|
||||
requestWarehouseSync: (storeId: string) =>
|
||||
api.post<StoreOperationResponse>(`/stores/${storeId}/warehouse-syncs`, undefined, {
|
||||
headers: { 'Idempotency-Key': crypto.randomUUID() },
|
||||
}),
|
||||
|
||||
/** 删除当前默认店铺时同平台还有其他店铺会返回 422 DEFAULT_STORE_REPLACEMENT_REQUIRED */
|
||||
remove: (storeId: string) =>
|
||||
api.delete<void>(`/stores/${storeId}`, {
|
||||
headers: { 'Idempotency-Key': crypto.randomUUID() },
|
||||
}),
|
||||
};
|
||||
Reference in New Issue
Block a user