feat(M08-A): 接入顾客端订单开门
This commit is contained in:
@@ -3,6 +3,10 @@ import { z } from 'zod';
|
||||
import type { AuthRepository } from '../auth/auth-repository.js';
|
||||
import { authenticateAccessToken } from '../auth/authenticate.js';
|
||||
import type { AccessProfile } from '../auth/rbac-repository.js';
|
||||
import {
|
||||
CustomerDeviceAccessError,
|
||||
type CustomerDeviceAccessRepository
|
||||
} from '../devices/customer-device-access-repository.js';
|
||||
import {
|
||||
DeviceControlError,
|
||||
type CommandContext,
|
||||
@@ -75,6 +79,10 @@ const socketTaskSchema = contextSchema.extend({
|
||||
const socketClearTaskSchema = contextSchema.extend({
|
||||
taskNum: z.number().int().min(0).max(20)
|
||||
});
|
||||
const orderParams = z.object({ orderId: id });
|
||||
const customerOpenDoorSchema = z.object({
|
||||
delayTime: z.number().int().min(1).max(14).default(4)
|
||||
}).strict();
|
||||
|
||||
export interface DeviceControlRouteOptions {
|
||||
service: Pick<DeviceControlService,
|
||||
@@ -82,6 +90,7 @@ export interface DeviceControlRouteOptions {
|
||||
| 'startTask' | 'extendTask' | 'cancelTask' | 'pairSubLock' | 'controlSubLock'
|
||||
| 'readSmartSocket' | 'switchSmartSocket' | 'scheduleSmartSocket'
|
||||
| 'clearSmartSocketTask'>;
|
||||
customerAccess?: Pick<CustomerDeviceAccessRepository, 'getDoorContext'>;
|
||||
authRepository: Pick<AuthRepository, 'validateSession'>;
|
||||
accessControl: { getAccessProfile(tenantId: string, userId: string): Promise<AccessProfile> };
|
||||
jwtSecret: string;
|
||||
@@ -119,6 +128,70 @@ export async function registerDeviceControlRoutes(
|
||||
register('/admin-api/device-control/socket/task/clear', socketClearTaskSchema,
|
||||
(context, body) => options.service.clearSmartSocketTask(context, body.taskNum));
|
||||
|
||||
app.post('/app-api/orders/:orderId/open-door', async (request, reply) => {
|
||||
if (!options.customerAccess) {
|
||||
return reply.status(404).send({
|
||||
code: 'CUSTOMER_DEVICE_CONTROL_NOT_AVAILABLE',
|
||||
message: 'Customer device control is not available.',
|
||||
traceId: request.traceId
|
||||
});
|
||||
}
|
||||
const params = orderParams.safeParse(request.params);
|
||||
const body = customerOpenDoorSchema.safeParse(request.body ?? {});
|
||||
if (!params.success || !body.success) return invalid(reply, request.traceId);
|
||||
const auth = await authenticateAccessToken(
|
||||
request.headers.authorization, options.authRepository, options.jwtSecret
|
||||
);
|
||||
if (!auth) {
|
||||
return reply.status(401).send({
|
||||
code: 'AUTH_SESSION_INVALID', message: 'Authentication required.',
|
||||
traceId: request.traceId
|
||||
});
|
||||
}
|
||||
try {
|
||||
const order = await options.customerAccess.getDoorContext({
|
||||
tenantId: auth.session.tenantId,
|
||||
userId: auth.session.user.id,
|
||||
orderId: params.data.orderId
|
||||
});
|
||||
const context: CommandContext = {
|
||||
tenantId: auth.session.tenantId,
|
||||
storeId: order.storeId,
|
||||
roomId: order.roomId,
|
||||
orderId: order.orderId,
|
||||
traceId: request.traceId,
|
||||
access: {
|
||||
roles: ['CUSTOMER'],
|
||||
capabilities: ['device.write'],
|
||||
storeIds: [order.storeId]
|
||||
},
|
||||
expiresAt: new Date(Date.now() + 30000)
|
||||
};
|
||||
return {
|
||||
code: 0,
|
||||
data: await options.service.controlDoor(context, {
|
||||
order: 'open',
|
||||
holdopen: 0,
|
||||
delayTime: body.data.delayTime
|
||||
}),
|
||||
traceId: request.traceId
|
||||
};
|
||||
} catch (error) {
|
||||
if (error instanceof CustomerDeviceAccessError) {
|
||||
return reply.status(403).send({
|
||||
code: error.code,
|
||||
message: 'The order does not allow door access.',
|
||||
traceId: request.traceId
|
||||
});
|
||||
}
|
||||
if (!(error instanceof DeviceControlError)) throw error;
|
||||
const status = error.code === 'DEVICE_OFFLINE' ? 409 : 400;
|
||||
return reply.status(status).send({
|
||||
code: error.code, message: 'Device control was rejected.', traceId: request.traceId
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function register<T extends z.ZodTypeAny>(
|
||||
url: string,
|
||||
schema: T,
|
||||
|
||||
Reference in New Issue
Block a user