diff --git a/apps/csms/src/routes/charge-points.ts b/apps/csms/src/routes/charge-points.ts index 385dd39..78a7b68 100644 --- a/apps/csms/src/routes/charge-points.ts +++ b/apps/csms/src/routes/charge-points.ts @@ -17,15 +17,25 @@ app.get("/", async (c) => { .where(isAdmin ? undefined : eq(chargePoint.registrationStatus, "Accepted")) .orderBy(desc(chargePoint.createdAt)); - // Attach connectors (connectorId > 0 only, excludes the main-controller row) - const connectors = cps.length - ? await db - .select() - .from(connector) - .where( - sql`${connector.chargePointId} = any(${sql.raw(`array[${cps.map((cp) => `'${cp.id}'`).join(",")}]`)}) and ${connector.connectorId} > 0`, - ) - : []; + if (!cps.length) return c.json([]); + + const cpIdList = sql.raw(`array[${cps.map((cp) => `'${cp.id}'`).join(",")}]`); + + // connectorId > 0: real connectors to display + const connectors = await db + .select() + .from(connector) + .where(sql`${connector.chargePointId} = any(${cpIdList}) and ${connector.connectorId} > 0`); + + // connectorId = 0: whole-station status row + const cpStatusRows = await db + .select({ + chargePointId: connector.chargePointId, + status: connector.status, + errorCode: connector.errorCode, + }) + .from(connector) + .where(sql`${connector.chargePointId} = any(${cpIdList}) and ${connector.connectorId} = 0`); const connectorsByCP: Record = {}; for (const conn of connectors) { @@ -33,10 +43,17 @@ app.get("/", async (c) => { connectorsByCP[conn.chargePointId].push(conn); } + const cpStatusByCP: Record = {}; + for (const row of cpStatusRows) { + cpStatusByCP[row.chargePointId] = { status: row.status, errorCode: row.errorCode }; + } + return c.json( cps.map((cp) => ({ ...cp, connectors: connectorsByCP[cp.id] ?? [], + chargePointStatus: cpStatusByCP[cp.id]?.status ?? null, + chargePointErrorCode: cpStatusByCP[cp.id]?.errorCode ?? null, })), ); }); @@ -88,9 +105,16 @@ app.get("/:id", async (c) => { if (!cp) return c.json({ error: "Not found" }, 404); - const connectors = await db.select().from(connector).where(eq(connector.chargePointId, id)); + const allConnectors = await db.select().from(connector).where(eq(connector.chargePointId, id)); + const cpStatus = allConnectors.find((conn) => conn.connectorId === 0); + const displayConnectors = allConnectors.filter((conn) => conn.connectorId > 0); - return c.json({ ...cp, connectors }); + return c.json({ + ...cp, + connectors: displayConnectors, + chargePointStatus: cpStatus?.status ?? null, + chargePointErrorCode: cpStatus?.errorCode ?? null, + }); }); /** PATCH /api/charge-points/:id — update charge point fields */ @@ -136,8 +160,16 @@ app.patch("/:id", async (c) => { if (!updated) return c.json({ error: "Not found" }, 404); - const connectors = await db.select().from(connector).where(eq(connector.chargePointId, id)); - return c.json({ ...updated, connectors }); + const allConnectors = await db.select().from(connector).where(eq(connector.chargePointId, id)); + const cpStatus = allConnectors.find((conn) => conn.connectorId === 0); + const displayConnectors = allConnectors.filter((conn) => conn.connectorId > 0); + + return c.json({ + ...updated, + connectors: displayConnectors, + chargePointStatus: cpStatus?.status ?? null, + chargePointErrorCode: cpStatus?.errorCode ?? null, + }); }); /** DELETE /api/charge-points/:id — delete a charge point (cascades to connectors, transactions, meter values) */ diff --git a/apps/web/app/dashboard/charge-points/[id]/page.tsx b/apps/web/app/dashboard/charge-points/[id]/page.tsx index 058fca5..b7db530 100644 --- a/apps/web/app/dashboard/charge-points/[id]/page.tsx +++ b/apps/web/app/dashboard/charge-points/[id]/page.tsx @@ -240,6 +240,35 @@ export default function ChargePointDetailPage({ params }: { params: Promise<{ id + {/* Info grid */} + {cp.chargePointStatus && ( +
+ + + {cp.chargePointStatus === "Available" + ? "正常" + : (statusLabelMap[cp.chargePointStatus] ?? cp.chargePointStatus)} + + {cp.chargePointErrorCode && cp.chargePointErrorCode !== "NoError" && ( + <> + · + {cp.chargePointErrorCode} + + )} + 整桩状态 +
+ )} + {/* Info grid */}
{/* Device info — admin only */} diff --git a/apps/web/app/dashboard/charge-points/page.tsx b/apps/web/app/dashboard/charge-points/page.tsx index 71c974e..a88d00f 100644 --- a/apps/web/app/dashboard/charge-points/page.tsx +++ b/apps/web/app/dashboard/charge-points/page.tsx @@ -282,8 +282,9 @@ export default function ChargePointsPage() { {isAdmin && 品牌 / 型号} {isAdmin && 注册状态} 电价(分/kWh) - 最后心跳 - 接口状态 + 最后发现 + 状态 + 接口 {isAdmin && {""}} @@ -297,6 +298,7 @@ export default function ChargePointsPage() { {""} {""} {""} + {""} {isAdmin && {""}} )} @@ -349,6 +351,22 @@ export default function ChargePointsPage() { )} + + {cp.chargePointStatus ? ( +
+ + + {cp.chargePointStatus === "Available" + ? "正常" + : (statusLabelMap[cp.chargePointStatus] ?? cp.chargePointStatus)} + +
+ ) : ( + + )} +
{cp.connectors.length === 0 ? ( diff --git a/apps/web/lib/api.ts b/apps/web/lib/api.ts index 59254ec..6365310 100644 --- a/apps/web/lib/api.ts +++ b/apps/web/lib/api.ts @@ -68,6 +68,8 @@ export type ChargePoint = { lastBootNotificationAt: string | null; feePerKwh: number; connectors: ConnectorSummary[]; + chargePointStatus: string | null; + chargePointErrorCode: string | null; }; export type ChargePointDetail = { @@ -89,6 +91,8 @@ export type ChargePointDetail = { createdAt: string; updatedAt: string; connectors: ConnectorDetail[]; + chargePointStatus: string | null; + chargePointErrorCode: string | null; }; export type Transaction = {