Files
WenwuMap/apps/api/src/routes/routes.service.ts
T
selfrelease 2d847e154f chore: 初始化仓库
中华文明全图鉴——文物全图系统(PC Web 地图 + NestJS API + 管理后台)。
含三大 IP(文物南迁北归 / 国宝海外回归 / 博物馆手艺人)、AI 文物对话、
文物地图与详情、以及 demo-video-kit 演示视频生成工具。
2026-06-13 20:55:44 +08:00

67 lines
1.9 KiB
TypeScript

import { Injectable, NotFoundException } from "@nestjs/common";
import { DatabaseService } from "../database/database.service";
export interface RouteRow {
code: string;
title: string;
type: string;
color: string | null;
summary: string | null;
artifact_id: string | null;
artifact_name: string | null;
artifact_dynasty: string | null;
artifact_category: string | null;
institution_name: string | null;
}
export interface StopRow {
seq: number;
name: string;
lng: number;
lat: number;
year_label: string | null;
event: string | null;
}
export type RouteDetail = RouteRow & { stops: StopRow[] };
const ROUTE_SELECT = `
SELECT nr.code, nr.title, nr.type, nr.color, nr.summary, nr.artifact_id,
a.name AS artifact_name, a.dynasty AS artifact_dynasty, a.category AS artifact_category,
i.name AS institution_name
FROM narrative_routes nr
LEFT JOIN artifacts a ON a.id = nr.artifact_id
LEFT JOIN artifact_locations al ON al.artifact_id = a.id AND al.is_current = true
LEFT JOIN institutions i ON i.id = al.institution_id`;
@Injectable()
export class RoutesService {
constructor(private db: DatabaseService) {}
async list(): Promise<RouteRow[]> {
const { rows } = await this.db.query<RouteRow>(
`${ROUTE_SELECT} ORDER BY nr.type, nr.code`
);
return rows;
}
async getByCode(code: string): Promise<RouteDetail> {
const { rows } = await this.db.query<RouteRow>(
`${ROUTE_SELECT} WHERE nr.code = $1 LIMIT 1`,
[code]
);
const route = rows[0];
if (!route) throw new NotFoundException("路线不存在");
const { rows: stops } = await this.db.query<StopRow>(
`SELECT seq, name, lng, lat, year_label, event
FROM route_stops rs
JOIN narrative_routes nr ON nr.id = rs.route_id
WHERE nr.code = $1
ORDER BY rs.seq`,
[code]
);
return { ...route, stops };
}
}