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.
1928 lines
105 KiB
1928 lines
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
|
|
|
|
```mermaid
|
|
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
|
|
|
|
```mermaid
|
|
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`.
|
|
|
|
## Editorial y legal
|
|
|
|
### `documento_legal`
|
|
|
|
| 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`.
|
|
|
|
### `version_legal`
|
|
|
|
| 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:
|
|
|
|
```sql
|
|
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:
|
|
|
|
```powershell
|
|
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.
|