From: Valentin Hulin Date: Sat, 28 Feb 2026 20:45:54 +0000 (+0100) Subject: Mise à jour price pour shema db demande par stephane X-Git-Url: https://git.digitality.be/?a=commitdiff_plain;h=4d434c0fa9597c653e6f2b0621d05e2ba34fe1b2;p=pdw25-26 Mise à jour price pour shema db demande par stephane --- diff --git a/Wallette/server/modules/price/repositories/pair.repository.js b/Wallette/server/modules/price/repositories/pair.repository.js index 484251b..609b549 100644 --- a/Wallette/server/modules/price/repositories/pair.repository.js +++ b/Wallette/server/modules/price/repositories/pair.repository.js @@ -3,11 +3,13 @@ import { db } from "../db.js"; /** * Accepte: * - BTC/EUR, BTC_USDT, BTC-USDT, "btc eur" - * Et match la DB qui stocke pair_code = BTC_EUR etc. + * Et match la DB qui stocke pair_code = BTC/EUR etc. */ export async function getActivePairIdByCode(pairCode) { const raw = String(pairCode || "").trim().toUpperCase(); - const normalized = raw.replace(/\s+/g, "").replace(/[-/]/g, "_"); + // IMPORTANT: la DB du projet stocke la paire AVEC le slash (ex: "BTC/EUR"). + // On ne convertit donc plus "/" (ni "-") en "_". + const normalized = raw.replace(/\s+/g, ""); const [rows] = await db.execute( ` diff --git a/Wallette/server/modules/price/repositories/price.repository.js b/Wallette/server/modules/price/repositories/price.repository.js index 6dec8cb..0218a67 100644 --- a/Wallette/server/modules/price/repositories/price.repository.js +++ b/Wallette/server/modules/price/repositories/price.repository.js @@ -2,15 +2,19 @@ import { db } from "../db.js"; import crypto from "crypto"; /** - * Insère / met à jour une BOUGIE OHLC (5 minutes) dans la table existante. - * - interval_sec = 300 - * - timestamp_ms = début du bucket (arrondi) - * - open = premier prix du bucket - * - high/low = extrêmes du bucket - * - close = dernier prix du bucket + * Schéma officiel du projet (table: price_points) + * Colonnes utilisées: + * - current_price + * - volume_24h + * - candle_close + * (+ métadonnées habituelles: price_id, pair_id, source, timestamp_ms, created_at_ms) + */ + +/** + * Insère un point de prix. * - * ⚠️ Pour que high/low varient, il faut appeler insertPricePoint - * plus souvent que toutes les 5 minutes (ex: toutes les 10–30s). + * NOTE: On n'utilise pas de colonnes OHLC (open/high/low/close) car elles n'existent + * pas dans le schéma du projet. */ export async function insertPricePoint({ pair_id, @@ -30,100 +34,38 @@ export async function insertPricePoint({ if (!Number.isFinite(price) || price <= 0) throw new Error("current_price invalide (>0)"); if (!src) throw new Error("source obligatoire"); - const intervalSec = 300; // 5 minutes - - // ✅ bucket start (début de la bougie) - const bucketStart = Math.floor(ts / (intervalSec * 1000)) * (intervalSec * 1000); - const bucketEnd = bucketStart + (intervalSec * 1000) - 1; - - const closeValue = candle_close != null ? Number(candle_close) : price; - if (!Number.isFinite(closeValue) || closeValue <= 0) { - throw new Error("candle_close invalide (>0)"); - } - const vol = volume_24h == null ? null : Number(volume_24h); if (vol != null && (!Number.isFinite(vol) || vol < 0)) { - throw new Error("volume invalide (>=0)"); + throw new Error("volume_24h invalide (>=0)"); } - // ✅ Cherche une bougie existante DANS le bucket (même si timestamp_ms n'est pas arrondi) - const [existing] = await db.execute( - ` - SELECT price_id, open_price, high_price, low_price, close_price, timestamp_ms - FROM price_points - WHERE pair_id = ? - AND source = ? - AND interval_sec = ? - AND timestamp_ms BETWEEN ? AND ? - ORDER BY timestamp_ms ASC - LIMIT 1 - `, - [pid, src, intervalSec, bucketStart, bucketEnd] - ); - - // 2) Si pas de bougie -> on crée une nouvelle - if (existing.length === 0) { - const priceId = crypto.randomUUID(); - - await db.execute( - ` - INSERT INTO price_points ( - price_id, - pair_id, - source, - interval_sec, - timestamp_ms, - open_price, - high_price, - low_price, - close_price, - volume, - created_at_ms - ) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - `, - [ - priceId, - pid, - src, - intervalSec, - bucketStart, - price, // open - price, // high - price, // low - closeValue, // close - vol, - Date.now() - ] - ); - return; + const closeValue = candle_close == null ? price : Number(candle_close); + if (!Number.isFinite(closeValue) || closeValue <= 0) { + throw new Error("candle_close invalide (>0)"); } - // 3) Sinon -> update OHLC (open ne change pas) - const row = existing[0]; - const prevHigh = Number(row.high_price); - const prevLow = Number(row.low_price); - - const newHigh = Number.isFinite(prevHigh) ? Math.max(prevHigh, price) : price; - const newLow = Number.isFinite(prevLow) ? Math.min(prevLow, price) : price; + const priceId = crypto.randomUUID(); await db.execute( ` - UPDATE price_points - SET timestamp_ms = ?, -- ✅ on "recalle" la bougie sur le bucketStart - high_price = ?, - low_price = ?, - close_price = ?, - volume = ?, - created_at_ms = ? - WHERE price_id = ? + INSERT INTO price_points ( + price_id, + pair_id, + source, + timestamp_ms, + current_price, + volume_24h, + candle_close, + created_at_ms + ) + VALUES (?, ?, ?, ?, ?, ?, ?, ?) `, - [bucketStart, newHigh, newLow, closeValue, vol, Date.now(), row.price_id] + [priceId, pid, src, ts, price, vol, closeValue, Date.now()] ); } /** - * Prix courant = dernière bougie (close_price) + * Prix courant = dernier point (ORDER BY timestamp_ms DESC, created_at_ms DESC) */ export async function getCurrentPrice(pair_id) { const pid = Number(pair_id); @@ -133,11 +75,9 @@ export async function getCurrentPrice(pair_id) { SELECT source, timestamp_ms, - open_price, - high_price, - low_price, - close_price, - volume + current_price, + volume_24h, + candle_close FROM price_points WHERE pair_id = ? ORDER BY timestamp_ms DESC, created_at_ms DESC @@ -152,18 +92,14 @@ export async function getCurrentPrice(pair_id) { return { source: r.source, timestamp_ms: r.timestamp_ms, - current_price: r.close_price, - open_price: r.open_price, - high_price: r.high_price, - low_price: r.low_price, - close_price: r.close_price, - volume_24h: r.volume ?? null, - candle_close: r.close_price + current_price: r.current_price, + volume_24h: r.volume_24h ?? null, + candle_close: r.candle_close ?? null }; } /** - * Historique = dernières bougies (OHLC) + * Historique = derniers points de prix. */ export async function getPriceHistory(pair_id, limit = 200) { const pid = Number(pair_id); @@ -174,11 +110,9 @@ export async function getPriceHistory(pair_id, limit = 200) { SELECT source, timestamp_ms, - open_price, - high_price, - low_price, - close_price, - volume + current_price, + volume_24h, + candle_close FROM price_points WHERE pair_id = ? ORDER BY timestamp_ms DESC, created_at_ms DESC @@ -190,12 +124,8 @@ export async function getPriceHistory(pair_id, limit = 200) { return rows.reverse().map((r) => ({ source: r.source, timestamp_ms: r.timestamp_ms, - current_price: r.close_price, - open_price: r.open_price, - high_price: r.high_price, - low_price: r.low_price, - close_price: r.close_price, - volume_24h: r.volume ?? null, - candle_close: r.close_price + current_price: r.current_price, + volume_24h: r.volume_24h ?? null, + candle_close: r.candle_close ?? null })); -} \ No newline at end of file +} diff --git a/Wallette/server/modules/price/routes/api.router.js b/Wallette/server/modules/price/routes/api.router.js index e0e16ae..999c494 100644 --- a/Wallette/server/modules/price/routes/api.router.js +++ b/Wallette/server/modules/price/routes/api.router.js @@ -70,11 +70,8 @@ router.get("/price/current", async (req, res) => { source: row.source, timestamp_ms: row.timestamp_ms, current_price: row.current_price, - open_price: row.open_price, - high_price: row.high_price, - low_price: row.low_price, - close_price: row.close_price, - volume_24h: row.volume_24h ?? null + volume_24h: row.volume_24h ?? null, + candle_close: row.candle_close ?? null }); } catch (err) { return fail(res, 500, "DB_ERROR", "Erreur lecture prix courant", err.message); diff --git a/Wallette/server/modules/price/server.js b/Wallette/server/modules/price/server.js deleted file mode 100644 index 566194c..0000000 --- a/Wallette/server/modules/price/server.js +++ /dev/null @@ -1,58 +0,0 @@ -// ========================================================= -// PRICE SERVICE - Serveur standalone -// ========================================================= -// Ce fichier encapsule le module price -// en microservice Express indépendant sur port 3001. -// -// USAGE : node modules/price/server.js (depuis Wallette/server/) -// PORT : process.env.PRICE_PORT || 3001 -// ========================================================= - -import cors from 'cors'; -import dotenv from 'dotenv'; -import express from 'express'; -import path from 'path'; -import { fileURLToPath } from 'url'; - -// Charger .env depuis Wallette/server/.env -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); -dotenv.config({ path: path.resolve(__dirname, '../../.env') }); - -// IMPORTANT : dotenv doit être chargé AVANT l'import du router -// car db.js lit process.env au moment de l'import -const { default: priceApiRouter } = await import('./routes/api.router.js'); - -const PORT = process.env.PRICE_PORT || 3001; - -const app = express(); -app.use(cors()); -app.use(express.json()); -app.use(express.static(path.join(__dirname, 'public'))); - -// ─── Health check (requis par le gateway) ──────────────── -app.get('/health', (req, res) => { - res.json({ ok: true, service: 'price-service', port: PORT }); -}); - -// ─── Routes API price ───────────────────────────────────── -// Le router expose /price/current, /price/history... -// On le monte sur /api pour que le gateway route /api/price/* correctement -app.use('/api', priceApiRouter); - -// ─── 404 ───────────────────────────────────────────────── -app.use((req, res) => { - res.status(404).json({ ok: false, error: { code: 'NOT_FOUND', message: 'Route non trouvée' } }); -}); - -// ─── Démarrage ──────────────────────────────────────────── -app.listen(PORT, () => { - console.log(` - ╔══════════════════════════════════════════╗ - ║ PRICE SERVICE ║ - ║ HTTP : http://localhost:${PORT} ║ - ║ → /health = OK ║ - ║ → /api/price/current?pair= = prix ║ - ║ → /api/price/history?pair= = historique ║ - ╚══════════════════════════════════════════╝`); -});