feat: enhance Admin and report services with CRUD operations and intervention links

This commit is contained in:
gabdevele
2026-06-15 11:47:44 +02:00 Unverified
parent d684477ac3
commit 170fadbfe0
13 changed files with 1258 additions and 164 deletions
+13 -3
View File
@@ -26,12 +26,17 @@ Authorization: Bearer <token>
## Reports
- `POST /reports`: create a report.
- `GET /reports`: list reports with optional `status`, `category_id`, `area_id`.
- `GET /report-categories`: list active report categories for authenticated users.
- `GET /operator/teams`: list active intervention teams for operators/admins.
- `GET /operator/reports/assigned`: list reports assigned to the current operator.
- `GET /operator/reports/open`: list open unassigned reports.
- `GET /operator/reports/others`: list reports assigned to other operators.
- `GET /reports/{id}`: report detail.
- `GET /reports/{id}/intervention-summary`: operator-facing report summary with
citizen contact data, media and latest messages.
- `GET /reports/{id}/team-suggestions`: suggest compatible teams by area/category.
- `POST /reports/{id}/intervention-link`: create or rotate public team link.
- `DELETE /reports/{id}/intervention-link`: revoke public team link.
- `PATCH /reports/{id}/status`: update status.
- `POST /reports/{id}/take`: assigned-queue operator takes an open report.
- `POST /reports/{id}/messages`: write a report message.
@@ -51,9 +56,12 @@ Authorization: Bearer <token>
- `GET /me/rewards`: return current points and reward event history.
## Admin Reference Data
- `GET|POST /admin/categories`
- `GET|POST /admin/areas`
- `GET|POST /admin/teams`
- `POST /admin/categories`
- `PATCH /admin/categories/{id}`
- `POST /admin/areas`
- `PATCH /admin/areas/{id}`
- `POST /admin/teams`
- `PATCH /admin/teams/{id}`
- `GET /admin/stats/overview`
- `GET /admin/moderation`: list moderation flags with optional `status`.
- `PATCH /admin/moderation/{id}`: resolve or dismiss a moderation flag, optionally
@@ -63,4 +71,6 @@ Authorization: Bearer <token>
- `PATCH /admin/users/{id}/ban`
## Public Data
- `GET /areas`: list active municipal areas.
- `GET /interventions/{token}`: public intervention summary for external teams.
- `GET /open-data/reports`: anonymized resolved/closed report export.
+468 -69
View File
@@ -136,6 +136,26 @@
],
"type": "object"
},
"AssignmentLinkResponse": {
"additionalProperties": false,
"properties": {
"expires_at": {
"type": "string"
},
"ok": {
"type": "boolean"
},
"url": {
"type": "string"
}
},
"required": [
"ok",
"url",
"expires_at"
],
"type": "object"
},
"Category": {
"additionalProperties": true,
"properties": {
@@ -1037,7 +1057,22 @@
},
"type": "array"
},
"reports_by_team": {
"assignments_by_team": {
"items": {
"additionalProperties": true,
"properties": {
"team": {
"type": "string"
},
"total": {
"type": "integer"
}
},
"type": "object"
},
"type": "array"
},
"resolved_reports_by_team": {
"items": {
"additionalProperties": true,
"properties": {
@@ -1059,7 +1094,8 @@
"reports_by_area",
"avg_resolution_hours",
"reports_by_category",
"reports_by_team",
"assignments_by_team",
"resolved_reports_by_team",
"monthly_reports",
"avg_resolution_hours_by_category",
"avg_resolution_hours_by_area"
@@ -1468,6 +1504,26 @@
],
"type": "object"
},
"TeamSuggestionListResponse": {
"additionalProperties": false,
"properties": {
"ok": {
"type": "boolean"
},
"suggestions": {
"items": {
"additionalProperties": true,
"type": "object"
},
"type": "array"
}
},
"required": [
"ok",
"suggestions"
],
"type": "object"
},
"TokenResponse": {
"additionalProperties": false,
"properties": {
@@ -1609,35 +1665,6 @@
"openapi": "3.0.3",
"paths": {
"/api/v1/admin/areas": {
"get": {
"description": "Lists configured municipal areas.",
"operationId": "AdminController.listAreas",
"parameters": [ ],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/AreaListResponse"
}
}
},
"description": "Area list."
},
"401": {
"$ref": "#/components/responses/Unauthorized"
}
},
"security": [
{
"bearerAuth": [ ]
}
],
"summary": "List areas",
"tags": [
"Admin"
]
},
"post": {
"description": "Creates a municipal area with GeoJSON boundary.",
"operationId": "AdminController.createArea",
@@ -1684,24 +1711,50 @@
]
}
},
"/api/v1/admin/categories": {
"get": {
"description": "Lists active report categories.",
"operationId": "AdminController.listCategories",
"parameters": [ ],
"/api/v1/admin/areas/{id}": {
"patch": {
"description": "Updates an existing municipal area.",
"operationId": "AdminController.updateArea",
"parameters": [
{
"in": "path",
"name": "id",
"required": true,
"schema": {
"format": "int64",
"type": "integer"
}
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CreateAreaRequest"
}
}
},
"required": true
},
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CategoryListResponse"
"$ref": "#/components/schemas/OkResponse"
}
}
},
"description": "Report category list."
"description": "Updated area result."
},
"401": {
"$ref": "#/components/responses/Unauthorized"
},
"403": {
"$ref": "#/components/responses/Forbidden"
},
"404": {
"$ref": "#/components/responses/NotFound"
}
},
"security": [
@@ -1709,11 +1762,13 @@
"bearerAuth": [ ]
}
],
"summary": "List categories",
"summary": "Update area",
"tags": [
"Admin"
]
},
}
},
"/api/v1/admin/categories": {
"post": {
"description": "Creates a report category.",
"operationId": "AdminController.createCategory",
@@ -1760,6 +1815,63 @@
]
}
},
"/api/v1/admin/categories/{id}": {
"patch": {
"description": "Updates an existing report category.",
"operationId": "AdminController.updateCategory",
"parameters": [
{
"in": "path",
"name": "id",
"required": true,
"schema": {
"format": "int64",
"type": "integer"
}
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CreateCategoryRequest"
}
}
},
"required": true
},
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/OkResponse"
}
}
},
"description": "Updated category result."
},
"401": {
"$ref": "#/components/responses/Unauthorized"
},
"403": {
"$ref": "#/components/responses/Forbidden"
},
"404": {
"$ref": "#/components/responses/NotFound"
}
},
"security": [
{
"bearerAuth": [ ]
}
],
"summary": "Update category",
"tags": [
"Admin"
]
}
},
"/api/v1/admin/moderation": {
"get": {
"description": "Lists moderation flags for admin/operator review.",
@@ -1916,35 +2028,6 @@
}
},
"/api/v1/admin/teams": {
"get": {
"description": "Lists intervention teams.",
"operationId": "AdminController.listTeams",
"parameters": [ ],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/TeamListResponse"
}
}
},
"description": "Team list."
},
"401": {
"$ref": "#/components/responses/Unauthorized"
}
},
"security": [
{
"bearerAuth": [ ]
}
],
"summary": "List teams",
"tags": [
"Admin"
]
},
"post": {
"description": "Creates an intervention team.",
"operationId": "AdminController.createTeam",
@@ -1991,6 +2074,86 @@
]
}
},
"/api/v1/admin/teams/{id}": {
"patch": {
"description": "Updates an existing intervention team.",
"operationId": "AdminController.updateTeam",
"parameters": [
{
"in": "path",
"name": "id",
"required": true,
"schema": {
"format": "int64",
"type": "integer"
}
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CreateTeamRequest"
}
}
},
"required": true
},
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/OkResponse"
}
}
},
"description": "Updated team result."
},
"401": {
"$ref": "#/components/responses/Unauthorized"
},
"403": {
"$ref": "#/components/responses/Forbidden"
},
"404": {
"$ref": "#/components/responses/NotFound"
}
},
"security": [
{
"bearerAuth": [ ]
}
],
"summary": "Update team",
"tags": [
"Admin"
]
}
},
"/api/v1/areas": {
"get": {
"description": "Lists active municipal areas.",
"operationId": "PublicController.listAreas",
"parameters": [ ],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/AreaListResponse"
}
}
},
"description": "Area list."
}
},
"summary": "List areas",
"tags": [
"Public"
]
}
},
"/api/v1/admin/users": {
"get": {
"description": "Lists platform users with role information.",
@@ -2697,6 +2860,41 @@
]
}
},
"/api/v1/interventions/{id}": {
"get": {
"description": "Returns public intervention summary for external intervention teams.",
"operationId": "PublicController.interventionSummary",
"parameters": [
{
"in": "path",
"name": "id",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InterventionSummaryResponse"
}
}
},
"description": "Public intervention summary."
},
"404": {
"$ref": "#/components/responses/NotFound"
}
},
"summary": "Public intervention summary",
"tags": [
"Public"
]
}
},
"/api/v1/operator/reports/assigned": {
"get": {
"description": "Lists reports assigned to the authenticated operator.",
@@ -2850,6 +3048,40 @@
]
}
},
"/api/v1/operator/teams": {
"get": {
"description": "Lists active intervention teams for operators and admins.",
"operationId": "ReportController.listTeams",
"parameters": [ ],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/TeamListResponse"
}
}
},
"description": "Team list."
},
"401": {
"$ref": "#/components/responses/Unauthorized"
},
"403": {
"$ref": "#/components/responses/Forbidden"
}
},
"security": [
{
"bearerAuth": [ ]
}
],
"summary": "List teams",
"tags": [
"Reports"
]
}
},
"/api/v1/ready": {
"get": {
"description": "Checks whether MySQL is reachable.",
@@ -2986,6 +3218,37 @@
]
}
},
"/api/v1/report-categories": {
"get": {
"description": "Lists active report categories for authenticated users.",
"operationId": "ReportController.listCategories",
"parameters": [ ],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CategoryListResponse"
}
}
},
"description": "Report category list."
},
"401": {
"$ref": "#/components/responses/Unauthorized"
}
},
"security": [
{
"bearerAuth": [ ]
}
],
"summary": "List categories",
"tags": [
"Reports"
]
}
},
"/api/v1/reports/{id}": {
"get": {
"description": "Returns report detail, status history and media.",
@@ -3030,6 +3293,95 @@
]
}
},
"/api/v1/reports/{id}/intervention-link": {
"delete": {
"description": "Revokes current public intervention link for assigned team.",
"operationId": "ReportController.revokeInterventionLink",
"parameters": [
{
"in": "path",
"name": "id",
"required": true,
"schema": {
"format": "int64",
"type": "integer"
}
}
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/OkResponse"
}
}
},
"description": "Link revocation result."
},
"401": {
"$ref": "#/components/responses/Unauthorized"
},
"403": {
"$ref": "#/components/responses/Forbidden"
}
},
"security": [
{
"bearerAuth": [ ]
}
],
"summary": "Revoke intervention link",
"tags": [
"Reports"
]
},
"post": {
"description": "Creates or rotates public intervention link for external intervention team.",
"operationId": "ReportController.createInterventionLink",
"parameters": [
{
"in": "path",
"name": "id",
"required": true,
"schema": {
"format": "int64",
"type": "integer"
}
}
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/AssignmentLinkResponse"
}
}
},
"description": "Generated public link."
},
"401": {
"$ref": "#/components/responses/Unauthorized"
},
"403": {
"$ref": "#/components/responses/Forbidden"
},
"409": {
"$ref": "#/components/responses/Conflict"
}
},
"security": [
{
"bearerAuth": [ ]
}
],
"summary": "Create intervention link",
"tags": [
"Reports"
]
}
},
"/api/v1/reports/{id}/assignments": {
"post": {
"description": "Assigns an intervention team to a report.",
@@ -3245,6 +3597,53 @@
]
}
},
"/api/v1/reports/{id}/team-suggestions": {
"get": {
"description": "Suggests compatible teams for report category and area.",
"operationId": "ReportController.teamSuggestions",
"parameters": [
{
"in": "path",
"name": "id",
"required": true,
"schema": {
"format": "int64",
"type": "integer"
}
}
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/TeamSuggestionListResponse"
}
}
},
"description": "Suggested teams."
},
"401": {
"$ref": "#/components/responses/Unauthorized"
},
"403": {
"$ref": "#/components/responses/Forbidden"
},
"404": {
"$ref": "#/components/responses/NotFound"
}
},
"security": [
{
"bearerAuth": [ ]
}
],
"summary": "Team suggestions",
"tags": [
"Reports"
]
}
},
"/api/v1/reports/{id}/media": {
"post": {
"description": "Uploads report images or videos as multipart files.",