fix(aihr): scope sop data to tenant
This commit is contained in:
+55
-50
@@ -37,6 +37,7 @@ import org.dromara.aihr.domain.AihrSopDto.UploadResponse;
|
||||
import org.dromara.aihr.domain.AihrSopDto.VectorIndexStatusResponse;
|
||||
import org.dromara.aihr.domain.AihrSopDto.VectorizeResponse;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.apache.tika.metadata.Metadata;
|
||||
import org.apache.tika.metadata.TikaCoreProperties;
|
||||
import org.apache.tika.parser.AutoDetectParser;
|
||||
@@ -205,7 +206,7 @@ public class AihrSopSeedService {
|
||||
firstNonBlank(request == null ? null : request.note(), ""),
|
||||
Timestamp.valueOf(java.time.LocalDateTime.now()),
|
||||
Timestamp.valueOf(java.time.LocalDateTime.now()),
|
||||
TENANT_ID,
|
||||
tenantId(),
|
||||
id
|
||||
);
|
||||
return updated == 0 ? null : sopReview(id);
|
||||
@@ -244,7 +245,7 @@ public class AihrSopSeedService {
|
||||
review_status = VALUES(review_status),
|
||||
update_time = VALUES(update_time)
|
||||
""",
|
||||
TENANT_ID,
|
||||
tenantId(),
|
||||
truncate(queryText, 1000),
|
||||
queryNorm,
|
||||
fragmentId,
|
||||
@@ -285,7 +286,7 @@ public class AihrSopSeedService {
|
||||
rs.getString("review_status"),
|
||||
formatTimestamp(rs.getTimestamp("create_time")),
|
||||
formatTimestamp(rs.getTimestamp("update_time"))
|
||||
), TENANT_ID, reviewStatus, reviewStatus, limitValue);
|
||||
), tenantId(), reviewStatus, reviewStatus, limitValue);
|
||||
}
|
||||
|
||||
public AnswerFeedbackReviewResponse reviewAnswerFeedback(Long id) {
|
||||
@@ -299,7 +300,7 @@ public class AihrSopSeedService {
|
||||
WHERE tenant_id = ? AND id = ?
|
||||
""",
|
||||
Timestamp.valueOf(java.time.LocalDateTime.now()),
|
||||
TENANT_ID,
|
||||
tenantId(),
|
||||
id
|
||||
);
|
||||
return updated == 0 ? null : new AnswerFeedbackReviewResponse(id, "已复核");
|
||||
@@ -397,7 +398,7 @@ public class AihrSopSeedService {
|
||||
rs.getLong("knowledge_id"),
|
||||
rs.getString("category"),
|
||||
rs.getString("doc_id")
|
||||
), TENANT_ID);
|
||||
), tenantId());
|
||||
|
||||
int documents = 0;
|
||||
int fragments = 0;
|
||||
@@ -408,7 +409,7 @@ public class AihrSopSeedService {
|
||||
from aihr_knowledge_fragment
|
||||
where tenant_id = ? and knowledge_id = ? and doc_id = ?
|
||||
order by idx asc
|
||||
""", String.class, TENANT_ID, target.knowledgeId(), target.docId());
|
||||
""", String.class, tenantId(), target.knowledgeId(), target.docId());
|
||||
EmbeddingBatch batch = embedFragments(target.knowledgeId(), target.category(), target.docId(), rows);
|
||||
if (batch.count() > 0) {
|
||||
documents++;
|
||||
@@ -452,7 +453,7 @@ public class AihrSopSeedService {
|
||||
update aihr_knowledge_fragment
|
||||
set embedding_json = null, embedding_model = null, embedding_time = null, update_time = now()
|
||||
where tenant_id = ?
|
||||
""", TENANT_ID);
|
||||
""", tenantId());
|
||||
deleteQdrantCollection();
|
||||
return vectorizeMissing();
|
||||
}
|
||||
@@ -492,7 +493,7 @@ public class AihrSopSeedService {
|
||||
rs.getTimestamp("create_time"),
|
||||
rs.getTimestamp("update_time"),
|
||||
rs.getString("remark")
|
||||
), TENANT_ID);
|
||||
), tenantId());
|
||||
} catch (DataAccessException e) {
|
||||
return List.of();
|
||||
}
|
||||
@@ -506,7 +507,7 @@ public class AihrSopSeedService {
|
||||
update aihr_knowledge_import_task
|
||||
set status = 4, current_file = '', finished_time = now(), update_time = now(), remark = '用户已取消'
|
||||
where tenant_id = ? and id = ? and status = 1
|
||||
""", TENANT_ID, taskId);
|
||||
""", tenantId(), taskId);
|
||||
return localImportTask(taskId).orElseThrow(() -> new IllegalArgumentException("导入任务不存在"));
|
||||
}
|
||||
|
||||
@@ -605,7 +606,7 @@ public class AihrSopSeedService {
|
||||
(tenant_id, directory, category, status, total_files, imported_files, failed_files, current_file, started_time, create_time, update_time, remark)
|
||||
values (?, ?, ?, 1, ?, 0, 0, '', now(), now(), now(), '任务已启动')
|
||||
""", Statement.RETURN_GENERATED_KEYS);
|
||||
ps.setString(1, TENANT_ID);
|
||||
ps.setString(1, tenantId());
|
||||
ps.setString(2, plan.directory());
|
||||
ps.setString(3, plan.category());
|
||||
ps.setInt(4, plan.files().size());
|
||||
@@ -633,7 +634,7 @@ public class AihrSopSeedService {
|
||||
rs.getTimestamp("create_time"),
|
||||
rs.getTimestamp("update_time"),
|
||||
rs.getString("remark")
|
||||
), TENANT_ID, taskId);
|
||||
), tenantId(), taskId);
|
||||
return rows.stream().findFirst();
|
||||
}
|
||||
|
||||
@@ -642,7 +643,7 @@ public class AihrSopSeedService {
|
||||
update aihr_knowledge_import_task
|
||||
set status = ?, imported_files = ?, failed_files = ?, current_file = ?, update_time = now(), remark = ?
|
||||
where tenant_id = ? and id = ? and status <> 4
|
||||
""", status, imported, failed, currentFile, limitRemark(remark), TENANT_ID, taskId);
|
||||
""", status, imported, failed, currentFile, limitRemark(remark), tenantId(), taskId);
|
||||
}
|
||||
|
||||
private void finishLocalImportTask(long taskId, int status, int imported, int failed, String remark) {
|
||||
@@ -650,7 +651,7 @@ public class AihrSopSeedService {
|
||||
update aihr_knowledge_import_task
|
||||
set status = ?, imported_files = ?, failed_files = ?, current_file = '', finished_time = now(), update_time = now(), remark = ?
|
||||
where tenant_id = ? and id = ? and status <> 4
|
||||
""", status, imported, failed, limitRemark(remark), TENANT_ID, taskId);
|
||||
""", status, imported, failed, limitRemark(remark), tenantId(), taskId);
|
||||
}
|
||||
|
||||
private boolean isLocalImportTaskCanceled(long taskId) {
|
||||
@@ -659,7 +660,7 @@ public class AihrSopSeedService {
|
||||
select count(1)
|
||||
from aihr_knowledge_import_task
|
||||
where tenant_id = ? and id = ? and status = 4
|
||||
""", Integer.class, TENANT_ID, taskId);
|
||||
""", Integer.class, tenantId(), taskId);
|
||||
return count != null && count > 0;
|
||||
} catch (DataAccessException e) {
|
||||
return false;
|
||||
@@ -740,13 +741,13 @@ public class AihrSopSeedService {
|
||||
}
|
||||
|
||||
deleteQdrantDoc(config.knowledgeId(), docId);
|
||||
jdbcTemplate.update("delete from aihr_knowledge_fragment where tenant_id = ? and knowledge_id = ? and doc_id = ?", TENANT_ID, config.knowledgeId(), docId);
|
||||
jdbcTemplate.update("delete from aihr_knowledge_fragment where tenant_id = ? and knowledge_id = ? and doc_id = ?", tenantId(), config.knowledgeId(), docId);
|
||||
for (int i = 0; i < fragments.size(); i++) {
|
||||
jdbcTemplate.update("""
|
||||
insert into aihr_knowledge_fragment
|
||||
(tenant_id, knowledge_id, idx, doc_id, content, create_time, update_time, remark)
|
||||
values (?, ?, ?, ?, ?, now(), now(), ?)
|
||||
""", TENANT_ID, config.knowledgeId(), i + 1, docId, fragments.get(i), "upload:" + fileName);
|
||||
""", tenantId(), config.knowledgeId(), i + 1, docId, fragments.get(i), "upload:" + fileName);
|
||||
}
|
||||
embedFragments(config.knowledgeId(), knowledgeName, docId, fragments);
|
||||
updateOssInsight(ossId, insight, fingerprint);
|
||||
@@ -883,7 +884,7 @@ public class AihrSopSeedService {
|
||||
where tenant_id = ?
|
||||
order by update_time desc, id desc
|
||||
limit 50
|
||||
""", (rs, rowNum) -> new CategoryOption(rs.getString("name"), rs.getString("description")), TENANT_ID);
|
||||
""", (rs, rowNum) -> new CategoryOption(rs.getString("name"), rs.getString("description")), tenantId());
|
||||
} catch (DataAccessException e) {
|
||||
return List.of();
|
||||
}
|
||||
@@ -908,7 +909,7 @@ public class AihrSopSeedService {
|
||||
rs.getString("model_name"),
|
||||
rs.getString("resolved_api_host"),
|
||||
rs.getString("resolved_api_key")
|
||||
), TENANT_ID);
|
||||
), tenantId());
|
||||
return rows.stream()
|
||||
.filter(runtime -> !isBlank(runtime.modelName()) && !isBlank(runtime.baseUrl()))
|
||||
.findFirst();
|
||||
@@ -938,7 +939,7 @@ public class AihrSopSeedService {
|
||||
rs.getString("model_name"),
|
||||
rs.getString("resolved_api_host"),
|
||||
rs.getString("resolved_api_key")
|
||||
), TENANT_ID);
|
||||
), tenantId());
|
||||
return rows.stream()
|
||||
.filter(runtime -> !isBlank(runtime.modelName()) && !isBlank(runtime.baseUrl()))
|
||||
.findFirst();
|
||||
@@ -1015,7 +1016,7 @@ public class AihrSopSeedService {
|
||||
where a.tenant_id = ? and a.id = ?
|
||||
limit 1
|
||||
""", (rs, rowNum) -> insightFromExt(rs.getString("ext1"), firstNonBlank(rs.getString("category"), fallbackCategory)),
|
||||
TENANT_ID, hit.attachId()));
|
||||
tenantId(), hit.attachId()));
|
||||
} catch (DataAccessException e) {
|
||||
return Optional.empty();
|
||||
}
|
||||
@@ -1061,7 +1062,7 @@ public class AihrSopSeedService {
|
||||
rs.getString("doc_id"),
|
||||
rs.getLong("oss_id"),
|
||||
rs.getString("match_type")
|
||||
), fingerprint.fileSha256(), fingerprint.textSha256(), TENANT_ID, fingerprint.fileSha256(), fingerprint.textSha256(), fileName, fingerprint.sizeBytes(), fingerprint.fileSha256(), fingerprint.textSha256());
|
||||
), fingerprint.fileSha256(), fingerprint.textSha256(), tenantId(), fingerprint.fileSha256(), fingerprint.textSha256(), fileName, fingerprint.sizeBytes(), fingerprint.fileSha256(), fingerprint.textSha256());
|
||||
} catch (DataAccessException e) {
|
||||
return List.of();
|
||||
}
|
||||
@@ -1071,13 +1072,13 @@ public class AihrSopSeedService {
|
||||
String docId = isBlank(hit.docId()) ? UUID.randomUUID().toString().replace("-", "") : hit.docId();
|
||||
if (hit.knowledgeId() != targetKnowledgeId && !isBlank(hit.docId())) {
|
||||
deleteQdrantDoc(hit.knowledgeId(), hit.docId());
|
||||
jdbcTemplate.update("delete from aihr_knowledge_fragment where tenant_id = ? and knowledge_id = ? and doc_id = ?", TENANT_ID, hit.knowledgeId(), hit.docId());
|
||||
jdbcTemplate.update("delete from aihr_knowledge_fragment where tenant_id = ? and knowledge_id = ? and doc_id = ?", tenantId(), hit.knowledgeId(), hit.docId());
|
||||
}
|
||||
jdbcTemplate.update("""
|
||||
update aihr_knowledge_attach
|
||||
set knowledge_id = ?, oss_id = ?, doc_id = ?, type = ?, status = 1, update_time = now(), remark = 'duplicate:reusing'
|
||||
where tenant_id = ? and id = ?
|
||||
""", targetKnowledgeId, hit.ossId(), docId, fileType(fileName), TENANT_ID, hit.attachId());
|
||||
""", targetKnowledgeId, hit.ossId(), docId, fileType(fileName), tenantId(), hit.attachId());
|
||||
return docId;
|
||||
}
|
||||
|
||||
@@ -1088,9 +1089,9 @@ public class AihrSopSeedService {
|
||||
}
|
||||
if (!isBlank(hit.docId())) {
|
||||
deleteQdrantDoc(hit.knowledgeId(), hit.docId());
|
||||
jdbcTemplate.update("delete from aihr_knowledge_fragment where tenant_id = ? and knowledge_id = ? and doc_id = ?", TENANT_ID, hit.knowledgeId(), hit.docId());
|
||||
jdbcTemplate.update("delete from aihr_knowledge_fragment where tenant_id = ? and knowledge_id = ? and doc_id = ?", tenantId(), hit.knowledgeId(), hit.docId());
|
||||
}
|
||||
jdbcTemplate.update("delete from aihr_knowledge_attach where tenant_id = ? and id = ?", TENANT_ID, hit.attachId());
|
||||
jdbcTemplate.update("delete from aihr_knowledge_attach where tenant_id = ? and id = ?", tenantId(), hit.attachId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1203,7 +1204,7 @@ public class AihrSopSeedService {
|
||||
insight.tags(),
|
||||
insight.reason()
|
||||
);
|
||||
}, TENANT_ID);
|
||||
}, tenantId());
|
||||
} catch (DataAccessException e) {
|
||||
return List.of();
|
||||
}
|
||||
@@ -1578,7 +1579,7 @@ public class AihrSopSeedService {
|
||||
update aihr_knowledge_fragment
|
||||
set embedding_json = ?, embedding_model = ?, embedding_time = now(), update_time = now()
|
||||
where tenant_id = ? and knowledge_id = ? and doc_id = ? and idx = ?
|
||||
""", embeddings.get(i), runtime.modelName(), TENANT_ID, knowledgeId, docId, i + 1);
|
||||
""", embeddings.get(i), runtime.modelName(), tenantId(), knowledgeId, docId, i + 1);
|
||||
}
|
||||
try {
|
||||
upsertQdrant(knowledgeId, category, docId, fragments, embeddings, runtime.modelName());
|
||||
@@ -1612,7 +1613,7 @@ public class AihrSopSeedService {
|
||||
rs.getString("resolved_api_host"),
|
||||
rs.getString("resolved_api_key"),
|
||||
rs.getInt("model_dimension")
|
||||
), TENANT_ID).stream()
|
||||
), tenantId()).stream()
|
||||
.filter(runtime -> configured(runtime.baseUrl(), runtime.modelName(), runtime.apiKey()))
|
||||
.toList());
|
||||
} catch (DataAccessException ignored) {
|
||||
@@ -1722,7 +1723,7 @@ public class AihrSopSeedService {
|
||||
rs.getString("model_name"),
|
||||
rs.getString("resolved_api_host"),
|
||||
rs.getString("resolved_api_key")
|
||||
), TENANT_ID);
|
||||
), tenantId());
|
||||
return rows.stream()
|
||||
.filter(runtime -> !isBlank(runtime.modelName()) && !isBlank(runtime.baseUrl()) && !isBlank(runtime.apiKey()))
|
||||
.findFirst();
|
||||
@@ -1814,7 +1815,7 @@ public class AihrSopSeedService {
|
||||
(tenant_id, query_text, category, position, source, status, create_time)
|
||||
VALUES (?, ?, ?, ?, ?, '待补充', ?)
|
||||
""",
|
||||
TENANT_ID,
|
||||
tenantId(),
|
||||
queryText.trim(),
|
||||
firstNonBlank(category, "sop"),
|
||||
firstNonBlank(position, "生活顾问"),
|
||||
@@ -1858,7 +1859,7 @@ public class AihrSopSeedService {
|
||||
(tenant_id, query_text, category, answer_text, reference_text, snippet_count, source, prompt_version, status, create_time, update_time)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, '待评审', ?, ?)
|
||||
""", Statement.RETURN_GENERATED_KEYS);
|
||||
ps.setString(1, TENANT_ID);
|
||||
ps.setString(1, tenantId());
|
||||
ps.setString(2, truncate(response.queryText(), 1000));
|
||||
ps.setString(3, firstNonBlank(response.category(), "sop"));
|
||||
ps.setString(4, truncate(response.answer(), 2000));
|
||||
@@ -1893,7 +1894,7 @@ public class AihrSopSeedService {
|
||||
reviewedTime == null ? "" : reviewedTime.toLocalDateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")),
|
||||
firstNonBlank(rs.getString("prompt_version"), "unknown")
|
||||
);
|
||||
}, TENANT_ID, id);
|
||||
}, tenantId(), id);
|
||||
return rows.isEmpty() ? null : rows.get(0);
|
||||
}
|
||||
|
||||
@@ -1903,7 +1904,7 @@ public class AihrSopSeedService {
|
||||
SELECT COUNT(*) AS total, COALESCE(SUM(CASE WHEN usable = 1 THEN 1 ELSE 0 END), 0) AS usable
|
||||
FROM aihr_sop_answer_review
|
||||
WHERE tenant_id = ? AND status = '已评审'
|
||||
""", (rs, rowNum) -> new SopReviewStats(rs.getInt("total"), rs.getInt("usable")), TENANT_ID);
|
||||
""", (rs, rowNum) -> new SopReviewStats(rs.getInt("total"), rs.getInt("usable")), tenantId());
|
||||
}
|
||||
|
||||
public void prepareSopReviewStats() {
|
||||
@@ -1918,7 +1919,7 @@ public class AihrSopSeedService {
|
||||
WHERE tenant_id = ? AND status = '已评审'
|
||||
AND reviewed_time >= ? AND reviewed_time < ?
|
||||
""", (rs, rowNum) -> new SopReviewStats(rs.getInt("total"), rs.getInt("usable")),
|
||||
TENANT_ID, Timestamp.valueOf(startTime), Timestamp.valueOf(endTime));
|
||||
tenantId(), Timestamp.valueOf(startTime), Timestamp.valueOf(endTime));
|
||||
}
|
||||
|
||||
private void ensureSopReviewTable() {
|
||||
@@ -2105,7 +2106,7 @@ public class AihrSopSeedService {
|
||||
WHERE tenant_id = ?
|
||||
AND query_norm = ?
|
||||
AND verdict = 'down'
|
||||
""", Long.class, TENANT_ID, queryNorm));
|
||||
""", Long.class, tenantId(), queryNorm));
|
||||
} catch (DataAccessException ignored) {
|
||||
return Set.of();
|
||||
}
|
||||
@@ -2160,7 +2161,7 @@ public class AihrSopSeedService {
|
||||
rs.getString("content"),
|
||||
rs.getInt("idx"),
|
||||
rs.getDouble("score")
|
||||
), queryText, TENANT_ID, category, category, queryText, limit);
|
||||
), queryText, tenantId(), category, category, queryText, limit);
|
||||
}
|
||||
|
||||
private List<KnowledgeHit> likeHits(String category, String queryText, int limit) {
|
||||
@@ -2194,7 +2195,7 @@ public class AihrSopSeedService {
|
||||
""".formatted(String.join(" + ", scoreParts), String.join(" or ", whereParts));
|
||||
List<Object> args = new ArrayList<>();
|
||||
terms.stream().map(AihrSopSeedService::likePattern).forEach(args::add);
|
||||
args.add(TENANT_ID);
|
||||
args.add(tenantId());
|
||||
args.add(category);
|
||||
args.add(category);
|
||||
terms.stream().map(AihrSopSeedService::likePattern).forEach(args::add);
|
||||
@@ -2236,7 +2237,7 @@ public class AihrSopSeedService {
|
||||
rs.getString("content"),
|
||||
rs.getInt("idx"),
|
||||
match.score()
|
||||
), TENANT_ID, match.docId(), match.idx());
|
||||
), tenantId(), match.docId(), match.idx());
|
||||
return rows.stream().findFirst();
|
||||
}
|
||||
|
||||
@@ -2453,7 +2454,7 @@ public class AihrSopSeedService {
|
||||
firstNonBlank(rs.getString("system_prompt"), SOP_ANSWER_SYSTEM_PROMPT),
|
||||
firstNonBlank(rs.getString("template"), SOP_ANSWER_USER_TEMPLATE),
|
||||
"sop_answer:" + firstNonBlank(rs.getString("version"), "v1")
|
||||
), TENANT_ID);
|
||||
), tenantId());
|
||||
if (!rows.isEmpty()) {
|
||||
return rows.get(0);
|
||||
}
|
||||
@@ -2682,7 +2683,7 @@ public class AihrSopSeedService {
|
||||
rs.getLong("id"),
|
||||
Math.max(200, rs.getInt("block_size")),
|
||||
Math.max(0, Math.min(rs.getInt("overlap_char"), 200))
|
||||
), TENANT_ID, category);
|
||||
), tenantId(), category);
|
||||
if (!rows.isEmpty()) {
|
||||
return rows.get(0);
|
||||
}
|
||||
@@ -2695,7 +2696,7 @@ public class AihrSopSeedService {
|
||||
text_block_size, enable_hybrid, hybrid_alpha, system_prompt, create_time, update_time, remark)
|
||||
values (?, ?, ?, ?, 120, 5, 0.5, 800, 1, 1.0, ?, now(), now(), 'upload')
|
||||
""", Statement.RETURN_GENERATED_KEYS);
|
||||
ps.setString(1, TENANT_ID);
|
||||
ps.setString(1, tenantId());
|
||||
ps.setString(2, category);
|
||||
ps.setString(3, category + " 上传知识库");
|
||||
ps.setString(4, "\n\n");
|
||||
@@ -2712,20 +2713,20 @@ public class AihrSopSeedService {
|
||||
from aihr_knowledge_attach
|
||||
where tenant_id = ? and knowledge_id = ? and name = ?
|
||||
limit 1
|
||||
""", String.class, TENANT_ID, knowledgeId, fileName);
|
||||
""", String.class, tenantId(), knowledgeId, fileName);
|
||||
String docId = rows.isEmpty() || rows.get(0) == null ? UUID.randomUUID().toString().replace("-", "") : rows.get(0);
|
||||
if (rows.isEmpty()) {
|
||||
jdbcTemplate.update("""
|
||||
insert into aihr_knowledge_attach
|
||||
(tenant_id, knowledge_id, oss_id, doc_id, name, type, status, create_time, update_time, remark)
|
||||
values (?, ?, ?, ?, ?, ?, 1, now(), now(), 'oss-upload:parsing')
|
||||
""", TENANT_ID, knowledgeId, ossId, docId, fileName, fileType(fileName));
|
||||
""", tenantId(), knowledgeId, ossId, docId, fileName, fileType(fileName));
|
||||
} else {
|
||||
jdbcTemplate.update("""
|
||||
update aihr_knowledge_attach
|
||||
set oss_id = ?, doc_id = ?, type = ?, status = 1, update_time = now(), remark = 'oss-upload:parsing'
|
||||
where tenant_id = ? and knowledge_id = ? and name = ?
|
||||
""", ossId, docId, fileType(fileName), TENANT_ID, knowledgeId, fileName);
|
||||
""", ossId, docId, fileType(fileName), tenantId(), knowledgeId, fileName);
|
||||
}
|
||||
return docId;
|
||||
}
|
||||
@@ -2735,7 +2736,7 @@ public class AihrSopSeedService {
|
||||
update aihr_knowledge_attach
|
||||
set status = ?, update_time = now(), remark = ?
|
||||
where tenant_id = ? and knowledge_id = ? and doc_id = ?
|
||||
""", status, limitRemark(remark), TENANT_ID, knowledgeId, docId);
|
||||
""", status, limitRemark(remark), tenantId(), knowledgeId, docId);
|
||||
}
|
||||
|
||||
private void upsertQdrant(long knowledgeId, String category, String docId, List<String> fragments, List<String> embeddings, String modelName) throws Exception {
|
||||
@@ -2756,7 +2757,7 @@ public class AihrSopSeedService {
|
||||
continue;
|
||||
}
|
||||
ObjectNode payload = objectMapper.createObjectNode();
|
||||
payload.put("tenant_id", TENANT_ID);
|
||||
payload.put("tenant_id", tenantId());
|
||||
payload.put("knowledge_id", knowledgeId);
|
||||
payload.put("category", category);
|
||||
payload.put("doc_id", docId);
|
||||
@@ -2849,7 +2850,7 @@ public class AihrSopSeedService {
|
||||
model,
|
||||
dimension
|
||||
);
|
||||
}, TENANT_ID);
|
||||
}, tenantId());
|
||||
} catch (DataAccessException e) {
|
||||
return new VectorDbStats(0, 0, "", null);
|
||||
}
|
||||
@@ -2922,7 +2923,7 @@ public class AihrSopSeedService {
|
||||
private ObjectNode qdrantFilter(Long knowledgeId, String docId, String category) {
|
||||
ObjectNode filter = objectMapper.createObjectNode();
|
||||
ArrayNode must = filter.putArray("must");
|
||||
must.add(qdrantMatch("tenant_id", TENANT_ID));
|
||||
must.add(qdrantMatch("tenant_id", tenantId()));
|
||||
if (knowledgeId != null) {
|
||||
must.add(qdrantMatch("knowledge_id", knowledgeId));
|
||||
}
|
||||
@@ -3243,6 +3244,10 @@ public class AihrSopSeedService {
|
||||
return value.substring(0, maxLength);
|
||||
}
|
||||
|
||||
private String tenantId() {
|
||||
return firstNonBlank(LoginHelper.getTenantId(), TENANT_ID);
|
||||
}
|
||||
|
||||
private static String normalizeBaseUrl(String baseUrl) {
|
||||
String normalized = baseUrl == null ? "" : baseUrl.trim();
|
||||
while (normalized.endsWith("/")) {
|
||||
@@ -3263,8 +3268,8 @@ public class AihrSopSeedService {
|
||||
return firstNonBlank(System.getProperty("aihr.qdrant.apiKey"), System.getenv("AIHR_QDRANT_API_KEY"), "");
|
||||
}
|
||||
|
||||
private static String qdrantPointId(long knowledgeId, String docId, int idx) {
|
||||
return UUID.nameUUIDFromBytes((TENANT_ID + ":" + knowledgeId + ":" + docId + ":" + idx).getBytes(StandardCharsets.UTF_8)).toString();
|
||||
private String qdrantPointId(long knowledgeId, String docId, int idx) {
|
||||
return UUID.nameUUIDFromBytes((tenantId() + ":" + knowledgeId + ":" + docId + ":" + idx).getBytes(StandardCharsets.UTF_8)).toString();
|
||||
}
|
||||
|
||||
private static boolean ok(int statusCode) {
|
||||
|
||||
@@ -94,3 +94,4 @@
|
||||
- 2026-07-14 门禁合同复核:`scripts/tests/demo-check-pilot-gates.test.sh` 通过,确认严格预检覆盖批次日期、唯一在职身份、1–2 个项目、每人至少 10 次训练、80% 完训、20 条校准、AI/人工分档一致率、SOP 可用率、满意度和音频/RAG schema;`scripts/tests/pilot-export-contract.test.mjs` 5/5 通过,AIHR 案例/对练定向测试 31/31 通过,`mobile-uni` 类型检查与 49 个单测通过。当前没有新增可安全实现的门禁缺口,剩余阻塞仍是正式组织映射、正式试点数据、视频样片和方言样本等外部输入。
|
||||
- 2026-07-14 租户边界复核:AI 面试结果、候选人入职主体关联、候选人补充资料、模型配置和案例记录的查询、写入、组织身份解析均改为使用当前登录租户;无登录上下文时仅保留本地开发默认租户 `000000` 回退。该修复已覆盖案例服务的列表、详情、项目范围、贡献者校验、保存和持久化回读路径,仍需在正式多租户环境做隔离回归。
|
||||
- 2026-07-14 仪表盘租户边界复核:管理端首页的面试、训练、知识库、案例、候选资料、组织和模型统计改为按当前登录租户查询;组织同步写入和组织快照查询也改为当前登录租户;无登录上下文时保留本地开发默认租户回退。尚未把批量知识处理队列和完整 SOP 服务的全部租户调用统一替换,后续继续按数据敏感度分批审查。
|
||||
- 2026-07-14 SOP/RAG 租户边界复核:知识库搜索、知识缺口、SOP 问答评审、答案反馈、异步导入任务、文档/片段/向量写入及 Qdrant tenant filter 改为当前登录租户;无登录上下文时回退本地开发租户 `000000`。定向 AIHR 测试 48/48 通过;正式多租户检索隔离仍需在测试环境做跨租户回归,批量上传队列仍待单独审查。
|
||||
|
||||
Reference in New Issue
Block a user