codenuk_backend_mine/services/template-manager/src/models/feature.js

265 lines
7.1 KiB
JavaScript

const database = require('../config/database');
const { v4: uuidv4 } = require('uuid');
class Feature {
constructor(data = {}) {
this.id = data.id;
this.template_id = data.template_id;
this.feature_id = data.feature_id;
this.name = data.name;
this.description = data.description;
this.feature_type = data.feature_type;
this.complexity = data.complexity;
this.display_order = data.display_order;
this.usage_count = data.usage_count;
this.user_rating = data.user_rating;
this.is_default = data.is_default;
this.created_by_user = data.created_by_user;
this.created_at = data.created_at;
this.updated_at = data.updated_at;
}
// Update feature fields
static async update(id, updateData) {
const fields = []
const values = []
let idx = 1
const allowed = [
'name',
'description',
'feature_type',
'complexity',
'display_order',
'is_default'
]
for (const key of allowed) {
if (updateData[key] !== undefined) {
fields.push(`${key} = $${idx++}`)
values.push(updateData[key])
}
}
if (fields.length === 0) {
return await Feature.getById(id)
}
const query = `
UPDATE template_features
SET ${fields.join(', ')}, updated_at = NOW()
WHERE id = $${idx}
RETURNING *
`
values.push(id)
const result = await database.query(query, values)
return result.rows.length > 0 ? new Feature(result.rows[0]) : null
}
// Delete a feature
static async delete(id) {
const result = await database.query('DELETE FROM template_features WHERE id = $1', [id])
return result.rowCount > 0
}
// Get all features for a template
static async getByTemplateId(templateId) {
const query = `
SELECT * FROM template_features
WHERE template_id = $1
ORDER BY
CASE feature_type
WHEN 'essential' THEN 1
WHEN 'suggested' THEN 2
WHEN 'custom' THEN 3
END,
display_order,
usage_count DESC,
name
`;
const result = await database.query(query, [templateId]);
return result.rows.map(row => new Feature(row));
}
// Get popular features across all templates
static async getPopularFeatures(limit = 10) {
const query = `
SELECT
tf.*,
t.title as template_title,
t.type as template_type
FROM template_features tf
JOIN templates t ON tf.template_id = t.id
WHERE tf.usage_count > 0
ORDER BY tf.usage_count DESC, tf.user_rating DESC
LIMIT $1
`;
const result = await database.query(query, [limit]);
return result.rows.map(row => new Feature(row));
}
// Create new feature
static async create(featureData) {
const id = uuidv4();
const query = `
INSERT INTO template_features (
id, template_id, feature_id, name, description,
feature_type, complexity, display_order, is_default, created_by_user
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
RETURNING *
`;
const values = [
id,
featureData.template_id,
featureData.feature_id,
featureData.name,
featureData.description,
featureData.feature_type,
featureData.complexity,
featureData.display_order || 0,
featureData.is_default || false,
featureData.created_by_user || false
];
const result = await database.query(query, values);
return new Feature(result.rows[0]);
}
// Increment usage count
async incrementUsage(userSession = null, projectId = null) {
const client = await database.getClient();
try {
await client.query('BEGIN');
// Update usage count
const updateQuery = `
UPDATE template_features
SET usage_count = usage_count + 1
WHERE id = $1
RETURNING *
`;
const updateResult = await client.query(updateQuery, [this.id]);
// Track usage
const trackQuery = `
INSERT INTO feature_usage (template_id, feature_id, user_session, project_id)
VALUES ($1, $2, $3, $4)
`;
await client.query(trackQuery, [this.template_id, this.id, userSession, projectId]);
await client.query('COMMIT');
if (updateResult.rows.length > 0) {
Object.assign(this, updateResult.rows[0]);
}
return this;
} catch (error) {
await client.query('ROLLBACK');
throw error;
} finally {
client.release();
}
}
// Update rating
async updateRating(newRating) {
const query = `
UPDATE template_features
SET user_rating = $2
WHERE id = $1
RETURNING *
`;
const result = await database.query(query, [this.id, newRating]);
if (result.rows.length > 0) {
Object.assign(this, result.rows[0]);
}
return this;
}
// Get feature by ID
static async getById(id) {
const query = `
SELECT tf.*, t.title as template_title, t.type as template_type
FROM template_features tf
JOIN templates t ON tf.template_id = t.id
WHERE tf.id = $1
`;
const result = await database.query(query, [id]);
return result.rows.length > 0 ? new Feature(result.rows[0]) : null;
}
// Get features by type
static async getByType(featureType, limit = 20) {
const query = `
SELECT tf.*, t.title as template_title, t.type as template_type
FROM template_features tf
JOIN templates t ON tf.template_id = t.id
WHERE tf.feature_type = $1
ORDER BY tf.usage_count DESC, tf.user_rating DESC
LIMIT $2
`;
const result = await database.query(query, [featureType, limit]);
return result.rows.map(row => new Feature(row));
}
// Get a template_features row by (template_id, feature_id)
static async getByFeatureId(templateId, featureId) {
const query = `
SELECT * FROM template_features
WHERE template_id = $1 AND feature_id = $2
LIMIT 1
`
const result = await database.query(query, [templateId, featureId])
return result.rows.length > 0 ? new Feature(result.rows[0]) : null
}
// Get feature statistics
static async getStats() {
const query = `
SELECT
feature_type,
COUNT(*) as count,
AVG(usage_count) as avg_usage,
AVG(user_rating) as avg_rating
FROM template_features
GROUP BY feature_type
ORDER BY count DESC
`;
const result = await database.query(query);
return result.rows;
}
// Search features
static async search(searchTerm, templateId = null) {
let query = `
SELECT tf.*, t.title as template_title, t.type as template_type
FROM template_features tf
JOIN templates t ON tf.template_id = t.id
WHERE (tf.name ILIKE $1 OR tf.description ILIKE $1)
`;
const params = [`%${searchTerm}%`];
if (templateId) {
query += ` AND tf.template_id = $2`;
params.push(templateId);
}
query += ` ORDER BY tf.usage_count DESC, tf.user_rating DESC`;
const result = await database.query(query, params);
return result.rows.map(row => new Feature(row));
}
}
module.exports = Feature;