feat(M08-D): 补门店与房间管理
This commit is contained in:
@@ -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([])
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user