feat(sgw): Add new services and utilities for ship, trip, and photo management

This commit is contained in:
Lê Tuấn Anh
2026-01-23 15:18:02 +07:00
parent e5b388505a
commit 1a06328c77
75 changed files with 9749 additions and 8 deletions

View File

@@ -0,0 +1,39 @@
import shipAlarmIcon from '../../../assets/ship_alarm.png';
import shipAlarmFishingIcon from '../../../assets/ship_alarm_fishing.png';
import shipOnlineIcon from '../../../assets/ship_online.png';
import shipOnlineFishingIcon from '../../../assets/ship_online_fishing.png';
import shipUndefineIcon from '../../../assets/ship_undefine.png';
import shipWarningIcon from '../../../assets/ship_warning.png';
import shipWarningFishingIcon from '../../../assets/ship_warning_fishing.png';
import shipSosIcon from '../../../assets/sos_icon.png';
export const getShipIcon = (type: number, isFishing: boolean) => {
if (type === 1 && !isFishing) {
return shipWarningIcon;
} else if (type === 2 && !isFishing) {
return shipAlarmIcon;
} else if (type === 0 && !isFishing) {
return shipOnlineIcon;
} else if (type === 1 && isFishing) {
return shipWarningFishingIcon;
} else if (type === 2 && isFishing) {
return shipAlarmFishingIcon;
} else if (type === 0 && isFishing) {
return shipOnlineFishingIcon;
} else if (type === 3) {
return shipSosIcon;
} else {
return shipUndefineIcon;
}
};
export const convertToDMS = (value: number, isLat: boolean): string => {
const deg = Math.floor(Math.abs(value));
const minFloat = (Math.abs(value) - deg) * 60;
const min = Math.floor(minFloat);
const sec = (minFloat - min) * 60;
const direction = value >= 0 ? (isLat ? 'N' : 'E') : isLat ? 'S' : 'W';
return `${deg}°${min}'${sec.toFixed(2)}"${direction}`;
};

View File

@@ -0,0 +1,38 @@
import { SGW_ROUTE_PHOTO } from '@/constants/slave/sgw/routes';
import { request } from '@umijs/max';
/**
* Get photo from server
* @param type Type of photo ('ship' or 'people')
* @param id ID of the entity
* @returns Photo as ArrayBuffer
*/
export async function apiGetPhoto(
type: SgwModel.PhotoGetParams['type'],
id: string,
): Promise<ArrayBuffer> {
return request<ArrayBuffer>(`${SGW_ROUTE_PHOTO}/${type}/${id}/main`, {
method: 'GET',
responseType: 'arraybuffer',
});
}
/**
* Upload photo to server
* @param type Type of photo ('ship' or 'people')
* @param id ID of the entity
* @param file File to upload
*/
export async function apiUploadPhoto(
type: SgwModel.PhotoUploadParams['type'],
id: string,
file: File,
): Promise<void> {
const formData = new FormData();
formData.append('file', file);
return request<void>(`${SGW_ROUTE_PHOTO}/${type}/${id}/main`, {
method: 'POST',
data: formData,
});
}

View File

@@ -0,0 +1,136 @@
import {
SGW_ROUTE_PORTS,
SGW_ROUTE_SHIP_GROUPS,
SGW_ROUTE_SHIP_TYPES,
SGW_ROUTE_SHIPS,
} from '@/constants/slave/sgw/routes';
import { request } from '@umijs/max';
// ========== Ship CRUD Operations ==========
/**
* Add a new ship
*/
export async function apiAddShip(
params: SgwModel.ShipCreateParams,
): Promise<SgwModel.ShipDetail> {
return request<SgwModel.ShipDetail>(SGW_ROUTE_SHIPS, {
method: 'POST',
data: params,
});
}
/**
* Update an existing ship
*/
export async function apiUpdateShip(
id: string,
update: SgwModel.ShipUpdateParams,
): Promise<SgwModel.ShipDetail> {
return request<SgwModel.ShipDetail>(`${SGW_ROUTE_SHIPS}/${id}`, {
method: 'PUT',
data: update,
});
}
/**
* Delete a ship by ID
*/
export async function apiDeleteShip(id: string): Promise<void> {
return request<void>(`${SGW_ROUTE_SHIPS}/${id}`, {
method: 'DELETE',
});
}
/**
* Query ships with filters
*/
export async function apiQueryShips(
params?: SgwModel.ShipQueryParams,
): Promise<SgwModel.ShipQueryResponse> {
return request<SgwModel.ShipQueryResponse>(SGW_ROUTE_SHIPS, {
params: params,
});
}
/**
* Get ship detail by thing ID
*/
export async function apiViewShip(
thingId: string,
): Promise<SgwModel.ShipDetail> {
return request<SgwModel.ShipDetail>(`${SGW_ROUTE_SHIPS}/${thingId}`);
}
/**
* Get all ship types
*/
export async function apiGetShipTypes(): Promise<SgwModel.ShipType[]> {
return request<SgwModel.ShipType[]>(SGW_ROUTE_SHIP_TYPES);
}
// ========== Ship Group Operations ==========
/**
* Add a new ship group
*/
export async function apiAddShipGroup(
params: SgwModel.ShipGroupCreateParams,
): Promise<SgwModel.ShipGroup> {
return request<SgwModel.ShipGroup>(SGW_ROUTE_SHIP_GROUPS, {
method: 'POST',
data: params,
});
}
/**
* Get all ship groups
*/
export async function apiGetShipGroups(): Promise<
SgwModel.GroupShipResponse[]
> {
return request<SgwModel.GroupShipResponse[]>(SGW_ROUTE_SHIP_GROUPS);
}
/**
* List all ship groups (alternative method)
*/
export async function apiListGroupShips(): Promise<SgwModel.ShipGroup[]> {
return request<SgwModel.ShipGroup[]>(SGW_ROUTE_SHIP_GROUPS);
}
/**
* Update a ship group
*/
export async function apiUpdateShipGroup(
id: string,
update: SgwModel.ShipGroupUpdateParams,
): Promise<SgwModel.ShipGroup> {
return request<SgwModel.ShipGroup>(`${SGW_ROUTE_SHIP_GROUPS}/${id}`, {
method: 'PUT',
data: update,
});
}
/**
* Delete a ship group
*/
export async function apiDeleteShipGroup(id: string): Promise<void> {
return request<void>(`${SGW_ROUTE_SHIP_GROUPS}/${id}`, {
method: 'DELETE',
});
}
// ========== Port Operations ==========
/**
* Query ports with filters
*/
export async function apiQueryPorts(
params?: SgwModel.PortQueryParams,
): Promise<SgwModel.PortQueryResponse> {
return request<SgwModel.PortQueryResponse>(SGW_ROUTE_PORTS, {
method: 'POST',
data: params,
});
}

View File

@@ -0,0 +1,224 @@
import {
SGW_ROUTE_CREW,
SGW_ROUTE_GET_FISH,
SGW_ROUTE_HAUL_HANDLE,
SGW_ROUTE_TRIP_CREW,
SGW_ROUTE_TRIPS,
SGW_ROUTE_TRIPS_BY_ID,
SGW_ROUTE_TRIPS_CREWS,
SGW_ROUTE_TRIPS_LAST,
SGW_ROUTE_TRIPS_LIST,
SGW_ROUTE_UPDATE_FISHING_LOGS,
SGW_ROUTE_UPDATE_TRIP_STATUS,
} from '@/constants/slave/sgw/routes';
import { request } from '@umijs/max';
/**
* Update trip state/status
* @param body Trip state update request
*/
export async function apiUpdateTripState(
body: SgwModel.TripUpdateStateRequest,
) {
return request(SGW_ROUTE_UPDATE_TRIP_STATUS, {
method: 'PUT',
data: body,
});
}
/**
* Start a new haul (fishing log)
* @param body New fishing log request
*/
export async function apiStartNewHaul(body: SgwModel.NewFishingLogRequest) {
return request(SGW_ROUTE_HAUL_HANDLE, {
method: 'PUT',
data: body,
});
}
/**
* Get list of fish species
*/
export async function apiGetFishSpecies() {
return request<SgwModel.FishSpeciesResponse[]>(SGW_ROUTE_GET_FISH);
}
/**
* Update fishing logs information
* @param body Fishing log data
*/
export async function apiUpdateFishingLogs(body: SgwModel.FishingLog) {
return request(SGW_ROUTE_UPDATE_FISHING_LOGS, {
method: 'PUT',
data: body,
});
}
/**
* Create a new trip for a thing (device)
* @param thing_id Thing/Device ID
* @param params Trip creation parameters
*/
export async function apiCreateTrip(
thing_id: string,
params: SgwModel.TripCreateParams,
): Promise<SgwModel.Trip> {
return request<SgwModel.Trip>(`${SGW_ROUTE_TRIPS}/${thing_id}`, {
method: 'POST',
data: params,
});
}
/**
* Query trips list with filters
* @param params Query parameters
*/
export async function apiQueryTrips(
params: SgwModel.TripQueryParams,
): Promise<SgwModel.TripQueryResponse> {
return request<SgwModel.TripQueryResponse>(SGW_ROUTE_TRIPS_LIST, {
method: 'POST',
data: params,
});
}
/**
* Update a trip by ID
* @param trip_id Trip ID
* @param update Trip update parameters
*/
export async function apiUpdateTrip(
trip_id: string,
update: SgwModel.TripUpdateParams,
): Promise<SgwModel.Trip> {
return request<SgwModel.Trip>(`${SGW_ROUTE_TRIPS}/${trip_id}`, {
method: 'PUT',
data: update,
});
}
/**
* Delete trip(s)
* @param data Trip deletion parameters (array of trip IDs)
*/
export async function apiDeleteTrip(
data: SgwModel.TripDeleteParams,
): Promise<void> {
return request(SGW_ROUTE_TRIPS, {
method: 'DELETE',
data: data,
});
}
/**
* Query last trip for a thing (device)
* @param thing_id Thing/Device ID
*/
export async function apiQueryLastTrips(
thing_id: string,
): Promise<SgwModel.Trip> {
return request<SgwModel.Trip>(`${SGW_ROUTE_TRIPS_LAST}/${thing_id}`);
}
/**
* Get trip by ID
* @param trip_id Trip ID
*/
export async function apiGetTripById(trip_id: string): Promise<SgwModel.Trip> {
return request<SgwModel.Trip>(`${SGW_ROUTE_TRIPS_BY_ID}/${trip_id}`);
}
/* ===========================
Crew Management APIs
=========================== */
/**
* Create a new crew member
* @param data Crew creation parameters
*/
export async function apiCreateCrew(
data: SgwModel.CrewCreateParams,
): Promise<SgwModel.TripCrewPerson> {
return request<SgwModel.TripCrewPerson>(SGW_ROUTE_CREW, {
method: 'POST',
data: data,
});
}
/**
* Get crew member by ID
* @param crew_id Crew member ID
*/
export async function apiGetCrew(
crew_id: string,
): Promise<SgwModel.TripCrewPerson> {
return request<SgwModel.TripCrewPerson>(`${SGW_ROUTE_TRIPS}/crew/${crew_id}`);
}
/**
* Update crew member information
* @param crew_id Crew member ID
* @param update Crew update parameters
*/
export async function apiUpdateCrew(
crew_id: string,
update: SgwModel.CrewUpdateParams,
): Promise<SgwModel.TripCrewPerson> {
return request<SgwModel.TripCrewPerson>(`${SGW_ROUTE_CREW}/${crew_id}`, {
method: 'PUT',
data: update,
});
}
/**
* Add crew member to a trip
* @param data Trip crew creation parameters
*/
export async function apiAddTripCrew(
data: SgwModel.TripCrewCreateParams,
): Promise<void> {
return request(SGW_ROUTE_TRIP_CREW, {
method: 'POST',
data: data,
});
}
/**
* Get all crew members for a trip
* @param trip_id Trip ID
*/
export async function apiGetTripCrew(
trip_id: string,
): Promise<SgwModel.TripCrewQueryResponse> {
return request<SgwModel.TripCrewQueryResponse>(
`${SGW_ROUTE_TRIPS_CREWS}/${trip_id}`,
);
}
/**
* Update trip crew information
* @param update Trip crew update parameters
*/
export async function apiUpdateTripCrew(
update: SgwModel.TripCrewUpdateParams,
): Promise<void> {
return request(SGW_ROUTE_TRIP_CREW, {
method: 'PUT',
data: update,
});
}
/**
* Remove crew member from a trip
* @param trip_id Trip ID
* @param crew_id Crew member ID
*/
export async function apiDeleteTripCrew(
trip_id: string,
crew_id: string,
): Promise<void> {
return request(`${SGW_ROUTE_TRIP_CREW}/${trip_id}/${crew_id}`, {
method: 'DELETE',
});
}

View File

@@ -0,0 +1,65 @@
import {
SGW_ROUTE_BANZONES,
SGW_ROUTE_BANZONES_LIST,
} from '@/constants/slave/sgw/routes';
import { request } from '@umijs/max';
/**
* Get all banzones with pagination and search
* @param body Search and pagination parameters
*/
export async function apiGetAllBanzones(
body: MasterModel.SearchPaginationBody,
) {
return request<SgwModel.ZoneResponse>(SGW_ROUTE_BANZONES_LIST, {
method: 'POST',
data: body,
});
}
/**
* Remove a banzone
* @param id Banzone ID
* @param groupID Group ID
*/
export async function apiRemoveBanzone(id: string, groupID: string) {
return request(`${SGW_ROUTE_BANZONES}/${id}/${groupID}`, {
method: 'DELETE',
});
}
/**
* Get banzone by ID
* @param zoneId Banzone ID
*/
export async function apiGetZoneById(
zoneId: string,
): Promise<SgwModel.Banzone> {
return request<SgwModel.Banzone>(`${SGW_ROUTE_BANZONES}/${zoneId}`);
}
/**
* Create a new banzone
* @param body Banzone data
*/
export async function apiCreateBanzone(body: SgwModel.ZoneBodyRequest) {
return request(SGW_ROUTE_BANZONES, {
method: 'POST',
data: body,
});
}
/**
* Update banzone by ID
* @param id Banzone ID
* @param body Updated banzone data
*/
export async function apiUpdateBanzone(
id: string,
body: SgwModel.ZoneBodyRequest,
) {
return request(`${SGW_ROUTE_BANZONES}/${id}`, {
method: 'PUT',
data: body,
});
}

View File

@@ -2,6 +2,7 @@
// 该文件由 OneAPI 自动生成,请勿手动修改!
declare namespace SgwModel {
// Thing
interface ThingMedata extends MasterModel.ThingMetadata {
gps?: string;
gps_time?: string;
@@ -22,4 +23,442 @@ declare namespace SgwModel {
type SgwThingsResponse = MasterModel.ThingsResponse<SgwModel.ThingMedata>;
type SgwThing = MasterModel.Thing<SgwModel.ThingMedata>;
// Ship
interface ShipMetadata {
crew_count?: number;
home_port?: string;
home_port_point?: string;
ship_type?: string;
trip_arrival_port?: string;
trip_arrival_port_point?: string;
trip_arrival_time?: Date;
trip_depart_port?: string;
trip_depart_port_point?: string;
trip_departure_time?: Date;
trip_id?: string;
trip_name?: string;
trip_state?: number;
}
interface ShipType {
id?: number;
name?: string;
description?: string;
}
interface ShipDetail {
id?: string;
thing_id?: string;
owner_id?: string;
name?: string;
ship_type?: number;
home_port?: number;
ship_length?: number;
ship_power?: number;
reg_number?: string;
imo_number?: string;
mmsi_number?: string;
fishing_license_number?: string;
fishing_license_expiry_date?: Date | string;
province_code?: string;
ship_group_id?: string | null;
created_at?: Date | string;
updated_at?: Date | string;
metadata?: ShipMetadata;
}
interface ShipCreateParams {
thing_id?: string;
name?: string;
reg_number?: string;
ship_type?: number;
ship_length?: number;
ship_power?: number;
ship_group_id?: string;
home_port?: number;
fishing_license_number?: string;
fishing_license_expiry_date?: string;
}
interface ShipUpdateParams {
name?: string;
reg_number?: string;
ship_type?: number;
ship_group_id?: string | null;
ship_length?: number;
ship_power?: number;
home_port?: number;
fishing_license_number?: string;
fishing_license_expiry_date?: string;
metadata?: Record<string, unknown>;
}
interface ShipQueryParams {
offset?: number;
limit?: number;
order?: string;
dir?: 'asc' | 'desc';
name?: string;
registration_number?: string;
ship_type?: number;
ship_group_id?: string;
thing_id?: string;
}
interface ShipQueryResponse {
ships: ShipDetail[];
total: number;
offset: number;
limit: number;
}
interface ShipsResponse<T extends ShipMetadata = ShipMetadata> {
total?: number;
offset?: number;
limit?: number;
order?: string;
direction?: string;
Ship?: Ship<T>[];
}
// Ship Group
interface ShipGroup {
id: string;
name: string;
owner_id?: string;
description?: string;
created_at?: Date | string;
updated_at?: Date | string;
metadata?: Record<string, unknown>;
}
interface GroupShipResponse {
id?: string;
name?: string;
owner_id?: string;
description?: string;
}
interface ShipGroupCreateParams {
name: string;
description?: string;
metadata?: Record<string, unknown>;
}
interface ShipGroupUpdateParams {
name?: string;
description?: string;
metadata?: Record<string, unknown>;
}
// Port
interface Port {
id: number;
name: string;
type: string;
classification: string;
position_point: string;
has_origin_confirm: boolean;
province_code: string;
updated_at: string;
is_deleted: boolean;
}
interface PortQueryParams {
name?: string;
order?: string;
dir?: 'asc' | 'desc';
limit?: number;
offset?: number;
metadata?: {
province_code?: string;
};
}
interface PortQueryResponse {
total: number;
offset: number;
limit: number;
ports: Port[];
}
// Trip Management
interface FishingGear {
name: string;
number: string;
}
interface TripCost {
type: string;
unit: string;
amount: string;
total_cost: string;
cost_per_unit: string;
}
interface TripCrewPerson {
personal_id: string;
name: string;
phone: string;
email: string;
birth_date: Date;
note: string;
address: string;
created_at: Date;
updated_at: Date;
}
interface TripCrews {
role: string;
joined_at: Date;
left_at: Date | null;
note: string | null;
Person: TripCrewPerson;
}
interface CrewCreateParams {
personal_id: string;
name: string;
phone?: string;
email?: string;
birth_date?: string;
address?: string;
note?: string;
}
interface CrewUpdateParams {
name?: string;
phone?: string;
email?: string;
birth_date?: string;
address?: string;
note?: string;
}
interface TripCrewCreateParams {
trip_id: string;
personal_id: string;
role: string;
note?: string;
}
interface TripCrewUpdateParams {
trip_id: string;
personal_id: string;
role?: string;
note?: string;
}
interface TripCrewQueryResponse {
trip_crews: TripCrews[];
total?: number;
}
interface FishingLogInfo {
fish_species_id?: number;
fish_name?: string;
catch_number?: number;
catch_unit?: string;
fish_size?: number;
fish_rarity?: number;
fish_condition?: string;
gear_usage?: string;
}
interface FishingLog {
fishing_log_id?: string;
trip_id: string;
start_at: Date;
end_at: Date;
start_lat: number;
start_lon: number;
haul_lat: number;
haul_lon: number;
status: number;
weather_description: string;
info?: FishingLogInfo[];
sync: boolean;
}
interface NewFishingLogRequest {
trip_id: string;
start_at: Date;
start_lat: number;
start_lon: number;
weather_description: string;
}
interface FishSpecies {
id: number;
name: string;
scientific_name?: string;
description?: string;
}
interface FishSpeciesResponse extends FishSpecies {}
interface Trip {
id: string;
ship_id: string;
ship_length: number;
vms_id: string;
name: string;
fishing_gears: FishingGear[];
crews?: TripCrews[];
departure_time: string;
departure_port_id: number;
arrival_time: string;
arrival_port_id: number;
fishing_ground_codes: number[];
total_catch_weight: number | null;
total_species_caught: number | null;
trip_cost: TripCost[];
trip_status: number;
approved_by: string;
notes: string | null;
fishing_logs: FishingLog[] | null;
sync: boolean;
}
interface TripUpdateStateRequest {
status: number;
note?: string;
}
interface TripCreateParams {
name: string;
departure_time: string;
departure_port_id: number;
arrival_time?: string;
arrival_port_id?: number;
fishing_ground_codes?: number[];
fishing_gears?: FishingGear[];
crews?: TripCrews[];
trip_cost?: TripCost[];
notes?: string;
}
interface TripFormValues {
name: string;
departure_time: any; // dayjs object or string
departure_port_id: number;
arrival_time?: any; // dayjs object or string
arrival_port_id?: number;
fishing_ground_codes?: number[];
fishing_gear?: FishingGear[];
trip_cost?: TripCost[];
ship_id?: string;
}
interface TripQueryParams {
name?: string;
order?: string;
dir?: 'asc' | 'desc';
offset?: number;
limit?: number;
metadata?: {
from?: string;
to?: string;
ship_name?: string;
reg_number?: string;
province_code?: string;
owner_id?: string;
ship_id?: string;
status?: string;
};
}
interface TripQueryResponse {
trips: Trip[];
total: number;
offset: number;
limit: number;
}
interface TripUpdateParams {
name?: string;
departure_time?: string;
departure_port_id?: number;
arrival_time?: string;
arrival_port_id?: number;
fishing_ground_codes?: number[];
fishing_gears?: FishingGear[];
crews?: TripCrews[];
trip_cost?: TripCost[];
trip_status?: number;
notes?: string;
}
interface TripDeleteParams {
trip_ids: string[];
}
// Photo Management
interface PhotoGetParams {
type: 'ship' | 'people';
id: string;
}
interface PhotoUploadParams {
type: 'ship' | 'people';
id: string;
file: File;
}
// Banzone Management
interface Banzone {
id?: string;
name?: string;
province_code?: string;
type?: number;
conditions?: Condition[];
description?: string;
geometry?: string;
enabled?: boolean;
created_at?: Date;
updated_at?: Date;
}
interface Condition {
max?: number;
min?: number;
type?: 'length_limit' | 'month_range' | 'date_range';
to?: number;
from?: number;
}
interface Geom {
geom_type?: number;
geom_poly?: string;
geom_lines?: string;
geom_point?: string;
geom_radius?: number;
}
interface ZoneResponse {
total?: number;
offset?: number;
limit?: number;
banzones?: Banzone[];
}
interface ZoneBodyRequest {
name?: string;
province_code?: string;
type?: number;
conditions?: Condition[];
description?: string;
geometry?: string;
enabled?: boolean;
}
}
declare namespace WsTypes {
interface WsThingResponse {
thing_id?: string;
key?: string;
data?: string;
time?: number;
}
}