This commit is contained in:
2025-11-01 14:29:19 +01:00
parent cf4d3ce7ae
commit 5854267f45

View File

@@ -361,46 +361,132 @@ CREATE TABLE emplois (
FOREIGN KEY (id_personne) REFERENCES personnes(id_personne),
FOREIGN KEY (id_societe) REFERENCES societe(id)
);
*/
insert into emplois values
-- ----------------------------------------------------------------------
-- Banque
-- ----------------------------------------------------------------------
create schema banque;
CREATE TABLE account (
id bigint generated always as identity,
-- Générateur de numéro aléatoire
-- ----------------------------------------------------------------------
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,
balance NUMERIC(18,2) NOT NULL DEFAULT 0,
currency CHAR(3) NOT NULL,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()
);
CREATE TABLE "transaction" (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
CREATE TABLE banque."transaction" (
id UUID PRIMARY KEY DEFAULT uuidv7(),
reference TEXT,
amount NUMERIC(18,2) NOT NULL,
currency CHAR(3) NOT NULL,
from_account BIGINT NOT NULL REFERENCES account(id),
to_account BIGINT NOT NULL REFERENCES account(id),
from_account BIGINT NOT NULL REFERENCES banque.account(id),
to_account BIGINT NOT NULL REFERENCES banque.account(id),
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
processed BOOLEAN NOT NULL DEFAULT FALSE -- indique si ledger + soldes ont été appliqués
);
-- ledger (écritures comptables immuables) : append-only
CREATE TABLE ledger_entry (
id bigint generated always as identity,
transaction_id UUID NOT NULL REFERENCES "transaction"(id),
account_id BIGINT NOT NULL REFERENCES account(id),
CREATE TABLE banque.ledger_entry (
id bigint primary key generated always as identity,
transaction_id UUID NOT NULL REFERENCES banque."transaction"(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)
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
description TEXT
);
-- 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)
CREATE TABLE outbox_event (
id bigint generated always as identity,
CREATE TABLE banque.outbox_event (
id bigint primary key generated always as identity,
occurrenced_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
topic TEXT NOT NULL,
payload JSONB NOT NULL,
@@ -410,12 +496,178 @@ CREATE TABLE outbox_event (
);
-- table very simple de blockchain / chain d'audit
CREATE TABLE block_chain (
id bigint generated always as identity,
CREATE TABLE banque.block_chain (
id bigint primary key generated always as identity,
block_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
tx_id UUID NOT NULL, -- transaction incluse dans ce bloc (ou multiple selon choix)
previous_hash TEXT NULL,
block_hash TEXT NOT NULL,
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)
);