UPDATE
This commit is contained in:
@@ -16,6 +16,11 @@ const PREFIX = "/api/crop-zoning";
|
||||
const AREA_CACHE_KEY_PREFIX = "crop-zoning:area";
|
||||
const AREA_CACHE_VERSION = "v1";
|
||||
const AREA_CACHE_TTL_MS = 1000 * 60 * 60 * 6;
|
||||
type CropZoningLayerEndpoint =
|
||||
| "area"
|
||||
| "water-need"
|
||||
| "soil-quality"
|
||||
| "cultivation-risk";
|
||||
|
||||
export interface Product {
|
||||
id: string;
|
||||
@@ -217,6 +222,7 @@ function getAreaCacheUserKey(): string {
|
||||
}
|
||||
|
||||
function getAreaCacheKey(
|
||||
endpoint: CropZoningLayerEndpoint,
|
||||
farmUuid: string,
|
||||
page: number,
|
||||
pageSize: number,
|
||||
@@ -225,6 +231,7 @@ function getAreaCacheKey(
|
||||
AREA_CACHE_KEY_PREFIX,
|
||||
AREA_CACHE_VERSION,
|
||||
getAreaCacheUserKey(),
|
||||
endpoint,
|
||||
farmUuid,
|
||||
page,
|
||||
pageSize,
|
||||
@@ -232,6 +239,7 @@ function getAreaCacheKey(
|
||||
}
|
||||
|
||||
function readCachedArea(
|
||||
endpoint: CropZoningLayerEndpoint,
|
||||
farmUuid: string,
|
||||
page: number,
|
||||
pageSize: number,
|
||||
@@ -239,7 +247,9 @@ function readCachedArea(
|
||||
if (typeof window === "undefined") return null;
|
||||
|
||||
try {
|
||||
const raw = localStorage.getItem(getAreaCacheKey(farmUuid, page, pageSize));
|
||||
const raw = localStorage.getItem(
|
||||
getAreaCacheKey(endpoint, farmUuid, page, pageSize),
|
||||
);
|
||||
|
||||
if (!raw) {
|
||||
return null;
|
||||
@@ -248,7 +258,7 @@ function readCachedArea(
|
||||
const parsed = JSON.parse(raw) as CachedAreaEntry;
|
||||
|
||||
if (!parsed?.expiresAt || parsed.expiresAt < Date.now()) {
|
||||
localStorage.removeItem(getAreaCacheKey(farmUuid, page, pageSize));
|
||||
localStorage.removeItem(getAreaCacheKey(endpoint, farmUuid, page, pageSize));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -259,6 +269,7 @@ function readCachedArea(
|
||||
}
|
||||
|
||||
function writeCachedArea(
|
||||
endpoint: CropZoningLayerEndpoint,
|
||||
farmUuid: string,
|
||||
page: number,
|
||||
pageSize: number,
|
||||
@@ -273,7 +284,7 @@ function writeCachedArea(
|
||||
};
|
||||
|
||||
localStorage.setItem(
|
||||
getAreaCacheKey(farmUuid, page, pageSize),
|
||||
getAreaCacheKey(endpoint, farmUuid, page, pageSize),
|
||||
JSON.stringify(payload),
|
||||
);
|
||||
} catch {
|
||||
@@ -290,6 +301,97 @@ function logAreaRequest(
|
||||
console.log(`[crop-zoning][area][${phase}]`, payload);
|
||||
}
|
||||
|
||||
function getLayerEndpointUrl(
|
||||
endpoint: CropZoningLayerEndpoint,
|
||||
params: URLSearchParams,
|
||||
): string {
|
||||
return `${PREFIX}/${endpoint}/?${params.toString()}`;
|
||||
}
|
||||
|
||||
function getLayerArea(
|
||||
endpoint: CropZoningLayerEndpoint,
|
||||
farmUuid: string,
|
||||
options?: { page?: number; pageSize?: number; useCache?: boolean },
|
||||
): Promise<CropZoningAreaResponse> {
|
||||
const page = options?.page ?? 1;
|
||||
const pageSize = options?.pageSize ?? 10;
|
||||
const useCache = options?.useCache ?? true;
|
||||
|
||||
if (useCache) {
|
||||
const cached = readCachedArea(endpoint, farmUuid, page, pageSize);
|
||||
|
||||
if (cached) {
|
||||
logAreaRequest("cache-hit", {
|
||||
endpoint,
|
||||
farmUuid,
|
||||
page,
|
||||
pageSize,
|
||||
pagination: cached.pagination ?? null,
|
||||
taskStatus: cached.task?.status ?? null,
|
||||
zonesCount: cached.zones?.length ?? 0,
|
||||
});
|
||||
return Promise.resolve(cached);
|
||||
}
|
||||
}
|
||||
|
||||
const params = new URLSearchParams({ farm_uuid: farmUuid });
|
||||
|
||||
params.set("page", String(page));
|
||||
params.set("page_size", String(pageSize));
|
||||
|
||||
const requestUrl = getLayerEndpointUrl(endpoint, params);
|
||||
|
||||
logAreaRequest("request", {
|
||||
endpoint,
|
||||
farmUuid,
|
||||
page,
|
||||
pageSize,
|
||||
endpointUrl: requestUrl,
|
||||
});
|
||||
|
||||
return unwrap(
|
||||
apiClient.get<ApiResponse<CropZoningAreaResponse>>(requestUrl),
|
||||
).then((response) => {
|
||||
if ("task_id" in response) {
|
||||
logAreaRequest("response", {
|
||||
endpoint,
|
||||
farmUuid,
|
||||
page,
|
||||
pageSize,
|
||||
taskId: response.task_id,
|
||||
status: response.status,
|
||||
});
|
||||
return normalizeTaskInitResponse(response);
|
||||
}
|
||||
|
||||
const normalized = normalizeAreaResult(response);
|
||||
const taskStatus = normalized.task?.status?.toLowerCase();
|
||||
|
||||
logAreaRequest("response", {
|
||||
endpoint,
|
||||
farmUuid,
|
||||
page,
|
||||
pageSize,
|
||||
taskStatus: normalized.task?.status ?? null,
|
||||
pagination: normalized.pagination ?? null,
|
||||
zonesCount: normalized.zones?.length ?? 0,
|
||||
hasArea: Boolean(normalized.area),
|
||||
});
|
||||
|
||||
if (
|
||||
normalized.area &&
|
||||
taskStatus !== "pending" &&
|
||||
taskStatus !== "processing" &&
|
||||
taskStatus !== "failure" &&
|
||||
taskStatus !== "failed"
|
||||
) {
|
||||
writeCachedArea(endpoint, farmUuid, page, pageSize, normalized);
|
||||
}
|
||||
|
||||
return normalized;
|
||||
});
|
||||
}
|
||||
|
||||
export const cropZoningService = {
|
||||
getProducts(): Promise<{ products: Product[] }> {
|
||||
return unwrap(
|
||||
@@ -323,79 +425,28 @@ export const cropZoningService = {
|
||||
farmUuid: string,
|
||||
options?: { page?: number; pageSize?: number; useCache?: boolean },
|
||||
): Promise<CropZoningAreaResponse> {
|
||||
const page = options?.page ?? 1;
|
||||
const pageSize = options?.pageSize ?? 10;
|
||||
const useCache = options?.useCache ?? true;
|
||||
return getLayerArea("area", farmUuid, options);
|
||||
},
|
||||
|
||||
if (useCache) {
|
||||
const cached = readCachedArea(farmUuid, page, pageSize);
|
||||
getWaterNeedArea(
|
||||
farmUuid: string,
|
||||
options?: { page?: number; pageSize?: number; useCache?: boolean },
|
||||
): Promise<CropZoningAreaResponse> {
|
||||
return getLayerArea("water-need", farmUuid, options);
|
||||
},
|
||||
|
||||
if (cached) {
|
||||
logAreaRequest("cache-hit", {
|
||||
farmUuid,
|
||||
page,
|
||||
pageSize,
|
||||
pagination: cached.pagination ?? null,
|
||||
taskStatus: cached.task?.status ?? null,
|
||||
zonesCount: cached.zones?.length ?? 0,
|
||||
});
|
||||
return Promise.resolve(cached);
|
||||
}
|
||||
}
|
||||
getSoilQualityArea(
|
||||
farmUuid: string,
|
||||
options?: { page?: number; pageSize?: number; useCache?: boolean },
|
||||
): Promise<CropZoningAreaResponse> {
|
||||
return getLayerArea("soil-quality", farmUuid, options);
|
||||
},
|
||||
|
||||
const params = new URLSearchParams({ farm_uuid: farmUuid });
|
||||
|
||||
params.set("page", String(page));
|
||||
params.set("page_size", String(pageSize));
|
||||
|
||||
const endpoint = `${PREFIX}/area/?${params.toString()}`;
|
||||
|
||||
logAreaRequest("request", {
|
||||
farmUuid,
|
||||
page,
|
||||
pageSize,
|
||||
endpoint,
|
||||
});
|
||||
|
||||
return unwrap(
|
||||
apiClient.get<ApiResponse<CropZoningAreaResponse>>(endpoint),
|
||||
).then((response) => {
|
||||
if ("task_id" in response) {
|
||||
logAreaRequest("response", {
|
||||
farmUuid,
|
||||
page,
|
||||
pageSize,
|
||||
taskId: response.task_id,
|
||||
status: response.status,
|
||||
});
|
||||
return normalizeTaskInitResponse(response);
|
||||
}
|
||||
|
||||
const normalized = normalizeAreaResult(response);
|
||||
const taskStatus = normalized.task?.status?.toLowerCase();
|
||||
|
||||
logAreaRequest("response", {
|
||||
farmUuid,
|
||||
page,
|
||||
pageSize,
|
||||
taskStatus: normalized.task?.status ?? null,
|
||||
pagination: normalized.pagination ?? null,
|
||||
zonesCount: normalized.zones?.length ?? 0,
|
||||
hasArea: Boolean(normalized.area),
|
||||
});
|
||||
|
||||
if (
|
||||
normalized.area &&
|
||||
taskStatus !== "pending" &&
|
||||
taskStatus !== "processing" &&
|
||||
taskStatus !== "failure" &&
|
||||
taskStatus !== "failed"
|
||||
) {
|
||||
writeCachedArea(farmUuid, page, pageSize, normalized);
|
||||
}
|
||||
|
||||
return normalized;
|
||||
});
|
||||
getCultivationRiskArea(
|
||||
farmUuid: string,
|
||||
options?: { page?: number; pageSize?: number; useCache?: boolean },
|
||||
): Promise<CropZoningAreaResponse> {
|
||||
return getLayerArea("cultivation-risk", farmUuid, options);
|
||||
},
|
||||
|
||||
getAreaStatus(
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
import { apiClient } from "../client";
|
||||
|
||||
const PREFIX = "/api/sensor-external-api";
|
||||
|
||||
export interface SensorExternalFarmSensor {
|
||||
uuid: string;
|
||||
sensor_catalog_uuid: string | null;
|
||||
physical_device_uuid: string;
|
||||
name: string;
|
||||
sensor_type: string;
|
||||
is_active: boolean;
|
||||
specifications?: Record<string, unknown> | null;
|
||||
power_source?: Record<string, unknown> | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface SensorExternalCatalog {
|
||||
uuid: string;
|
||||
code: string;
|
||||
name: string;
|
||||
description?: string | null;
|
||||
customizable_fields?: unknown[];
|
||||
supported_power_sources?: unknown[];
|
||||
returned_data_fields?: string[];
|
||||
sample_payload?: Record<string, unknown> | null;
|
||||
is_active: boolean;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface SensorExternalRequestLog {
|
||||
id: number;
|
||||
farm_uuid: string;
|
||||
sensor_catalog_uuid: string | null;
|
||||
physical_device_uuid: string;
|
||||
farm_sensor: SensorExternalFarmSensor | null;
|
||||
sensor_catalog: SensorExternalCatalog | null;
|
||||
payload: Record<string, unknown> | null;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface SensorExternalRequestLogsResponse {
|
||||
code: number;
|
||||
msg: string;
|
||||
count: number;
|
||||
next: string | null;
|
||||
previous: string | null;
|
||||
data: SensorExternalRequestLog[];
|
||||
}
|
||||
|
||||
export const sensorExternalApiService = {
|
||||
listRequestLogs(params: {
|
||||
farmUuid: string;
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
}): Promise<SensorExternalRequestLogsResponse> {
|
||||
const searchParams = new URLSearchParams({ farm_uuid: params.farmUuid });
|
||||
|
||||
if (typeof params.page === "number") {
|
||||
searchParams.set("page", String(params.page));
|
||||
}
|
||||
|
||||
if (typeof params.pageSize === "number") {
|
||||
searchParams.set("page_size", String(params.pageSize));
|
||||
}
|
||||
|
||||
return apiClient.get<SensorExternalRequestLogsResponse>(
|
||||
`${PREFIX}/logs/?${searchParams.toString()}`,
|
||||
);
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user