feat(M08-D): 补门店与房间管理

This commit is contained in:
Codex
2026-08-10 11:40:09 +08:00
parent 04afce4417
commit 1ca783e91b
9 changed files with 1017 additions and 14 deletions
+1 -1
View File
@@ -29,7 +29,7 @@ const storeSchema = z.object({
timezone: z.string().trim().min(1).max(64).default('Asia/Shanghai'),
businessStatus: z.enum(['OPEN', 'CLOSED', 'SUSPENDED']).default('OPEN'),
wifiSsid: z.string().trim().max(128).default(''),
wifiPassword: z.string().max(255).default(''),
wifiPassword: z.string().max(255).optional(),
notificationUrl: z.union([z.string().url(), z.literal('')]).default(''),
sortOrder: z.number().int().min(-100000).max(100000).default(0),
businessHours: hoursSchema.default([])
+34 -6
View File
@@ -13,7 +13,7 @@ export interface StoreInput {
timezone: string;
businessStatus: 'OPEN' | 'CLOSED' | 'SUSPENDED';
wifiSsid: string;
wifiPassword: string;
wifiPassword?: string;
notificationUrl: string;
sortOrder: number;
businessHours: Array<{
@@ -60,7 +60,10 @@ interface StoreRow extends RowDataPacket {
id: string; name: string; address: string; city: string; district: string;
longitude: string | null; latitude: string | null;
contactPhone: string; timezone: string; businessStatus: string; wifiSsid: string;
notificationUrl: string; sortOrder: number;
wifiConfigured: number; notificationUrl: string; sortOrder: number;
}
interface StoreBusinessHourRow extends RowDataPacket {
storeId: string; weekday: number; openMinute: number; closeMinute: number; isClosed: number;
}
interface RoomRow extends RowDataPacket {
id: string; storeId: string; categoryName: string; name: string; roomNo: string; capacity: number;
@@ -85,17 +88,42 @@ export class StoreRoomRepository {
`SELECT s.id, s.name, s.address, s.city, s.district, s.longitude, s.latitude,
s.contact_phone AS contactPhone, s.timezone,
s.business_status AS businessStatus, s.wifi_ssid AS wifiSsid,
CASE WHEN s.wifi_password <> '' THEN 1 ELSE 0 END AS wifiConfigured,
s.notification_url AS notificationUrl, s.sort_order AS sortOrder
FROM qipai_stores s
WHERE s.tenant_id = ? AND s.deleted_at IS NULL AND ${scope.sql}
ORDER BY s.sort_order, s.id`,
[actor.tenantId, ...scope.params]
);
if (rows.length === 0) return [];
const storeIds = rows.map((row) => String(row.id));
const [hourRows] = await this.pool.execute<StoreBusinessHourRow[]>(
`SELECT store_id AS storeId, weekday, open_minute AS openMinute,
close_minute AS closeMinute, is_closed AS isClosed
FROM qipai_store_business_hours
WHERE tenant_id = ? AND store_id IN (${storeIds.map(() => '?').join(',')})
ORDER BY store_id, weekday`,
[actor.tenantId, ...storeIds]
);
const hoursByStore = new Map<string, StoreInput['businessHours']>();
for (const hour of hourRows) {
const storeId = String(hour.storeId);
const items = hoursByStore.get(storeId) ?? [];
items.push({
weekday: Number(hour.weekday),
openMinute: Number(hour.openMinute),
closeMinute: Number(hour.closeMinute),
isClosed: Boolean(hour.isClosed)
});
hoursByStore.set(storeId, items);
}
return rows.map((row) => ({
...row,
id: String(row.id),
longitude: row.longitude === null ? null : Number(row.longitude),
latitude: row.latitude === null ? null : Number(row.latitude)
latitude: row.latitude === null ? null : Number(row.latitude),
wifiConfigured: Boolean(row.wifiConfigured),
businessHours: hoursByStore.get(String(row.id)) ?? []
}));
}
@@ -109,7 +137,7 @@ export class StoreRoomRepository {
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[actor.tenantId, input.name, input.address, input.city, input.district, input.longitude ?? null,
input.latitude ?? null, input.contactPhone, input.timezone, input.businessStatus,
input.wifiSsid, input.wifiPassword, input.notificationUrl, input.sortOrder]
input.wifiSsid, input.wifiPassword ?? '', input.notificationUrl, input.sortOrder]
);
const storeId = String(result.insertId);
await this.replaceHours(connection, actor.tenantId, storeId, input.businessHours);
@@ -125,12 +153,12 @@ export class StoreRoomRepository {
`UPDATE qipai_stores SET name = ?, address = ?, city = ?, district = ?,
longitude = ?, latitude = ?,
contact_phone = ?, timezone = ?, business_status = ?, wifi_ssid = ?,
wifi_password = ?, notification_url = ?, sort_order = ?
wifi_password = COALESCE(?, wifi_password), notification_url = ?, sort_order = ?
WHERE tenant_id = ? AND id = ?`,
[input.name, input.address, input.city, input.district,
input.longitude ?? null, input.latitude ?? null,
input.contactPhone, input.timezone, input.businessStatus, input.wifiSsid,
input.wifiPassword, input.notificationUrl, input.sortOrder, actor.tenantId, storeId]
input.wifiPassword ?? null, input.notificationUrl, input.sortOrder, actor.tenantId, storeId]
);
await this.replaceHours(connection, actor.tenantId, storeId, input.businessHours);
await this.audit(connection, actor, 'STORE_UPDATED', 'STORE', storeId);