diff --git a/backend/migrations/20251220000000-add-reputation-to-character.cjs b/backend/migrations/20251220000000-add-reputation-to-character.cjs new file mode 100644 index 0000000..bbd557f --- /dev/null +++ b/backend/migrations/20251220000000-add-reputation-to-character.cjs @@ -0,0 +1,79 @@ +"use strict"; + +module.exports = { + async up(queryInterface, Sequelize) { + // falukant_data.character.reputation (integer, default random 20..80) + // Wichtig: Schema explizit angeben + // Vorgehen: + // - Spalte anlegen (falls noch nicht vorhanden) + // - bestehende Zeilen initialisieren (random 20..80) + // - DEFAULT setzen (random 20..80) + // - NOT NULL + CHECK 0..100 erzwingen + await queryInterface.sequelize.query(` + DO $$ + BEGIN + IF NOT EXISTS ( + SELECT 1 + FROM information_schema.columns + WHERE table_schema = 'falukant_data' + AND table_name = 'character' + AND column_name = 'reputation' + ) THEN + ALTER TABLE falukant_data."character" + ADD COLUMN reputation integer; + END IF; + END$$; + `); + + // Backfill: nur NULLs initialisieren (damit bestehende Werte nicht überschrieben werden) + await queryInterface.sequelize.query(` + UPDATE falukant_data."character" + SET reputation = (floor(random()*61)+20)::int + WHERE reputation IS NULL; + `); + + // DEFAULT + NOT NULL (nach Backfill) + await queryInterface.sequelize.query(` + ALTER TABLE falukant_data."character" + ALTER COLUMN reputation SET DEFAULT (floor(random()*61)+20)::int; + `); + await queryInterface.sequelize.query(` + ALTER TABLE falukant_data."character" + ALTER COLUMN reputation SET NOT NULL; + `); + + // Enforce 0..100 at DB level (percent) + // (IF NOT EXISTS pattern, because deployments can be re-run) + await queryInterface.sequelize.query(` + DO $$ + BEGIN + IF NOT EXISTS ( + SELECT 1 + FROM pg_constraint c + JOIN pg_class t ON t.oid = c.conrelid + JOIN pg_namespace n ON n.oid = t.relnamespace + WHERE c.conname = 'character_reputation_0_100_chk' + AND n.nspname = 'falukant_data' + AND t.relname = 'character' + ) THEN + ALTER TABLE falukant_data."character" + ADD CONSTRAINT character_reputation_0_100_chk + CHECK (reputation >= 0 AND reputation <= 100); + END IF; + END$$; + `); + }, + + async down(queryInterface, Sequelize) { + await queryInterface.sequelize.query(` + ALTER TABLE falukant_data."character" + DROP CONSTRAINT IF EXISTS character_reputation_0_100_chk; + `); + await queryInterface.sequelize.query(` + ALTER TABLE falukant_data."character" + DROP COLUMN IF EXISTS reputation; + `); + }, +}; + + diff --git a/backend/models/falukant/data/character.js b/backend/models/falukant/data/character.js index 6cc449f..e76b99b 100644 --- a/backend/models/falukant/data/character.js +++ b/backend/models/falukant/data/character.js @@ -34,6 +34,18 @@ FalukantCharacter.init( type: DataTypes.INTEGER, allowNull: false, defaultValue: 1} + , + reputation: { + type: DataTypes.INTEGER, + allowNull: false, + // Initialisierung: zufällig 20..80 (Prozent) + // DB-seitig per DEFAULT umgesetzt, damit es auch ohne App-Logic gilt. + defaultValue: sequelize.literal('(floor(random()*61)+20)'), + validate: { + min: 0, + max: 100 + } + } }, { sequelize, diff --git a/backend/services/falukantService.js b/backend/services/falukantService.js index 5232c1b..2777674 100644 --- a/backend/services/falukantService.js +++ b/backend/services/falukantService.js @@ -372,7 +372,7 @@ class FalukantService extends BaseService { { model: TitleOfNobility, as: 'nobleTitle', attributes: ['labelTr', 'id'] }, { model: CharacterTrait, as: 'traits', attributes: ['id', 'tr'] } ], - attributes: ['id', 'birthdate', 'gender', 'moodId', 'health'] + attributes: ['id', 'birthdate', 'gender', 'moodId', 'health', 'reputation'] }, { model: UserHouse, @@ -496,7 +496,7 @@ class FalukantService extends BaseService { { model: FalukantCharacter, as: 'character', - attributes: ['birthdate', 'health'], + attributes: ['birthdate', 'health', 'reputation'], include: [ { model: Relationship, diff --git a/frontend/src/i18n/locales/de/falukant.json b/frontend/src/i18n/locales/de/falukant.json index a5e373b..9c9b13c 100644 --- a/frontend/src/i18n/locales/de/falukant.json +++ b/frontend/src/i18n/locales/de/falukant.json @@ -742,7 +742,8 @@ "reputation": { "title": "Reputation", "overview": { - "title": "Übersicht" + "title": "Übersicht", + "current": "Deine aktuelle Reputation" }, "party": { "title": "Feste", diff --git a/frontend/src/i18n/locales/en/falukant.json b/frontend/src/i18n/locales/en/falukant.json index fd7ed8f..897bf48 100644 --- a/frontend/src/i18n/locales/en/falukant.json +++ b/frontend/src/i18n/locales/en/falukant.json @@ -198,6 +198,16 @@ "nobility": { "cooldown": "You can only advance again on {date}." }, + "reputation": { + "title": "Reputation", + "overview": { + "title": "Overview", + "current": "Your current reputation" + }, + "party": { + "title": "Parties" + } + }, "branchProduction": { "storageAvailable": "Free storage" }, diff --git a/frontend/src/views/falukant/ReputationView.vue b/frontend/src/views/falukant/ReputationView.vue index 973dbf6..cd810ce 100644 --- a/frontend/src/views/falukant/ReputationView.vue +++ b/frontend/src/views/falukant/ReputationView.vue @@ -12,7 +12,10 @@
Deine aktuelle Reputation: …
++ {{ $t('falukant.reputation.overview.current') }}: + {{ reputationDisplay }} +