This commit is contained in:
Kris
2026-03-27 18:01:17 +08:00
parent 86c2d549e2
commit f439bfffe8
450 changed files with 63870 additions and 1408 deletions

285
tests/MenuImportTest.php Normal file
View File

@@ -0,0 +1,285 @@
<?php
/**
* 菜单导入多语言与兼容测试
* 计划:.tasks/plans/menu-multilang-export-import.md TODO 5RED、TODO 6/7GREEN
* 用例TC-I1TC-I6新格式主表+多语言、旧格式仅主表、重复导入无残留、部分语种、空 name_lang、单条部分语种
* 当前仅编写测试,不实现多语言写入/删除。
* RED 证据(需 DB 可用时TC-I1/I3/I4/I6 会因多语言表无数据或未先 deleteLang 而断言失败TC-I2/I5 可能已绿(仅主表、不写多语言)。
*/
use CompanysBundle\Services\CommonLangModService;
use SuperAdminBundle\Services\ShopMenuService;
class MenuImportTest extends TestCase
{
/** @var string[] config 语种,与 config/langue.php 一致 */
private const CONFIG_LANGS = ['zh-CN', 'en-CN', 'ar-SA'];
/**
* 构造一条可供 uploadMenus 使用的最小合法菜单行(可含 name_lang
*/
private function buildUploadMenuRow(array $overrides = []): array
{
$defaults = [
'shopmenu_id' => 1,
'version' => 1,
'company_id' => 0,
'alias_name' => 'import_test_' . uniqid(),
'name' => 'Test Menu',
'url' => '/test',
'sort' => 1,
'pid' => 0,
'apis' => '',
'icon' => '',
'is_show' => true,
'is_menu' => true,
'disabled' => false,
'menu_type' => ['all'],
];
return array_merge($defaults, $overrides);
}
/**
* 导入后按 version+company_id 取第一条菜单的 shopmenu_id用于单条断言
*/
private function getFirstInsertedShopmenuId(ShopMenuService $service, int $version, int $companyId): ?int
{
$res = $service->shopMenuRepository->lists(
['version' => $version, 'company_id' => $companyId],
'shopmenu_id',
1,
1,
['shopmenu_id' => 'ASC']
);
$list = $res['list'] ?? [];
return isset($list[0]['shopmenu_id']) ? (int) $list[0]['shopmenu_id'] : null;
}
/**
* 按 alias_name 查找导入后的 shopmenu_id
*/
private function getShopmenuIdByAlias(ShopMenuService $service, int $version, int $companyId, string $aliasName): ?int
{
$res = $service->shopMenuRepository->getInfo([
'version' => $version,
'company_id' => $companyId,
'alias_name' => $aliasName,
]);
return isset($res['shopmenu_id']) ? (int) $res['shopmenu_id'] : null;
}
/**
* TC-I1新格式文件含 3 语种 name_lang。
* #given 新格式 list 含 name 与 name_lang(zh-CN, en-CN, ar-SA)
* #when 调用 uploadMenus 导入
* #then 主表 name 正确3 个多语言表均有对应 data_id 的 attribute_value当前 RED未实现 saveLang
*/
public function testImportNewFormatWithThreeLanguagesWritesMainTableAndMultilangTcI1(): void
{
$companyId = 0;
$version = 91; // SMALLINT 范围内,避免与业务 version 1-7 冲突
$row = $this->buildUploadMenuRow([
'version' => $version,
'company_id'=> $companyId,
'name' => '首页',
'name_lang' => [
'zh-CN' => '首页',
'en-CN' => 'Home',
'ar-SA' => 'الرئيسية',
],
]);
$service = new ShopMenuService();
$service->uploadMenus([$row], $companyId);
$sid = $this->getFirstInsertedShopmenuId($service, $version, $companyId);
$this->assertNotNull($sid, '导入后应能查到菜单 (TC-I1)');
$mainMenu = $service->shopMenuRepository->getInfo(['shopmenu_id' => $sid]);
$this->assertSame('首页', $mainMenu['name'] ?? null, '主表 name 应为 list[].name (TC-I1)');
$commonLang = new CommonLangModService();
foreach (self::CONFIG_LANGS as $lang) {
$value = $commonLang->getFieldByLangue($companyId, $lang, 'name', $sid, 'shop_menu', 'shop_menu');
$this->assertSame($row['name_lang'][$lang], $value, "多语言表 {$lang} 应有对应 data_id 的 attribute_value (TC-I1)");
}
}
/**
* TC-I2旧格式文件无 name_lang
* #given 旧格式 list 仅有 name无 name_lang
* #when 调用 uploadMenus 导入
* #then 导入成功,主表 name 正确,多语言表不写入该批(当前可实现:未写多语言即满足)
*/
public function testImportOldFormatWithoutNameLangWritesOnlyMainTableTcI2(): void
{
$companyId = 0;
$version = 92; // SMALLINT 范围内
$row = $this->buildUploadMenuRow([
'version' => $version,
'company_id'=> $companyId,
'name' => '旧格式菜单',
]);
unset($row['name_lang']);
$service = new ShopMenuService();
$service->uploadMenus([$row], $companyId);
$sid = $this->getFirstInsertedShopmenuId($service, $version, $companyId);
$this->assertNotNull($sid, '导入后应能查到菜单 (TC-I2)');
$mainMenu = $service->shopMenuRepository->getInfo(['shopmenu_id' => $sid]);
$this->assertSame('旧格式菜单', $mainMenu['name'] ?? null, '主表 name 应与文件中 name 一致 (TC-I2)');
$commonLang = new CommonLangModService();
foreach (self::CONFIG_LANGS as $lang) {
$value = $commonLang->getFieldByLangue($companyId, $lang, 'name', $sid, 'shop_menu', 'shop_menu');
$this->assertSame('', $value, '旧格式导入时多语言表不写入该批 (TC-I2)');
}
}
/**
* TC-I3先导入新格式再导入同 version+company 的另一新格式。
* #given 第一次导入含 name_lang第二次导入同 version+company 不同 name_lang
* #when 第二次导入完成
* #then 无第一次的多语言残留,以第二次文件为准(当前 RED未先 deleteLang 再写)
*/
public function testImportSameVersionCompanyTwiceHasNoResidualMultilangTcI3(): void
{
$companyId = 0;
$version = 93; // SMALLINT 范围内
$alias = 'import_tc3_' . uniqid();
$firstRow = $this->buildUploadMenuRow([
'version' => $version,
'company_id' => $companyId,
'alias_name' => $alias,
'name' => '第一版',
'name_lang' => ['zh-CN' => '第一版', 'en-CN' => 'First', 'ar-SA' => ''],
]);
$secondRow = $this->buildUploadMenuRow([
'version' => $version,
'company_id' => $companyId,
'alias_name' => $alias,
'name' => '第二版',
'name_lang' => ['zh-CN' => '第二版', 'en-CN' => 'Second', 'ar-SA' => ''],
]);
$service = new ShopMenuService();
$service->uploadMenus([$firstRow], $companyId);
$sid = $this->getShopmenuIdByAlias($service, $version, $companyId, $alias);
$this->assertNotNull($sid);
$service->uploadMenus([$secondRow], $companyId);
$sid2 = $this->getShopmenuIdByAlias($service, $version, $companyId, $alias);
$this->assertNotNull($sid2, '第二次导入后应有一条菜单 (TC-I3)');
$mainMenu = $service->shopMenuRepository->getInfo(['shopmenu_id' => $sid2]);
$this->assertSame('第二版', $mainMenu['name'] ?? null, '主表以第二次为准 (TC-I3)');
$commonLang = new CommonLangModService();
$zhValue = $commonLang->getFieldByLangue($companyId, 'zh-CN', 'name', $sid2, 'shop_menu', 'shop_menu');
$this->assertSame('第二版', $zhValue, '第二次导入后无第一次多语言残留zh-CN 应以第二次文件为准 (TC-I3)');
}
/**
* TC-I4新格式但某条 name_lang 仅含 zh-CN。
* #given list 一项 name_lang 仅含 zh-CN
* #when 调用 uploadMenus 导入
* #then 主表 name 取自 list[].name仅 zh-CN 多语言表有该条en-CN/ar-SA 无该 data_id当前 RED未实现 saveLang
*/
public function testImportNewFormatWithOnlyZhCnInNameLangWritesOnlyZhCnTcI4(): void
{
$companyId = 0;
$version = 94; // SMALLINT 范围内
$row = $this->buildUploadMenuRow([
'version' => $version,
'company_id'=> $companyId,
'name' => '概况',
'name_lang' => ['zh-CN' => '概况'],
]);
$service = new ShopMenuService();
$service->uploadMenus([$row], $companyId);
$sid = $this->getFirstInsertedShopmenuId($service, $version, $companyId);
$this->assertNotNull($sid, '导入后应能查到菜单 (TC-I4)');
$mainMenu = $service->shopMenuRepository->getInfo(['shopmenu_id' => $sid]);
$this->assertSame('概况', $mainMenu['name'] ?? null, '主表 name 取自 list[].name (TC-I4)');
$commonLang = new CommonLangModService();
$zh = $commonLang->getFieldByLangue($companyId, 'zh-CN', 'name', $sid, 'shop_menu', 'shop_menu');
$this->assertSame('概况', $zh, '仅 zh-CN 多语言表应有该条 (TC-I4)');
$en = $commonLang->getFieldByLangue($companyId, 'en-CN', 'name', $sid, 'shop_menu', 'shop_menu');
$this->assertSame('', $en, 'en-CN 无该 data_id (TC-I4)');
$ar = $commonLang->getFieldByLangue($companyId, 'ar-SA', 'name', $sid, 'shop_menu', 'shop_menu');
$this->assertSame('', $ar, 'ar-SA 无该 data_id (TC-I4)');
}
/**
* TC-I5新格式但 name_lang 为空对象 {}。
* #given list 一项 name_lang 为 {}
* #when 调用 uploadMenus 导入
* #then 主表 name 用 list[].name多语言表不写入该条当前可实现未写即满足
*/
public function testImportNewFormatWithEmptyNameLangObjectWritesOnlyMainTableTcI5(): void
{
$companyId = 0;
$version = 95; // SMALLINT 范围内
$row = $this->buildUploadMenuRow([
'version' => $version,
'company_id'=> $companyId,
'name' => '仅主表名称',
'name_lang' => [],
]);
$service = new ShopMenuService();
$service->uploadMenus([$row], $companyId);
$sid = $this->getFirstInsertedShopmenuId($service, $version, $companyId);
$this->assertNotNull($sid, '导入后应能查到菜单 (TC-I5)');
$mainMenu = $service->shopMenuRepository->getInfo(['shopmenu_id' => $sid]);
$this->assertSame('仅主表名称', $mainMenu['name'] ?? null, '主表 name 用 list[].name (TC-I5)');
$commonLang = new CommonLangModService();
foreach (self::CONFIG_LANGS as $lang) {
$value = $commonLang->getFieldByLangue($companyId, $lang, 'name', $sid, 'shop_menu', 'shop_menu');
$this->assertSame('', $value, 'name_lang 为空对象时多语言表不写入该条 (TC-I5)');
}
}
/**
* TC-I6单条菜单、name_lang 含部分语种。
* #given 单条 listname_lang 仅含 zh-CN 与 en-CN
* #when 调用 uploadMenus 导入
* #then 仅存在的语种写入多语言表(当前 RED未实现 saveLang
*/
public function testImportSingleMenuWithPartialNameLangWritesOnlyPresentLangsTcI6(): void
{
$companyId = 0;
$version = 96; // SMALLINT 范围内
$row = $this->buildUploadMenuRow([
'version' => $version,
'company_id'=> $companyId,
'name' => '边界单条',
'name_lang' => ['zh-CN' => '边界单条', 'en-CN' => 'Single Partial'],
]);
$service = new ShopMenuService();
$service->uploadMenus([$row], $companyId);
$sid = $this->getFirstInsertedShopmenuId($service, $version, $companyId);
$this->assertNotNull($sid, '导入后应能查到菜单 (TC-I6)');
$mainMenu = $service->shopMenuRepository->getInfo(['shopmenu_id' => $sid]);
$this->assertSame('边界单条', $mainMenu['name'] ?? null, '主表 name 用 list[].name (TC-I6)');
$commonLang = new CommonLangModService();
$this->assertSame('边界单条', $commonLang->getFieldByLangue($companyId, 'zh-CN', 'name', $sid, 'shop_menu', 'shop_menu'), 'zh-CN 应写入 (TC-I6)');
$this->assertSame('Single Partial', $commonLang->getFieldByLangue($companyId, 'en-CN', 'name', $sid, 'shop_menu', 'shop_menu'), 'en-CN 应写入 (TC-I6)');
$this->assertSame('', $commonLang->getFieldByLangue($companyId, 'ar-SA', 'name', $sid, 'shop_menu', 'shop_menu'), 'ar-SA 未提供则不写入 (TC-I6)');
}
}