You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
senzapaura_es/docs/modelo-datos.md

105 KiB

Modelo de datos completo de Senza Paura

Generado el 2026-09-14T20:26:39.479Z desde el esquema PostgreSQL aplicado y contrastado con los contratos TypeScript de la aplicación.

Alcance: 83 tablas, 1 vista, 3 secuencias, 4 funciones de base y 42 migraciones aplicadas.

Fuentes y convenciones

  • Esquema declarativo: src/lib/server/db/schema/.
  • Historial físico: drizzle/.
  • Campos TypeScript en camelCase; nombres PostgreSQL en snake_case.
  • text es el identificador opaco habitual. pais.codigo es la excepción: usa ISO 3166-1 alfa-2.
  • Los importes monetarios son enteros en céntimos y la moneda usa código ISO en minúsculas.
  • Las marcas de tiempo son timestamp with time zone.
  • Los estados editoriales comunes son borrador, publicado y retirado.

Vista conceptual

flowchart LR
  BASE[Base y artistas] --> CAT[Catálogo musical]
  CAT --> INT[Interacción y escucha]
  CTA[Cuentas y suscripciones] --> INT
  CTA --> COM[Comercio]
  CAT --> COM
  COM --> FAC[Facturación]
  CAT --> EDI[Editorial y legal]
  BASE --> TALLER[Taller de letras]
  CAT --> TALLER
  CTA --> JUKE[Jukebox]
  CAT --> JUKE

La entidad central es cancion. Se relaciona con álbumes, estilos, intérpretes, audios, autoría, etiquetas, ventas, descargas, listas, reproducciones, likes/valoraciones, entradas editoriales, géneros del taller y prompts.

Reglas que definen el modelo

  • Una versión musical es otra fila de cancion; version_de_id apunta a la canción principal. Una versión no puede venderse por separado.
  • valoracion tiene una sola fila por (usuario_id, cancion_id): like y value no se pisan. Quitar un like conserva value y date.
  • artista es la identidad pública; persona es la persona física o acreditada. artista_persona permite solistas, grupos y proyectos.
  • compra materializa el permiso de descarga. El pedido conserva el hecho comercial; la factura conserva el hecho fiscal.
  • Una factura emitida no se reescribe: las correcciones se modelan como facturas rectificativas.
  • La programación del Jukebox vive como franjas JSON versionadas mediante revision para control de concurrencia.
  • El taller usa vocabularios normalizados y tablas puente para géneros, países, metros, compases, instrumentos, figuras, obras, versos y glosario.

Relaciones principales

erDiagram
  USUARIO ||--o{ VALORACION : emite
  CANCION ||--o{ VALORACION : recibe
  USUARIO ||--o{ FAVORITO : guarda
  USUARIO ||--o{ LISTA_REPRODUCCION : crea
  LISTA_REPRODUCCION ||--o{ LISTA_REPRODUCCION_CANCION : contiene
  CANCION ||--o{ LISTA_REPRODUCCION_CANCION : aparece
  ESTILO ||--o{ ALBUM : agrupa
  ESTILO ||--o{ CANCION : clasifica
  ALBUM ||--o{ CANCION : publica
  CANCION ||--o{ CANCION_AUDIO : dispone
  MEDIO ||--o{ CANCION_AUDIO : almacena
  ARTISTA ||--o{ CANCION_INTERPRETE : interpreta
  CANCION ||--o{ CANCION_INTERPRETE : acredita
  USUARIO ||--o{ PEDIDO : realiza
  PEDIDO ||--o{ PEDIDO_LINEA : contiene
  PEDIDO ||--o{ FACTURA : documenta
  USUARIO ||--o{ JUKEBOX : configura

Contratos JSON de la aplicación

Contenido editorial por bloques

artista.secciones, album.secciones, cancion.inspiracion_secciones y entrada.secciones contienen una lista ordenada de bloques:

  • Base: { id, tipo, titulo? }.
  • Texto: { tipo: "texto", texto }.
  • Código: { tipo: "codigo", codigo, lenguaje? }.
  • Medio: { tipo: "video" | "imagen" | "audio", url, pie?, subtitulos?, alt? }.

Presentación editorial

artista.presentacion, album.presentacion, cancion.presentacion y entrada.presentacion usan { plantilla, heroe }, donde heroe es automatico, visible u oculto.

Jukebox

jukebox.franjas y programacion_jukebox.franjas contienen objetos { id, inicio, fin, orden, catalogo, etiquetas?, contenidos }. orden es secuencial o aleatoria; contenidos referencia listas o canciones mediante { tipo, id }; varias etiquetas se combinan como unión.

Comercio abierto

producto.datos, pedido_linea.datos, intencion_pago.datos y factura.datos_congelados conservan atributos variables o una instantánea del momento comercial. Los campos consultables y las cantidades monetarias permanecen normalizados en columnas.

Base y artistas

artista

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
codigo text no ('ART-'::text || lpad((nextval('artista_codigo_seq'::regclass))::text, 6, '0'::text))
slug text no —
tipo text no 'persona'::text
nombre text no —
descripcion_corta text no ''::text
biografia text no ''::text
imagen_id text sí —
pais_codigo character(2) sí —
lugar_origen text sí —
inicio integer sí —
fin integer sí —
sitio_web text sí —
estado text no 'borrador'::text
origen text no 'manual'::text
modelo_origen text sí —
importado_en timestamp with time zone sí —
revisado_por text sí —
revisado_en timestamp with time zone sí —
creado_en timestamp with time zone no now()
actualizado_en timestamp with time zone sí —
secciones jsonb no '[]'::jsonb
presentacion jsonb no '{}'::jsonb
es_virtual boolean no false
nombre_visual text no 'texto'::text
logotipo_id text sí —
logotipo_claro_id text sí —
logotipo_oscuro_id text sí —

Claves y restricciones:

  • CHECK artista_estado_conocido: CHECK (estado = ANY (ARRAY['borrador'::text, 'publicado'::text, 'retirado'::text]))
  • CHECK artista_nombre_visual_conocido: CHECK (nombre_visual = ANY (ARRAY['texto'::text, 'logotipo'::text]))
  • CHECK artista_origen_conocido: CHECK (origen = ANY (ARRAY['manual'::text, 'importado'::text]))
  • CHECK artista_periodo_valido: CHECK (fin IS NULL OR inicio IS NULL OR fin >= inicio)
  • CHECK artista_presentacion_objeto: CHECK (jsonb_typeof(presentacion) = 'object'::text)
  • CHECK artista_secciones_array: CHECK (jsonb_typeof(secciones) = 'array'::text)
  • CHECK artista_tipo_conocido: CHECK (tipo = ANY (ARRAY['persona'::text, 'grupo'::text, 'proyecto'::text, 'virtual'::text]))
  • FK artista_imagen_fk: FOREIGN KEY (imagen_id) REFERENCES medio(id) ON DELETE SET NULL
  • FK artista_logotipo_claro_id_medio_id_fk: FOREIGN KEY (logotipo_claro_id) REFERENCES medio(id) ON DELETE SET NULL
  • FK artista_logotipo_id_medio_id_fk: FOREIGN KEY (logotipo_id) REFERENCES medio(id) ON DELETE SET NULL
  • FK artista_logotipo_oscuro_id_medio_id_fk: FOREIGN KEY (logotipo_oscuro_id) REFERENCES medio(id) ON DELETE SET NULL
  • FK artista_pais_fk: FOREIGN KEY (pais_codigo) REFERENCES pais(codigo)
  • PK artista_pkey: PRIMARY KEY (id)

Índices adicionales: artista_codigo_unico, artista_estado, artista_nombre, artista_slug_unico.

artista_identificador

Columna Tipo PostgreSQL Nulo Valor por defecto
artista_id text no —
sistema text no —
valor text no —

Claves y restricciones:

  • FK artista_identificador_artista_fk: FOREIGN KEY (artista_id) REFERENCES artista(id) ON DELETE CASCADE
  • PK artista_identificador_pk: PRIMARY KEY (artista_id, sistema)

Índices adicionales: artista_identificador_externo_unico.

artista_persona

Columna Tipo PostgreSQL Nulo Valor por defecto
artista_id text no —
persona_id text no —
relacion text no —
desde integer sí —
hasta integer sí —
orden integer no 0

Claves y restricciones:

  • CHECK artista_persona_periodo_valido: CHECK (hasta IS NULL OR desde IS NULL OR hasta >= desde)
  • CHECK artista_persona_relacion_conocida: CHECK (relacion = ANY (ARRAY['identidad'::text, 'miembro'::text, 'fundador'::text]))
  • FK artista_persona_artista_fk: FOREIGN KEY (artista_id) REFERENCES artista(id) ON DELETE CASCADE
  • FK artista_persona_persona_fk: FOREIGN KEY (persona_id) REFERENCES persona(id) ON DELETE CASCADE
  • PK artista_persona_pk: PRIMARY KEY (artista_id, persona_id, relacion)

Índices adicionales: artista_persona_persona.

medio

Archivo administrado: imagen, audio, muestra o documento, con metadatos técnicos e integridad SHA-256.

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
clase text no —
clave text no —
publico boolean no false
mime text no —
bytes integer no —
duracion_s integer sí —
ancho integer sí —
alto integer sí —
sha256 text no —
alt text sí —
creado_en timestamp with time zone no now()

Claves y restricciones:

  • PK medio_pkey: PRIMARY KEY (id)
  • UNIQUE medio_clave_unique: UNIQUE (clave)

Índices adicionales: medio_clase, medio_sha.

pais

Columna Tipo PostgreSQL Nulo Valor por defecto
codigo character(2) no —
nombre text no —
gentilicio text sí —
region text sí —

Claves y restricciones:

  • PK pais_pkey: PRIMARY KEY (codigo)

persona

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
slug text no —
nombre text no —
nombre_legal text sí —
ipi text sí —
nacimiento integer sí —
defuncion integer sí —
pais_codigo character(2) sí —
retrato_id text sí —
semblanza text sí —
estado text no 'borrador'::text
creado_en timestamp with time zone no now()
actualizado_en timestamp with time zone sí —
origen text no 'manual'::text
modelo_origen text sí —
importado_en timestamp with time zone sí —
revisado_por text sí —
revisado_en timestamp with time zone sí —

Claves y restricciones:

  • FK persona_pais_codigo_pais_codigo_fk: FOREIGN KEY (pais_codigo) REFERENCES pais(codigo)
  • FK persona_retrato_id_medio_id_fk: FOREIGN KEY (retrato_id) REFERENCES medio(id) ON DELETE SET NULL
  • PK persona_pkey: PRIMARY KEY (id)
  • UNIQUE persona_slug_unique: UNIQUE (slug)

Índices adicionales: persona_nombre, persona_pais.

Catálogo musical

album

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
slug text no —
titulo text no —
estilo_id text no —
tipo text no 'album'::text
fecha text no —
portada_id text sí —
sello text sí —
resumen text no ''::text
destacado boolean no false
numero_catalogo text sí —
formatos text[] sí —
pais_edicion_codigo character(2) sí —
estado text no 'borrador'::text
creado_en timestamp with time zone no now()
actualizado_en timestamp with time zone sí —
cuerpo text no ''::text
secciones jsonb no '[]'::jsonb
presentacion jsonb no '{}'::jsonb

Claves y restricciones:

  • CHECK album_presentacion_objeto: CHECK (jsonb_typeof(presentacion) = 'object'::text)
  • CHECK album_secciones_array: CHECK (jsonb_typeof(secciones) = 'array'::text)
  • FK album_estilo_id_estilo_id_fk: FOREIGN KEY (estilo_id) REFERENCES estilo(id)
  • FK album_pais_edicion_codigo_pais_codigo_fk: FOREIGN KEY (pais_edicion_codigo) REFERENCES pais(codigo)
  • FK album_portada_id_medio_id_fk: FOREIGN KEY (portada_id) REFERENCES medio(id) ON DELETE SET NULL
  • PK album_pkey: PRIMARY KEY (id)
  • UNIQUE album_slug_unique: UNIQUE (slug)

Índices adicionales: album_estilo, album_fecha.

album_enlace

Columna Tipo PostgreSQL Nulo Valor por defecto
album_id text no —
plataforma text no —
url text no —

Claves y restricciones:

  • FK album_enlace_album_id_album_id_fk: FOREIGN KEY (album_id) REFERENCES album(id) ON DELETE CASCADE
  • PK album_enlace_album_id_plataforma_pk: PRIMARY KEY (album_id, plataforma)

autoria

Columna Tipo PostgreSQL Nulo Valor por defecto
cancion_id text no —
persona_id text no —
rol text no —
porcentaje numeric(5,2) no —

Claves y restricciones:

  • FK autoria_cancion_id_cancion_id_fk: FOREIGN KEY (cancion_id) REFERENCES cancion(id) ON DELETE CASCADE
  • FK autoria_persona_id_persona_id_fk: FOREIGN KEY (persona_id) REFERENCES persona(id)
  • PK autoria_cancion_id_persona_id_rol_pk: PRIMARY KEY (cancion_id, persona_id, rol)
  • t autoria_suma_cien_trg: TRIGGER DEFERRABLE INITIALLY DEFERRED

Disparadores:

  • autoria_suma_cien_trg: CREATE CONSTRAINT TRIGGER autoria_suma_cien_trg AFTER INSERT OR DELETE OR UPDATE ON autoria DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION autoria_suma_cien()

cancion

Tema principal o versión musical. version_de_id apunta a otra canción y separa obra principal de arreglo o grabación.

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
slug text no —
titulo text no —
version_de_id text sí —
version_nombre text sí —
version_orden integer no 0
estilo_id text no —
album_id text sí —
numero integer sí —
duracion_s integer sí —
ano integer sí —
letra text sí —
idioma text no —
inspiracion text sí —
tiene_letra boolean no true
destacada boolean no false
portada_id text sí —
ala_venta boolean no false
estado text no 'borrador'::text
creado_en timestamp with time zone no now()
actualizado_en timestamp with time zone sí —
es_version boolean sí (version_de_id IS NOT NULL)
padre_no_es_version boolean sí CASE WHEN (version_de_id IS NULL) THEN NULL::boolean ELSE false END
version_nota text sí —
codigo text no ('CAN-'::text || lpad((nextval('cancion_codigo_seq'::regclass))::text, 6, '0'::text))
version_codigo text sí —
formacion_vocal formacion_vocal sí —
acceso acceso_cancion no 'publico'::acceso_cancion
inspiracion_secciones jsonb no '[]'::jsonb
presentacion jsonb no '{}'::jsonb

Claves y restricciones:

  • CHECK cancion_estado_conocido: CHECK (estado = ANY (ARRAY['borrador'::text, 'publicado'::text, 'retirado'::text]))
  • CHECK cancion_idioma_bcp47: CHECK (idioma ~ '^[a-z]{2,8}(-[A-Za-z0-9]{1,8})*$'::text)
  • CHECK cancion_inspiracion_secciones_array: CHECK (jsonb_typeof(inspiracion_secciones) = 'array'::text)
  • CHECK cancion_no_es_su_propia_version: CHECK (version_de_id IS NULL OR version_de_id <> id)
  • CHECK cancion_presentacion_objeto: CHECK (jsonb_typeof(presentacion) = 'object'::text)
  • CHECK cancion_version_codigo_presente: CHECK (version_de_id IS NULL AND version_codigo IS NULL OR version_de_id IS NOT NULL AND NULLIF(btrim(version_codigo), ''::text) IS NOT NULL)
  • CHECK cancion_version_no_se_vende: CHECK (version_de_id IS NULL OR NOT ala_venta)
  • FK cancion_album_id_album_id_fk: FOREIGN KEY (album_id) REFERENCES album(id) ON DELETE SET NULL
  • FK cancion_estilo_id_estilo_id_fk: FOREIGN KEY (estilo_id) REFERENCES estilo(id)
  • FK cancion_portada_id_medio_id_fk: FOREIGN KEY (portada_id) REFERENCES medio(id) ON DELETE SET NULL
  • FK cancion_version_de_fk: FOREIGN KEY (version_de_id) REFERENCES cancion(id) ON DELETE CASCADE
  • FK cancion_version_de_principal: FOREIGN KEY (version_de_id, padre_no_es_version) REFERENCES cancion(id, es_version)
  • PK cancion_pkey: PRIMARY KEY (id)
  • t cancion_publicable: TRIGGER DEFERRABLE INITIALLY DEFERRED
  • UNIQUE cancion_id_es_version_unica: UNIQUE (id, es_version)
  • UNIQUE cancion_slug_unique: UNIQUE (slug)

Índices adicionales: cancion_album, cancion_codigo_unico, cancion_destacada, cancion_estilo, cancion_version_codigo_unico, cancion_version_de.

Disparadores:

  • cancion_publicable: CREATE CONSTRAINT TRIGGER cancion_publicable AFTER INSERT OR UPDATE ON cancion DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION guardar_invariantes_publicacion()

cancion_audio

Columna Tipo PostgreSQL Nulo Valor por defecto
cancion_id text no —
medio_id text no —
uso text no —
formato text no —
bitrate_kbps integer sí —
frecuencia_hz integer sí —
bits_muestra integer sí —
canales integer sí —
orden integer no 0

Claves y restricciones:

  • CHECK cancion_audio_bitrate_valido: CHECK (bitrate_kbps IS NULL OR bitrate_kbps > 0)
  • CHECK cancion_audio_bits_validos: CHECK (bits_muestra IS NULL OR bits_muestra > 0)
  • CHECK cancion_audio_canales_validos: CHECK (canales IS NULL OR canales > 0)
  • CHECK cancion_audio_frecuencia_valida: CHECK (frecuencia_hz IS NULL OR frecuencia_hz > 0)
  • CHECK cancion_audio_uso_conocido: CHECK (uso = ANY (ARRAY['stream'::text, 'descarga'::text, 'master'::text]))
  • FK cancion_audio_cancion_fk: FOREIGN KEY (cancion_id) REFERENCES cancion(id) ON DELETE CASCADE
  • FK cancion_audio_medio_fk: FOREIGN KEY (medio_id) REFERENCES medio(id) ON DELETE RESTRICT
  • PK cancion_audio_pk: PRIMARY KEY (cancion_id, uso, formato)
  • t cancion_audios_publicables: TRIGGER DEFERRABLE INITIALLY DEFERRED

Índices adicionales: cancion_audio_cancion, cancion_audio_medio_unico.

Disparadores:

  • cancion_audios_publicables: CREATE CONSTRAINT TRIGGER cancion_audios_publicables AFTER INSERT OR DELETE OR UPDATE ON cancion_audio DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION guardar_invariantes_publicacion()

cancion_enlace

Columna Tipo PostgreSQL Nulo Valor por defecto
cancion_id text no —
plataforma text no —
url text no —

Claves y restricciones:

  • FK cancion_enlace_cancion_id_cancion_id_fk: FOREIGN KEY (cancion_id) REFERENCES cancion(id) ON DELETE CASCADE
  • PK cancion_enlace_cancion_id_plataforma_pk: PRIMARY KEY (cancion_id, plataforma)

cancion_etiqueta

Columna Tipo PostgreSQL Nulo Valor por defecto
cancion_id text no —
etiqueta_id text no —
orden integer no 0

Claves y restricciones:

  • FK cancion_etiqueta_cancion_id_cancion_id_fk: FOREIGN KEY (cancion_id) REFERENCES cancion(id) ON DELETE CASCADE
  • FK cancion_etiqueta_etiqueta_id_etiqueta_id_fk: FOREIGN KEY (etiqueta_id) REFERENCES etiqueta(id) ON DELETE CASCADE
  • PK cancion_etiqueta_cancion_id_etiqueta_id_pk: PRIMARY KEY (cancion_id, etiqueta_id)

cancion_interprete

Columna Tipo PostgreSQL Nulo Valor por defecto
cancion_id text no —
artista_id text no —
rol text no 'principal'::text
instrumento text sí —
acreditado_como text sí —
orden integer no 0

Claves y restricciones:

  • CHECK cancion_interprete_rol_conocido: CHECK (rol = ANY (ARRAY['principal'::text, 'invitado'::text, 'coros'::text, 'instrumentista'::text]))
  • FK cancion_interprete_artista_fk: FOREIGN KEY (artista_id) REFERENCES artista(id)
  • FK cancion_interprete_cancion_fk: FOREIGN KEY (cancion_id) REFERENCES cancion(id) ON DELETE CASCADE
  • PK cancion_interprete_pk: PRIMARY KEY (cancion_id, artista_id, rol)
  • t cancion_creditos_publicables: TRIGGER DEFERRABLE INITIALLY DEFERRED

Índices adicionales: cancion_interprete_artista.

Disparadores:

  • cancion_creditos_publicables: CREATE CONSTRAINT TRIGGER cancion_creditos_publicables AFTER INSERT OR DELETE OR UPDATE ON cancion_interprete DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION guardar_invariantes_publicacion()

cancion_persona

Columna Tipo PostgreSQL Nulo Valor por defecto
cancion_id text no —
persona_id text no —
rol text no —
orden integer no 0

Claves y restricciones:

  • FK cancion_persona_cancion_id_cancion_id_fk: FOREIGN KEY (cancion_id) REFERENCES cancion(id) ON DELETE CASCADE
  • FK cancion_persona_persona_id_persona_id_fk: FOREIGN KEY (persona_id) REFERENCES persona(id)
  • PK cancion_persona_cancion_id_persona_id_rol_pk: PRIMARY KEY (cancion_id, persona_id, rol)

credito_album

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
album_id text no —
persona_id text sí —
nombre text sí —
rol text no —
orden integer no 0

Claves y restricciones:

  • FK credito_album_album_id_album_id_fk: FOREIGN KEY (album_id) REFERENCES album(id) ON DELETE CASCADE
  • FK credito_album_persona_id_persona_id_fk: FOREIGN KEY (persona_id) REFERENCES persona(id)
  • PK credito_album_pkey: PRIMARY KEY (id)

Índices adicionales: credito_album_album.

estilo

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
slug text no —
nombre text no —
resumen text no ''::text
orden integer no 0
principal boolean no false
imagen_id text sí —
estado text no 'borrador'::text
creado_en timestamp with time zone no now()
actualizado_en timestamp with time zone sí —
cuerpo text no ''::text

Claves y restricciones:

  • FK estilo_imagen_id_medio_id_fk: FOREIGN KEY (imagen_id) REFERENCES medio(id) ON DELETE SET NULL
  • PK estilo_pkey: PRIMARY KEY (id)
  • UNIQUE estilo_slug_unique: UNIQUE (slug)

Índices adicionales: estilo_orden, estilo_principal_unico.

etiqueta

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
slug text no —
nombre text no —

Claves y restricciones:

  • PK etiqueta_pkey: PRIMARY KEY (id)
  • UNIQUE etiqueta_slug_unique: UNIQUE (slug)

registro_obra

Columna Tipo PostgreSQL Nulo Valor por defecto
cancion_id text no —
iswc text sí —
isrc text sí —
entidad text sí —
numero text sí —
editorial text sí —

Claves y restricciones:

  • FK registro_obra_cancion_id_cancion_id_fk: FOREIGN KEY (cancion_id) REFERENCES cancion(id) ON DELETE CASCADE
  • PK registro_obra_pkey: PRIMARY KEY (cancion_id)

traduccion

Columna Tipo PostgreSQL Nulo Valor por defecto
cancion_id text no —
idioma text no —
texto text no —

Claves y restricciones:

  • FK traduccion_cancion_id_cancion_id_fk: FOREIGN KEY (cancion_id) REFERENCES cancion(id) ON DELETE CASCADE
  • PK traduccion_cancion_id_idioma_pk: PRIMARY KEY (cancion_id, idioma)

video

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
cancion_id text no —
youtube_id text no —
titulo text no —
orden integer no 0

Claves y restricciones:

  • FK video_cancion_id_cancion_id_fk: FOREIGN KEY (cancion_id) REFERENCES cancion(id) ON DELETE CASCADE
  • PK video_pkey: PRIMARY KEY (id)

Índices adicionales: video_cancion.

Cuentas y suscripciones

codigo_acceso

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
email text no —
hash text no —
expira_en timestamp with time zone no —
intentos integer no 0
usado_en timestamp with time zone sí —
creado_en timestamp with time zone no now()

Claves y restricciones:

  • PK codigo_acceso_pkey: PRIMARY KEY (id)

Índices adicionales: codigo_acceso_email.

identidad

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
proveedor text no —
sujeto text no —
usuario_id text no —
email text sí —
creada_en timestamp with time zone no now()
ultimo_acceso_en timestamp with time zone sí —

Claves y restricciones:

  • FK identidad_usuario_id_usuario_id_fk: FOREIGN KEY (usuario_id) REFERENCES usuario(id) ON DELETE CASCADE
  • PK identidad_pkey: PRIMARY KEY (id)

Índices adicionales: identidad_proveedor_sujeto, identidad_usuario.

limite

Columna Tipo PostgreSQL Nulo Valor por defecto
clave text no —
intentos integer no —
expira_en bigint no —

Claves y restricciones:

  • PK limite_pkey: PRIMARY KEY (clave)

Índices adicionales: limite_expira.

plan_suscripcion

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
codigo text no —
nombre text no —
descripcion text sí —
nivel integer no —
precio_centimos integer no —
moneda text no 'eur'::text
periodicidad text no —
intervalos integer no 1
referencia_precio_externa text sí —
prestaciones jsonb no '[]'::jsonb
estado text no 'borrador'::text
creado_en timestamp with time zone no now()
actualizado_en timestamp with time zone no now()
incluye_publicidad boolean no false
calidad_escucha text sí 'media'::text
descargas_incluidas integer no 0
calidad_descarga text sí —
acceso_versiones boolean no false
ambito text no 'musica'::text
acceso_contenido_taller boolean no false
descarga_material_taller boolean no false
acceso_jukebox boolean no false

Claves y restricciones:

  • CHECK plan_suscripcion_ambito_conocido: CHECK (ambito = ANY (ARRAY['musica'::text, 'taller_letras'::text]))
  • CHECK plan_suscripcion_calidad_descarga_conocida: CHECK (calidad_descarga IS NULL OR (calidad_descarga = ANY (ARRAY['media'::text, 'alta'::text])))
  • CHECK plan_suscripcion_calidad_escucha_conocida: CHECK (calidad_escucha IS NULL OR (calidad_escucha = ANY (ARRAY['media'::text, 'alta'::text])))
  • CHECK plan_suscripcion_calidad_solo_con_descargas: CHECK (descargas_incluidas > 0 OR calidad_descarga IS NULL)
  • CHECK plan_suscripcion_capacidades_segun_ambito: CHECK (ambito = 'musica'::text AND calidad_escucha IS NOT NULL AND NOT acceso_contenido_taller AND NOT descarga_material_taller OR ambito = 'taller_letras'::text AND calidad_escucha IS NULL AND NOT incluye_publicidad AND descargas_incluidas = 0 AND calidad_descarga IS NULL AND NOT acceso_versiones AND NOT acceso_jukebox)
  • CHECK plan_suscripcion_codigo_no_vacio: CHECK (btrim(codigo) <> ''::text)
  • CHECK plan_suscripcion_descargas_no_negativas: CHECK (descargas_incluidas >= 0)
  • CHECK plan_suscripcion_estado_conocido: CHECK (estado = ANY (ARRAY['borrador'::text, 'disponible'::text, 'retirado'::text]))
  • CHECK plan_suscripcion_intervalos_positivos: CHECK (intervalos > 0)
  • CHECK plan_suscripcion_moneda_iso: CHECK (moneda ~ '^[a-z]{3}$'::text)
  • CHECK plan_suscripcion_nivel_positivo: CHECK (nivel > 0)
  • CHECK plan_suscripcion_nombre_no_vacio: CHECK (btrim(nombre) <> ''::text)
  • CHECK plan_suscripcion_periodicidad_conocida: CHECK (periodicidad = ANY (ARRAY['dia'::text, 'semana'::text, 'mes'::text, 'ano'::text]))
  • CHECK plan_suscripcion_precio_no_negativo: CHECK (precio_centimos >= 0)
  • CHECK plan_suscripcion_prestaciones_array: CHECK (jsonb_typeof(prestaciones) = 'array'::text)
  • PK plan_suscripcion_pkey: PRIMARY KEY (id)
  • UNIQUE plan_suscripcion_codigo_unique: UNIQUE (codigo)
  • UNIQUE plan_suscripcion_referencia_precio_externa_unique: UNIQUE (referencia_precio_externa)

Índices adicionales: plan_suscripcion_estado_nivel.

sesion

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
usuario_id text no —
expira_en timestamp with time zone no —
creada_en timestamp with time zone no now()

Claves y restricciones:

  • FK sesion_usuario_id_usuario_id_fk: FOREIGN KEY (usuario_id) REFERENCES usuario(id) ON DELETE CASCADE
  • PK sesion_pkey: PRIMARY KEY (id)

Índices adicionales: sesion_usuario.

suscripcion

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
usuario_id text no —
estado text no 'pendiente'::text
inicia_en timestamp with time zone no now()
termina_en timestamp with time zone sí —
proveedor text sí —
referencia_externa text sí —
creada_en timestamp with time zone no now()
actualizada_en timestamp with time zone no now()
plan_id text sí —

Claves y restricciones:

  • CHECK suscripcion_estado_conocido: CHECK (estado = ANY (ARRAY['pendiente'::text, 'activa'::text, 'pausada'::text, 'cancelada'::text, 'vencida'::text]))
  • CHECK suscripcion_periodo_valido: CHECK (termina_en IS NULL OR termina_en > inicia_en)
  • CHECK suscripcion_referencia_completa: CHECK ((proveedor IS NULL) = (referencia_externa IS NULL))
  • FK suscripcion_plan_id_plan_suscripcion_id_fk: FOREIGN KEY (plan_id) REFERENCES plan_suscripcion(id) ON DELETE RESTRICT
  • FK suscripcion_usuario_id_usuario_id_fk: FOREIGN KEY (usuario_id) REFERENCES usuario(id) ON DELETE CASCADE
  • PK suscripcion_pkey: PRIMARY KEY (id)

Índices adicionales: suscripcion_proveedor_referencia, suscripcion_usuario_estado.

usuario

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
email text no —
nombre text sí —
creado_en timestamp with time zone no now()
ultimo_acceso_en timestamp with time zone sí —
rol text no 'persona'::text

Claves y restricciones:

  • CHECK usuario_rol_conocido: CHECK (rol = ANY (ARRAY['persona'::text, 'admin'::text]))
  • PK usuario_pkey: PRIMARY KEY (id)
  • UNIQUE usuario_email_unique: UNIQUE (email)

Interacción y escucha

acceso_cancion_usuario

Columna Tipo PostgreSQL Nulo Valor por defecto
usuario_id text no —
cancion_id text no —
concedido_en timestamp with time zone no now()

Claves y restricciones:

  • FK acceso_cancion_usuario_cancion_id_cancion_id_fk: FOREIGN KEY (cancion_id) REFERENCES cancion(id) ON DELETE CASCADE
  • FK acceso_cancion_usuario_usuario_id_usuario_id_fk: FOREIGN KEY (usuario_id) REFERENCES usuario(id) ON DELETE CASCADE
  • PK acceso_cancion_usuario_usuario_id_cancion_id_pk: PRIMARY KEY (usuario_id, cancion_id)

Índices adicionales: acceso_cancion_usuario_cancion.

favorito

Columna Tipo PostgreSQL Nulo Valor por defecto
usuario_id text no —
marcado_en timestamp with time zone no now()
cancion_id text no —

Claves y restricciones:

  • FK favorito_cancion_id_cancion_id_fk: FOREIGN KEY (cancion_id) REFERENCES cancion(id) ON DELETE CASCADE
  • FK favorito_usuario_id_usuario_id_fk: FOREIGN KEY (usuario_id) REFERENCES usuario(id) ON DELETE CASCADE
  • PK favorito_usuario_id_cancion_id_pk: PRIMARY KEY (usuario_id, cancion_id)

Índices adicionales: idx_favorito_usuario.

lista_reproduccion

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
usuario_id text no —
nombre text no —
creada_en timestamp with time zone no now()
actualizada_en timestamp with time zone no now()

Claves y restricciones:

  • CHECK lista_reproduccion_nombre_valido: CHECK (NULLIF(btrim(nombre), ''::text) IS NOT NULL AND char_length(btrim(nombre)) <= 80)
  • FK lista_reproduccion_usuario_id_usuario_id_fk: FOREIGN KEY (usuario_id) REFERENCES usuario(id) ON DELETE CASCADE
  • PK lista_reproduccion_pkey: PRIMARY KEY (id)

Índices adicionales: lista_reproduccion_nombre_usuario, lista_reproduccion_usuario.

lista_reproduccion_cancion

Columna Tipo PostgreSQL Nulo Valor por defecto
lista_id text no —
cancion_id text no —
orden integer no —
anadida_en timestamp with time zone no now()

Claves y restricciones:

  • CHECK lista_reproduccion_orden_positivo: CHECK (orden >= 0)
  • FK lista_reproduccion_cancion_cancion_id_cancion_id_fk: FOREIGN KEY (cancion_id) REFERENCES cancion(id) ON DELETE CASCADE
  • FK lista_reproduccion_cancion_lista_id_lista_reproduccion_id_fk: FOREIGN KEY (lista_id) REFERENCES lista_reproduccion(id) ON DELETE CASCADE
  • PK lista_reproduccion_cancion_lista_id_cancion_id_pk: PRIMARY KEY (lista_id, cancion_id)

Índices adicionales: lista_reproduccion_cancion_tema, lista_reproduccion_orden_unico.

reproduccion

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
usuario_id text sí —
segundos integer no 0
dia text no —
creado_en timestamp with time zone no now()
cancion_id text no —

Claves y restricciones:

  • FK reproduccion_cancion_id_cancion_id_fk: FOREIGN KEY (cancion_id) REFERENCES cancion(id) ON DELETE CASCADE
  • FK reproduccion_usuario_id_usuario_id_fk: FOREIGN KEY (usuario_id) REFERENCES usuario(id) ON DELETE SET NULL
  • PK reproduccion_pkey: PRIMARY KEY (id)

Índices adicionales: reproduccion_cancion, reproduccion_dia, reproduccion_usuario.

valoracion

Interacción unificada por cuenta y canción. like alimenta el contador público; value conserva la puntuación de 1 a 5 para un uso profesional posterior. Son independientes.

Columna Tipo PostgreSQL Nulo Valor por defecto
usuario_id text no —
value integer sí —
date timestamp with time zone no now()
actualizada_en timestamp with time zone no now()
cancion_id text no —
like boolean no false

Claves y restricciones:

  • CHECK valoracion_de_uno_a_cinco: CHECK (value >= 1 AND value <= 5)
  • FK valoracion_cancion_id_cancion_id_fk: FOREIGN KEY (cancion_id) REFERENCES cancion(id) ON DELETE CASCADE
  • FK valoracion_usuario_id_usuario_id_fk: FOREIGN KEY (usuario_id) REFERENCES usuario(id) ON DELETE CASCADE
  • PK valoracion_usuario_id_cancion_id_pk: PRIMARY KEY (usuario_id, cancion_id)

Índices adicionales: idx_valoracion_cancion.

Comercio

carrito

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
usuario_id text sí —
creado_en timestamp with time zone no now()
actualizado_en timestamp with time zone no now()

Claves y restricciones:

  • FK carrito_usuario_id_usuario_id_fk: FOREIGN KEY (usuario_id) REFERENCES usuario(id) ON DELETE CASCADE
  • PK carrito_pkey: PRIMARY KEY (id)

Índices adicionales: carrito_usuario_unico.

carrito_item

Columna Tipo PostgreSQL Nulo Valor por defecto
carrito_id text no —
anadido_en timestamp with time zone no now()
cancion_id text no —

Claves y restricciones:

  • FK carrito_item_cancion_id_cancion_id_fk: FOREIGN KEY (cancion_id) REFERENCES cancion(id) ON DELETE CASCADE
  • FK carrito_item_carrito_id_carrito_id_fk: FOREIGN KEY (carrito_id) REFERENCES carrito(id) ON DELETE CASCADE
  • PK carrito_item_carrito_id_cancion_id_pk: PRIMARY KEY (carrito_id, cancion_id)

carrito_producto

Columna Tipo PostgreSQL Nulo Valor por defecto
carrito_id text no —
producto_id text no —
cantidad integer no 1
anadido_en timestamp with time zone no now()

Claves y restricciones:

  • CHECK carrito_producto_cantidad_positiva: CHECK (cantidad > 0)
  • FK carrito_producto_carrito_id_carrito_id_fk: FOREIGN KEY (carrito_id) REFERENCES carrito(id) ON DELETE CASCADE
  • FK carrito_producto_producto_id_producto_id_fk: FOREIGN KEY (producto_id) REFERENCES producto(id) ON DELETE CASCADE
  • PK carrito_producto_carrito_id_producto_id_pk: PRIMARY KEY (carrito_id, producto_id)

compra

Permiso materializado de descarga de una canción adquirida.

Columna Tipo PostgreSQL Nulo Valor por defecto
usuario_id text no —
pedido_id text no —
comprado_en timestamp with time zone no now()
cancion_id text no —

Claves y restricciones:

  • FK compra_cancion_id_cancion_id_fk: FOREIGN KEY (cancion_id) REFERENCES cancion(id) ON DELETE RESTRICT
  • FK compra_pedido_id_pedido_id_fk: FOREIGN KEY (pedido_id) REFERENCES pedido(id) ON DELETE CASCADE
  • FK compra_usuario_id_usuario_id_fk: FOREIGN KEY (usuario_id) REFERENCES usuario(id) ON DELETE CASCADE
  • PK compra_usuario_id_cancion_id_pk: PRIMARY KEY (usuario_id, cancion_id)

intencion_pago

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
usuario_id text no —
huella text no —
datos jsonb no —
sesion_pago_id text sí —
estado text no 'abierta'::text
creado_en timestamp with time zone no now()

Claves y restricciones:

  • FK intencion_pago_usuario_id_usuario_id_fk: FOREIGN KEY (usuario_id) REFERENCES usuario(id) ON DELETE CASCADE
  • PK intencion_pago_pkey: PRIMARY KEY (id)
  • UNIQUE intencion_pago_sesion_pago_id_unique: UNIQUE (sesion_pago_id)

Índices adicionales: intencion_pago_abierta.

pedido

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
usuario_id text no —
sesion_pago_id text no —
estado text no 'pendiente'::text
packs integer no —
total_centimos integer no —
moneda text no 'eur'::text
creado_en timestamp with time zone no now()
pagado_en timestamp with time zone sí —

Claves y restricciones:

  • FK pedido_usuario_id_usuario_id_fk: FOREIGN KEY (usuario_id) REFERENCES usuario(id) ON DELETE CASCADE
  • PK pedido_pkey: PRIMARY KEY (id)
  • UNIQUE pedido_sesion_pago_id_unique: UNIQUE (sesion_pago_id)

Índices adicionales: pedido_usuario.

pedido_item

Columna Tipo PostgreSQL Nulo Valor por defecto
pedido_id text no —
titulo text no —
cancion_id text no —

Claves y restricciones:

  • FK pedido_item_cancion_id_cancion_id_fk: FOREIGN KEY (cancion_id) REFERENCES cancion(id) ON DELETE RESTRICT
  • FK pedido_item_pedido_id_pedido_id_fk: FOREIGN KEY (pedido_id) REFERENCES pedido(id) ON DELETE CASCADE
  • PK pedido_item_pedido_id_cancion_id_pk: PRIMARY KEY (pedido_id, cancion_id)

pedido_linea

Columna Tipo PostgreSQL Nulo Valor por defecto
pedido_id text no —
codigo text no —
producto_id text sí —
tipo text no —
nombre text no —
descripcion text sí —
cantidad integer no —
precio_unitario_centimos integer no —
total_centimos integer no —
moneda text no —
modalidad text no —
datos jsonb no '{}'::jsonb

Claves y restricciones:

  • CHECK pedido_linea_cantidad_positiva: CHECK (cantidad > 0)
  • CHECK pedido_linea_datos_objeto: CHECK (jsonb_typeof(datos) = 'object'::text)
  • CHECK pedido_linea_importes_validos: CHECK (precio_unitario_centimos >= 0 AND total_centimos = (precio_unitario_centimos * cantidad))
  • CHECK pedido_linea_modalidad_conocida: CHECK (modalidad = ANY (ARRAY['pago_unico'::text, 'suscripcion'::text]))
  • CHECK pedido_linea_moneda_iso: CHECK (moneda ~ '^[a-z]{3}$'::text)
  • CHECK pedido_linea_tipo_conocido: CHECK (tipo = ANY (ARRAY['pack_canciones'::text, 'libro'::text, 'partitura'::text, 'midi'::text, 'stems'::text, 'servicio'::text, 'suscripcion'::text]))
  • FK pedido_linea_pedido_id_pedido_id_fk: FOREIGN KEY (pedido_id) REFERENCES pedido(id) ON DELETE CASCADE
  • FK pedido_linea_producto_id_producto_id_fk: FOREIGN KEY (producto_id) REFERENCES producto(id) ON DELETE SET NULL
  • PK pedido_linea_pedido_id_codigo_pk: PRIMARY KEY (pedido_id, codigo)

Índices adicionales: pedido_linea_tipo.

producto

Oferta comercial genérica; sus datos específicos se conservan en JSON.

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
codigo text no —
tipo text no —
nombre text no —
descripcion text sí —
estado text no 'borrador'::text
precio_centimos integer no —
moneda text no 'eur'::text
metadatos jsonb no '{}'::jsonb
creado_en timestamp with time zone no now()
actualizado_en timestamp with time zone no now()

Claves y restricciones:

  • CHECK producto_codigo_no_vacio: CHECK (btrim(codigo) <> ''::text)
  • CHECK producto_estado_conocido: CHECK (estado = ANY (ARRAY['borrador'::text, 'disponible'::text, 'retirado'::text]))
  • CHECK producto_metadatos_objeto: CHECK (jsonb_typeof(metadatos) = 'object'::text)
  • CHECK producto_moneda_iso: CHECK (moneda ~ '^[a-z]{3}$'::text)
  • CHECK producto_nombre_no_vacio: CHECK (btrim(nombre) <> ''::text)
  • CHECK producto_precio_no_negativo: CHECK (precio_centimos >= 0)
  • CHECK producto_tipo_conocido: CHECK (tipo = ANY (ARRAY['libro'::text, 'partitura'::text, 'midi'::text, 'stems'::text, 'servicio'::text]))
  • PK producto_pkey: PRIMARY KEY (id)
  • UNIQUE producto_codigo_unique: UNIQUE (codigo)

Índices adicionales: producto_tipo_estado.

Facturación

contador_factura

Columna Tipo PostgreSQL Nulo Valor por defecto
serie text no —
ultimo_numero integer no 0
actualizado_en timestamp with time zone no now()

Claves y restricciones:

  • PK contador_factura_pkey: PRIMARY KEY (serie)

datos_facturacion

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
usuario_id text no —
tipo text no 'particular'::text
nombre text no —
nif text sí —
direccion text sí —
cp text sí —
ciudad text sí —
provincia text sí —
pais_codigo character(2) no —
predeterminada boolean no false
creado_en timestamp with time zone no now()
actualizado_en timestamp with time zone sí —

Claves y restricciones:

  • FK datos_facturacion_pais_codigo_pais_codigo_fk: FOREIGN KEY (pais_codigo) REFERENCES pais(codigo)
  • FK datos_facturacion_usuario_id_usuario_id_fk: FOREIGN KEY (usuario_id) REFERENCES usuario(id) ON DELETE CASCADE
  • PK datos_facturacion_pkey: PRIMARY KEY (id)

Índices adicionales: datos_facturacion_usuario.

descarga

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
usuario_id text no —
bajada_en timestamp with time zone no now()
ip text sí —
agente text sí —
cancion_id text no —

Claves y restricciones:

  • FK descarga_cancion_id_cancion_id_fk: FOREIGN KEY (cancion_id) REFERENCES cancion(id) ON DELETE RESTRICT
  • FK descarga_usuario_id_usuario_id_fk: FOREIGN KEY (usuario_id) REFERENCES usuario(id) ON DELETE CASCADE
  • PK descarga_pkey: PRIMARY KEY (id)

Índices adicionales: descarga_fecha, descarga_usuario.

factura

Documento fiscal inmutable; una rectificativa referencia otra factura.

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
pedido_id text no —
serie text no —
numero integer no —
tipo text no 'normal'::text
rectifica_a_id text sí —
fecha timestamp with time zone no now()
base_centimos integer no —
iva_tipo integer no —
iva_cuota_centimos integer no —
total_centimos integer no —
moneda text no 'eur'::text
datos_congelados jsonb no —
pais_codigo character(2) no —

Claves y restricciones:

  • CHECK factura_rectificativa_apunta: CHECK ((tipo = 'rectificativa'::text) = (rectifica_a_id IS NOT NULL))
  • FK factura_pais_codigo_pais_codigo_fk: FOREIGN KEY (pais_codigo) REFERENCES pais(codigo)
  • FK factura_pedido_id_pedido_id_fk: FOREIGN KEY (pedido_id) REFERENCES pedido(id)
  • FK factura_rectifica_fk: FOREIGN KEY (rectifica_a_id) REFERENCES factura(id)
  • PK factura_pkey: PRIMARY KEY (id)

Índices adicionales: factura_fecha, factura_pedido, factura_serie_numero.

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
slug text no —
titulo text no —
resumen text no ''::text
orden integer no 0
version_vigente_id text sí —

Claves y restricciones:

  • FK documento_legal_version_vigente_fk: FOREIGN KEY (version_vigente_id) REFERENCES version_legal(id) ON DELETE SET NULL
  • PK documento_legal_pkey: PRIMARY KEY (id)
  • UNIQUE documento_legal_slug_unique: UNIQUE (slug)

entrada

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
slug text no —
titulo text no —
fecha text no —
resumen text no ''::text
cuerpo text no ''::text
imagen_id text sí —
estado text no 'borrador'::text
creado_en timestamp with time zone no now()
actualizado_en timestamp with time zone sí —
secciones jsonb no '[]'::jsonb
presentacion jsonb no '{}'::jsonb

Claves y restricciones:

  • CHECK entrada_presentacion_objeto: CHECK (jsonb_typeof(presentacion) = 'object'::text)
  • CHECK entrada_secciones_array: CHECK (jsonb_typeof(secciones) = 'array'::text)
  • FK entrada_imagen_id_medio_id_fk: FOREIGN KEY (imagen_id) REFERENCES medio(id) ON DELETE SET NULL
  • PK entrada_pkey: PRIMARY KEY (id)
  • UNIQUE entrada_slug_unique: UNIQUE (slug)

Índices adicionales: entrada_fecha.

entrada_cancion

Columna Tipo PostgreSQL Nulo Valor por defecto
entrada_id text no —
cancion_id text no —

Claves y restricciones:

  • FK entrada_cancion_cancion_id_cancion_id_fk: FOREIGN KEY (cancion_id) REFERENCES cancion(id) ON DELETE CASCADE
  • FK entrada_cancion_entrada_id_entrada_id_fk: FOREIGN KEY (entrada_id) REFERENCES entrada(id) ON DELETE CASCADE
  • PK entrada_cancion_entrada_id_cancion_id_pk: PRIMARY KEY (entrada_id, cancion_id)

entrada_etiqueta

Columna Tipo PostgreSQL Nulo Valor por defecto
entrada_id text no —
etiqueta_id text no —

Claves y restricciones:

  • FK entrada_etiqueta_entrada_id_entrada_id_fk: FOREIGN KEY (entrada_id) REFERENCES entrada(id) ON DELETE CASCADE
  • FK entrada_etiqueta_etiqueta_id_etiqueta_id_fk: FOREIGN KEY (etiqueta_id) REFERENCES etiqueta(id) ON DELETE CASCADE
  • PK entrada_etiqueta_entrada_id_etiqueta_id_pk: PRIMARY KEY (entrada_id, etiqueta_id)

pagina

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
slug text no —
titulo text no —
descripcion text no ''::text
cuerpo text no ''::text
estado text no 'borrador'::text
creado_en timestamp with time zone no now()
actualizado_en timestamp with time zone sí —

Claves y restricciones:

  • PK pagina_pkey: PRIMARY KEY (id)
  • UNIQUE pagina_slug_unique: UNIQUE (slug)

redireccion

Columna Tipo PostgreSQL Nulo Valor por defecto
desde text no —
hacia text no —
permanente boolean no true
motivo text sí —
creada_en timestamp with time zone no now()

Claves y restricciones:

  • PK redireccion_pkey: PRIMARY KEY (desde)

Índices adicionales: redireccion_hacia.

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
documento_id text no —
cuerpo text no —
vigente_desde timestamp with time zone no now()
resumen_cambios text sí —

Claves y restricciones:

  • FK version_legal_documento_id_documento_legal_id_fk: FOREIGN KEY (documento_id) REFERENCES documento_legal(id) ON DELETE CASCADE
  • PK version_legal_pkey: PRIMARY KEY (id)

Índices adicionales: version_legal_documento.

Taller de letras

acepcion

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
termino_id text no —
numero integer no —
categoria text no —
definicion text no —
ampliacion text sí —
ejemplo text sí —

Claves y restricciones:

  • FK acepcion_termino_id_termino_id_fk: FOREIGN KEY (termino_id) REFERENCES termino(id) ON DELETE CASCADE
  • PK acepcion_pkey: PRIMARY KEY (id)

Índices adicionales: acepcion_numero.

animo

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
codigo text no —
nombre text no —
gems text sí —
valencia integer sí —
energia integer sí —
descripcion text sí —

Claves y restricciones:

  • PK animo_pkey: PRIMARY KEY (id)
  • UNIQUE animo_codigo_unique: UNIQUE (codigo)

cancion_genero

Columna Tipo PostgreSQL Nulo Valor por defecto
cancion_id text no —
genero_id text no —

Claves y restricciones:

  • FK cancion_genero_cancion_id_cancion_id_fk: FOREIGN KEY (cancion_id) REFERENCES cancion(id) ON DELETE CASCADE
  • FK cancion_genero_genero_id_genero_id_fk: FOREIGN KEY (genero_id) REFERENCES genero(id) ON DELETE CASCADE
  • PK cancion_genero_cancion_id_genero_id_pk: PRIMARY KEY (cancion_id, genero_id)

capitulo

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
slug text no —
titulo text no —
tipo text no —
genero_id text sí —
orden integer no 0
resumen text no ''::text
cuerpo text no ''::text
estado text no 'borrador'::text
creado_en timestamp with time zone no now()
actualizado_en timestamp with time zone sí —

Claves y restricciones:

  • FK capitulo_genero_id_genero_id_fk: FOREIGN KEY (genero_id) REFERENCES genero(id) ON DELETE SET NULL
  • PK capitulo_pkey: PRIMARY KEY (id)
  • UNIQUE capitulo_slug_unique: UNIQUE (slug)

Índices adicionales: capitulo_tipo.

capitulo_acepcion

Columna Tipo PostgreSQL Nulo Valor por defecto
capitulo_id text no —
acepcion_id text no —
automatico boolean no true
confirmado boolean no false

Claves y restricciones:

  • FK capitulo_acepcion_acepcion_id_acepcion_id_fk: FOREIGN KEY (acepcion_id) REFERENCES acepcion(id) ON DELETE CASCADE
  • FK capitulo_acepcion_capitulo_id_capitulo_id_fk: FOREIGN KEY (capitulo_id) REFERENCES capitulo(id) ON DELETE CASCADE
  • PK capitulo_acepcion_capitulo_id_acepcion_id_pk: PRIMARY KEY (capitulo_id, acepcion_id)

compas

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
codigo text no —
nombre text sí —
numerador integer no —
denominador integer no —
nota text sí —

Claves y restricciones:

  • PK compas_pkey: PRIMARY KEY (id)
  • UNIQUE compas_codigo_unique: UNIQUE (codigo)

genero

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
slug text no —
nombre text no —
resumen text no ''::text
origen_lugar text sí —
origen_ano integer sí —
tempo text sí —
rima text sí —
metrica_nota text sí —
compas_nota text sí —
rasgos text[] sí —
estado text no 'borrador'::text
origen text no 'manual'::text
modelo_origen text sí —
importado_en timestamp with time zone sí —
revisado_por text sí —
revisado_en timestamp with time zone sí —
creado_en timestamp with time zone no now()
actualizado_en timestamp with time zone sí —

Claves y restricciones:

  • PK genero_pkey: PRIMARY KEY (id)
  • UNIQUE genero_slug_unique: UNIQUE (slug)

Índices adicionales: genero_nombre, genero_origen_ano.

genero_alias

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
genero_id text no —
nombre text no —
pais_codigo character(2) sí —
nota text sí —

Claves y restricciones:

  • FK genero_alias_genero_id_genero_id_fk: FOREIGN KEY (genero_id) REFERENCES genero(id) ON DELETE CASCADE
  • FK genero_alias_pais_codigo_pais_codigo_fk: FOREIGN KEY (pais_codigo) REFERENCES pais(codigo)
  • PK genero_alias_pkey: PRIMARY KEY (id)

Índices adicionales: genero_alias_nombre.

genero_compas

Columna Tipo PostgreSQL Nulo Valor por defecto
genero_id text no —
compas_id text no —
principal boolean no false
nota text sí —

Claves y restricciones:

  • FK genero_compas_compas_id_compas_id_fk: FOREIGN KEY (compas_id) REFERENCES compas(id)
  • FK genero_compas_genero_id_genero_id_fk: FOREIGN KEY (genero_id) REFERENCES genero(id) ON DELETE CASCADE
  • PK genero_compas_genero_id_compas_id_pk: PRIMARY KEY (genero_id, compas_id)

genero_instrumento

Columna Tipo PostgreSQL Nulo Valor por defecto
genero_id text no —
instrumento_id text no —
importancia text no 'habitual'::text
papel text sí —
orden integer no 0

Claves y restricciones:

  • FK genero_instrumento_genero_id_genero_id_fk: FOREIGN KEY (genero_id) REFERENCES genero(id) ON DELETE CASCADE
  • FK genero_instrumento_instrumento_id_instrumento_id_fk: FOREIGN KEY (instrumento_id) REFERENCES instrumento(id)
  • PK genero_instrumento_genero_id_instrumento_id_pk: PRIMARY KEY (genero_id, instrumento_id)

genero_metro

Columna Tipo PostgreSQL Nulo Valor por defecto
genero_id text no —
metro_id text no —
predominante boolean no false

Claves y restricciones:

  • FK genero_metro_genero_id_genero_id_fk: FOREIGN KEY (genero_id) REFERENCES genero(id) ON DELETE CASCADE
  • FK genero_metro_metro_id_metro_id_fk: FOREIGN KEY (metro_id) REFERENCES metro(id)
  • PK genero_metro_genero_id_metro_id_pk: PRIMARY KEY (genero_id, metro_id)

genero_origen

Columna Tipo PostgreSQL Nulo Valor por defecto
genero_id text no —
origen_id text no —
tipo text no —
principal boolean no false
nota text sí —
orden integer no 0

Claves y restricciones:

  • CHECK genero_origen_no_reflexivo: CHECK (genero_id <> origen_id)
  • FK genero_origen_genero_id_genero_id_fk: FOREIGN KEY (genero_id) REFERENCES genero(id) ON DELETE CASCADE
  • FK genero_origen_origen_id_genero_id_fk: FOREIGN KEY (origen_id) REFERENCES genero(id) ON DELETE CASCADE
  • PK genero_origen_genero_id_origen_id_tipo_pk: PRIMARY KEY (genero_id, origen_id, tipo)

Índices adicionales: genero_origen_origen, genero_origen_principal_unico.

Disparadores:

  • genero_sin_ciclos_trg: CREATE TRIGGER genero_sin_ciclos_trg BEFORE INSERT OR UPDATE ON genero_origen FOR EACH ROW EXECUTE FUNCTION genero_sin_ciclos()

genero_pais

Columna Tipo PostgreSQL Nulo Valor por defecto
genero_id text no —
pais_codigo character(2) no —
relacion text no 'adoptado'::text
nota text sí —

Claves y restricciones:

  • FK genero_pais_genero_id_genero_id_fk: FOREIGN KEY (genero_id) REFERENCES genero(id) ON DELETE CASCADE
  • FK genero_pais_pais_codigo_pais_codigo_fk: FOREIGN KEY (pais_codigo) REFERENCES pais(codigo)
  • PK genero_pais_genero_id_pais_codigo_pk: PRIMARY KEY (genero_id, pais_codigo)

genero_persona

Columna Tipo PostgreSQL Nulo Valor por defecto
genero_id text no —
persona_id text no —
papel text no —
nota text sí —
orden integer no 0

Claves y restricciones:

  • FK genero_persona_genero_id_genero_id_fk: FOREIGN KEY (genero_id) REFERENCES genero(id) ON DELETE CASCADE
  • FK genero_persona_persona_id_persona_id_fk: FOREIGN KEY (persona_id) REFERENCES persona(id)
  • PK genero_persona_genero_id_persona_id_papel_pk: PRIMARY KEY (genero_id, persona_id, papel)

genero_seccion

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
genero_id text no —
clave text no —
titulo text no —
cuerpo text no —
orden integer no 0

Claves y restricciones:

  • FK genero_seccion_genero_id_genero_id_fk: FOREIGN KEY (genero_id) REFERENCES genero(id) ON DELETE CASCADE
  • PK genero_seccion_pkey: PRIMARY KEY (id)

Índices adicionales: genero_seccion_clave.

genero_tema

Columna Tipo PostgreSQL Nulo Valor por defecto
genero_id text no —
etiqueta_id text no —
orden integer no 0

Claves y restricciones:

  • FK genero_tema_etiqueta_id_etiqueta_id_fk: FOREIGN KEY (etiqueta_id) REFERENCES etiqueta(id) ON DELETE CASCADE
  • FK genero_tema_genero_id_genero_id_fk: FOREIGN KEY (genero_id) REFERENCES genero(id) ON DELETE CASCADE
  • PK genero_tema_genero_id_etiqueta_id_pk: PRIMARY KEY (genero_id, etiqueta_id)

instrumento

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
slug text no —
nombre text no —
familia text no —
subfamilia text sí —
pais_codigo character(2) sí —
descripcion text sí —
afinacion text sí —
imagen_id text sí —
muestra_id text sí —
estado text no 'borrador'::text
origen text no 'manual'::text
modelo_origen text sí —
importado_en timestamp with time zone sí —
revisado_por text sí —
revisado_en timestamp with time zone sí —

Claves y restricciones:

  • FK instrumento_imagen_id_medio_id_fk: FOREIGN KEY (imagen_id) REFERENCES medio(id) ON DELETE SET NULL
  • FK instrumento_muestra_id_medio_id_fk: FOREIGN KEY (muestra_id) REFERENCES medio(id) ON DELETE SET NULL
  • FK instrumento_pais_codigo_pais_codigo_fk: FOREIGN KEY (pais_codigo) REFERENCES pais(codigo)
  • PK instrumento_pkey: PRIMARY KEY (id)
  • UNIQUE instrumento_slug_unique: UNIQUE (slug)

Índices adicionales: instrumento_familia.

metro

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
codigo text no —
nombre text no —
silabas integer sí —
termino_id text sí —
nota text sí —

Claves y restricciones:

  • FK metro_termino_fk: FOREIGN KEY (termino_id) REFERENCES termino(id) ON DELETE SET NULL
  • PK metro_pkey: PRIMARY KEY (id)
  • UNIQUE metro_codigo_unique: UNIQUE (codigo)

modelo_ia

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
codigo text no —
familia text no —
version text no —
nombre text no —
vigente boolean no true
nota_sintaxis text sí —
esquema jsonb sí —

Claves y restricciones:

  • PK modelo_ia_pkey: PRIMARY KEY (id)
  • UNIQUE modelo_ia_codigo_unique: UNIQUE (codigo)

Índices adicionales: modelo_ia_familia.

obra_referencia

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
slug text no —
titulo text no —
ano integer sí —
genero_id text sí —
nota text sí —
cita text sí —
estado text no 'borrador'::text
origen text no 'manual'::text
modelo_origen text sí —
importado_en timestamp with time zone sí —
revisado_por text sí —
revisado_en timestamp with time zone sí —

Claves y restricciones:

  • FK obra_referencia_genero_id_genero_id_fk: FOREIGN KEY (genero_id) REFERENCES genero(id) ON DELETE SET NULL
  • PK obra_referencia_pkey: PRIMARY KEY (id)
  • UNIQUE obra_referencia_slug_unique: UNIQUE (slug)

Índices adicionales: obra_referencia_genero.

obra_referencia_enlace

Columna Tipo PostgreSQL Nulo Valor por defecto
obra_id text no —
plataforma text no —
url text no —

Claves y restricciones:

  • FK obra_referencia_enlace_obra_id_obra_referencia_id_fk: FOREIGN KEY (obra_id) REFERENCES obra_referencia(id) ON DELETE CASCADE
  • PK obra_referencia_enlace_obra_id_plataforma_pk: PRIMARY KEY (obra_id, plataforma)

obra_referencia_persona

Columna Tipo PostgreSQL Nulo Valor por defecto
obra_id text no —
persona_id text no —
papel text no —
orden integer no 0

Claves y restricciones:

  • FK obra_referencia_persona_obra_id_obra_referencia_id_fk: FOREIGN KEY (obra_id) REFERENCES obra_referencia(id) ON DELETE CASCADE
  • FK obra_referencia_persona_persona_id_persona_id_fk: FOREIGN KEY (persona_id) REFERENCES persona(id)
  • PK obra_referencia_persona_obra_id_persona_id_papel_pk: PRIMARY KEY (obra_id, persona_id, papel)

prompt

Prompt probado para un modelo de IA, género, ánimo y, opcionalmente, una canción resultante.

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
modelo_ia_id text no —
genero_id text sí —
animo_id text sí —
idioma text no 'en'::text
contenido jsonb no —
notas text sí —
valoracion integer sí —
ejemplo_id text sí —
preferido boolean no false
visibilidad text no 'privado'::text
estado text no 'borrador'::text
creado_en timestamp with time zone no now()
actualizado_en timestamp with time zone sí —
cancion_id text sí —

Claves y restricciones:

  • FK prompt_animo_id_animo_id_fk: FOREIGN KEY (animo_id) REFERENCES animo(id)
  • FK prompt_cancion_id_cancion_id_fk: FOREIGN KEY (cancion_id) REFERENCES cancion(id) ON DELETE CASCADE
  • FK prompt_ejemplo_id_medio_id_fk: FOREIGN KEY (ejemplo_id) REFERENCES medio(id) ON DELETE SET NULL
  • FK prompt_genero_id_genero_id_fk: FOREIGN KEY (genero_id) REFERENCES genero(id) ON DELETE CASCADE
  • FK prompt_modelo_ia_id_modelo_ia_id_fk: FOREIGN KEY (modelo_ia_id) REFERENCES modelo_ia(id)
  • PK prompt_pkey: PRIMARY KEY (id)

Índices adicionales: prompt_animo, prompt_cancion, prompt_genero, prompt_modelo.

termino

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
slug text no —
termino text no —

Claves y restricciones:

  • PK termino_pkey: PRIMARY KEY (id)
  • UNIQUE termino_slug_unique: UNIQUE (slug)
  • UNIQUE termino_termino_unique: UNIQUE (termino)

termino_variante

Columna Tipo PostgreSQL Nulo Valor por defecto
termino_id text no —
forma text no —

Claves y restricciones:

  • FK termino_variante_termino_id_termino_id_fk: FOREIGN KEY (termino_id) REFERENCES termino(id) ON DELETE CASCADE
  • PK termino_variante_termino_id_forma_pk: PRIMARY KEY (termino_id, forma)

verso

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
genero_id text no —
titulo text no —
ilustra text no —
fuente text no 'ejemplo'::text
analisis text sí —
medida text sí —
rima text sí —
obra_id text sí —
cancion_id text sí —
orden integer no 0
estado text no 'borrador'::text
origen text no 'manual'::text
modelo_origen text sí —
importado_en timestamp with time zone sí —
revisado_por text sí —
revisado_en timestamp with time zone sí —

Claves y restricciones:

  • CHECK verso_cita_acredita: CHECK (fuente <> 'cita'::text OR obra_id IS NOT NULL)
  • CHECK verso_propio_apunta: CHECK (fuente <> 'propio'::text OR cancion_id IS NOT NULL)
  • FK verso_cancion_id_cancion_id_fk: FOREIGN KEY (cancion_id) REFERENCES cancion(id) ON DELETE SET NULL
  • FK verso_genero_id_genero_id_fk: FOREIGN KEY (genero_id) REFERENCES genero(id) ON DELETE CASCADE
  • FK verso_obra_id_obra_referencia_id_fk: FOREIGN KEY (obra_id) REFERENCES obra_referencia(id) ON DELETE SET NULL
  • PK verso_pkey: PRIMARY KEY (id)

Índices adicionales: verso_genero.

verso_linea

Columna Tipo PostgreSQL Nulo Valor por defecto
verso_id text no —
numero integer no —
texto text no —
silabas text[] sí —
tonicas integer[] sí —
medida integer sí —
rima text sí —

Claves y restricciones:

  • CHECK verso_linea_medida_creible: CHECK (medida IS NULL OR medida >= 1 AND medida <= 20)
  • FK verso_linea_verso_id_verso_id_fk: FOREIGN KEY (verso_id) REFERENCES verso(id) ON DELETE CASCADE
  • PK verso_linea_verso_id_numero_pk: PRIMARY KEY (verso_id, numero)

Jukebox

jukebox

Programación independiente y versionada de una cuenta.

Columna Tipo PostgreSQL Nulo Valor por defecto
id text no —
usuario_id text no —
numero integer no —
zona_horaria text no —
franjas jsonb no —
revision integer no 1
actualizada_en timestamp with time zone no now()
nombre text no ''::text
descripcion text no ''::text

Claves y restricciones:

  • CHECK jukebox_franjas_array: CHECK (jsonb_typeof(franjas) = 'array'::text)
  • CHECK jukebox_revision_positiva: CHECK (revision > 0)
  • FK jukebox_usuario_id_fkey: FOREIGN KEY (usuario_id) REFERENCES usuario(id) ON DELETE CASCADE
  • PK jukebox_pkey: PRIMARY KEY (id)

Índices adicionales: jukebox_usuario_numero.

programacion_jukebox

Programación histórica por cuenta, conservada por compatibilidad con el modelo anterior de un único Jukebox.

Columna Tipo PostgreSQL Nulo Valor por defecto
usuario_id text no —
zona_horaria text no 'Europe/Madrid'::text
franjas jsonb no '[]'::jsonb
revision integer no 1
actualizada_en timestamp with time zone no CURRENT_TIMESTAMP

Claves y restricciones:

  • CHECK programacion_jukebox_franjas_array: CHECK (jsonb_typeof(franjas) = 'array'::text)
  • CHECK programacion_jukebox_revision_positiva: CHECK (revision > 0)
  • FK programacion_jukebox_usuario_id_fkey: FOREIGN KEY (usuario_id) REFERENCES usuario(id) ON DELETE CASCADE
  • PK programacion_jukebox_pkey: PRIMARY KEY (usuario_id)

Vistas

cancion_principal

Columna Tipo PostgreSQL Nulo
id text sí
slug text sí
titulo text sí
version_de_id text sí
version_nombre text sí
version_orden integer sí
estilo_id text sí
album_id text sí
numero integer sí
duracion_s integer sí
ano integer sí
letra text sí
idioma text sí
inspiracion text sí
tiene_letra boolean sí
destacada boolean sí
portada_id text sí
ala_venta boolean sí
estado text sí
creado_en timestamp with time zone sí
actualizado_en timestamp with time zone sí
es_version boolean sí
padre_no_es_version boolean sí
version_nota text sí
codigo text sí
version_codigo text sí
formacion_vocal formacion_vocal sí
acceso acceso_cancion sí

Definición:

 SELECT id,
    slug,
    titulo,
    version_de_id,
    version_nombre,
    version_orden,
    estilo_id,
    album_id,
    numero,
    duracion_s,
    ano,
    letra,
    idioma,
    inspiracion,
    tiene_letra,
    destacada,
    portada_id,
    ala_venta,
    estado,
    creado_en,
    actualizado_en,
    es_version,
    padre_no_es_version,
    version_nota,
    codigo,
    version_codigo,
    formacion_vocal,
    acceso
   FROM cancion
  WHERE version_de_id IS NULL;

Enumeraciones PostgreSQL

  • acceso_cancion: ninguna, publico, exclusiva, privada.
  • formacion_vocal: hombre, mujer, dueto-femenino, dueto-masculino, dueto-mixto, multiple-mixto, multiple-femenino, multiple-masculino.

Los demás vocabularios cerrados se almacenan como text y aparecen en las restricciones CHECK de cada tabla.

Funciones de base de datos

Función Argumentos Resultado Lenguaje
autoria_suma_cien — trigger plpgsql
comprobar_grabacion_publicable identificador text void plpgsql
genero_sin_ciclos — trigger plpgsql
guardar_invariantes_publicacion — trigger plpgsql

Las funciones asociadas a disparadores se localizan además bajo la tabla afectada, en el apartado “Disparadores”.

Secuencias

Secuencia Tipo Inicio Incremento Ciclo
artista_codigo_seq bigint 1 1 no
artista_virtual_codigo_seq bigint 1 1 no
cancion_codigo_seq bigint 1 1 no

Mantenimiento

Este documento se regenera desde la base aplicada:

node --env-file=.env scripts/db/documentar-modelo.mjs

Después de una migración, regénéralo y revisa también los contratos JSON si cambió su estructura TypeScript.

Powered by TurnKey Linux.