banque
This commit is contained in:
@@ -361,46 +361,132 @@ CREATE TABLE emplois (
|
|||||||
FOREIGN KEY (id_personne) REFERENCES personnes(id_personne),
|
FOREIGN KEY (id_personne) REFERENCES personnes(id_personne),
|
||||||
FOREIGN KEY (id_societe) REFERENCES societe(id)
|
FOREIGN KEY (id_societe) REFERENCES societe(id)
|
||||||
);
|
);
|
||||||
|
*/
|
||||||
|
|
||||||
insert into emplois values
|
-- ----------------------------------------------------------------------
|
||||||
|
-- Banque
|
||||||
|
-- ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
create schema banque;
|
||||||
|
|
||||||
CREATE TABLE account (
|
-- Générateur de numéro aléatoire
|
||||||
id bigint generated always as identity,
|
-- ----------------------------------------------------------------------
|
||||||
|
CREATE OR REPLACE FUNCTION banque.rand_account(n integer)
|
||||||
|
RETURNS text AS $$
|
||||||
|
DECLARE
|
||||||
|
chars text := '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||||
|
out text := '';
|
||||||
|
b bytea := gen_random_bytes(n); -- n octets aléatoires
|
||||||
|
i int;
|
||||||
|
idx int;
|
||||||
|
BEGIN
|
||||||
|
IF n <= 0 THEN
|
||||||
|
RAISE EXCEPTION 'La longueur doit être > 0';
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
FOR i IN 0..(n - 1) LOOP
|
||||||
|
idx := (get_byte(b, i) % length(chars)) + 1;
|
||||||
|
out := out || substr(chars, idx, 1);
|
||||||
|
END LOOP;
|
||||||
|
|
||||||
|
RETURN out;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
-- Devises (currencies)
|
||||||
|
-- ----------------------------------------------------------------------
|
||||||
|
create table currency (
|
||||||
|
code text not null,
|
||||||
|
num4217 integer default null,
|
||||||
|
symbole character varying(5) default null,
|
||||||
|
nom text default null,
|
||||||
|
format text default null,
|
||||||
|
division integer default 0,
|
||||||
|
minor text default null,
|
||||||
|
minors text default null
|
||||||
|
);
|
||||||
|
|
||||||
|
alter table currency
|
||||||
|
add check (code ~ '^[A-Z]{3}$');
|
||||||
|
|
||||||
|
create table pays_devises (
|
||||||
|
pays_code text not null,
|
||||||
|
devise_code text not null,
|
||||||
|
valide daterange default null
|
||||||
|
);
|
||||||
|
|
||||||
|
alter table pays_devises
|
||||||
|
add check (pays_code ~ '^[A-Z]{2}$');
|
||||||
|
|
||||||
|
alter table pays_devises
|
||||||
|
add check (devise_code ~ '^[A-Z]{3}$');
|
||||||
|
|
||||||
|
create unique index currency_pk
|
||||||
|
on currency
|
||||||
|
using btree (code);
|
||||||
|
|
||||||
|
alter table currency
|
||||||
|
add primary key using index currency_pk;
|
||||||
|
|
||||||
|
\copy currency from '/tmp/geo/devises.csv' (FORMAT CSV, header, delimiter ',', ENCODING 'UTF8');
|
||||||
|
\copy pays_devises from '/tmp/geo/devises_pays.csv' (FORMAT CSV, header, delimiter ',', ENCODING 'UTF8');
|
||||||
|
|
||||||
|
-- pays_devises -> pays
|
||||||
|
alter table only pays_devises
|
||||||
|
add foreign key (pays_code)
|
||||||
|
references geo.pays (code2);
|
||||||
|
|
||||||
|
-- pays_devises -> devises
|
||||||
|
alter table only pays_devises
|
||||||
|
add foreign key (devise_code)
|
||||||
|
references currency (code);
|
||||||
|
|
||||||
|
CREATE TABLE banque.exchange_rate (
|
||||||
|
from_currency CHAR(3) references currency(code),
|
||||||
|
to_currency CHAR(3) references currency(code),
|
||||||
|
rate DECIMAL(12,6) NOT NULL,
|
||||||
|
fee_percent DECIMAL(5,2) DEFAULT 0, -- frais en %
|
||||||
|
last_updated TIMESTAMP DEFAULT NOW(),
|
||||||
|
PRIMARY KEY (from_currency, to_currency)
|
||||||
|
);
|
||||||
|
-- ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
CREATE TABLE banque.account (
|
||||||
|
id bigint primary key generated always as identity,
|
||||||
account_number TEXT UNIQUE NOT NULL,
|
account_number TEXT UNIQUE NOT NULL,
|
||||||
balance NUMERIC(18,2) NOT NULL DEFAULT 0,
|
balance NUMERIC(18,2) NOT NULL DEFAULT 0,
|
||||||
currency CHAR(3) NOT NULL,
|
currency CHAR(3) NOT NULL,
|
||||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()
|
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE "transaction" (
|
CREATE TABLE banque."transaction" (
|
||||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
id UUID PRIMARY KEY DEFAULT uuidv7(),
|
||||||
reference TEXT,
|
reference TEXT,
|
||||||
amount NUMERIC(18,2) NOT NULL,
|
amount NUMERIC(18,2) NOT NULL,
|
||||||
currency CHAR(3) NOT NULL,
|
currency CHAR(3) NOT NULL,
|
||||||
from_account BIGINT NOT NULL REFERENCES account(id),
|
from_account BIGINT NOT NULL REFERENCES banque.account(id),
|
||||||
to_account BIGINT NOT NULL REFERENCES account(id),
|
to_account BIGINT NOT NULL REFERENCES banque.account(id),
|
||||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
||||||
processed BOOLEAN NOT NULL DEFAULT FALSE -- indique si ledger + soldes ont été appliqués
|
processed BOOLEAN NOT NULL DEFAULT FALSE -- indique si ledger + soldes ont été appliqués
|
||||||
);
|
);
|
||||||
|
|
||||||
-- ledger (écritures comptables immuables) : append-only
|
-- ledger (écritures comptables immuables) : append-only
|
||||||
CREATE TABLE ledger_entry (
|
CREATE TABLE banque.ledger_entry (
|
||||||
id bigint generated always as identity,
|
id bigint primary key generated always as identity,
|
||||||
transaction_id UUID NOT NULL REFERENCES "transaction"(id),
|
transaction_id UUID NOT NULL REFERENCES banque."transaction"(id),
|
||||||
account_id BIGINT NOT NULL REFERENCES account(id),
|
account_id BIGINT NOT NULL REFERENCES banque.account(id),
|
||||||
amount NUMERIC(18,2) NOT NULL, -- convention: positif = crédit, négatif = débit (ici from = -amount, to = +amount)
|
amount NUMERIC(18,2) NOT NULL, -- convention: positif = crédit, négatif = débit (ici from = -amount, to = +amount)
|
||||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
||||||
description TEXT
|
description TEXT
|
||||||
);
|
);
|
||||||
|
|
||||||
-- index pour performance et idempotence par transaction/account
|
-- index pour performance et idempotence par transaction/account
|
||||||
CREATE UNIQUE INDEX ux_ledger_tx_account ON ledger_entry(transaction_id, account_id);
|
CREATE UNIQUE INDEX ux_ledger_tx_account
|
||||||
|
ON banque.ledger_entry(transaction_id, account_id);
|
||||||
|
|
||||||
-- outbox pour publisher reliable (pattern outbox)
|
-- outbox pour publisher reliable (pattern outbox)
|
||||||
CREATE TABLE outbox_event (
|
CREATE TABLE banque.outbox_event (
|
||||||
id bigint generated always as identity,
|
id bigint primary key generated always as identity,
|
||||||
occurrenced_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
occurrenced_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
||||||
topic TEXT NOT NULL,
|
topic TEXT NOT NULL,
|
||||||
payload JSONB NOT NULL,
|
payload JSONB NOT NULL,
|
||||||
@@ -410,12 +496,178 @@ CREATE TABLE outbox_event (
|
|||||||
);
|
);
|
||||||
|
|
||||||
-- table very simple de blockchain / chain d'audit
|
-- table very simple de blockchain / chain d'audit
|
||||||
CREATE TABLE block_chain (
|
CREATE TABLE banque.block_chain (
|
||||||
id bigint generated always as identity,
|
id bigint primary key generated always as identity,
|
||||||
block_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
block_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
||||||
tx_id UUID NOT NULL, -- transaction incluse dans ce bloc (ou multiple selon choix)
|
tx_id UUID NOT NULL, -- transaction incluse dans ce bloc (ou multiple selon choix)
|
||||||
previous_hash TEXT NULL,
|
previous_hash TEXT NULL,
|
||||||
block_hash TEXT NOT NULL,
|
block_hash TEXT NOT NULL,
|
||||||
block_data JSONB NOT NULL -- stockage lisible des éléments du bloc (pour audit)
|
block_data JSONB NOT NULL -- stockage lisible des éléments du bloc (pour audit)
|
||||||
);
|
);
|
||||||
CREATE INDEX idx_block_chain_txid ON block_chain(tx_id);
|
|
||||||
|
CREATE INDEX idx_block_chain_txid ON banque.block_chain(tx_id);
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION perform_transaction(
|
||||||
|
from_account_id INT,
|
||||||
|
to_account_id INT,
|
||||||
|
amount DECIMAL(18,2),
|
||||||
|
description TEXT
|
||||||
|
) RETURNS VOID AS $$
|
||||||
|
DECLARE
|
||||||
|
from_currency CHAR(3);
|
||||||
|
to_currency CHAR(3);
|
||||||
|
rate DECIMAL(12,6);
|
||||||
|
fee DECIMAL(18,2);
|
||||||
|
base_amount DECIMAL(18,2);
|
||||||
|
converted_amount DECIMAL(18,2);
|
||||||
|
tx_id INT;
|
||||||
|
prev_hash TEXT;
|
||||||
|
new_hash TEXT;
|
||||||
|
BEGIN
|
||||||
|
SELECT currency_code INTO from_currency FROM banque.account WHERE id = from_account_id;
|
||||||
|
SELECT currency_code INTO to_currency FROM banque.account WHERE id = to_account_id;
|
||||||
|
|
||||||
|
SELECT hash INTO prev_hash FROM banque.transaction ORDER BY id DESC LIMIT 1;
|
||||||
|
|
||||||
|
-- Création de la transaction principale
|
||||||
|
INSERT INTO banque.transaction (description, previous_hash)
|
||||||
|
VALUES (description, prev_hash)
|
||||||
|
RETURNING id INTO tx_id;
|
||||||
|
|
||||||
|
IF from_currency = to_currency THEN
|
||||||
|
rate := 1;
|
||||||
|
fee := 0;
|
||||||
|
converted_amount := amount;
|
||||||
|
ELSE
|
||||||
|
SELECT rate, fee_percent INTO rate, fee
|
||||||
|
FROM banque.exchange_rate
|
||||||
|
WHERE from_currency = from_currency AND to_currency = to_currency;
|
||||||
|
|
||||||
|
converted_amount := amount * rate * (1 - fee/100);
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- Débit
|
||||||
|
INSERT INTO banque.ledger_entry (transaction_id, account_id, amount, currency_code, entry_type, rate_to_base, converted_amount)
|
||||||
|
VALUES (tx_id, from_account_id, -amount, from_currency, 'debit', rate, amount * rate);
|
||||||
|
|
||||||
|
-- Crédit
|
||||||
|
INSERT INTO banque.ledger_entry (transaction_id, account_id, amount, currency_code, entry_type, rate_to_base, converted_amount)
|
||||||
|
VALUES (tx_id, to_account_id, converted_amount, to_currency, 'credit', rate, converted_amount);
|
||||||
|
|
||||||
|
-- Mise à jour des soldes
|
||||||
|
UPDATE banque.account SET balance = balance - amount WHERE id = from_account_id;
|
||||||
|
UPDATE bansue.account SET balance = balance + converted_amount WHERE id = to_account_id;
|
||||||
|
|
||||||
|
-- Génération du hash blockchain
|
||||||
|
SELECT encode(digest(concat(tx_id, description, prev_hash, NOW()::text), 'sha256'), 'hex') INTO new_hash;
|
||||||
|
UPDATE banque.transaction SET hash = new_hash WHERE id = tx_id;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION notify_transaction()
|
||||||
|
RETURNS TRIGGER AS $$
|
||||||
|
DECLARE
|
||||||
|
payload JSON;
|
||||||
|
BEGIN
|
||||||
|
payload := json_build_object(
|
||||||
|
'transaction_id', NEW.id,
|
||||||
|
'description', NEW.description,
|
||||||
|
'timestamp', NEW.timestamp,
|
||||||
|
'hash', NEW.hash
|
||||||
|
);
|
||||||
|
PERFORM pg_notify('transactions', payload::text); -- canal PostgreSQL NOTIFY
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
CREATE TRIGGER tr_notify_transaction
|
||||||
|
AFTER INSERT ON banque.transaction
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION notify_transaction();
|
||||||
|
|
||||||
|
-- ----------------------------------------------------------------------
|
||||||
|
-- Business Intelligence
|
||||||
|
-- ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
create schema business;
|
||||||
|
|
||||||
|
-- Chronologie
|
||||||
|
create table business.chronologie as
|
||||||
|
with recursive calendrier as (
|
||||||
|
select
|
||||||
|
'2010-01-01 00:00:00'::timestamp as jour
|
||||||
|
union all
|
||||||
|
select
|
||||||
|
jour + interval '1 day'
|
||||||
|
from calendrier
|
||||||
|
where jour + interval '1 day' <= '2026-12-31'
|
||||||
|
)
|
||||||
|
select
|
||||||
|
extract(epoch from jour) / 86400::int as jj,
|
||||||
|
jour,
|
||||||
|
extract (year from jour) as annee,
|
||||||
|
extract (month from jour) as mois,
|
||||||
|
extract (day from jour) as jmois,
|
||||||
|
extract (week from jour) as semaine,
|
||||||
|
extract (dow from jour) as jsemaine,
|
||||||
|
extract (doy from jour) as jannee,
|
||||||
|
floor((extract(month from jour) - 1) / 6) + 1 as semestre,
|
||||||
|
floor((extract(month from jour) - 1) / 4) + 1 as quadrimestre,
|
||||||
|
extract(quarter from jour)::int as trimestre,
|
||||||
|
floor((extract(month from jour) - 1) / 2) + 1 as bimestre,
|
||||||
|
extract (day from jour) / extract (day from (date_trunc('month', '2025-03-16'::date) + interval '1 month' - interval '1 day')) as frac_mois,
|
||||||
|
extract (doy from jour) / extract (doy from (extract (year from jour)||'-12-31')::date) as frac_annee
|
||||||
|
from calendrier;
|
||||||
|
|
||||||
|
comment on column business.chronologie.jj
|
||||||
|
is 'jour julien';
|
||||||
|
|
||||||
|
-- ----------------------------------------------------------------------
|
||||||
|
-- Musique
|
||||||
|
-- ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- ----------------------------------------------------------------------
|
||||||
|
-- Biblio
|
||||||
|
-- ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
create schema biblio;
|
||||||
|
|
||||||
|
CREATE TABLE biblio.genres (
|
||||||
|
genre_id int primary key,
|
||||||
|
genre text
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO biblio.genres (genre_id, genre) VALUES
|
||||||
|
(1,'Science-Fiction'),
|
||||||
|
(2,'Fantasy'),
|
||||||
|
(3,'Young adult'),
|
||||||
|
(4,'Bit-lit'),
|
||||||
|
(5,'Policier'),
|
||||||
|
(6,'Romance'),
|
||||||
|
(7,'Espionnage'),
|
||||||
|
(8,'Aventure'),
|
||||||
|
(9,'Fantastique'),
|
||||||
|
(10,'Historique'),
|
||||||
|
(11,'Noir'),
|
||||||
|
(12,'Biographie'),
|
||||||
|
(13,'Cyberpunk'),
|
||||||
|
(14,'Steampunk');
|
||||||
|
|
||||||
|
create table biblio.auteurs (
|
||||||
|
auteur_id integer not null,
|
||||||
|
nom text not null,
|
||||||
|
"references" text[]
|
||||||
|
);
|
||||||
|
|
||||||
|
create table biblio.editeurs (
|
||||||
|
editeur_id integer not null,
|
||||||
|
editeur_nom text not null,
|
||||||
|
ville text
|
||||||
|
);
|
||||||
|
|
||||||
|
create table biblio.oeuvres (
|
||||||
|
oeuvre_id integer not null,
|
||||||
|
titre text not null,
|
||||||
|
infos jsonb
|
||||||
|
--constraint fk_oeuvre_genre foreign key (genre_id) references genres (genre_id)
|
||||||
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user