feat(platform): I3 contracts, lines, status machine, and audit API

Add Flyway V3 tables, contract CRUD and line endpoints, PATCH status
transitions with validation, M10-F01 audit-events listing, 409 handler,
and integration tests. Refresh OpenAPI contract snapshot.

Made-with: Cursor
This commit is contained in:
2026-04-06 21:29:21 +08:00
parent 5b50bf0fd8
commit 69f7ee11df
26 changed files with 2439 additions and 0 deletions
@@ -170,6 +170,139 @@
}
}
},
"/api/v1/contracts/{id}" : {
"get" : {
"tags" : [ "contract-controller" ],
"operationId" : "get_2",
"parameters" : [ {
"name" : "id",
"in" : "path",
"required" : true,
"schema" : {
"type" : "integer",
"format" : "int64"
}
} ],
"responses" : {
"200" : {
"description" : "OK",
"content" : {
"*/*" : {
"schema" : {
"$ref" : "#/components/schemas/ContractResponse"
}
}
}
}
}
},
"put" : {
"tags" : [ "contract-controller" ],
"operationId" : "update_2",
"parameters" : [ {
"name" : "id",
"in" : "path",
"required" : true,
"schema" : {
"type" : "integer",
"format" : "int64"
}
} ],
"requestBody" : {
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/ContractUpdateRequest"
}
}
},
"required" : true
},
"responses" : {
"200" : {
"description" : "OK",
"content" : {
"*/*" : {
"schema" : {
"$ref" : "#/components/schemas/ContractResponse"
}
}
}
}
}
}
},
"/api/v1/contracts/{id}/lines/{lineId}" : {
"put" : {
"tags" : [ "contract-controller" ],
"operationId" : "updateLine",
"parameters" : [ {
"name" : "id",
"in" : "path",
"required" : true,
"schema" : {
"type" : "integer",
"format" : "int64"
}
}, {
"name" : "lineId",
"in" : "path",
"required" : true,
"schema" : {
"type" : "integer",
"format" : "int64"
}
} ],
"requestBody" : {
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/ContractLineRequest"
}
}
},
"required" : true
},
"responses" : {
"200" : {
"description" : "OK",
"content" : {
"*/*" : {
"schema" : {
"$ref" : "#/components/schemas/ContractLineResponse"
}
}
}
}
}
},
"delete" : {
"tags" : [ "contract-controller" ],
"operationId" : "deleteLine",
"parameters" : [ {
"name" : "id",
"in" : "path",
"required" : true,
"schema" : {
"type" : "integer",
"format" : "int64"
}
}, {
"name" : "lineId",
"in" : "path",
"required" : true,
"schema" : {
"type" : "integer",
"format" : "int64"
}
} ],
"responses" : {
"204" : {
"description" : "No Content"
}
}
}
},
"/api/v1/projects" : {
"get" : {
"tags" : [ "project-controller" ],
@@ -317,6 +450,160 @@
}
}
},
"/api/v1/contracts" : {
"get" : {
"tags" : [ "contract-controller" ],
"operationId" : "list_2",
"parameters" : [ {
"name" : "page",
"in" : "query",
"required" : false,
"schema" : {
"type" : "integer",
"format" : "int32",
"default" : 0,
"minimum" : 0
}
}, {
"name" : "size",
"in" : "query",
"required" : false,
"schema" : {
"type" : "integer",
"format" : "int32",
"default" : 20,
"maximum" : 200,
"minimum" : 1
}
}, {
"name" : "customerId",
"in" : "query",
"required" : false,
"schema" : {
"type" : "integer",
"format" : "int64"
}
}, {
"name" : "projectId",
"in" : "query",
"required" : false,
"schema" : {
"type" : "integer",
"format" : "int64"
}
}, {
"name" : "keyword",
"in" : "query",
"required" : false,
"schema" : {
"type" : "string"
}
} ],
"responses" : {
"200" : {
"description" : "OK",
"content" : {
"*/*" : {
"schema" : {
"$ref" : "#/components/schemas/PageResponseContractResponse"
}
}
}
}
}
},
"post" : {
"tags" : [ "contract-controller" ],
"operationId" : "create_2",
"requestBody" : {
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/ContractCreateRequest"
}
}
},
"required" : true
},
"responses" : {
"201" : {
"description" : "Created",
"content" : {
"*/*" : {
"schema" : {
"$ref" : "#/components/schemas/ContractResponse"
}
}
}
}
}
}
},
"/api/v1/contracts/{id}/lines" : {
"get" : {
"tags" : [ "contract-controller" ],
"operationId" : "listLines",
"parameters" : [ {
"name" : "id",
"in" : "path",
"required" : true,
"schema" : {
"type" : "integer",
"format" : "int64"
}
} ],
"responses" : {
"200" : {
"description" : "OK",
"content" : {
"*/*" : {
"schema" : {
"type" : "array",
"items" : {
"$ref" : "#/components/schemas/ContractLineResponse"
}
}
}
}
}
}
},
"post" : {
"tags" : [ "contract-controller" ],
"operationId" : "addLine",
"parameters" : [ {
"name" : "id",
"in" : "path",
"required" : true,
"schema" : {
"type" : "integer",
"format" : "int64"
}
} ],
"requestBody" : {
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/ContractLineRequest"
}
}
},
"required" : true
},
"responses" : {
"201" : {
"description" : "Created",
"content" : {
"*/*" : {
"schema" : {
"$ref" : "#/components/schemas/ContractLineResponse"
}
}
}
}
}
}
},
"/api/v1/auth/login" : {
"post" : {
"tags" : [ "auth-controller" ],
@@ -349,6 +636,43 @@
}
}
},
"/api/v1/contracts/{id}/status" : {
"patch" : {
"tags" : [ "contract-controller" ],
"operationId" : "patchStatus",
"parameters" : [ {
"name" : "id",
"in" : "path",
"required" : true,
"schema" : {
"type" : "integer",
"format" : "int64"
}
} ],
"requestBody" : {
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/ContractStatusPatchRequest"
}
}
},
"required" : true
},
"responses" : {
"200" : {
"description" : "OK",
"content" : {
"*/*" : {
"schema" : {
"$ref" : "#/components/schemas/ContractResponse"
}
}
}
}
}
}
},
"/api/v1/ping" : {
"get" : {
"tags" : [ "ping-controller" ],
@@ -398,6 +722,62 @@
}
}
}
},
"/api/v1/audit-events" : {
"get" : {
"tags" : [ "audit-controller" ],
"operationId" : "list_3",
"parameters" : [ {
"name" : "entityType",
"in" : "query",
"required" : true,
"schema" : {
"type" : "string",
"minLength" : 1
}
}, {
"name" : "entityId",
"in" : "query",
"required" : true,
"schema" : {
"type" : "integer",
"format" : "int64"
}
}, {
"name" : "page",
"in" : "query",
"required" : false,
"schema" : {
"type" : "integer",
"format" : "int32",
"default" : 0,
"minimum" : 0
}
}, {
"name" : "size",
"in" : "query",
"required" : false,
"schema" : {
"type" : "integer",
"format" : "int32",
"default" : 20,
"maximum" : 200,
"minimum" : 1
}
} ],
"responses" : {
"200" : {
"description" : "OK",
"content" : {
"*/*" : {
"schema" : {
"$ref" : "#/components/schemas/PageResponseAuditEventResponse"
}
}
}
}
}
}
}
},
"components" : {
@@ -496,6 +876,167 @@
}
}
},
"ContractUpdateRequest" : {
"type" : "object",
"properties" : {
"title" : {
"type" : "string",
"maxLength" : 256,
"minLength" : 0
},
"remarks" : {
"type" : "string",
"maxLength" : 4000,
"minLength" : 0
}
}
},
"ContractLineResponse" : {
"type" : "object",
"properties" : {
"id" : {
"type" : "integer",
"format" : "int64"
},
"contractId" : {
"type" : "integer",
"format" : "int64"
},
"sortOrder" : {
"type" : "integer",
"format" : "int32"
},
"itemName" : {
"type" : "string"
},
"quantity" : {
"type" : "number"
},
"unit" : {
"type" : "string"
},
"amount" : {
"type" : "number"
},
"remark" : {
"type" : "string"
},
"createdAt" : {
"type" : "string",
"format" : "date-time"
},
"updatedAt" : {
"type" : "string",
"format" : "date-time"
}
}
},
"ContractResponse" : {
"type" : "object",
"properties" : {
"id" : {
"type" : "integer",
"format" : "int64"
},
"customerId" : {
"type" : "integer",
"format" : "int64"
},
"projectId" : {
"type" : "integer",
"format" : "int64"
},
"title" : {
"type" : "string"
},
"remarks" : {
"type" : "string"
},
"status" : {
"type" : "string"
},
"createdAt" : {
"type" : "string",
"format" : "date-time"
},
"updatedAt" : {
"type" : "string",
"format" : "date-time"
},
"lines" : {
"type" : "array",
"items" : {
"$ref" : "#/components/schemas/ContractLineResponse"
}
}
}
},
"ContractLineRequest" : {
"type" : "object",
"properties" : {
"sortOrder" : {
"type" : "integer",
"format" : "int32"
},
"itemName" : {
"type" : "string",
"maxLength" : 256,
"minLength" : 0
},
"quantity" : {
"type" : "number",
"minimum" : 1.0E-4
},
"unit" : {
"type" : "string",
"maxLength" : 32,
"minLength" : 0
},
"amount" : {
"type" : "number"
},
"remark" : {
"type" : "string",
"maxLength" : 512,
"minLength" : 0
}
},
"required" : [ "itemName", "quantity" ]
},
"ContractCreateRequest" : {
"type" : "object",
"properties" : {
"customerId" : {
"type" : "integer",
"format" : "int64"
},
"projectId" : {
"type" : "integer",
"format" : "int64"
},
"title" : {
"type" : "string",
"maxLength" : 256,
"minLength" : 0
},
"remarks" : {
"type" : "string",
"maxLength" : 4000,
"minLength" : 0
}
},
"required" : [ "customerId", "projectId" ]
},
"ContractStatusPatchRequest" : {
"type" : "object",
"properties" : {
"status" : {
"type" : "string",
"minLength" : 1
}
},
"required" : [ "status" ]
},
"PageResponseProjectResponse" : {
"type" : "object",
"properties" : {
@@ -556,6 +1097,87 @@
"format" : "int32"
}
}
},
"PageResponseContractResponse" : {
"type" : "object",
"properties" : {
"content" : {
"type" : "array",
"items" : {
"$ref" : "#/components/schemas/ContractResponse"
}
},
"totalElements" : {
"type" : "integer",
"format" : "int64"
},
"number" : {
"type" : "integer",
"format" : "int32"
},
"size" : {
"type" : "integer",
"format" : "int32"
}
}
},
"AuditEventResponse" : {
"type" : "object",
"properties" : {
"id" : {
"type" : "integer",
"format" : "int64"
},
"entityType" : {
"type" : "string"
},
"entityId" : {
"type" : "integer",
"format" : "int64"
},
"action" : {
"type" : "string"
},
"fieldName" : {
"type" : "string"
},
"oldValue" : {
"type" : "string"
},
"newValue" : {
"type" : "string"
},
"actorUserId" : {
"type" : "string"
},
"createdAt" : {
"type" : "string",
"format" : "date-time"
}
}
},
"PageResponseAuditEventResponse" : {
"type" : "object",
"properties" : {
"content" : {
"type" : "array",
"items" : {
"$ref" : "#/components/schemas/AuditEventResponse"
}
},
"totalElements" : {
"type" : "integer",
"format" : "int64"
},
"number" : {
"type" : "integer",
"format" : "int32"
},
"size" : {
"type" : "integer",
"format" : "int32"
}
}
}
},
"securitySchemes" : {