Add product price history model and database schema: Implement associations for ProductPriceHistory with ProductType and RegionData. Update FalukantService to utilize active character for user-related operations. Ensure product price history table exists in the database with appropriate structure and indexing.
This commit is contained in:
@@ -421,6 +421,13 @@ export default function setupAssociations() {
|
|||||||
DaySell.belongsTo(FalukantUser, { foreignKey: 'sellerId', as: 'user' });
|
DaySell.belongsTo(FalukantUser, { foreignKey: 'sellerId', as: 'user' });
|
||||||
FalukantUser.hasMany(DaySell, { foreignKey: 'sellerId', as: 'daySells' });
|
FalukantUser.hasMany(DaySell, { foreignKey: 'sellerId', as: 'daySells' });
|
||||||
|
|
||||||
|
// Produkt-Preishistorie (Zeitreihe für Preiskurven)
|
||||||
|
ProductPriceHistory.belongsTo(ProductType, { foreignKey: 'productId', as: 'productType' });
|
||||||
|
ProductType.hasMany(ProductPriceHistory, { foreignKey: 'productId', as: 'priceHistory' });
|
||||||
|
|
||||||
|
ProductPriceHistory.belongsTo(RegionData, { foreignKey: 'regionId', as: 'region' });
|
||||||
|
RegionData.hasMany(ProductPriceHistory, { foreignKey: 'regionId', as: 'productPriceHistory' });
|
||||||
|
|
||||||
Notification.belongsTo(FalukantUser, { foreignKey: 'userId', as: 'user' });
|
Notification.belongsTo(FalukantUser, { foreignKey: 'userId', as: 'user' });
|
||||||
FalukantUser.hasMany(Notification, { foreignKey: 'userId', as: 'notifications' });
|
FalukantUser.hasMany(Notification, { foreignKey: 'userId', as: 'notifications' });
|
||||||
|
|
||||||
|
|||||||
44
backend/models/falukant/log/product_price_history.js
Normal file
44
backend/models/falukant/log/product_price_history.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import { Model, DataTypes } from 'sequelize';
|
||||||
|
import { sequelize } from '../../../utils/sequelize.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preishistorie pro Produkt und Region (Zeitreihe für Preis-Graphen).
|
||||||
|
* Aktuell wird diese Tabelle noch nicht befüllt; sie dient nur als Grundlage.
|
||||||
|
*/
|
||||||
|
class ProductPriceHistory extends Model { }
|
||||||
|
|
||||||
|
ProductPriceHistory.init({
|
||||||
|
productId: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
regionId: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
price: {
|
||||||
|
type: DataTypes.DECIMAL(12, 2),
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
recordedAt: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: sequelize.literal('CURRENT_TIMESTAMP')
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
sequelize,
|
||||||
|
modelName: 'ProductPriceHistory',
|
||||||
|
tableName: 'product_price_history',
|
||||||
|
schema: 'falukant_log',
|
||||||
|
timestamps: false,
|
||||||
|
underscored: true,
|
||||||
|
indexes: [
|
||||||
|
{
|
||||||
|
name: 'product_price_history_product_region_recorded_idx',
|
||||||
|
fields: ['product_id', 'region_id', 'recorded_at']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
export default ProductPriceHistory;
|
||||||
|
|
||||||
@@ -89,6 +89,7 @@ import Learning from './falukant/data/learning.js';
|
|||||||
import Credit from './falukant/data/credit.js';
|
import Credit from './falukant/data/credit.js';
|
||||||
import DebtorsPrism from './falukant/data/debtors_prism.js';
|
import DebtorsPrism from './falukant/data/debtors_prism.js';
|
||||||
import HealthActivity from './falukant/log/health_activity.js';
|
import HealthActivity from './falukant/log/health_activity.js';
|
||||||
|
import ProductPriceHistory from './falukant/log/product_price_history.js';
|
||||||
|
|
||||||
// — Match3 Minigame —
|
// — Match3 Minigame —
|
||||||
import Match3Campaign from './match3/campaign.js';
|
import Match3Campaign from './match3/campaign.js';
|
||||||
@@ -239,6 +240,7 @@ const models = {
|
|||||||
Credit,
|
Credit,
|
||||||
DebtorsPrism,
|
DebtorsPrism,
|
||||||
HealthActivity,
|
HealthActivity,
|
||||||
|
ProductPriceHistory,
|
||||||
RegionDistance,
|
RegionDistance,
|
||||||
VehicleType,
|
VehicleType,
|
||||||
Vehicle,
|
Vehicle,
|
||||||
|
|||||||
@@ -2901,9 +2901,9 @@ class FalukantService extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getGifts(hashedUserId) {
|
async getGifts(hashedUserId) {
|
||||||
// 1) Mein User & Character
|
// 1) Mein aktiver Falukant-User & dessen aktueller Charakter
|
||||||
const user = await this.getFalukantUserByHashedId(hashedUserId);
|
const user = await this.getFalukantUserByHashedId(hashedUserId);
|
||||||
const myChar = await FalukantCharacter.findOne({ where: { userId: user.id } });
|
const myChar = user.character;
|
||||||
if (!myChar) throw new Error('Character not found');
|
if (!myChar) throw new Error('Character not found');
|
||||||
|
|
||||||
// 2) Beziehung finden und „anderen“ Character bestimmen
|
// 2) Beziehung finden und „anderen“ Character bestimmen
|
||||||
@@ -3520,12 +3520,10 @@ class FalukantService extends BaseService {
|
|||||||
|
|
||||||
async baptise(hashedUserId, childId, firstName) {
|
async baptise(hashedUserId, childId, firstName) {
|
||||||
try {
|
try {
|
||||||
const falukantUser = await getFalukantUserOrFail(hashedUserId);
|
// Nutze den aktuell aktiven Charakter (wie in getFalukantUserByHashedId definiert),
|
||||||
const parentCharacter = await FalukantCharacter.findOne({
|
// statt „irgendeinen“ Charakter des Users per userId zu suchen.
|
||||||
where: {
|
const falukantUser = await this.getFalukantUserByHashedId(hashedUserId);
|
||||||
userId: falukantUser.id,
|
const parentCharacter = falukantUser.character;
|
||||||
},
|
|
||||||
});
|
|
||||||
if (!parentCharacter) {
|
if (!parentCharacter) {
|
||||||
throw new Error('Parent character not found');
|
throw new Error('Parent character not found');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -539,6 +539,27 @@ const syncDatabase = async () => {
|
|||||||
console.warn('⚠️ relationship_change_log/Trigger konnten nicht sichergestellt werden:', e?.message || e);
|
console.warn('⚠️ relationship_change_log/Trigger konnten nicht sichergestellt werden:', e?.message || e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Preishistorie für Produkte (Zeitreihe) – nur Schema/Struktur, noch ohne Logik
|
||||||
|
console.log("Ensuring falukant_log.product_price_history exists...");
|
||||||
|
try {
|
||||||
|
await sequelize.query(`
|
||||||
|
CREATE TABLE IF NOT EXISTS falukant_log.product_price_history (
|
||||||
|
id serial PRIMARY KEY,
|
||||||
|
product_id integer NOT NULL,
|
||||||
|
region_id integer NOT NULL,
|
||||||
|
price numeric(12,2) NOT NULL,
|
||||||
|
recorded_at timestamp with time zone NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
`);
|
||||||
|
await sequelize.query(`
|
||||||
|
CREATE INDEX IF NOT EXISTS product_price_history_product_region_recorded_idx
|
||||||
|
ON falukant_log.product_price_history (product_id, region_id, recorded_at);
|
||||||
|
`);
|
||||||
|
console.log("✅ product_price_history ist vorhanden.");
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('⚠️ product_price_history konnte nicht sichergestellt werden:', e?.message || e);
|
||||||
|
}
|
||||||
|
|
||||||
// Vorab: Stelle kritische Spalten sicher, damit Index-Erstellung nicht fehlschlägt
|
// Vorab: Stelle kritische Spalten sicher, damit Index-Erstellung nicht fehlschlägt
|
||||||
console.log("Pre-ensure Taxi columns (traffic_light) ...");
|
console.log("Pre-ensure Taxi columns (traffic_light) ...");
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user