diff --git a/src/main/java/com/labelsys/backend/entity/SysMenu.java b/src/main/java/com/labelsys/backend/entity/SysMenu.java index e1a124b..cd6395c 100644 --- a/src/main/java/com/labelsys/backend/entity/SysMenu.java +++ b/src/main/java/com/labelsys/backend/entity/SysMenu.java @@ -18,10 +18,10 @@ public class SysMenu { @TableId(type = IdType.INPUT) private Long id; private Long companyId; - private String permissionCode; private String menuCode; private String menuName; private String path; + private String visiblePositions; private Integer sortOrder; private LocalDateTime createdAt; private LocalDateTime updatedAt; diff --git a/src/main/java/com/labelsys/backend/interceptor/AuthInterceptor.java b/src/main/java/com/labelsys/backend/interceptor/AuthInterceptor.java index 37acd88..7467e42 100644 --- a/src/main/java/com/labelsys/backend/interceptor/AuthInterceptor.java +++ b/src/main/java/com/labelsys/backend/interceptor/AuthInterceptor.java @@ -1,5 +1,13 @@ package com.labelsys.backend.interceptor; +import java.time.Duration; +import java.util.Set; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.HandlerInterceptor; + import com.labelsys.backend.annotation.RequirePosition; import com.labelsys.backend.common.exception.ForbiddenException; import com.labelsys.backend.common.exception.UnauthorizedException; @@ -8,48 +16,30 @@ import com.labelsys.backend.context.UserContext; import com.labelsys.backend.entity.SysCompany; import com.labelsys.backend.entity.SysUser; import com.labelsys.backend.enums.CompanyStatus; -import com.labelsys.backend.enums.UserPosition; import com.labelsys.backend.enums.UserStatus; import com.labelsys.backend.mapper.SysCompanyMapper; import com.labelsys.backend.mapper.SysUserMapper; import com.labelsys.backend.service.session.TokenSessionRepository; + import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import java.time.Duration; -import java.util.Set; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; -import org.springframework.web.method.HandlerMethod; -import org.springframework.web.servlet.HandlerInterceptor; @Component public class AuthInterceptor implements HandlerInterceptor { - private static final Set OPEN_PATHS = Set.of( - "/label/api/auth/companies", - "/label/api/auth/login", - "/label/swagger-ui.html", - "/label/v3/api-docs", - "/label/v3/api-docs/swagger-config" - ); + private static final Set OPEN_PATHS = Set.of("/label/api/auth/companies", "/label/api/auth/login", + "/label/swagger-ui.html", "/label/v3/api-docs", "/label/v3/api-docs/swagger-config"); - private static final Set ALLOWED_WHEN_MUST_CHANGE_PASSWORD = Set.of( - "/label/api/auth/change-password", - "/label/api/auth/logout", - "/label/api/auth/me" - ); + private static final Set ALLOWED_WHEN_MUST_CHANGE_PASSWORD = + Set.of("/label/api/auth/change-password", "/label/api/auth/logout", "/label/api/auth/me"); private final TokenSessionRepository tokenSessionRepository; private final SysUserMapper sysUserMapper; private final SysCompanyMapper sysCompanyMapper; private final Duration sessionTtl; - public AuthInterceptor( - TokenSessionRepository tokenSessionRepository, - SysUserMapper sysUserMapper, - SysCompanyMapper sysCompanyMapper, - @Value("${labelsys.session.ttl:PT2H}") Duration sessionTtl - ) { + public AuthInterceptor(TokenSessionRepository tokenSessionRepository, SysUserMapper sysUserMapper, + SysCompanyMapper sysCompanyMapper, @Value("${labelsys.session.ttl:PT2H}") Duration sessionTtl) { this.tokenSessionRepository = tokenSessionRepository; this.sysUserMapper = sysUserMapper; this.sysCompanyMapper = sysCompanyMapper; @@ -69,12 +59,13 @@ public class AuthInterceptor implements HandlerInterceptor { return true; } String token = extractToken(request.getHeader("Authorization")); - LoginUser loginUser = tokenSessionRepository.find(token) - .orElseThrow(() -> new UnauthorizedException("未登录或登录已过期")); + LoginUser loginUser = + tokenSessionRepository.find(token).orElseThrow(() -> new UnauthorizedException("未登录或登录已过期")); SysUser user = sysUserMapper.findByIdAndCompanyId(loginUser.userId(), loginUser.companyId()); SysCompany company = sysCompanyMapper.selectById(loginUser.companyId()); - if (user == null || company == null || user.getStatus() != UserStatus.ENABLED || company.getStatus() != CompanyStatus.ENABLED) { + if (user == null || company == null || user.getStatus() != UserStatus.ENABLED + || company.getStatus() != CompanyStatus.ENABLED) { throw new UnauthorizedException("未登录或登录已过期"); } if (!user.getSessionVersion().equals(loginUser.sessionVersion())) { @@ -96,7 +87,8 @@ public class AuthInterceptor implements HandlerInterceptor { } @Override - public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, + Exception ex) { UserContext.clear(); } diff --git a/src/main/java/com/labelsys/backend/mapper/SysMenuMapper.java b/src/main/java/com/labelsys/backend/mapper/SysMenuMapper.java index 5a42212..823e073 100644 --- a/src/main/java/com/labelsys/backend/mapper/SysMenuMapper.java +++ b/src/main/java/com/labelsys/backend/mapper/SysMenuMapper.java @@ -7,5 +7,5 @@ import org.apache.ibatis.annotations.Param; public interface SysMenuMapper extends BaseMapper { - List listCurrentMenus(@Param("companyId") Long companyId, @Param("positionCodes") List positionCodes); + List listCurrentMenus(@Param("companyId") Long companyId, @Param("visiblePositions") List visiblePositions); } diff --git a/src/main/java/com/labelsys/backend/service/MenuService.java b/src/main/java/com/labelsys/backend/service/MenuService.java index 31af6d6..fdae154 100644 --- a/src/main/java/com/labelsys/backend/service/MenuService.java +++ b/src/main/java/com/labelsys/backend/service/MenuService.java @@ -14,11 +14,11 @@ public class MenuService { private final SysMenuMapper sysMenuMapper; public List listCurrentMenus(LoginUser currentUser) { - List positionCodes = java.util.Arrays.stream(com.labelsys.backend.enums.UserPosition.values()) + List visiblePositions = java.util.Arrays.stream(com.labelsys.backend.enums.UserPosition.values()) .filter(position -> currentUser.position().canAccess(position)) .map(Enum::name) .toList(); - return sysMenuMapper.listCurrentMenus(currentUser.companyId(), positionCodes) + return sysMenuMapper.listCurrentMenus(currentUser.companyId(), visiblePositions) .stream() .map(MenuResponse::from) .toList(); diff --git a/src/main/resources/mapper/SysMenuMapper.xml b/src/main/resources/mapper/SysMenuMapper.xml index 9aedbdf..dde48e6 100644 --- a/src/main/resources/mapper/SysMenuMapper.xml +++ b/src/main/resources/mapper/SysMenuMapper.xml @@ -4,26 +4,24 @@ - + diff --git a/src/main/resources/sql/data.sql b/src/main/resources/sql/data.sql index 568c728..0ea5129 100644 --- a/src/main/resources/sql/data.sql +++ b/src/main/resources/sql/data.sql @@ -1,23 +1,156 @@ -insert into sys_company (id, company_code, company_name, status) values - (1, 'PLATFORM', '平台公司', 'ENABLED'), - (2, 'ALPHA', '甲公司', 'ENABLED') -on conflict do nothing; +BEGIN; + +INSERT INTO sys_company (id, company_code, company_name, status) VALUES + (1, 'PLATFORM', '平台公司', 'ENABLED'), + (2, 'ALPHA', '甲公司', 'ENABLED') +ON CONFLICT DO NOTHING; + +INSERT INTO sys_user ( + id, company_id, phone, username, role, position, real_name, + password_hash, must_change_password, status, session_version +) VALUES + (1, 1, '13900000000', 'platform-admin', 'ENGINEER', 'ADMIN', '平台管理员', + '$2a$10$TGPk5rNNhKNJQvTWImw5J.LVzw9HDFWR6hyNJCkLDcp0GU8/vp0aS', FALSE, 'ENABLED', 1), + (2, 2, '13800138000', 'alpha-admin', 'ENGINEER', 'ADMIN', '甲公司管理员', + '$2a$10$/hSD8ch7A9lFWi/DOb8yJOHdlrhV57p95CBv9Uv93Yky7t6c4Rs/S', TRUE, 'ENABLED', 1), + (3, 2, '13700000000', 'alpha-annotator', 'EMPLOYEE', 'ANNOTATOR', '甲公司标注员', + '$2a$10$bRMZPcIaiB1BUx6HPw6FSODPSuph8kUi8/JZOM6lACwjjhkbBL5mq', TRUE, 'ENABLED', 1), + (4, 2, '13600000000', 'alpha-trainer', 'EMPLOYEE', 'DATA_TRAINER', '甲公司数据训练师', + '$2a$10$bRMZPcIaiB1BUx6HPw6FSODPSuph8kUi8/JZOM6lACwjjhkbBL5mq', TRUE, 'ENABLED', 1), + (5, 2, '13500000000', 'alpha-reviewer', 'MANAGER', 'REVIEWER', '甲公司审核员', + '$2a$10$bRMZPcIaiB1BUx6HPw6FSODPSuph8kUi8/JZOM6lACwjjhkbBL5mq', TRUE, 'ENABLED', 1), + (6, 2, '13400000000', 'alpha-chief-engineer', 'ENGINEER', 'REVIEWER', '甲公司总工程师', + '$2a$10$bRMZPcIaiB1BUx6HPw6FSODPSuph8kUi8/JZOM6lACwjjhkbBL5mq', TRUE, 'ENABLED', 1) +ON CONFLICT DO NOTHING; + +INSERT INTO sys_menu (id, company_id, menu_code, menu_name, path, visible_positions, sort_order) VALUES + (201, 2, 'USER_MANAGE', '用户管理', '/users', 'ADMIN', 1), + (202, 2, 'RESOURCE_MANAGE', '资源管理', '/resources', 'ANNOTATOR,DATA_TRAINER,REVIEWER,ADMIN', 2), + (203, 2, 'TASK_MANAGE', '任务管理', '/tasks', 'ANNOTATOR,DATA_TRAINER,REVIEWER,ADMIN', 3), + (204, 2, 'RESULT_REVIEW', '结果审核', '/results', 'REVIEWER,ADMIN', 4), + (205, 2, 'DATASET_EXPORT', '训练集导出', '/datasets', 'DATA_TRAINER,ADMIN', 5), + (206, 2, 'SYSTEM_CONFIG', '系统配置', '/configs', 'ADMIN', 6) +ON CONFLICT DO NOTHING; + +INSERT INTO sys_config ( + id, company_id, config_type, config_name, config_value, status, creator_id +) VALUES + (401, 2, 'MODEL', 'qwen-max', + '{"modelUrl":"https://api.example.com/extract","apiKey":"extract-api-key-demo"}', 'ENABLED', 2), + (402, 2, 'MODEL', 'glm-4.5', + '{"modelUrl":"https://api.example.com/verify","apiKey":"verify-api-key-demo"}', 'ENABLED', 2), + (403, 2, 'PROMPT', 'extractPrompt', + '请根据输入内容提取结构化问答对。', 'ENABLED', 2), + (404, 2, 'PROMPT', 'verifyPrompt', + '请核验抽取结果是否准确,并给出修正答案。', 'ENABLED', 2), + (406, 2, 'MODEL', 'qwen-vl-max', + '{"modelUrl":"https://api.example.com/extract-vl","apiKey":"extract-vl-api-key-demo"}', 'ENABLED', 2), + (407, 2, 'MODEL', 'glm-4.5v', + '{"modelUrl":"https://api.example.com/verify-vl","apiKey":"verify-vl-api-key-demo"}', 'ENABLED', 2), + (408, 2, 'PROMPT', 'imageExtractPrompt', + '请根据输入图片内容提取结构化问答对。', 'ENABLED', 2), + (409, 2, 'PROMPT', 'imageVerifyPrompt', + '请核验图片问答结果是否准确。', 'ENABLED', 2), + (405, 2, 'SYSTEM', 'storageProvider', + '{"provider":"rustfs","defaultBucket":"source-data"}', 'ENABLED', 2) +ON CONFLICT DO NOTHING; + +INSERT INTO biz_data_record (id, company_id, creator_id, creator_role, record_name) VALUES + (501, 2, 3, 'EMPLOYEE', '员工创建的数据'), + (502, 2, 5, 'MANAGER', '经理创建的数据'), + (503, 2, 6, 'ENGINEER', '总工程师创建的数据') +ON CONFLICT DO NOTHING; + +INSERT INTO source_resource ( + id, company_id, creator_id, creator_role, resource_name, resource_type, + bucket_name, file_path, file_size, source_status, storage_provider, remark +) VALUES + (601, 2, 3, 'EMPLOYEE', '设备巡检规范.txt', 'TEXT', + 'source-data', 'text/202604/601.txt', 20480, 'READY', 'rustfs', '文本资源示例'), + (602, 2, 3, 'EMPLOYEE', '控制柜照片.jpg', 'IMAGE', + 'source-data', 'image/202604/602.jpg', 532480, 'READY', 'rustfs', '图片资源示例') +ON CONFLICT DO NOTHING; -insert into sys_user (id, company_id, phone, username, role, position, real_name, password_hash, must_change_password, status, session_version) values - (1, 1, '13900000000', 'platform-admin', 'ENGINEER', 'ADMIN', '平台管理员', '$2a$10$TGPk5rNNhKNJQvTWImw5J.LVzw9HDFWR6hyNJCkLDcp0GU8/vp0aS', false, 'ENABLED', 1), - (2, 2, '13800138000', 'alpha-admin', 'EMPLOYEE', 'ADMIN', '甲公司管理员', '$2a$10$/hSD8ch7A9lFWi/DOb8yJOHdlrhV57p95CBv9Uv93Yky7t6c4Rs/S', true, 'ENABLED', 1), - (3, 2, '13700000000', 'alpha-annotator', 'EMPLOYEE', 'ANNOTATOR', '甲公司标注员', '$2a$10$bRMZPcIaiB1BUx6HPw6FSODPSuph8kUi8/JZOM6lACwjjhkbBL5mq', false, 'ENABLED', 1), - (4, 2, '13600000000', 'alpha-manager', 'MANAGER', 'REVIEWER', '甲公司经理', '$2a$10$bRMZPcIaiB1BUx6HPw6FSODPSuph8kUi8/JZOM6lACwjjhkbBL5mq', false, 'ENABLED', 1), - (5, 2, '13500000000', 'alpha-engineer', 'ENGINEER', 'ADMIN', '甲公司工程师', '$2a$10$bRMZPcIaiB1BUx6HPw6FSODPSuph8kUi8/JZOM6lACwjjhkbBL5mq', false, 'ENABLED', 1) -on conflict do nothing; +INSERT INTO annotation_task ( + id, company_id, creator_id, creator_role, task_name, industry_type, task_type, + extract_model_config_id, + extract_model_name, extract_model_url, extract_model_api_key, + verify_model_config_id, + verify_model_name, verify_model_url, verify_model_api_key, + extract_prompt_config_id, extract_prompt, + verify_prompt_config_id, verify_prompt, + task_status, is_deleted, started_at, finished_at, error_message +) VALUES + (701, 2, 4, 'EMPLOYEE', '多资源问答抽取任务', 'electricity', 'EXTRACT_QA', + 401, + 'qwen-max', 'https://api.example.com/extract', 'extract-api-key-demo', + 402, + 'glm-4.5', 'https://api.example.com/verify', 'verify-api-key-demo', + 403, '请根据输入文本提取结构化问答对。', + 404, '请核验生成答案是否与原始内容一致。', + 'PENDING', FALSE, NULL, NULL, NULL), + (702, 2, 4, 'EMPLOYEE', '图片问答抽取任务', 'transport', 'EXTRACT_QA', + 406, + 'qwen-vl-max', 'https://api.example.com/extract-vl', 'extract-vl-api-key-demo', + 407, + 'glm-4.5v', 'https://api.example.com/verify-vl', 'verify-vl-api-key-demo', + 408, '请根据输入图片内容提取结构化问答对。', + 409, '请核验图片问答结果是否准确。', + 'COMPLETED', FALSE, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL) +ON CONFLICT DO NOTHING; -insert into sys_menu (id, company_id, permission_code, menu_code, menu_name, path, sort_order) values - (201, 2, 'USER_MANAGE', 'USER_MANAGE', '员工管理', '/users', 1), - (202, 2, 'DATA_RECORD_VIEW', 'DATA_RECORDS', '数据记录', '/data-records', 2) -on conflict do nothing; +INSERT INTO annotation_task_resource ( + id, company_id, task_id, resource_id +) VALUES + (711, 2, 701, 601), + (712, 2, 701, 602), + (713, 2, 702, 602) +ON CONFLICT DO NOTHING; -insert into biz_data_record (id, company_id, creator_id, creator_role, record_name) values - (401, 2, 3, 'EMPLOYEE', '员工创建的数据'), - (402, 2, 4, 'MANAGER', '经理创建的数据'), - (403, 2, 5, 'ENGINEER', '工程师创建的数据') -on conflict do nothing; +INSERT INTO annotation_result ( + id, company_id, creator_id, creator_role, task_id, resource_id, + qa_content_json, qa_content_storage_mode, qa_content_file_path, diff_summary, + requires_manual_review, is_deleted, reviewer_id, review_comment, reviewed_at +) VALUES + (801, 2, 3, 'EMPLOYEE', 701, 601, + '{"question":"巡检开始前需要做什么?","answer":"详见外置结果文件,包含完整步骤与注意事项。"}', + 'EXTERNAL', 'annotation-results/202604/801-qa.json', + '{"extract_question":"巡检开始前需要做什么?","extract_answer":"开始前检查设备状态和作业环境。","verify_answer":"开始前应确认设备状态、防护用品和现场环境安全。","mismatch_fields":["answer"],"reason":"抽取答案遗漏了安全检查要点。"}', + TRUE, FALSE, NULL, NULL, NULL), + (802, 2, 3, 'EMPLOYEE', 702, 602, + '{"question":"图片中的控制柜当前状态如何?","answer":"控制柜处于运行状态,绿色指示灯亮起。"}', + 'INLINE', NULL, + '{"extract_question":"图片中的控制柜当前状态如何?","extract_answer":"控制柜处于运行状态,绿色指示灯亮起。","verify_answer":"控制柜正在运行,指示灯显示正常。","mismatch_fields":[],"reason":"校验结果与抽取结果基本一致。"}', + FALSE, FALSE, 5, '结果可通过。', CURRENT_TIMESTAMP) +ON CONFLICT DO NOTHING; + +INSERT INTO annotation_result_history ( + id, company_id, creator_id, creator_role, source_result_id, task_id, resource_id, + qa_content_json, qa_content_storage_mode, qa_content_file_path, archive_reason, archived_by, archived_at +) VALUES + (901, 2, 3, 'EMPLOYEE', 802, 702, 602, + '{"question":"图片中的控制柜当前状态如何?","answer":"控制柜处于运行状态,绿色指示灯亮起。"}', + 'INLINE', + NULL, + '审核通过后归档', 5, CURRENT_TIMESTAMP) +ON CONFLICT DO NOTHING; + +INSERT INTO training_dataset ( + id, company_id, creator_id, creator_role, result_history_id, sample_type, glm_format_json, dataset_status +) VALUES + (1001, 2, 4, 'EMPLOYEE', 901, 'TEXT', + '{"messages":[{"role":"system","content":"你是专业知识问答助手"},{"role":"user","content":"图片中的控制柜当前状态如何?"},{"role":"assistant","content":"控制柜处于运行状态,绿色指示灯亮起。"}]}', + 'DRAFT') +ON CONFLICT DO NOTHING; + +INSERT INTO export_batch ( + id, company_id, creator_id, creator_role, batch_no, dataset_file_path, sample_count, finetune_job_id, finetune_status +) VALUES + (1101, 2, 4, 'EMPLOYEE', 'BATCH-20260424-001', 'export/BATCH-20260424-001.jsonl', 1, NULL, 'NOT_STARTED') +ON CONFLICT DO NOTHING; + +INSERT INTO export_batch_item (id, batch_id, dataset_id) VALUES + (1201, 1101, 1001) +ON CONFLICT DO NOTHING; + +COMMIT; diff --git a/src/main/resources/sql/schema.sql b/src/main/resources/sql/schema.sql index b598e1e..5e65adc 100644 --- a/src/main/resources/sql/schema.sql +++ b/src/main/resources/sql/schema.sql @@ -1,47 +1,437 @@ -create table if not exists sys_company ( - id bigint primary key, - company_code varchar(64) not null unique, - company_name varchar(128) not null, - status varchar(32) not null, - created_at timestamp default current_timestamp, - updated_at timestamp default current_timestamp +-- Active: 1775801470429@@39.107.112.174@5432@lablesystem +begin; + +-- Drop Tables (按依赖关系倒序删除) +DROP TABLE IF EXISTS export_batch_item CASCADE; +DROP TABLE IF EXISTS export_batch CASCADE; +DROP TABLE IF EXISTS training_dataset CASCADE; +DROP TABLE IF EXISTS annotation_result_history CASCADE; +DROP TABLE IF EXISTS annotation_result CASCADE; +DROP TABLE IF EXISTS annotation_task_resource CASCADE; +DROP TABLE IF EXISTS annotation_task CASCADE; +DROP TABLE IF EXISTS source_resource CASCADE; +DROP TABLE IF EXISTS biz_data_record CASCADE; +DROP TABLE IF EXISTS sys_config CASCADE; +DROP TABLE IF EXISTS sys_menu CASCADE; +DROP TABLE IF EXISTS sys_user CASCADE; +DROP TABLE IF EXISTS sys_company CASCADE; + + +CREATE TABLE IF NOT EXISTS sys_company ( + id BIGINT PRIMARY KEY, + company_code VARCHAR(64) NOT NULL UNIQUE, + company_name VARCHAR(128) NOT NULL, + status VARCHAR(32) NOT NULL DEFAULT 'ENABLED', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -create table if not exists sys_user ( - id bigint primary key, - company_id bigint not null, - phone varchar(32) not null, - username varchar(64), - role varchar(32) not null, - position varchar(32) not null, - real_name varchar(64) not null, - password_hash varchar(255) not null, - must_change_password boolean not null default true, - status varchar(32) not null, - session_version integer not null default 1, - created_at timestamp default current_timestamp, - updated_at timestamp default current_timestamp, - constraint uq_sys_user_company_phone unique (company_id, phone) +COMMENT ON TABLE sys_company IS '公司表。'; +COMMENT ON COLUMN sys_company.id IS '公司主键ID。'; +COMMENT ON COLUMN sys_company.company_code IS '公司编码,登录时通过 companyCode 使用。'; +COMMENT ON COLUMN sys_company.company_name IS '公司名称。'; +COMMENT ON COLUMN sys_company.status IS '公司状态,默认 ENABLED,可按需改为 DISABLED 等状态。'; +COMMENT ON COLUMN sys_company.created_at IS '创建时间。'; +COMMENT ON COLUMN sys_company.updated_at IS '更新时间。'; + +CREATE TABLE IF NOT EXISTS sys_user ( + id BIGINT PRIMARY KEY, + company_id BIGINT NOT NULL, + phone VARCHAR(32) NOT NULL, + username VARCHAR(64), + role VARCHAR(32) NOT NULL DEFAULT 'EMPLOYEE', + position VARCHAR(32) NOT NULL DEFAULT 'ANNOTATOR', + real_name VARCHAR(64) NOT NULL, + password_hash VARCHAR(255) NOT NULL, + must_change_password BOOLEAN NOT NULL DEFAULT TRUE, + status VARCHAR(32) NOT NULL DEFAULT 'ENABLED', + session_version INTEGER NOT NULL DEFAULT 1, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT uq_sys_user_company_phone UNIQUE (company_id, phone), + CONSTRAINT fk_sys_user_company FOREIGN KEY (company_id) REFERENCES sys_company(id) ); -create table if not exists sys_menu ( - id bigint primary key, - company_id bigint not null, - permission_code varchar(64) not null, - menu_code varchar(64) not null, - menu_name varchar(128) not null, - path varchar(255) not null, - sort_order integer not null default 0, - created_at timestamp default current_timestamp, - updated_at timestamp default current_timestamp +COMMENT ON TABLE sys_user IS '用户表。role 表示数据权限角色,position 表示岗位。'; +COMMENT ON COLUMN sys_user.id IS '用户主键ID。'; +COMMENT ON COLUMN sys_user.company_id IS '所属公司ID,关联 sys_company.id。'; +COMMENT ON COLUMN sys_user.phone IS '登录手机号,同公司内唯一。'; +COMMENT ON COLUMN sys_user.username IS '用户名或账号别名,用于展示。'; +COMMENT ON COLUMN sys_user.role IS '数据权限角色,默认 EMPLOYEE,可选 EMPLOYEE、MANAGER、ENGINEER。'; +COMMENT ON COLUMN sys_user.position IS '岗位,默认 ANNOTATOR,可选 ANNOTATOR、DATA_TRAINER、REVIEWER、ADMIN。'; +COMMENT ON COLUMN sys_user.real_name IS '用户真实姓名。'; +COMMENT ON COLUMN sys_user.password_hash IS '密码哈希值。'; +COMMENT ON COLUMN sys_user.must_change_password IS '是否首次登录强制改密。'; +COMMENT ON COLUMN sys_user.status IS '用户状态,默认 ENABLED。'; +COMMENT ON COLUMN sys_user.session_version IS '会话版本号,用于强制旧 Token 失效。'; +COMMENT ON COLUMN sys_user.created_at IS '创建时间。'; +COMMENT ON COLUMN sys_user.updated_at IS '更新时间。'; + +CREATE TABLE IF NOT EXISTS sys_menu ( + id BIGINT PRIMARY KEY, + company_id BIGINT NOT NULL, + menu_code VARCHAR(64) NOT NULL, + menu_name VARCHAR(128) NOT NULL, + path VARCHAR(255) NOT NULL, + visible_positions VARCHAR(255) NOT NULL DEFAULT 'ADMIN', + sort_order INTEGER NOT NULL DEFAULT 0, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT fk_sys_menu_company FOREIGN KEY (company_id) REFERENCES sys_company(id) ); -create table if not exists biz_data_record ( - id bigint primary key, - company_id bigint not null, - creator_id bigint not null, - creator_role varchar(32) not null, - record_name varchar(255) not null, - created_at timestamp default current_timestamp, - updated_at timestamp default current_timestamp +COMMENT ON TABLE sys_menu IS '菜单表。'; +COMMENT ON COLUMN sys_menu.id IS '菜单主键ID。'; +COMMENT ON COLUMN sys_menu.company_id IS '所属公司ID。'; +COMMENT ON COLUMN sys_menu.menu_code IS '菜单编码。'; +COMMENT ON COLUMN sys_menu.menu_name IS '菜单名称。'; +COMMENT ON COLUMN sys_menu.path IS '前端路由路径。'; +COMMENT ON COLUMN sys_menu.visible_positions IS '可见岗位列表,使用逗号分隔存储,如 ANNOTATOR,DATA_TRAINER,REVIEWER,ADMIN。'; +COMMENT ON COLUMN sys_menu.sort_order IS '菜单排序号,默认 0。'; +COMMENT ON COLUMN sys_menu.created_at IS '创建时间。'; +COMMENT ON COLUMN sys_menu.updated_at IS '更新时间。'; + +CREATE TABLE IF NOT EXISTS sys_config ( + id BIGINT PRIMARY KEY, + company_id BIGINT NOT NULL, + config_type VARCHAR(32) NOT NULL DEFAULT 'SYSTEM', + config_name VARCHAR(128) NOT NULL, + config_value TEXT NOT NULL, + status VARCHAR(32) NOT NULL DEFAULT 'ENABLED', + creator_id BIGINT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT uq_sys_config_company_name UNIQUE (company_id, config_name), + CONSTRAINT fk_sys_config_company FOREIGN KEY (company_id) REFERENCES sys_company(id), + CONSTRAINT fk_sys_config_creator FOREIGN KEY (creator_id) REFERENCES sys_user(id) ); + +COMMENT ON TABLE sys_config IS '系统配置表,保存模型配置、Prompt 配置和系统参数配置。'; +COMMENT ON COLUMN sys_config.id IS '配置主键ID。'; +COMMENT ON COLUMN sys_config.company_id IS '所属公司ID。'; +COMMENT ON COLUMN sys_config.config_type IS '配置类型,默认 SYSTEM,可选 MODEL、PROMPT、SYSTEM。'; +COMMENT ON COLUMN sys_config.config_name IS '配置名称。MODEL 类型时存模型名如 qwen-max;PROMPT 类型时存名称如 extractPrompt、verifyPrompt;SYSTEM 类型时存参数名。'; +COMMENT ON COLUMN sys_config.config_value IS '配置值。MODEL 类型建议保存 JSON,至少包含 modelName、modelUrl、apiKey;PROMPT 类型保存提示词文本;SYSTEM 类型预留后续扩展。'; +COMMENT ON COLUMN sys_config.status IS '配置状态,默认 ENABLED。'; +COMMENT ON COLUMN sys_config.creator_id IS '创建人用户ID。'; +COMMENT ON COLUMN sys_config.created_at IS '创建时间。'; +COMMENT ON COLUMN sys_config.updated_at IS '更新时间。'; + +CREATE TABLE IF NOT EXISTS biz_data_record ( + id BIGINT PRIMARY KEY, + company_id BIGINT NOT NULL, + creator_id BIGINT NOT NULL, + creator_role VARCHAR(32) NOT NULL DEFAULT 'EMPLOYEE', + record_name VARCHAR(255) NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT fk_biz_data_record_company FOREIGN KEY (company_id) REFERENCES sys_company(id), + CONSTRAINT fk_biz_data_record_creator FOREIGN KEY (creator_id) REFERENCES sys_user(id) +); + +COMMENT ON TABLE biz_data_record IS '一期数据权限演示表。'; +COMMENT ON COLUMN biz_data_record.id IS '演示记录主键ID。'; +COMMENT ON COLUMN biz_data_record.company_id IS '所属公司ID。'; +COMMENT ON COLUMN biz_data_record.creator_id IS '创建人用户ID。'; +COMMENT ON COLUMN biz_data_record.creator_role IS '创建人数据权限角色,默认 EMPLOYEE。'; +COMMENT ON COLUMN biz_data_record.record_name IS '演示记录名称。'; +COMMENT ON COLUMN biz_data_record.created_at IS '创建时间。'; +COMMENT ON COLUMN biz_data_record.updated_at IS '更新时间。'; + +CREATE TABLE IF NOT EXISTS source_resource ( + id BIGINT PRIMARY KEY, + company_id BIGINT NOT NULL, + creator_id BIGINT NOT NULL, + creator_role VARCHAR(32) NOT NULL DEFAULT 'EMPLOYEE', + resource_name VARCHAR(255) NOT NULL, + resource_type VARCHAR(32) NOT NULL DEFAULT 'TEXT', + bucket_name VARCHAR(128) NOT NULL, + file_path VARCHAR(512) NOT NULL, + file_size BIGINT NOT NULL DEFAULT 0, + source_status VARCHAR(32) NOT NULL DEFAULT 'UPLOADED', + storage_provider VARCHAR(64) NOT NULL DEFAULT 'rustfs', + remark VARCHAR(255), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT fk_source_resource_company FOREIGN KEY (company_id) REFERENCES sys_company(id), + CONSTRAINT fk_source_resource_creator FOREIGN KEY (creator_id) REFERENCES sys_user(id) +); + +COMMENT ON TABLE source_resource IS '资源表,保存文本、图片、视频资源元数据。'; +COMMENT ON COLUMN source_resource.id IS '资源主键ID。'; +COMMENT ON COLUMN source_resource.company_id IS '所属公司ID。'; +COMMENT ON COLUMN source_resource.creator_id IS '上传人或创建人用户ID。'; +COMMENT ON COLUMN source_resource.creator_role IS '创建人数据权限角色,默认 EMPLOYEE。'; +COMMENT ON COLUMN source_resource.resource_name IS '资源名称。'; +COMMENT ON COLUMN source_resource.resource_type IS '资源类型,默认 TEXT,可选 TEXT、IMAGE、VIDEO。'; +COMMENT ON COLUMN source_resource.bucket_name IS '对象存储桶名称。'; +COMMENT ON COLUMN source_resource.file_path IS '文件存储路径,表示对象在存储系统中的实际路径。'; +COMMENT ON COLUMN source_resource.file_size IS '文件大小,单位字节,默认 0。'; +COMMENT ON COLUMN source_resource.source_status IS '资源状态,默认 UPLOADED,可选 PROCESSING、READY、ARCHIVED。'; +COMMENT ON COLUMN source_resource.storage_provider IS '存储提供方,默认 rustfs。'; +COMMENT ON COLUMN source_resource.remark IS '备注说明。'; +COMMENT ON COLUMN source_resource.created_at IS '创建时间。'; +COMMENT ON COLUMN source_resource.updated_at IS '更新时间。'; + +CREATE TABLE IF NOT EXISTS annotation_task ( + id BIGINT PRIMARY KEY, + company_id BIGINT NOT NULL, + creator_id BIGINT NOT NULL, + creator_role VARCHAR(32) NOT NULL DEFAULT 'EMPLOYEE', + task_name VARCHAR(255) NOT NULL, + industry_type VARCHAR(32) NOT NULL DEFAULT 'transport', + task_type VARCHAR(32) NOT NULL DEFAULT 'EXTRACT_QA', + extract_model_config_id BIGINT, + extract_model_name VARCHAR(128), + extract_model_url VARCHAR(255), + extract_model_api_key VARCHAR(255), + verify_model_config_id BIGINT, + verify_model_name VARCHAR(128), + verify_model_url VARCHAR(255), + verify_model_api_key VARCHAR(255), + extract_prompt_config_id BIGINT, + extract_prompt TEXT, + verify_prompt_config_id BIGINT, + verify_prompt TEXT, + task_status VARCHAR(32) NOT NULL DEFAULT 'PENDING', + is_deleted BOOLEAN NOT NULL DEFAULT FALSE, + started_at TIMESTAMP, + finished_at TIMESTAMP, + error_message TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT fk_annotation_task_company FOREIGN KEY (company_id) REFERENCES sys_company(id), + CONSTRAINT fk_annotation_task_creator FOREIGN KEY (creator_id) REFERENCES sys_user(id), + CONSTRAINT fk_annotation_task_extract_model_config FOREIGN KEY (extract_model_config_id) REFERENCES sys_config(id), + CONSTRAINT fk_annotation_task_verify_model_config FOREIGN KEY (verify_model_config_id) REFERENCES sys_config(id), + CONSTRAINT fk_annotation_task_extract_prompt_config FOREIGN KEY (extract_prompt_config_id) REFERENCES sys_config(id), + CONSTRAINT fk_annotation_task_verify_prompt_config FOREIGN KEY (verify_prompt_config_id) REFERENCES sys_config(id) +); + +COMMENT ON TABLE annotation_task IS '任务表,保存任务、配置引用与执行快照。'; +COMMENT ON COLUMN annotation_task.id IS '任务主键ID。'; +COMMENT ON COLUMN annotation_task.company_id IS '所属公司ID。'; +COMMENT ON COLUMN annotation_task.creator_id IS '任务创建人用户ID。'; +COMMENT ON COLUMN annotation_task.creator_role IS '任务创建人数据权限角色,默认 EMPLOYEE。'; +COMMENT ON COLUMN annotation_task.task_name IS '任务名称。'; +COMMENT ON COLUMN annotation_task.industry_type IS '行业类型简写,默认 transport,可选值按业务扩展,例如 electricity。'; +COMMENT ON COLUMN annotation_task.task_type IS '任务类型,默认 EXTRACT_QA。'; +COMMENT ON COLUMN annotation_task.extract_model_config_id IS '抽取模型配置ID,关联 sys_config.id。'; +COMMENT ON COLUMN annotation_task.extract_model_name IS '抽取模型名称。'; +COMMENT ON COLUMN annotation_task.extract_model_url IS '抽取模型调用地址。'; +COMMENT ON COLUMN annotation_task.extract_model_api_key IS '抽取模型调用密钥。'; +COMMENT ON COLUMN annotation_task.verify_model_config_id IS '校验模型配置ID,关联 sys_config.id。'; +COMMENT ON COLUMN annotation_task.verify_model_name IS '校验模型名称。'; +COMMENT ON COLUMN annotation_task.verify_model_url IS '校验模型调用地址。'; +COMMENT ON COLUMN annotation_task.verify_model_api_key IS '校验模型调用密钥。'; +COMMENT ON COLUMN annotation_task.extract_prompt_config_id IS '抽取Prompt配置ID,关联 sys_config.id。'; +COMMENT ON COLUMN annotation_task.extract_prompt IS '抽取 Prompt 文本。'; +COMMENT ON COLUMN annotation_task.verify_prompt_config_id IS '校验Prompt配置ID,关联 sys_config.id。'; +COMMENT ON COLUMN annotation_task.verify_prompt IS '校验 Prompt 文本。'; +COMMENT ON COLUMN annotation_task.task_status IS '任务状态,默认 PENDING,可选 RUNNING、COMPLETED、FAILED。'; +COMMENT ON COLUMN annotation_task.is_deleted IS '任务软删除标记,默认 FALSE。'; +COMMENT ON COLUMN annotation_task.started_at IS '任务开始时间。'; +COMMENT ON COLUMN annotation_task.finished_at IS '任务结束时间。'; +COMMENT ON COLUMN annotation_task.error_message IS '任务失败错误信息。'; +COMMENT ON COLUMN annotation_task.created_at IS '创建时间。'; +COMMENT ON COLUMN annotation_task.updated_at IS '更新时间。'; + +CREATE TABLE IF NOT EXISTS annotation_task_resource ( + id BIGINT PRIMARY KEY, + company_id BIGINT NOT NULL, + task_id BIGINT NOT NULL, + resource_id BIGINT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT uq_annotation_task_resource UNIQUE (task_id, resource_id), + CONSTRAINT fk_annotation_task_resource_company FOREIGN KEY (company_id) REFERENCES sys_company(id), + CONSTRAINT fk_annotation_task_resource_task FOREIGN KEY (task_id) REFERENCES annotation_task(id), + CONSTRAINT fk_annotation_task_resource_resource FOREIGN KEY (resource_id) REFERENCES source_resource(id) +); + +COMMENT ON TABLE annotation_task_resource IS '任务与资源关联表,一个任务可绑定多个资源。'; +COMMENT ON COLUMN annotation_task_resource.id IS '任务资源关联主键ID。'; +COMMENT ON COLUMN annotation_task_resource.company_id IS '所属公司ID。'; +COMMENT ON COLUMN annotation_task_resource.task_id IS '任务ID。'; +COMMENT ON COLUMN annotation_task_resource.resource_id IS '资源ID。'; +COMMENT ON COLUMN annotation_task_resource.created_at IS '创建时间。'; + +CREATE TABLE IF NOT EXISTS annotation_result ( + id BIGINT PRIMARY KEY, + company_id BIGINT NOT NULL, + creator_id BIGINT NOT NULL, + creator_role VARCHAR(32) NOT NULL DEFAULT 'EMPLOYEE', + task_id BIGINT NOT NULL, + resource_id BIGINT NOT NULL, + qa_content_json TEXT NOT NULL DEFAULT '{}', + qa_content_storage_mode VARCHAR(32) NOT NULL DEFAULT 'INLINE', + qa_content_file_path VARCHAR(512), + diff_summary TEXT NOT NULL DEFAULT '{}', + requires_manual_review BOOLEAN NOT NULL DEFAULT FALSE, + is_deleted BOOLEAN NOT NULL DEFAULT FALSE, + reviewer_id BIGINT, + review_comment TEXT, + reviewed_at TIMESTAMP, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT fk_annotation_result_company FOREIGN KEY (company_id) REFERENCES sys_company(id), + CONSTRAINT fk_annotation_result_creator FOREIGN KEY (creator_id) REFERENCES sys_user(id), + CONSTRAINT fk_annotation_result_task FOREIGN KEY (task_id) REFERENCES annotation_task(id), + CONSTRAINT fk_annotation_result_resource FOREIGN KEY (resource_id) REFERENCES source_resource(id), + CONSTRAINT fk_annotation_result_reviewer FOREIGN KEY (reviewer_id) REFERENCES sys_user(id) +); + +COMMENT ON TABLE annotation_result IS '当前标注结果表。'; +COMMENT ON COLUMN annotation_result.id IS '标注结果主键ID。'; +COMMENT ON COLUMN annotation_result.company_id IS '所属公司ID。'; +COMMENT ON COLUMN annotation_result.creator_id IS '结果创建人用户ID。'; +COMMENT ON COLUMN annotation_result.creator_role IS '结果创建人数据权限角色,默认 EMPLOYEE。'; +COMMENT ON COLUMN annotation_result.task_id IS '关联任务ID。'; +COMMENT ON COLUMN annotation_result.resource_id IS '关联资源ID。'; +COMMENT ON COLUMN annotation_result.qa_content_json IS '问答内容 JSON 字符串。字段类型为 TEXT,建议结构为 {\"question\":\"...\",\"answer\":\"...\"}。中小体积内容默认直接入库。'; +COMMENT ON COLUMN annotation_result.qa_content_storage_mode IS '问答内容存储模式,默认 INLINE,可选 INLINE、EXTERNAL。当完整问答内容较大时,可设为 EXTERNAL,仅在表内保留摘要或索引信息。'; +COMMENT ON COLUMN annotation_result.qa_content_file_path IS '当 qa_content_storage_mode = EXTERNAL 时,记录外置问答内容文件路径。'; +COMMENT ON COLUMN annotation_result.diff_summary IS '差异摘要 JSON 字符串。字段类型为 TEXT,建议结构为 {\"extract_question\":\"...\",\"extract_answer\":\"...\",\"verify_answer\":\"...\",\"mismatch_fields\":[\"question\",\"answer\"],\"reason\":\"...\"}。'; +COMMENT ON COLUMN annotation_result.requires_manual_review IS '是否需要人工审核,默认 FALSE。'; +COMMENT ON COLUMN annotation_result.is_deleted IS '软删除标记,默认 FALSE。'; +COMMENT ON COLUMN annotation_result.reviewer_id IS '审核人用户ID。'; +COMMENT ON COLUMN annotation_result.review_comment IS '审核意见。'; +COMMENT ON COLUMN annotation_result.reviewed_at IS '审核时间。'; +COMMENT ON COLUMN annotation_result.created_at IS '创建时间。'; +COMMENT ON COLUMN annotation_result.updated_at IS '更新时间。'; + +CREATE TABLE IF NOT EXISTS annotation_result_history ( + id BIGINT PRIMARY KEY, + company_id BIGINT NOT NULL, + creator_id BIGINT NOT NULL, + creator_role VARCHAR(32) NOT NULL DEFAULT 'EMPLOYEE', + source_result_id BIGINT, + task_id BIGINT NOT NULL, + resource_id BIGINT NOT NULL, + qa_content_json TEXT NOT NULL DEFAULT '{}', + qa_content_storage_mode VARCHAR(32) NOT NULL DEFAULT 'INLINE', + qa_content_file_path VARCHAR(512), + archive_reason VARCHAR(255), + archived_by BIGINT, + archived_at TIMESTAMP, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT fk_annotation_result_history_company FOREIGN KEY (company_id) REFERENCES sys_company(id), + CONSTRAINT fk_annotation_result_history_creator FOREIGN KEY (creator_id) REFERENCES sys_user(id), + CONSTRAINT fk_annotation_result_history_result FOREIGN KEY (source_result_id) REFERENCES annotation_result(id), + CONSTRAINT fk_annotation_result_history_task FOREIGN KEY (task_id) REFERENCES annotation_task(id), + CONSTRAINT fk_annotation_result_history_resource FOREIGN KEY (resource_id) REFERENCES source_resource(id), + CONSTRAINT fk_annotation_result_history_archived_by FOREIGN KEY (archived_by) REFERENCES sys_user(id) +); + +COMMENT ON TABLE annotation_result_history IS '历史归档结果表。'; +COMMENT ON COLUMN annotation_result_history.id IS '历史结果主键ID。'; +COMMENT ON COLUMN annotation_result_history.company_id IS '所属公司ID。'; +COMMENT ON COLUMN annotation_result_history.creator_id IS '历史记录创建人用户ID。'; +COMMENT ON COLUMN annotation_result_history.creator_role IS '历史记录创建人数据权限角色,默认 EMPLOYEE。'; +COMMENT ON COLUMN annotation_result_history.source_result_id IS '来源运行态结果ID。'; +COMMENT ON COLUMN annotation_result_history.task_id IS '关联任务ID。'; +COMMENT ON COLUMN annotation_result_history.resource_id IS '关联资源ID。'; +COMMENT ON COLUMN annotation_result_history.qa_content_json IS '归档后的问答内容 JSON 字符串。字段类型为 TEXT,建议结构为 {"question":"...","answer":"..."}。'; +COMMENT ON COLUMN annotation_result_history.qa_content_storage_mode IS '归档后的问答内容存储模式,默认 INLINE,可选 INLINE、EXTERNAL。'; +COMMENT ON COLUMN annotation_result_history.qa_content_file_path IS '当 qa_content_storage_mode = EXTERNAL 时,记录归档后的外置问答内容文件路径。'; +COMMENT ON COLUMN annotation_result_history.archive_reason IS '归档原因说明。'; +COMMENT ON COLUMN annotation_result_history.archived_by IS '归档操作人用户ID。'; +COMMENT ON COLUMN annotation_result_history.archived_at IS '归档时间。'; +COMMENT ON COLUMN annotation_result_history.created_at IS '创建时间。'; + +CREATE TABLE IF NOT EXISTS training_dataset ( + id BIGINT PRIMARY KEY, + company_id BIGINT NOT NULL, + creator_id BIGINT NOT NULL, + creator_role VARCHAR(32) NOT NULL DEFAULT 'EMPLOYEE', + result_history_id BIGINT NOT NULL, + sample_type VARCHAR(32) NOT NULL DEFAULT 'TEXT', + glm_format_json TEXT NOT NULL, + dataset_status VARCHAR(32) NOT NULL DEFAULT 'DRAFT', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT fk_training_dataset_company FOREIGN KEY (company_id) REFERENCES sys_company(id), + CONSTRAINT fk_training_dataset_creator FOREIGN KEY (creator_id) REFERENCES sys_user(id), + CONSTRAINT fk_training_dataset_result_history FOREIGN KEY (result_history_id) REFERENCES annotation_result_history(id) +); + +COMMENT ON TABLE training_dataset IS '训练样本表。'; +COMMENT ON COLUMN training_dataset.id IS '训练样本主键ID。'; +COMMENT ON COLUMN training_dataset.company_id IS '所属公司ID。'; +COMMENT ON COLUMN training_dataset.creator_id IS '样本创建人用户ID。'; +COMMENT ON COLUMN training_dataset.creator_role IS '样本创建人数据权限角色,默认 EMPLOYEE。'; +COMMENT ON COLUMN training_dataset.result_history_id IS '来源历史结果ID。'; +COMMENT ON COLUMN training_dataset.sample_type IS '样本类型,默认 TEXT,可按需扩展 IMAGE、VIDEO。'; +COMMENT ON COLUMN training_dataset.glm_format_json IS 'GLM微调格式 JSON 字符串。'; +COMMENT ON COLUMN training_dataset.dataset_status IS '样本状态,默认 DRAFT,可选 EXPORTED。'; +COMMENT ON COLUMN training_dataset.created_at IS '创建时间。'; +COMMENT ON COLUMN training_dataset.updated_at IS '更新时间。'; + +CREATE TABLE IF NOT EXISTS export_batch ( + id BIGINT PRIMARY KEY, + company_id BIGINT NOT NULL, + creator_id BIGINT NOT NULL, + creator_role VARCHAR(32) NOT NULL DEFAULT 'EMPLOYEE', + batch_no VARCHAR(64) NOT NULL UNIQUE, + dataset_file_path VARCHAR(512), + sample_count INTEGER NOT NULL DEFAULT 0, + finetune_job_id VARCHAR(128), + finetune_status VARCHAR(32) NOT NULL DEFAULT 'NOT_STARTED', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT fk_export_batch_company FOREIGN KEY (company_id) REFERENCES sys_company(id), + CONSTRAINT fk_export_batch_creator FOREIGN KEY (creator_id) REFERENCES sys_user(id) +); + +COMMENT ON TABLE export_batch IS '导出批次表。'; +COMMENT ON COLUMN export_batch.id IS '导出批次主键ID。'; +COMMENT ON COLUMN export_batch.company_id IS '所属公司ID。'; +COMMENT ON COLUMN export_batch.creator_id IS '批次创建人用户ID。'; +COMMENT ON COLUMN export_batch.creator_role IS '批次创建人数据权限角色,默认 EMPLOYEE。'; +COMMENT ON COLUMN export_batch.batch_no IS '批次编号,全局唯一。'; +COMMENT ON COLUMN export_batch.dataset_file_path IS '导出的数据文件路径。'; +COMMENT ON COLUMN export_batch.sample_count IS '批次包含的样本数量,默认 0。'; +COMMENT ON COLUMN export_batch.finetune_job_id IS '微调任务ID。'; +COMMENT ON COLUMN export_batch.finetune_status IS '微调状态,默认 NOT_STARTED,可选 RUNNING、SUCCESS、FAILED。'; +COMMENT ON COLUMN export_batch.created_at IS '创建时间。'; +COMMENT ON COLUMN export_batch.updated_at IS '更新时间。'; + +CREATE TABLE IF NOT EXISTS export_batch_item ( + id BIGINT PRIMARY KEY, + batch_id BIGINT NOT NULL, + dataset_id BIGINT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT fk_export_batch_item_batch FOREIGN KEY (batch_id) REFERENCES export_batch(id), + CONSTRAINT fk_export_batch_item_dataset FOREIGN KEY (dataset_id) REFERENCES training_dataset(id), + CONSTRAINT uq_export_batch_item UNIQUE (batch_id, dataset_id) +); + +COMMENT ON TABLE export_batch_item IS '导出批次与训练样本关系表。'; +COMMENT ON COLUMN export_batch_item.id IS '批次样本关系主键ID。'; +COMMENT ON COLUMN export_batch_item.batch_id IS '关联导出批次ID。'; +COMMENT ON COLUMN export_batch_item.dataset_id IS '关联训练样本ID。'; +COMMENT ON COLUMN export_batch_item.created_at IS '创建时间。'; + +CREATE INDEX IF NOT EXISTS idx_sys_user_company ON sys_user(company_id); +CREATE INDEX IF NOT EXISTS idx_sys_user_role ON sys_user(company_id, role); +CREATE INDEX IF NOT EXISTS idx_sys_user_position ON sys_user(company_id, position); +CREATE INDEX IF NOT EXISTS idx_sys_menu_company_sort ON sys_menu(company_id, sort_order); +CREATE INDEX IF NOT EXISTS idx_sys_config_company_type ON sys_config(company_id, config_type); +CREATE INDEX IF NOT EXISTS idx_biz_data_record_company_role ON biz_data_record(company_id, creator_role); +CREATE INDEX IF NOT EXISTS idx_source_resource_company_type ON source_resource(company_id, resource_type); +CREATE INDEX IF NOT EXISTS idx_source_resource_company_status ON source_resource(company_id, source_status); +CREATE INDEX IF NOT EXISTS idx_source_resource_creator ON source_resource(company_id, creator_id); +CREATE INDEX IF NOT EXISTS idx_annotation_task_company_status ON annotation_task(company_id, task_status); +CREATE INDEX IF NOT EXISTS idx_annotation_task_company_deleted ON annotation_task(company_id, is_deleted); +CREATE INDEX IF NOT EXISTS idx_annotation_task_creator ON annotation_task(company_id, creator_id); +CREATE INDEX IF NOT EXISTS idx_annotation_task_resource_company_task ON annotation_task_resource(company_id, task_id); +CREATE INDEX IF NOT EXISTS idx_annotation_task_resource_company_resource ON annotation_task_resource(company_id, resource_id); +CREATE INDEX IF NOT EXISTS idx_annotation_result_company_deleted ON annotation_result(company_id, is_deleted); +CREATE INDEX IF NOT EXISTS idx_annotation_result_company_manual ON annotation_result(company_id, requires_manual_review); +CREATE INDEX IF NOT EXISTS idx_annotation_result_task ON annotation_result(company_id, task_id); +CREATE INDEX IF NOT EXISTS idx_annotation_result_history_company ON annotation_result_history(company_id); +CREATE INDEX IF NOT EXISTS idx_annotation_result_history_task ON annotation_result_history(company_id, task_id); +CREATE INDEX IF NOT EXISTS idx_annotation_result_history_resource ON annotation_result_history(company_id, resource_id); +CREATE INDEX IF NOT EXISTS idx_training_dataset_company_status ON training_dataset(company_id, dataset_status); +CREATE INDEX IF NOT EXISTS idx_export_batch_company_status ON export_batch(company_id, finetune_status); + +COMMIT;