fix(admin): load picker warehouses from domestic master

This commit is contained in:
2026-08-26 11:42:46 +08:00
parent 011195012b
commit 8307510fc3
3 changed files with 127 additions and 24 deletions
+27 -17
View File
@@ -1,6 +1,5 @@
import { beforeEach, describe, expect, it, vi } from 'vitest' import { beforeEach, describe, expect, it, vi } from 'vitest'
import { http } from '@/shared/api/http' import { http } from '@/shared/api/http'
import { overseasHttp } from '@/shared/api/overseasHttp'
import { import {
approveEnterpriseReview, approveEnterpriseReview,
archivePlatformTenant, archivePlatformTenant,
@@ -51,10 +50,6 @@ vi.mock('@/shared/api/http', () => ({
unwrapApiResponse: <T>(response: { data: T }) => response.data, unwrapApiResponse: <T>(response: { data: T }) => response.data,
})) }))
vi.mock('@/shared/api/overseasHttp', () => ({
overseasHttp: { get: vi.fn() },
}))
const role: PlatformRoleResource = { const role: PlatformRoleResource = {
id: 4000002, id: 4000002,
tenantId: 0, tenantId: 0,
@@ -123,7 +118,6 @@ describe('platform IAM roles', () => {
vi.mocked(http.post).mockReset() vi.mocked(http.post).mockReset()
vi.mocked(http.put).mockReset() vi.mocked(http.put).mockReset()
vi.mocked(http.delete).mockReset() vi.mocked(http.delete).mockReset()
vi.mocked(overseasHttp.get).mockReset()
}) })
it('loads roles from the platform IAM prefix', async () => { it('loads roles from the platform IAM prefix', async () => {
@@ -218,9 +212,13 @@ describe('platform IAM roles', () => {
}) })
it('maintains paginated platform users through the unified user contract', async () => { it('maintains paginated platform users through the unified user contract', async () => {
const userOptions = { const userOptionsPayload = {
tenants: [{ id: '0', name: '平台', status: 'ACTIVE' }], tenants: [{ id: '0', name: '平台', status: 'ACTIVE' }],
roles: [{ id: '40001', tenantId: '0', roleCode: 'PLATFORM_OPERATOR', name: '运营管理员', status: 'ACTIVE' }], roles: [{ id: '40001', tenantId: '0', name: '运营管理员', status: 'ACTIVE' }],
}
const userOptions = {
...userOptionsPayload,
roles: [{ ...userOptionsPayload.roles[0], roleCode: 'PLATFORM_OPERATOR' }],
} }
const create = { const create = {
username: 'zhangwei', displayName: '张伟', phone: '13800138000', email: 'zhangwei@example.com', username: 'zhangwei', displayName: '张伟', phone: '13800138000', email: 'zhangwei@example.com',
@@ -234,7 +232,11 @@ describe('platform IAM roles', () => {
vi.mocked(http.get).mockResolvedValueOnce({ vi.mocked(http.get).mockResolvedValueOnce({
code: '200', message: 'success', data: { ...userPage, page: '2', size: '10', total: '11', pages: '2' }, code: '200', message: 'success', data: { ...userPage, page: '2', size: '10', total: '11', pages: '2' },
}) })
.mockResolvedValueOnce({ code: '200', message: 'success', data: userOptions }) .mockResolvedValueOnce({ code: '200', message: 'success', data: userOptionsPayload })
.mockResolvedValueOnce({
code: '200', message: 'success',
data: [{ ...role, id: '40001', roleCode: 'PLATFORM_OPERATOR' }],
})
vi.mocked(http.post).mockResolvedValue({ code: '200', message: 'success', data: managedUser }) vi.mocked(http.post).mockResolvedValue({ code: '200', message: 'success', data: managedUser })
vi.mocked(http.put).mockResolvedValue({ code: '200', message: 'success', data: managedUser }) vi.mocked(http.put).mockResolvedValue({ code: '200', message: 'success', data: managedUser })
@@ -249,28 +251,36 @@ describe('platform IAM roles', () => {
params: { page: 2, size: 10, keyword: '张伟', status: 'ACTIVE' }, params: { page: 2, size: 10, keyword: '张伟', status: 'ACTIVE' },
}) })
expect(http.get).toHaveBeenNthCalledWith(2, '/platform/v1/iam/users/options') expect(http.get).toHaveBeenNthCalledWith(2, '/platform/v1/iam/users/options')
expect(http.get).toHaveBeenNthCalledWith(3, '/platform/v1/iam/roles')
expect(http.post).toHaveBeenCalledWith('/platform/v1/iam/users', create) expect(http.post).toHaveBeenCalledWith('/platform/v1/iam/users', create)
expect(http.put).toHaveBeenNthCalledWith(1, `/platform/v1/iam/users/${managedUser.membershipId}`, update) expect(http.put).toHaveBeenNthCalledWith(1, `/platform/v1/iam/users/${managedUser.membershipId}`, update)
expect(http.put).toHaveBeenNthCalledWith(2, `/platform/v1/iam/users/${managedUser.membershipId}/status`, { status: 'DISABLED' }) expect(http.put).toHaveBeenNthCalledWith(2, `/platform/v1/iam/users/${managedUser.membershipId}/status`, { status: 'DISABLED' })
expect(http.put).toHaveBeenNthCalledWith(3, `/platform/v1/iam/users/${managedUser.membershipId}/password`, { newPassword: 'ChangeMe123' }) expect(http.put).toHaveBeenNthCalledWith(3, `/platform/v1/iam/users/${managedUser.membershipId}/password`, { newPassword: 'ChangeMe123' })
}) })
it('loads only active shared receiving overseas warehouses through the overseas gateway', async () => { it('loads only active shared receiving overseas warehouses from domestic warehouse master data', async () => {
vi.mocked(overseasHttp.get).mockResolvedValue({ vi.mocked(http.get).mockResolvedValue({
code: '200', message: 'success', data: { code: '200', message: 'success', data: {
records: [ records: [
{ id: '90071992547409931', customerId: '0', warehouseCode: 'LAX-01', warehouseName: '洛杉矶收货仓', warehouseType: 'OVERSEAS', countryCode: 'US', status: 'ACTIVE' }, { id: '90071992547409931', tenantId: '0', warehouseCode: 'LAX-01', warehouseName: '洛杉矶收货仓', warehouseType: 'OVERSEAS', countryCode: 'US', status: 'ACTIVE' },
{ id: '2', customerId: '100', warehouseCode: 'CUST', warehouseName: '客户仓', warehouseType: 'OVERSEAS', countryCode: 'US', status: 'ACTIVE' }, { id: '90071992547409932', tenantId: '0', warehouseCode: 'NYC-01', warehouseName: '纽约收货仓', warehouseType: 'OVERSEAS', countryCode: 'US', status: 'ACTIVE' },
{ id: '3', customerId: '0', warehouseCode: 'PARTNER', warehouseName: '合作仓', warehouseType: 'OVERSEAS_PARTNER', countryCode: 'US', status: 'ACTIVE' }, { id: '2', tenantId: '100', warehouseCode: 'CUST', warehouseName: '客户仓', warehouseType: 'OVERSEAS', countryCode: 'US', status: 'ACTIVE' },
], page: 1, size: 200, total: 3, pages: 1, { id: '3', tenantId: '0', warehouseCode: 'PARTNER', warehouseName: '合作仓', warehouseType: 'OVERSEAS_PARTNER', countryCode: 'US', status: 'ACTIVE' },
{ id: '4', tenantId: '0', warehouseCode: 'ONT8', warehouseName: 'ONT8', warehouseType: 'FBA', countryCode: 'US', status: 'ACTIVE' },
{ id: '5', tenantId: '0', warehouseCode: 'SEA-INACTIVE', warehouseName: '西雅图停用仓', warehouseType: 'OVERSEAS', countryCode: 'US', status: 'INACTIVE' },
], page: 1, size: 200, total: 6, pages: 1,
}, },
}) })
await expect(listSharedReceivingOverseasWarehouseOptions()).resolves.toEqual([ await expect(listSharedReceivingOverseasWarehouseOptions()).resolves.toEqual([
{ id: '90071992547409931', warehouseCode: 'LAX-01', warehouseName: '洛杉矶收货仓', countryCode: 'US' }, { id: '90071992547409931', warehouseCode: 'LAX-01', warehouseName: '洛杉矶收货仓', countryCode: 'US' },
{ id: '90071992547409932', warehouseCode: 'NYC-01', warehouseName: '纽约收货仓', countryCode: 'US' },
]) ])
expect(overseasHttp.get).toHaveBeenCalledWith('/platform/v1/warehouse-admin', { expect(http.get).toHaveBeenCalledWith('/platform/v1/warehouse-admin', {
params: { customerId: '0', page: 1, size: 200, capabilityCode: 'RECEIVING', status: 'ACTIVE' }, params: {
tenantId: '0', page: 1, size: 200, capabilityCode: 'RECEIVING',
warehouseType: 'OVERSEAS', status: 'ACTIVE',
},
}) })
}) })
+32 -7
View File
@@ -1,5 +1,4 @@
import { http, unwrapApiResponse, type ApiEnvelope } from '@/shared/api/http' import { http, unwrapApiResponse, type ApiEnvelope } from '@/shared/api/http'
import { overseasHttp } from '@/shared/api/overseasHttp'
import type { PlatformPermissionResource } from '@/shared/types/platformPermission' import type { PlatformPermissionResource } from '@/shared/types/platformPermission'
export type { PlatformPermissionResource } from '@/shared/types/platformPermission' export type { PlatformPermissionResource } from '@/shared/types/platformPermission'
@@ -277,6 +276,11 @@ export interface PlatformUserOptions {
roles: Array<{ id: number | string; tenantId: number | string; roleCode: string; name: string; status: string }> roles: Array<{ id: number | string; tenantId: number | string; roleCode: string; name: string; status: string }>
} }
interface PlatformUserOptionsPayload {
tenants: PlatformUserOptions['tenants']
roles: Array<Omit<PlatformUserOptions['roles'][number], 'roleCode'>>
}
export interface SharedReceivingOverseasWarehouseOption { export interface SharedReceivingOverseasWarehouseOption {
id: string id: string
warehouseCode: string warehouseCode: string
@@ -565,15 +569,27 @@ export async function listPlatformManagedUsers(query: PlatformManagedUserQuery)
} }
} }
export function getPlatformUserOptions() { export async function getPlatformUserOptions(): Promise<PlatformUserOptions> {
return getData<PlatformUserOptions>('/platform/v1/iam/users/options') const [optionsResponse, rolesResponse] = await Promise.all([
http.get<ApiEnvelope<PlatformUserOptionsPayload>, ApiEnvelope<PlatformUserOptionsPayload>>('/platform/v1/iam/users/options'),
http.get<ApiEnvelope<PlatformRoleResource[]>, ApiEnvelope<PlatformRoleResource[]>>('/platform/v1/iam/roles'),
])
const options = unwrapApiResponse(optionsResponse)
const roleCodes = new Map(unwrapApiResponse(rolesResponse).map((role) => [String(role.id), role.roleCode]))
return {
tenants: options.tenants,
roles: options.roles.map((role) => ({
...role,
roleCode: roleCodes.get(String(role.id)) ?? '',
})),
}
} }
export async function listSharedReceivingOverseasWarehouseOptions() { export async function listSharedReceivingOverseasWarehouseOptions() {
interface WarehousePage { interface WarehousePage {
records: Array<{ records: Array<{
id: number | string id: number | string
customerId: number | string tenantId: number | string
warehouseCode: string warehouseCode: string
warehouseName: string warehouseName: string
warehouseType: string warehouseType: string
@@ -581,12 +597,21 @@ export async function listSharedReceivingOverseasWarehouseOptions() {
status: string status: string
}> }>
} }
const response = await overseasHttp.get<ApiEnvelope<WarehousePage>, ApiEnvelope<WarehousePage>>( const response = await http.get<ApiEnvelope<WarehousePage>, ApiEnvelope<WarehousePage>>(
'/platform/v1/warehouse-admin', '/platform/v1/warehouse-admin',
{ params: { customerId: '0', page: 1, size: 200, capabilityCode: 'RECEIVING', status: 'ACTIVE' } }, {
params: {
tenantId: '0',
page: 1,
size: 200,
capabilityCode: 'RECEIVING',
warehouseType: 'OVERSEAS',
status: 'ACTIVE',
},
},
) )
return unwrapApiResponse(response).records return unwrapApiResponse(response).records
.filter((warehouse) => String(warehouse.customerId) === '0' .filter((warehouse) => String(warehouse.tenantId) === '0'
&& warehouse.warehouseType === 'OVERSEAS' && warehouse.warehouseType === 'OVERSEAS'
&& warehouse.status === 'ACTIVE') && warehouse.status === 'ACTIVE')
.map((warehouse) => ({ .map((warehouse) => ({
@@ -220,6 +220,74 @@ describe('InternalUsersView platform internal user management', () => {
}) })
}) })
it('clears a warehouse binding after switching away from WAREHOUSE_PICKER', async () => {
vi.mocked(getPlatformUserOptions).mockResolvedValueOnce({
tenants: [{ id: '0', name: '平台', status: 'ACTIVE' }],
roles: [
{ id: '40', tenantId: '0', roleCode: 'PLATFORM_OPERATOR', name: '运营管理员', status: 'ACTIVE' },
{ id: '4000005', tenantId: '0', roleCode: 'WAREHOUSE_PICKER', name: '仓库拣货员', status: 'ACTIVE' },
],
})
const wrapper = await mountView()
const setupState = (wrapper.vm.$ as unknown as {
setupState: {
openCreate: () => void
form: { roleIds: string[]; overseasWarehouseId: string }
}
}).setupState
setupState.openCreate()
setupState.form.roleIds = ['4000005']
await flushPromises()
setupState.form.overseasWarehouseId = '90071992547409931'
setupState.form.roleIds = ['40']
await flushPromises()
expect(setupState.form.overseasWarehouseId).toBe('')
expect(document.body.textContent).not.toContain('实际海外收货仓')
})
it('submits the selected picker warehouse as an unchanged long-id string', async () => {
vi.mocked(getPlatformUserOptions).mockResolvedValueOnce({
tenants: [{ id: '0', name: '平台', status: 'ACTIVE' }],
roles: [{ id: '4000005', tenantId: '0', roleCode: 'WAREHOUSE_PICKER', name: '仓库拣货员', status: 'ACTIVE' }],
})
const wrapper = await mountView()
const setupState = (wrapper.vm.$ as unknown as {
setupState: {
openCreate: () => void
saveUser: () => Promise<void>
form: Record<string, unknown>
}
}).setupState
setupState.openCreate()
Object.assign(setupState.form, {
username: 'overseas_picker_dev',
displayName: '海外仓操作员',
phone: '17512095155',
email: 'overseas_picker_dev@qq.com',
roleIds: ['4000005'],
overseasWarehouseId: '90071992547409931',
password: 'ChangeMe123!',
confirmPassword: 'ChangeMe123!',
status: 'ACTIVE',
})
await flushPromises()
await setupState.saveUser()
expect(createPlatformManagedUser).toHaveBeenCalledWith({
username: 'overseas_picker_dev',
displayName: '海外仓操作员',
phone: '17512095155',
email: 'overseas_picker_dev@qq.com',
tenantId: '0',
roleIds: ['4000005'],
overseasWarehouseId: '90071992547409931',
password: 'ChangeMe123!',
status: 'ACTIVE',
})
})
it('requests server-side pages and filters instead of slicing locally', async () => { it('requests server-side pages and filters instead of slicing locally', async () => {
const wrapper = await mountView() const wrapper = await mountView()
await wrapper.find('button[aria-label="第 2 页"]').trigger('click') await wrapper.find('button[aria-label="第 2 页"]').trigger('click')