fix: resolve dashboard OpenAPI integration issues

- FCRs & Transfer to Laying: add ExampleResponse field to routeMeta and
  inject example payloads into OpenAPI 200 responses for list and detail
  endpoints so dashboard consumers have concrete response shapes to work with

- Chick In: enable GET /api/production/chickins/ list endpoint (was
  commented out); add P_ChickinsGetAll permission constant and wire it
  into the route; add OpenAPI spec entry with query params and example

- Recording GET all: fix N+1 query bottleneck (2-3s response time) by
  pre-fetching approved transfer maps per PFK ID in two batch queries
  before the per-recording loop; add evaluatePopulationMutationStateFromCaches
  that uses the pre-fetched maps and caches hasAnyRecordingOnTransferTargets
  results by transfer ID — reducing per-page query count from ~20-40 to ~10-12

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Adnan Zahir
2026-05-02 10:57:45 +07:00
parent c804c59f05
commit 3768892a17
6 changed files with 370 additions and 38 deletions
+114 -11
View File
@@ -44,14 +44,15 @@ type parameterMeta struct {
}
type routeMeta struct {
Group string
Tag string
Summary string
Description string
Security securityMode
ListStyle bool
QueryParams []parameterMeta
Exclude bool
Group string
Tag string
Summary string
Description string
Security securityMode
ListStyle bool
QueryParams []parameterMeta
ExampleResponse any
Exclude bool
}
func RegisterRoutes(router fiber.Router) {
@@ -187,6 +188,13 @@ func buildOpenAPIDocument(routes []normalizedRoute) map[string]any {
}
openAPIPath := toOpenAPIPath(route.Path)
responseContent := map[string]any{
"schema": successSchema(meta),
}
if meta.ExampleResponse != nil {
responseContent["example"] = meta.ExampleResponse
}
operation := map[string]any{
"summary": meta.Summary,
"description": meta.Description,
@@ -195,9 +203,7 @@ func buildOpenAPIDocument(routes []normalizedRoute) map[string]any {
"200": map[string]any{
"description": "Successful response",
"content": map[string]any{
"application/json": map[string]any{
"schema": successSchema(meta),
},
"application/json": responseContent,
},
},
"401": map[string]any{
@@ -777,6 +783,31 @@ func describeRoute(route normalizedRoute) routeMeta {
{Name: "limit", In: "query", Description: "Page size.", Example: 10},
{Name: "search", In: "query", Description: "Search keyword.", Example: "fcr"},
}
meta.ExampleResponse = map[string]any{
"code": 200, "status": "success", "message": "Get all fcrs successfully",
"meta": map[string]any{"page": 1, "limit": 10, "total_pages": 1, "total_results": 1},
"data": []map[string]any{
{
"id": 1, "name": "FCR Broiler Standard",
"created_user": map[string]any{"id": 1, "name": "Admin"},
"created_at": "2026-01-01T00:00:00Z", "updated_at": "2026-01-01T00:00:00Z",
},
},
}
case "/api/master-data/fcrs/:id":
meta.ExampleResponse = map[string]any{
"code": 200, "status": "success", "message": "Get fcr successfully",
"data": map[string]any{
"id": 1, "name": "FCR Broiler Standard",
"created_user": map[string]any{"id": 1, "name": "Admin"},
"created_at": "2026-01-01T00:00:00Z", "updated_at": "2026-01-01T00:00:00Z",
"fcr_standards": []map[string]any{
{"id": 1, "weight": 0.5, "fcr_number": 1.2, "mortality": 0.5},
{"id": 2, "weight": 1.0, "fcr_number": 1.35, "mortality": 0.3},
{"id": 3, "weight": 1.5, "fcr_number": 1.5, "mortality": 0.25},
},
},
}
case "/api/master-data/flocks":
meta.QueryParams = []parameterMeta{
{Name: "page", In: "query", Description: "Page number.", Example: 1},
@@ -926,6 +957,31 @@ func describeRoute(route normalizedRoute) routeMeta {
{Name: "project_flock_kandang_id", In: "query", Description: "Project flock kandang id.", Required: true, Example: 1, PostmanValue: "{{project_flock_kandang_id}}"},
{Name: "record_date", In: "query", Description: "Recording date (YYYY-MM-DD).", Required: true, Example: "2026-01-01"},
}
case "/api/production/chickins":
meta.QueryParams = []parameterMeta{
{Name: "page", In: "query", Description: "Page number.", Example: 1},
{Name: "limit", In: "query", Description: "Page size.", Example: 10},
{Name: "project_flock_kandang_id", In: "query", Description: "Project flock kandang id filter.", Example: 1, PostmanValue: "{{project_flock_kandang_id}}"},
}
meta.ExampleResponse = map[string]any{
"code": 200, "status": "success", "message": "Get all chickins successfully",
"meta": map[string]any{"page": 1, "limit": 10, "total_pages": 1, "total_results": 1},
"data": []map[string]any{
{
"id": 1, "project_flock_kandang_id": 1,
"chick_in_date": "2026-01-01T00:00:00Z",
"product_warehouse_id": 1,
"product_warehouse": map[string]any{
"id": 1,
"product": map[string]any{"id": 1, "name": "DOC Broiler"},
"warehouse": map[string]any{"id": 1, "name": "Gudang DOC"},
},
"usage_qty": 10000.0, "pending_usage_qty": 0.0, "notes": "",
"created_user": map[string]any{"id": 1, "name": "Admin"},
"created_at": "2026-01-01T00:00:00Z", "updated_at": "2026-01-01T00:00:00Z",
},
},
}
case "/api/production/transfer_layings":
meta.QueryParams = []parameterMeta{
{Name: "page", In: "query", Description: "Page number.", Example: 1},
@@ -937,6 +993,53 @@ func describeRoute(route normalizedRoute) routeMeta {
{Name: "flock_destination", In: "query", Description: "Comma separated destination flock ids.", Example: "3,4"},
{Name: "status", In: "query", Description: "Comma separated status values.", Example: "DRAFT,APPROVED"},
}
meta.ExampleResponse = map[string]any{
"code": 200, "status": "success", "message": "Get all transferLayings successfully",
"meta": map[string]any{"page": 1, "limit": 10, "total_pages": 1, "total_results": 1},
"data": []map[string]any{
{
"id": 1, "transfer_number": "TL-00001",
"transfer_date": "2026-01-15T00:00:00Z",
"economic_cutoff_date": "2026-01-20T00:00:00Z",
"effective_move_date": "2026-01-18T00:00:00Z",
"executed_at": nil, "notes": "",
"from_project_flock": map[string]any{"id": 1, "flock_name": "Flock A Period 1"},
"to_project_flock": map[string]any{"id": 2, "flock_name": "Flock B Period 1"},
"created_by": 1,
"created_user": map[string]any{"id": 1, "name": "Admin"},
"created_at": "2026-01-15T00:00:00Z",
"approval": map[string]any{"step_number": 1, "step_name": "Pengajuan", "action": nil},
},
},
}
case "/api/production/transfer_layings/:id":
meta.ExampleResponse = map[string]any{
"code": 200, "status": "success", "message": "Get transferLaying successfully",
"data": map[string]any{
"id": 1, "transfer_number": "TL-00001",
"transfer_date": "2026-01-15T00:00:00Z",
"economic_cutoff_date": "2026-01-20T00:00:00Z",
"effective_move_date": "2026-01-18T00:00:00Z",
"executed_at": nil, "notes": "",
"from_project_flock": map[string]any{"id": 1, "flock_name": "Flock A Period 1"},
"to_project_flock": map[string]any{"id": 2, "flock_name": "Flock B Period 1"},
"created_by": 1, "created_user": map[string]any{"id": 1, "name": "Admin"},
"created_at": "2026-01-15T00:00:00Z",
"approval": map[string]any{"step_number": 1, "step_name": "Pengajuan", "action": nil},
"sources": []map[string]any{
{
"source_project_flock_kandang": map[string]any{"id": 1, "kandang_id": 1, "project_flock_id": 1, "kandang": map[string]any{"id": 1, "name": "Kandang A"}},
"qty": 5000.0, "note": "",
},
},
"targets": []map[string]any{
{
"target_project_flock_kandang": map[string]any{"id": 2, "kandang_id": 2, "project_flock_id": 2, "kandang": map[string]any{"id": 2, "name": "Kandang B"}},
"qty": 5000.0, "note": "",
},
},
},
}
case "/api/production/uniformities":
meta.QueryParams = []parameterMeta{
{Name: "page", In: "query", Description: "Page number.", Example: 1},