This commit is contained in:
owenchen 2026-06-04 16:02:01 +08:00
parent cb3fdca3e1
commit ce37c632d3
6 changed files with 89 additions and 67 deletions

View File

@ -227,18 +227,13 @@ namespace CloudBuilder.AI.Service
if (aiChapters != null && aiChapters.Length > 0) if (aiChapters != null && aiChapters.Length > 0)
{ {
foreach (AiChapterEntity aiChapter in aiChapters) AiSentenceEntity[] aiSentences = repositoryAiSentenceEntity.DetachedEntities.Where(x => x.BookId == data.AiBook.BookId).ToArray();
{
AiSentenceEntity[] aiSentences = repositoryAiSentenceEntity.DetachedEntities.Where(x => x.ChapterId == aiChapter.ChapterId).ToArray();
if (aiSentences != null && aiSentences.Length > 0)
{
if (aiSentences != null && aiSentences.Length > 0) if (aiSentences != null && aiSentences.Length > 0)
{ {
repositoryAiSentenceEntity.DeleteNow(aiSentences); repositoryAiSentenceEntity.DeleteNow(aiSentences);
} }
}
}
repositoryAiChapterEntity.DeleteNow(aiChapters); repositoryAiChapterEntity.DeleteNow(aiChapters);
} }

View File

@ -13,12 +13,8 @@ namespace CloudBuilder.AI.Entity
{ {
} }
[Column("content")]
public string Content { get; set; }
[Column("status")] [Column("status")]
public string? Status { get; set; } public string? Status { get; set; }
[Column("paragraph_id")]
public long ParagraphId { get; set; }
[Column("sentence_index")] [Column("sentence_index")]
public int SentenceIndex { get; set; } public int SentenceIndex { get; set; }
[Column("dialogue_indc")] [Column("dialogue_indc")]
@ -29,53 +25,61 @@ namespace CloudBuilder.AI.Entity
public string? Gender { get; set; } public string? Gender { get; set; }
[Column("sentiment")] [Column("sentiment")]
public string? Sentiment { get; set; } public string? Sentiment { get; set; }
[Column("created_datetime")] [Column("content")]
public DateTime CreatedDatetime { get; set; } public string Content { get; set; }
[Column("created_by")]
public string CreatedBy { get; set; }
[Column("updated_datetime")] [Column("updated_datetime")]
public DateTime UpdatedDatetime { get; set; } public DateTime UpdatedDatetime { get; set; }
[Column("updated_by")] [Column("updated_by")]
public string UpdatedBy { get; set; } public string UpdatedBy { get; set; }
[Column("book_id")]
public string BookId { get; set; }
[Key] [Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column("sentence_id")] [Column("sentence_id")]
public long SentenceId { get; set; } public long SentenceId { get; set; }
[Column("chapter_id")] [Column("chapter_id")]
public int ChapterId { get; set; } public int ChapterId { get; set; }
[Column("paragraph_id")]
public long ParagraphId { get; set; }
[Column("created_datetime")]
public DateTime CreatedDatetime { get; set; }
[Column("created_by")]
public string CreatedBy { get; set; }
public const string CONTENT = "Content";
public const string STATUS = "Status"; public const string STATUS = "Status";
public const string PARAGRAPH_ID = "ParagraphId";
public const string SENTENCE_INDEX = "SentenceIndex"; public const string SENTENCE_INDEX = "SentenceIndex";
public const string DIALOGUE_INDC = "DialogueIndc"; public const string DIALOGUE_INDC = "DialogueIndc";
public const string PERSON_NAME = "PersonName"; public const string PERSON_NAME = "PersonName";
public const string GENDER = "Gender"; public const string GENDER = "Gender";
public const string SENTIMENT = "Sentiment"; public const string SENTIMENT = "Sentiment";
public const string CREATED_DATETIME = "CreatedDatetime"; public const string CONTENT = "Content";
public const string CREATED_BY = "CreatedBy";
public const string UPDATED_DATETIME = "UpdatedDatetime"; public const string UPDATED_DATETIME = "UpdatedDatetime";
public const string UPDATED_BY = "UpdatedBy"; public const string UPDATED_BY = "UpdatedBy";
public const string BOOK_ID = "BookId";
public const string SENTENCE_ID = "SentenceId"; public const string SENTENCE_ID = "SentenceId";
public const string CHAPTER_ID = "ChapterId"; public const string CHAPTER_ID = "ChapterId";
public const string PARAGRAPH_ID = "ParagraphId";
public const string CREATED_DATETIME = "CreatedDatetime";
public const string CREATED_BY = "CreatedBy";
public const string DB_NAME_AI_SENTENCE = "ai_sentence"; public const string DB_NAME_AI_SENTENCE = "ai_sentence";
public const string DB_NAME_FIELDS="content,status,paragraph_id,sentence_index,dialogue_indc,person_name,gender,sentiment,created_datetime,created_by,updated_datetime,updated_by,sentence_id,chapter_id"; public const string DB_NAME_FIELDS="status,sentence_index,dialogue_indc,person_name,gender,sentiment,content,updated_datetime,updated_by,book_id,sentence_id,chapter_id,paragraph_id,created_datetime,created_by";
public const string DB_CONTENT = "content";
public const string DB_STATUS = "status"; public const string DB_STATUS = "status";
public const string DB_PARAGRAPH_ID = "paragraph_id";
public const string DB_SENTENCE_INDEX = "sentence_index"; public const string DB_SENTENCE_INDEX = "sentence_index";
public const string DB_DIALOGUE_INDC = "dialogue_indc"; public const string DB_DIALOGUE_INDC = "dialogue_indc";
public const string DB_PERSON_NAME = "person_name"; public const string DB_PERSON_NAME = "person_name";
public const string DB_GENDER = "gender"; public const string DB_GENDER = "gender";
public const string DB_SENTIMENT = "sentiment"; public const string DB_SENTIMENT = "sentiment";
public const string DB_CREATED_DATETIME = "created_datetime"; public const string DB_CONTENT = "content";
public const string DB_CREATED_BY = "created_by";
public const string DB_UPDATED_DATETIME = "updated_datetime"; public const string DB_UPDATED_DATETIME = "updated_datetime";
public const string DB_UPDATED_BY = "updated_by"; public const string DB_UPDATED_BY = "updated_by";
public const string DB_BOOK_ID = "book_id";
public const string DB_SENTENCE_ID = "sentence_id"; public const string DB_SENTENCE_ID = "sentence_id";
public const string DB_CHAPTER_ID = "chapter_id"; public const string DB_CHAPTER_ID = "chapter_id";
public const string DB_PARAGRAPH_ID = "paragraph_id";
public const string DB_CREATED_DATETIME = "created_datetime";
public const string DB_CREATED_BY = "created_by";
public static AiSentenceEntity GetInstance() public static AiSentenceEntity GetInstance()
@ -95,27 +99,28 @@ namespace CloudBuilder.AI.Entity
throw new NullReferenceException("Source entity is null."); throw new NullReferenceException("Source entity is null.");
} }
this.Content = source.Content;
this.Status = source.Status; this.Status = source.Status;
this.ParagraphId = source.ParagraphId;
this.SentenceIndex = source.SentenceIndex; this.SentenceIndex = source.SentenceIndex;
this.DialogueIndc = source.DialogueIndc; this.DialogueIndc = source.DialogueIndc;
this.PersonName = source.PersonName; this.PersonName = source.PersonName;
this.Gender = source.Gender; this.Gender = source.Gender;
this.Sentiment = source.Sentiment; this.Sentiment = source.Sentiment;
this.CreatedDatetime = source.CreatedDatetime; this.Content = source.Content;
this.CreatedBy = source.CreatedBy;
this.UpdatedDatetime = source.UpdatedDatetime; this.UpdatedDatetime = source.UpdatedDatetime;
this.UpdatedBy = source.UpdatedBy; this.UpdatedBy = source.UpdatedBy;
this.BookId = source.BookId;
this.SentenceId = source.SentenceId; this.SentenceId = source.SentenceId;
this.ChapterId = source.ChapterId; this.ChapterId = source.ChapterId;
this.ParagraphId = source.ParagraphId;
this.CreatedDatetime = source.CreatedDatetime;
this.CreatedBy = source.CreatedBy;
if (includeSystemFields) if (includeSystemFields)
{ {
this.CreatedDatetime = source.CreatedDatetime;
this.CreatedBy = source.CreatedBy;
this.UpdatedDatetime = source.UpdatedDatetime; this.UpdatedDatetime = source.UpdatedDatetime;
this.UpdatedBy = source.UpdatedBy; this.UpdatedBy = source.UpdatedBy;
this.CreatedDatetime = source.CreatedDatetime;
this.CreatedBy = source.CreatedBy;
} }
} }

View File

@ -21,16 +21,19 @@ namespace CloudBuilder.AI.Entity
public DateTime UpdatedDatetime { get; set; } public DateTime UpdatedDatetime { get; set; }
[Column("updated_by")] [Column("updated_by")]
public string UpdatedBy { get; set; } public string UpdatedBy { get; set; }
[Column("book_id")]
public string BookId { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column("sentence_id")] [Column("sentence_id")]
public long SentenceId { get; set; } public long SentenceId { get; set; }
[Column("chapter_id")] [Column("sentiment")]
public int ChapterId { get; set; } public string? Sentiment { get; set; }
[Column("content")] [Column("content")]
public string Content { get; set; } public string Content { get; set; }
[Column("status")] [Column("status")]
public string? Status { get; set; } public string? Status { get; set; }
[Column("book_id")] [Column("chapter_id")]
public string BookId { get; set; } public int ChapterId { get; set; }
[Column("paragraph_id")] [Column("paragraph_id")]
public long ParagraphId { get; set; } public long ParagraphId { get; set; }
[Column("sentence_index")] [Column("sentence_index")]
@ -41,43 +44,41 @@ namespace CloudBuilder.AI.Entity
public string? PersonName { get; set; } public string? PersonName { get; set; }
[Column("gender")] [Column("gender")]
public string? Gender { get; set; } public string? Gender { get; set; }
[Column("sentiment")]
public string? Sentiment { get; set; }
public const string CREATED_DATETIME = "CreatedDatetime"; public const string CREATED_DATETIME = "CreatedDatetime";
public const string CREATED_BY = "CreatedBy"; public const string CREATED_BY = "CreatedBy";
public const string UPDATED_DATETIME = "UpdatedDatetime"; public const string UPDATED_DATETIME = "UpdatedDatetime";
public const string UPDATED_BY = "UpdatedBy"; public const string UPDATED_BY = "UpdatedBy";
public const string BOOK_ID = "BookId";
public const string SENTENCE_ID = "SentenceId"; public const string SENTENCE_ID = "SentenceId";
public const string CHAPTER_ID = "ChapterId"; public const string SENTIMENT = "Sentiment";
public const string CONTENT = "Content"; public const string CONTENT = "Content";
public const string STATUS = "Status"; public const string STATUS = "Status";
public const string BOOK_ID = "BookId"; public const string CHAPTER_ID = "ChapterId";
public const string PARAGRAPH_ID = "ParagraphId"; public const string PARAGRAPH_ID = "ParagraphId";
public const string SENTENCE_INDEX = "SentenceIndex"; public const string SENTENCE_INDEX = "SentenceIndex";
public const string DIALOGUE_INDC = "DialogueIndc"; public const string DIALOGUE_INDC = "DialogueIndc";
public const string PERSON_NAME = "PersonName"; public const string PERSON_NAME = "PersonName";
public const string GENDER = "Gender"; public const string GENDER = "Gender";
public const string SENTIMENT = "Sentiment";
public const string DB_NAME_AI_SENTENCE_VIEW = "ai_sentence_view"; public const string DB_NAME_AI_SENTENCE_VIEW = "ai_sentence_view";
public const string DB_NAME_FIELDS="created_datetime,created_by,updated_datetime,updated_by,sentence_id,chapter_id,content,status,book_id,paragraph_id,sentence_index,dialogue_indc,person_name,gender,sentiment"; public const string DB_NAME_FIELDS="created_datetime,created_by,updated_datetime,updated_by,book_id,sentence_id,sentiment,content,status,chapter_id,paragraph_id,sentence_index,dialogue_indc,person_name,gender";
public const string DB_CREATED_DATETIME = "created_datetime"; public const string DB_CREATED_DATETIME = "created_datetime";
public const string DB_CREATED_BY = "created_by"; public const string DB_CREATED_BY = "created_by";
public const string DB_UPDATED_DATETIME = "updated_datetime"; public const string DB_UPDATED_DATETIME = "updated_datetime";
public const string DB_UPDATED_BY = "updated_by"; public const string DB_UPDATED_BY = "updated_by";
public const string DB_BOOK_ID = "book_id";
public const string DB_SENTENCE_ID = "sentence_id"; public const string DB_SENTENCE_ID = "sentence_id";
public const string DB_CHAPTER_ID = "chapter_id"; public const string DB_SENTIMENT = "sentiment";
public const string DB_CONTENT = "content"; public const string DB_CONTENT = "content";
public const string DB_STATUS = "status"; public const string DB_STATUS = "status";
public const string DB_BOOK_ID = "book_id"; public const string DB_CHAPTER_ID = "chapter_id";
public const string DB_PARAGRAPH_ID = "paragraph_id"; public const string DB_PARAGRAPH_ID = "paragraph_id";
public const string DB_SENTENCE_INDEX = "sentence_index"; public const string DB_SENTENCE_INDEX = "sentence_index";
public const string DB_DIALOGUE_INDC = "dialogue_indc"; public const string DB_DIALOGUE_INDC = "dialogue_indc";
public const string DB_PERSON_NAME = "person_name"; public const string DB_PERSON_NAME = "person_name";
public const string DB_GENDER = "gender"; public const string DB_GENDER = "gender";
public const string DB_SENTIMENT = "sentiment";
public static AiSentenceViewEntity GetInstance() public static AiSentenceViewEntity GetInstance()
@ -101,17 +102,17 @@ namespace CloudBuilder.AI.Entity
this.CreatedBy = source.CreatedBy; this.CreatedBy = source.CreatedBy;
this.UpdatedDatetime = source.UpdatedDatetime; this.UpdatedDatetime = source.UpdatedDatetime;
this.UpdatedBy = source.UpdatedBy; this.UpdatedBy = source.UpdatedBy;
this.BookId = source.BookId;
this.SentenceId = source.SentenceId; this.SentenceId = source.SentenceId;
this.ChapterId = source.ChapterId; this.Sentiment = source.Sentiment;
this.Content = source.Content; this.Content = source.Content;
this.Status = source.Status; this.Status = source.Status;
this.BookId = source.BookId; this.ChapterId = source.ChapterId;
this.ParagraphId = source.ParagraphId; this.ParagraphId = source.ParagraphId;
this.SentenceIndex = source.SentenceIndex; this.SentenceIndex = source.SentenceIndex;
this.DialogueIndc = source.DialogueIndc; this.DialogueIndc = source.DialogueIndc;
this.PersonName = source.PersonName; this.PersonName = source.PersonName;
this.Gender = source.Gender; this.Gender = source.Gender;
this.Sentiment = source.Sentiment;
if (includeSystemFields) if (includeSystemFields)
{ {

View File

@ -45,6 +45,7 @@ namespace CloudBuilder.AI.Entity
builder.Entity<AiChapterEntity>().HasKey(c => new {c.BookId,c.ChapterId}); builder.Entity<AiChapterEntity>().HasKey(c => new {c.BookId,c.ChapterId});
builder.Entity<AiSentenceDialogueViewEntity>(eb => { eb.HasNoKey(); }); builder.Entity<AiSentenceDialogueViewEntity>(eb => { eb.HasNoKey(); });
builder.Entity<AiSentenceViewEntity>(eb => { eb.HasNoKey(); }); builder.Entity<AiSentenceViewEntity>(eb => { eb.HasNoKey(); });
builder.Entity<AiSentenceViewEntity>(eb => { eb.HasNoKey(); });
//***END*** //***END***
} }
} }

View File

@ -56,6 +56,7 @@ namespace CloudBuilder.AI.Utility
aiParagraph = new AiSentenceEntity(); aiParagraph = new AiSentenceEntity();
aiParagraph.Content = text; aiParagraph.Content = text;
aiParagraph.BookId = aiChapter.BookId;
aiParagraph.ChapterId = aiChapter.ChapterId; aiParagraph.ChapterId = aiChapter.ChapterId;
aiParagraph.ParagraphId = index++; aiParagraph.ParagraphId = index++;
aiParagraph.DialogueIndc = sentenceSplitter.IsDialogue(text) ? YesNoPolicy.YES : YesNoPolicy.NO; aiParagraph.DialogueIndc = sentenceSplitter.IsDialogue(text) ? YesNoPolicy.YES : YesNoPolicy.NO;

View File

@ -48,27 +48,44 @@ namespace CloudBuilder.AI.Utility
/// <summary> /// <summary>
/// 初始化动态正则:根据配置的成对引号生成匹配规则 /// 初始化动态正则:根据配置的成对引号生成匹配规则
/// </summary> /// </summary>
//private void InitDynamicRegex()
//{
// if (_quotePairs.Count == 0)
// {
// // 空配置时生成空正则,避免匹配任何内容
// _dynamicQuotePattern = new Regex(@"^$", RegexOptions.Compiled);
// return;
// }
// // 步骤1转义所有引号避免正则特殊字符冲突拼接左/右引号匹配组
// string escapedLeftQuotes = string.Join("|", _quotePairs.Select(p => Regex.Escape(p.LeftQuote)));
// string escapedRightQuotes = string.Join("|", _quotePairs.Select(p => Regex.Escape(p.RightQuote)));
// // 步骤2转义标点集合避免正则解析错误
// string escapedPunctuations = Regex.Escape(_endPunctuations);
// // 步骤3构建动态正则表达式
// // 分组说明:
// // ?<left>:匹配配置的任意左引号
// // ?<content>:匹配引号内的内容(必须以指定标点结尾)
// // ?<right>:匹配配置的任意右引号
// string regexPattern = $@"(?<left>{escapedLeftQuotes})(?<content>.+?[{escapedPunctuations}])(?<right>{escapedRightQuotes})";
// _dynamicQuotePattern = new Regex(regexPattern, RegexOptions.Compiled | RegexOptions.Singleline);
//}
private void InitDynamicRegex() private void InitDynamicRegex()
{ {
if (_quotePairs.Count == 0) if (_quotePairs.Count == 0)
{ {
// 空配置时生成空正则,避免匹配任何内容
_dynamicQuotePattern = new Regex(@"^$", RegexOptions.Compiled); _dynamicQuotePattern = new Regex(@"^$", RegexOptions.Compiled);
return; return;
} }
// 步骤1转义所有引号避免正则特殊字符冲突拼接左/右引号匹配组
string escapedLeftQuotes = string.Join("|", _quotePairs.Select(p => Regex.Escape(p.LeftQuote))); string escapedLeftQuotes = string.Join("|", _quotePairs.Select(p => Regex.Escape(p.LeftQuote)));
string escapedRightQuotes = string.Join("|", _quotePairs.Select(p => Regex.Escape(p.RightQuote))); string escapedRightQuotes = string.Join("|", _quotePairs.Select(p => Regex.Escape(p.RightQuote)));
// 步骤2转义标点集合避免正则解析错误
string escapedPunctuations = Regex.Escape(_endPunctuations);
// 步骤3构建动态正则表达式 // ✅ 修复:移除必须以标点结尾的限制,匹配任意引号内容
// 分组说明: string regexPattern = $@"(?<left>{escapedLeftQuotes})(?<content>.+?)(?<right>{escapedRightQuotes})";
// ?<left>:匹配配置的任意左引号
// ?<content>:匹配引号内的内容(必须以指定标点结尾)
// ?<right>:匹配配置的任意右引号
string regexPattern = $@"(?<left>{escapedLeftQuotes})(?<content>.+?[{escapedPunctuations}])(?<right>{escapedRightQuotes})";
_dynamicQuotePattern = new Regex(regexPattern, RegexOptions.Compiled | RegexOptions.Singleline); _dynamicQuotePattern = new Regex(regexPattern, RegexOptions.Compiled | RegexOptions.Singleline);
} }
@ -108,6 +125,7 @@ namespace CloudBuilder.AI.Utility
if (!string.IsNullOrEmpty(prefixText)) if (!string.IsNullOrEmpty(prefixText))
{ {
aiSentence = new AiSentenceEntity(); aiSentence = new AiSentenceEntity();
aiSentence.BookId = aiParagraphEntity.BookId;
aiSentence.ChapterId = aiParagraphEntity.ChapterId; aiSentence.ChapterId = aiParagraphEntity.ChapterId;
aiSentence.Content = prefixText; aiSentence.Content = prefixText;
aiSentence.DialogueIndc = YesNoPolicy.NO; aiSentence.DialogueIndc = YesNoPolicy.NO;
@ -123,6 +141,7 @@ namespace CloudBuilder.AI.Utility
if (!string.IsNullOrEmpty(quotedContent)) if (!string.IsNullOrEmpty(quotedContent))
{ {
aiSentence = new AiSentenceEntity(); aiSentence = new AiSentenceEntity();
aiSentence.BookId = aiParagraphEntity.BookId;
aiSentence.ChapterId = aiParagraphEntity.ChapterId; aiSentence.ChapterId = aiParagraphEntity.ChapterId;
aiSentence.Content = quotedContent; aiSentence.Content = quotedContent;
aiSentence.DialogueIndc = YesNoPolicy.YES; aiSentence.DialogueIndc = YesNoPolicy.YES;
@ -142,6 +161,7 @@ namespace CloudBuilder.AI.Utility
if (!string.IsNullOrEmpty(suffixText)) if (!string.IsNullOrEmpty(suffixText))
{ {
aiSentence = new AiSentenceEntity(); aiSentence = new AiSentenceEntity();
aiSentence.BookId = aiParagraphEntity.BookId;
aiSentence.ChapterId = aiParagraphEntity.ChapterId; aiSentence.ChapterId = aiParagraphEntity.ChapterId;
aiSentence.Content = suffixText; aiSentence.Content = suffixText;
aiSentence.DialogueIndc = YesNoPolicy.NO; aiSentence.DialogueIndc = YesNoPolicy.NO;
@ -181,16 +201,15 @@ namespace CloudBuilder.AI.Utility
.Replace("", "'"); .Replace("", "'");
// 3. 核心判断规则: // 3. 核心判断规则:
// - 包含成对/单个双引号(排除仅含引号无内容的情况) var hasQuote = Regex.IsMatch(cleanedText, @""".+?""") ||
// - 包含冒号+引号(如:"XXX说"XXX"" 场景) Regex.IsMatch(cleanedText, @"[:,]\s*"""); // 冒号/逗号+引号(说话标记)
var hasQuote = Regex.IsMatch(cleanedText, @""".+?""") || // 成对双引号(含内容)
Regex.IsMatch(cleanedText, @":\s*"""); // 冒号后接引号(说话人+对话)
var hasSingleQuote = Regex.IsMatch(cleanedText, @"'[^']+'"); // 成对单引号(英文对话)
// 4. 排除仅含引号符号的无效情况(如 ""、''、“”) var hasSingleQuote = Regex.IsMatch(cleanedText, @"'[^']+'");
var onlyQuote = Regex.IsMatch(cleanedText, @"^\s*[""''“”]{1,2}\s*$");
// 最终判断:包含有效引号且不是仅含引号 → 判定为对话 // 4. 排除仅含引号符号的无效情况(修复:只匹配替换后的符号)
var onlyQuote = Regex.IsMatch(cleanedText, @"^\s*[""']{1,2}\s*$");
// 最终判断
return (hasQuote || hasSingleQuote) && !onlyQuote; return (hasQuote || hasSingleQuote) && !onlyQuote;
} }
} }