创建时间: 2026-06-22 15:32 | 步骤: 6 个
解析用户上传的代码文件与自定义分析要求,智能生成UML图与深度代码文本分析报告,并将两者逻辑融合为完整的结构化交付物
{
"workflow_id": "code_uml_text_analysis_workflow",
"input": {
"analysis_points": {
"field_name": "analysis_points",
"default": "",
"description": "用户对UML图和代码分析的自定义要求/关注点,可不填",
"example": "重点说明权限校验逻辑,忽略Getter/Setter",
"type": "string",
"required": false
},
"code": {
"field_name": "code",
"default": "",
"description": "用户输入的原始代码文本,多个文件可通过特定分隔符拼接",
"example": "/*\n * Copyright 2002-2019 Drew Noakes and contributors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * More information about this project is available at:\n *\n * https://drewnoakes.com/code/exif/\n * https://github.com/drewnoakes/metadata-extractor\n */\n\npackage com.drew.tools;\n\nimport com.adobe.internal.xmp.XMPException;\nimport com.adobe.internal.xmp.XMPIterator;\nimport com.adobe.internal.xmp.XMPMeta;\nimport com.adobe.internal.xmp.options.IteratorOptions;\nimport com.adobe.internal.xmp.properties.XMPPropertyInfo;\nimport com.drew.imaging.FileType;\nimport com.drew.imaging.FileTypeDetector;\nimport com.drew.imaging.ImageMetadataReader;\nimport com.drew.lang.StringUtil;\nimport com.drew.lang.annotations.NotNull;\nimport com.drew.lang.annotations.Nullable;\nimport com.drew.metadata.Directory;\nimport com.drew.metadata.Metadata;\nimport com.drew.metadata.Tag;\nimport com.drew.metadata.exif.ExifIFD0Directory;\nimport com.drew.metadata.exif.ExifSubIFDDirectory;\nimport com.drew.metadata.exif.ExifThumbnailDirectory;\nimport com.drew.metadata.file.FileSystemDirectory;\nimport com.drew.metadata.xmp.XmpDirectory;\n\nimport java.io.*;\nimport java.util.*;\n\n/**\n * @author Drew Noakes https://drewnoakes.com\n */\npublic class ProcessAllImagesInFolderUtility\n{\n public static void main(String[] args) throws IOException\n {\n Locale.setDefault(new Locale(\"en\", \"US\"));\n System.setProperty(\"user.timezone\", \"Australia/Sydney\");\n\n List<String> directories = new ArrayList<String>();\n\n FileHandler handler = null;\n PrintStream log = System.out;\n\n for (int i = 0; i < args.length; i++) {\n String arg = args[i];\n if (arg.equalsIgnoreCase(\"--text\")) {\n // If \"--text\" is specified, write the discovered metadata into a sub-folder relative to the image\n handler = new TextFileOutputHandler();\n } else if (arg.equalsIgnoreCase(\"--markdown\")) {\n // If \"--markdown\" is specified, write a summary table in markdown format to standard out\n handler = new MarkdownTableOutputHandler();\n } else if (arg.equalsIgnoreCase(\"--unknown\")) {\n // If \"--unknown\" is specified, write CSV tallying unknown tag counts\n handler = new UnknownTagHandler();\n } else if (arg.equalsIgnoreCase(\"--log-file\")) {\n if (i == args.length - 1) {\n printUsage();\n System.exit(1);\n }\n log = new PrintStream(new FileOutputStream(args[++i], false), true);\n } else {\n // Treat this argument as a directory\n directories.add(arg);\n }\n }\n\n if (directories.isEmpty()) {\n System.err.println(\"Expects one or more directories as arguments.\");\n printUsage();\n System.exit(1);\n }\n\n if (handler == null) {\n handler = new BasicFileHandler();\n }\n\n long start = System.nanoTime();\n\n for (String directory : directories) {\n processDirectory(new File(directory), handler, \"\", log);\n }\n\n handler.onScanCompleted(log);\n\n System.out.println(String.format(\"Completed in %d ms\", (System.nanoTime() - start) / 1000000));\n\n if (log != System.out) {\n log.close();\n }\n }\n\n private static void printUsage()\n {\n System.out.println(\"Usage:\");\n System.out.println();\n System.out.println(\" java com.drew.tools.ProcessAllImagesInFolderUtility [--text|--markdown|--unknown] [--log-file <file-name>]\");\n }\n\n private static void processDirectory(@NotNull File path, @NotNull FileHandler handler, @NotNull String relativePath, PrintStream log)\n {\n handler.onStartingDirectory(path);\n\n String[] pathItems = path.list();\n\n if (pathItems == null) {\n return;\n }\n\n // Order alphabetically so that output is stable across invocations\n Arrays.sort(pathItems);\n\n for (String pathItem : pathItems) {\n File file = new File(path, pathItem);\n\n if (file.isDirectory()) {\n processDirectory(file, handler, relativePath.length() == 0 ? pathItem : relativePath + \"/\" + pathItem, log);\n } else if (handler.shouldProcess(file)) {\n\n handler.onBeforeExtraction(file, log, relativePath);\n\n // Read metadata\n final Metadata metadata;\n try {\n metadata = ImageMetadataReader.readMetadata(file);\n } catch (Throwable t) {\n handler.onExtractionError(file, t, log);\n continue;\n }\n\n handler.onExtractionSuccess(file, metadata, relativePath, log);\n }\n }\n }\n\n interface FileHandler\n {\n /** Called when the scan is about to start processing files in directory <code>path</code>. */\n void onStartingDirectory(@NotNull File directoryPath);\n\n /** Called to determine whether the implementation should process <code>filePath</code>. */\n boolean shouldProcess(@NotNull File file);\n\n /** Called before extraction is performed on <code>filePath</code>. */\n void onBeforeExtraction(@NotNull File file, @NotNull PrintStream log, @NotNull String relativePath);\n\n /** Called when extraction on <code>filePath</code> completed without an exception. */\n void onExtractionSuccess(@NotNull File file, @NotNull Metadata metadata, @NotNull String relativePath, @NotNull PrintStream log);\n\n /** Called when extraction on <code>filePath</code> resulted in an exception. */\n void onExtractionError(@NotNull File file, @NotNull Throwable throwable, @NotNull PrintStream log);\n\n /** Called when all files have been processed. */\n void onScanCompleted(@NotNull PrintStream log);\n }\n\n abstract static class FileHandlerBase implements FileHandler\n {\n // TODO obtain these from FileType enum directly\n private final Set<String> _supportedExtensions = new HashSet<String>(\n Arrays.asList(\n \"3fr\",\n \"3g2\",\n \"3gp\",\n \"ai\",\n \"arw\",\n \"avi\",\n \"avif\",\n \"bmp\",\n \"cam\",\n \"cr2\",\n \"cr3\",\n \"crw\",\n \"dcr\",\n \"dng\",\n \"eps\",\n \"fuzzed\",\n \"gif\",\n \"gpr\",\n \"heic\",\n \"heif\",\n \"ico\",\n \"j2c\",\n \"jp2\",\n \"jpeg\",\n \"jpf\",\n \"jpg\",\n \"jpm\",\n \"jxr\",\n \"kdc\",\n \"m2ts\",\n \"m2v\",\n \"m4a\",\n \"m4v\",\n \"mj2\",\n \"mov\",\n \"mp3\",\n \"mp4\",\n \"mpg\",\n \"mts\",\n \"nef\",\n \"orf\",\n \"pbm\",\n \"pcx\",\n \"pef\",\n \"pgm\",\n \"png\",\n \"pnm\",\n \"ppm\",\n \"psd\",\n \"raf\",\n \"rw2\",\n \"rwl\",\n \"srw\",\n \"tif\",\n \"tiff\",\n \"wav\",\n \"webp\",\n \"x3f\"));\n\n private int _processedFileCount = 0;\n private int _exceptionCount = 0;\n private int _errorCount = 0;\n private long _processedByteCount = 0;\n\n public void onStartingDirectory(@NotNull File directoryPath)\n {}\n\n public boolean shouldProcess(@NotNull File file)\n {\n String extension = getExtension(file);\n return extension != null && _supportedExtensions.contains(extension.toLowerCase());\n }\n\n public void onBeforeExtraction(@NotNull File file, @NotNull PrintStream log, @NotNull String relativePath)\n {\n _processedFileCount++;\n _processedByteCount += file.length();\n }\n\n public void onExtractionError(@NotNull File file, @NotNull Throwable throwable, @NotNull PrintStream log)\n {\n _exceptionCount++;\n log.printf(\"\\t[%s] %s\\n\", throwable.getClass().getName(), throwable.getMessage());\n }\n\n public void onExtractionSuccess(@NotNull File file, @NotNull Metadata metadata, @NotNull String relativePath, @NotNull PrintStream log)\n {\n if (metadata.hasErrors()) {\n log.print(file);\n log.print('\\n');\n for (Directory directory : metadata.getDirectories()) {\n if (!directory.hasErrors())\n continue;\n for (String error : directory.getErrors()) {\n log.printf(\"\\t[%s] %s\\n\", directory.getName(), error);\n _errorCount++;\n }\n }\n }\n }\n\n public void onScanCompleted(@NotNull PrintStream log)\n {\n if (_processedFileCount > 0) {\n log.print(String.format(\n \"Processed %,d files (%,d bytes) with %,d exceptions and %,d file errors\\n\",\n _processedFileCount, _processedByteCount, _exceptionCount, _errorCount\n ));\n }\n }\n\n @Nullable\n protected String getExtension(@NotNull File file)\n {\n String fileName = file.getName();\n int i = fileName.lastIndexOf('.');\n if (i == -1)\n return null;\n if (i == fileName.length() - 1)\n return null;\n return fileName.substring(i + 1);\n }\n }\n\n /**\n * Writes a text file containing the extracted metadata for each input file.\n */\n static class TextFileOutputHandler extends FileHandlerBase\n {\n /** Standardise line ending so that generated files can be more easily diffed. */\n private static final String NEW_LINE = \"\\n\";\n\n @Override\n public void onStartingDirectory(@NotNull File directoryPath)\n {\n super.onStartingDirectory(directoryPath);\n\n // Delete any existing 'metadata' folder\n File metadataDirectory = new File(directoryPath + \"/metadata/java\");\n if (metadataDirectory.exists())\n deleteRecursively(metadataDirectory);\n }\n\n private static void deleteRecursively(@NotNull File directory)\n {\n if (!directory.isDirectory())\n throw new IllegalArgumentException(\"Must be a directory.\");\n\n if (directory.exists()) {\n String[] list = directory.list();\n if (list != null) {\n for (String item : list) {\n File file = new File(item);\n if (file.isDirectory())\n deleteRecursively(file);\n else\n file.delete();\n }\n }\n }\n\n directory.delete();\n }\n\n @Override\n public void onBeforeExtraction(@NotNull File file, @NotNull PrintStream log, @NotNull String relativePath)\n {\n super.onBeforeExtraction(file, log, relativePath);\n log.print(file.getAbsoluteFile());\n log.print(NEW_LINE);\n }\n\n @Override\n public void onExtractionSuccess(@NotNull File file, @NotNull Metadata metadata, @NotNull String relativePath, @NotNull PrintStream log)\n {\n super.onExtractionSuccess(file, metadata, relativePath, log);\n\n try {\n PrintWriter writer = null;\n try\n {\n writer = openWriter(file);\n\n // Write any errors\n if (metadata.hasErrors()) {\n for (Directory directory : metadata.getDirectories()) {\n if (!directory.hasErrors())\n continue;\n for (String error : directory.getErrors())\n writer.format(\"[ERROR: %s] %s%s\", directory.getName(), error, NEW_LINE);\n }\n writer.write(NEW_LINE);\n }\n\n // Write tag values for each directory\n for (Directory directory : metadata.getDirectories()) {\n String directoryName = directory.getName();\n // Write the directory's tags\n for (Tag tag : directory.getTags()) {\n String tagName = tag.getTagName();\n String description;\n try {\n description = tag.getDescription();\n } catch (Exception ex) {\n description = \"ERROR: \" + ex.getMessage();\n }\n if (description == null)\n description = \"\";\n // Skip the file write-time as this changes based on the time at which the regression test image repository was cloned\n if (directory instanceof FileSystemDirectory && tag.getTagType() == FileSystemDirectory.TAG_FILE_MODIFIED_DATE)\n description = \"<omitted for regression testing as checkout dependent>\";\n writer.format(\"[%s - %s] %s = %s%s\", directoryName, tag.getTagTypeHex(), tagName, description, NEW_LINE);\n }\n if (directory.getTagCount() != 0)\n writer.write(NEW_LINE);\n // Special handling for XMP directory data\n if (directory instanceof XmpDirectory) {\n boolean wrote = false;\n XmpDirectory xmpDirectory = (XmpDirectory)directory;\n XMPMeta xmpMeta = xmpDirectory.getXMPMeta();\n try {\n IteratorOptions options = new IteratorOptions().setJustLeafnodes(true);\n XMPIterator iterator = xmpMeta.iterator(options);\n while (iterator.hasNext()) {\n XMPPropertyInfo prop = (XMPPropertyInfo)iterator.next();\n String ns = prop.getNamespace();\n String path = prop.getPath();\n String value = prop.getValue();\n\n if (path == null)\n continue;\n if (ns == null)\n ns = \"\";\n\n final int MAX_XMP_VALUE_LENGTH = 512;\n if (value == null)\n value = \"\";\n else if (value.length() > MAX_XMP_VALUE_LENGTH)\n value = String.format(\"%s <truncated from %d characters>\", value.substring(0, MAX_XMP_VALUE_LENGTH), value.length());\n\n writer.format(\"[XMPMeta - %s] %s = %s%s\", ns, path, value, NEW_LINE);\n wrote = true;\n }\n } catch (XMPException e) {\n e.printStackTrace();\n }\n if (wrote)\n writer.write(NEW_LINE);\n }\n }\n\n // Write file structure\n writeHierarchyLevel(metadata, writer, null, 0);\n\n writer.write(NEW_LINE);\n } finally {\n closeWriter(writer);\n }\n } catch (IOException e) {\n e.printStackTrace();\n }\n }\n\n private static void writeHierarchyLevel(@NotNull Metadata metadata, @NotNull PrintWriter writer, @Nullable Directory parent, int level)\n {\n final int indent = 4;\n\n for (Directory child : metadata.getDirectories()) {\n if (parent == null) {\n if (child.getParent() != null)\n continue;\n } else if (!parent.equals(child.getParent())) {\n continue;\n }\n\n for (int i = 0; i < level*indent; i++) {\n writer.write(' ');\n }\n writer.write(\"- \");\n writer.write(child.getName());\n writer.write(NEW_LINE);\n writeHierarchyLevel(metadata, writer, child, level + 1);\n }\n }\n\n @Override\n public void onExtractionError(@NotNull File file, @NotNull Throwable throwable, @NotNull PrintStream log)\n {\n super.onExtractionError(file, throwable, log);\n\n try {\n PrintWriter writer = null;\n try {\n writer = openWriter(file);\n writer.write(\"EXCEPTION: \" + throwable.getMessage() + NEW_LINE);\n writer.write(NEW_LINE);\n } finally {\n closeWriter(writer);\n }\n } catch (IOException e) {\n log.printf(\"IO exception writing metadata file: %s%s\", e.getMessage(), NEW_LINE);\n }\n }\n\n @NotNull\n private static PrintWriter openWriter(@NotNull File file) throws IOException\n {\n // Create the output directory if it doesn't exist\n File metadataDir = new File(String.format(\"%s/metadata\", file.getParent()));\n if (!metadataDir.exists())\n metadataDir.mkdir();\n\n File javaDir = new File(String.format(\"%s/metadata/java\", file.getParent()));\n if (!javaDir.exists())\n javaDir.mkdir();\n\n String outputPath = String.format(\"%s/metadata/java/%s.txt\", file.getParent(), file.getName());\n Writer writer = new OutputStreamWriter(\n new FileOutputStream(outputPath),\n \"UTF-8\"\n );\n writer.write(\"FILE: \" + file.getName() + NEW_LINE);\n\n // Detect file type\n BufferedInputStream stream = null;\n try {\n stream = new BufferedInputStream(new FileInputStream(file));\n FileType fileType = FileTypeDetector.detectFileType(stream, file.getName());\n writer.write(String.format(\"TYPE: %s\" + NEW_LINE, fileType.toString().toUpperCase()));\n writer.write(NEW_LINE);\n } finally {\n if (stream != null) {\n stream.close();\n }\n }\n\n return new PrintWriter(writer);\n }\n\n private static void closeWriter(@Nullable Writer writer) throws IOException\n {\n if (writer != null) {\n writer.write(\"Generated using metadata-extractor\" + NEW_LINE);\n writer.write(\"https://drewnoakes.com/code/exif/\" + NEW_LINE);\n writer.flush();\n writer.close();\n }\n }\n }\n\n /**\n * Creates a table describing sample images using Wiki markdown.\n */\n static class MarkdownTableOutputHandler extends FileHandlerBase\n {\n private final Map<String, String> _extensionEquivalence = new HashMap<String, String>();\n private final Map<String, List<Row>> _rowListByExtension = new HashMap<String, List<Row>>();\n\n static class Row\n {\n final File file;\n final Metadata metadata;\n @NotNull final String relativePath;\n @Nullable private String manufacturer;\n @Nullable private String model;\n @Nullable private String exifVersion;\n @Nullable private String thumbnail;\n @Nullable private String makernote;\n\n Row(@NotNull File file, @NotNull Metadata metadata, @NotNull String relativePath)\n {\n this.file = file;\n this.metadata = metadata;\n this.relativePath = relativePath;\n\n ExifIFD0Directory ifd0Dir = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);\n ExifSubIFDDirectory subIfdDir = metadata.getFirstDirectoryOfType(ExifSubIFDDirectory.class);\n ExifThumbnailDirectory thumbDir = metadata.getFirstDirectoryOfType(ExifThumbnailDirectory.class);\n if (ifd0Dir != null) {\n manufacturer = ifd0Dir.getDescription(ExifIFD0Directory.TAG_MAKE);\n model = ifd0Dir.getDescription(ExifIFD0Directory.TAG_MODEL);\n }\n boolean hasMakernoteData = false;\n if (subIfdDir != null) {\n exifVersion = subIfdDir.getDescription(ExifSubIFDDirectory.TAG_EXIF_VERSION);\n hasMakernoteData = subIfdDir.containsTag(ExifSubIFDDirectory.TAG_MAKERNOTE);\n }\n if (thumbDir != null) {\n Integer width = thumbDir.getInteger(ExifThumbnailDirectory.TAG_IMAGE_WIDTH);\n Integer height = thumbDir.getInteger(ExifThumbnailDirectory.TAG_IMAGE_HEIGHT);\n thumbnail = width != null && height != null\n ? String.format(\"Yes (%s x %s)\", width, height)\n : \"Yes\";\n }\n for (Directory directory : metadata.getDirectories()) {\n if (directory.getClass().getName().contains(\"Makernote\")) {\n makernote = directory.getName().replace(\"Makernote\", \"\").trim();\n break;\n }\n }\n if (makernote == null) {\n makernote = hasMakernoteData ? \"(Unknown)\" : \"N/A\";\n }\n }\n }\n\n public MarkdownTableOutputHandler()\n {\n _extensionEquivalence.put(\"jpeg\", \"jpg\");\n }\n\n @Override\n public void onExtractionSuccess(@NotNull File file, @NotNull Metadata metadata, @NotNull String relativePath, @NotNull PrintStream log)\n {\n super.onExtractionSuccess(file, metadata, relativePath, log);\n\n String extension = getExtension(file);\n\n if (extension == null) {\n return;\n }\n\n // Sanitise the extension\n extension = extension.toLowerCase();\n if (_extensionEquivalence.containsKey(extension))\n extension = _extensionEquivalence.get(extension);\n\n List<Row> list = _rowListByExtension.get(extension);\n if (list == null) {\n list = new ArrayList<Row>();\n _rowListByExtension.put(extension, list);\n }\n list.add(new Row(file, metadata, relativePath));\n }\n\n @Override\n public void onScanCompleted(@NotNull PrintStream log)\n {\n super.onScanCompleted(log);\n\n OutputStream outputStream = null;\n PrintStream stream = null;\n try {\n outputStream = new FileOutputStream(\"../wiki/ImageDatabaseSummary.md\", false);\n stream = new PrintStream(outputStream, false);\n writeOutput(stream);\n stream.flush();\n } catch (IOException e) {\n e.printStackTrace();\n } finally {\n if (stream != null)\n stream.close();\n if (outputStream != null)\n try {\n outputStream.close();\n } catch (IOException e) {\n e.printStackTrace();\n }\n }\n }\n\n private void writeOutput(@NotNull PrintStream stream) throws IOException\n {\n Writer writer = new OutputStreamWriter(stream);\n writer.write(\"# Image Database Summary\\n\\n\");\n\n for (Map.Entry<String, List<Row>> entry : _rowListByExtension.entrySet()) {\n String extension = entry.getKey();\n writer.write(\"## \" + extension.toUpperCase() + \" Files\\n\\n\");\n\n writer.write(\"File|Manufacturer|Model|Dir Count|Exif?|Makernote|Thumbnail|All Data\\n\");\n writer.write(\"----|------------|-----|---------|-----|---------|---------|--------\\n\");\n\n List<Row> rows = entry.getValue();\n\n // Order by manufacturer, then model\n Collections.sort(rows, new Comparator<Row>() {\n public int compare(Row o1, Row o2)\n {\n int c1 = StringUtil.compare(o1.manufacturer, o2.manufacturer);\n return c1 != 0 ? c1 : StringUtil.compare(o1.model, o2.model);\n }\n });\n\n for (Row row : rows) {\n writer.write(String.format(\"[%s](https://raw.githubusercontent.com/drewnoakes/metadata-extractor-images/master/%s/%s)|%s|%s|%d|%s|%s|%s|[metadata](https://raw.githubusercontent.com/drewnoakes/metadata-extractor-images/master/%s/metadata/%s.txt)\\n\",\n row.file.getName(),\n row.relativePath,\n StringUtil.urlEncode(row.file.getName()),\n row.manufacturer == null ? \"\" : row.manufacturer,\n row.model == null ? \"\" : row.model,\n row.metadata.getDirectoryCount(),\n row.exifVersion == null ? \"\" : row.exifVersion,\n row.makernote == null ? \"\" : row.makernote,\n row.thumbnail == null ? \"\" : row.thumbnail,\n row.relativePath,\n StringUtil.urlEncode(row.file.getName()).toLowerCase()\n ));\n }\n\n writer.write('\\n');\n }\n writer.flush();\n }\n }\n\n /**\n * Keeps track of unknown tags.\n */\n static class UnknownTagHandler extends FileHandlerBase\n {\n private HashMap<String, HashMap<Integer, Integer>> _occurrenceCountByTagByDirectory = new HashMap<String, HashMap<Integer, Integer>>();\n\n @Override\n public void onExtractionSuccess(@NotNull File file, @NotNull Metadata metadata, @NotNull String relativePath, @NotNull PrintStream log)\n {\n super.onExtractionSuccess(file, metadata, relativePath, log);\n\n for (Directory directory : metadata.getDirectories()) {\n for (Tag tag : directory.getTags()) {\n\n // Only interested in unknown tags (those without names)\n if (tag.hasTagName())\n continue;\n\n HashMap<Integer, Integer> occurrenceCountByTag = _occurrenceCountByTagByDirectory.get(directory.getName());\n if (occurrenceCountByTag == null) {\n occurrenceCountByTag = new HashMap<Integer, Integer>();\n _occurrenceCountByTagByDirectory.put(directory.getName(), occurrenceCountByTag);\n }\n\n Integer count = occurrenceCountByTag.get(tag.getTagType());\n if (count == null) {\n count = 0;\n occurrenceCountByTag.put(tag.getTagType(), 0);\n }\n\n occurrenceCountByTag.put(tag.getTagType(), count + 1);\n }\n }\n }\n\n @Override\n public void onScanCompleted(@NotNull PrintStream log)\n {\n super.onScanCompleted(log);\n\n for (Map.Entry<String, HashMap<Integer, Integer>> pair1 : _occurrenceCountByTagByDirectory.entrySet()) {\n String directoryName = pair1.getKey();\n List<Map.Entry<Integer, Integer>> counts = new ArrayList<Map.Entry<Integer, Integer>>(pair1.getValue().entrySet());\n Collections.sort(counts, new Comparator<Map.Entry<Integer, Integer>>()\n {\n public int compare(Map.Entry<Integer, Integer> o1, Map.Entry<Integer, Integer> o2)\n {\n return o2.getValue().compareTo(o1.getValue());\n }\n });\n for (Map.Entry<Integer, Integer> pair2 : counts) {\n Integer tagType = pair2.getKey();\n Integer count = pair2.getValue();\n log.format(\"%s, 0x%04X, %d\\n\", directoryName, tagType, count);\n }\n }\n }\n }\n\n /**\n * Does nothing with the output except enumerate it in memory and format descriptions. This is useful in order to\n * flush out any potential exceptions raised during the formatting of extracted value descriptions.\n */\n static class BasicFileHandler extends FileHandlerBase\n {\n @Override\n public void onExtractionSuccess(@NotNull File file, @NotNull Metadata metadata, @NotNull String relativePath, @NotNull PrintStream log)\n {\n super.onExtractionSuccess(file, metadata, relativePath, log);\n\n // Iterate through all values, calling toString to flush out any formatting exceptions\n for (Directory directory : metadata.getDirectories()) {\n directory.getName();\n for (Tag tag : directory.getTags()) {\n tag.getTagName();\n tag.getDescription();\n }\n }\n }\n }\n}",
"type": "string",
"required": true
}
},
"steps": [
{
"point_code": "",
"id": "step_1_code_structure_analyzer",
"result": [],
"api_code": "code_structure_analyzer",
"backend_type": "prompt_api",
"status": "PENDING",
"name": "代码结构智能解析",
"action": "run",
"params": {
"analysis_points": "{{@input.analysis_points}}",
"code": "{{@input.code}}"
}
},
{
"id": "step_2_uml_prompt_generator",
"point_code": "",
"result": [],
"api_code": "uml_prompt_generator",
"backend_type": "prompt_api",
"status": "PENDING",
"name": "UML图PlantUML提示词生成",
"action": "run",
"params": {
"structured_code_segments": "{{@step_1_code_structure_analyzer.result.data.structured_code_segments}}",
"language_type": "{{@step_1_code_structure_analyzer.result.data.language_type}}",
"structured_intent": "{{@step_1_code_structure_analyzer.result.data.structured_intent}}",
"extended_analysis_dimensions": "{{@step_1_code_structure_analyzer.result.data.extended_analysis_dimensions}}"
},
"depends_on": [
"step_1_code_structure_analyzer"
]
},
{
"id": "step_3_code_text_analyzer",
"point_code": "",
"result": [],
"api_code": "code_text_analyzer",
"backend_type": "prompt_api",
"status": "PENDING",
"name": "代码文本结构化深度分析",
"action": "run",
"params": {
"structured_code_segments": "{{@step_1_code_structure_analyzer.result.data.structured_code_segments}}",
"language_type": "{{@step_1_code_structure_analyzer.result.data.language_type}}",
"structured_intent": "{{@step_1_code_structure_analyzer.result.data.structured_intent}}",
"extended_analysis_dimensions": "{{@step_1_code_structure_analyzer.result.data.extended_analysis_dimensions}}"
},
"depends_on": [
"step_1_code_structure_analyzer"
]
},
{
"id": "step_4_generate_uml_image",
"point_code": "1",
"result": [],
"api_code": "3373",
"backend_type": "sys_api",
"status": "PENDING",
"name": "启动UML图像生成任务",
"action": "run",
"params": {
"prompt": "{{@step_2_uml_prompt_generator.result.data.plantuml_prompt}}",
"aspect_ratio": "1:1"
},
"depends_on": [
"step_2_uml_prompt_generator"
]
},
{
"id": "step_5_poll_uml_image_status",
"point_code": "",
"result": [],
"api_code": "polling",
"backend_type": "sys_api",
"status": "PENDING",
"name": "轮询UML图像生成状态",
"action": "run",
"params": {
"result_url": "{{@step_4_generate_uml_image.result.data.result_url}}"
},
"depends_on": [
"step_4_generate_uml_image"
],
"polling": {
"interval_seconds": 5,
"timeout_seconds": 600,
"success_condition": "{{@step_5_poll_uml_image_status.result.data.task_status}} === 'SUCCEEDED'"
}
},
{
"id": "step_6_analysis_report_merger",
"point_code": "",
"result": [],
"api_code": "analysis_report_merger",
"backend_type": "prompt_api",
"status": "PENDING",
"name": "UML图与文本分析智能融合",
"action": "run",
"params": {
"architecture_summary": "{{@step_3_code_text_analyzer.result.data.architecture_summary}}",
"key_findings": "{{@step_3_code_text_analyzer.result.data.key_findings}}",
"uml_image_url": "{{@step_5_poll_uml_image_status.result.data.result_url}}",
"uml_focus_description": "{{@step_2_uml_prompt_generator.result.data.uml_focus_description}}",
"analysis_report": "{{@step_3_code_text_analyzer.result.data.analysis_report}}"
},
"depends_on": [
"step_5_poll_uml_image_status",
"step_3_code_text_analyzer"
]
}
],
"output": {
"final_integrated_result": {
"field_name": "final_integrated_result",
"description": "融合UML图与文本分析的最终完整结构化交付物",
"value": "{{@step_6_analysis_report_merger.result.data.final_integrated_result}}",
"type": "object"
},
"uml_image_url": {
"field_name": "uml_image_url",
"description": "生成的UML图像URL地址",
"value": "{{@step_5_poll_uml_image_status.result.data.result_url}}",
"type": "string"
},
"consistency_check": {
"field_name": "consistency_check",
"description": "UML图与文本分析的一致性校验结果",
"value": "{{@step_6_analysis_report_merger.result.data.final_integrated_result.consistency_check}}",
"type": "object"
}
}
} 提交后立即返回 task_id,系统每 2 秒自动轮询一次运行状态并可视化执行步骤,适合耗时较长的 Skill。
analysis_pointsstring用户对UML图和代码分析的自定义要求/关注点,可不填
code必填string用户输入的原始代码文本,多个文件可通过特定分隔符拼接
把这个 Skill 接入你的应用或 AI Agent —— 自带 REST API、OpenAPI 规范与 Agent 技能包。
task_id,再轮询查询任务状态与结果。下方按调用顺序列出两个端点。 https://route.showapi.com/flow/execute/6a38a9b83c51f2dc1a01de44?appKey={AppKey}https://route.showapi.com/flow/task/query/{task_id}?appKey={AppKey}执行中途出错时
status 返回 FAILED, remark 给出错误信息; step_status_list 仅包含已执行到的部分阶段, output 为已产出的部分结果(可能为空或不完整)。
所有 API 响应均使用平台统一封装,业务数据嵌套在 showapi_res_body 中

7*24小时服务
保证您的售后无忧

1v1专属服务
保证服务质量

担保交易
全程担保交易保证资金安全

服务全程监管
全周期保证商品服务质量
© 2015-2023 WWW.SHOWAPI.COM ALL RIGHTS RESERVED.昆明秀派科技有限公司
本网站所列接口及文档全部由SHOWAPI网站提供,并对其拥有最终解释权 POWERED BY SHOWAPI