diff --git a/admin-frontend-vue/src/pages/shop/shop-manage-api.ts b/admin-frontend-vue/src/pages/shop/shop-manage-api.ts index 75c6033e..fe813d49 100644 --- a/admin-frontend-vue/src/pages/shop/shop-manage-api.ts +++ b/admin-frontend-vue/src/pages/shop/shop-manage-api.ts @@ -13,6 +13,7 @@ import { } from './shop-dto.ts' import { toShopManageCreateRequest, + toShopManageUpdateRequest, validateShopManageForm, type ShopManageFormValues, } from './shop-manage-form-model.ts' @@ -46,3 +47,17 @@ export async function submitCreateShopManage(form: ShopManageFormValues, created } return item } + +/** 提交编辑店铺:先校验(失败抛首条错误不发请求),PUT /api/admin/shop-manages/{id}。 */ +export async function submitUpdateShopManage(id: number, form: ShopManageFormValues): Promise { + const { valid, errors } = validateShopManageForm(form) + if (!valid) { + throw new Error(Object.values(errors)[0] || '店铺表单校验未通过') + } + const { data } = await http.put(`${SHOP_MANAGE_ENDPOINT}/${id}`, toShopManageUpdateRequest(form)) + const item = toShopSummary(unwrap(data)) + if (!item) { + throw new Error('更新店铺响应异常:未返回有效记录') + } + return item +} diff --git a/admin-frontend-vue/src/pages/shop/shop-manage-edit-form.ts b/admin-frontend-vue/src/pages/shop/shop-manage-edit-form.ts new file mode 100644 index 00000000..e3c6d187 --- /dev/null +++ b/admin-frontend-vue/src/pages/shop/shop-manage-edit-form.ts @@ -0,0 +1,31 @@ +/** 店铺编辑表单模型(任务 92):由店铺行预填可编辑字段(分组/名称/账号),纯逻辑。 */ +import type { ShopSummary } from './shop-dto.ts' +import type { ShopManageFormValues } from './shop-manage-form-model.ts' + +function finiteId(value: unknown): number | null { + return typeof value === 'number' && Number.isFinite(value) && value >= 1 ? Math.floor(value) : null +} + +function groupIdOr(item: ShopSummary): number | null { + return typeof item.groupId === 'number' && Number.isFinite(item.groupId) && item.groupId >= 1 + ? Math.floor(item.groupId) + : null +} + +function textField(value: unknown): string { + return typeof value === 'string' ? value : '' +} + +/** 由店铺行预填编辑表单:密码掩码不回填明文,需保存时重新录入(后端更新要求必填)。 */ +export function buildShopManageEditForm(item: ShopSummary | null | undefined): ShopManageFormValues | null { + if (!item || typeof item !== 'object') return null + if (finiteId(item.id) === null) return null + return { + groupId: groupIdOr(item), + shopName: textField(item.shopName), + mallName: textField(item.mallName), + znUsername: textField(item.znUsername), + account: textField(item.account), + password: '', + } +} diff --git a/admin-frontend-vue/src/pages/shop/shop-manage-form-model.ts b/admin-frontend-vue/src/pages/shop/shop-manage-form-model.ts index 6e94f860..424e6f43 100644 --- a/admin-frontend-vue/src/pages/shop/shop-manage-form-model.ts +++ b/admin-frontend-vue/src/pages/shop/shop-manage-form-model.ts @@ -33,6 +33,16 @@ export interface ShopManageCreatePayload { znUsername?: string } +/** 发送 PUT /api/admin/shop-manages/{id} 的请求体(与 Java ShopManageUpdateRequest camelCase 对齐)。 */ +export interface ShopManageUpdatePayload { + groupId: number + shopName: string + mallName: string + account: string + password: string + znUsername?: string +} + export function emptyShopManageForm(): ShopManageFormValues { return { groupId: null, shopName: '', mallName: '', znUsername: '', account: '', password: '' } } @@ -66,8 +76,18 @@ export function validateShopManageForm(form: ShopManageFormValues): { valid: boo return { valid: Object.keys(errors).length === 0, errors } } -/** 新增表单 → 创建请求体;缺有效分组/必填时抛可操作错误(调用前应先校验)。 */ -export function toShopManageCreateRequest(form: ShopManageFormValues, createdById: number): ShopManageCreatePayload { +/** 新增/更新共用的必填业务段。 */ +interface ManagePayloadBase { + groupId: number + shopName: string + mallName: string + account: string + password: string + znUsername?: string +} + +/** 通用必填段(校验 + 归一);缺有效分组/必填时抛可操作错误(调用前应先校验)。 */ +function requiredBase(form: ShopManageFormValues): ManagePayloadBase { const groupId = positiveGroupId(form.groupId) if (groupId === null) { throw new Error('分组不能为空') @@ -76,15 +96,24 @@ export function toShopManageCreateRequest(form: ShopManageFormValues, createdByI if (!errorText(form.mallName)) throw new Error('商城名称不能为空') if (!errorText(form.account)) throw new Error('账号不能为空') if (!errorText(form.password)) throw new Error('密码不能为空') - const payload: ShopManageCreatePayload = { + const payload: ManagePayloadBase = { groupId, shopName: errorText(form.shopName), mallName: errorText(form.mallName), account: errorText(form.account), password: errorText(form.password), - createdById, } const znUsername = (form.znUsername || '').trim() if (znUsername) payload.znUsername = znUsername return payload } + +/** 新增表单 → 创建请求体(含 createdById)。 */ +export function toShopManageCreateRequest(form: ShopManageFormValues, createdById: number): ShopManageCreatePayload { + return { ...requiredBase(form), createdById } +} + +/** 编辑表单 → 更新请求体(与新增同字段,不含 createdById)。 */ +export function toShopManageUpdateRequest(form: ShopManageFormValues): ShopManageUpdatePayload { + return requiredBase(form) +} diff --git a/admin-frontend-vue/tests/task-92.test.ts b/admin-frontend-vue/tests/task-92.test.ts new file mode 100644 index 00000000..748b5f47 --- /dev/null +++ b/admin-frontend-vue/tests/task-92.test.ts @@ -0,0 +1,86 @@ +import test from 'node:test' +import assert from 'node:assert/strict' +import { readSource } from './helpers.ts' +import { buildShopManageEditForm } from '../src/pages/shop/shop-manage-edit-form.ts' +import { toShopManageUpdateRequest, validateShopManageForm } from '../src/pages/shop/shop-manage-form-model.ts' +import type { ShopSummary } from '../src/pages/shop/shop-dto.ts' + +function row(id: number, over: Partial = {}): ShopSummary { + return { id, groupId: 3, groupName: '华东组', shopName: '', mallName: '', znUsername: '', account: '', ...over } +} + +test('test_task_092_shop_manage_edit_form_normal_primary_path', () => { + // 正常主路径:由店铺行预填可编辑字段;密码需重新录入(掩码不回填)。 + const form = buildShopManageEditForm(row(9, { groupId: 3, shopName: 'BlueWave', mallName: 'M', znUsername: 'robot', account: 'a@b.c', passwordMasked: '******' })) + assert.ok(form) + assert.equal(form!.groupId, 3) + assert.equal(form!.shopName, 'BlueWave') + assert.equal(form!.mallName, 'M') + assert.equal(form!.znUsername, 'robot') + assert.equal(form!.account, 'a@b.c') + assert.equal(form!.password, '', '掩码不回填为明文') + const filled = { ...form!, password: 'new-pw' } + const { valid } = validateShopManageForm(filled) + assert.equal(valid, true) + const request = toShopManageUpdateRequest(filled) + assert.equal(request.groupId, 3) + assert.equal(request.password, 'new-pw') + assert.equal('createdById' in request, false, '更新请求体不带 createdById') +}) + +test('test_task_092_shop_manage_edit_form_normal_variant_input', () => { + // 正常变体:无分组行回填为空分组待重选。 + const form = buildShopManageEditForm(row(4, { groupId: null, shopName: 'RedSun' })) + assert.ok(form) + assert.equal(form!.groupId, null) + assert.equal(form!.shopName, 'RedSun') +}) + +test('test_task_092_shop_manage_edit_form_repeated_is_idempotent', () => { + // 正常重复:预填不改原记录、结果稳定。 + const item = row(2, { shopName: 'S', account: 'a' }) + const first = buildShopManageEditForm(item) + const second = buildShopManageEditForm(item) + assert.deepEqual(first, second) + assert.equal(item.shopName, 'S') +}) + +test('test_task_092_shop_manage_edit_form_boundary_empty_input', () => { + // 边界空值:缺省可选字段回填为空串且结构完整。 + const form = buildShopManageEditForm(row(1)) + assert.ok(form) + assert.equal(form!.znUsername, '') + assert.equal(form!.mallName, '') +}) + +test('test_task_092_shop_manage_edit_form_boundary_single_item', () => { + // 边界单元素:仅 id/店铺名的最小行仍可预填。 + const form = buildShopManageEditForm({ id: 6, groupId: 1, groupName: 'g', shopName: 'X', mallName: '', znUsername: '', account: '', createdAt: 't' } as ShopSummary) + assert.ok(form) + assert.equal(form!.shopName, 'X') +}) + +test('test_task_092_shop_manage_edit_form_boundary_limit_or_missing_field', () => { + // 边界上限/缺字段:账号名/店铺名按行值保留,不因掩码显示而截断。 + const longAccount = 'u'.repeat(200) + const form = buildShopManageEditForm(row(7, { account: longAccount, shopName: '长店名'.repeat(30) })) + assert.ok(form) + assert.equal(form!.account, longAccount) +}) + +test('test_task_092_shop_manage_edit_form_invalid_input_rejected', () => { + // 异常输入:空记录/缺 id 无法构建编辑表单。 + assert.equal(buildShopManageEditForm(null as unknown as ShopSummary), null) + assert.equal(buildShopManageEditForm({ id: Number.NaN } as unknown as ShopSummary), null) +}) + +test('test_task_092_shop_manage_edit_form_dependency_failure_returns_actionable_message', () => { + // 依赖失败/提交走 adapter:编辑模型纯逻辑,adapter PUT /api/admin/shop-manages/{id}。 + const model = readSource('src/pages/shop/shop-manage-edit-form.ts') + assert.equal(/axios|http\.|vue/.test(model), false, '编辑模型保持纯逻辑') + assert.match(model, /掩码|敏感/) + const api = readSource('src/pages/shop/shop-manage-api.ts') + assert.match(api, /submitUpdateShopManage/) + assert.match(api, /http\.put/) + assert.match(api, /\/api\/admin\/shop-manages/) +})