feat(M03-C): 完成地图选店与距离排序
This commit is contained in:
@@ -15,6 +15,7 @@ import { RbacRepository } from '../dist/auth/rbac-repository.js';
|
||||
import { UserManagementRepository } from '../dist/auth/user-management-repository.js';
|
||||
import { StoreRoomRepository, StoreRoomError } from '../dist/stores/store-room-repository.js';
|
||||
import { ContentRepository, ContentError } from '../dist/content/content-repository.js';
|
||||
import { StoreDiscoveryRepository } from '../dist/stores/store-discovery-repository.js';
|
||||
import {
|
||||
executeMigrationPlan,
|
||||
loadMigrationPlan,
|
||||
@@ -72,10 +73,10 @@ async function readMigrationVersions(pool) {
|
||||
const [rows] = await pool.query(
|
||||
`SELECT version, name
|
||||
FROM qipai_schema_migrations
|
||||
WHERE version IN (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
WHERE version IN (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ORDER BY version`,
|
||||
['2026061601', '2026061802', '2026061803', '2026061804',
|
||||
'2026061805', '2026061806', '2026061807', '2026061808']
|
||||
'2026061805', '2026061806', '2026061807', '2026061808', '2026061809']
|
||||
);
|
||||
return rows;
|
||||
}
|
||||
@@ -341,7 +342,7 @@ async function assertStoreRoomDomain(pool, context) {
|
||||
traceId: 'm03a-live-test', ip: '127.0.0.1', userAgent: 'M03-A live test'
|
||||
};
|
||||
const store = await repository.createStore(actor, {
|
||||
name: 'M03A Store', address: 'Sanitized address',
|
||||
name: 'M03A Store', address: 'Sanitized address', city: '上海市', district: '黄浦区',
|
||||
longitude: 121.4737, latitude: 31.2304, contactPhone: '13800000003',
|
||||
timezone: 'Asia/Shanghai', businessStatus: 'OPEN',
|
||||
wifiSsid: 'M03A-WIFI', wifiPassword: 'sanitized-password',
|
||||
@@ -393,6 +394,37 @@ async function assertStoreRoomDomain(pool, context) {
|
||||
]);
|
||||
}
|
||||
|
||||
async function assertStoreDiscovery(pool, context) {
|
||||
const [secondStore] = await pool.query(
|
||||
`INSERT INTO qipai_stores
|
||||
(tenant_id, name, address, city, district, longitude, latitude, business_status, sort_order)
|
||||
VALUES (?, 'M03C Far Store', 'Sanitized address B', '上海市', '浦东新区',
|
||||
121.6000000, 31.3000000, 'OPEN', 2)`,
|
||||
[context.tenantId]
|
||||
);
|
||||
await pool.query(
|
||||
`INSERT INTO qipai_store_business_hours
|
||||
(tenant_id, store_id, weekday, open_minute, close_minute, is_closed)
|
||||
VALUES (?, ?, 4, 0, 0, 0)`,
|
||||
[context.tenantId, secondStore.insertId]
|
||||
);
|
||||
const repository = new StoreDiscoveryRepository(pool);
|
||||
const stores = await repository.findStores({
|
||||
tenantId: context.tenantId,
|
||||
city: '上海市',
|
||||
latitude: 31.2304,
|
||||
longitude: 121.4737,
|
||||
now: new Date('2026-06-18T04:00:00.000Z')
|
||||
});
|
||||
assert.equal(stores[0].name, 'M03A Store');
|
||||
assert.equal(stores[0].distanceMeters, 0);
|
||||
assert.ok(stores[1].distanceMeters > stores[0].distanceMeters);
|
||||
assert.equal(stores.every((store) => store.city === '上海市'), true);
|
||||
assert.equal((await repository.findStores({
|
||||
tenantId: context.tenantId, city: '不存在的城市'
|
||||
})).length, 0);
|
||||
}
|
||||
|
||||
async function assertContentManagement(pool, context) {
|
||||
const [adminRows] = await pool.query(
|
||||
`SELECT u.id FROM qipai_users u
|
||||
@@ -488,7 +520,8 @@ try {
|
||||
{ version: '2026061805', name: 'm02c_rbac' },
|
||||
{ version: '2026061806', name: 'm02d_user_management' },
|
||||
{ version: '2026061807', name: 'm03a_store_room_domain' },
|
||||
{ version: '2026061808', name: 'm03b_decoration_ads_media' }
|
||||
{ version: '2026061808', name: 'm03b_decoration_ads_media' },
|
||||
{ version: '2026061809', name: 'm03c_store_discovery' }
|
||||
]);
|
||||
await assertTaskDurability(pool);
|
||||
const loginContext = await assertPlatformTenantIsolation(pool);
|
||||
@@ -496,6 +529,7 @@ try {
|
||||
await assertUserManagement(pool, loginContext);
|
||||
await assertStoreRoomDomain(pool, loginContext);
|
||||
await assertContentManagement(pool, loginContext);
|
||||
await assertStoreDiscovery(pool, loginContext);
|
||||
await assertLegacyCompatibility(pool);
|
||||
console.log('PASS: first up, verify, tenant isolation and revocable auth checks completed.');
|
||||
|
||||
@@ -515,7 +549,8 @@ try {
|
||||
{ version: '2026061805', name: 'm02c_rbac' },
|
||||
{ version: '2026061806', name: 'm02d_user_management' },
|
||||
{ version: '2026061807', name: 'm03a_store_room_domain' },
|
||||
{ version: '2026061808', name: 'm03b_decoration_ads_media' }
|
||||
{ version: '2026061808', name: 'm03b_decoration_ads_media' },
|
||||
{ version: '2026061809', name: 'm03c_store_discovery' }
|
||||
]);
|
||||
await assertLegacyCompatibility(pool);
|
||||
console.log('PASS: second up and verify restored the schema.');
|
||||
@@ -560,7 +595,10 @@ try {
|
||||
'tenant-isolated media asset',
|
||||
'versioned decoration publish and archive',
|
||||
'store advertisement delivery scope',
|
||||
'platform advertisement rejection'
|
||||
'platform advertisement rejection',
|
||||
'city fallback store filtering',
|
||||
'server-side distance sorting',
|
||||
'empty manual city result'
|
||||
]
|
||||
}, null, 2));
|
||||
} finally {
|
||||
|
||||
Reference in New Issue
Block a user