diff --git a/.editorconfig b/.editorconfig index 8125a2c..a5547cf 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,100 +1,100 @@ -# Rules in this file were initially inferred by Visual Studio IntelliCode from the C:\workspaces\opensource\uuac codebase based on best match to current usage at 1/18/2022 -# There already existed an .editorconfig file in this directory. Copy rules from this .editorconfig.inferred file to the existing .editorconfig file as desired to have them take effect at this location. -# You can modify the rules from these initially generated values to suit your own policies -# You can learn more about editorconfig here: https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference -[*.cs] - -file_header_template = Copyright (c) Xuanye Wong. All rights reserved.\nLicensed under MIT license -dotnet_diagnostic.IDE0073.severity = warning -#Core editorconfig formatting - indentation - -#use soft tabs (spaces) for indentation -indent_style = space -indent_size = 4 -#Formatting - new line options - -#require members of object intializers to be on separate lines -csharp_new_line_before_members_in_object_initializers = true -#require braces to be on a new line for lambdas, types, control_blocks, and methods (also known as "Allman" style) -csharp_new_line_before_open_brace = all - -#Formatting - organize using options - -#do not place System.* using directives before other using directives -dotnet_sort_system_directives_first = false - -#Formatting - spacing options - -#require NO space between a cast and the value -csharp_space_after_cast = false -#require a space before the colon for bases or interfaces in a type declaration -csharp_space_after_colon_in_inheritance_clause = true -#require a space after a keyword in a control flow statement such as a for loop -csharp_space_after_keywords_in_control_flow_statements = true -#require a space before the colon for bases or interfaces in a type declaration -csharp_space_before_colon_in_inheritance_clause = true -#remove space within empty argument list parentheses -csharp_space_between_method_call_empty_parameter_list_parentheses = false -#remove space between method call name and opening parenthesis -csharp_space_between_method_call_name_and_opening_parenthesis = false -#do not place space characters after the opening parenthesis and before the closing parenthesis of a method call -csharp_space_between_method_call_parameter_list_parentheses = false -#place a space character after the opening parenthesis and before the closing parenthesis of a method declaration parameter list. -csharp_space_between_method_declaration_parameter_list_parentheses = false - -#Formatting - wrapping options - -#leave code block on separate lines -csharp_preserve_single_line_blocks = true - -#Style - Code block preferences - -#prefer curly braces even for one line of code -csharp_prefer_braces = when_multiline:suggestion - -#Style - expression bodied member options - -#prefer block bodies for accessors -csharp_style_expression_bodied_accessors = false:suggestion -#prefer block bodies for constructors -csharp_style_expression_bodied_constructors = false:suggestion -#prefer block bodies for methods -csharp_style_expression_bodied_methods = false:suggestion -#prefer block bodies for properties -csharp_style_expression_bodied_properties = true:suggestion - -#Style - Expression-level preferences - -#prefer objects to be initialized using object initializers when possible -dotnet_style_object_initializer = true:suggestion - -#Style - implicit and explicit types - -#prefer var over explicit type in all cases, unless overridden by another code style rule -csharp_style_var_elsewhere = true:suggestion -#prefer var when the type is already mentioned on the right-hand side of a declaration expression -csharp_style_var_when_type_is_apparent = true:suggestion - -#Style - language keyword and framework type options - -#prefer the language keyword for local variables, method parameters, and class members, instead of the type name, for types that have a keyword to represent them -dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion - -#Style - modifier options - -#prefer accessibility modifiers to be declared except for public interface members. This will currently not differ from always and will act as future proofing for if C# adds default interface methods. -dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion - -#Style - Modifier preferences - -#when this rule is set to a list of modifiers, prefer the specified ordering. -csharp_preferred_modifier_order = public,private,static,readonly:suggestion - -#Style - qualification options - -#prefer fields not to be prefaced with this. or Me. in Visual Basic -dotnet_style_qualification_for_field = false:suggestion -#prefer properties not to be prefaced with this. or Me. in Visual Basic +# Rules in this file were initially inferred by Visual Studio IntelliCode from the C:\workspaces\opensource\uuac codebase based on best match to current usage at 1/18/2022 +# There already existed an .editorconfig file in this directory. Copy rules from this .editorconfig.inferred file to the existing .editorconfig file as desired to have them take effect at this location. +# You can modify the rules from these initially generated values to suit your own policies +# You can learn more about editorconfig here: https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference +[*.cs] + +file_header_template = Copyright (c) Xuanye Wang. All rights reserved.\nLicensed under MIT license +dotnet_diagnostic.IDE0073.severity = warning +#Core editorconfig formatting - indentation + +#use soft tabs (spaces) for indentation +indent_style = space +indent_size = 4 +#Formatting - new line options + +#require members of object intializers to be on separate lines +csharp_new_line_before_members_in_object_initializers = true +#require braces to be on a new line for lambdas, types, control_blocks, and methods (also known as "Allman" style) +csharp_new_line_before_open_brace = all + +#Formatting - organize using options + +#do place System.* using directives before other using directives +dotnet_sort_system_directives_first = true + +#Formatting - spacing options + +#require NO space between a cast and the value +csharp_space_after_cast = false +#require a space before the colon for bases or interfaces in a type declaration +csharp_space_after_colon_in_inheritance_clause = true +#require a space after a keyword in a control flow statement such as a for loop +csharp_space_after_keywords_in_control_flow_statements = true +#require a space before the colon for bases or interfaces in a type declaration +csharp_space_before_colon_in_inheritance_clause = true +#remove space within empty argument list parentheses +csharp_space_between_method_call_empty_parameter_list_parentheses = false +#remove space between method call name and opening parenthesis +csharp_space_between_method_call_name_and_opening_parenthesis = false +#do not place space characters after the opening parenthesis and before the closing parenthesis of a method call +csharp_space_between_method_call_parameter_list_parentheses = false +#place a space character after the opening parenthesis and before the closing parenthesis of a method declaration parameter list. +csharp_space_between_method_declaration_parameter_list_parentheses = false + +#Formatting - wrapping options + +#leave code block on separate lines +csharp_preserve_single_line_blocks = true + +#Style - Code block preferences + +#prefer curly braces even for one line of code +csharp_prefer_braces = when_multiline:suggestion + +#Style - expression bodied member options + +#prefer block bodies for accessors +csharp_style_expression_bodied_accessors = false:suggestion +#prefer block bodies for constructors +csharp_style_expression_bodied_constructors = false:suggestion +#prefer block bodies for methods +csharp_style_expression_bodied_methods = false:suggestion +#prefer block bodies for properties +csharp_style_expression_bodied_properties = true:suggestion + +#Style - Expression-level preferences + +#prefer objects to be initialized using object initializers when possible +dotnet_style_object_initializer = true:suggestion + +#Style - implicit and explicit types + +#prefer var over explicit type in all cases, unless overridden by another code style rule +csharp_style_var_elsewhere = true:suggestion +#prefer var when the type is already mentioned on the right-hand side of a declaration expression +csharp_style_var_when_type_is_apparent = true:suggestion + +#Style - language keyword and framework type options + +#prefer the language keyword for local variables, method parameters, and class members, instead of the type name, for types that have a keyword to represent them +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion + +#Style - modifier options + +#prefer accessibility modifiers to be declared except for public interface members. This will currently not differ from always and will act as future proofing for if C# adds default interface methods. +dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion + +#Style - Modifier preferences + +#when this rule is set to a list of modifiers, prefer the specified ordering. +csharp_preferred_modifier_order = public,private,static,readonly:suggestion + +#Style - qualification options + +#prefer fields not to be prefaced with this. or Me. in Visual Basic +dotnet_style_qualification_for_field = false:suggestion +#prefer properties not to be prefaced with this. or Me. in Visual Basic dotnet_style_qualification_for_property = false:suggestion csharp_indent_labels = one_less_than_current csharp_using_directive_placement = outside_namespace:silent @@ -132,7 +132,7 @@ csharp_style_prefer_extended_property_pattern = true:suggestion csharp_style_prefer_not_pattern = true:suggestion csharp_style_var_for_built_in_types = false:silent csharp_space_around_binary_operators = before_and_after -csharp_style_prefer_readonly_struct = true:suggestion +csharp_style_prefer_readonly_struct = true:suggestion [*.{cs,vb}] #### Naming styles #### diff --git a/DotXxlJob.sln b/DotXxlJob.sln index a2013f7..c919323 100644 --- a/DotXxlJob.sln +++ b/DotXxlJob.sln @@ -13,6 +13,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCoreExecutor", "sampl EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotXxlJob.Core", "src\DotXxlJob.Core\DotXxlJob.Core.csproj", "{4584B4D5-0DA9-425F-A4C7-7A19A75D3E73}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotXxlJob.Core.UnitTests", "tests\DotXxlJob.Core.UnitTests\DotXxlJob.Core.UnitTests.csproj", "{7D0FB732-636F-4B72-B1E9-262077F1DB82}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -47,6 +49,18 @@ Global {4584B4D5-0DA9-425F-A4C7-7A19A75D3E73}.Release|x64.Build.0 = Release|Any CPU {4584B4D5-0DA9-425F-A4C7-7A19A75D3E73}.Release|x86.ActiveCfg = Release|Any CPU {4584B4D5-0DA9-425F-A4C7-7A19A75D3E73}.Release|x86.Build.0 = Release|Any CPU + {7D0FB732-636F-4B72-B1E9-262077F1DB82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7D0FB732-636F-4B72-B1E9-262077F1DB82}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7D0FB732-636F-4B72-B1E9-262077F1DB82}.Debug|x64.ActiveCfg = Debug|Any CPU + {7D0FB732-636F-4B72-B1E9-262077F1DB82}.Debug|x64.Build.0 = Debug|Any CPU + {7D0FB732-636F-4B72-B1E9-262077F1DB82}.Debug|x86.ActiveCfg = Debug|Any CPU + {7D0FB732-636F-4B72-B1E9-262077F1DB82}.Debug|x86.Build.0 = Debug|Any CPU + {7D0FB732-636F-4B72-B1E9-262077F1DB82}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7D0FB732-636F-4B72-B1E9-262077F1DB82}.Release|Any CPU.Build.0 = Release|Any CPU + {7D0FB732-636F-4B72-B1E9-262077F1DB82}.Release|x64.ActiveCfg = Release|Any CPU + {7D0FB732-636F-4B72-B1E9-262077F1DB82}.Release|x64.Build.0 = Release|Any CPU + {7D0FB732-636F-4B72-B1E9-262077F1DB82}.Release|x86.ActiveCfg = Release|Any CPU + {7D0FB732-636F-4B72-B1E9-262077F1DB82}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -54,6 +68,7 @@ Global GlobalSection(NestedProjects) = preSolution {942A8837-BBAB-4DC6-8ABB-4E3B7AD3EB4E} = {E959F9B5-F3EB-48B1-B842-2CDDFDB01900} {4584B4D5-0DA9-425F-A4C7-7A19A75D3E73} = {97756BA5-1E7C-4536-A49E-AE2190C0E6A5} + {7D0FB732-636F-4B72-B1E9-262077F1DB82} = {352EC932-F112-4A2F-9DC3-F0761C85E068} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {F4A8B63E-6284-4D00-9719-BAB1D955DACF} diff --git a/LICENSE b/LICENSE index 1672b90..086d077 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019 xuanye wong +Copyright (c) 2019 Xuanye Wang Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 6697656..bf8001e 100644 --- a/README.md +++ b/README.md @@ -156,78 +156,6 @@ public class DemoJobHandler:AbstractJobHandler } ``` -``` -classDiagram - - class IJobHandler { - <> - +Execute(JobExecuteContext) Task - +Init() - +Destroy() - } - - class AbstractJobHandler { - <> - +Execute(JobExecuteContext)* Task - +Init() - +Destroy() - } - - class JobHandlerAttribute { - +Name: string - } - - class XxlJobExecutorOptions { - +AdminAddresses: string - +AppName: string - +SpecialBindAddress: string - +Port: int - +AutoRegistry: bool - +AccessToken: string - +LogPath: string - +LogRetentionDays: int - } - - class JobExecuteContext { - +JobId: int - +JobParameter: string - +ShardIndex: int - +ShardTotal: int - +JobLogger: IJobLogger - } - - class ReturnT { - +Code: int - +Msg: string - +Content: object - +SUCCESS: ReturnT - +FAIL: ReturnT - } - - class XxlJobExecutorMiddleware { - -IJobHandlerFactory _jobHandlerFactory - +InvokeAsync(HttpContext) - } - - class IJobHandlerFactory { - <> - +GetJobHandler(string) IJobHandler - } - - class DefaultJobHandlerFactory { - -IServiceProvider _serviceProvider - +GetJobHandler(string) IJobHandler - } - - IJobHandler <|.. AbstractJobHandler - AbstractJobHandler <|-- DemoJobHandler - IJobHandler -- JobHandlerAttribute - IJobHandlerFactory <|.. DefaultJobHandlerFactory - XxlJobExecutorMiddleware --> IJobHandlerFactory - IJobHandler --> JobExecuteContext - IJobHandler --> ReturnT -``` - ## 其他说明 注意XXL-JOB 2.0.1版本请使用 1.0.8的执行器实现 diff --git a/doc/class-diagram-for-v2.excalidraw b/doc/class-diagram-for-v2.excalidraw new file mode 100644 index 0000000..49434ae --- /dev/null +++ b/doc/class-diagram-for-v2.excalidraw @@ -0,0 +1,6512 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "id": "oFANkLUwWIEaaAorcCqKO", + "type": "rectangle", + "x": -724.4553053008119, + "y": 31.87738444657566, + "width": 407.7375183105469, + "height": 166.1999969482422, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "WnM9qAProwkMr9OdSBoE4" + ], + "frameId": null, + "index": "aZ", + "roundness": null, + "seed": 475089208, + "version": 38, + "versionNonce": 1134584888, + "isDeleted": false, + "boundElements": [ + { + "id": "lW7K8ojUFTnXvZ2kLXWhp", + "type": "arrow" + }, + { + "id": "y82hlgCPmSZldIo4aimIs", + "type": "arrow" + }, + { + "id": "tMwqRNo_ew4MY7L5gEYnL", + "type": "arrow" + } + ], + "updated": 1733992303818, + "link": null, + "locked": false + }, + { + "id": "r_3o0vJjFSwPMyWohRkAn", + "type": "rectangle", + "x": -1448.0571550718807, + "y": 7.472880788386135, + "width": 540.0982560946505, + "height": 239.68637461582298, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "MXDu2ncHNciV763YDgYZV" + ], + "frameId": null, + "index": "aa", + "roundness": null, + "seed": 1259599416, + "version": 602, + "versionNonce": 1868627784, + "isDeleted": false, + "boundElements": [ + { + "id": "y82hlgCPmSZldIo4aimIs", + "type": "arrow" + }, + { + "id": "nX40c4f-8rTE6dXoKzvj6", + "type": "arrow" + } + ], + "updated": 1733965399682, + "link": null, + "locked": false + }, + { + "id": "e56x6FB6dKoj_87rpjQdJ", + "type": "rectangle", + "x": -114.047253126864, + "y": 15.363566019179189, + "width": 469.7875061035156, + "height": 264.6000061035156, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "9q0lYYsmi-DY5VRPxp1x8" + ], + "frameId": null, + "index": "ab", + "roundness": null, + "seed": 1657045816, + "version": 250, + "versionNonce": 1554684984, + "isDeleted": false, + "boundElements": [ + { + "id": "lW7K8ojUFTnXvZ2kLXWhp", + "type": "arrow" + }, + { + "id": "x-_XhCdeDdJTYQmhCB8QO", + "type": "arrow" + }, + { + "id": "bSspGR4Fmx5xt9ZuQfkGy", + "type": "arrow" + }, + { + "id": "-vCCuOTCRPYsJBmQrXQhZ", + "type": "arrow" + }, + { + "id": "lrdVqr717vyaM9w9PwE5O", + "type": "arrow" + } + ], + "updated": 1733966012681, + "link": null, + "locked": false + }, + { + "id": "68_PaYm53bCiLJWXjEMEB", + "type": "rectangle", + "x": 963.2931367158062, + "y": 263.7222463349584, + "width": 402.1875, + "height": 133.39999389648438, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "7AWB43RyhpRxnWrDWw-1O" + ], + "frameId": null, + "index": "ac", + "roundness": null, + "seed": 1187352376, + "version": 199, + "versionNonce": 1959730248, + "isDeleted": false, + "boundElements": [ + { + "id": "x-_XhCdeDdJTYQmhCB8QO", + "type": "arrow" + }, + { + "id": "Sj_WjDfNUNLKxgXuBK4Xr", + "type": "arrow" + } + ], + "updated": 1733965841031, + "link": null, + "locked": false + }, + { + "id": "D7ZX7PpdY4qI_vFQIOWN0", + "type": "rectangle", + "x": 1053.1494933238764, + "y": 834.6480302341076, + "width": 167.5500030517578, + "height": 67.80000305175781, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "KkIwDOsZv2q-9C-Ik7JB2" + ], + "frameId": null, + "index": "ad", + "roundness": null, + "seed": 920517176, + "version": 68, + "versionNonce": 700441928, + "isDeleted": false, + "boundElements": [ + { + "id": "jjYgK6dCOHsqYAFk3UNwp", + "type": "arrow" + } + ], + "updated": 1733965867587, + "link": null, + "locked": false + }, + { + "id": "Wj7TVf3rcaWwdZYNfjytP", + "type": "rectangle", + "x": -785.0122605087536, + "y": 586.0052172915229, + "width": 509.6875, + "height": 264.6000061035156, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "NZSrNeEBjy8Yde8mxO84U" + ], + "frameId": null, + "index": "ae", + "roundness": null, + "seed": 157521992, + "version": 246, + "versionNonce": 1950897976, + "isDeleted": false, + "boundElements": [ + { + "id": "bSspGR4Fmx5xt9ZuQfkGy", + "type": "arrow" + }, + { + "id": "nX40c4f-8rTE6dXoKzvj6", + "type": "arrow" + }, + { + "id": "e1C6sNmWzJdSS86Jb2-Ds", + "type": "arrow" + }, + { + "id": "aXlrkz9amYy_em2rBA4b3", + "type": "arrow" + }, + { + "id": "P8_HDDPhmxXtvS5WwZg3-", + "type": "arrow" + } + ], + "updated": 1733965807565, + "link": null, + "locked": false + }, + { + "id": "xOVrZbZchmc3HECqH_vWQ", + "type": "rectangle", + "x": -680.7321065242536, + "y": 1009.5529867575755, + "width": 302.5874938964844, + "height": 67.80000305175781, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "qsjttUhl-ZvXmFGYyjOvM" + ], + "frameId": null, + "index": "af", + "roundness": null, + "seed": 1362139720, + "version": 105, + "versionNonce": 1638984, + "isDeleted": false, + "boundElements": [ + { + "id": "e1C6sNmWzJdSS86Jb2-Ds", + "type": "arrow" + } + ], + "updated": 1733965811834, + "link": null, + "locked": false + }, + { + "id": "zZR1u30Sw2xdC9Dq5ipFk", + "type": "rectangle", + "x": -1215.5715659589666, + "y": 678.3877154516138, + "width": 158.22500610351562, + "height": 67.80000305175781, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#d0bfff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "bvvZBnRAhAJTIuwbgHkaQ" + ], + "frameId": null, + "index": "ag", + "roundness": null, + "seed": 1171924808, + "version": 229, + "versionNonce": 1655648568, + "isDeleted": false, + "boundElements": [ + { + "id": "aXlrkz9amYy_em2rBA4b3", + "type": "arrow" + }, + { + "id": "GgXb7X_cX6kbO8L6BjkiB", + "type": "arrow" + } + ], + "updated": 1733965819162, + "link": null, + "locked": false + }, + { + "id": "JyjrWYWUxQwmfnKfClBFs", + "type": "rectangle", + "x": 357.8296890258789, + "y": 583.4000015258789, + "width": 432.9125061035156, + "height": 330.20001220703125, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "ZCIR2q1iWJTb3D-5hDfij" + ], + "frameId": null, + "index": "ah", + "roundness": null, + "seed": 613170760, + "version": 17, + "versionNonce": 528521016, + "isDeleted": false, + "boundElements": [ + { + "id": "-vCCuOTCRPYsJBmQrXQhZ", + "type": "arrow" + }, + { + "id": "lrdVqr717vyaM9w9PwE5O", + "type": "arrow" + } + ], + "updated": 1733966036178, + "link": null, + "locked": false + }, + { + "id": "7X-Mfr2Y5OYr_a09VKvXR", + "type": "rectangle", + "x": 940.6911781951944, + "y": 564.6692531312572, + "width": 434.42498779296875, + "height": 133.39999389648438, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "SIWbZNjsHMWgSKVOhZHJv" + ], + "frameId": null, + "index": "ai", + "roundness": null, + "seed": 1476663352, + "version": 80, + "versionNonce": 739419976, + "isDeleted": false, + "boundElements": [ + { + "id": "Sj_WjDfNUNLKxgXuBK4Xr", + "type": "arrow" + }, + { + "id": "8dpBECz-Ujox8hZElY4Y4", + "type": "arrow" + }, + { + "id": "jjYgK6dCOHsqYAFk3UNwp", + "type": "arrow" + } + ], + "updated": 1733965855278, + "link": null, + "locked": false + }, + { + "id": "D4iW_7kKGpl5-aA2TeIYM", + "type": "rectangle", + "x": 1583.3110821895107, + "y": 566.4164793832125, + "width": 441.1499938964844, + "height": 133.39999389648438, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "u_Ic0rEhZV5nVk-h4vj5X" + ], + "frameId": null, + "index": "aj", + "roundness": null, + "seed": 1195190344, + "version": 109, + "versionNonce": 1740035640, + "isDeleted": false, + "boundElements": [ + { + "id": "8dpBECz-Ujox8hZElY4Y4", + "type": "arrow" + }, + { + "id": "Zi1-Hxx1A34SYr1r-i8RR", + "type": "arrow" + } + ], + "updated": 1733965864735, + "link": null, + "locked": false + }, + { + "id": "B5xHLTkYPyHU_il7rR2uM", + "type": "rectangle", + "x": -76.1776332167558, + "y": 673.8919814847175, + "width": 265.6499938964844, + "height": 67.80000305175781, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "Jr9OhsXudjtG69vnzdqxN" + ], + "frameId": null, + "index": "ak", + "roundness": null, + "seed": 1988695352, + "version": 147, + "versionNonce": 1995944760, + "isDeleted": false, + "boundElements": [ + { + "id": "P8_HDDPhmxXtvS5WwZg3-", + "type": "arrow" + } + ], + "updated": 1733965791341, + "link": null, + "locked": false + }, + { + "id": "qLhSEPXaycBzfOmRybYVa", + "type": "rectangle", + "x": 1668.0255734875268, + "y": 843.3043361857829, + "width": 243.6750030517578, + "height": 100.5999984741211, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "eK4rEk8qRP2XpsofAbWDM" + ], + "frameId": null, + "index": "al", + "roundness": null, + "seed": 2079134536, + "version": 62, + "versionNonce": 59281720, + "isDeleted": false, + "boundElements": [ + { + "id": "Zi1-Hxx1A34SYr1r-i8RR", + "type": "arrow" + }, + { + "id": "JmEJEJNnoGthRLyeUYddW", + "type": "arrow" + } + ], + "updated": 1733965998228, + "link": null, + "locked": false + }, + { + "id": "pi4wMraB2vHkJIs8Qrb0u", + "type": "rectangle", + "x": 1582.9484596050088, + "y": 1065.3292079008306, + "width": 433.20001220703125, + "height": 100.5999984741211, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "va-86zXh6tsa2alo38VHN" + ], + "frameId": null, + "index": "am", + "roundness": null, + "seed": 1675274568, + "version": 118, + "versionNonce": 1331012168, + "isDeleted": false, + "boundElements": [ + { + "id": "JmEJEJNnoGthRLyeUYddW", + "type": "arrow" + }, + { + "id": "go9VGinMsdJsei6R7ro_c", + "type": "arrow" + } + ], + "updated": 1733965996567, + "link": null, + "locked": false + }, + { + "id": "96wonXcpSe6wlSs0h7JKg", + "type": "rectangle", + "x": 1562.3215461621435, + "y": 1324.4018202416494, + "width": 465.29998779296875, + "height": 133.39999389648438, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "Iqfxylxz9NnmOXuk1w7iv" + ], + "frameId": null, + "index": "an", + "roundness": null, + "seed": 1888028744, + "version": 201, + "versionNonce": 433015096, + "isDeleted": false, + "boundElements": [ + { + "id": "JUDJAsgnkBcXQOpaaG3o_", + "type": "arrow" + }, + { + "id": "mdnDLwz6TT_dr0ZbGGNvy", + "type": "arrow" + }, + { + "id": "go9VGinMsdJsei6R7ro_c", + "type": "arrow" + } + ], + "updated": 1733965993158, + "link": null, + "locked": false + }, + { + "id": "jLvGy0UMMe8O_41Kgrhuj", + "type": "rectangle", + "x": 1673.4104589813717, + "y": 1588.1440337153074, + "width": 253.41885056198473, + "height": 103.59704012660268, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "ngjAJqdsBfc8P6TWwgM9I" + ], + "frameId": null, + "index": "ao", + "roundness": null, + "seed": 1518493256, + "version": 101, + "versionNonce": 1267937080, + "isDeleted": false, + "boundElements": [ + { + "id": "JUDJAsgnkBcXQOpaaG3o_", + "type": "arrow" + }, + { + "id": "mdnDLwz6TT_dr0ZbGGNvy", + "type": "arrow" + } + ], + "updated": 1733992350065, + "link": null, + "locked": false + }, + { + "id": "zFW-E1YLreofKeHVO7YhW", + "type": "rectangle", + "x": -1365.9805533941321, + "y": -221.15535163745727, + "width": 414.9771381763252, + "height": 156.0256723608074, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "mDj4E91mUMdNl9Zv4vrcc" + ], + "frameId": null, + "index": "ap", + "roundness": null, + "seed": 1721334584, + "version": 288, + "versionNonce": 1811283784, + "isDeleted": false, + "boundElements": [], + "updated": 1733992312510, + "link": null, + "locked": false + }, + { + "id": "BXtm0928vvtIbSOiuRBtk", + "type": "rectangle", + "x": 1096.112863658682, + "y": 1419.693050570531, + "width": 256.5, + "height": 67.80000305175781, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffd8a8", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "ZGBsIl7PTYgq080ACjzFT" + ], + "frameId": null, + "index": "aq", + "roundness": null, + "seed": 1993407288, + "version": 533, + "versionNonce": 1463383112, + "isDeleted": false, + "boundElements": [ + { + "id": "22l4uePe4aPVjdAZo1pkm", + "type": "arrow" + } + ], + "updated": 1733993219960, + "link": null, + "locked": false + }, + { + "id": "6dgirSDtPpWHYnLle_a6x", + "type": "rectangle", + "x": -741.0922932457586, + "y": -305.4836636665516, + "width": 465.2847132999015, + "height": 269.9565649349224, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffec99", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "ar", + "roundness": null, + "seed": 1346009912, + "version": 412, + "versionNonce": 1368058184, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "Zp8abAj33wHX4sw_ZIGF3" + }, + { + "id": "tMwqRNo_ew4MY7L5gEYnL", + "type": "arrow" + } + ], + "updated": 1733965774695, + "link": null, + "locked": false + }, + { + "id": "LRE05T2Vg1kS3hlmJ5Y6Y", + "type": "rectangle", + "x": 281.71030746623114, + "y": 1081.4637991116895, + "width": 567.832652141692, + "height": 252.7045271444815, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffec99", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "as", + "roundness": null, + "seed": 1521898552, + "version": 540, + "versionNonce": 1973531448, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "VnDhZcBXbbNmSu7gU7LvK" + }, + { + "id": "lrdVqr717vyaM9w9PwE5O", + "type": "arrow" + } + ], + "updated": 1733992369491, + "link": null, + "locked": false + }, + { + "id": "a9pzHPMy5bUFV_DTfRP8D", + "type": "rectangle", + "x": -1287.1086183285538, + "y": 859.4637384226895, + "width": 250.8707470301797, + "height": 49.298462900576624, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffec99", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "at", + "roundness": null, + "seed": 978259256, + "version": 221, + "versionNonce": 908481848, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "alwXgy959OQFKUdCV8Cz_" + }, + { + "id": "GgXb7X_cX6kbO8L6BjkiB", + "type": "arrow" + } + ], + "updated": 1733965433595, + "link": null, + "locked": false + }, + { + "id": "ZkrPInYNFOxO3OLpFX_9D", + "type": "rectangle", + "x": 2066.078815502823, + "y": 1631.8564810659204, + "width": 274.29905170482243, + "height": 31.6, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffec99", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "au", + "roundness": null, + "seed": 2020124216, + "version": 230, + "versionNonce": 771798840, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "-obs1g-l6K_mXEegkyLyh" + }, + { + "id": "mdnDLwz6TT_dr0ZbGGNvy", + "type": "arrow" + } + ], + "updated": 1733992352043, + "link": null, + "locked": false + }, + { + "id": "NwKxqAUsNgg1zdO4_A8of", + "type": "rectangle", + "x": -1376.8322209282144, + "y": -505.8914250698699, + "width": 460.88426254167643, + "height": 140, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffec99", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "av", + "roundness": null, + "seed": 1374101304, + "version": 253, + "versionNonce": 398241352, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "uvYskOKRYp3rX_B11Y7ad" + }, + { + "id": "fjtkdaiF-PtdIJx-7ixb4", + "type": "arrow" + } + ], + "updated": 1733992314229, + "link": null, + "locked": false + }, + { + "id": "uroc63qy0bSRVORNyQpta", + "type": "rectangle", + "x": 1065.2433605571243, + "y": 1238.531679724807, + "width": 317.07501220703125, + "height": 47.79999923706055, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffec99", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aw", + "roundness": null, + "seed": 1194227768, + "version": 684, + "versionNonce": 689549384, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "iwgsAGVRpwo_N_hfZDomR" + }, + { + "id": "22l4uePe4aPVjdAZo1pkm", + "type": "arrow" + } + ], + "updated": 1733992342736, + "link": null, + "locked": false + }, + { + "id": "ozYWe5ckQXQF_JLkiVU8i", + "type": "line", + "x": -724.4553053008119, + "y": 72.67738177628758, + "width": 407.7375183105469, + "height": 0, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "WnM9qAProwkMr9OdSBoE4" + ], + "frameId": null, + "index": "ax", + "roundness": null, + "seed": 1455645240, + "version": 36, + "versionNonce": 579897400, + "isDeleted": false, + "boundElements": [], + "updated": 1733992303818, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 407.7375183105469, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "TyDAclLMuHY4HFdkrX89l", + "type": "line", + "x": -724.4553053008119, + "y": 154.27738025040867, + "width": 407.7375183105469, + "height": 0, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "WnM9qAProwkMr9OdSBoE4" + ], + "frameId": null, + "index": "ay", + "roundness": null, + "seed": 227171128, + "version": 36, + "versionNonce": 1891811640, + "isDeleted": false, + "boundElements": [], + "updated": 1733992303818, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 407.7375183105469, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "uIaZAEsMr8ZcevpvUTEue", + "type": "line", + "x": -1448.0571550718807, + "y": 74.14301338407091, + "width": 540.0982560946505, + "height": 0, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "MXDu2ncHNciV763YDgYZV" + ], + "frameId": null, + "index": "az", + "roundness": null, + "seed": 1386770232, + "version": 601, + "versionNonce": 431416392, + "isDeleted": false, + "boundElements": [], + "updated": 1733965399682, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 540.0982560946505, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "qGCxwKDyUcM6WVTrd9EQU", + "type": "line", + "x": -1448.0571550718807, + "y": 88.63652020716933, + "width": 540.0982560946505, + "height": 0, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "MXDu2ncHNciV763YDgYZV" + ], + "frameId": null, + "index": "b00", + "roundness": null, + "seed": 1128646712, + "version": 601, + "versionNonce": 417536840, + "isDeleted": false, + "boundElements": [], + "updated": 1733965399682, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 540.0982560946505, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "cg9e23dtdKQGBbHLD47L0", + "type": "line", + "x": -114.047253126864, + "y": 56.16356620991405, + "width": 469.7875061035156, + "height": 0, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "9q0lYYsmi-DY5VRPxp1x8" + ], + "frameId": null, + "index": "b01", + "roundness": null, + "seed": 515857464, + "version": 246, + "versionNonce": 74053944, + "isDeleted": false, + "boundElements": [], + "updated": 1733966012681, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 469.7875061035156, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "6FMxnJK-aJ7P2bptbkKxW", + "type": "line", + "x": -114.047253126864, + "y": 170.5635639210957, + "width": 469.7875061035156, + "height": 0, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "9q0lYYsmi-DY5VRPxp1x8" + ], + "frameId": null, + "index": "b02", + "roundness": null, + "seed": 670504248, + "version": 246, + "versionNonce": 407245368, + "isDeleted": false, + "boundElements": [], + "updated": 1733966012681, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 469.7875061035156, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "V6uaJ4pwPjk4VLt7UejHz", + "type": "line", + "x": 963.2931367158062, + "y": 304.522242710996, + "width": 402.1875, + "height": 0, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "7AWB43RyhpRxnWrDWw-1O" + ], + "frameId": null, + "index": "b03", + "roundness": null, + "seed": 1992222776, + "version": 198, + "versionNonce": 1484106808, + "isDeleted": false, + "boundElements": [], + "updated": 1733965841031, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 402.1875, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "kbXQAekpTFxTdZaHQD5Rl", + "type": "line", + "x": 963.2931367158062, + "y": 353.32224194805656, + "width": 402.1875, + "height": 0, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "7AWB43RyhpRxnWrDWw-1O" + ], + "frameId": null, + "index": "b04", + "roundness": null, + "seed": 1881481528, + "version": 198, + "versionNonce": 1412715336, + "isDeleted": false, + "boundElements": [], + "updated": 1733965841031, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 402.1875, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "-7FIppB9y8ZsulK8KFGwv", + "type": "line", + "x": 1053.1494933238764, + "y": 875.4480323321911, + "width": 167.5500030517578, + "height": 0, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "KkIwDOsZv2q-9C-Ik7JB2" + ], + "frameId": null, + "index": "b05", + "roundness": null, + "seed": 1049752376, + "version": 68, + "versionNonce": 572783688, + "isDeleted": false, + "boundElements": [], + "updated": 1733965867587, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 167.5500030517578, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "vTFWSEl7GS06wO_dnTUu5", + "type": "line", + "x": 1053.1494933238764, + "y": 891.4480323321911, + "width": 167.5500030517578, + "height": 0, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "KkIwDOsZv2q-9C-Ik7JB2" + ], + "frameId": null, + "index": "b06", + "roundness": null, + "seed": 2136919096, + "version": 68, + "versionNonce": 1139618632, + "isDeleted": false, + "boundElements": [], + "updated": 1733965867587, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 167.5500030517578, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "bxl0aMsrLxqG3Qwf9WaKC", + "type": "line", + "x": -785.0122605087536, + "y": 626.8052174822577, + "width": 509.6875, + "height": 0, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "NZSrNeEBjy8Yde8mxO84U" + ], + "frameId": null, + "index": "b07", + "roundness": null, + "seed": 1490636616, + "version": 240, + "versionNonce": 343267400, + "isDeleted": false, + "boundElements": [], + "updated": 1733965807565, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 509.6875, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "3LWImKin3mK7s3reUqxh0", + "type": "line", + "x": -785.0122605087536, + "y": 774.0052144304999, + "width": 509.6875, + "height": 0, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "NZSrNeEBjy8Yde8mxO84U" + ], + "frameId": null, + "index": "b08", + "roundness": null, + "seed": 1048585800, + "version": 240, + "versionNonce": 2024321080, + "isDeleted": false, + "boundElements": [], + "updated": 1733965807565, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 509.6875, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "PA1pKTNaAVIukBqqJNAlL", + "type": "line", + "x": -680.7321065242536, + "y": 1050.352988855659, + "width": 302.5874938964844, + "height": 0, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "qsjttUhl-ZvXmFGYyjOvM" + ], + "frameId": null, + "index": "b09", + "roundness": null, + "seed": 1672876360, + "version": 105, + "versionNonce": 1805985336, + "isDeleted": false, + "boundElements": [], + "updated": 1733965811834, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 302.5874938964844, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "s-B_xjti-Ui4kGZvPIonb", + "type": "line", + "x": -680.7321065242536, + "y": 1066.352988855659, + "width": 302.5874938964844, + "height": 0, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "qsjttUhl-ZvXmFGYyjOvM" + ], + "frameId": null, + "index": "b0A", + "roundness": null, + "seed": 343189576, + "version": 105, + "versionNonce": 1766947144, + "isDeleted": false, + "boundElements": [], + "updated": 1733965811834, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 302.5874938964844, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "tsTrfGVU66Qqao5Ed6fI3", + "type": "line", + "x": -1215.5715659589666, + "y": 719.1877175496973, + "width": 158.22500610351562, + "height": 0, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "#d0bfff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "bvvZBnRAhAJTIuwbgHkaQ" + ], + "frameId": null, + "index": "b0B", + "roundness": null, + "seed": 94764616, + "version": 228, + "versionNonce": 1208065608, + "isDeleted": false, + "boundElements": [], + "updated": 1733965819162, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 158.22500610351562, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "g7quAyO1bsC5fMRVYeLKL", + "type": "line", + "x": -1215.5715659589666, + "y": 735.1877175496973, + "width": 158.22500610351562, + "height": 0, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "#d0bfff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "bvvZBnRAhAJTIuwbgHkaQ" + ], + "frameId": null, + "index": "b0C", + "roundness": null, + "seed": 736032072, + "version": 228, + "versionNonce": 1763985976, + "isDeleted": false, + "boundElements": [], + "updated": 1733965819162, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 158.22500610351562, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "fzxaJeFdOVG_Nq600TzMY", + "type": "line", + "x": 357.8296890258789, + "y": 624.2000036239624, + "width": 432.9125061035156, + "height": 0, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "ZCIR2q1iWJTb3D-5hDfij" + ], + "frameId": null, + "index": "b0D", + "roundness": null, + "seed": 335361352, + "version": 16, + "versionNonce": 93602360, + "isDeleted": false, + "boundElements": [], + "updated": 1733966036178, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 432.9125061035156, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "wjUqh5ufxESURonjgCt-H", + "type": "line", + "x": 357.8296890258789, + "y": 738.600001335144, + "width": 432.9125061035156, + "height": 0, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "ZCIR2q1iWJTb3D-5hDfij" + ], + "frameId": null, + "index": "b0E", + "roundness": null, + "seed": 2016889928, + "version": 16, + "versionNonce": 1866414904, + "isDeleted": false, + "boundElements": [], + "updated": 1733966036178, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 432.9125061035156, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "K26U3YKtG0xeRmcqhqdMl", + "type": "line", + "x": 940.6911629364054, + "y": 638.2692506517039, + "width": 434.4250183105469, + "height": 0, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "SIWbZNjsHMWgSKVOhZHJv" + ], + "frameId": null, + "index": "b0F", + "roundness": null, + "seed": 1363431736, + "version": 78, + "versionNonce": 281968200, + "isDeleted": false, + "boundElements": [], + "updated": 1733965855278, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 434.4250183105469, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "HP9LCBLRi4AjCPC7sDVbC", + "type": "line", + "x": 940.6911629364054, + "y": 654.2692506517039, + "width": 434.4250183105469, + "height": 0, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "SIWbZNjsHMWgSKVOhZHJv" + ], + "frameId": null, + "index": "b0G", + "roundness": null, + "seed": 135621176, + "version": 78, + "versionNonce": 1039627592, + "isDeleted": false, + "boundElements": [], + "updated": 1733965855278, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 434.4250183105469, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "GcZA7U-Irkci0DBGzORjc", + "type": "line", + "x": 1583.3110821895107, + "y": 607.2164757592501, + "width": 441.1499938964844, + "height": 0, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "u_Ic0rEhZV5nVk-h4vj5X" + ], + "frameId": null, + "index": "b0H", + "roundness": null, + "seed": 1157685064, + "version": 108, + "versionNonce": 879937864, + "isDeleted": false, + "boundElements": [], + "updated": 1733965864735, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 441.1499938964844, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "VYNEms260IS7bzFndmj4S", + "type": "line", + "x": 1583.3110821895107, + "y": 688.8164742333712, + "width": 441.1499938964844, + "height": 0, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "u_Ic0rEhZV5nVk-h4vj5X" + ], + "frameId": null, + "index": "b0I", + "roundness": null, + "seed": 1597355592, + "version": 108, + "versionNonce": 401559352, + "isDeleted": false, + "boundElements": [], + "updated": 1733965864735, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 441.1499938964844, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "hBO8J38oWDwT4HyqbN2LO", + "type": "line", + "x": -76.17764084615033, + "y": 714.691983582801, + "width": 265.65000915527344, + "height": 0, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "Jr9OhsXudjtG69vnzdqxN" + ], + "frameId": null, + "index": "b0J", + "roundness": null, + "seed": 807513656, + "version": 145, + "versionNonce": 1543126088, + "isDeleted": false, + "boundElements": [], + "updated": 1733965791341, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 265.65000915527344, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "g3c7ZBc7_LILaSkrRD-pZ", + "type": "line", + "x": -76.17764084615033, + "y": 730.691983582801, + "width": 265.65000915527344, + "height": 0, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "Jr9OhsXudjtG69vnzdqxN" + ], + "frameId": null, + "index": "b0K", + "roundness": null, + "seed": 1027548984, + "version": 145, + "versionNonce": 621579320, + "isDeleted": false, + "boundElements": [], + "updated": 1733965791341, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 265.65000915527344, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "0-w7aCZvwG-2_byehMHMZ", + "type": "line", + "x": 1668.0255734875268, + "y": 916.9043365672526, + "width": 243.6750030517578, + "height": 0, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "eK4rEk8qRP2XpsofAbWDM" + ], + "frameId": null, + "index": "b0L", + "roundness": null, + "seed": 1383819848, + "version": 61, + "versionNonce": 1139686984, + "isDeleted": false, + "boundElements": [], + "updated": 1733965998228, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 243.6750030517578, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "SL90ExL4MHLT1u78X-udW", + "type": "line", + "x": 1668.0255734875268, + "y": 932.9043365672526, + "width": 243.6750030517578, + "height": 0, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "eK4rEk8qRP2XpsofAbWDM" + ], + "frameId": null, + "index": "b0M", + "roundness": null, + "seed": 321315144, + "version": 61, + "versionNonce": 1741793848, + "isDeleted": false, + "boundElements": [], + "updated": 1733965998228, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 243.6750030517578, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "4jRkTPQvwkdcR_18OT4Bs", + "type": "line", + "x": 1582.9484596050088, + "y": 1106.1292071378912, + "width": 433.20001220703125, + "height": 0, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "va-86zXh6tsa2alo38VHN" + ], + "frameId": null, + "index": "b0N", + "roundness": null, + "seed": 1413890120, + "version": 117, + "versionNonce": 872580664, + "isDeleted": false, + "boundElements": [], + "updated": 1733965996568, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 433.20001220703125, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "8foWhf7ZTY_P_M-a5sa-Y", + "type": "line", + "x": 1582.9484596050088, + "y": 1122.1292071378912, + "width": 433.20001220703125, + "height": 0, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "va-86zXh6tsa2alo38VHN" + ], + "frameId": null, + "index": "b0O", + "roundness": null, + "seed": 19330888, + "version": 117, + "versionNonce": 1744443720, + "isDeleted": false, + "boundElements": [], + "updated": 1733965996568, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 433.20001220703125, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "oWg-8GMA8ZcbFKZcl8qz7", + "type": "line", + "x": 1562.3215309033544, + "y": 1398.0018177620962, + "width": 465.3000183105469, + "height": 0, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "Iqfxylxz9NnmOXuk1w7iv" + ], + "frameId": null, + "index": "b0P", + "roundness": null, + "seed": 433110856, + "version": 197, + "versionNonce": 613353032, + "isDeleted": false, + "boundElements": [], + "updated": 1733965993158, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 465.3000183105469, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "qehHjm6SumZVAgS8wbb5o", + "type": "line", + "x": 1562.3215309033544, + "y": 1414.0018177620962, + "width": 465.3000183105469, + "height": 0, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "Iqfxylxz9NnmOXuk1w7iv" + ], + "frameId": null, + "index": "b0Q", + "roundness": null, + "seed": 1326028360, + "version": 197, + "versionNonce": 1954636344, + "isDeleted": false, + "boundElements": [], + "updated": 1733965993158, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 465.3000183105469, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "3oe9qs7EgfGveTS8oZx_B", + "type": "line", + "x": 1673.4104511246846, + "y": 1663.9367007976882, + "width": 253.41886627535854, + "height": 0, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "ngjAJqdsBfc8P6TWwgM9I" + ], + "frameId": null, + "index": "b0R", + "roundness": null, + "seed": 1714378056, + "version": 98, + "versionNonce": 1875219848, + "isDeleted": false, + "boundElements": [], + "updated": 1733968054157, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 253.41886627535854, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "eqGvBCnKUMUT_IDpwGUVO", + "type": "line", + "x": 1673.4104511246846, + "y": 1680.413367469329, + "width": 253.41886627535854, + "height": 0, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "ngjAJqdsBfc8P6TWwgM9I" + ], + "frameId": null, + "index": "b0S", + "roundness": null, + "seed": 2133158984, + "version": 98, + "versionNonce": 881435784, + "isDeleted": false, + "boundElements": [], + "updated": 1733968054157, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 253.41886627535854, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "_BNS6THQOT85lfcDgwyNo", + "type": "line", + "x": -1365.9805533941321, + "y": -182.8530223199068, + "width": 414.9771381763252, + "height": 0, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "mDj4E91mUMdNl9Zv4vrcc" + ], + "frameId": null, + "index": "b0T", + "roundness": null, + "seed": 1295340600, + "version": 289, + "versionNonce": 713479752, + "isDeleted": false, + "boundElements": [], + "updated": 1733992312510, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 414.9771381763252, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "VoIgrX7s8vRki_Y1E7V3P", + "type": "line", + "x": -1365.9805533941321, + "y": -75.45629013753165, + "width": 414.9771381763252, + "height": 0, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "mDj4E91mUMdNl9Zv4vrcc" + ], + "frameId": null, + "index": "b0U", + "roundness": null, + "seed": 82709816, + "version": 289, + "versionNonce": 786746696, + "isDeleted": false, + "boundElements": [], + "updated": 1733992312510, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 414.9771381763252, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "bCUTqUpEs-aQu_5-iRPqq", + "type": "line", + "x": 1096.112863658682, + "y": 1460.4930526686146, + "width": 256.5, + "height": 0, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "#ffd8a8", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "ZGBsIl7PTYgq080ACjzFT" + ], + "frameId": null, + "index": "b0V", + "roundness": null, + "seed": 1786565688, + "version": 531, + "versionNonce": 663989064, + "isDeleted": false, + "boundElements": [], + "updated": 1733993219960, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 256.5, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "3dWb55p6lXKtukzpujdeB", + "type": "line", + "x": 1096.112863658682, + "y": 1476.4930526686146, + "width": 256.5, + "height": 0, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "#ffd8a8", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "ZGBsIl7PTYgq080ACjzFT" + ], + "frameId": null, + "index": "b0W", + "roundness": null, + "seed": 222661944, + "version": 531, + "versionNonce": 2106524232, + "isDeleted": false, + "boundElements": [], + "updated": 1733993219960, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 256.5, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "lW7K8ojUFTnXvZ2kLXWhp", + "type": "arrow", + "x": -311.93454462369266, + "y": 111.55122676488489, + "width": 184.20357960342415, + "height": 4.260916656832137, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0X", + "roundness": { + "type": 2 + }, + "seed": 1376351544, + "version": 338, + "versionNonce": 460791096, + "isDeleted": false, + "boundElements": [], + "updated": 1733992303818, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 184.20357960342415, + -4.260916656832137 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "oFANkLUwWIEaaAorcCqKO", + "focus": 0.01594577152321734, + "gap": 4.783242366572381, + "fixedPoint": null + }, + "endBinding": { + "elementId": "e56x6FB6dKoj_87rpjQdJ", + "focus": 0.289284207498608, + "gap": 13.683711893404507, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "y82hlgCPmSZldIo4aimIs", + "type": "arrow", + "x": -740.5960941452195, + "y": 125.88842426712418, + "width": 161.86741384146183, + "height": 0.9289905856872736, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0Y", + "roundness": { + "type": 2 + }, + "seed": 2090208824, + "version": 819, + "versionNonce": 437171768, + "isDeleted": false, + "boundElements": [], + "updated": 1733992303818, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -161.86741384146183, + -0.9289905856872736 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "oFANkLUwWIEaaAorcCqKO", + "focus": -0.1444608448106515, + "gap": 16.140788844407552, + "fixedPoint": null + }, + "endBinding": { + "elementId": "r_3o0vJjFSwPMyWohRkAn", + "focus": -0.1137429993866322, + "gap": 5.4953909905489695, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "x-_XhCdeDdJTYQmhCB8QO", + "type": "arrow", + "x": 356.7402529766515, + "y": 232.06104349777732, + "width": 600.55286237685, + "height": 100.12890869333606, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0Z", + "roundness": { + "type": 2 + }, + "seed": 1009223480, + "version": 454, + "versionNonce": 803925560, + "isDeleted": false, + "boundElements": [], + "updated": 1733966012681, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 600.55286237685, + 100.12890869333606 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "e56x6FB6dKoj_87rpjQdJ", + "focus": 0.2628399014670393, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "5Rz0B4ib8J4ITP0uHFqEi", + "focus": -0.4616016488365746, + "gap": 13.500021362304778, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "bSspGR4Fmx5xt9ZuQfkGy", + "type": "arrow", + "x": -105.42041073097886, + "y": 280.9635721226948, + "width": 381.02724310612734, + "height": 299.041629910039, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0a", + "roundness": { + "type": 2 + }, + "seed": 1562501176, + "version": 480, + "versionNonce": 1910531896, + "isDeleted": false, + "boundElements": [], + "updated": 1733966012681, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -381.02724310612734, + 299.041629910039 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "e56x6FB6dKoj_87rpjQdJ", + "focus": 0.13984156886236054, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "Wj7TVf3rcaWwdZYNfjytP", + "focus": -0.6011120328101902, + "gap": 6.0000152587890625, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "-vCCuOTCRPYsJBmQrXQhZ", + "type": "arrow", + "x": 225.82465960832388, + "y": 280.9635721226948, + "width": 334.9050182987932, + "height": 301.60263588901637, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0b", + "roundness": { + "type": 2 + }, + "seed": 2137594168, + "version": 290, + "versionNonce": 37449032, + "isDeleted": false, + "boundElements": [], + "updated": 1733966318774, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 334.9050182987932, + 301.60263588901637 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "e56x6FB6dKoj_87rpjQdJ", + "focus": 0.11273006804656369, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "kyAjj4J5Rg-pp1wk9rc6q", + "focus": 0.32728022503630577, + "gap": 5.83379446784204, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "Sj_WjDfNUNLKxgXuBK4Xr", + "type": "arrow", + "x": 1161.8244522973564, + "y": 399.1222249726537, + "width": 0.7270814404332668, + "height": 161.55004366884634, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0c", + "roundness": { + "type": 2 + }, + "seed": 643904056, + "version": 294, + "versionNonce": 1523641416, + "isDeleted": false, + "boundElements": [], + "updated": 1733965517531, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.7270814404332668, + 161.55004366884634 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "-T6VS-Ar_ZRdtmYUfGRu5", + "focus": -0.1393195890246326, + "gap": 13.799983024597168, + "fixedPoint": null + }, + "endBinding": { + "elementId": "Xzcl3wHVdj1TmI9FVOPdW", + "focus": 0.422636434168226, + "gap": 8.996979721385514, + "fixedPoint": null + }, + "startArrowhead": "diamond", + "endArrowhead": null, + "elbowed": false + }, + { + "id": "8dpBECz-Ujox8hZElY4Y4", + "type": "arrow", + "x": 1576.317227535446, + "y": 630.7354231323657, + "width": 195.76961503821212, + "height": 2.4003937716173596, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0d", + "roundness": { + "type": 2 + }, + "seed": 1223568184, + "version": 249, + "versionNonce": 781079176, + "isDeleted": false, + "boundElements": [], + "updated": 1733968040969, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -195.76961503821212, + 2.4003937716173596 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "HC5oEvx35OuAoI_1koyFn", + "focus": 0.0785460468264263, + "gap": 14.493854654064762, + "fixedPoint": null + }, + "endBinding": { + "elementId": "7X-Mfr2Y5OYr_a09VKvXR", + "focus": 0.00842568964988867, + "gap": 5.431446509070611, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "triangle_outline", + "elbowed": false + }, + { + "id": "jjYgK6dCOHsqYAFk3UNwp", + "type": "arrow", + "x": 1143.7567618513026, + "y": 693.0752780482271, + "width": 5.00692086847198, + "height": 135.5727216683024, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0e", + "roundness": { + "type": 2 + }, + "seed": 1634993208, + "version": 161, + "versionNonce": 1580939080, + "isDeleted": false, + "boundElements": [], + "updated": 1733965525229, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 5.00692086847198, + 135.5727216683024 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "dBS5Ve_oQpkDIcmuHX2Xa", + "focus": -0.1350306808581165, + "gap": 6.806027396523177, + "fixedPoint": null + }, + "endBinding": { + "elementId": "Xc1R8pT_2EKRUFV9bwOji", + "focus": 0.44386651813970907, + "gap": 11.000031471252441, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "nX40c4f-8rTE6dXoKzvj6", + "type": "arrow", + "x": -613.7774580215764, + "y": 585.0052172915227, + "width": 289.7684165415915, + "height": 398.88350065718623, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0f", + "roundness": { + "type": 2 + }, + "seed": 2111225144, + "version": 848, + "versionNonce": 665422408, + "isDeleted": false, + "boundElements": [], + "updated": 1733965722715, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -289.7684165415915, + -398.88350065718623 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "Wj7TVf3rcaWwdZYNfjytP", + "focus": 0.037687880622472525, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "r_3o0vJjFSwPMyWohRkAn", + "focus": -0.47113047836361777, + "gap": 4.41302441406242, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "e1C6sNmWzJdSS86Jb2-Ds", + "type": "arrow", + "x": -551.1807627932321, + "y": 854.6022650475203, + "width": 4.720927502047289, + "height": 148.45069577011373, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0g", + "roundness": { + "type": 2 + }, + "seed": 1333417528, + "version": 413, + "versionNonce": 1713617976, + "isDeleted": false, + "boundElements": [], + "updated": 1733965461450, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -4.720927502047289, + 148.45069577011373 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "Wj7TVf3rcaWwdZYNfjytP", + "focus": 0.20940912571902928, + "gap": 3.997041652481812, + "fixedPoint": null + }, + "endBinding": { + "elementId": "yjx8Z588nnQCK7sg-4Kx9", + "focus": 0.018685674136147804, + "gap": 11.500026893615768, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "aXlrkz9amYy_em2rBA4b3", + "type": "arrow", + "x": -793.1491780550613, + "y": 718.6152089046951, + "width": 261.27027709056676, + "height": 7.309477661395363, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0h", + "roundness": { + "type": 2 + }, + "seed": 994234168, + "version": 520, + "versionNonce": 377893112, + "isDeleted": false, + "boundElements": [], + "updated": 1733968001109, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -261.27027709056676, + -7.309477661395363 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "Ni0WMEMiWFFak58950lKy", + "focus": -0.4955708551792916, + "gap": 15.6369175463077, + "fixedPoint": null + }, + "endBinding": { + "elementId": "zZR1u30Sw2xdC9Dq5ipFk", + "focus": -0.02367806787598579, + "gap": 2.92710470982297, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "P8_HDDPhmxXtvS5WwZg3-", + "type": "arrow", + "x": -274.3247605087538, + "y": 708.9421649489103, + "width": 190.64852336309104, + "height": 0.5490079319412189, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0i", + "roundness": { + "type": 2 + }, + "seed": 1771317304, + "version": 445, + "versionNonce": 861132616, + "isDeleted": false, + "boundElements": [], + "updated": 1733965458682, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 190.64852336309104, + -0.5490079319412189 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "Wj7TVf3rcaWwdZYNfjytP", + "focus": -0.11195984530488705, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "675ODiNfv06lZo6Stmnew", + "focus": -1.1360040084908136, + "gap": 14.998596299512428, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "Zi1-Hxx1A34SYr1r-i8RR", + "type": "arrow", + "x": 1786.3871543485234, + "y": 697.8194316272151, + "width": 0.7882048616809243, + "height": 140.80987632980793, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0j", + "roundness": { + "type": 2 + }, + "seed": 1448239416, + "version": 228, + "versionNonce": 904416072, + "isDeleted": false, + "boundElements": [], + "updated": 1733965884406, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -0.7882048616809243, + 140.80987632980793 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "D4iW_7kKGpl5-aA2TeIYM", + "focus": 0.08443612421369733, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "U3dD7lzpBKWb977JkJtrp", + "focus": 0.23444086682670132, + "gap": 9.675026321411224, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "JmEJEJNnoGthRLyeUYddW", + "type": "arrow", + "x": 1788.212923843524, + "y": 1067.326135227863, + "width": 1.6677567102167359, + "height": 118.0445126962635, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0k", + "roundness": { + "type": 2 + }, + "seed": 1625389624, + "version": 221, + "versionNonce": 1065382968, + "isDeleted": false, + "boundElements": [], + "updated": 1733965887176, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1.6677567102167359, + -118.0445126962635 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "32fYxhfFFLW2FZz2Edujg", + "focus": 0.15867421622256758, + "gap": 3.003070765618986, + "fixedPoint": null + }, + "endBinding": { + "elementId": "qLhSEPXaycBzfOmRybYVa", + "focus": 0.02569913627951435, + "gap": 5.377287871695557, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "triangle_outline", + "elbowed": false + }, + { + "id": "go9VGinMsdJsei6R7ro_c", + "type": "arrow", + "x": 1779.4875920472703, + "y": 1172.949066274439, + "width": 1.3412520993190356, + "height": 146.57802836108453, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0l", + "roundness": { + "type": 2 + }, + "seed": 1545958200, + "version": 390, + "versionNonce": 1029546296, + "isDeleted": false, + "boundElements": [], + "updated": 1733965898422, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1.3412520993190356, + 146.57802836108453 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "pi4wMraB2vHkJIs8Qrb0u", + "focus": 0.09483713809671336, + "gap": 7.019859899487301, + "fixedPoint": null + }, + "endBinding": { + "elementId": "fc1c9qwaqYqHg2STfpE3i", + "focus": 0.03827318514818343, + "gap": 9.87472083775424, + "fixedPoint": null + }, + "startArrowhead": "diamond", + "endArrowhead": null, + "elbowed": false + }, + { + "id": "JUDJAsgnkBcXQOpaaG3o_", + "type": "arrow", + "x": 1789.553155969112, + "y": 1585.7945283298557, + "width": 2.369151768681377, + "height": 126.03034252469092, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0m", + "roundness": { + "type": 2 + }, + "seed": 511855672, + "version": 395, + "versionNonce": 1681924744, + "isDeleted": false, + "boundElements": [], + "updated": 1733968054158, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -2.369151768681377, + -126.03034252469092 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "iBQJ8ZSU9zwrFXbHty2yi", + "focus": 0.07715662989369859, + "gap": 7.498461756167671, + "fixedPoint": null + }, + "endBinding": { + "elementId": "aq1gN7tcebMpownkg3SiD", + "focus": -0.2847153250434576, + "gap": 13.76236804306859, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "triangle_outline", + "elbowed": false + }, + { + "id": "tMwqRNo_ew4MY7L5gEYnL", + "type": "arrow", + "x": -515.9641521173824, + "y": -34.527098731629174, + "width": 2.0221821387309546, + "height": 65.40448317820483, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0n", + "roundness": { + "type": 2 + }, + "seed": 299631928, + "version": 299, + "versionNonce": 1951084344, + "isDeleted": false, + "boundElements": [], + "updated": 1733992303818, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -2.0221821387309546, + 65.40448317820483 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "6dgirSDtPpWHYnLle_a6x", + "focus": 0, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "oFANkLUwWIEaaAorcCqKO", + "focus": 0, + "gap": 1, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": null, + "elbowed": false + }, + { + "id": "lrdVqr717vyaM9w9PwE5O", + "type": "arrow", + "x": 563.586424508152, + "y": 1074.4697158067258, + "width": 3.76998390019628, + "height": 159.8697020738157, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0o", + "roundness": { + "type": 2 + }, + "seed": 1133944376, + "version": 874, + "versionNonce": 692602168, + "isDeleted": false, + "boundElements": [], + "updated": 1733992369492, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 3.76998390019628, + -159.8697020738157 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "LRE05T2Vg1kS3hlmJ5Y6Y", + "focus": -0.018071814257425226, + "gap": 6.994083304963624, + "fixedPoint": null + }, + "endBinding": { + "elementId": "JyjrWYWUxQwmfnKfClBFs", + "focus": 0.04791016625281401, + "gap": 1, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": null, + "elbowed": false + }, + { + "id": "GgXb7X_cX6kbO8L6BjkiB", + "type": "arrow", + "x": -1147.0156368137848, + "y": 858.4637384226895, + "width": 2.4922749057570854, + "height": 106.75990495573012, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0p", + "roundness": { + "type": 2 + }, + "seed": 1955993400, + "version": 505, + "versionNonce": 1279969592, + "isDeleted": false, + "boundElements": [], + "updated": 1733965435630, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 2.4922749057570854, + -106.75990495573012 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "a9pzHPMy5bUFV_DTfRP8D", + "focus": 0.10194649657211073, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "zZR1u30Sw2xdC9Dq5ipFk", + "focus": 0.08940920072052783, + "gap": 5.516114963587825, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": null, + "elbowed": false + }, + { + "id": "mdnDLwz6TT_dr0ZbGGNvy", + "type": "arrow", + "x": 2060.540894418844, + "y": 1642.3753831436236, + "width": 119.53254520486144, + "height": 0.9822958716565608, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0q", + "roundness": { + "type": 2 + }, + "seed": 691957816, + "version": 547, + "versionNonce": 1354123576, + "isDeleted": false, + "boundElements": [], + "updated": 1733992352043, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -119.53254520486144, + 0.9822958716565608 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "ZkrPInYNFOxO3OLpFX_9D", + "focus": 0.38126366965011443, + "gap": 5.537921083978972, + "fixedPoint": null + }, + "endBinding": { + "elementId": "jLvGy0UMMe8O_41Kgrhuj", + "focus": 0.1548949634516829, + "gap": 14.179039670626139, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": null, + "elbowed": false + }, + { + "id": "fjtkdaiF-PtdIJx-7ixb4", + "type": "arrow", + "x": -1159.0000215954917, + "y": -364.8914250698699, + "width": 0.19838989093773307, + "height": 145.93149082013332, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0r", + "roundness": { + "type": 2 + }, + "seed": 963881272, + "version": 657, + "versionNonce": 1309391944, + "isDeleted": false, + "boundElements": [], + "updated": 1733992314229, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.19838989093773307, + 145.93149082013332 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "NwKxqAUsNgg1zdO4_A8of", + "focus": 0.05511669832477761, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "7D3CZXdL1Kz57-irUaoeW", + "focus": 0.2796711287820796, + "gap": 2.498492244878591, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": null, + "elbowed": false + }, + { + "id": "22l4uePe4aPVjdAZo1pkm", + "type": "arrow", + "x": 1226.7624412679427, + "y": 1287.3316789618675, + "width": 2.249409978188851, + "height": 123.4458504647705, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0s", + "roundness": { + "type": 2 + }, + "seed": 1620689464, + "version": 1700, + "versionNonce": 265810488, + "isDeleted": false, + "boundElements": [], + "updated": 1733992343268, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 2.249409978188851, + 123.4458504647705 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "uroc63qy0bSRVORNyQpta", + "focus": -0.015901132796693997, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "BXtm0928vvtIbSOiuRBtk", + "focus": 0.04212975124478476, + "gap": 8.915521143893102, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": null, + "elbowed": false + }, + { + "id": "0IUJ8YJN3t3cvFL0ROykL", + "type": "text", + "x": -668.7927991972963, + "y": 36.877380631878395, + "width": 233.51976013183594, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "WnM9qAProwkMr9OdSBoE4" + ], + "frameId": null, + "index": "b0t", + "roundness": null, + "seed": 587871288, + "version": 37, + "versionNonce": 239458872, + "isDeleted": false, + "boundElements": [], + "updated": 1733992303818, + "link": null, + "locked": false, + "text": "XxlRestfulServiceHandler", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "XxlRestfulServiceHandler", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "JTHkRqLbQrNjiJaT4savA", + "type": "text", + "x": -716.9553053008119, + "y": 81.67738177628758, + "width": 283.27972412109375, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "WnM9qAProwkMr9OdSBoE4" + ], + "frameId": null, + "index": "b0u", + "roundness": null, + "seed": 220654904, + "version": 38, + "versionNonce": 503173944, + "isDeleted": false, + "boundElements": [], + "updated": 1733992303818, + "link": null, + "locked": false, + "text": "- JobDispatcher _jobDispatcher", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "- JobDispatcher _jobDispatcher", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "VruPOjdC8s_ya3PEJ2X7m", + "type": "text", + "x": -716.9553053008119, + "y": 114.47738101334812, + "width": 220.7598114013672, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "WnM9qAProwkMr9OdSBoE4" + ], + "frameId": null, + "index": "b0v", + "roundness": null, + "seed": 1698989624, + "version": 39, + "versionNonce": 1179561016, + "isDeleted": false, + "boundElements": [ + { + "id": "y82hlgCPmSZldIo4aimIs", + "type": "arrow" + } + ], + "updated": 1733992303819, + "link": null, + "locked": false, + "text": "- IJobLogger _jobLogger", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "- IJobLogger _jobLogger", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "yJZNuU9TMye4Qpb0pCsI_", + "type": "text", + "x": -716.9553053008119, + "y": 159.27738025040867, + "width": 321.7997131347656, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "WnM9qAProwkMr9OdSBoE4" + ], + "frameId": null, + "index": "b0w", + "roundness": null, + "seed": 2017918776, + "version": 37, + "versionNonce": 1092379960, + "isDeleted": false, + "boundElements": [], + "updated": 1733992303819, + "link": null, + "locked": false, + "text": "+HandleAync(HttpContext context)", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "+HandleAync(HttpContext context)", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "E68foDXi7P6QQhOwyCEDW", + "type": "text", + "x": -1236.2821179648129, + "y": 12.002100806723945, + "width": 87.90586853027344, + "height": 24.45779276397846, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "MXDu2ncHNciV763YDgYZV" + ], + "frameId": null, + "index": "b0x", + "roundness": null, + "seed": 1221591352, + "version": 604, + "versionNonce": 1936749128, + "isDeleted": false, + "boundElements": [ + { + "id": "y82hlgCPmSZldIo4aimIs", + "type": "arrow" + } + ], + "updated": 1733965399682, + "link": null, + "locked": false, + "text": "«interface»", + "fontSize": 18.116883528872933, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "«interface»", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "bj7FU9eh4w6H8mjMchOSa", + "type": "text", + "x": -1235.5687636025507, + "y": 41.713790830731966, + "width": 90.52719116210938, + "height": 24.45779276397846, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "MXDu2ncHNciV763YDgYZV" + ], + "frameId": null, + "index": "b0y", + "roundness": null, + "seed": 1923815992, + "version": 603, + "versionNonce": 1614473544, + "isDeleted": false, + "boundElements": [], + "updated": 1733965399682, + "link": null, + "locked": false, + "text": "IJobLogger", + "fontSize": 18.116883528872933, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "IJobLogger", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "oKCbibRODmbwUXHtIKINc", + "type": "text", + "x": -1441.2633237485534, + "y": 93.1657410893875, + "width": 310.76727294921875, + "height": 24.45779276397846, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "MXDu2ncHNciV763YDgYZV" + ], + "frameId": null, + "index": "b0z", + "roundness": null, + "seed": 1136333624, + "version": 607, + "versionNonce": 1797268360, + "isDeleted": false, + "boundElements": [], + "updated": 1733968009833, + "link": null, + "locked": false, + "text": "+SetLogFile(long logTime, long logId)", + "fontSize": 18.116883528872933, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "+SetLogFile(long logTime, long logId)", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "Ip4cDU6cR8nzwkVzNAZDE", + "type": "text", + "x": -1441.2633237485534, + "y": 122.8774293856348, + "width": 366.58221435546875, + "height": 24.457792763978464, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "MXDu2ncHNciV763YDgYZV" + ], + "frameId": null, + "index": "b10", + "roundness": null, + "seed": 1113844792, + "version": 606, + "versionNonce": 265985784, + "isDeleted": false, + "boundElements": [], + "updated": 1733968014181, + "link": null, + "locked": false, + "text": "+Log(string pattern, params object[] format) ", + "fontSize": 18.116883528872936, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "+Log(string pattern, params object[] format) ", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "bg8MypuO69FWsV68Tj-q3", + "type": "text", + "x": -1441.2633237485534, + "y": 152.58911768188233, + "width": 202.306640625, + "height": 24.45779276397846, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "MXDu2ncHNciV763YDgYZV" + ], + "frameId": null, + "index": "b11", + "roundness": null, + "seed": 143238456, + "version": 606, + "versionNonce": 313779848, + "isDeleted": false, + "boundElements": [], + "updated": 1733968018235, + "link": null, + "locked": false, + "text": "+LogError(Exception ex) ", + "fontSize": 18.116883528872933, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "+LogError(Exception ex) ", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "lW_XQl8760VDOyb6CgNyr", + "type": "text", + "x": -1441.2633237485534, + "y": 182.30080597812963, + "width": 402.8746032714844, + "height": 24.45779276397846, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "MXDu2ncHNciV763YDgYZV" + ], + "frameId": null, + "index": "b12", + "roundness": null, + "seed": 1401962040, + "version": 607, + "versionNonce": 853151624, + "isDeleted": false, + "boundElements": [], + "updated": 1733968022639, + "link": null, + "locked": false, + "text": "+ReadLog(long logTime, long logId, int fromLine)", + "fontSize": 18.116883528872933, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "+ReadLog(long logTime, long logId, int fromLine)", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "JG9p8fzEV2SS_8xiVFKWN", + "type": "text", + "x": -1441.2633237485534, + "y": 212.01249427437693, + "width": 147.83180236816406, + "height": 24.45779276397846, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "MXDu2ncHNciV763YDgYZV" + ], + "frameId": null, + "index": "b13", + "roundness": null, + "seed": 341555000, + "version": 606, + "versionNonce": 148271864, + "isDeleted": false, + "boundElements": [], + "updated": 1733968027988, + "link": null, + "locked": false, + "text": "+LogSpecialFile() ", + "fontSize": 18.116883528872933, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "+LogSpecialFile() ", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "xXFhJhHDPjJlGcgPJ1EKL", + "type": "text", + "x": 37.471499924893806, + "y": 20.363565065504872, + "width": 128.77987670898438, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "9q0lYYsmi-DY5VRPxp1x8" + ], + "frameId": null, + "index": "b14", + "roundness": null, + "seed": 63942200, + "version": 247, + "versionNonce": 1798769464, + "isDeleted": false, + "boundElements": [], + "updated": 1733966012681, + "link": null, + "locked": false, + "text": "JobDispatcher", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "JobDispatcher", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "MYKBRCQmJquJi9SG2ypmp", + "type": "text", + "x": -106.547253126864, + "y": 65.16356620991405, + "width": 362.21966552734375, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "9q0lYYsmi-DY5VRPxp1x8" + ], + "frameId": null, + "index": "b15", + "roundness": null, + "seed": 1798121272, + "version": 247, + "versionNonce": 127984696, + "isDeleted": false, + "boundElements": [], + "updated": 1733966012681, + "link": null, + "locked": false, + "text": "- TaskExecutorFactory _executorFactory", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "- TaskExecutorFactory _executorFactory", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "R4MNot3-DF7_tm7BZvfMb", + "type": "text", + "x": -106.547253126864, + "y": 97.9635654469746, + "width": 341.2196960449219, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "9q0lYYsmi-DY5VRPxp1x8" + ], + "frameId": null, + "index": "b16", + "roundness": null, + "seed": 1517518904, + "version": 247, + "versionNonce": 191339832, + "isDeleted": false, + "boundElements": [], + "updated": 1733966012681, + "link": null, + "locked": false, + "text": "- CallbackQueue _callbackTaskQueue", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "- CallbackQueue _callbackTaskQueue", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "E_8MDCEJdGg70WzjFff_g", + "type": "text", + "x": -106.547253126864, + "y": 130.76356468403515, + "width": 382.35968017578125, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "9q0lYYsmi-DY5VRPxp1x8" + ], + "frameId": null, + "index": "b17", + "roundness": null, + "seed": 562477368, + "version": 247, + "versionNonce": 938423864, + "isDeleted": false, + "boundElements": [], + "updated": 1733966012681, + "link": null, + "locked": false, + "text": "- ConcurrentDictionary RUNNING_QUEUE", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "- ConcurrentDictionary RUNNING_QUEUE", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "0qPCgJ3G29drLO0AyqzDA", + "type": "text", + "x": -106.547253126864, + "y": 175.5635639210957, + "width": 270.8597717285156, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "9q0lYYsmi-DY5VRPxp1x8" + ], + "frameId": null, + "index": "b18", + "roundness": null, + "seed": 1942671928, + "version": 248, + "versionNonce": 1520831048, + "isDeleted": false, + "boundElements": [ + { + "id": "lW7K8ojUFTnXvZ2kLXWhp", + "type": "arrow" + } + ], + "updated": 1733966015156, + "link": null, + "locked": false, + "text": "+TryRemoveJobTask(int jobId)", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "+TryRemoveJobTask(int jobId)", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "uOExZ0DeBAN1CxYUcZBCe", + "type": "text", + "x": -106.547253126864, + "y": 208.36356315815624, + "width": 343.15960693359375, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "9q0lYYsmi-DY5VRPxp1x8" + ], + "frameId": null, + "index": "b19", + "roundness": null, + "seed": 2015611704, + "version": 247, + "versionNonce": 575735864, + "isDeleted": false, + "boundElements": [], + "updated": 1733966012681, + "link": null, + "locked": false, + "text": "+Execute(TriggerParam triggerParam)", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "+Execute(TriggerParam triggerParam)", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "4jz2NjmNUG0AkC5Td2Afn", + "type": "text", + "x": -106.547253126864, + "y": 241.1635623952168, + "width": 173.9998016357422, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "9q0lYYsmi-DY5VRPxp1x8" + ], + "frameId": null, + "index": "b1A", + "roundness": null, + "seed": 1492493368, + "version": 247, + "versionNonce": 611533112, + "isDeleted": false, + "boundElements": [], + "updated": 1733966012681, + "link": null, + "locked": false, + "text": "+IdleBeat(int jobId)", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "+IdleBeat(int jobId)", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "2sQtpY5JUoqhZr3oTm5tw", + "type": "text", + "x": 1043.0743867158062, + "y": 268.72224156658683, + "width": 188.75982666015625, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "7AWB43RyhpRxnWrDWw-1O" + ], + "frameId": null, + "index": "b1B", + "roundness": null, + "seed": 844905016, + "version": 199, + "versionNonce": 1986225464, + "isDeleted": false, + "boundElements": [], + "updated": 1733965841031, + "link": null, + "locked": false, + "text": "TaskExecutorFactory", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "TaskExecutorFactory", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "5Rz0B4ib8J4ITP0uHFqEi", + "type": "text", + "x": 970.7931367158062, + "y": 313.522242710996, + "width": 173.7798309326172, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "7AWB43RyhpRxnWrDWw-1O" + ], + "frameId": null, + "index": "b1C", + "roundness": null, + "seed": 810038072, + "version": 200, + "versionNonce": 534424136, + "isDeleted": false, + "boundElements": [ + { + "id": "x-_XhCdeDdJTYQmhCB8QO", + "type": "arrow" + } + ], + "updated": 1733965841031, + "link": null, + "locked": false, + "text": "- Dictionary _cache", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "- Dictionary _cache", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "-T6VS-Ar_ZRdtmYUfGRu5", + "type": "text", + "x": 970.7931367158062, + "y": 358.32224194805656, + "width": 317.69964599609375, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "7AWB43RyhpRxnWrDWw-1O" + ], + "frameId": null, + "index": "b1D", + "roundness": null, + "seed": 1615662136, + "version": 200, + "versionNonce": 1613889080, + "isDeleted": false, + "boundElements": [ + { + "id": "Sj_WjDfNUNLKxgXuBK4Xr", + "type": "arrow" + } + ], + "updated": 1733965841031, + "link": null, + "locked": false, + "text": "+GetTaskExecutor(string glueType)", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "+GetTaskExecutor(string glueType)", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "Xc1R8pT_2EKRUFV9bwOji", + "type": "text", + "x": 1060.6494933238764, + "y": 839.648031187782, + "width": 122.99989318847656, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "KkIwDOsZv2q-9C-Ik7JB2" + ], + "frameId": null, + "index": "b1E", + "roundness": null, + "seed": 589858104, + "version": 70, + "versionNonce": 1702333000, + "isDeleted": false, + "boundElements": [ + { + "id": "jjYgK6dCOHsqYAFk3UNwp", + "type": "arrow" + } + ], + "updated": 1733965867587, + "link": null, + "locked": false, + "text": "TriggerParam", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "TriggerParam", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "nO0oLmS6Pb3GrlNrHDO9p", + "type": "text", + "x": -617.0247650863903, + "y": 591.0052163378485, + "width": 137.41989135742188, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "NZSrNeEBjy8Yde8mxO84U" + ], + "frameId": null, + "index": "b1F", + "roundness": null, + "seed": 389291336, + "version": 241, + "versionNonce": 1201901384, + "isDeleted": false, + "boundElements": [], + "updated": 1733965807565, + "link": null, + "locked": false, + "text": "CallbackQueue", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "CallbackQueue", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "ExYoCjrH2fRRFQslIOxjS", + "type": "text", + "x": -777.5122605087536, + "y": 635.8052174822577, + "width": 253.3197479248047, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "NZSrNeEBjy8Yde8mxO84U" + ], + "frameId": null, + "index": "b1G", + "roundness": null, + "seed": 1146172488, + "version": 242, + "versionNonce": 509110264, + "isDeleted": false, + "boundElements": [], + "updated": 1733967993801, + "link": null, + "locked": false, + "text": "- AdminClient _adminClient", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "- AdminClient _adminClient", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "B20RyPUnJJKCaIeYfrIzM", + "type": "text", + "x": -777.5122605087536, + "y": 668.6052167193183, + "width": 220.7598114013672, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "NZSrNeEBjy8Yde8mxO84U" + ], + "frameId": null, + "index": "b1H", + "roundness": null, + "seed": 464743240, + "version": 242, + "versionNonce": 709405688, + "isDeleted": false, + "boundElements": [], + "updated": 1733967997153, + "link": null, + "locked": false, + "text": "- IJobLogger _jobLogger", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "- IJobLogger _jobLogger", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "Ni0WMEMiWFFak58950lKy", + "type": "text", + "x": -777.5122605087536, + "y": 701.4052159563788, + "width": 360.35968017578125, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "NZSrNeEBjy8Yde8mxO84U" + ], + "frameId": null, + "index": "b1I", + "roundness": null, + "seed": 1471595080, + "version": 243, + "versionNonce": 1777790968, + "isDeleted": false, + "boundElements": [ + { + "id": "aXlrkz9amYy_em2rBA4b3", + "type": "arrow" + } + ], + "updated": 1733968001109, + "link": null, + "locked": false, + "text": "- RetryCallbackTaskQueue _retryQueue", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "- RetryCallbackTaskQueue _retryQueue", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "4C24WIgiipk99oU2u2n_j", + "type": "text", + "x": -777.5122605087536, + "y": 734.2052151934394, + "width": 276.479736328125, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "NZSrNeEBjy8Yde8mxO84U" + ], + "frameId": null, + "index": "b1J", + "roundness": null, + "seed": 121059656, + "version": 241, + "versionNonce": 141450568, + "isDeleted": false, + "boundElements": [], + "updated": 1733965807565, + "link": null, + "locked": false, + "text": "- ConcurrentQueue taskQueue", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "- ConcurrentQueue taskQueue", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "YTMhh0-9py0gAIqr4h4mR", + "type": "text", + "x": -777.5122605087536, + "y": 779.0052144304999, + "width": 408.4795837402344, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "NZSrNeEBjy8Yde8mxO84U" + ], + "frameId": null, + "index": "b1K", + "roundness": null, + "seed": 2106642504, + "version": 241, + "versionNonce": 1109850936, + "isDeleted": false, + "boundElements": [], + "updated": 1733965807565, + "link": null, + "locked": false, + "text": "+Push(HandleCallbackParam callbackParam)", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "+Push(HandleCallbackParam callbackParam)", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "Ov2MTKa4bY9bj7WLSsbC3", + "type": "text", + "x": -777.5122605087536, + "y": 811.8052136675605, + "width": 98.45991516113281, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "NZSrNeEBjy8Yde8mxO84U" + ], + "frameId": null, + "index": "b1L", + "roundness": null, + "seed": 1117751112, + "version": 241, + "versionNonce": 339965000, + "isDeleted": false, + "boundElements": [], + "updated": 1733965807565, + "link": null, + "locked": false, + "text": "+Dispose()", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "+Dispose()", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "yjx8Z588nnQCK7sg-4Kx9", + "type": "text", + "x": -673.2321065242536, + "y": 1014.5529877112498, + "width": 228.77981567382812, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "qsjttUhl-ZvXmFGYyjOvM" + ], + "frameId": null, + "index": "b1M", + "roundness": null, + "seed": 2135449416, + "version": 107, + "versionNonce": 697540408, + "isDeleted": false, + "boundElements": [ + { + "id": "e1C6sNmWzJdSS86Jb2-Ds", + "type": "arrow" + } + ], + "updated": 1733965811834, + "link": null, + "locked": false, + "text": "RetryCallbackTaskQueue", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "RetryCallbackTaskQueue", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "JIY3ug6Ghz9SnXX8aXfWL", + "type": "text", + "x": -1208.0715659589666, + "y": 683.3877164052881, + "width": 114.07989501953125, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#d0bfff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "bvvZBnRAhAJTIuwbgHkaQ" + ], + "frameId": null, + "index": "b1N", + "roundness": null, + "seed": 769723464, + "version": 229, + "versionNonce": 1640404296, + "isDeleted": false, + "boundElements": [], + "updated": 1733965819162, + "link": null, + "locked": false, + "text": "AdminClient", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "AdminClient", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "kyAjj4J5Rg-pp1wk9rc6q", + "type": "text", + "x": 490.1296920776367, + "y": 588.4000024795532, + "width": 131.3399200439453, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "ZCIR2q1iWJTb3D-5hDfij" + ], + "frameId": null, + "index": "b1O", + "roundness": null, + "seed": 454460232, + "version": 18, + "versionNonce": 1242811976, + "isDeleted": false, + "boundElements": [ + { + "id": "-vCCuOTCRPYsJBmQrXQhZ", + "type": "arrow" + } + ], + "updated": 1733966037986, + "link": null, + "locked": false, + "text": "JobTaskQueue", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "JobTaskQueue", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "mowdZvvS7UxN7-KmW-xnK", + "type": "text", + "x": 365.3296890258789, + "y": 633.2000036239624, + "width": 307.19976806640625, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "ZCIR2q1iWJTb3D-5hDfij" + ], + "frameId": null, + "index": "b1P", + "roundness": null, + "seed": 720820808, + "version": 17, + "versionNonce": 1864617272, + "isDeleted": false, + "boundElements": [], + "updated": 1733966036178, + "link": null, + "locked": false, + "text": "- ConcurrentQueue TASK_QUEUE", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "- ConcurrentQueue TASK_QUEUE", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "8xheZgpfIwRnH_vP0LoJq", + "type": "text", + "x": 365.3296890258789, + "y": 666.000002861023, + "width": 339.49969482421875, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "ZCIR2q1iWJTb3D-5hDfij" + ], + "frameId": null, + "index": "b1Q", + "roundness": null, + "seed": 716460360, + "version": 17, + "versionNonce": 883254840, + "isDeleted": false, + "boundElements": [], + "updated": 1733966036178, + "link": null, + "locked": false, + "text": "- ConcurrentDictionary ID_IN_QUEUE", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "- ConcurrentDictionary ID_IN_QUEUE", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "jysm-hXAIDjRtqv3lnx6D", + "type": "text", + "x": 365.3296890258789, + "y": 698.8000020980835, + "width": 230.6997528076172, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "ZCIR2q1iWJTb3D-5hDfij" + ], + "frameId": null, + "index": "b1R", + "roundness": null, + "seed": 1638590536, + "version": 17, + "versionNonce": 1753997112, + "isDeleted": false, + "boundElements": [], + "updated": 1733966036178, + "link": null, + "locked": false, + "text": "+ EventHandler CallBack;", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "+ EventHandler CallBack;", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "3-c5Dqwnnds2_XpWprWNN", + "type": "text", + "x": 365.3296890258789, + "y": 743.600001335144, + "width": 116.35987854003906, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "ZCIR2q1iWJTb3D-5hDfij" + ], + "frameId": null, + "index": "b1S", + "roundness": null, + "seed": 1270259528, + "version": 17, + "versionNonce": 725301304, + "isDeleted": false, + "boundElements": [], + "updated": 1733966036178, + "link": null, + "locked": false, + "text": "+IsRunning()", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "+IsRunning()", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "qK0VfbuLh91tqRGWrgycP", + "type": "text", + "x": 365.3296890258789, + "y": 776.4000005722046, + "width": 344.1596374511719, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "ZCIR2q1iWJTb3D-5hDfij" + ], + "frameId": null, + "index": "b1T", + "roundness": null, + "seed": 154367560, + "version": 17, + "versionNonce": 1888973112, + "isDeleted": false, + "boundElements": [], + "updated": 1733966036178, + "link": null, + "locked": false, + "text": "+Replace(TriggerParam triggerParam)", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "+Replace(TriggerParam triggerParam)", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "puOJf7OY8nkbEL51WsSEH", + "type": "text", + "x": 365.3296890258789, + "y": 809.1999998092651, + "width": 317.0596618652344, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "ZCIR2q1iWJTb3D-5hDfij" + ], + "frameId": null, + "index": "b1U", + "roundness": null, + "seed": 1325231432, + "version": 17, + "versionNonce": 268732984, + "isDeleted": false, + "boundElements": [], + "updated": 1733966036178, + "link": null, + "locked": false, + "text": "+Push(TriggerParam triggerParam)", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "+Push(TriggerParam triggerParam)", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "pTaKYchdxsj4Tl3XUSP_e", + "type": "text", + "x": 365.3296890258789, + "y": 841.9999990463257, + "width": 68.13993835449219, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "ZCIR2q1iWJTb3D-5hDfij" + ], + "frameId": null, + "index": "b1V", + "roundness": null, + "seed": 1450214472, + "version": 17, + "versionNonce": 1416004408, + "isDeleted": false, + "boundElements": [], + "updated": 1733966036178, + "link": null, + "locked": false, + "text": "+Stop()", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "+Stop()", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "1tXqYHEh6JNxwnqbKXMai", + "type": "text", + "x": 365.3296890258789, + "y": 874.7999982833862, + "width": 98.45991516113281, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "ZCIR2q1iWJTb3D-5hDfij" + ], + "frameId": null, + "index": "b1W", + "roundness": null, + "seed": 1298709320, + "version": 17, + "versionNonce": 203512888, + "isDeleted": false, + "boundElements": [], + "updated": 1733966036178, + "link": null, + "locked": false, + "text": "+Dispose()", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "+Dispose()", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "Xzcl3wHVdj1TmI9FVOPdW", + "type": "text", + "x": 1093.572419039921, + "y": 569.6692483628856, + "width": 97.07987976074219, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "SIWbZNjsHMWgSKVOhZHJv" + ], + "frameId": null, + "index": "b1X", + "roundness": null, + "seed": 1465606968, + "version": 80, + "versionNonce": 1760136264, + "isDeleted": false, + "boundElements": [ + { + "id": "Sj_WjDfNUNLKxgXuBK4Xr", + "type": "arrow" + } + ], + "updated": 1733965855278, + "link": null, + "locked": false, + "text": "«interface»", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "«interface»", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "FvnBX5mUeRG_FMPMamcgv", + "type": "text", + "x": 1076.916169039921, + "y": 602.4692495072948, + "width": 126.93988037109375, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "SIWbZNjsHMWgSKVOhZHJv" + ], + "frameId": null, + "index": "b1Y", + "roundness": null, + "seed": 494550072, + "version": 79, + "versionNonce": 998294344, + "isDeleted": false, + "boundElements": [], + "updated": 1733965855278, + "link": null, + "locked": false, + "text": "ITaskExecutor", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "ITaskExecutor", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "dBS5Ve_oQpkDIcmuHX2Xa", + "type": "text", + "x": 948.1911629364054, + "y": 659.2692506517039, + "width": 343.15960693359375, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "SIWbZNjsHMWgSKVOhZHJv" + ], + "frameId": null, + "index": "b1Z", + "roundness": null, + "seed": 1280701752, + "version": 80, + "versionNonce": 1142495816, + "isDeleted": false, + "boundElements": [ + { + "id": "jjYgK6dCOHsqYAFk3UNwp", + "type": "arrow" + } + ], + "updated": 1733965855278, + "link": null, + "locked": false, + "text": "+Execute(TriggerParam triggerParam)", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "+Execute(TriggerParam triggerParam)", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "FoxOuSX52HCMa83ziAugt", + "type": "text", + "x": 1697.7173245601161, + "y": 571.4164746148409, + "width": 168.05984497070312, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "u_Ic0rEhZV5nVk-h4vj5X" + ], + "frameId": null, + "index": "b1a", + "roundness": null, + "seed": 716680520, + "version": 109, + "versionNonce": 484073544, + "isDeleted": false, + "boundElements": [], + "updated": 1733965864735, + "link": null, + "locked": false, + "text": "BeanTaskExecutor", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "BeanTaskExecutor", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "HC5oEvx35OuAoI_1koyFn", + "type": "text", + "x": 1590.8110821895107, + "y": 616.2164757592501, + "width": 336.61968994140625, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "u_Ic0rEhZV5nVk-h4vj5X" + ], + "frameId": null, + "index": "b1b", + "roundness": null, + "seed": 1088603208, + "version": 111, + "versionNonce": 138937224, + "isDeleted": false, + "boundElements": [ + { + "id": "8dpBECz-Ujox8hZElY4Y4", + "type": "arrow" + } + ], + "updated": 1733968040968, + "link": null, + "locked": false, + "text": "-IJobHandlerFactory _handlerFactory", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "-IJobHandlerFactory _handlerFactory", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "rc0cXIYtUEX71kEsEktk8", + "type": "text", + "x": 1590.8110821895107, + "y": 649.0164749963106, + "width": 215.53982543945312, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "u_Ic0rEhZV5nVk-h4vj5X" + ], + "frameId": null, + "index": "b1c", + "roundness": null, + "seed": 798161736, + "version": 111, + "versionNonce": 774688392, + "isDeleted": false, + "boundElements": [ + { + "id": "8dpBECz-Ujox8hZElY4Y4", + "type": "arrow" + } + ], + "updated": 1733968046333, + "link": null, + "locked": false, + "text": "-IJobLogger _jobLogger", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "-IJobLogger _jobLogger", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "675ODiNfv06lZo6Stmnew", + "type": "text", + "x": -68.67764084615033, + "y": 678.8919824383918, + "width": 202.21981811523438, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "Jr9OhsXudjtG69vnzdqxN" + ], + "frameId": null, + "index": "b1d", + "roundness": null, + "seed": 440762424, + "version": 147, + "versionNonce": 1761309512, + "isDeleted": false, + "boundElements": [ + { + "id": "P8_HDDPhmxXtvS5WwZg3-", + "type": "arrow" + } + ], + "updated": 1733965791341, + "link": null, + "locked": false, + "text": "HandleCallbackParam", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "HandleCallbackParam", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "U3dD7lzpBKWb977JkJtrp", + "type": "text", + "x": 1725.531821961648, + "y": 848.3043342784342, + "width": 97.07987976074219, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "eK4rEk8qRP2XpsofAbWDM" + ], + "frameId": null, + "index": "b1e", + "roundness": null, + "seed": 1028356168, + "version": 63, + "versionNonce": 1998533960, + "isDeleted": false, + "boundElements": [ + { + "id": "Zi1-Hxx1A34SYr1r-i8RR", + "type": "arrow" + } + ], + "updated": 1733965998228, + "link": null, + "locked": false, + "text": "«interface»", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "«interface»", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "u98r6DY8QpDpubvM9VC-0", + "type": "text", + "x": 1675.5255734875268, + "y": 881.1043354228434, + "width": 176.57984924316406, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "eK4rEk8qRP2XpsofAbWDM" + ], + "frameId": null, + "index": "b1f", + "roundness": null, + "seed": 80031560, + "version": 62, + "versionNonce": 1972347704, + "isDeleted": false, + "boundElements": [], + "updated": 1733965998228, + "link": null, + "locked": false, + "text": "IJobHandlerFactory", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "IJobHandlerFactory", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "32fYxhfFFLW2FZz2Edujg", + "type": "text", + "x": 1688.6859626567666, + "y": 1070.329205993482, + "width": 171.33985900878906, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "va-86zXh6tsa2alo38VHN" + ], + "frameId": null, + "index": "b1g", + "roundness": null, + "seed": 1591266888, + "version": 119, + "versionNonce": 1061486392, + "isDeleted": false, + "boundElements": [ + { + "id": "JmEJEJNnoGthRLyeUYddW", + "type": "arrow" + } + ], + "updated": 1733965996568, + "link": null, + "locked": false, + "text": "JobHandlerFactory", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "JobHandlerFactory", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "zloEB04Sfpul5wnSHQxGS", + "type": "text", + "x": 1590.4484596050088, + "y": 1127.1292071378912, + "width": 339.5996398925781, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "va-86zXh6tsa2alo38VHN" + ], + "frameId": null, + "index": "b1h", + "roundness": null, + "seed": 1983366472, + "version": 120, + "versionNonce": 49786952, + "isDeleted": false, + "boundElements": [], + "updated": 1733965996568, + "link": null, + "locked": false, + "text": "+GetJobHandler(string handlerName)", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "+GetJobHandler(string handlerName)", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "fc1c9qwaqYqHg2STfpE3i", + "type": "text", + "x": 1730.64028700687, + "y": 1329.4018154732778, + "width": 97.07987976074219, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "Iqfxylxz9NnmOXuk1w7iv" + ], + "frameId": null, + "index": "b1i", + "roundness": null, + "seed": 890211656, + "version": 199, + "versionNonce": 409402696, + "isDeleted": false, + "boundElements": [ + { + "id": "go9VGinMsdJsei6R7ro_c", + "type": "arrow" + } + ], + "updated": 1733965993158, + "link": null, + "locked": false, + "text": "«interface»", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "«interface»", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "DzBl-DjskkCdf17gPLk3n", + "type": "text", + "x": 1724.4340415845068, + "y": 1362.201816617687, + "width": 109.51991271972656, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "Iqfxylxz9NnmOXuk1w7iv" + ], + "frameId": null, + "index": "b1j", + "roundness": null, + "seed": 1967039560, + "version": 198, + "versionNonce": 1133009720, + "isDeleted": false, + "boundElements": [], + "updated": 1733965993158, + "link": null, + "locked": false, + "text": "IJobHandler", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "IJobHandler", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "aq1gN7tcebMpownkg3SiD", + "type": "text", + "x": 1569.8215309033544, + "y": 1419.0018177620962, + "width": 334.859619140625, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "Iqfxylxz9NnmOXuk1w7iv" + ], + "frameId": null, + "index": "b1k", + "roundness": null, + "seed": 762501960, + "version": 204, + "versionNonce": 1973143032, + "isDeleted": false, + "boundElements": [ + { + "id": "JUDJAsgnkBcXQOpaaG3o_", + "type": "arrow" + } + ], + "updated": 1733968052486, + "link": null, + "locked": false, + "text": "Execute(JobExecuteContext context) ", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "Execute(JobExecuteContext context) ", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "iBQJ8ZSU9zwrFXbHty2yi", + "type": "text", + "x": 1739.1240358020323, + "y": 1593.2929900860233, + "width": 94.34329223632812, + "height": 27.804375008393453, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "ngjAJqdsBfc8P6TWwgM9I" + ], + "frameId": null, + "index": "b1l", + "roundness": null, + "seed": 1598071624, + "version": 102, + "versionNonce": 66311048, + "isDeleted": false, + "boundElements": [ + { + "id": "JUDJAsgnkBcXQOpaaG3o_", + "type": "arrow" + } + ], + "updated": 1733968054157, + "link": null, + "locked": false, + "text": "«abstract»", + "fontSize": 20.595833339550705, + "fontFamily": 6, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": null, + "originalText": "«abstract»", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "tqjN1dAzYvBChFH8TxCOL", + "type": "text", + "x": 1704.3818481103108, + "y": 1624.4870732295494, + "width": 185.83567810058594, + "height": 27.804375008393457, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "ngjAJqdsBfc8P6TWwgM9I" + ], + "frameId": null, + "index": "b1m", + "roundness": null, + "seed": 618461768, + "version": 119, + "versionNonce": 875205000, + "isDeleted": false, + "boundElements": [ + { + "id": "mdnDLwz6TT_dr0ZbGGNvy", + "type": "arrow" + } + ], + "updated": 1733968054158, + "link": null, + "locked": false, + "text": "AbstractJobHandler", + "fontSize": 20.59583333955071, + "fontFamily": 6, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": null, + "originalText": "AbstractJobHandler", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "7D3CZXdL1Kz57-irUaoeW", + "type": "text", + "x": -1265.7244302420972, + "y": -216.461442004858, + "width": 163.43535369819097, + "height": 25.347131354362848, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "mDj4E91mUMdNl9Zv4vrcc" + ], + "frameId": null, + "index": "b1n", + "roundness": null, + "seed": 388474424, + "version": 293, + "versionNonce": 40625224, + "isDeleted": false, + "boundElements": [ + { + "id": "fjtkdaiF-PtdIJx-7ixb4", + "type": "arrow" + } + ], + "updated": 1733992312510, + "link": null, + "locked": false, + "text": "JobExecuteContext", + "fontSize": 18.775652855083592, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "JobExecuteContext", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "93qOaxkuAfaP6nmCld1zF", + "type": "text", + "x": -1358.9396835734758, + "y": -174.40397853511922, + "width": 181.81746623278838, + "height": 25.347131354362848, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "mDj4E91mUMdNl9Zv4vrcc" + ], + "frameId": null, + "index": "b1o", + "roundness": null, + "seed": 1086725944, + "version": 292, + "versionNonce": 1927639624, + "isDeleted": false, + "boundElements": [], + "updated": 1733992312510, + "link": null, + "locked": false, + "text": "+string JobParameter", + "fontSize": 18.775652855083592, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "+string JobParameter", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "67dYpZbugUnYnjQXgxHvb", + "type": "text", + "x": -1358.9396835734758, + "y": -143.61190856901646, + "width": 197.18633365739527, + "height": 25.347131354362848, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "mDj4E91mUMdNl9Zv4vrcc" + ], + "frameId": null, + "index": "b1p", + "roundness": null, + "seed": 36534328, + "version": 292, + "versionNonce": 688908616, + "isDeleted": false, + "boundElements": [], + "updated": 1733992312510, + "link": null, + "locked": false, + "text": "+IJobLogger JobLogger", + "fontSize": 18.775652855083592, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "+IJobLogger JobLogger", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "KW1UWeo7kqxKIeYb_q9T-", + "type": "text", + "x": -1358.9396835734758, + "y": -112.81983860291368, + "width": 324.49118631647565, + "height": 25.347131354362848, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "mDj4E91mUMdNl9Zv4vrcc" + ], + "frameId": null, + "index": "b1q", + "roundness": null, + "seed": 792834360, + "version": 292, + "versionNonce": 1087602760, + "isDeleted": false, + "boundElements": [], + "updated": 1733992312510, + "link": null, + "locked": false, + "text": "+CancellationToken cancellationToken", + "fontSize": 18.775652855083592, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "+CancellationToken cancellationToken", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "eQMGXECwYU6VTsEoUgqJn", + "type": "text", + "x": 1128.1524640362625, + "y": 1427.2762347744113, + "width": 185.09982299804688, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffd8a8", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "ZGBsIl7PTYgq080ACjzFT" + ], + "frameId": null, + "index": "b1r", + "roundness": null, + "seed": 680470072, + "version": 553, + "versionNonce": 1208449352, + "isDeleted": false, + "boundElements": [ + { + "id": "22l4uePe4aPVjdAZo1pkm", + "type": "arrow" + } + ], + "updated": 1733993219960, + "link": null, + "locked": false, + "text": "JobHandlerAttribute", + "fontSize": 20, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "JobHandlerAttribute", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "Zp8abAj33wHX4sw_ZIGF3", + "type": "text", + "x": -736.0922932457586, + "y": -300.4836636665516, + "width": 403.1438903808594, + "height": 237.60000000000002, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1s", + "roundness": null, + "seed": 443592760, + "version": 124, + "versionNonce": 1321235016, + "isDeleted": false, + "boundElements": [], + "updated": 1733965774679, + "link": null, + "locked": false, + "text": "用于接收Http请求和验证票据是否有效\n 一共有5中请求方法:\n 1. beat 心跳\n 2. idlebeat 用于检查Job是否在执行中\n 3. run 用于触发Job的执行,接口中包含Job执行的参数\n 4. kill 终止Job\n 5. log 用于获取Job执行的日志\n _jobDispatcher 用于处理2,3,4\n _jobLogger 用于处理5\n 内部直接处理1并返回\n ", + "fontSize": 16, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "top", + "containerId": "6dgirSDtPpWHYnLle_a6x", + "originalText": "用于接收Http请求和验证票据是否有效\n 一共有5中请求方法:\n 1. beat 心跳\n 2. idlebeat 用于检查Job是否在执行中\n 3. run 用于触发Job的执行,接口中包含Job执行的参数\n 4. kill 终止Job\n 5. log 用于获取Job执行的日志\n _jobDispatcher 用于处理2,3,4\n _jobLogger 用于处理5\n 内部直接处理1并返回\n ", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "VnDhZcBXbbNmSu7gU7LvK", + "type": "text", + "x": 286.71030746623114, + "y": 1086.4637991116895, + "width": 571.5118408203125, + "height": 216, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1t", + "roundness": null, + "seed": 1247668536, + "version": 441, + "versionNonce": 1047956536, + "isDeleted": false, + "boundElements": [], + "updated": 1733992369491, + "link": null, + "locked": false, + "text": "任务分发类\n 1. _executorFactory 用于根据GlueType获取具体的执行器\n 2. _callbackTaskQueue 队列用于存储需要回调的任务列表,向admin提交执行\n结果\n 3. RUNNING_QUEUE 正在执行中的任务队列\n 4. TryRemoveJobTask admin请求移除/取消job \n 5. Execute 执行Job,其实是将Job添加到执行队列等待被执行,这里要注意可\n能上一次还没执行完成,需要根据策略做对应的操作\n 6. IdleBeat 查询下Job是否在执行中.\n ", + "fontSize": 16, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "top", + "containerId": "LRE05T2Vg1kS3hlmJ5Y6Y", + "originalText": "任务分发类\n 1. _executorFactory 用于根据GlueType获取具体的执行器\n 2. _callbackTaskQueue 队列用于存储需要回调的任务列表,向admin提交执行结果\n 3. RUNNING_QUEUE 正在执行中的任务队列\n 4. TryRemoveJobTask admin请求移除/取消job \n 5. Execute 执行Job,其实是将Job添加到执行队列等待被执行,这里要注意可能上一次还没执行完成,需要根据策略做对应的操作\n 6. IdleBeat 查询下Job是否在执行中.\n ", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "alwXgy959OQFKUdCV8Cz_", + "type": "text", + "x": -1265.0652125869015, + "y": 873.3129698729779, + "width": 206.783935546875, + "height": 21.6, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1u", + "roundness": null, + "seed": 1513882168, + "version": 222, + "versionNonce": 169274936, + "isDeleted": false, + "boundElements": [], + "updated": 1733965433595, + "link": null, + "locked": false, + "text": "封装调用XxlJobAdmin的接口", + "fontSize": 16, + "fontFamily": 6, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "a9pzHPMy5bUFV_DTfRP8D", + "originalText": "封装调用XxlJobAdmin的接口", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "-obs1g-l6K_mXEegkyLyh", + "type": "text", + "x": 2071.078815502823, + "y": 1636.8564810659204, + "width": 256, + "height": 21.6, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1v", + "roundness": null, + "seed": 1492056888, + "version": 187, + "versionNonce": 475336760, + "isDeleted": false, + "boundElements": [], + "updated": 1733992352043, + "link": null, + "locked": false, + "text": "所有真实的业务逻辑应该实现该接口", + "fontSize": 16, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "top", + "containerId": "ZkrPInYNFOxO3OLpFX_9D", + "originalText": "所有真实的业务逻辑应该实现该接口", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "uvYskOKRYp3rX_B11Y7ad", + "type": "text", + "x": -1371.8322209282144, + "y": -500.8914250698699, + "width": 432.8158264160156, + "height": 129.60000000000002, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1w", + "roundness": null, + "seed": 1490736184, + "version": 199, + "versionNonce": 664773960, + "isDeleted": false, + "boundElements": [], + "updated": 1733992314229, + "link": null, + "locked": false, + "text": "JobHandler 执行的上下文\n JobParameter 的执行参数,Handler自行解析\n JobLogger 用于在执行期间记录日志[该日志可以在admin\nweb端查看]\n cancellationToken 取消的票据,可用于取消执行\n ", + "fontSize": 16, + "fontFamily": 6, + "textAlign": "left", + "verticalAlign": "top", + "containerId": "NwKxqAUsNgg1zdO4_A8of", + "originalText": "JobHandler 执行的上下文\n JobParameter 的执行参数,Handler自行解析\n JobLogger 用于在执行期间记录日志[该日志可以在admin web端查看]\n cancellationToken 取消的票据,可用于取消执行\n ", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "id": "iwgsAGVRpwo_N_hfZDomR", + "type": "text", + "x": 1126.7488918681595, + "y": 1251.6316793433373, + "width": 194.06394958496094, + "height": 21.6, + "angle": 0, + "strokeColor": "#000", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1x", + "roundness": null, + "seed": 2145619256, + "version": 685, + "versionNonce": 1242326856, + "isDeleted": false, + "boundElements": [], + "updated": 1733992342736, + "link": null, + "locked": false, + "text": "用于标记JobHandler的名字", + "fontSize": 16, + "fontFamily": 6, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "uroc63qy0bSRVORNyQpta", + "originalText": "用于标记JobHandler的名字", + "autoResize": true, + "lineHeight": 1.35 + } + ], + "appState": { + "gridSize": 20, + "gridStep": 5, + "gridModeEnabled": false, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/doc/class-diagram-for-v2.puml b/doc/class-diagram-for-v2.puml new file mode 100644 index 0000000..f0db93e --- /dev/null +++ b/doc/class-diagram-for-v2.puml @@ -0,0 +1,123 @@ +@startuml XxlJob.Core Class Diagram +title XxlJob.Core Class Diagram + +class XxlRestfulServiceHandler { + - JobDispatcher _jobDispatcher + - IJobLogger _jobLogger + + HandleAync(HttpContext context) +} + + + +interface IJobLogger { + + SetLogFile(long logTime, long logId) + + Log(string pattern, params object[] format) + + LogError(Exception ex) + + ReadLog(long logTime, long logId, int fromLine) + + LogSpecialFile() +} + + +class JobDispatcher { + - TaskExecutorFactory _executorFactory + - CallbackQueue _callbackTaskQueue + - ConcurrentDictionary RUNNING_QUEUE + + + TryRemoveJobTask(int jobId) + + Execute(TriggerParam triggerParam) + + IdleBeat(int jobId) +} + +class TaskExecutorFactory { + - Dictionary _cache + + GetTaskExecutor(string glueType) +} + +class CallbackQueue { + - AdminClient _adminClient + - IJobLogger _jobLogger + - RetryCallbackTaskQueue _retryQueue + - ConcurrentQueue taskQueue + + + Push(HandleCallbackParam callbackParam) + + Dispose() +} + +class RetryCallbackTaskQueue {} + +class AdminClient { +} + + +class JobTaskQueue { + - ConcurrentQueue TASK_QUEUE + - ConcurrentDictionary ID_IN_QUEUE + + EventHandler CallBack + + + IsRunning() + + Replace(TriggerParam triggerParam) + + Push(TriggerParam triggerParam) + + Stop() + + Dispose() +} + +interface ITaskExecutor { + + Execute(TriggerParam triggerParam) +} + +class BeanTaskExecutor { + - IJobHandlerFactory _handlerFactory + - IJobLogger _jobLogger +} + +class TriggerParam {} + +class HandleCallbackParam {} + +interface IJobHandlerFactory {} + +class JobHandlerFactory { + + GetJobHandler(string handlerName) +} + + +interface IJobHandler { + + Execute(JobExecuteContext context) +} + +abstract class AbstractJobHandler {} + + +class JobExecuteContext { + + string JobParameter + + IJobLogger JobLogger + + CancellationToken cancellationToken +} + +class JobHandlerAttribute {} + +note "用于标记JobHandler的名字" as N1 + +XxlRestfulServiceHandler -right-> JobDispatcher +XxlRestfulServiceHandler --> IJobLogger + +JobDispatcher --> TaskExecutorFactory +JobDispatcher --> CallbackQueue +JobDispatcher --> JobTaskQueue + +TaskExecutorFactory *-- ITaskExecutor +BeanTaskExecutor -up-|> ITaskExecutor + +CallbackQueue --> IJobLogger +CallbackQueue --> RetryCallbackTaskQueue +CallbackQueue --> AdminClient +CallbackQueue --> HandleCallbackParam + +BeanTaskExecutor --> IJobHandlerFactory +JobHandlerFactory -right-|> IJobHandlerFactory +JobHandlerFactory *-left- IJobHandler + +AbstractJobHandler -right-|> IJobHandler + +N1 .. JobHandlerAttribute +@enduml \ No newline at end of file diff --git a/doc/class-diagram-for-v2.svg b/doc/class-diagram-for-v2.svg new file mode 100644 index 0000000..bc61ae5 --- /dev/null +++ b/doc/class-diagram-for-v2.svg @@ -0,0 +1 @@ +XxlJob.Core Class DiagramXxlJob.Core Class DiagramXxlRestfulServiceHandlerJobDispatcher _jobDispatcherIJobLogger _jobLoggerHandleAync(HttpContext context)IJobLoggerSetLogFile(long logTime, long logId)Log(string pattern, params object[] format)LogError(Exception ex)ReadLog(long logTime, long logId, int fromLine)LogSpecialFile()JobDispatcherTaskExecutorFactory _executorFactoryCallbackQueue _callbackTaskQueueConcurrentDictionary<int, JobTaskQueue> RUNNING_QUEUETryRemoveJobTask(int jobId)Execute(TriggerParam triggerParam)IdleBeat(int jobId)TaskExecutorFactoryDictionary<string, ITaskExecutor> _cacheGetTaskExecutor(string glueType)CallbackQueueAdminClient _adminClientIJobLogger _jobLoggerRetryCallbackTaskQueue _retryQueueConcurrentQueue<HandleCallbackParam> taskQueuePush(HandleCallbackParam callbackParam)Dispose()RetryCallbackTaskQueueAdminClientJobTaskQueueConcurrentQueue<TriggerParam> TASK_QUEUEConcurrentDictionary<long, byte> ID_IN_QUEUEEventHandler<HandleCallbackParam> CallBackIsRunning()Replace(TriggerParam triggerParam)Push(TriggerParam triggerParam)Stop()Dispose()ITaskExecutorExecute(TriggerParam triggerParam)BeanTaskExecutorIJobHandlerFactory _handlerFactoryIJobLogger _jobLoggerTriggerParamHandleCallbackParamIJobHandlerFactoryJobHandlerFactoryGetJobHandler(string handlerName)IJobHandlerExecute(JobExecuteContext context)AbstractJobHandlerJobExecuteContextstring JobParameterIJobLogger JobLoggerCancellationToken cancellationTokenJobHandlerAttribute用于标记JobHandler的名字 \ No newline at end of file diff --git a/doc/class-diagram-for-v3.puml b/doc/class-diagram-for-v3.puml new file mode 100644 index 0000000..4e04ee2 --- /dev/null +++ b/doc/class-diagram-for-v3.puml @@ -0,0 +1,127 @@ +@startuml XxlJob.Core - Class Diagram + +title XxlJob.Core - Class Diagram + + +class XxlJobHttpHandler{ + + HandleAsync(HttpContext context) +} + +interface ICommandExecutorFactory{ + + GetCommandExecutor(string commandName) +} + +class CommandExecutorFactory{} + +interface ICommandExecutor{ + + string CommandName + + ExecuteAsync(byte[] payload) +} +class BeatCommandExecutor{} +class IdleBeatCommandExecutor{} +class TiggerCommandExecutor{} +class AbortCommandExecutor{} + + +interface IJobDispatcher{ + + StartAsync() + + StopAsync() + + DisposeAsync() + + + TiggerJobAsync(Job job) + + ReplaceJobAsync(Job job) + + AbortJobAsync(int jobId) +} +class JobDispatcher{ + - JobQueue _jobQueue + - CallQueue _callbackQueue +} + +interface IJobExecutorFactory{ + + GetTaskExecutor(string glueType) +} +class JobExecutorFactory{ +} +interface IJobExecutor{ + + string GlueType + + ExecuteAsync(Job job) +} + +class BeanJobExecutor{ +} + + +interface IJobHandlerFactory{ + + GetJobHandler(string handlerName) +} + +class JobHandlerFactory{ + +} + +interface IJobHandler{ + + string Name + + HandleAsync(JobContext context) +} + +class JobWorker{ + - Queue _jobQueue + + + DisposeAsync() + + + Event OnJobCompleted + + + EnqueueTaskAsync(JobTask task) + + ReplaceJobAsync(Job job) + + AbortJobAsync(JobId jobId) + +} +class JobExecuteResult{} +class CallBackWorker{ + - Queue _callbackQueue + + + DisposeAsync() + + + EnqueueAsync(JobExecuteResult result) +} + +interface IAdminClient{} + +XxlJobHttpHandler ..> ICommandExecutorFactory + +CommandExecutorFactory -up-|> ICommandExecutorFactory + +BeatCommandExecutor -up-|> ICommandExecutor +IdleBeatCommandExecutor -up-|> ICommandExecutor +TiggerCommandExecutor -up-|> ICommandExecutor +AbortCommandExecutor -up-|> ICommandExecutor + +ICommandExecutor .up.* CommandExecutorFactory + +BeanJobExecutor -right-|> IJobExecutor + +IJobExecutor .right.* JobExecutorFactory + +JobDispatcher -up-|> IJobDispatcher + +JobDispatcher ..> JobWorker +JobDispatcher ..> CallBackWorker + + +JobWorker ..> IJobExecutorFactory +CallBackWorker ..> IAdminClient + +JobExecutorFactory -up-|> IJobExecutorFactory + + +BeatCommandExecutor ..> IJobDispatcher +IdleBeatCommandExecutor ..> IJobDispatcher +TiggerCommandExecutor ..> IJobDispatcher +AbortCommandExecutor ..> IJobDispatcher + +BeanJobExecutor ..> IJobHandlerFactory +JobHandlerFactory -up-|> IJobHandlerFactory +IJobHandler ..* JobHandlerFactory + + +@enduml diff --git a/doc/class-diagram-for-v3.svg b/doc/class-diagram-for-v3.svg new file mode 100644 index 0000000..83eca00 --- /dev/null +++ b/doc/class-diagram-for-v3.svg @@ -0,0 +1 @@ +XxlJob.Core - Class DiagramXxlJob.Core - Class DiagramXxlJobHttpHandlerHandleAsync(HttpContext context)ICommandExecutorFactoryGetCommandExecutor(string commandName)CommandExecutorFactoryICommandExecutorstring CommandNameExecuteAsync(byte[] payload)BeatCommandExecutorIdleBeatCommandExecutorTiggerCommandExecutorAbortCommandExecutorIJobDispatcherStartAsync()StopAsync()DisposeAsync() AddJobAsync(Job job)ReplaceJobAsync(Job job)AbortJobAsync(int jobId)JobDispatcherJobQueue _jobQueueCallQueue _callbackQueueIJobExecutorFactoryGetTaskExecutor(string glueType)JobExecutorFactoryIJobExecutorstring GlueTypeExecuteAsync(Job job)BeanJobExecutorIJobHandlerFactoryGetJobHandler(string handlerName)JobHandlerFactoryIJobHandlerstring NameHandleAsync(JobContext context)JobWorkerQueue _jobQueue  Event OnJobCompletedStartAsync()StopAsync()DisposeAsync()EnqueueAsync(Job job)ReplaceAsync(Job job)AbortAsync(int jobId)JobExecuteResultCallBackWorkerQueue _callbackQueueStartAsync()StopAsync()DisposeAsync() EnqueueAsync(JobExecuteResult result)IAdminClient \ No newline at end of file diff --git a/src/DotXxlJob.Core/CommandExecutors/BeatCommandExecutor.cs b/src/DotXxlJob.Core/CommandExecutors/BeatCommandExecutor.cs index 41746f0..bae2459 100644 --- a/src/DotXxlJob.Core/CommandExecutors/BeatCommandExecutor.cs +++ b/src/DotXxlJob.Core/CommandExecutors/BeatCommandExecutor.cs @@ -1,19 +1,18 @@ -// Copyright (c) Xuanye Wong. All rights reserved. +// Copyright (c) Xuanye Wang. All rights reserved. // Licensed under MIT license using DotXxlJob.Core.Models; -using System.Threading; using System.Threading.Tasks; namespace DotXxlJob.Core.CommandExecutors { - public class BeatCommandExecutor:ICommandExecutor + public class BeatCommandExecutor : ICommandExecutor { public string CommandName => "Beat"; - public Task ExecuteAsync(byte[] payload,CancellationToken cancellationToken = default) + public Task ExecuteAsync(byte[] payload) { - return Task.FromResult(ExecutorResult.Success()); + return Task.FromResult(ApiResult.Success()); } } } \ No newline at end of file diff --git a/src/DotXxlJob.Core/CommandExecutors/CommandExecutorFactory.cs b/src/DotXxlJob.Core/CommandExecutors/CommandExecutorFactory.cs new file mode 100644 index 0000000..9383cda --- /dev/null +++ b/src/DotXxlJob.Core/CommandExecutors/CommandExecutorFactory.cs @@ -0,0 +1,28 @@ +// Copyright (c) Xuanye Wang. All rights reserved. +// Licensed under MIT license + +using System.Collections.Generic; +using System.Linq; + +namespace DotXxlJob.Core.CommandExecutors +{ + public class CommandExecutorFactory : ICommandExecutorFactory + { + private readonly Dictionary _commandExecutors; + + public CommandExecutorFactory(IEnumerable commandExecutors) + { + _commandExecutors = commandExecutors.ToDictionary(x => x.CommandName, y => y); + } + + public ICommandExecutor? GetCommandExecutor(string commandName) + { + + if (_commandExecutors.TryGetValue(commandName, out var commandExecutor)) + { + return commandExecutor; + } + return null; + } + } +} diff --git a/src/DotXxlJob.Core/CommandExecutors/IdleBeatCommandExecutor.cs b/src/DotXxlJob.Core/CommandExecutors/IdleBeatCommandExecutor.cs index 381d840..976a9ce 100644 --- a/src/DotXxlJob.Core/CommandExecutors/IdleBeatCommandExecutor.cs +++ b/src/DotXxlJob.Core/CommandExecutors/IdleBeatCommandExecutor.cs @@ -1,13 +1,12 @@ -// Copyright (c) Xuanye Wong. All rights reserved. +// Copyright (c) Xuanye Wang. All rights reserved. // Licensed under MIT license using DotXxlJob.Core.Models; -using System.Threading; using System.Threading.Tasks; namespace DotXxlJob.Core.CommandExecutors { - public class IdleBeatCommandExecutor:ICommandExecutor + public class IdleBeatCommandExecutor : ICommandExecutor { private readonly ISerializer _serializer; @@ -16,13 +15,13 @@ namespace DotXxlJob.Core.CommandExecutors _serializer = serializer; } public string CommandName => "IdleBeat"; - - public Task ExecuteAsync(byte[] payload,CancellationToken cancellationToken = default) + + public Task ExecuteAsync(byte[] payload) { var idleBeat = _serializer.Deserialize(payload); if (idleBeat == null) { - return Task.FromResult(ExecutorResult.Failure("Command[IdleBrat],parameter is empty")); + return Task.FromResult(ApiResult.Failure("Command[IdleBrat],parameter is empty")); } throw new System.NotImplementedException(); } diff --git a/src/DotXxlJob.Core/CommandExecutors/KillCommandExecutor.cs b/src/DotXxlJob.Core/CommandExecutors/KillCommandExecutor.cs index 1bd94a1..cbed8af 100644 --- a/src/DotXxlJob.Core/CommandExecutors/KillCommandExecutor.cs +++ b/src/DotXxlJob.Core/CommandExecutors/KillCommandExecutor.cs @@ -1,13 +1,12 @@ -// Copyright (c) Xuanye Wong. All rights reserved. +// Copyright (c) Xuanye Wang. All rights reserved. // Licensed under MIT license using DotXxlJob.Core.Models; -using System.Threading; using System.Threading.Tasks; namespace DotXxlJob.Core.CommandExecutors { - public class KillCommandExecutor: ICommandExecutor + public class KillCommandExecutor : ICommandExecutor { private readonly ISerializer _serializer; @@ -16,12 +15,12 @@ namespace DotXxlJob.Core.CommandExecutors _serializer = serializer; } public string CommandName => "kill"; - public Task ExecuteAsync(byte[] payload, CancellationToken cancellationToken) + public Task ExecuteAsync(byte[] payload) { var command = _serializer.Deserialize(payload); if (command == null) { - return Task.FromResult(ExecutorResult.Failure("Command[Kill],parameter is empty")); + return Task.FromResult(ApiResult.Failure("Command[Kill],parameter is empty")); } throw new System.NotImplementedException(); } diff --git a/src/DotXxlJob.Core/CommandExecutors/TriggerCommandExecutor.cs b/src/DotXxlJob.Core/CommandExecutors/TriggerCommandExecutor.cs index 08ccc0c..9f4e799 100644 --- a/src/DotXxlJob.Core/CommandExecutors/TriggerCommandExecutor.cs +++ b/src/DotXxlJob.Core/CommandExecutors/TriggerCommandExecutor.cs @@ -1,13 +1,12 @@ -// Copyright (c) Xuanye Wong. All rights reserved. +// Copyright (c) Xuanye Wang. All rights reserved. // Licensed under MIT license -using DotXxlJob.Core.Models; -using System.Threading; using System.Threading.Tasks; +using DotXxlJob.Core.Models; namespace DotXxlJob.Core.CommandExecutors { - public class TriggerCommandExecutor: ICommandExecutor + public class TriggerCommandExecutor : ICommandExecutor { private readonly ISerializer _serializer; @@ -16,12 +15,12 @@ namespace DotXxlJob.Core.CommandExecutors _serializer = serializer; } public string CommandName => "Run"; - public Task ExecuteAsync(byte[] payload, CancellationToken cancellationToken) + public Task ExecuteAsync(byte[] payload) { var command = _serializer.Deserialize(payload); - if (command== null) + if (command == null) { - return Task.FromResult(ExecutorResult.Failure("command[run],parameter is empty")); + return Task.FromResult(ApiResult.Failure("command[run],parameter is empty")); } throw new System.NotImplementedException(); } diff --git a/src/DotXxlJob.Core/DependencyExtensions.cs b/src/DotXxlJob.Core/DependencyExtensions.cs index e7b4eda..ff4c824 100644 --- a/src/DotXxlJob.Core/DependencyExtensions.cs +++ b/src/DotXxlJob.Core/DependencyExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Xuanye Wong. All rights reserved. +// Copyright (c) Xuanye Wang. All rights reserved. // Licensed under MIT license namespace DotXxlJob.Core diff --git a/src/DotXxlJob.Core/DotXxlJob.Core.csproj b/src/DotXxlJob.Core/DotXxlJob.Core.csproj index e56f99a..329fa4c 100644 --- a/src/DotXxlJob.Core/DotXxlJob.Core.csproj +++ b/src/DotXxlJob.Core/DotXxlJob.Core.csproj @@ -1,11 +1,12 @@  - netstandard2.1 + net6.0;net7.0;net8.0;net9.0 enable - + + diff --git a/src/DotXxlJob.Core/ICommandExecutor.cs b/src/DotXxlJob.Core/ICommandExecutor.cs index 1195b17..2085794 100644 --- a/src/DotXxlJob.Core/ICommandExecutor.cs +++ b/src/DotXxlJob.Core/ICommandExecutor.cs @@ -1,8 +1,7 @@ -// Copyright (c) Xuanye Wong. All rights reserved. +// Copyright (c) Xuanye Wang. All rights reserved. // Licensed under MIT license using DotXxlJob.Core.Models; -using System.Threading; using System.Threading.Tasks; namespace DotXxlJob.Core @@ -10,8 +9,8 @@ namespace DotXxlJob.Core public interface ICommandExecutor { string CommandName { get; } - Task ExecuteAsync(byte[] payload,CancellationToken cancellationToken); + Task ExecuteAsync(byte[] payload); } - + } \ No newline at end of file diff --git a/src/DotXxlJob.Core/ICommandExecutorFactory.cs b/src/DotXxlJob.Core/ICommandExecutorFactory.cs index 3c88a32..eb79313 100644 --- a/src/DotXxlJob.Core/ICommandExecutorFactory.cs +++ b/src/DotXxlJob.Core/ICommandExecutorFactory.cs @@ -1,10 +1,10 @@ -// Copyright (c) Xuanye Wong. All rights reserved. +// Copyright (c) Xuanye Wang. All rights reserved. // Licensed under MIT license namespace DotXxlJob.Core { public interface ICommandExecutorFactory { - ICommandExecutor GetCommandExecutor(string commandName); + ICommandExecutor? GetCommandExecutor(string commandName); } } \ No newline at end of file diff --git a/src/DotXxlJob.Core/IJobDispatcher.cs b/src/DotXxlJob.Core/IJobDispatcher.cs new file mode 100644 index 0000000..7de93b8 --- /dev/null +++ b/src/DotXxlJob.Core/IJobDispatcher.cs @@ -0,0 +1,19 @@ +// Copyright (c) Xuanye Wang. All rights reserved. +// Licensed under MIT license + +using System.Threading; +using System.Threading.Tasks; +using DotXxlJob.Core.Models; + +namespace DotXxlJob.Core +{ + public interface IJobDispatcher + { + Task StartAsync(CancellationToken cancellationToken); + Task StopAsync(CancellationToken cancellationToken); + + Task AddJobAsync(Job job); + Task ReplaceJobAsync(Job job); + Task AbortJobAsync(JobId jobId); + } +} diff --git a/src/DotXxlJob.Core/IJobExecutor.cs b/src/DotXxlJob.Core/IJobExecutor.cs new file mode 100644 index 0000000..4fa80e3 --- /dev/null +++ b/src/DotXxlJob.Core/IJobExecutor.cs @@ -0,0 +1,16 @@ +// Copyright (c) Xuanye Wang. All rights reserved. +// Licensed under MIT license + +using System.Threading; +using System.Threading.Tasks; +using DotXxlJob.Core.Models; + +namespace DotXxlJob.Core +{ + public interface IJobExecutor + { + string GlueType { get; } + + Task ExecuteAsync(JobExecuteContext context, Job job, CancellationToken cancellationToken); + } +} diff --git a/src/DotXxlJob.Core/IJobExecutorFactory.cs b/src/DotXxlJob.Core/IJobExecutorFactory.cs new file mode 100644 index 0000000..47b531b --- /dev/null +++ b/src/DotXxlJob.Core/IJobExecutorFactory.cs @@ -0,0 +1,11 @@ +// Copyright (c) Xuanye Wang. All rights reserved. +// Licensed under MIT license + +namespace DotXxlJob.Core +{ + public interface IJobExecutorFactory + { + IJobExecutor? GetJobExecutor(string glueType); + } + +} diff --git a/src/DotXxlJob.Core/IJobLogger.cs b/src/DotXxlJob.Core/IJobLogger.cs new file mode 100644 index 0000000..38b4af6 --- /dev/null +++ b/src/DotXxlJob.Core/IJobLogger.cs @@ -0,0 +1,12 @@ +// Copyright (c) Xuanye Wang. All rights reserved. +// Licensed under MIT license + +using System.Threading.Tasks; + +namespace DotXxlJob.Core +{ + public interface IJobLogger + { + Task LogAsync(string message); + } +} diff --git a/src/DotXxlJob.Core/ISerializer.cs b/src/DotXxlJob.Core/ISerializer.cs index 334b53f..b2e2b1f 100644 --- a/src/DotXxlJob.Core/ISerializer.cs +++ b/src/DotXxlJob.Core/ISerializer.cs @@ -1,4 +1,4 @@ -// Copyright (c) Xuanye Wong. All rights reserved. +// Copyright (c) Xuanye Wang. All rights reserved. // Licensed under MIT license using System; @@ -12,7 +12,7 @@ namespace DotXxlJob.Core { T? Deserialize(byte[] data) where T : class; byte[] Serialize(T item) where T : class; - object? Deserialize(byte[] data,Type type); - byte[] Serialize(object item,Type type); + object? Deserialize(byte[] data, Type type); + byte[] Serialize(object item, Type type); } } \ No newline at end of file diff --git a/src/DotXxlJob.Core/Internal/CallbackWorker.cs b/src/DotXxlJob.Core/Internal/CallbackWorker.cs new file mode 100644 index 0000000..c8cce06 --- /dev/null +++ b/src/DotXxlJob.Core/Internal/CallbackWorker.cs @@ -0,0 +1,16 @@ +// Copyright (c) Xuanye Wang. All rights reserved. +// Licensed under MIT license + +using System; +using System.Threading.Tasks; + +namespace DotXxlJob.Core.Internal +{ + internal class CallbackWorker : IAsyncDisposable + { + public ValueTask DisposeAsync() + { + throw new NotImplementedException(); + } + } +} diff --git a/src/DotXxlJob.Core/Internal/JobCompletedArgs.cs b/src/DotXxlJob.Core/Internal/JobCompletedArgs.cs new file mode 100644 index 0000000..2ee9a73 --- /dev/null +++ b/src/DotXxlJob.Core/Internal/JobCompletedArgs.cs @@ -0,0 +1,14 @@ +// Copyright (c) Xuanye Wang. All rights reserved. +// Licensed under MIT license + +using DotXxlJob.Core.Models; + +namespace DotXxlJob.Core.Internal +{ + internal class JobCompletedArgs + { + public Job Job { get; set; } = null!; + + public TaskResult TaskResult { get; set; } = null!; + } +} diff --git a/src/DotXxlJob.Core/Internal/JobDispatcher.cs b/src/DotXxlJob.Core/Internal/JobDispatcher.cs new file mode 100644 index 0000000..39e7703 --- /dev/null +++ b/src/DotXxlJob.Core/Internal/JobDispatcher.cs @@ -0,0 +1,58 @@ +// Copyright (c) Xuanye Wang. All rights reserved. +// Licensed under MIT license + +using System; +using System.Threading; +using System.Threading.Tasks; +using DotXxlJob.Core.Models; +using Microsoft.Extensions.DependencyInjection; + +namespace DotXxlJob.Core.Internal +{ + public class JobDispatcher : IJobDispatcher + { + private readonly JobWorker _jobWorker; + private readonly CallbackWorker _callbackWorker; + public JobDispatcher(IServiceProvider provider) + { + _jobWorker = ActivatorUtilities.CreateInstance(provider); + _jobWorker.JobCompleted += _jobWorker_JobCompleted; + + _callbackWorker = ActivatorUtilities.CreateInstance(provider); + } + + private void _jobWorker_JobCompleted(object? sender, JobCompletedArgs e) + { + //TODO:add job callback task + throw new NotImplementedException(); + } + + public Task StartAsync(CancellationToken cancellationToken) + { + return Task.CompletedTask; + } + + public async Task StopAsync(CancellationToken cancellationToken) + { + var t1 = _jobWorker.DisposeAsync(); + var t2 = _callbackWorker.DisposeAsync(); + await Task.WhenAll(t1.AsTask(), t2.AsTask()); + + _jobWorker.JobCompleted -= _jobWorker_JobCompleted; + } + + public Task AddJobAsync(Job job) + { + return _jobWorker.EnqueueJobAsync(job); + } + public Task ReplaceJobAsync(Job job) + { + return _jobWorker.ReplaceJobAsync(job); + } + public Task AbortJobAsync(JobId jobId) + { + return _jobWorker.AbortJobAsync(jobId); + } + + } +} diff --git a/src/DotXxlJob.Core/Internal/JobExecutorFactory.cs b/src/DotXxlJob.Core/Internal/JobExecutorFactory.cs new file mode 100644 index 0000000..1945936 --- /dev/null +++ b/src/DotXxlJob.Core/Internal/JobExecutorFactory.cs @@ -0,0 +1,26 @@ +// Copyright (c) Xuanye Wang. All rights reserved. +// Licensed under MIT license + +using System.Collections.Generic; +using System.Linq; + +namespace DotXxlJob.Core.Internal +{ + internal class JobExecutorFactory : IJobExecutorFactory + { + private readonly Dictionary _jobExecutors; + public JobExecutorFactory(IEnumerable jobExecutors) + { + _jobExecutors = jobExecutors.ToDictionary(x => x.GlueType); + } + public IJobExecutor? GetJobExecutor(string glueType) + { + if (_jobExecutors.TryGetValue(glueType, out var executor)) + { + return executor; + } + return null; + } + } + +} diff --git a/src/DotXxlJob.Core/Internal/JobQueue.cs b/src/DotXxlJob.Core/Internal/JobQueue.cs new file mode 100644 index 0000000..8684682 --- /dev/null +++ b/src/DotXxlJob.Core/Internal/JobQueue.cs @@ -0,0 +1,134 @@ +// Copyright (c) Xuanye Wang. All rights reserved. +// Licensed under MIT license + +using System; +using System.Collections.Concurrent; +using System.Threading; +using System.Threading.Tasks; +using DotXxlJob.Core.Models; + +namespace DotXxlJob.Core.Internal +{ + internal class JobQueue + { + private readonly ConcurrentQueue _jobs = new ConcurrentQueue(); + private readonly IJobLogger _jobLogger; + private CancellationTokenSource? _cancellationTokenSource; + private Task? _runningTask; + + public JobQueue(IJobExecutor executor, IJobLogger jobLogger) + { + Executor = executor; + _jobLogger = jobLogger; + } + + public IJobExecutor Executor { get; private set; } = null!; + + public event EventHandler? OnJobTaskCompleted; + + public async Task SetExecutor(IJobExecutor executor) + { + Executor = executor; + //TODO: cancel all pending jobs + await Stop(); + + _jobs.Clear(); + } + + public void EnqueueJob(Job job) + { + _jobs.Enqueue(job); + _runningTask = Start(); + } + + public bool IsRunning() + { + return _cancellationTokenSource != null && !_cancellationTokenSource.IsCancellationRequested; + } + + public async Task Replace(Job job) + { + _jobs.Clear(); + await Stop(); + EnqueueJob(job); + } + + private Task Start() + { + if (_cancellationTokenSource != null) + { + return _runningTask!; + } + _cancellationTokenSource = new CancellationTokenSource(); + var ct = _cancellationTokenSource.Token; + return Task.Factory.StartNew(async () => + { + + while (!ct.IsCancellationRequested) + { + if (_jobs.IsEmpty) + { + //_logger.LogInformation("task queue is empty!"); + break; + } + + TaskResult? result = null; + Job? jobTask = null; + try + { + + if (_jobs.TryDequeue(out jobTask)) + { + + //TODO: set Logger; + //_jobLogger.SetLogFile(jobTask.LogDateTime, jobTask.LogId); + //_jobLogger.Log("
----------- xxl-job job execute start -----------
----------- Param:{0}", jobTask.ExecutorParams); + + var exectorToken = ct; + CancellationTokenSource? timeoutCts = null; + if (jobTask.ExecutorTimeout > 0) + { + timeoutCts = new CancellationTokenSource(jobTask.ExecutorTimeout * 1000); + exectorToken = CancellationTokenSource.CreateLinkedTokenSource(exectorToken, timeoutCts.Token).Token; + } + + result = await Executor.ExecuteAsync(new JobExecuteContext(_jobLogger) { }, jobTask!, exectorToken); + + if (timeoutCts != null && timeoutCts.IsCancellationRequested) + { + result = TaskResult.Timeout(); + timeoutCts.Dispose(); + timeoutCts = null; + } + + //_jobLogger.Log("
----------- xxl-job job execute end(finish) -----------
----------- ReturnT:" + result.Code); + } + } + catch (Exception ex) + { + result = TaskResult.Failure("Dequeue Task Failed:" + ex.Message); + //_jobLogger.Log("
----------- JobThread Exception:" + ex.Message + "
----------- xxl-job job execute end(error) -----------"); + } + + if (jobTask != null) + { + OnJobTaskCompleted?.Invoke(this, new JobCompletedArgs() { Job = jobTask, TaskResult = result! }); + } + + } + + _cancellationTokenSource?.Dispose(); + _cancellationTokenSource = null; + }, _cancellationTokenSource.Token); + + } + private Task Stop() + { + _cancellationTokenSource?.Cancel(); + _cancellationTokenSource?.Dispose(); + _cancellationTokenSource = null; + + return _runningTask!; + } + } +} diff --git a/src/DotXxlJob.Core/Internal/JobWorker.cs b/src/DotXxlJob.Core/Internal/JobWorker.cs new file mode 100644 index 0000000..29e0fa9 --- /dev/null +++ b/src/DotXxlJob.Core/Internal/JobWorker.cs @@ -0,0 +1,97 @@ +// Copyright (c) Xuanye Wang. All rights reserved. +// Licensed under MIT license + +using System; +using System.Collections.Concurrent; +using System.Threading.Tasks; +using DotXxlJob.Core.Models; +using ErrorOr; + +namespace DotXxlJob.Core.Internal +{ + internal class JobWorker : IAsyncDisposable + { + private readonly IJobExecutorFactory _executorFactory; + + private readonly ConcurrentDictionary _jobsQueue = new ConcurrentDictionary(); + + public JobWorker(IJobExecutorFactory executorFactory) + { + _executorFactory = executorFactory; + } + + public event EventHandler? JobCompleted; + + + private void OnJobTaskCompleted(object? sender, JobCompletedArgs args) + { + JobCompleted?.Invoke(this, args); + } + + public async Task> EnqueueJobAsync(Job job) + { + var executor = _executorFactory.GetJobExecutor(job.GlueType); + + if (executor == null) + { + return TaskResult.Failure("Executor not found"); + } + //new job serial + if (!_jobsQueue.TryGetValue(job.JobId, out var queue)) + { + queue = new JobQueue(executor); + queue.OnJobTaskCompleted += OnJobTaskCompleted; + queue.EnqueueJob(job); + + if (_jobsQueue.TryAdd(job.JobId, queue)) + { + return TaskResult.Success(); + } + + return TaskResult.Failure("add running queue executor error"); + } + + //change executor + if (queue.Executor != executor) + { + queue.SetExecutor(executor); + queue.EnqueueJob(job); + return TaskResult.Success(); + } + //丢弃后续的 + if (job.ExecutorBlockStrategy == ExecutorBlockStrategy.DISCARD_LATER.ToString()) + { + //存在还没执行完成的任务 + if (queue.IsRunning()) + { + return TaskResult.Failure($"block strategy effect:{job.ExecutorBlockStrategy}"); + } + } + else if (job.ExecutorBlockStrategy == ExecutorBlockStrategy.COVER_EARLY.ToString()) //覆盖较早的 + { + return queue.Replace(job); + } + + queue.EnqueueJob(job); + return TaskResult.Success(); + + } + + + + public Task ReplaceJobAsync(Job job) + { + throw new NotImplementedException(); + } + + public Task AbortJobAsync(JobId jobId) + { + throw new NotImplementedException(); + } + + public ValueTask DisposeAsync() + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/src/DotXxlJob.Core/Internal/TextJsonSerializer.cs b/src/DotXxlJob.Core/Internal/TextJsonSerializer.cs index e833234..534dc41 100644 --- a/src/DotXxlJob.Core/Internal/TextJsonSerializer.cs +++ b/src/DotXxlJob.Core/Internal/TextJsonSerializer.cs @@ -1,4 +1,4 @@ -// Copyright (c) Xuanye Wong. All rights reserved. +// Copyright (c) Xuanye Wang. All rights reserved. // Licensed under MIT license using System; @@ -10,7 +10,7 @@ using System.Threading.Tasks; namespace DotXxlJob.Core.Internal { - public class TextJsonSerializer:ISerializer + public class TextJsonSerializer : ISerializer { public T? Deserialize(byte[] data) where T : class { @@ -26,14 +26,14 @@ namespace DotXxlJob.Core.Internal public object? Deserialize(byte[] data, Type type) { var json = Encoding.UTF8.GetString(data); - return JsonSerializer.Deserialize(json,type); + return JsonSerializer.Deserialize(json, type); } - public byte[] Serialize(object item,Type type) + public byte[] Serialize(object item, Type type) { - return JsonSerializer.SerializeToUtf8Bytes(item); + return JsonSerializer.SerializeToUtf8Bytes(item); } - + } } \ No newline at end of file diff --git a/src/DotXxlJob.Core/JobExecuteContext.cs b/src/DotXxlJob.Core/JobExecuteContext.cs new file mode 100644 index 0000000..02b1ae5 --- /dev/null +++ b/src/DotXxlJob.Core/JobExecuteContext.cs @@ -0,0 +1,15 @@ +// Copyright (c) Xuanye Wang. All rights reserved. +// Licensed under MIT license + +namespace DotXxlJob.Core +{ + public class JobExecuteContext + { + public JobExecuteContext(IJobLogger jobLogger) + { + JobLogger = jobLogger; + } + + public IJobLogger JobLogger { get; } = null!; + } +} diff --git a/src/DotXxlJob.Core/Models/ApiResult.cs b/src/DotXxlJob.Core/Models/ApiResult.cs new file mode 100644 index 0000000..18f8298 --- /dev/null +++ b/src/DotXxlJob.Core/Models/ApiResult.cs @@ -0,0 +1,42 @@ +// Copyright (c) Xuanye Wang. All rights reserved. +// Licensed under MIT license + +using Microsoft.AspNetCore.Http; +using System.Runtime.Serialization; + +namespace DotXxlJob.Core.Models +{ + public class ApiResult + { + protected const int SUCCESS_CODE = StatusCodes.Status200OK; + protected const int FAILURE_CODE = StatusCodes.Status500InternalServerError; + protected const int TIMEOUT_CODE = StatusCodes.Status502BadGateway; + + [DataMember(Name = "code", Order = 1)] + public int Code { get; set; } + [DataMember(Name = "msg", Order = 2)] + public string? Message { get; set; } + + public static ApiResult Success(string message = "") + { + return new ApiResult() { Code = SUCCESS_CODE, Message = message }; + } + public static ApiResult Failure(string message) + { + return new ApiResult() { Code = FAILURE_CODE, Message = message }; + } + + } + + public class ApiResult : ApiResult where T : class + { + [DataMember(Name = "content", Order = 3)] + public T Data { get; set; } = default!; + + public static ApiResult Success(string message, T data) + { + return new ApiResult() { Code = SUCCESS_CODE, Message = message, Data = data }; + } + + } +} \ No newline at end of file diff --git a/src/DotXxlJob.Core/Models/ExecutorBlockStrategy.cs b/src/DotXxlJob.Core/Models/ExecutorBlockStrategy.cs new file mode 100644 index 0000000..d67df83 --- /dev/null +++ b/src/DotXxlJob.Core/Models/ExecutorBlockStrategy.cs @@ -0,0 +1,12 @@ +// Copyright (c) Xuanye Wang. All rights reserved. +// Licensed under MIT license + +namespace DotXxlJob.Core.Models +{ + public enum ExecutorBlockStrategy + { + SERIAL_EXECUTION, + DISCARD_LATER, + COVER_EARLY + } +} diff --git a/src/DotXxlJob.Core/Models/ExecutorResult.cs b/src/DotXxlJob.Core/Models/ExecutorResult.cs deleted file mode 100644 index 80fdac6..0000000 --- a/src/DotXxlJob.Core/Models/ExecutorResult.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) Xuanye Wong. All rights reserved. -// Licensed under MIT license - -using System.Runtime.Serialization; - -namespace DotXxlJob.Core.Models -{ - public class ExecutorResult - { - protected const int SUCCESS_CODE = 200; - protected const int FAILURE_CODE = 500; - protected const int TIMEOUT_CODE = 502; - [DataMember(Name = "code",Order = 1)] - public int Code { get; set; } - [DataMember(Name = "msg",Order = 2)] - public string? Message { get; set; } - - public static ExecutorResult Success(string message="") - { - return new ExecutorResult(){Code = SUCCESS_CODE, Message = message}; - } - public static ExecutorResult Failure(string message) - { - return new ExecutorResult(){Code = FAILURE_CODE, Message = message}; - } - - } - - public class ExecutorResult : ExecutorResult where T : class - { - [DataMember(Name = "content",Order = 3)] - public T Data { get; set; } = default!; - - public static ExecutorResult Success(string message, T data) - { - return new ExecutorResult(){Code = SUCCESS_CODE, Message = message,Data = data}; - } - - } -} \ No newline at end of file diff --git a/src/DotXxlJob.Core/Models/IdleBeatCommand.cs b/src/DotXxlJob.Core/Models/IdleBeatCommand.cs index 9ba9073..12848aa 100644 --- a/src/DotXxlJob.Core/Models/IdleBeatCommand.cs +++ b/src/DotXxlJob.Core/Models/IdleBeatCommand.cs @@ -1,4 +1,4 @@ -// Copyright (c) Xuanye Wong. All rights reserved. +// Copyright (c) Xuanye Wang. All rights reserved. // Licensed under MIT license using System.Runtime.Serialization; diff --git a/src/DotXxlJob.Core/Models/Job.cs b/src/DotXxlJob.Core/Models/Job.cs new file mode 100644 index 0000000..67b893a --- /dev/null +++ b/src/DotXxlJob.Core/Models/Job.cs @@ -0,0 +1,22 @@ +// Copyright (c) Xuanye Wang. All rights reserved. +// Licensed under MIT license + +namespace DotXxlJob.Core.Models +{ + + public readonly record struct JobId(int Value); + + public class Job + { + public JobId JobId { get; set; } + + public string GlueType { get; set; } = null!; + public string HandlerName { get; set; } = null!; + public string ExecutorParams { get; set; } = null!; + public IJobExecutor? Executor { get; set; } + + public string ExecutorBlockStrategy { get; set; } = null!; + + public int ExecutorTimeout { get; set; } + } +} diff --git a/src/DotXxlJob.Core/Models/KillCommand.cs b/src/DotXxlJob.Core/Models/KillCommand.cs index 70f682f..38454b1 100644 --- a/src/DotXxlJob.Core/Models/KillCommand.cs +++ b/src/DotXxlJob.Core/Models/KillCommand.cs @@ -1,4 +1,4 @@ -// Copyright (c) Xuanye Wong. All rights reserved. +// Copyright (c) Xuanye Wang. All rights reserved. // Licensed under MIT license using System.Runtime.Serialization; diff --git a/src/DotXxlJob.Core/Models/TaskResult.cs b/src/DotXxlJob.Core/Models/TaskResult.cs new file mode 100644 index 0000000..00e593b --- /dev/null +++ b/src/DotXxlJob.Core/Models/TaskResult.cs @@ -0,0 +1,41 @@ +// Copyright (c) Xuanye Wang. All rights reserved. +// Licensed under MIT license + +namespace DotXxlJob.Core.Models +{ + public enum ResultCodes + { + Success = 200, + Failure = 500, + Timeout = 502 + } + + public class TaskResult + { + public ResultCodes Code { get; set; } + + public string? Message { get; set; } + + private static TaskResult _successResult = new() { Code = ResultCodes.Success }; + private static TaskResult _timeoutResult = new() { Code = ResultCodes.Timeout }; + + public static TaskResult Failure(string message) + { + return new TaskResult() { Code = ResultCodes.Failure, Message = message }; + } + public static TaskResult Timeout() + { + return _timeoutResult; + } + public static TaskResult Success() + { + return _successResult; + } + + } + + internal class TaskResult : TaskResult where T : class + { + public T Data { get; set; } = default!; + } +} diff --git a/src/DotXxlJob.Core/Models/TriggerCommand.cs b/src/DotXxlJob.Core/Models/TriggerCommand.cs index 2d3d257..93c6cf3 100644 --- a/src/DotXxlJob.Core/Models/TriggerCommand.cs +++ b/src/DotXxlJob.Core/Models/TriggerCommand.cs @@ -1,4 +1,4 @@ -// Copyright (c) Xuanye Wong. All rights reserved. +// Copyright (c) Xuanye Wang. All rights reserved. // Licensed under MIT license using System.Runtime.Serialization; @@ -15,32 +15,32 @@ namespace DotXxlJob.Core.Models public string ExecutorHandler { get; set; } = null!; [DataMember(Name = "executorParams", Order = 3)] - public string ExecutorParams{ get; set; } = null!; - + public string ExecutorParams { get; set; } = null!; + [DataMember(Name = "executorBlockStrategy", Order = 4)] - public string ExecutorBlockStrategy{ get; set; }= null!; - + public string ExecutorBlockStrategy { get; set; } = null!; + [DataMember(Name = "executorTimeout", Order = 5)] - public int ExecutorTimeout{ get; set; } - - [DataMember(Name = "logId",Order = 5)] + public int ExecutorTimeout { get; set; } + + [DataMember(Name = "logId", Order = 5)] public long LogId { get; set; } [DataMember(Name = "logDateTime", Order = 6)] - public long LogDateTime{ get; set; } - - - [DataMember(Name = "glueType",Order = 7)] - public string? GlueType{ get; set; } - - [DataMember(Name = "glueSource",Order = 8)] - public string? GlueSource{ get; set; } - + public long LogDateTime { get; set; } + + + [DataMember(Name = "glueType", Order = 7)] + public string? GlueType { get; set; } + + [DataMember(Name = "glueSource", Order = 8)] + public string? GlueSource { get; set; } + [DataMember(Name = "glueUpdatetime", Order = 9)] - public long GlueUpdateTime{ get; set; } + public long GlueUpdateTime { get; set; } - [DataMember(Name = "broadcastIndex",Order = 10)] - public int BroadcastIndex{ get; set; } - [DataMember(Name = "broadcastTotal",Order = 11)] - public int BroadcastTotal{ get; set; } + [DataMember(Name = "broadcastIndex", Order = 10)] + public int BroadcastIndex { get; set; } + [DataMember(Name = "broadcastTotal", Order = 11)] + public int BroadcastTotal { get; set; } } } \ No newline at end of file diff --git a/src/DotXxlJob.Core/Properties/launchSettings.json b/src/DotXxlJob.Core/Properties/launchSettings.json new file mode 100644 index 0000000..ab22b3e --- /dev/null +++ b/src/DotXxlJob.Core/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "profiles": { + "DotXxlJob.Core": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:51910;http://localhost:51911" + } + } +} \ No newline at end of file diff --git a/src/DotXxlJob.Core/XxlJobExecutorOptions.cs b/src/DotXxlJob.Core/XxlJobExecutorOptions.cs new file mode 100644 index 0000000..f4c0528 --- /dev/null +++ b/src/DotXxlJob.Core/XxlJobExecutorOptions.cs @@ -0,0 +1,67 @@ +// Copyright (c) Xuanye Wang. All rights reserved. +// Licensed under MIT license + +using System; +using System.IO; + +namespace DotXxlJob.Core +{ + public class XxlJobExecutorOptions + { + /// + /// admin site url, separated by semicolons + /// + public string AdminSiteUrl { get; set; } = null!; + + + /// + /// App name, must be consistent with the configuration on the admin side when auto-registering + /// + public string AppName { get; set; } = "xxl-job-executor-dotnet"; + + + /// + /// Special URL to bind, if this is configured, SpecialBindAddress and Port will be ignored + /// + public string? SpecialBindUrl { get; set; } + + /// + /// Address to submit when auto-registering, if empty, the internal network address will be automatically obtained + /// + public string? SpecialBindAddress { get; set; } + + + /// + /// Port to bind + /// + public int Port { get; set; } + + /// + /// Whether to auto-register + /// + public bool AutoRegistry { get; set; } + + /// + /// Access token + /// + public string? AccessToken { get; set; } + + + /// + /// Log directory, defaults to the logs subdirectory of the execution directory, please configure an absolute path + /// + public string LogPath { get; set; } = Path.Combine(AppContext.BaseDirectory, "./logs"); + + + /// + /// Log retention days + /// + public int LogRetentionDays { get; set; } = 30; + + + /// + /// Callback interval in milliseconds + /// + public int CallBackInterval { get; set; } = 500; + } +} diff --git a/src/DotXxlJob.Core/XxlJobHttpHandler.cs b/src/DotXxlJob.Core/XxlJobHttpHandler.cs new file mode 100644 index 0000000..53bc0e7 --- /dev/null +++ b/src/DotXxlJob.Core/XxlJobHttpHandler.cs @@ -0,0 +1,111 @@ +// Copyright (c) Xuanye Wang. All rights reserved. +// Licensed under MIT license + +using DotXxlJob.Core.Models; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Options; +using System; +using System.IO; +using System.Threading.Tasks; + + +namespace DotXxlJob.Core +{ + public class XxlJobHttpHandler + { + private readonly ICommandExecutorFactory _commandExecutorFactory; + private readonly ISerializer _serializer; + private readonly XxlJobExecutorOptions _options; + public XxlJobHttpHandler(ICommandExecutorFactory commandExecutorFactory, ISerializer serializer, IOptions optionsAccessor) + { + if (optionsAccessor?.Value == null) + { + throw new ArgumentNullException(nameof(optionsAccessor)); + } + _options = optionsAccessor.Value; + _commandExecutorFactory = commandExecutorFactory; + _serializer = serializer; + } + public async Task HandleAsync(HttpContext context) + { + var request = context.Request; + var response = context.Response; + var command = GetMethodName(request.Path.Value); + + if (string.IsNullOrEmpty(command)) + { + //no need to do anything + return; + } + if (!ValidateAccessToken(request)) + { + await SendErrorResponse(response, StatusCodes.Status401Unauthorized, "Unauthorized"); + return; + } + var executor = _commandExecutorFactory.GetCommandExecutor(command); + + if (executor == null) + { + await SendErrorResponse(response, StatusCodes.Status400BadRequest, "The method have not been implemented"); + return; + } + + byte[] payload; + using (var memoryStream = new MemoryStream()) + { + await request.Body.CopyToAsync(memoryStream); + payload = memoryStream.ToArray(); + } + var result = await executor.ExecuteAsync(payload); + await SendResponse(response, result.Code, result); + } + + private bool ValidateAccessToken(HttpRequest request) + { + if (string.IsNullOrEmpty(_options.AccessToken)) + { + return true; + } + + if (request.Headers.TryGetValue("XXL-JOB-ACCESS-TOKEN", out var accessToken) && _options.AccessToken.Equals(accessToken)) + { + return true; + } + + return false; + } + + private static string GetMethodName(string? path) + { + if (string.IsNullOrEmpty(path)) + { + return string.Empty; + } + + var arrParts = path.Split('/'); + if (arrParts.Length < 1) + { + return string.Empty; + } + return arrParts[arrParts.Length - 1].ToLower(); + } + + private Task SendResponse(HttpResponse response, int statusCode, object? data = null) + { + response.StatusCode = statusCode; + response.ContentType = "application/json"; + if (data != null) + { + var bytes = _serializer.Serialize(data, data.GetType()); + return response.Body.WriteAsync(bytes, 0, bytes.Length); + } + return Task.CompletedTask; + } + + private Task SendErrorResponse(HttpResponse response, int statusCode, string message) + { + return SendResponse(response, statusCode, ApiResult.Failure(message)); + } + } +} + diff --git a/tests/DotXxlJob.Core.UnitTests/CommandExecutorFactoryTests.cs b/tests/DotXxlJob.Core.UnitTests/CommandExecutorFactoryTests.cs new file mode 100644 index 0000000..3f26d95 --- /dev/null +++ b/tests/DotXxlJob.Core.UnitTests/CommandExecutorFactoryTests.cs @@ -0,0 +1,44 @@ +// Copyright (c) Xuanye Wang. All rights reserved. +// Licensed under MIT license + +using DotXxlJob.Core.CommandExecutors; +using Moq; + +namespace DotXxlJob.Core.UnitTests +{ + public class CommandExecutorFactoryTests + { + [Fact] + public void GetCommandExecutor_ReturnsExecutor_WhenExecutorExists() + { + // Arrange + var mockExecutor = new Mock(); + mockExecutor.Setup(e => e.CommandName).Returns("TestCommand"); + var executors = new List { mockExecutor.Object }; + var factory = new CommandExecutorFactory(executors); + + // Act + var result = factory.GetCommandExecutor("TestCommand"); + + // Assert + Assert.NotNull(result); + Assert.Equal("TestCommand", result?.CommandName); + } + + [Fact] + public void GetCommandExecutor_ReturnsNull_WhenExecutorDoesNotExist() + { + // Arrange + var mockExecutor = new Mock(); + mockExecutor.Setup(e => e.CommandName).Returns("TestCommand"); + var executors = new List { mockExecutor.Object }; + var factory = new CommandExecutorFactory(executors); + + // Act + var result = factory.GetCommandExecutor("NonExistentCommand"); + + // Assert + Assert.Null(result); + } + } +} diff --git a/tests/DotXxlJob.Core.UnitTests/DotXxlJob.Core.UnitTests.csproj b/tests/DotXxlJob.Core.UnitTests/DotXxlJob.Core.UnitTests.csproj new file mode 100644 index 0000000..68f1936 --- /dev/null +++ b/tests/DotXxlJob.Core.UnitTests/DotXxlJob.Core.UnitTests.csproj @@ -0,0 +1,28 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + + + + + + + + + + diff --git a/tests/DotXxlJob.Core.UnitTests/XxlJobHttpHandlerTests.cs b/tests/DotXxlJob.Core.UnitTests/XxlJobHttpHandlerTests.cs new file mode 100644 index 0000000..26d1558 --- /dev/null +++ b/tests/DotXxlJob.Core.UnitTests/XxlJobHttpHandlerTests.cs @@ -0,0 +1,84 @@ +// Copyright (c) Xuanye Wang. All rights reserved. +// Licensed under MIT license + +using DotXxlJob.Core.Models; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Options; +using Moq; +using System.Text; + +namespace DotXxlJob.Core.UnitTests +{ + + public class XxlJobHttpHandlerTests + { + private readonly Mock _commandExecutorFactoryMock; + private readonly Mock _serializerMock; + private readonly Mock> _optionsAccessorMock; + private readonly XxlJobHttpHandler _handler; + + public XxlJobHttpHandlerTests() + { + _commandExecutorFactoryMock = new Mock(); + _serializerMock = new Mock(); + _optionsAccessorMock = new Mock>(); + _optionsAccessorMock.Setup(o => o.Value).Returns(new XxlJobExecutorOptions { AccessToken = "test-token" }); + + _handler = new XxlJobHttpHandler(_commandExecutorFactoryMock.Object, _serializerMock.Object, _optionsAccessorMock.Object); + } + + [Fact] + public async Task HandleAsync_ShouldReturnUnauthorized_WhenAccessTokenIsInvalid() + { + // Arrange + var context = new DefaultHttpContext(); + context.Request.Path = "/test-command"; + context.Request.Headers["XXL-JOB-ACCESS-TOKEN"] = "invalid-token"; + + // Act + await _handler.HandleAsync(context); + + // Assert + Assert.Equal(StatusCodes.Status401Unauthorized, context.Response.StatusCode); + } + + [Fact] + public async Task HandleAsync_ShouldReturnBadRequest_WhenCommandExecutorIsNull() + { + // Arrange + var context = new DefaultHttpContext(); + context.Request.Path = "/test-command"; + context.Request.Headers["XXL-JOB-ACCESS-TOKEN"] = "test-token"; + + _commandExecutorFactoryMock.Setup(f => f.GetCommandExecutor(It.IsAny())).Returns((ICommandExecutor)null); + + // Act + await _handler.HandleAsync(context); + + // Assert + Assert.Equal(StatusCodes.Status400BadRequest, context.Response.StatusCode); + } + + [Fact] + public async Task HandleAsync_ShouldReturnSuccess_WhenCommandExecutorExecutesSuccessfully() + { + // Arrange + var context = new DefaultHttpContext(); + context.Request.Path = "/test-command"; + context.Request.Headers["XXL-JOB-ACCESS-TOKEN"] = "test-token"; + context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes("test-payload")); + + var executorMock = new Mock(); + executorMock.Setup(e => e.ExecuteAsync(It.IsAny())).ReturnsAsync(new ApiResult { Code = 200 }); + + _commandExecutorFactoryMock.Setup(f => f.GetCommandExecutor(It.IsAny())).Returns(executorMock.Object); + + // Act + await _handler.HandleAsync(context); + + // Assert + Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode); + } + } + +}