71 lines
1.8 KiB
TypeScript
71 lines
1.8 KiB
TypeScript
/**
|
|
* PortSearchService
|
|
*
|
|
* Domain service for port search and autocomplete
|
|
*
|
|
* Business Rules:
|
|
* - Fuzzy search on port name, city, and code
|
|
* - Return top 10 results by default
|
|
* - Support country filtering
|
|
*/
|
|
|
|
import { Port } from '../entities/port.entity';
|
|
import {
|
|
GetPortsPort,
|
|
PortSearchInput,
|
|
PortSearchOutput,
|
|
GetPortInput,
|
|
} from '../ports/in/get-ports.port';
|
|
import { PortRepository } from '../ports/out/port.repository';
|
|
import { PortNotFoundException } from '../exceptions/port-not-found.exception';
|
|
|
|
export class PortSearchService implements GetPortsPort {
|
|
private static readonly DEFAULT_LIMIT = 10;
|
|
|
|
constructor(private readonly portRepository: PortRepository) {}
|
|
|
|
async search(input: PortSearchInput): Promise<PortSearchOutput> {
|
|
const limit = input.limit || PortSearchService.DEFAULT_LIMIT;
|
|
const query = input.query.trim();
|
|
|
|
if (query.length === 0) {
|
|
return {
|
|
ports: [],
|
|
totalMatches: 0,
|
|
};
|
|
}
|
|
|
|
// Search using repository fuzzy search
|
|
const ports = await this.portRepository.search(query, limit, input.countryFilter);
|
|
|
|
return {
|
|
ports,
|
|
totalMatches: ports.length,
|
|
};
|
|
}
|
|
|
|
async getByCode(input: GetPortInput): Promise<Port> {
|
|
const port = await this.portRepository.findByCode(input.portCode);
|
|
|
|
if (!port) {
|
|
throw new PortNotFoundException(input.portCode);
|
|
}
|
|
|
|
return port;
|
|
}
|
|
|
|
async getByCodes(portCodes: string[]): Promise<Port[]> {
|
|
const ports = await this.portRepository.findByCodes(portCodes);
|
|
|
|
// Check if all ports were found
|
|
const foundCodes = ports.map(p => p.code);
|
|
const missingCodes = portCodes.filter(code => !foundCodes.includes(code));
|
|
|
|
if (missingCodes.length > 0) {
|
|
throw new PortNotFoundException(missingCodes[0]);
|
|
}
|
|
|
|
return ports;
|
|
}
|
|
}
|