Add CFCFRMS (MHA/I4C) Bank API and Simulator module

CFCFRMS integration for cyber fraud complaint management:
- Bank-side: complaint CRUD, lien/freeze/resolve actions, escalation, audit trail, dashboard stats
- Simulator (I4C/MHA): push complaints, hold directives, bulk push, receive updates/escalations, account/transaction verification, bank performance metrics
- Domain: ComplaintTicket, ComplaintAuditLog, CfcfrmsSimulatorLog entities
- Liquibase 0226: 3 tables (MySQL+PostgreSQL), indexes, permissions
- Seed data: 13 realistic complaint tickets + 5 simulator log entries
- JAX-RS endpoints at /v1/cfcfrms/complaints/* and /v1/simulator/cfcfrms/*
- Full Swagger @Operation annotations

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Aditya
2026-03-18 17:49:54 +05:30
co-authored by Claude Opus 4.6
parent 6a464291d5
commit 78f37257a7
22 changed files with 2758 additions and 0 deletions
@@ -244,4 +244,5 @@
<include file="parts/0223_add_sanctions_screening_log.xml" relativeToChangelogFile="true"/>
<include file="parts/0224_add_mobile_lookup_log.xml" relativeToChangelogFile="true"/>
<include file="parts/0225_add_current_account_support.xml" relativeToChangelogFile="true"/>
<include file="parts/0226_add_cfcfrms_tables.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>
@@ -0,0 +1,426 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<!--
CFCFRMS (Citizen Financial Cyber Fraud Reporting and Management System)
MHA/I4C Bank Integration tables.
Tables created:
m_cfcfrms_complaint_ticket – Fraud complaint tickets received from CFCFRMS
m_cfcfrms_complaint_audit_log – Audit trail for each ticket status change
m_cfcfrms_simulator_log – I4C/MHA simulator-side event log
Permissions inserted:
READ_CFCFRMS, CREATE_CFCFRMS, UPDATE_CFCFRMS, ESCALATE_CFCFRMS
-->
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
<!-- ═══════════════════════════════════════════════════════════════════════
MySQL – m_cfcfrms_complaint_ticket
═══════════════════════════════════════════════════════════════════ -->
<changeSet author="fineract" id="1" context="mysql">
<preConditions onFail="MARK_RAN">
<not><tableExists tableName="m_cfcfrms_complaint_ticket"/></not>
</preConditions>
<createTable tableName="m_cfcfrms_complaint_ticket">
<column autoIncrement="true" name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="ticket_id" type="VARCHAR(50)">
<constraints nullable="false" unique="true"/>
</column>
<column name="complainant_name" type="VARCHAR(200)">
<constraints nullable="false"/>
</column>
<column name="complainant_mobile" type="VARCHAR(15)">
<constraints nullable="false"/>
</column>
<column name="fraud_amount" type="DECIMAL(19,2)">
<constraints nullable="false"/>
</column>
<column name="transaction_ref_id" type="VARCHAR(100)">
<constraints nullable="false"/>
</column>
<column name="fraud_account_number" type="VARCHAR(30)">
<constraints nullable="false"/>
</column>
<column name="fraud_bank_ifsc" type="VARCHAR(11)">
<constraints nullable="false"/>
</column>
<column name="victim_account_number" type="VARCHAR(30)">
<constraints nullable="false"/>
</column>
<column name="victim_bank_ifsc" type="VARCHAR(11)">
<constraints nullable="false"/>
</column>
<column name="upi_id" type="VARCHAR(50)"/>
<column name="complaint_source" type="VARCHAR(20)">
<constraints nullable="false"/>
</column>
<column name="status" type="VARCHAR(20)">
<constraints nullable="false"/>
</column>
<column name="action_taken" type="TEXT"/>
<column name="available_balance" type="DECIMAL(19,2)"/>
<column name="funds_held" type="DECIMAL(19,2)"/>
<column name="next_hop_account_number" type="VARCHAR(30)"/>
<column name="next_hop_bank_ifsc" type="VARCHAR(11)"/>
<column name="received_at" type="DATETIME">
<constraints nullable="false"/>
</column>
<column name="resolved_at" type="DATETIME"/>
<column name="assigned_officer" type="VARCHAR(200)">
<constraints nullable="false"/>
</column>
<column name="state_police_ref" type="VARCHAR(50)"/>
<column name="created_by" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="created_on_utc" type="DATETIME">
<constraints nullable="false"/>
</column>
<column name="last_modified_by" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="last_modified_on_utc" type="DATETIME">
<constraints nullable="false"/>
</column>
</createTable>
</changeSet>
<!-- PostgreSQL – m_cfcfrms_complaint_ticket -->
<changeSet author="fineract" id="1" context="postgresql">
<preConditions onFail="MARK_RAN">
<not><tableExists tableName="m_cfcfrms_complaint_ticket"/></not>
</preConditions>
<createTable tableName="m_cfcfrms_complaint_ticket">
<column autoIncrement="true" name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="ticket_id" type="VARCHAR(50)">
<constraints nullable="false" unique="true"/>
</column>
<column name="complainant_name" type="VARCHAR(200)">
<constraints nullable="false"/>
</column>
<column name="complainant_mobile" type="VARCHAR(15)">
<constraints nullable="false"/>
</column>
<column name="fraud_amount" type="DECIMAL(19,2)">
<constraints nullable="false"/>
</column>
<column name="transaction_ref_id" type="VARCHAR(100)">
<constraints nullable="false"/>
</column>
<column name="fraud_account_number" type="VARCHAR(30)">
<constraints nullable="false"/>
</column>
<column name="fraud_bank_ifsc" type="VARCHAR(11)">
<constraints nullable="false"/>
</column>
<column name="victim_account_number" type="VARCHAR(30)">
<constraints nullable="false"/>
</column>
<column name="victim_bank_ifsc" type="VARCHAR(11)">
<constraints nullable="false"/>
</column>
<column name="upi_id" type="VARCHAR(50)"/>
<column name="complaint_source" type="VARCHAR(20)">
<constraints nullable="false"/>
</column>
<column name="status" type="VARCHAR(20)">
<constraints nullable="false"/>
</column>
<column name="action_taken" type="TEXT"/>
<column name="available_balance" type="DECIMAL(19,2)"/>
<column name="funds_held" type="DECIMAL(19,2)"/>
<column name="next_hop_account_number" type="VARCHAR(30)"/>
<column name="next_hop_bank_ifsc" type="VARCHAR(11)"/>
<column name="received_at" type="TIMESTAMP WITH TIME ZONE">
<constraints nullable="false"/>
</column>
<column name="resolved_at" type="TIMESTAMP WITH TIME ZONE"/>
<column name="assigned_officer" type="VARCHAR(200)">
<constraints nullable="false"/>
</column>
<column name="state_police_ref" type="VARCHAR(50)"/>
<column name="created_by" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="created_on_utc" type="TIMESTAMP WITH TIME ZONE">
<constraints nullable="false"/>
</column>
<column name="last_modified_by" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="last_modified_on_utc" type="TIMESTAMP WITH TIME ZONE">
<constraints nullable="false"/>
</column>
</createTable>
</changeSet>
<!-- ═══════════════════════════════════════════════════════════════════════
MySQL – m_cfcfrms_complaint_audit_log
═══════════════════════════════════════════════════════════════════ -->
<changeSet author="fineract" id="2" context="mysql">
<preConditions onFail="MARK_RAN">
<not><tableExists tableName="m_cfcfrms_complaint_audit_log"/></not>
</preConditions>
<createTable tableName="m_cfcfrms_complaint_audit_log">
<column autoIncrement="true" name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="ticket_id" type="VARCHAR(50)">
<constraints nullable="false"/>
</column>
<column name="event" type="VARCHAR(50)">
<constraints nullable="false"/>
</column>
<column name="description" type="TEXT"/>
<column name="actor" type="VARCHAR(200)">
<constraints nullable="false"/>
</column>
<column name="event_timestamp" type="DATETIME">
<constraints nullable="false"/>
</column>
<column name="created_by" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="created_on_utc" type="DATETIME">
<constraints nullable="false"/>
</column>
<column name="last_modified_by" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="last_modified_on_utc" type="DATETIME">
<constraints nullable="false"/>
</column>
</createTable>
</changeSet>
<!-- PostgreSQL – m_cfcfrms_complaint_audit_log -->
<changeSet author="fineract" id="2" context="postgresql">
<preConditions onFail="MARK_RAN">
<not><tableExists tableName="m_cfcfrms_complaint_audit_log"/></not>
</preConditions>
<createTable tableName="m_cfcfrms_complaint_audit_log">
<column autoIncrement="true" name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="ticket_id" type="VARCHAR(50)">
<constraints nullable="false"/>
</column>
<column name="event" type="VARCHAR(50)">
<constraints nullable="false"/>
</column>
<column name="description" type="TEXT"/>
<column name="actor" type="VARCHAR(200)">
<constraints nullable="false"/>
</column>
<column name="event_timestamp" type="TIMESTAMP WITH TIME ZONE">
<constraints nullable="false"/>
</column>
<column name="created_by" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="created_on_utc" type="TIMESTAMP WITH TIME ZONE">
<constraints nullable="false"/>
</column>
<column name="last_modified_by" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="last_modified_on_utc" type="TIMESTAMP WITH TIME ZONE">
<constraints nullable="false"/>
</column>
</createTable>
</changeSet>
<!-- ═══════════════════════════════════════════════════════════════════════
MySQL – m_cfcfrms_simulator_log
═══════════════════════════════════════════════════════════════════ -->
<changeSet author="fineract" id="3" context="mysql">
<preConditions onFail="MARK_RAN">
<not><tableExists tableName="m_cfcfrms_simulator_log"/></not>
</preConditions>
<createTable tableName="m_cfcfrms_simulator_log">
<column autoIncrement="true" name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="cfcfrms_ref" type="VARCHAR(50)">
<constraints nullable="false"/>
</column>
<column name="ticket_id" type="VARCHAR(50)"/>
<column name="bank_ifsc" type="VARCHAR(11)"/>
<column name="direction" type="VARCHAR(10)">
<constraints nullable="false"/>
</column>
<column name="event_type" type="VARCHAR(50)">
<constraints nullable="false"/>
</column>
<column name="payload" type="TEXT"/>
<column name="response_payload" type="TEXT"/>
<column name="event_timestamp" type="DATETIME">
<constraints nullable="false"/>
</column>
<column name="created_by" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="created_on_utc" type="DATETIME">
<constraints nullable="false"/>
</column>
<column name="last_modified_by" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="last_modified_on_utc" type="DATETIME">
<constraints nullable="false"/>
</column>
</createTable>
</changeSet>
<!-- PostgreSQL – m_cfcfrms_simulator_log -->
<changeSet author="fineract" id="3" context="postgresql">
<preConditions onFail="MARK_RAN">
<not><tableExists tableName="m_cfcfrms_simulator_log"/></not>
</preConditions>
<createTable tableName="m_cfcfrms_simulator_log">
<column autoIncrement="true" name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="cfcfrms_ref" type="VARCHAR(50)">
<constraints nullable="false"/>
</column>
<column name="ticket_id" type="VARCHAR(50)"/>
<column name="bank_ifsc" type="VARCHAR(11)"/>
<column name="direction" type="VARCHAR(10)">
<constraints nullable="false"/>
</column>
<column name="event_type" type="VARCHAR(50)">
<constraints nullable="false"/>
</column>
<column name="payload" type="TEXT"/>
<column name="response_payload" type="TEXT"/>
<column name="event_timestamp" type="TIMESTAMP WITH TIME ZONE">
<constraints nullable="false"/>
</column>
<column name="created_by" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="created_on_utc" type="TIMESTAMP WITH TIME ZONE">
<constraints nullable="false"/>
</column>
<column name="last_modified_by" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="last_modified_on_utc" type="TIMESTAMP WITH TIME ZONE">
<constraints nullable="false"/>
</column>
</createTable>
</changeSet>
<!-- Indexes -->
<changeSet author="fineract" id="4">
<preConditions onFail="MARK_RAN">
<not><indexExists indexName="idx_cfcfrms_ticket_id"/></not>
</preConditions>
<createIndex tableName="m_cfcfrms_complaint_ticket" indexName="idx_cfcfrms_ticket_id">
<column name="ticket_id"/>
</createIndex>
</changeSet>
<changeSet author="fineract" id="5">
<preConditions onFail="MARK_RAN">
<not><indexExists indexName="idx_cfcfrms_ticket_status"/></not>
</preConditions>
<createIndex tableName="m_cfcfrms_complaint_ticket" indexName="idx_cfcfrms_ticket_status">
<column name="status"/>
</createIndex>
</changeSet>
<changeSet author="fineract" id="6">
<preConditions onFail="MARK_RAN">
<not><indexExists indexName="idx_cfcfrms_fraud_account"/></not>
</preConditions>
<createIndex tableName="m_cfcfrms_complaint_ticket" indexName="idx_cfcfrms_fraud_account">
<column name="fraud_account_number"/>
</createIndex>
</changeSet>
<changeSet author="fineract" id="7">
<preConditions onFail="MARK_RAN">
<not><indexExists indexName="idx_cfcfrms_audit_ticket_id"/></not>
</preConditions>
<createIndex tableName="m_cfcfrms_complaint_audit_log" indexName="idx_cfcfrms_audit_ticket_id">
<column name="ticket_id"/>
</createIndex>
</changeSet>
<changeSet author="fineract" id="8">
<preConditions onFail="MARK_RAN">
<not><indexExists indexName="idx_cfcfrms_sim_ref"/></not>
</preConditions>
<createIndex tableName="m_cfcfrms_simulator_log" indexName="idx_cfcfrms_sim_ref">
<column name="cfcfrms_ref"/>
</createIndex>
</changeSet>
<!-- ═══════════════════════════════════════════════════════════════════════
Permissions
═══════════════════════════════════════════════════════════════════ -->
<changeSet author="fineract" id="9">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">
SELECT COUNT(*) FROM m_permission WHERE code = 'READ_CFCFRMS'
</sqlCheck>
</preConditions>
<insert tableName="m_permission">
<column name="grouping" value="portfolio"/>
<column name="code" value="READ_CFCFRMS"/>
<column name="entity_name" value="CFCFRMS"/>
<column name="action_name" value="READ"/>
<column name="can_maker_checker" valueBoolean="false"/>
</insert>
<insert tableName="m_permission">
<column name="grouping" value="portfolio"/>
<column name="code" value="CREATE_CFCFRMS"/>
<column name="entity_name" value="CFCFRMS"/>
<column name="action_name" value="CREATE"/>
<column name="can_maker_checker" valueBoolean="true"/>
</insert>
<insert tableName="m_permission">
<column name="grouping" value="portfolio"/>
<column name="code" value="UPDATE_CFCFRMS"/>
<column name="entity_name" value="CFCFRMS"/>
<column name="action_name" value="UPDATE"/>
<column name="can_maker_checker" valueBoolean="true"/>
</insert>
<insert tableName="m_permission">
<column name="grouping" value="portfolio"/>
<column name="code" value="ESCALATE_CFCFRMS"/>
<column name="entity_name" value="CFCFRMS"/>
<column name="action_name" value="ESCALATE"/>
<column name="can_maker_checker" valueBoolean="true"/>
</insert>
</changeSet>
</databaseChangeLog>