114 lines
3.2 KiB
JavaScript
114 lines
3.2 KiB
JavaScript
const { query } = require('../database/db');
|
|
|
|
class AiInsight {
|
|
/**
|
|
* Get AI insight by cycle and kandang
|
|
* @param {string} cycleId - Cycle ID
|
|
* @param {string|null} kandangId - Kandang ID (null for all kandangs)
|
|
* @returns {Promise<Object|null>} Insight object or null if not found or expired
|
|
*/
|
|
static async getByCycleAndKandang(cycleId, kandangId = null) {
|
|
try {
|
|
const result = await query(
|
|
`SELECT * FROM ai_insights
|
|
WHERE cycle_id = $1
|
|
AND ($2::text IS NULL AND kandang_id IS NULL OR kandang_id = $2)
|
|
AND expires_at > NOW()
|
|
ORDER BY generated_at DESC
|
|
LIMIT 1`,
|
|
[cycleId, kandangId]
|
|
);
|
|
|
|
if (result.rows.length === 0) return null;
|
|
|
|
return this.formatInsight(result.rows[0]);
|
|
} catch (error) {
|
|
console.error('Error getting AI insight:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create or update AI insight
|
|
* @param {Object} insightData - Insight data
|
|
* @returns {Promise<Object>} Created/updated insight
|
|
*/
|
|
static async upsert(insightData) {
|
|
try {
|
|
const { cycleId, kandangId, insightText, version = 'v1' } = insightData;
|
|
|
|
// Calculate expiry date (6 hours from now)
|
|
const expiresAt = new Date();
|
|
expiresAt.setHours(expiresAt.getHours() + 6);
|
|
|
|
// Check if insight already exists
|
|
const existing = await this.getByCycleAndKandang(cycleId, kandangId);
|
|
|
|
if (existing) {
|
|
// Update existing
|
|
const result = await query(
|
|
`UPDATE ai_insights
|
|
SET insight_text = $1, version = $2, generated_at = NOW(), expires_at = $3, updated_at = NOW()
|
|
WHERE id = $4
|
|
RETURNING *`,
|
|
[insightText, version, expiresAt, existing.id]
|
|
);
|
|
|
|
return this.formatInsight(result.rows[0]);
|
|
} else {
|
|
// Create new
|
|
const result = await query(
|
|
`INSERT INTO ai_insights (cycle_id, kandang_id, insight_text, version, generated_at, expires_at)
|
|
VALUES ($1, $2, $3, $4, NOW(), $5)
|
|
RETURNING *`,
|
|
[cycleId, kandangId, insightText, version, expiresAt]
|
|
);
|
|
|
|
return this.formatInsight(result.rows[0]);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error upserting AI insight:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Delete expired insights
|
|
* @returns {Promise<number>} Number of deleted rows
|
|
*/
|
|
static async cleanupExpired() {
|
|
try {
|
|
const result = await query(
|
|
'DELETE FROM ai_insights WHERE expires_at < NOW()',
|
|
[]
|
|
);
|
|
|
|
return result.rowCount;
|
|
} catch (error) {
|
|
console.error('Error cleaning up expired insights:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Format database row to API response format
|
|
* @param {Object} row - Database row
|
|
* @returns {Object} Formatted insight
|
|
*/
|
|
static formatInsight(row) {
|
|
return {
|
|
id: row.id,
|
|
cycleId: row.cycle_id,
|
|
kandangId: row.kandang_id,
|
|
insightText: row.insight_text,
|
|
version: row.version,
|
|
generatedAt: row.generated_at,
|
|
expiresAt: row.expires_at,
|
|
createdAt: row.created_at,
|
|
updatedAt: row.updated_at,
|
|
};
|
|
}
|
|
}
|
|
|
|
module.exports = AiInsight;
|