feat: add semantic_id field to post-related data structures and database migrations

This commit is contained in:
SquidSpirit 2025-10-12 13:36:12 +08:00
parent bb9a6bbb12
commit 6efd941395
11 changed files with 45 additions and 9 deletions

View File

@ -6,6 +6,7 @@ use crate::domain::entity::{post::Post, post_info::PostInfo};
#[derive(Deserialize, ToSchema, Clone)] #[derive(Deserialize, ToSchema, Clone)]
pub struct CreatePostRequestDto { pub struct CreatePostRequestDto {
pub semantic_id: String,
pub title: String, pub title: String,
pub description: String, pub description: String,
pub content: String, pub content: String,
@ -24,6 +25,7 @@ impl CreatePostRequestDto {
id: -1, id: -1,
info: PostInfo { info: PostInfo {
id: -1, id: -1,
semantic_id: self.semantic_id,
title: self.title, title: self.title,
description: self.description, description: self.description,
preview_image_url: self.preview_image_url, preview_image_url: self.preview_image_url,

View File

@ -8,6 +8,7 @@ use super::label_response_dto::LabelResponseDto;
#[derive(Serialize, ToSchema)] #[derive(Serialize, ToSchema)]
pub struct PostInfoResponseDto { pub struct PostInfoResponseDto {
pub id: i32, pub id: i32,
pub semantic_id: String,
pub title: String, pub title: String,
pub description: String, pub description: String,
pub labels: Vec<LabelResponseDto>, pub labels: Vec<LabelResponseDto>,
@ -23,6 +24,7 @@ impl From<PostInfo> for PostInfoResponseDto {
fn from(entity: PostInfo) -> Self { fn from(entity: PostInfo) -> Self {
Self { Self {
id: entity.id, id: entity.id,
semantic_id: entity.semantic_id,
title: entity.title, title: entity.title,
description: entity.description, description: entity.description,
preview_image_url: entity.preview_image_url, preview_image_url: entity.preview_image_url,

View File

@ -6,6 +6,7 @@ use crate::domain::entity::{post::Post, post_info::PostInfo};
#[derive(Deserialize, ToSchema, Clone)] #[derive(Deserialize, ToSchema, Clone)]
pub struct UpdatePostRequestDto { pub struct UpdatePostRequestDto {
pub semantic_id: String,
pub title: String, pub title: String,
pub description: String, pub description: String,
pub content: String, pub content: String,
@ -24,6 +25,7 @@ impl UpdatePostRequestDto {
id, id,
info: PostInfo { info: PostInfo {
id, id,
semantic_id: self.semantic_id,
title: self.title, title: self.title,
description: self.description, description: self.description,
preview_image_url: self.preview_image_url, preview_image_url: self.preview_image_url,

View File

@ -4,6 +4,7 @@ use crate::{adapter::gateway::label_db_mapper::LabelMapper, domain::entity::post
pub struct PostInfoMapper { pub struct PostInfoMapper {
pub id: i32, pub id: i32,
pub semantic_id: String,
pub title: String, pub title: String,
pub description: String, pub description: String,
pub preview_image_url: String, pub preview_image_url: String,
@ -15,13 +16,18 @@ impl PostInfoMapper {
pub fn into_entity(self) -> PostInfo { pub fn into_entity(self) -> PostInfo {
PostInfo { PostInfo {
id: self.id, id: self.id,
semantic_id: self.semantic_id,
title: self.title.clone(), title: self.title.clone(),
description: self.description.clone(), description: self.description.clone(),
preview_image_url: self.preview_image_url.clone(), preview_image_url: self.preview_image_url.clone(),
published_time: self published_time: self
.published_time .published_time
.map(|dt| DateTime::<Utc>::from_naive_utc_and_offset(dt, Utc)), .map(|dt| DateTime::<Utc>::from_naive_utc_and_offset(dt, Utc)),
labels: self.labels.into_iter().map(LabelMapper::into_entity).collect(), labels: self
.labels
.into_iter()
.map(LabelMapper::into_entity)
.collect(),
} }
} }
} }

View File

@ -44,6 +44,7 @@ impl PostRepository for PostRepositoryImpl {
async fn create_post(&self, post: Post, label_ids: &[i32]) -> Result<i32, PostError> { async fn create_post(&self, post: Post, label_ids: &[i32]) -> Result<i32, PostError> {
let info_mapper = PostInfoMapper { let info_mapper = PostInfoMapper {
id: post.info.id, id: post.info.id,
semantic_id: post.info.semantic_id,
title: post.info.title, title: post.info.title,
description: post.info.description, description: post.info.description,
preview_image_url: post.info.preview_image_url, preview_image_url: post.info.preview_image_url,
@ -65,6 +66,7 @@ impl PostRepository for PostRepositoryImpl {
async fn update_post(&self, post: Post, label_ids: &[i32]) -> Result<(), PostError> { async fn update_post(&self, post: Post, label_ids: &[i32]) -> Result<(), PostError> {
let info_mapper = PostInfoMapper { let info_mapper = PostInfoMapper {
id: post.info.id, id: post.info.id,
semantic_id: post.info.semantic_id,
title: post.info.title, title: post.info.title,
description: post.info.description, description: post.info.description,
preview_image_url: post.info.preview_image_url, preview_image_url: post.info.preview_image_url,

View File

@ -4,6 +4,7 @@ use super::label::Label;
pub struct PostInfo { pub struct PostInfo {
pub id: i32, pub id: i32,
pub semantic_id: String,
pub title: String, pub title: String,
pub description: String, pub description: String,
pub preview_image_url: String, pub preview_image_url: String,

View File

@ -37,6 +37,7 @@ impl PostDbService for PostDbServiceImpl {
r#" r#"
SELECT SELECT
p.id AS post_id, p.id AS post_id,
p.semantic_id,
p.title, p.title,
p.description, p.description,
p.preview_image_url, p.preview_image_url,
@ -74,6 +75,7 @@ impl PostDbService for PostDbServiceImpl {
.entry(record.post_id) .entry(record.post_id)
.or_insert_with(|| PostInfoMapper { .or_insert_with(|| PostInfoMapper {
id: record.post_id, id: record.post_id,
semantic_id: record.semantic_id.clone(),
title: record.title.clone(), title: record.title.clone(),
description: record.description.clone(), description: record.description.clone(),
preview_image_url: record.preview_image_url.clone(), preview_image_url: record.preview_image_url.clone(),
@ -111,6 +113,7 @@ impl PostDbService for PostDbServiceImpl {
r#" r#"
SELECT SELECT
p.id AS post_id, p.id AS post_id,
p.semantic_id,
p.title, p.title,
p.description, p.description,
p.preview_image_url, p.preview_image_url,
@ -151,6 +154,7 @@ impl PostDbService for PostDbServiceImpl {
.or_insert_with(|| PostMapper { .or_insert_with(|| PostMapper {
id: record.post_id, id: record.post_id,
info: PostInfoMapper { info: PostInfoMapper {
semantic_id: record.semantic_id.clone(),
id: record.post_id, id: record.post_id,
title: record.title.clone(), title: record.title.clone(),
description: record.description.clone(), description: record.description.clone(),
@ -194,10 +198,11 @@ impl PostDbService for PostDbServiceImpl {
let post_id = sqlx::query_scalar!( let post_id = sqlx::query_scalar!(
r#" r#"
INSERT INTO post ( INSERT INTO post (
title, description, preview_image_url, content, published_time semantic_id, title, description, preview_image_url, content, published_time
) VALUES ($1, $2, $3, $4, $5) ) VALUES ($1, $2, $3, $4, $5, $6)
RETURNING id RETURNING id
"#, "#,
post.info.semantic_id,
post.info.title, post.info.title,
post.info.description, post.info.description,
post.info.preview_image_url, post.info.preview_image_url,
@ -243,13 +248,15 @@ impl PostDbService for PostDbServiceImpl {
r#" r#"
UPDATE post UPDATE post
SET SET
title = $1, semantic_id = $1,
description = $2, title = $2,
preview_image_url = $3, description = $3,
content = $4, preview_image_url = $4,
published_time = $5 content = $5,
WHERE id = $6 published_time = $6
WHERE id = $7
"#, "#,
post.info.semantic_id,
post.info.title, post.info.title,
post.info.description, post.info.description,
post.info.preview_image_url, post.info.preview_image_url,

View File

@ -3,6 +3,7 @@ use chrono::NaiveDateTime;
#[derive(sqlx::FromRow)] #[derive(sqlx::FromRow)]
pub struct PostInfoWithLabelRecord { pub struct PostInfoWithLabelRecord {
pub post_id: i32, pub post_id: i32,
pub semantic_id: String,
pub title: String, pub title: String,
pub description: String, pub description: String,
pub preview_image_url: String, pub preview_image_url: String,

View File

@ -3,6 +3,7 @@ use chrono::NaiveDateTime;
#[derive(sqlx::FromRow)] #[derive(sqlx::FromRow)]
pub struct PostWithLabelRecord { pub struct PostWithLabelRecord {
pub post_id: i32, pub post_id: i32,
pub semantic_id: String,
pub title: String, pub title: String,
pub description: String, pub description: String,
pub preview_image_url: String, pub preview_image_url: String,

View File

@ -0,0 +1,5 @@
-- Drop the unique index on semantic_id
DROP INDEX IF EXISTS idx_post_semantic_id;
-- Remove the semantic_id column from post table
ALTER TABLE post DROP COLUMN IF EXISTS "semantic_id";

View File

@ -0,0 +1,7 @@
ALTER TABLE post ADD COLUMN "semantic_id" VARCHAR(100) NOT NULL DEFAULT '';
-- Update existing records to use their id as semantic_id
UPDATE post SET semantic_id = id::VARCHAR WHERE semantic_id = '';
-- Create unique index on semantic_id
CREATE UNIQUE INDEX idx_post_semantic_id ON post (semantic_id);