@jingyi0605/codingns 0.9.5 → 0.9.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/codingns.mjs +75 -0
- package/dist/public/assets/{AdaptiveButlerPage-kkJDsnCO.js → AdaptiveButlerPage-khJQh6a_.js} +1 -1
- package/dist/public/assets/{App-DrNI9lWA.js → App-If9gThKM.js} +6 -6
- package/dist/public/assets/{BootstrapPage-QgVH5Mps.js → BootstrapPage-DcfYtoLC.js} +1 -1
- package/dist/public/assets/{ConversationPage-DVk8VfIj.js → ConversationPage-Bfb7GLTM.js} +1 -1
- package/dist/public/assets/{DesktopDetachPreviewPage-BhfP0TpH.js → DesktopDetachPreviewPage-CXUPMcBz.js} +1 -1
- package/dist/public/assets/{DesktopModal-DRmDrv0S.js → DesktopModal-bMdI1jEe.js} +1 -1
- package/dist/public/assets/{DesktopWindowPage-DNbJXnSs.js → DesktopWindowPage-D1xwgS-7.js} +1 -1
- package/dist/public/assets/{FileContextPanel---fLO4ve.js → FileContextPanel-C4syif3B.js} +1 -1
- package/dist/public/assets/{GitSidebar-sXUE0TqT.js → GitSidebar-DduL9aTV.js} +1 -1
- package/dist/public/assets/{MobileCreateSessionSheet-BftZ5pvb.js → MobileCreateSessionSheet-DWPBsEx8.js} +1 -1
- package/dist/public/assets/{MobileSheet-nw5SCa3N.js → MobileSheet-BXvQPkxt.js} +1 -1
- package/dist/public/assets/{MobileTopHeaderFrame-DH_D02Wy.js → MobileTopHeaderFrame-vdYOyaaB.js} +1 -1
- package/dist/public/assets/{MobileWorkspaceSwitcherHeader-2K406G5p.js → MobileWorkspaceSwitcherHeader-DT330cAx.js} +1 -1
- package/dist/public/assets/{PluginAccessOverview-BVJihw3D.js → PluginAccessOverview-C77TeZTK.js} +1 -1
- package/dist/public/assets/{PluginContainerPage-CR4vStvr.js → PluginContainerPage-DdSwOCw-.js} +1 -1
- package/dist/public/assets/{PluginDetailPage-CrMX0Mnm.js → PluginDetailPage-BK1yTzvO.js} +1 -1
- package/dist/public/assets/{PluginsListPage-FtIL71Yg.js → PluginsListPage-DAAwSc6W.js} +1 -1
- package/dist/public/assets/{RelayConnectEntryPage-Bt1apX53.js → RelayConnectEntryPage-4Yyo2p8b.js} +1 -1
- package/dist/public/assets/{ServerSettingsModal-D-guzPrI.js → ServerSettingsModal-C_DEisHs.js} +1 -1
- package/dist/public/assets/{SessionIndexPage-CX2FppcJ.js → SessionIndexPage-DyMikN_x.js} +1 -1
- package/dist/public/assets/SettingsPage-CDAVsPr3.js +2 -0
- package/dist/public/assets/{TerminalManagerPanel-B5MKGPy-.js → TerminalManagerPanel-4OR47vcf.js} +1 -1
- package/dist/public/assets/{TerminalPage-C2dTNGHK.js → TerminalPage-Pvx396YX.js} +1 -1
- package/dist/public/assets/{TerminalRuntimeFallbackModal-DAqOxFD8.js → TerminalRuntimeFallbackModal-KvG6k4AQ.js} +1 -1
- package/dist/public/assets/{ToolFilesPage-IsNwyE6T.js → ToolFilesPage-DrYHk0N-.js} +1 -1
- package/dist/public/assets/{ToolGitPage-BK1JBERN.js → ToolGitPage-Dz1q-Ns_.js} +1 -1
- package/dist/public/assets/{ToolProcessesPage-DwTYUQCK.js → ToolProcessesPage-CRhphOmM.js} +1 -1
- package/dist/public/assets/{ToolsHomePage-BLOy7lPg.js → ToolsHomePage-BJSDLR6T.js} +1 -1
- package/dist/public/assets/{WorkbenchLandingPage-CqZKR6EA.js → WorkbenchLandingPage-BlkxdOLC.js} +1 -1
- package/dist/public/assets/WorkbenchLayout-D-U7ghT0.js +1022 -0
- package/dist/public/assets/{WorkbenchModal-BM-OeW-b.js → WorkbenchModal-xbx1o6MO.js} +1 -1
- package/dist/public/assets/WorkbenchShellRoute-DyWSCHz_.js +1 -0
- package/dist/public/assets/WorkbenchShellRoute-htbkGbtW.css +1 -0
- package/dist/public/assets/{WorkspaceDebugDetailPage-BMsEN5iG.js → WorkspaceDebugDetailPage-B4ol2_a5.js} +1 -1
- package/dist/public/assets/{WorkspaceDetailPage-5H9Gosx2.js → WorkspaceDetailPage-DMakfmHR.js} +1 -1
- package/dist/public/assets/{WorkspaceHomePage-DQiXKgiP.js → WorkspaceHomePage-tmCafatd.js} +1 -1
- package/dist/public/assets/{client-runtime-manager-CgPJq21V.js → client-runtime-manager-Bwau7p1v.js} +1 -1
- package/dist/public/assets/index-DmUJ8tIw.css +1 -0
- package/dist/public/assets/index-_OCkVmfl.js +50 -0
- package/dist/public/assets/{login-direct-candidate-resolver-CGaxAXV8.js → login-direct-candidate-resolver-CKUQ07IA.js} +1 -1
- package/dist/public/assets/{plugin-permission-copy-BR9gWy8b.js → plugin-permission-copy-DIVk5jNp.js} +1 -1
- package/dist/public/assets/{plugins-api-CdCsrG2e.js → plugins-api-DHJVvPZw.js} +1 -1
- package/dist/public/assets/{preferences-service-lOhnlxzP.js → preferences-service-CyxxeBmS.js} +1 -1
- package/dist/public/assets/{relay-entry-CQpxTS8y.js → relay-entry-B5GmiOrR.js} +1 -1
- package/dist/public/assets/{terminal-runtime-meta-oteTx66X.js → terminal-runtime-meta-DBsyT35T.js} +1 -1
- package/dist/public/assets/{useRegisteredDebugTemplates-Bu2ykZ6s.js → useRegisteredDebugTemplates-wCGD2SLW.js} +1 -1
- package/dist/public/assets/{workbench-navigation-DlgXuFW2.js → workbench-navigation-RyUjchbD.js} +1 -1
- package/dist/public/index.html +2 -2
- package/dist/server/modules/affairs-indexer/core/src/repositories/catalog-repository.d.ts +1 -0
- package/dist/server/modules/affairs-indexer/core/src/repositories/catalog-repository.js +31 -0
- package/dist/server/modules/affairs-indexer/core/src/repositories/catalog-repository.js.map +1 -1
- package/dist/server/modules/affairs-indexer/core/src/services/dirty/dirty-scope-resolver.d.ts +1 -0
- package/dist/server/modules/affairs-indexer/core/src/services/dirty/dirty-scope-resolver.js +1 -0
- package/dist/server/modules/affairs-indexer/core/src/services/dirty/dirty-scope-resolver.js.map +1 -1
- package/dist/server/modules/affairs-indexer/core/src/services/export/export-builder.js +4 -1
- package/dist/server/modules/affairs-indexer/core/src/services/export/export-builder.js.map +1 -1
- package/dist/server/modules/affairs-indexer/core/src/services/search/search-index-builder.js +140 -16
- package/dist/server/modules/affairs-indexer/core/src/services/search/search-index-builder.js.map +1 -1
- package/dist/server/modules/assistant-capability/assistant-capability-controller.d.ts +1 -1
- package/dist/server/modules/assistant-capability/assistant-capability-controller.js +8 -7
- package/dist/server/modules/assistant-capability/assistant-capability-controller.js.map +1 -1
- package/dist/server/modules/assistant-capability/assistant-capability-service.d.ts +14 -13
- package/dist/server/modules/assistant-capability/assistant-capability-service.js +27 -24
- package/dist/server/modules/assistant-capability/assistant-capability-service.js.map +1 -1
- package/dist/server/modules/auth/auth-controller.d.ts +27 -1
- package/dist/server/modules/auth/auth-controller.js +20 -0
- package/dist/server/modules/auth/auth-controller.js.map +1 -1
- package/dist/server/modules/auth/auth-service.d.ts +32 -1
- package/dist/server/modules/auth/auth-service.js +217 -2
- package/dist/server/modules/auth/auth-service.js.map +1 -1
- package/dist/server/modules/bootstrap/bootstrap-service.js +1 -0
- package/dist/server/modules/bootstrap/bootstrap-service.js.map +1 -1
- package/dist/server/modules/butler/assistant-automation-service.d.ts +1 -1
- package/dist/server/modules/butler/assistant-automation-service.js +9 -10
- package/dist/server/modules/butler/assistant-automation-service.js.map +1 -1
- package/dist/server/modules/butler/butler-action-context-service.js +2 -2
- package/dist/server/modules/butler/butler-action-context-service.js.map +1 -1
- package/dist/server/modules/butler/butler-control-action-service.d.ts +5 -5
- package/dist/server/modules/butler/butler-control-action-service.js +19 -19
- package/dist/server/modules/butler/butler-control-action-service.js.map +1 -1
- package/dist/server/modules/butler/butler-control-session-service.d.ts +1 -1
- package/dist/server/modules/butler/butler-control-session-service.js +27 -26
- package/dist/server/modules/butler/butler-control-session-service.js.map +1 -1
- package/dist/server/modules/butler/butler-control-timer-service.js +4 -5
- package/dist/server/modules/butler/butler-control-timer-service.js.map +1 -1
- package/dist/server/modules/butler/butler-controller.d.ts +2 -2
- package/dist/server/modules/butler/butler-controller.js +19 -17
- package/dist/server/modules/butler/butler-controller.js.map +1 -1
- package/dist/server/modules/butler/butler-profile-service.d.ts +5 -5
- package/dist/server/modules/butler/butler-profile-service.js +17 -16
- package/dist/server/modules/butler/butler-profile-service.js.map +1 -1
- package/dist/server/modules/butler/butler-project-service.d.ts +7 -6
- package/dist/server/modules/butler/butler-project-service.js +35 -21
- package/dist/server/modules/butler/butler-project-service.js.map +1 -1
- package/dist/server/modules/butler/butler-session-service.d.ts +2 -2
- package/dist/server/modules/butler/butler-session-service.js +33 -27
- package/dist/server/modules/butler/butler-session-service.js.map +1 -1
- package/dist/server/modules/butler/butler-session-summary-service.d.ts +2 -2
- package/dist/server/modules/butler/butler-session-summary-service.js +23 -9
- package/dist/server/modules/butler/butler-session-summary-service.js.map +1 -1
- package/dist/server/modules/butler/context-aggregator.js +9 -9
- package/dist/server/modules/butler/context-aggregator.js.map +1 -1
- package/dist/server/modules/butler/patrol-execution-service.d.ts +0 -1
- package/dist/server/modules/butler/patrol-execution-service.js +6 -12
- package/dist/server/modules/butler/patrol-execution-service.js.map +1 -1
- package/dist/server/modules/sessions/session-controller.js +3 -3
- package/dist/server/modules/sessions/session-controller.js.map +1 -1
- package/dist/server/modules/sessions/session-history-service.d.ts +8 -3
- package/dist/server/modules/sessions/session-history-service.js +126 -11
- package/dist/server/modules/sessions/session-history-service.js.map +1 -1
- package/dist/server/modules/sessions/session-live-runtime-service.js +25 -15
- package/dist/server/modules/sessions/session-live-runtime-service.js.map +1 -1
- package/dist/server/modules/sessions/session-permission-request-service.js +28 -15
- package/dist/server/modules/sessions/session-permission-request-service.js.map +1 -1
- package/dist/server/modules/workbench/workbench-service.js +9 -9
- package/dist/server/modules/workbench/workbench-service.js.map +1 -1
- package/dist/server/modules/workspace/affairs-library-service.d.ts +3 -1
- package/dist/server/modules/workspace/affairs-library-service.js +112 -54
- package/dist/server/modules/workspace/affairs-library-service.js.map +1 -1
- package/dist/server/modules/workspace/affairs-tag-service.d.ts +12 -0
- package/dist/server/modules/workspace/affairs-tag-service.js +527 -2
- package/dist/server/modules/workspace/affairs-tag-service.js.map +1 -1
- package/dist/server/modules/workspace/workspace-controller.d.ts +1 -1
- package/dist/server/modules/workspace/workspace-controller.js +7 -7
- package/dist/server/modules/workspace/workspace-controller.js.map +1 -1
- package/dist/server/modules/workspace/workspace-service.d.ts +7 -0
- package/dist/server/modules/workspace/workspace-service.js +151 -10
- package/dist/server/modules/workspace/workspace-service.js.map +1 -1
- package/dist/server/routes/auth.js +6 -0
- package/dist/server/routes/auth.js.map +1 -1
- package/dist/server/server/create-server.d.ts +4 -0
- package/dist/server/server/create-server.js +26 -0
- package/dist/server/server/create-server.js.map +1 -1
- package/dist/server/server/start-host.js +20 -0
- package/dist/server/server/start-host.js.map +1 -1
- package/dist/server/shared/http/request-diagnostics.d.ts +56 -0
- package/dist/server/shared/http/request-diagnostics.js +256 -0
- package/dist/server/shared/http/request-diagnostics.js.map +1 -0
- package/dist/server/storage/repositories/auth-token-repository.d.ts +1 -0
- package/dist/server/storage/repositories/auth-token-repository.js +8 -0
- package/dist/server/storage/repositories/auth-token-repository.js.map +1 -1
- package/dist/server/storage/repositories/auth-user-repository.d.ts +50 -0
- package/dist/server/storage/repositories/auth-user-repository.js +198 -27
- package/dist/server/storage/repositories/auth-user-repository.js.map +1 -1
- package/dist/server/storage/repositories/butler-control-session-repository.d.ts +4 -1
- package/dist/server/storage/repositories/butler-control-session-repository.js +55 -8
- package/dist/server/storage/repositories/butler-control-session-repository.js.map +1 -1
- package/dist/server/storage/repositories/butler-profile-repository.d.ts +2 -1
- package/dist/server/storage/repositories/butler-profile-repository.js +35 -6
- package/dist/server/storage/repositories/butler-profile-repository.js.map +1 -1
- package/dist/server/storage/repositories/butler-project-repository.d.ts +2 -0
- package/dist/server/storage/repositories/butler-project-repository.js +38 -4
- package/dist/server/storage/repositories/butler-project-repository.js.map +1 -1
- package/dist/server/storage/repositories/butler-session-repository.d.ts +2 -1
- package/dist/server/storage/repositories/butler-session-repository.js +35 -6
- package/dist/server/storage/repositories/butler-session-repository.js.map +1 -1
- package/dist/server/storage/repositories/session-binding-repository.d.ts +3 -0
- package/dist/server/storage/repositories/session-binding-repository.js +70 -2
- package/dist/server/storage/repositories/session-binding-repository.js.map +1 -1
- package/dist/server/storage/repositories/session-index-repository.js +7 -5
- package/dist/server/storage/repositories/session-index-repository.js.map +1 -1
- package/dist/server/storage/repositories/workspace-repository.d.ts +3 -1
- package/dist/server/storage/repositories/workspace-repository.js +24 -10
- package/dist/server/storage/repositories/workspace-repository.js.map +1 -1
- package/dist/server/storage/sqlite/client.js +288 -0
- package/dist/server/storage/sqlite/client.js.map +1 -1
- package/dist/server/storage/sqlite/schema.sql +33 -9
- package/dist/server/types/domain.d.ts +8 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/claude-session-store.js +34 -7
- package/node_modules/@codingns/session-sync-core/dist/providers/claude-session-store.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/runtime/claude-runtime.js +3 -7
- package/node_modules/@codingns/session-sync-core/dist/runtime/claude-runtime.js.map +1 -1
- package/package.json +1 -1
- package/dist/public/assets/SettingsPage-BI2Olcvr.js +0 -2
- package/dist/public/assets/WorkbenchLayout-CJHQtwuL.js +0 -1022
- package/dist/public/assets/WorkbenchShellRoute-2bKI6Q9k.js +0 -1
- package/dist/public/assets/WorkbenchShellRoute-BjuZD101.css +0 -1
- package/dist/public/assets/index-BARqMVSw.css +0 -1
- package/dist/public/assets/index-BUoNjVrY.js +0 -50
|
@@ -39,6 +39,15 @@ export interface AffairsResolvedTagSourceDto {
|
|
|
39
39
|
confidence: number;
|
|
40
40
|
priority: number;
|
|
41
41
|
}
|
|
42
|
+
export type AffairsTagRecommendationReason = "name_match" | "folder_context" | "smart_rule" | "time_pattern";
|
|
43
|
+
export interface AffairsTagRecommendationDto {
|
|
44
|
+
tagId: string;
|
|
45
|
+
path: string;
|
|
46
|
+
name: string;
|
|
47
|
+
score: number;
|
|
48
|
+
reason: AffairsTagRecommendationReason;
|
|
49
|
+
evidence: string;
|
|
50
|
+
}
|
|
42
51
|
export interface AffairsDocumentTagDetailsDto {
|
|
43
52
|
documentId: string;
|
|
44
53
|
path: string;
|
|
@@ -51,6 +60,7 @@ export interface AffairsDocumentTagDetailsDto {
|
|
|
51
60
|
tagPath: string;
|
|
52
61
|
}>;
|
|
53
62
|
resolvedTags: AffairsResolvedTagSourceDto[];
|
|
63
|
+
recommendedTags: AffairsTagRecommendationDto[];
|
|
54
64
|
}
|
|
55
65
|
export interface AffairsFolderTagDetailsDto {
|
|
56
66
|
folderPath: string;
|
|
@@ -62,6 +72,7 @@ export interface AffairsFolderTagDetailsDto {
|
|
|
62
72
|
tagPath: string;
|
|
63
73
|
applyMode: string;
|
|
64
74
|
}>;
|
|
75
|
+
recommendedTags: AffairsTagRecommendationDto[];
|
|
65
76
|
}
|
|
66
77
|
export interface AffairsTagRecomputeRequestResultDto {
|
|
67
78
|
taskId: string;
|
|
@@ -171,4 +182,5 @@ export declare class AffairsTagService {
|
|
|
171
182
|
private registerBackgroundTasks;
|
|
172
183
|
private toTagNodeDto;
|
|
173
184
|
private buildTagDocumentCountMap;
|
|
185
|
+
private recommendTagsForTarget;
|
|
174
186
|
}
|
|
@@ -339,11 +339,22 @@ export class AffairsTagService {
|
|
|
339
339
|
const manualBindings = repository.listManualDocumentTagBindingsByDocumentIds([documentId]);
|
|
340
340
|
const folderBindings = repository.listEffectiveFolderTagBindingsForDocumentPaths([context.path]);
|
|
341
341
|
const resolved = repository.listResolvedDocumentTagsByDocumentIds([documentId]);
|
|
342
|
+
const manualTagIds = new Set(manualBindings.map(item => item.tagId));
|
|
343
|
+
const excludedTagIds = new Set([
|
|
344
|
+
...manualBindings.map(item => item.tagId),
|
|
345
|
+
...folderBindings.map(item => item.tagId),
|
|
346
|
+
...resolved.map(item => item.tagId),
|
|
347
|
+
]);
|
|
348
|
+
const excludedTagPaths = new Set([
|
|
349
|
+
...manualBindings.map(item => item.tagPath),
|
|
350
|
+
...folderBindings.map(item => item.tagPath),
|
|
351
|
+
...resolved.map(item => item.path),
|
|
352
|
+
]);
|
|
342
353
|
return {
|
|
343
354
|
documentId,
|
|
344
355
|
path: context.path,
|
|
345
356
|
title: context.title,
|
|
346
|
-
manualTagIds:
|
|
357
|
+
manualTagIds: [...manualTagIds],
|
|
347
358
|
effectiveFolderBindings: folderBindings.map(item => ({
|
|
348
359
|
id: item.id,
|
|
349
360
|
folderPath: item.folderPath,
|
|
@@ -358,6 +369,15 @@ export class AffairsTagService {
|
|
|
358
369
|
confidence: item.confidence,
|
|
359
370
|
priority: resolvePriority(item.sourceType),
|
|
360
371
|
})),
|
|
372
|
+
recommendedTags: this.recommendTagsForTarget(repository, {
|
|
373
|
+
kind: "document",
|
|
374
|
+
path: context.path,
|
|
375
|
+
title: context.title,
|
|
376
|
+
extension: context.extension,
|
|
377
|
+
modifiedAt: context.modifiedAt,
|
|
378
|
+
excludedTagIds,
|
|
379
|
+
excludedTagPaths,
|
|
380
|
+
}),
|
|
361
381
|
};
|
|
362
382
|
}
|
|
363
383
|
saveDocumentTagBindings(workspaceId, userId, documentId, tagIds) {
|
|
@@ -414,16 +434,23 @@ export class AffairsTagService {
|
|
|
414
434
|
}).exists;
|
|
415
435
|
const repository = new CatalogRepository(dbPath);
|
|
416
436
|
const bindings = repository.listFolderTagBindingsByPaths([normalizedFolderPath]);
|
|
437
|
+
const assignedTagIds = new Set(bindings.map(item => item.tagId));
|
|
417
438
|
return {
|
|
418
439
|
folderPath: normalizedFolderPath,
|
|
419
440
|
exists,
|
|
420
|
-
bindingTagIds:
|
|
441
|
+
bindingTagIds: [...assignedTagIds],
|
|
421
442
|
bindings: bindings.map(item => ({
|
|
422
443
|
id: item.id,
|
|
423
444
|
tagId: item.tagId,
|
|
424
445
|
tagPath: item.tagPath,
|
|
425
446
|
applyMode: item.applyMode,
|
|
426
447
|
})),
|
|
448
|
+
recommendedTags: this.recommendTagsForTarget(repository, {
|
|
449
|
+
kind: "folder",
|
|
450
|
+
path: normalizedFolderPath,
|
|
451
|
+
title: normalizedFolderPath === "." ? path.basename(rootDir) : path.posix.basename(normalizedFolderPath),
|
|
452
|
+
excludedTagIds: assignedTagIds,
|
|
453
|
+
}),
|
|
427
454
|
};
|
|
428
455
|
}
|
|
429
456
|
getFolderTagApplyTaskSnapshot(workspaceId, userId, folderPath) {
|
|
@@ -617,6 +644,89 @@ export class AffairsTagService {
|
|
|
617
644
|
});
|
|
618
645
|
return countByPath;
|
|
619
646
|
}
|
|
647
|
+
recommendTagsForTarget(repository, target) {
|
|
648
|
+
const definitions = repository.listTagDefinitions(false)
|
|
649
|
+
.filter((tag) => isBusinessTagDefinition(tag)
|
|
650
|
+
&& !target.excludedTagIds.has(tag.id)
|
|
651
|
+
&& !target.excludedTagPaths?.has(tag.path));
|
|
652
|
+
if (definitions.length === 0) {
|
|
653
|
+
return [];
|
|
654
|
+
}
|
|
655
|
+
const candidates = new Map();
|
|
656
|
+
const tagById = new Map(definitions.map(tag => [tag.id, tag]));
|
|
657
|
+
const countByPath = this.buildTagDocumentCountMap(repository);
|
|
658
|
+
const targetText = buildRecommendationTargetText(target.path, target.title);
|
|
659
|
+
for (const tag of definitions) {
|
|
660
|
+
const score = scoreTagNameMatch(tag, targetText);
|
|
661
|
+
if (score <= 0) {
|
|
662
|
+
continue;
|
|
663
|
+
}
|
|
664
|
+
setTagRecommendation(candidates, tag, {
|
|
665
|
+
score,
|
|
666
|
+
reason: "name_match",
|
|
667
|
+
evidence: "文件夹或文件名称里出现了这个标签的关键词",
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
for (const binding of repository.listAllFolderTagBindings()) {
|
|
671
|
+
const tag = tagById.get(binding.tagId);
|
|
672
|
+
if (!tag) {
|
|
673
|
+
continue;
|
|
674
|
+
}
|
|
675
|
+
const relationScore = scoreFolderContext(target.kind, target.path, binding.folderPath);
|
|
676
|
+
if (relationScore <= 0) {
|
|
677
|
+
continue;
|
|
678
|
+
}
|
|
679
|
+
setTagRecommendation(candidates, tag, {
|
|
680
|
+
score: relationScore,
|
|
681
|
+
reason: "folder_context",
|
|
682
|
+
evidence: binding.folderPath === "."
|
|
683
|
+
? "根目录已经配置过这个标签"
|
|
684
|
+
: `相关文件夹“${binding.folderPath}”已经配置过这个标签`,
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
const rulesByTagId = new Map();
|
|
688
|
+
for (const rule of repository.listAllEnabledTagRules()) {
|
|
689
|
+
const rules = rulesByTagId.get(rule.tagId) ?? [];
|
|
690
|
+
rules.push(rule);
|
|
691
|
+
rulesByTagId.set(rule.tagId, rules);
|
|
692
|
+
}
|
|
693
|
+
for (const [tagId, rules] of rulesByTagId) {
|
|
694
|
+
const tag = tagById.get(tagId);
|
|
695
|
+
const matchedRule = tag ? resolveMatchedRecommendationRule(target, rules) : null;
|
|
696
|
+
if (!tag || !matchedRule) {
|
|
697
|
+
continue;
|
|
698
|
+
}
|
|
699
|
+
setTagRecommendation(candidates, tag, {
|
|
700
|
+
score: 82,
|
|
701
|
+
reason: "smart_rule",
|
|
702
|
+
evidence: resolveRecommendationRuleEvidence(matchedRule),
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
if (target.kind === "document" && target.modifiedAt) {
|
|
706
|
+
const timeScore = scoreRecentModifiedAt(target.modifiedAt);
|
|
707
|
+
if (timeScore > 0) {
|
|
708
|
+
for (const tag of definitions) {
|
|
709
|
+
if (!isTimeLikeBusinessTag(tag)) {
|
|
710
|
+
continue;
|
|
711
|
+
}
|
|
712
|
+
setTagRecommendation(candidates, tag, {
|
|
713
|
+
score: timeScore,
|
|
714
|
+
reason: "time_pattern",
|
|
715
|
+
evidence: "最近修改时间和这个标签有关",
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
return [...candidates.values()]
|
|
721
|
+
.map(item => ({
|
|
722
|
+
...item,
|
|
723
|
+
score: Math.min(100, item.score + Math.min(8, Math.log2((countByPath.get(item.path) ?? 0) + 1) * 1.5)),
|
|
724
|
+
}))
|
|
725
|
+
.sort((left, right) => right.score - left.score
|
|
726
|
+
|| left.path.localeCompare(right.path, "zh-Hans-CN"))
|
|
727
|
+
.slice(0, 8)
|
|
728
|
+
.map(item => ({ ...item, score: Number(item.score.toFixed(2)) }));
|
|
729
|
+
}
|
|
620
730
|
}
|
|
621
731
|
function isDescendantTag(definitions, candidateId, ancestorId) {
|
|
622
732
|
let current = definitions.find(item => item.id === candidateId) ?? null;
|
|
@@ -716,4 +826,419 @@ function normalizeTagRulesInput(rules) {
|
|
|
716
826
|
}))
|
|
717
827
|
.sort((left, right) => left.priority - right.priority);
|
|
718
828
|
}
|
|
829
|
+
function isBusinessTagDefinition(tag) {
|
|
830
|
+
if (tag.status === "disabled") {
|
|
831
|
+
return false;
|
|
832
|
+
}
|
|
833
|
+
const rootType = tag.rootType.trim().toLowerCase();
|
|
834
|
+
return rootType !== "类型" && rootType !== "type" && rootType !== "时间" && rootType !== "time";
|
|
835
|
+
}
|
|
836
|
+
function buildRecommendationTargetText(targetPath, title) {
|
|
837
|
+
return buildSearchableRecommendationText([
|
|
838
|
+
targetPath,
|
|
839
|
+
path.posix.basename(targetPath),
|
|
840
|
+
title,
|
|
841
|
+
targetPath.split(/[\/._\-\s]+/g).join(" "),
|
|
842
|
+
]);
|
|
843
|
+
}
|
|
844
|
+
function normalizeRecommendationText(value) {
|
|
845
|
+
return value
|
|
846
|
+
.trim()
|
|
847
|
+
.toLowerCase()
|
|
848
|
+
.replace(/[\\/_\-–—.()[\]{}【】()]+/g, " ")
|
|
849
|
+
.replace(/\s+/g, " ");
|
|
850
|
+
}
|
|
851
|
+
function buildSearchableRecommendationText(parts) {
|
|
852
|
+
const values = new Set();
|
|
853
|
+
for (const part of parts) {
|
|
854
|
+
const normalized = normalizeRecommendationText(part);
|
|
855
|
+
const compact = compactRecommendationText(part);
|
|
856
|
+
const stripped = stripMeaninglessRecommendationWords(compact);
|
|
857
|
+
if (normalized) {
|
|
858
|
+
values.add(normalized);
|
|
859
|
+
}
|
|
860
|
+
if (compact) {
|
|
861
|
+
values.add(compact);
|
|
862
|
+
}
|
|
863
|
+
if (stripped) {
|
|
864
|
+
values.add(stripped);
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
return [...values].join(" ");
|
|
868
|
+
}
|
|
869
|
+
function scoreTagNameMatch(tag, targetText) {
|
|
870
|
+
const tokens = splitRecommendationTokens(targetText);
|
|
871
|
+
const tokenSet = new Set(tokens);
|
|
872
|
+
const segments = tag.path.split("/").map(normalizeRecommendationText).filter((segment) => isUsefulRecommendationToken(segment));
|
|
873
|
+
const tagName = normalizeRecommendationText(tag.name);
|
|
874
|
+
const tagPath = normalizeRecommendationText(tag.path);
|
|
875
|
+
const tagNameTokens = splitRecommendationTokens(tagName).filter(isUsefulRecommendationToken);
|
|
876
|
+
const tagMatchTexts = buildTagMatchTexts(tag);
|
|
877
|
+
let score = 0;
|
|
878
|
+
if (tagName && isUsefulRecommendationToken(tagName) && tokenSet.has(tagName)) {
|
|
879
|
+
score = Math.max(score, 90);
|
|
880
|
+
}
|
|
881
|
+
if (tagName && isUsefulRecommendationToken(tagName) && tokens.some(token => token.length > tagName.length && token.includes(tagName))) {
|
|
882
|
+
score = Math.max(score, 86);
|
|
883
|
+
}
|
|
884
|
+
if (tagPath && targetText.includes(tagPath)) {
|
|
885
|
+
score = Math.max(score, 94);
|
|
886
|
+
}
|
|
887
|
+
for (const tagText of tagMatchTexts) {
|
|
888
|
+
const overlapLength = findLongestCommonChineseTextLength(tagText, targetText);
|
|
889
|
+
if (overlapLength >= MIN_RECOMMENDATION_OVERLAP_LENGTH) {
|
|
890
|
+
score = Math.max(score, scoreTextOverlap(overlapLength, tagText.length));
|
|
891
|
+
continue;
|
|
892
|
+
}
|
|
893
|
+
const bestSimilarity = Math.max(0, ...tokens.map(token => similarityRatio(tagText, stripMeaninglessRecommendationWords(token))));
|
|
894
|
+
if (tagText.length >= 4 && bestSimilarity >= 0.88) {
|
|
895
|
+
score = Math.max(score, 76 + Math.round((bestSimilarity - 0.88) * 70));
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
const matchedSegments = segments.filter(segment => tokenSet.has(segment));
|
|
899
|
+
if (matchedSegments.length >= 2) {
|
|
900
|
+
score = Math.max(score, 84 + matchedSegments.length * 3);
|
|
901
|
+
}
|
|
902
|
+
const matchedNameTokens = tagNameTokens.filter(token => tokenSet.has(token));
|
|
903
|
+
if (tagNameTokens.length >= 2 && matchedNameTokens.length === tagNameTokens.length) {
|
|
904
|
+
score = Math.max(score, 88);
|
|
905
|
+
}
|
|
906
|
+
if (score === 0 && tagName.length >= 3) {
|
|
907
|
+
const bestSimilarity = Math.max(0, ...tokens.map(token => similarityRatio(tagName, token)));
|
|
908
|
+
if (bestSimilarity >= 0.86) {
|
|
909
|
+
score = Math.max(score, 78 + Math.round((bestSimilarity - 0.86) * 60));
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
return Math.min(score, 94);
|
|
913
|
+
}
|
|
914
|
+
function buildTagMatchTexts(tag) {
|
|
915
|
+
const rawNames = [
|
|
916
|
+
tag.name,
|
|
917
|
+
path.posix.basename(tag.path),
|
|
918
|
+
];
|
|
919
|
+
const candidates = new Set();
|
|
920
|
+
for (const rawName of rawNames) {
|
|
921
|
+
const compact = compactRecommendationText(rawName);
|
|
922
|
+
const stripped = stripMeaninglessRecommendationWords(compact);
|
|
923
|
+
for (const candidate of [compact, stripped]) {
|
|
924
|
+
if (isUsefulTagMatchText(candidate)) {
|
|
925
|
+
candidates.add(candidate);
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
return [...candidates].sort((left, right) => right.length - left.length);
|
|
930
|
+
}
|
|
931
|
+
function compactRecommendationText(value) {
|
|
932
|
+
return normalizeRecommendationText(value).replace(/\s+/g, "");
|
|
933
|
+
}
|
|
934
|
+
function stripMeaninglessRecommendationWords(value) {
|
|
935
|
+
let result = value;
|
|
936
|
+
let changed = true;
|
|
937
|
+
while (changed) {
|
|
938
|
+
changed = false;
|
|
939
|
+
for (const word of MEANINGLESS_RECOMMENDATION_WORDS) {
|
|
940
|
+
if (word && result.includes(word)) {
|
|
941
|
+
result = result.replaceAll(word, "");
|
|
942
|
+
changed = true;
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
return result;
|
|
947
|
+
}
|
|
948
|
+
function isUsefulTagMatchText(value) {
|
|
949
|
+
if (value.length < MIN_RECOMMENDATION_OVERLAP_LENGTH) {
|
|
950
|
+
return false;
|
|
951
|
+
}
|
|
952
|
+
if (GENERIC_RECOMMENDATION_TOKENS.has(value)) {
|
|
953
|
+
return false;
|
|
954
|
+
}
|
|
955
|
+
return !MEANINGLESS_RECOMMENDATION_WORDS.includes(value);
|
|
956
|
+
}
|
|
957
|
+
function findLongestCommonChineseTextLength(left, right) {
|
|
958
|
+
const leftText = compactRecommendationText(left);
|
|
959
|
+
const rightText = compactRecommendationText(right);
|
|
960
|
+
const maxLength = Math.min(leftText.length, rightText.length);
|
|
961
|
+
for (let length = maxLength; length >= MIN_RECOMMENDATION_OVERLAP_LENGTH; length -= 1) {
|
|
962
|
+
for (let index = 0; index + length <= leftText.length; index += 1) {
|
|
963
|
+
const fragment = leftText.slice(index, index + length);
|
|
964
|
+
if (isUsefulTagMatchText(fragment) && rightText.includes(fragment)) {
|
|
965
|
+
return length;
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
return 0;
|
|
970
|
+
}
|
|
971
|
+
function scoreTextOverlap(overlapLength, tagTextLength) {
|
|
972
|
+
const coverage = tagTextLength > 0 ? overlapLength / tagTextLength : 0;
|
|
973
|
+
if (overlapLength >= 6 || coverage >= 0.72) {
|
|
974
|
+
return 92;
|
|
975
|
+
}
|
|
976
|
+
if (overlapLength >= 4 || coverage >= 0.56) {
|
|
977
|
+
return 86;
|
|
978
|
+
}
|
|
979
|
+
return 80;
|
|
980
|
+
}
|
|
981
|
+
function splitRecommendationTokens(value) {
|
|
982
|
+
return value
|
|
983
|
+
.split(/\s+/g)
|
|
984
|
+
.map(token => token.trim())
|
|
985
|
+
.filter(Boolean);
|
|
986
|
+
}
|
|
987
|
+
function isUsefulRecommendationToken(value) {
|
|
988
|
+
const token = value.trim().toLowerCase();
|
|
989
|
+
if (token.length < 2) {
|
|
990
|
+
return false;
|
|
991
|
+
}
|
|
992
|
+
return !GENERIC_RECOMMENDATION_TOKENS.has(token);
|
|
993
|
+
}
|
|
994
|
+
const GENERIC_RECOMMENDATION_TOKENS = new Set([
|
|
995
|
+
"文档",
|
|
996
|
+
"文件",
|
|
997
|
+
"资料",
|
|
998
|
+
"附件",
|
|
999
|
+
"其他",
|
|
1000
|
+
"相关",
|
|
1001
|
+
"临时",
|
|
1002
|
+
"新建",
|
|
1003
|
+
"默认",
|
|
1004
|
+
"document",
|
|
1005
|
+
"documents",
|
|
1006
|
+
"file",
|
|
1007
|
+
"files",
|
|
1008
|
+
"other",
|
|
1009
|
+
"misc",
|
|
1010
|
+
"temp",
|
|
1011
|
+
"有限公司",
|
|
1012
|
+
"有限责任公司",
|
|
1013
|
+
"股份有限公司",
|
|
1014
|
+
"集团有限公司",
|
|
1015
|
+
"公司",
|
|
1016
|
+
"集团",
|
|
1017
|
+
]);
|
|
1018
|
+
const MEANINGLESS_RECOMMENDATION_WORDS = [
|
|
1019
|
+
"集团有限公司",
|
|
1020
|
+
"股份有限公司",
|
|
1021
|
+
"有限责任公司",
|
|
1022
|
+
"有限公司",
|
|
1023
|
+
"集团公司",
|
|
1024
|
+
"总公司",
|
|
1025
|
+
"分公司",
|
|
1026
|
+
"公司",
|
|
1027
|
+
"集团",
|
|
1028
|
+
"co.,ltd",
|
|
1029
|
+
"coltd",
|
|
1030
|
+
"ltd",
|
|
1031
|
+
"inc",
|
|
1032
|
+
"llc",
|
|
1033
|
+
"corp",
|
|
1034
|
+
"corporation",
|
|
1035
|
+
"company",
|
|
1036
|
+
"采购",
|
|
1037
|
+
"服务",
|
|
1038
|
+
"管理",
|
|
1039
|
+
"建设",
|
|
1040
|
+
];
|
|
1041
|
+
const MIN_RECOMMENDATION_OVERLAP_LENGTH = 3;
|
|
1042
|
+
function similarityRatio(left, right) {
|
|
1043
|
+
if (!left || !right) {
|
|
1044
|
+
return 0;
|
|
1045
|
+
}
|
|
1046
|
+
if (left === right) {
|
|
1047
|
+
return 1;
|
|
1048
|
+
}
|
|
1049
|
+
const maxLength = Math.max(left.length, right.length);
|
|
1050
|
+
if (maxLength === 0) {
|
|
1051
|
+
return 1;
|
|
1052
|
+
}
|
|
1053
|
+
const distance = levenshteinDistance(left, right);
|
|
1054
|
+
return (maxLength - distance) / maxLength;
|
|
1055
|
+
}
|
|
1056
|
+
function levenshteinDistance(left, right) {
|
|
1057
|
+
const previous = Array.from({ length: right.length + 1 }, (_, index) => index);
|
|
1058
|
+
const current = Array.from({ length: right.length + 1 }, () => 0);
|
|
1059
|
+
for (let i = 1; i <= left.length; i += 1) {
|
|
1060
|
+
current[0] = i;
|
|
1061
|
+
for (let j = 1; j <= right.length; j += 1) {
|
|
1062
|
+
const substitutionCost = left[i - 1] === right[j - 1] ? 0 : 1;
|
|
1063
|
+
current[j] = Math.min(current[j - 1] + 1, previous[j] + 1, previous[j - 1] + substitutionCost);
|
|
1064
|
+
}
|
|
1065
|
+
for (let j = 0; j < previous.length; j += 1) {
|
|
1066
|
+
previous[j] = current[j];
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
return previous[right.length] ?? 0;
|
|
1070
|
+
}
|
|
1071
|
+
function setTagRecommendation(target, tag, input) {
|
|
1072
|
+
const current = target.get(tag.id);
|
|
1073
|
+
if (current && current.score > input.score) {
|
|
1074
|
+
return;
|
|
1075
|
+
}
|
|
1076
|
+
target.set(tag.id, {
|
|
1077
|
+
tagId: tag.id,
|
|
1078
|
+
path: tag.path,
|
|
1079
|
+
name: tag.name,
|
|
1080
|
+
score: input.score,
|
|
1081
|
+
reason: input.reason,
|
|
1082
|
+
evidence: input.evidence,
|
|
1083
|
+
});
|
|
1084
|
+
}
|
|
1085
|
+
function scoreFolderContext(targetKind, targetPath, bindingFolderPath) {
|
|
1086
|
+
const targetFolderPath = targetKind === "document"
|
|
1087
|
+
? normalizeFolderPath(targetPath.includes("/") ? path.posix.dirname(targetPath) : ".")
|
|
1088
|
+
: normalizeFolderPath(targetPath);
|
|
1089
|
+
const bindingPath = normalizeFolderPath(bindingFolderPath);
|
|
1090
|
+
if (bindingPath === ".") {
|
|
1091
|
+
return 0;
|
|
1092
|
+
}
|
|
1093
|
+
if (bindingPath === targetFolderPath) {
|
|
1094
|
+
return 86;
|
|
1095
|
+
}
|
|
1096
|
+
if (targetFolderPath.startsWith(`${bindingPath}/`)) {
|
|
1097
|
+
return 80;
|
|
1098
|
+
}
|
|
1099
|
+
const parentPath = normalizeFolderPath(path.posix.dirname(targetFolderPath));
|
|
1100
|
+
if (bindingPath === parentPath) {
|
|
1101
|
+
return 76;
|
|
1102
|
+
}
|
|
1103
|
+
if (parentPath !== "." && path.posix.dirname(bindingPath) === parentPath && areSiblingFolderNamesRelated(targetFolderPath, bindingPath)) {
|
|
1104
|
+
return 72;
|
|
1105
|
+
}
|
|
1106
|
+
return 0;
|
|
1107
|
+
}
|
|
1108
|
+
function areSiblingFolderNamesRelated(leftPath, rightPath) {
|
|
1109
|
+
const leftName = normalizeRecommendationText(path.posix.basename(leftPath));
|
|
1110
|
+
const rightName = normalizeRecommendationText(path.posix.basename(rightPath));
|
|
1111
|
+
if (!leftName || !rightName) {
|
|
1112
|
+
return false;
|
|
1113
|
+
}
|
|
1114
|
+
const leftTokens = splitRecommendationTokens(leftName).filter(isUsefulRecommendationToken);
|
|
1115
|
+
const rightTokens = splitRecommendationTokens(rightName).filter(isUsefulRecommendationToken);
|
|
1116
|
+
if (leftTokens.some(token => rightTokens.includes(token))) {
|
|
1117
|
+
return true;
|
|
1118
|
+
}
|
|
1119
|
+
return similarityRatio(leftName, rightName) >= 0.82;
|
|
1120
|
+
}
|
|
1121
|
+
function matchesRecommendationRule(target, rule) {
|
|
1122
|
+
switch (rule.ruleType) {
|
|
1123
|
+
case "file_name_contains": {
|
|
1124
|
+
const keyword = String(rule.matcher.keyword ?? "").trim().toLowerCase();
|
|
1125
|
+
return keyword.length > 0 && path.posix.basename(target.path).toLowerCase().includes(keyword);
|
|
1126
|
+
}
|
|
1127
|
+
case "file_content_contains": {
|
|
1128
|
+
const keyword = String(rule.matcher.keyword ?? "").trim().toLowerCase();
|
|
1129
|
+
const text = `${target.title}\n${target.path}`.toLowerCase();
|
|
1130
|
+
return keyword.length > 0 && text.includes(keyword);
|
|
1131
|
+
}
|
|
1132
|
+
case "file_extension_in": {
|
|
1133
|
+
if (target.kind !== "document") {
|
|
1134
|
+
return false;
|
|
1135
|
+
}
|
|
1136
|
+
const rawExtensions = Array.isArray(rule.matcher.extensions)
|
|
1137
|
+
? rule.matcher.extensions ?? []
|
|
1138
|
+
: [];
|
|
1139
|
+
const extensions = rawExtensions
|
|
1140
|
+
.map(item => item.trim().toLowerCase())
|
|
1141
|
+
.filter(Boolean)
|
|
1142
|
+
.map(item => item.startsWith(".") ? item : `.${item}`);
|
|
1143
|
+
return Boolean(target.extension) && extensions.includes(String(target.extension).toLowerCase());
|
|
1144
|
+
}
|
|
1145
|
+
case "modified_time_between": {
|
|
1146
|
+
if (target.kind !== "document" || !target.modifiedAt) {
|
|
1147
|
+
return false;
|
|
1148
|
+
}
|
|
1149
|
+
const matcher = rule.matcher;
|
|
1150
|
+
const modifiedAt = new Date(target.modifiedAt).getTime();
|
|
1151
|
+
if (Number.isNaN(modifiedAt)) {
|
|
1152
|
+
return false;
|
|
1153
|
+
}
|
|
1154
|
+
const startTime = matcher.start ? new Date(matcher.start).getTime() : null;
|
|
1155
|
+
const endTime = matcher.end ? new Date(matcher.end).getTime() : null;
|
|
1156
|
+
if (startTime !== null && (Number.isNaN(startTime) || modifiedAt < startTime)) {
|
|
1157
|
+
return false;
|
|
1158
|
+
}
|
|
1159
|
+
if (endTime !== null && (Number.isNaN(endTime) || modifiedAt > endTime)) {
|
|
1160
|
+
return false;
|
|
1161
|
+
}
|
|
1162
|
+
return startTime !== null || endTime !== null;
|
|
1163
|
+
}
|
|
1164
|
+
case "document_path_in_folder": {
|
|
1165
|
+
const folderPath = normalizeFolderPath(rule.matcher.folderPath ?? "");
|
|
1166
|
+
return matchesPathInFolderScope(target.path, folderPath);
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
function resolveMatchedRecommendationRule(target, rules) {
|
|
1171
|
+
const sortedRules = [...rules].sort((left, right) => left.priority - right.priority);
|
|
1172
|
+
const matchedAnd = [];
|
|
1173
|
+
const matchedOr = [];
|
|
1174
|
+
let hasOrRule = false;
|
|
1175
|
+
for (const rule of sortedRules) {
|
|
1176
|
+
const matched = matchesRecommendationRule(target, rule);
|
|
1177
|
+
if (rule.relation === "not") {
|
|
1178
|
+
if (matched) {
|
|
1179
|
+
return null;
|
|
1180
|
+
}
|
|
1181
|
+
continue;
|
|
1182
|
+
}
|
|
1183
|
+
if (rule.relation === "or") {
|
|
1184
|
+
hasOrRule = true;
|
|
1185
|
+
if (matched) {
|
|
1186
|
+
matchedOr.push(rule);
|
|
1187
|
+
}
|
|
1188
|
+
continue;
|
|
1189
|
+
}
|
|
1190
|
+
if (!matched) {
|
|
1191
|
+
return null;
|
|
1192
|
+
}
|
|
1193
|
+
matchedAnd.push(rule);
|
|
1194
|
+
}
|
|
1195
|
+
if (hasOrRule && matchedOr.length === 0) {
|
|
1196
|
+
return null;
|
|
1197
|
+
}
|
|
1198
|
+
return matchedAnd[0] ?? matchedOr[0] ?? null;
|
|
1199
|
+
}
|
|
1200
|
+
function resolveRecommendationRuleEvidence(rule) {
|
|
1201
|
+
switch (rule.ruleType) {
|
|
1202
|
+
case "file_name_contains":
|
|
1203
|
+
return `智能规则:文件名包含“${String(rule.matcher.keyword ?? "").trim()}”`;
|
|
1204
|
+
case "file_content_contains":
|
|
1205
|
+
return `智能规则:标题、路径或内容包含“${String(rule.matcher.keyword ?? "").trim()}”`;
|
|
1206
|
+
case "file_extension_in": {
|
|
1207
|
+
const extensions = Array.isArray(rule.matcher.extensions)
|
|
1208
|
+
? rule.matcher.extensions ?? []
|
|
1209
|
+
: [];
|
|
1210
|
+
return `智能规则:文件类型命中 ${extensions.join("、")}`;
|
|
1211
|
+
}
|
|
1212
|
+
case "modified_time_between":
|
|
1213
|
+
return "智能规则:修改时间命中";
|
|
1214
|
+
case "document_path_in_folder": {
|
|
1215
|
+
const folderPath = normalizeFolderPath(rule.matcher.folderPath ?? "");
|
|
1216
|
+
return folderPath === "." ? "智能规则:位于根目录下" : `智能规则:位于“${folderPath}”下`;
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
function matchesPathInFolderScope(targetPath, folderPath) {
|
|
1221
|
+
if (folderPath === ".") {
|
|
1222
|
+
return true;
|
|
1223
|
+
}
|
|
1224
|
+
return targetPath === folderPath || targetPath.startsWith(`${folderPath}/`);
|
|
1225
|
+
}
|
|
1226
|
+
function scoreRecentModifiedAt(modifiedAt) {
|
|
1227
|
+
const time = new Date(modifiedAt).getTime();
|
|
1228
|
+
if (Number.isNaN(time)) {
|
|
1229
|
+
return 0;
|
|
1230
|
+
}
|
|
1231
|
+
const dayDistance = Math.floor((Date.now() - time) / 86400000);
|
|
1232
|
+
if (dayDistance <= 7) {
|
|
1233
|
+
return 54;
|
|
1234
|
+
}
|
|
1235
|
+
if (dayDistance <= 30) {
|
|
1236
|
+
return 44;
|
|
1237
|
+
}
|
|
1238
|
+
return 0;
|
|
1239
|
+
}
|
|
1240
|
+
function isTimeLikeBusinessTag(tag) {
|
|
1241
|
+
const text = `${tag.path}/${tag.name}`.toLowerCase();
|
|
1242
|
+
return /最近|近期|本周|本月|待处理|跟进|urgent|recent|week|month|todo|follow/.test(text);
|
|
1243
|
+
}
|
|
719
1244
|
//# sourceMappingURL=affairs-tag-service.js.map
|