| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394 |
- #!/usr/bin/env python3
- # -*- coding: utf-8 -*-
- """Add JavaDoc to impl package public methods. Documentation-only; no logic changes."""
- import os
- import re
- import sys
- from pathlib import Path
- ROOT = Path(r"d:/ylrz_saas_new/java")
- IMPL_DIR = ROOT / "fs-service/src/main/java/com/fs/company/service/workflow/impl"
- WORKFLOW_DIR = ROOT / "fs-service/src/main/java/com/fs/company/service/workflow"
- CLASS_DESC = {
- "LobsterEvolutionEngineImpl": "��Ϻ��������ʵ�֣����ֶԻ�����������װ�� AI ���ɡ��������֡��Ϲ������дʹ��˵�������ˮ�ߡ�",
- "LobsterWorkflowExecutorImpl": "��Ϻ������ִ����ʵ�֣�ʵ���������ڹ������ڵ��ƽ�����ϢͶ����������漯�ɡ�",
- "DynamicNodeExecutorImpl": "��̬�ڵ�ִ����ʵ�֣����ڵ�����ע�� handler��ִ��ö�ٽڵ��� AI ��������",
- "ContextAssemblerImpl": "��������װ��ʵ�֣����м��ػ���֪ʶ�⡢��ʷ�������ȶ�Դ�����Ĺ� AI ʹ�á�",
- "SummaryGeneratorImpl": "�Ի�ժҪ������ʵ�֣�ʵ��/ȫ��ժҪ��������ȡ���첽ժҪ���¡�",
- "LobsterE2eTestServiceImpl": "��Ϻ�˵��˲��Է���ʵ�֣������� E2E �����������ƽ�����ֶԻ����ԡ�",
- "VariableStoreImpl": "�����������洢ʵ�֣��ڴ� + Redis + ���ݿ�������д����ʵ������ʱ�����־û���",
- "MultiTurnDialogueManagerImpl": "���ֶԻ�������ʵ�֣�������Ϣ�ռ��ڵ���ִο��ơ�������ȡ������ж���",
- "QualityScoringServiceImpl": "�������ַ���ʵ�֣���ά�� AI �ظ����֡���������У��������������ɡ�",
- "SensitiveWordServiceImpl": "���дʹ��˷���ʵ�֣��⻧�����дʼ�⡢�滻��߷��մʴ��á�",
- "ComplianceServiceImpl": "�Ϲ������ʵ�֣�AI �ظ��Ϲ���У����ּ���ȫ�����滻��",
- "ConditionEvaluatorImpl": "��������ʽ��ֵ��ʵ�֣���������֧�ڵ�������жϡ�",
- "DynamicNodeAdjusterImpl": "��̬�ڵ������ʵ�֣��������������·�ɾ�����ת�˹��ж���",
- "DynamicNodeImplServiceImpl": "��̬�ڵ�ʵ�ֹ������ڵ� handler ע�������� CRUD��",
- "DuplicateReplyDetector": "�ظ��ظ����������ֹ��������ͬһ�ͻ��ظ�������ͬ���ݡ�",
- "FactDecayScheduler": "��ʵ˥����ʱ�������������û���ʵ�빵ͨϰ���档",
- "LobsterBillingServiceImpl": "��Ϻ�Ʒѷ���ʵ�֣��⻧��Token ���ļ�¼��ͳ�ƻ��ܡ�",
- "LobsterConversationSummaryServiceImpl": "�Ի�ժҪ��ѯ����ʵ�֣�������ժҪ�б������顣",
- "LobsterEventAuditServiceImpl": "�¼���Ʒ���ʵ�֣�����/ѧϰ�¼����˹������ת��",
- "LobsterEvolutionSuggestionServiceImpl": "�����������ʵ�֣�AI ���������б��������ָ��ͳ�ơ�",
- "LobsterInstanceStatsServiceImpl": "ʵ��ͳ�Ʒ���ʵ�֣�������ʵ������ָ����ܡ�",
- "LobsterIntegrationTestServiceImpl": "���ɲ��Է���ʵ�֣��������ð�̲�����ڵ�������֤��",
- "LobsterLearningCorpusServiceImpl": "ѧϰ���Ϸ���ʵ�֣��⻧ѧϰ���ϲ�ѯ��",
- "LobsterModelConfigServiceImpl": "ģ�����÷���ʵ�֣��⻧�� LLM ·�����������ģ�����á�",
- "LobsterNodeTypeServiceImpl": "�ڵ�����ע�����ʵ�֣��������ڵ�����Ԫ���ݻ����� CRUD��",
- "LobsterPromptServiceImpl": "ϵͳ Prompt ��������ʵ�֣����� Prompt ��ϵͳ Prompt CRUD��",
- "LobsterSalesCorpusServiceImpl": "�������Ϸ���ʵ�֣����ڶԻ����롢�����볡����ѯ��",
- "LobsterTestScenarioServiceImpl": "���Գ�������ʵ�֣�E2E ���Գ��� CRUD �붨ʱ������",
- "MultiModelWorkflowGeneratorImpl": "��ģ������������ʵ�֣�AI �Զ�����/�����Ż������� JSON��",
- "NodePromptServiceImpl": "�ڵ� Prompt ����ʵ�֣��ڵ��� Prompt ģ��Ĺ������档",
- "OutputQualityCheckerImpl": "������������ʵ�֣��������ݵĻ�������У���������",
- "PendingAuditKnowledgeServiceImpl": "�����֪ʶ����ʵ�֣��������ʴ������������֪ʶ�⡣",
- "ProfileEnrichmentService": "�û�������������Ϊ�����첽���¿ͻ�����",
- "PromptManagerImpl": "Prompt ������ʵ�֣��ڵ�/ȫ�� Prompt ��ȡ��־û���",
- "QualityScoreRuleEngine": "�������ֹ������棺��̬����У�������ⳡ���ӷ���Ϲ�����",
- "ReplaceWordServiceImpl": "Υ����/�״����滻����ʵ�֣����⻧�����滻�ʱ���Ӧ�á�",
- "SemanticTakeoverDetectorImpl": "����ӹܼ����ʵ�֣����ͻ�Ҫ��ת�˹��� AI ����ת����ͼ��",
- "ToolCallFrameworkImpl": "���ߵ��ÿ��ʵ�֣����� AI ���ߵ��ò�ִ�ж���/SMS �����ù��ߡ�",
- "VariableSubstitutionEngineImpl": "�����滻����ʵ�֣�ģ�� ${var} ռλ���滻�������ȡ��",
- "WorkflowTemplateAdminServiceImpl": "������ģ���������ʵ�֣�ģ���б����ڵ㱣���뷢����ѯ��",
- }
- METHOD_CN = {
- "init": "��ʼ�������ע�� handler�����ػ���ȣ���",
- "refreshCache": "ˢ�±���/Redis ���档",
- "evolve": "ִ�е��ֶԻ�������ˮ�ߣ����� AI �ظ�����һ�ڵ㡣",
- "enrichWithDynamicNode": "AI ��̬���ɲ�������Ի��������ڵ㡣",
- "learnCustomerHabit": "�ӱ��ֽ���ѧϰ�ͻ���ͨϰ�߲�д�� habit ����",
- "startWorkflow": "�����µĹ�����ʵ����ִ���ڵ㡣",
- "executeNextNode": "���տͻ��ظ����ƽ�����������һ�ڵ㡣",
- "pauseWorkflow": "��ͣ������ʵ��ִ�С�",
- "resumeWorkflow": "�ָ�����ͣ�Ĺ�����ʵ����",
- "terminateWorkflow": "��ֹ������ʵ������¼ԭ��",
- "getInstanceState": "��ѯ������ʵ����ǰ״̬��������ա�",
- "simulateExecution": "ģ��ִ�й���������ʵ�ʷ���Ϣ�������ڵ��ԡ�",
- "deliverProactiveMessage": "������ͻ�Ͷ�ݹ�������Ϣ������/���ѣ���",
- "execute": "ִ��ָ�����͵Ķ�̬�ڵ� handler��",
- "supports": "�жϽڵ������Ƿ���ע�� handler��",
- "registerHandler": "ע���Զ���ڵ����� handler��",
- "getRegisteredHandlers": "������ע���ȫ���ڵ� handler ӳ�䡣",
- "assembleContext": "��װ�����������ı���token ���ޣ���",
- "assembleForNode": "Ϊָ���ڵ�������װ�����ġ�",
- "getContextVariables": "��ȡʵ�������ı��� Map��",
- "assembleFullContext": "���м��ض�Դ�����IJ���װ���� Map��",
- "buildAiPrompt": "�������� Map ��ʽ��Ϊ AI Prompt �ı���",
- "formatKnowledgeForScoring": "��ʽ��֪ʶ��Ƭ�ι���������ʹ�á�",
- "generateSummary": "���ڶԻ���ʷ����ժҪ�ı���",
- "extractKeyPoints": "�ӶԻ���ʷ��ȡ�ؼ�Ҫ�㡣",
- "analyzeSentiment": "����������Ϣ�������",
- "generateSummaryAsync": "�첽����ʵ��ժҪ���£�����ֵ����",
- "forceGenerateSummary": "ǿ���������ɲ��־û�ʵ��ժҪ��",
- "forceGenerateSummaryFromHistory": "���ڸ�����ʷ�ı�ǿ������ժҪ��",
- "doGenerateSummary": "ִ��ժҪ���ɵĺ�������ͬ������",
- "getCachedSummary": "��ȡ�ѻ�����û�/ʵ��ժҪ��",
- "updateUserProfile": "��ժҪ��Ϣ�����û������ֶΡ�",
- "extractConversationVariables": "�ӿͻ���Ϣ�� LLM ��ȡ�ṹ��������",
- "generateMicroSummary": "������� N �ֶԻ���ժҪ��",
- "generateGlobalSummary": "���ɿ�Ự��ȫ���û�ժҪ��",
- "runE2e": "�������� E2E ���Բ����ر��档",
- "getReport": "�� runId ��ѯ E2E ���Ա��档",
- "stepNext": "E2E �����ƽ���ע���û����벢ִ����һ�ڵ㡣",
- "multiTurn": "E2E ���ֶԻ����ԣ�����ע������û����롣",
- "listRuns": "��ҳ�г� E2E �������м�¼��",
- "setVariable": "���õ���ʵ���������ڴ�+Redis+DB����",
- "setVariables": "��������ʵ��������",
- "getVariable": "��ȡ����ʵ��������",
- "getAllVariables": "��ȡʵ��ȫ��������",
- "removeVariable": "ɾ������ʵ��������",
- "clearVariables": "���ʵ��ȫ��������",
- "persistToDatabase": "���ڴ�����־û������ݿ⡣",
- "loadFromDatabase": "�����ݿ���ر������ڴ滺�档",
- "processDialogue": "�������ֶԻ��ڵ��һ�ֽ�����",
- "isMultiTurnNode": "�жϽڵ��Ƿ�����Ϊ���ֶԻ���",
- "isDialogueComplete": "�ж϶��ֶԻ��Ƿ�����ɡ�",
- "getDialogueRound": "��ȡ��ǰ���ֶԻ��ִΡ�",
- "resetDialogue": "���ö��ֶԻ�״̬��",
- "scoreWithRetry": "�������ֲ��ڲ����ʱ�����������ɡ�",
- "score": "�� AI �ظ����ж�ά���������֡�",
- "getImprovementHint": "���ݵ�ǰ�÷����ɸĽ���ʾ��",
- "getNodeWarmupHint": "��ȡ�ڵ㼶����Ԥ����ʾ�ʡ�",
- "filterSensitiveWords": "�����ı��е����дʲ��滻��",
- "containsSensitiveWord": "����ı��Ƿ�������дʡ�",
- "detectSensitiveWords": "��Ⲣ�������д���ϸ�б���",
- "isHighRiskSensitiveWord": "�ж��Ƿ߷������дʣ�����/����/թƭ����",
- "addSensitiveWord": "�����⻧���дʲ�ˢ�»��档",
- "removeSensitiveWord": "�������дʲ�ˢ�»��档",
- "getSensitiveWordList": "��ѯ�⻧���õ����д��б���",
- "substitute": "��ģ���еı���ռλ���滻Ϊʵ��ֵ��",
- "extractVariables": "���������г�ȡ������ֵ��",
- "checkAiReplyCompliance": "У�� AI �ظ��Ϲ��Բ����طּ������",
- "adjustNode": "��������������ڽڵ�·���������",
- "getPrompt": "��ȡ�ڵ�/ȫ�� Prompt ���ݡ�",
- "getSystemPrompt": "��ȡ�ڵ�ϵͳ Prompt��",
- "getInstructionPrompt": "��ȡ�ڵ�ָ�� Prompt��",
- "getFallbackPrompt": "��ȡ�ڵ㶵�� Prompt��",
- "hasConfiguredNodePrompt": "�жϽڵ��Ƿ�������ָ������ Prompt��",
- "saveNodePrompt": "����ڵ� Prompt �����ݿ⡣",
- "saveGlobalPrompt": "����ȫ�� Prompt �����ݿ⡣",
- "listPrompts": "�г��⻧ Prompt ���á�",
- "applyReplace": "���ı�Ӧ��Υ�����������״��ʾ�����",
- "listWords": "��ҳ��ѯ�滻���б���",
- "saveWord": "�����滻�����á�",
- "deleteWord": "ɾ���滻�ʡ�",
- "executeTool": "ִ��ָ�����ƵĹ��ߵ��á�",
- "extractToolCall": "�� AI �ظ� JSON �н������ߵ��á�",
- "isToolAvailable": "�жϹ��߶��⻧�Ƿ���á�",
- "getAvailableTools": "�г��⻧���ù��ߡ�",
- "registerTool": "ע���Զ��幤�ߡ�",
- "recordExecLog": "��¼����ִ����־��",
- "queryOrder": "��ѯ�������飨���ù��ߣ���",
- "queryUserOrders": "��ѯ�û������б������ù��ߣ���",
- "sendSms": "���Ͷ��ţ����ù��ߣ���",
- "detectTakeover": "���ͻ��Ƿ�Ҫ��ת�˹��ӹܡ�",
- "detectAITransfer": "��� AI �Ƿ�Ӧ����ת���˹���",
- "checkQuality": "�����������������",
- "checkAndCorrect": "����������ڱ�Ҫʱ�������ݡ�",
- "cleanupExpiredFacts": "��ʱ����������ʵ��ϰ�����ݡ�",
- "isDuplicate": "���ظ��Ƿ�����ڷ��������ظ���",
- "enrichProfile": "�첽�����û�����",
- "repairJson": "�� LLM ���صĻ��� JSON �ַ�����",
- }
- # Build caller index once
- def build_caller_index():
- index = {}
- java_files = list(ROOT.rglob("*.java"))
- for jf in java_files:
- if "impl" in jf.parts and jf.parent.name == "impl" and jf.parent.parent.name == "workflow":
- continue # skip self for class name search
- try:
- text = jf.read_text(encoding="utf-8", errors="replace")
- except Exception:
- continue
- for m in re.finditer(r'\b([A-Z][a-zA-Z0-9]+(?:Service|Executor|Engine|Manager|Store|Assembler|Generator|Framework|Detector|Evaluator|Adjuster|Scheduler|Checker|Router)?)\b', text):
- name = m.group(1)
- if name.endswith("Impl"):
- iface = name[:-4]
- index.setdefault(iface, set()).add(jf.stem)
- index.setdefault(name, set()).add(jf.stem)
- # Also scan for interface usage patterns like "VariableStore variableStore"
- for jf in java_files:
- try:
- text = jf.read_text(encoding="utf-8", errors="replace")
- except Exception:
- continue
- for m in re.finditer(r'\b(Lobster[A-Za-z]+|ContextAssembler|VariableStore|SummaryGenerator|QualityScoringService|SensitiveWordService|DynamicNodeExecutor|MultiTurnDialogueManager|PromptManager|ComplianceService|ToolCallFramework|ConditionEvaluator|DynamicNodeAdjuster|ReplaceWordService|OutputQualityChecker|SemanticTakeoverDetector|VariableSubstitutionEngine|MultiModelWorkflowGenerator|PendingAuditKnowledgeService|I[A-Z][a-zA-Z]+Service)\s+\w+', text):
- iface = m.group(1)
- if jf.stem not in {iface, iface + "Impl"}:
- index.setdefault(iface, set()).add(jf.stem)
- return index
- CALLER_INDEX = None
- def get_callers(class_name, method_name=None):
- global CALLER_INDEX
- if CALLER_INDEX is None:
- CALLER_INDEX = build_caller_index()
- iface = class_name.replace("Impl", "")
- if class_name == "ProfileEnrichmentService":
- iface = class_name
- callers = set()
- for key in (class_name, iface, "I" + iface if not iface.startswith("I") else iface):
- callers.update(CALLER_INDEX.get(key, set()))
- callers.discard(class_name)
- # Filter impl siblings unless they're real callers
- filtered = sorted(c for c in callers if not c.endswith("Impl") or c in {
- "LobsterWorkflowExecutorImpl", "LobsterEvolutionEngineImpl", "DynamicNodeExecutorImpl",
- "ContextAssemblerImpl", "MultiTurnDialogueManagerImpl", "LobsterE2eTestServiceImpl",
- "LobsterTravelValidationServiceImpl", "LobsterIntegrationTestServiceImpl",
- })
- if not filtered:
- filtered = sorted(callers)[:6]
- return filtered[:8]
- def parse_interface_methods(class_name):
- iface = class_name.replace("Impl", "")
- if class_name.startswith("I") and class_name.endswith("ServiceImpl"):
- iface = class_name # ILobsterXServiceImpl -> ILobsterXService
- iface_file = WORKFLOW_DIR / (iface.replace("Impl", "") + ".java")
- else:
- candidates = [
- WORKFLOW_DIR / (iface + ".java"),
- WORKFLOW_DIR / ("I" + iface + ".java"),
- ]
- iface_file = next((c for c in candidates if c.exists()), None)
- methods = {}
- if iface_file and iface_file.exists():
- text = iface_file.read_text(encoding="utf-8", errors="replace")
- # Match javadoc + method signature
- pattern = re.compile(
- r'/\*\*(.*?)\*/\s*(?:@[\w.]+\s*)*([\w<>,\[\]\s.?]+?\s+(\w+)\s*\([^)]*\)\s*;)',
- re.DOTALL,
- )
- for m in pattern.finditer(text):
- doc = m.group(1).strip()
- method = m.group(3)
- # Extract first line of javadoc as purpose
- first_line = re.sub(r'^\s*\*\s?', '', doc.split('\n')[0]).strip()
- if first_line:
- methods[method] = first_line
- return methods
- def has_javadoc(lines, idx):
- """Check if line at idx already has javadoc immediately above (skipping annotations)."""
- j = idx - 1
- while j >= 0 and lines[j].strip().startswith("@"):
- j -= 1
- if j >= 0 and lines[j].strip().endswith("*/"):
- return True
- if j >= 0 and lines[j].strip().startswith("/**"):
- return True
- return False
- def method_purpose(class_name, method_name, iface_methods):
- if method_name in iface_methods:
- return iface_methods[method_name]
- if method_name in METHOD_CN:
- return METHOD_CN[method_name]
- if method_name.startswith("get") and method_name != "get":
- return f"��ȡ{method_name[3:]}��"
- if method_name.startswith("set") and method_name != "set":
- return f"����{method_name[3:]}��"
- if method_name.startswith("is") and method_name != "is":
- return f"�ж��Ƿ�{method_name[2:]}��"
- if method_name.startswith("list"):
- return f"��ѯ{method_name[4:]}�б���"
- if method_name.startswith("save") or method_name.startswith("create"):
- return f"����/����{method_name}������ݡ�"
- if method_name.startswith("delete") or method_name.startswith("remove"):
- return f"ɾ��{method_name}������ݡ�"
- if method_name.startswith("update"):
- return f"����{method_name[6:]}��"
- if method_name.startswith("batch"):
- return f"����{method_name[5:]}��"
- if method_name.startswith("run") or method_name.startswith("test"):
- return f"����{method_name}���Ի�����"
- return f"{method_name} �Ĺ�����ڡ�"
- def format_callers(callers):
- if not callers:
- return "Spring ����ע�����"
- return "��".join(f"{{@code {c}}}" for c in callers)
- def make_method_javadoc(class_name, method_name, iface_methods):
- purpose = method_purpose(class_name, method_name, iface_methods)
- callers = get_callers(class_name, method_name)
- lines = [
- " /**",
- f" * {purpose}",
- f" * <p>���÷���{format_callers(callers)}��",
- " */",
- ]
- return lines
- def make_class_javadoc(class_name, implements=None):
- desc = CLASS_DESC.get(class_name, f"{class_name}��workflow impl �� Spring ����ʵ�֡�")
- callers = get_callers(class_name)
- lines = [
- "/**",
- f" * {desc}",
- f" * <p>��Ҫ���÷���{format_callers(callers)}��",
- " */",
- ]
- return lines
- def process_file(path: Path):
- text = path.read_text(encoding="utf-8")
- class_name = path.stem
- lines = text.splitlines(keepends=True)
- iface_methods = parse_interface_methods(class_name)
- # Find class declaration
- class_idx = None
- for i, line in enumerate(lines):
- if re.match(r'public\s+(?:final\s+)?class\s+' + re.escape(class_name), line):
- class_idx = i
- break
- if class_idx is None:
- return False
- modified = False
- new_lines = []
- # Check class-level javadoc
- has_class_doc = False
- if class_idx > 0:
- j = class_idx - 1
- while j >= 0 and (lines[j].strip().startswith("@") or lines[j].strip() == ""):
- j -= 1
- if j >= 0 and (lines[j].strip().startswith("/**") or lines[j].strip().endswith("*/")):
- has_class_doc = True
- i = 0
- while i < len(lines):
- line = lines[i]
- stripped = line.strip()
- # Insert class javadoc before class if missing
- if i == class_idx and not has_class_doc:
- doc = make_class_javadoc(class_name)
- for dl in doc:
- new_lines.append(dl + "\n")
- modified = True
- # Detect public methods (including @Override public)
- pub_match = re.match(r'^(\s*)(@Override\s+)?public\s+(?!class\b|interface\b|enum\b|static\s+class\b)', stripped)
- if pub_match and not has_javadoc(lines, i):
- indent = pub_match.group(1)
- # Extract method name
- sig = stripped
- # combine with next line if signature spans lines
- combined = sig
- k = i
- while ";" not in combined and "{" not in combined and k + 1 < len(lines):
- k += 1
- combined += " " + lines[k].strip()
- mm = re.search(r'\b(\w+)\s*\(', combined)
- if mm:
- method_name = mm.group(1)
- if method_name not in ("if", "for", "while", "switch", "catch", "synchronized"):
- doc_lines = make_method_javadoc(class_name, method_name, iface_methods)
- # Adjust indent
- for dl in doc_lines:
- new_lines.append(indent + dl.strip() + "\n")
- modified = True
- new_lines.append(line)
- i += 1
- if modified:
- path.write_text("".join(new_lines), encoding="utf-8")
- return modified
- def main():
- modified_files = []
- for jf in sorted(IMPL_DIR.glob("*.java")):
- if process_file(jf):
- modified_files.append(str(jf))
- print(f"MODIFIED: {jf.name}")
- else:
- print(f"SKIP (no changes): {jf.name}")
- print(f"\nTotal modified: {len(modified_files)}")
- return modified_files
- if __name__ == "__main__":
- main()
|