feat(personal): add scanned PDF OCR schema

This commit is contained in:
2026-07-12 21:14:53 +08:00
parent 92e2140abf
commit 88bfe1d815
3 changed files with 68 additions and 2 deletions
@@ -45,7 +45,18 @@ public final class PersonalAssistantDto {
public record ItemResponse(long id, String sourceType, String title, String originalUrl, String mimeType, public record ItemResponse(long id, String sourceType, String title, String originalUrl, String mimeType,
long sizeBytes, String status, String errorCode, String errorMessage, String summary, long sizeBytes, String status, String errorCode, String errorMessage, String summary,
List<String> tags, LocalDateTime capturedAt, LocalDateTime parsedAt) { List<String> tags, LocalDateTime capturedAt, LocalDateTime parsedAt,
OcrProgressResponse ocr) {
public ItemResponse(long id, String sourceType, String title, String originalUrl, String mimeType,
long sizeBytes, String status, String errorCode, String errorMessage, String summary,
List<String> tags, LocalDateTime capturedAt, LocalDateTime parsedAt) {
this(id, sourceType, title, originalUrl, mimeType, sizeBytes, status, errorCode, errorMessage, summary,
tags, capturedAt, parsedAt, null);
}
}
public record OcrProgressResponse(boolean required, String status, int totalPages, int processedPages,
int successPages, int failedPages, List<Integer> failedPageNumbers) {
} }
public record PageResponse<T>(List<T> rows, long total, int pageNum, int pageSize) { public record PageResponse<T>(List<T> rows, long total, int pageNum, int pageSize) {
@@ -59,8 +59,10 @@ class PersonalSchemaContractTest {
String session = tableDefinition(sql, "aihr_personal_chat_session"); String session = tableDefinition(sql, "aihr_personal_chat_session");
String message = tableDefinition(sql, "aihr_personal_chat_message"); String message = tableDefinition(sql, "aihr_personal_chat_message");
String cleanup = tableDefinition(sql, "aihr_personal_cleanup_job"); String cleanup = tableDefinition(sql, "aihr_personal_cleanup_job");
String ocrJob = tableDefinition(sql, "aihr_personal_ocr_job");
String ocrPage = tableDefinition(sql, "aihr_personal_ocr_page");
for (String definition : new String[] {space, item, fragment, session, message, cleanup}) { for (String definition : new String[] {space, item, fragment, session, message, cleanup, ocrJob, ocrPage}) {
assertTrue(definition.contains("`owner_user_id` bigint not null"), assertTrue(definition.contains("`owner_user_id` bigint not null"),
"Every personal table must carry a non-null owner_user_id"); "Every personal table must carry a non-null owner_user_id");
} }
@@ -123,6 +125,20 @@ class PersonalSchemaContractTest {
assertTrue(cleanup.contains("unique key `uk_personal_cleanup_item` (`tenant_id`, `owner_user_id`, `item_id`)")); assertTrue(cleanup.contains("unique key `uk_personal_cleanup_item` (`tenant_id`, `owner_user_id`, `item_id`)"));
assertTrue(cleanup.contains("key `idx_personal_cleanup_status` (`status`, `update_time`)")); assertTrue(cleanup.contains("key `idx_personal_cleanup_status` (`status`, `update_time`)"));
assertTrue(ocrJob.contains("`total_pages` int not null"));
assertTrue(ocrJob.contains("`processed_pages` int not null default 0"));
assertTrue(ocrJob.contains("`success_pages` int not null default 0"));
assertTrue(ocrJob.contains("`failed_pages` int not null default 0"));
assertTrue(ocrJob.contains("`failed_page_numbers_json` json"));
assertTrue(ocrJob.contains("unique key `uk_personal_ocr_job_item` (`tenant_id`, `owner_user_id`, `item_id`)"));
assertTrue(ocrJob.contains("key `idx_personal_ocr_job_status` (`status`, `update_time`)"));
assertTrue(ocrPage.contains("`page_number` int not null"));
assertTrue(ocrPage.contains("`ocr_text` longtext"));
assertTrue(ocrPage.contains("`attempt_count` int not null default 0"));
assertTrue(ocrPage.contains("unique key `uk_personal_ocr_page_number` (`tenant_id`, `owner_user_id`, `item_id`, `page_number`)"));
assertTrue(ocrPage.contains("key `idx_personal_ocr_page_status` (`status`, `update_time`)"));
assertFalse(sql.contains("alter table aihr_knowledge_fragment"), assertFalse(sql.contains("alter table aihr_knowledge_fragment"),
"Personal schema must not mutate enterprise knowledge tables"); "Personal schema must not mutate enterprise knowledge tables");
assertFalse(sql.contains("alter table `aihr_knowledge_fragment`"), assertFalse(sql.contains("alter table `aihr_knowledge_fragment`"),
@@ -128,3 +128,42 @@ CREATE TABLE IF NOT EXISTS `aihr_personal_cleanup_job` (
UNIQUE KEY `uk_personal_cleanup_item` (`tenant_id`, `owner_user_id`, `item_id`), UNIQUE KEY `uk_personal_cleanup_item` (`tenant_id`, `owner_user_id`, `item_id`),
KEY `idx_personal_cleanup_status` (`status`, `update_time`) KEY `idx_personal_cleanup_status` (`status`, `update_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='个人AI助理资料清理任务'; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='个人AI助理资料清理任务';
CREATE TABLE IF NOT EXISTS `aihr_personal_ocr_job` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'OCR任务ID',
`tenant_id` varchar(20) NOT NULL COMMENT '租户编号',
`owner_user_id` bigint NOT NULL COMMENT '资料所属用户ID',
`item_id` bigint NOT NULL COMMENT '资料ID',
`status` varchar(20) NOT NULL DEFAULT 'PENDING' COMMENT 'PENDING/RUNNING/RETRY/COMPLETED/FAILED',
`total_pages` int NOT NULL COMMENT 'PDF总页数',
`processed_pages` int NOT NULL DEFAULT 0 COMMENT '已处理页数',
`success_pages` int NOT NULL DEFAULT 0 COMMENT '识别成功页数',
`failed_pages` int NOT NULL DEFAULT 0 COMMENT '识别失败页数',
`failed_page_numbers_json` json COMMENT '识别失败页码JSON',
`next_page` int NOT NULL DEFAULT 1 COMMENT '下一待处理页码,从1开始',
`attempt_count` int NOT NULL DEFAULT 0 COMMENT '任务领取次数',
`last_error_code` varchar(80) DEFAULT NULL COMMENT '脱敏后的错误码',
`lease_until` datetime DEFAULT NULL COMMENT '任务租约截止时间',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_personal_ocr_job_item` (`tenant_id`, `owner_user_id`, `item_id`),
KEY `idx_personal_ocr_job_status` (`status`, `update_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='个人AI助理扫描PDF OCR任务';
CREATE TABLE IF NOT EXISTS `aihr_personal_ocr_page` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'OCR页记录ID',
`tenant_id` varchar(20) NOT NULL COMMENT '租户编号',
`owner_user_id` bigint NOT NULL COMMENT '资料所属用户ID',
`item_id` bigint NOT NULL COMMENT '资料ID',
`page_number` int NOT NULL COMMENT '页码,从1开始',
`status` varchar(20) NOT NULL DEFAULT 'PENDING' COMMENT 'PENDING/RUNNING/SUCCESS/FAILED',
`ocr_text` longtext COMMENT '页级OCR文本',
`attempt_count` int NOT NULL DEFAULT 0 COMMENT '识别尝试次数',
`last_error_code` varchar(80) DEFAULT NULL COMMENT '脱敏后的错误码',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_personal_ocr_page_number` (`tenant_id`, `owner_user_id`, `item_id`, `page_number`),
KEY `idx_personal_ocr_page_status` (`status`, `update_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='个人AI助理扫描PDF OCR页结果';