This commit is contained in:
2024-12-06 17:21:56 +09:00
10 changed files with 614 additions and 108 deletions

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,211 @@
<mxfile host="app.diagrams.net" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36" version="24.9.2">
<diagram name="ページ1" id="oKiyF3b1qzm0IX1SNAWJ">
<mxGraphModel dx="1395" dy="1078" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="0" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="Clf12EPbcqlM1huzJpPN-73" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#d0cee2;strokeColor=#56517e;" vertex="1" parent="1">
<mxGeometry x="92" y="645" width="720" height="180" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-71" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1">
<mxGeometry x="110" y="605" width="720" height="180" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-53" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#ffcccc;strokeColor=#36393d;" vertex="1" parent="1">
<mxGeometry x="140" y="570" width="720" height="180" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-52" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#cdeb8b;strokeColor=#36393d;" vertex="1" parent="1">
<mxGeometry x="170" y="530" width="720" height="180" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-51" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#ffcc99;strokeColor=#36393d;" vertex="1" parent="1">
<mxGeometry x="210" y="490" width="720" height="180" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-49" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="90" y="20" width="1080" height="290" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-10" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="Clf12EPbcqlM1huzJpPN-1" target="Clf12EPbcqlM1huzJpPN-9">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-1" value="タスク開始" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="120" y="215" width="90" height="50" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-45" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;dashed=1;curved=1;" edge="1" parent="1" source="Clf12EPbcqlM1huzJpPN-8" target="Clf12EPbcqlM1huzJpPN-9">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-47" value="DBからクロールのタスクを取得する" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="Clf12EPbcqlM1huzJpPN-45">
<mxGeometry x="0.2449" y="53" relative="1" as="geometry">
<mxPoint x="17" y="-44" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-79" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" edge="1" parent="1" source="Clf12EPbcqlM1huzJpPN-8" target="Clf12EPbcqlM1huzJpPN-78">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-8" value="DB" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;fillColor=#cce5ff;strokeColor=#36393d;" vertex="1" parent="1">
<mxGeometry x="570" y="-110" width="100" height="70" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-9" value="&lt;div style=&quot;background-color: rgb(255, 255, 254); font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 14px; line-height: 19px; white-space: pre;&quot;&gt;&lt;span style=&quot;&quot;&gt;タスクスケジューラ&lt;/span&gt;&lt;/div&gt;" style="whiteSpace=wrap;html=1;rounded=1;fontColor=default;" vertex="1" parent="1">
<mxGeometry x="260" y="217.5" width="140" height="45" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-17" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;curved=1;startArrow=block;startFill=1;endArrow=none;endFill=0;dashed=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;" edge="1" parent="1" source="Clf12EPbcqlM1huzJpPN-15" target="Clf12EPbcqlM1huzJpPN-9">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-18" value="タスクの割り当て" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="Clf12EPbcqlM1huzJpPN-17">
<mxGeometry x="0.04" relative="1" as="geometry">
<mxPoint x="-18" y="30" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-32" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.75;exitY=0;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;dashed=1;curved=1;" edge="1" parent="1" source="Clf12EPbcqlM1huzJpPN-15" target="Clf12EPbcqlM1huzJpPN-31">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-15" value="&lt;b&gt;RabbitMQ&lt;/b&gt;" style="whiteSpace=wrap;html=1;rounded=1;fillColor=#1ba1e2;fontColor=#ffffff;strokeColor=#006EAF;" vertex="1" parent="1">
<mxGeometry x="510" y="380" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-22" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="Clf12EPbcqlM1huzJpPN-19" target="Clf12EPbcqlM1huzJpPN-21">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-19" value="MQコマンドの受信" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="250" y="560" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-20" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;curved=1;dashed=1;" edge="1" parent="1" source="Clf12EPbcqlM1huzJpPN-15" target="Clf12EPbcqlM1huzJpPN-19">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-24" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="Clf12EPbcqlM1huzJpPN-21" target="Clf12EPbcqlM1huzJpPN-23">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-21" value="データ収集" style="whiteSpace=wrap;html=1;rounded=0;" vertex="1" parent="1">
<mxGeometry x="490" y="560" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-25" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;curved=1;dashed=1;" edge="1" parent="1" source="Clf12EPbcqlM1huzJpPN-23" target="Clf12EPbcqlM1huzJpPN-15">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-23" value="結果をMQメッセージを変換" style="whiteSpace=wrap;html=1;rounded=0;" vertex="1" parent="1">
<mxGeometry x="740" y="560" width="170" height="60" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-33" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;exitPerimeter=0;dashed=1;curved=1;" edge="1" parent="1" source="Clf12EPbcqlM1huzJpPN-26" target="Clf12EPbcqlM1huzJpPN-31">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="980" y="390" />
<mxPoint x="980" y="350" />
<mxPoint x="745" y="350" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-26" value="データ収集結果&lt;br&gt;一時保存" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;fillColor=#eeeeee;strokeColor=#36393d;" vertex="1" parent="1">
<mxGeometry x="970" y="390" width="100" height="70" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-27" value="タスクコマンド" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="1">
<mxGeometry x="340" y="450" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-28" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="Clf12EPbcqlM1huzJpPN-23" target="Clf12EPbcqlM1huzJpPN-26">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-29" value="コンテンツなど大きい情報を一時保存" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" vertex="1" parent="1">
<mxGeometry x="950" y="500" width="230" height="30" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-30" value="収集結果通知MQ" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" vertex="1" parent="1">
<mxGeometry x="740" y="450" width="110" height="30" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-37" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="Clf12EPbcqlM1huzJpPN-31" target="Clf12EPbcqlM1huzJpPN-36">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-31" value="メッセージキューから&lt;div&gt;データ受信&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="677" y="212.5" width="150" height="50" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-34" value="MQのキーで収集結果を取得" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" vertex="1" parent="1">
<mxGeometry x="760" y="350" width="170" height="30" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-39" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="Clf12EPbcqlM1huzJpPN-36" target="Clf12EPbcqlM1huzJpPN-38">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-36" value="&lt;div style=&quot;background-color: rgb(255, 255, 254); font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; line-height: 19px; white-space: pre;&quot;&gt;&lt;span&gt;データクリーニング&lt;br&gt;と重複排除&lt;/span&gt;&lt;/div&gt;" style="whiteSpace=wrap;html=1;rounded=1;fontColor=default;fontSize=11;" vertex="1" parent="1">
<mxGeometry x="890" y="207.5" width="148" height="60" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-43" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="Clf12EPbcqlM1huzJpPN-38" target="Clf12EPbcqlM1huzJpPN-42">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-38" value="データベースに保存" style="whiteSpace=wrap;html=1;fontSize=11;rounded=1;" vertex="1" parent="1">
<mxGeometry x="904" y="60" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-44" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="Clf12EPbcqlM1huzJpPN-42" target="Clf12EPbcqlM1huzJpPN-31">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-42" value="収集結果からサブ項目を抽出" style="whiteSpace=wrap;html=1;fontSize=11;rounded=1;" vertex="1" parent="1">
<mxGeometry x="692" y="60" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-46" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;curved=1;dashed=1;" edge="1" parent="1" source="Clf12EPbcqlM1huzJpPN-38" target="Clf12EPbcqlM1huzJpPN-8">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-48" value="クロール結果をDBに登録する" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="Clf12EPbcqlM1huzJpPN-46">
<mxGeometry x="0.1149" y="13" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-50" value="&lt;b&gt;メインプログラム&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="540" y="20" width="130" height="30" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-54" value="&lt;b&gt;クローラー サブプログラム&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="485" y="490" width="185" height="30" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-55" value="&lt;b&gt;SharePointクローラー&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="220" y="640" width="130" height="30" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-60" value="MQ情報" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;align=center;fontSize=14;fillColor=#dae8fc;strokeColor=#6c8ebf;gradientColor=#7ea6e0;" vertex="1" parent="1">
<mxGeometry x="1024" y="630" width="160" height="236" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-61" value="MQキー" style="text;strokeColor=none;fillColor=none;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=12;whiteSpace=wrap;html=1;" vertex="1" parent="Clf12EPbcqlM1huzJpPN-60">
<mxGeometry y="26" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-70" value="クローラーID" style="text;strokeColor=none;fillColor=none;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=12;whiteSpace=wrap;html=1;" vertex="1" parent="Clf12EPbcqlM1huzJpPN-60">
<mxGeometry y="56" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-62" value="分類" style="text;strokeColor=none;fillColor=none;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=12;whiteSpace=wrap;html=1;" vertex="1" parent="Clf12EPbcqlM1huzJpPN-60">
<mxGeometry y="86" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-63" value="タイトル" style="text;strokeColor=none;fillColor=none;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=12;whiteSpace=wrap;html=1;" vertex="1" parent="Clf12EPbcqlM1huzJpPN-60">
<mxGeometry y="116" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-64" value="URL" style="text;strokeColor=none;fillColor=none;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=12;whiteSpace=wrap;html=1;" vertex="1" parent="Clf12EPbcqlM1huzJpPN-60">
<mxGeometry y="146" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-65" value="サブ項目" style="text;strokeColor=none;fillColor=none;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=12;whiteSpace=wrap;html=1;" vertex="1" parent="Clf12EPbcqlM1huzJpPN-60">
<mxGeometry y="176" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-66" value="コンテンツ" style="text;strokeColor=none;fillColor=none;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=12;whiteSpace=wrap;html=1;" vertex="1" parent="Clf12EPbcqlM1huzJpPN-60">
<mxGeometry y="206" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-67" value="" style="endArrow=none;dashed=1;html=1;dashPattern=1 3;strokeWidth=2;rounded=0;entryX=1;entryY=0.75;entryDx=0;entryDy=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;" edge="1" parent="1" source="Clf12EPbcqlM1huzJpPN-60" target="Clf12EPbcqlM1huzJpPN-23">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="930" y="840" as="sourcePoint" />
<mxPoint x="980" y="790" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-68" value="&lt;b&gt;OneNodeクローラー&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="180" y="680" width="130" height="30" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-69" value="&lt;b&gt;EIM クローラー&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="150" y="720" width="130" height="30" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-72" value="&lt;b&gt;Coredasuクローラー&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="120" y="755" width="130" height="30" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-74" value="&lt;b&gt;その他クローラー&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="92" y="795" width="130" height="30" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-75" value="PostGre SQL" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" vertex="1" parent="1">
<mxGeometry x="585" y="-40" width="90" height="30" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-76" value="再帰的な処理" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="1030" y="140" width="80" height="50" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-77" value="NEO4J" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;" vertex="1" parent="1">
<mxGeometry x="570" y="-390" width="100" height="80" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-78" value="データ抽出" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="560" y="-250" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-80" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="Clf12EPbcqlM1huzJpPN-78" target="Clf12EPbcqlM1huzJpPN-77">
<mxGeometry relative="1" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

View File

@@ -1,6 +1,6 @@
{
"name": "k-tune",
"version": "0.2.0",
"version": "2.0.0 Beta",
"description": "Kintoneアプリの自動生成とデプロイを支援ツールです",
"productName": "k-tune | kintoneジェネレーター",
"author": "maxiaozhe@alicorns.co.jp <maxiaozhe@alicorns.co.jp>",

View File

@@ -2,7 +2,7 @@
<q-dialog :auto-close="false" :model-value="visible" persistent bordered>
<q-card class="dialog-content" >
<q-toolbar class="bg-grey-4">
<q-toolbar-title>{{domain.name}}のドメインを共有する</q-toolbar-title>
<q-toolbar-title>{{domain.name}}のドメイン利用権限設定</q-toolbar-title>
<q-btn flat round dense icon="close" @click="close" />
</q-toolbar>
@@ -17,12 +17,12 @@
input-debounce="0"
:options="canSharedUserFilteredOptions"
clearable
:placeholder="canSharedUserFilter ? '' : domain.domainActive ? '共有するユーザーを選択' : 'ドメインが有効でないため、共有ができない。'"
:placeholder="canSharedUserFilter ? '' : domain.domainActive ? '権限を付与するユーザーを選択' : 'ドメインが無効なため、権限を付与できません'"
@filter="filterFn"
:display-value="canSharedUserFilter?`${canSharedUserFilter.fullName} ${canSharedUserFilter.email}`:''">
<template v-slot:after>
<q-btn :disable="!canSharedUserFilter" :loading="addLoading" label="共有する" color="primary" @click="shareTo(canSharedUserFilter as IUserDisplay)" />
<q-btn :disable="!canSharedUserFilter" :loading="addLoading" label="付与" color="primary" @click="shareTo(canSharedUserFilter as IUserDisplay)" />
</template>
<template v-slot:option="scope">
@@ -33,9 +33,9 @@
</q-item>
</template>
</q-select>
<sharing-user-list class="q-mt-md" style="height: 330px" :users="sharedUsers" :loading="loading" title="現在の共有ユーザー">
<sharing-user-list class="q-mt-md" style="height: 330px" :users="sharedUsers" :loading="loading" title="ドメイン利用権限を持つユーザー">
<template v-slot:actions="{ row }">
<q-btn flat color="primary" padding="xs" size="1em" :loading="row.id == removingUser?.id" icon="person_off" @click="removeShareTo(row)" />
<q-btn title="解除" flat color="primary" padding="xs" size="1em" :loading="row.id == removingUser?.id" icon="person_off" @click="removeShareTo(row)" />
</template>
</sharing-user-list>
</q-card-section>

View File

@@ -0,0 +1,65 @@
<template>
<q-btn flat padding="xs" round size="1em" icon="more_vert" class="action-menu">
<q-menu>
<q-list dense :style="'min-width:' + minWidth ">
<template v-for="(item, index) in actions" :key="index" >
<q-item v-if="isAction(item)" :class="item.class" clickable v-close-popup @click="item.action(row)">
<q-item-section side style="color: inherit;">
<q-icon size="1.2em" :name="item.icon" />
</q-item-section>
<q-item-section>{{ item.label }}</q-item-section>
</q-item>
<q-separator v-else />
</template>
</q-list>
</q-menu>
</q-btn>
</template>
<script lang="ts">
import { PropType } from 'vue';
import { IDomainOwnerDisplay } from '../types/DomainTypes';
interface Action {
label: string;
icon?: string;
action: (row: any) => void|Promise<void>;
class?: string;
}
interface Separator {
separator: boolean;
}
type MenuItem = Action | Separator;
export default {
name: 'TableActionMenu',
props: {
row: {
type: Object as PropType<IDomainOwnerDisplay>,
required: true
},
minWidth: {
type: String,
default: '100px'
},
actions: {
type: Array as PropType<MenuItem[]>,
required: true
}
},
methods: {
isAction(item: MenuItem): item is Action {
return !('separator' in item);
}
}
};
</script>
<style lang="scss" scoped>
.q-table tr > td:last-child .action-menu {
opacity: 0.25;
}
.q-table tr:hover > td:last-child .action-menu {
opacity: 1;
}
</style>

View File

@@ -1,14 +1,16 @@
<template>
<q-card :class="['domain-card', small ? 'small' : '']">
<q-card-section :class="[small ? 'q-py-sm' : '']">
<div class="row items-center no-wrap">
<q-card :class="['domain-card', item.id == activeId ? 'default': '']">
<q-card-section>
<div class="row no-wrap">
<div class="col">
<div :class="[small ? 'text-subtitle2' : 'text-h6']">{{ item.name }} <q-icon v-if="isNotOwnerFunc(item.id)" name="account_circle" color="teal" class="q-mb-xs"></q-icon> </div>
<div :class="[small ? 'text-caption' : 'text-subtitle2']">{{ item.url }}</div>
<div class="text-h6 ellipsis">{{ item.name }}</div>
<div class="text-subtitle2">{{ item.url }}</div>
</div>
<!-- <div v-if="isNotOwnerFunc(item.id)" class="col-auto">
<q-icon name="account_circle" color="teal" size="2em"></q-icon>
</div> -->
<div v-if="!isOwnerFunc(item.owner.id)" class="col-auto">
<!-- <q-badge color="secondary" text-color="white" align="middle" class="q-mb-xs" label="他人の所有" /> -->
<q-chip square color="secondary" text-color="white" icon="people" label="他人の所有" size="sm" />
</div>
</div>
</q-card-section>
<q-card-section>
@@ -17,13 +19,13 @@
<div class="text-grey-7 text-caption text-weight-medium">
アカウント
</div>
<div class="smaller-font-size">{{ item.kintoneuser }}</div>
<div class="smaller-font-size">{{ item.user }}</div>
</div>
<div v-if="isNotOwnerFunc(item.id)" class="col-auto">
<div class="col-auto">
<div class="text-grey-7 text-caption text-weight-medium">
所有者
</div>
<div class="smaller-font-size">{{ item.owner }}</div>
<div class="smaller-font-size">{{ item.owner.fullName }}</div>
</div>
</div>
</q-card-section>
@@ -34,24 +36,27 @@
<script setup lang="ts">
import { defineProps, computed } from 'vue';
import { IDomain } from 'src/types/DomainTypes';
import { IDomainOwnerDisplay } from 'src/types/DomainTypes';
import { useAuthStore } from 'stores/useAuthStore';
const props = defineProps<{
item: IDomain;
small: boolean;
item: IDomainOwnerDisplay;
activeId: number;
}>();
const authStore = useAuthStore();
const isNotOwnerFunc = computed(() => (ownerId: string) => {
const isOwnerFunc = computed(() => (ownerId: string) => {
return ownerId == authStore.userId;
});
</script>
<style lang="scss" scoped>
.domain-card.small {
width: 18rem;
.domain-card.default {
box-shadow: 0 6px 6px -3px rgba(0, 0, 0, 0.2),
0 10px 14px 1px rgba(0, 0, 0, 0.14),
0 4px 18px 3px rgba(0, 0, 0, 0.12),
inset 0 0 0px 2px #1976D2;
}
.domain-card {
width: 22rem;

View File

@@ -128,7 +128,7 @@ const version = process.env.version;
const productName = process.env.productName;
onMounted(() => {
authStore.toggleLeftMenu();
authStore.setLeftMenu(true);
});
function toggleLeftDrawer() {

View File

@@ -16,6 +16,11 @@
</template>
</q-input>
</template>
<template v-slot:body-cell-name="prop">
<q-td :props="prop">
<q-btn flat dense :label="prop.row.name" @click="toEditFlowPage(prop.row)" ></q-btn>
</q-td>
</template>
<template v-slot:body-cell-url="prop">
<q-td :props="prop">
<a :href="prop.row.url" target="_blank" :title="prop.row.name" >
@@ -25,11 +30,7 @@
</template>
<template v-slot:body-cell-actions="p">
<q-td :props="p">
<q-btn-group flat>
<q-btn flat color="primary" padding="xs" size="1em" icon="edit_note" @click="toEditFlowPage(p.row)" />
<q-btn disabled flat color="primary" padding="xs" size="1em" icon="history" @click="showHistory(p.row)" />
<q-btn disabled flat color="negative" padding="xs" size="1em" icon="delete_outline" @click="removeRow(p.row)" />
</q-btn-group>
<table-action-menu :row="p.row" :actions="actionList" />
</q-td>
</template>
</q-table>
@@ -49,6 +50,7 @@
<script setup lang="ts">
import { ref, onMounted, watch, reactive } from 'vue';
import { useQuasar } from 'quasar'
import { api } from 'boot/axios';
import { useAuthStore } from 'stores/useAuthStore';
import { useFlowEditorStore } from 'stores/flowEditor';
@@ -57,6 +59,7 @@ import { date } from 'quasar'
import { IManagedApp } from 'src/types/AppTypes';
import ShowDialog from 'src/components/ShowDialog.vue';
import AppSelectBox from 'src/components/AppSelectBox.vue';
import TableActionMenu from 'components/TableActionMenu.vue';
interface IAppDisplay{
id:string;
@@ -78,7 +81,7 @@ const columns = [
{ name: 'user', label: '最後更新者', field: 'user', align: 'left', sortable: true},
{ name: 'updatetime', label: '最後更新日', field: 'updatetime', align: 'left', sortable: true},
{ name: 'version', label: 'バージョン', field: 'version', align: 'left', sortable: true, sort: numberStringSorting },
{ name: 'actions', label: '操作', field: 'actions' }
{ name: 'actions', label: '', field: 'actions' }
];
const pagination = ref({ sortBy: 'id', descending: true, rowsPerPage: 20 });
@@ -87,24 +90,40 @@ const filter = ref('');
const rows = ref<IAppDisplay[]>([]);
const rowIds = new Set<string>();
const $q = useQuasar()
const store = useFlowEditorStore();
const appDialog = ref();
const showSelectApp=ref(false);
const isAdding = ref(false);
const actionList = [
{ label: '設定', icon: 'account_tree', action: toEditFlowPage },
{ label: '履歴', icon: 'history', action: showHistory },
{ separator: true },
{ label: '削除', icon: 'delete_outline', class: 'text-red', action: removeRow },
];
const getApps = async () => {
loading.value = true;
rowIds.clear();
const result = await api.get('api/apps');
rows.value = result.data.map((item: IManagedApp) => {
try {
const { data } = await api.get('api/apps');
rows.value = data.data.map((item: IManagedApp) => {
rowIds.add(item.appid);
return appToAppDisplay(item)
}).sort((a: IAppDisplay, b: IAppDisplay) => a.sortId - b.sortId); // set default order
} catch (error) {
$q.notify({
icon: 'error',
color: 'negative',
message: 'アプリ一覧の読み込みに失敗しました'
});
} finally {
loading.value = false;
}
}
onMounted(async () => {
authStore.setLeftMenu(false);
await getApps();
});
@@ -130,11 +149,11 @@ const closeSelectAppDialog = async (val: 'OK'|'Cancel') => {
isAdding.value = false;
}
const removeRow = (app:IAppDisplay) => {
function removeRow(app:IAppDisplay) {
return
}
const showHistory = (app:IAppDisplay) => {
function showHistory(app:IAppDisplay) {
return
}
@@ -150,7 +169,7 @@ const appToAppDisplay = (app: IManagedApp) => {
}
}
const toEditFlowPage = (app:IAppDisplay) => {
function toEditFlowPage(app:IAppDisplay) {
store.setApp({
appId: app.id,
name: app.name

View File

@@ -17,24 +17,23 @@
</q-input>
</template>
<template v-slot:body-cell-name="p">
<q-td class="flex justify-between items-center" :props="p">
<div>
{{ p.row.name }}
<!-- <q-icon class="q-ml-xs" title="共有中です" name="people" :color="p.row.domainActive ? 'grey-8' : 'grey-4'" /> -->
</div>
<q-badge v-if="!p.row.domainActive" color="grey">未启用</q-badge>
<q-badge v-if="p.row.id == currentDomainId" color="primary">現在</q-badge>
<template v-slot:header-cell-active="p">
<q-th auto-width :props="p">
<q-select class="filter-header" v-model="activeFilter" :options="activeOptions" @update:model-value="activeFilterUpdate" borderless
dense options-dense hide-bottom-space/>
</q-th>
</template>
<template v-slot:body-cell-active="p">
<q-td auto-width :props="p">
<q-badge v-if="!p.row.domainActive" color="grey">未使用</q-badge>
<q-badge v-if="p.row.id == currentDomainId" color="primary">既定</q-badge>
</q-td>
</template>
<template v-slot:body-cell-actions="p">
<q-td :props="p">
<q-btn-group flat>
<q-btn flat color="primary" padding="xs" size="1em" icon="edit_note" @click="editRow(p.row)" />
<q-btn flat color="primary" padding="xs" size="1em" icon="person_add_alt" @click="openShareDg(p.row)" />
<q-btn flat color="negative" padding="xs" size="1em" icon="delete_outline" @click="removeRow(p.row)" />
</q-btn-group>
<table-action-menu :row="p.row" :actions="actionList" />
</q-td>
</template>
@@ -115,7 +114,7 @@
<q-card>
<q-card-section v-if="deleteLoadingState == -1" class="row items-center">
<q-spinner color="primary" size="2em"/>
<span class="q-ml-sm">共有ユーザーのチェック</span>
<span class="q-ml-sm">ドメイン利用権限を確認中</span>
</q-card-section>
<q-card-section v-else-if="deleteLoadingState == 0" class="row items-center">
<q-icon name="warning" color="warning" size="2em" />
@@ -123,7 +122,7 @@
</q-card-section>
<q-card-section v-else class="row items-center">
<q-icon name="error" color="negative" size="2em" />
<span class="q-ml-sm">共有ユーザーが存在しキャンセルする必要がある</span>
<span class="q-ml-sm">ドメイン利用権限が存在しキャンセルする必要がある</span>
</q-card-section>
<q-card-actions align="right">
@@ -145,6 +144,7 @@ import { api } from 'boot/axios';
import { useAuthStore } from 'stores/useAuthStore';
import { useDomainStore } from 'stores/useDomainStore';
import ShareDomainDialog from 'components/ShareDomain/ShareDomainDialog.vue';
import TableActionMenu from 'components/TableActionMenu.vue';
import { IDomain, IDomainOwnerDisplay, IDomainSubmit } from '../types/DomainTypes';
const authStore = useAuthStore();
@@ -163,10 +163,11 @@ const columns = [
// classes: inactiveRowClass
// },
{ name: 'name', label: '環境名', field: 'name', align: 'left', sortable: true, classes: inactiveRowClass },
{ name: 'active', label: 'x', align: 'left', field: 'domainActive', classes: inactiveRowClass },
{ name: 'url', label: 'URL', field: 'url', align: 'left', sortable: true, classes: inactiveRowClass },
{ name: 'user', label: 'ログイン名', field: 'user', align: 'left', classes: inactiveRowClass },
{ name: 'owner', label: '所有者', field: row => row.owner.fullName, align: 'left', classes: inactiveRowClass },
{ name: 'actions', label: '操作', field: 'actions', classes: inactiveRowClass }
{ name: 'actions', label: '', field: 'actions', classes: inactiveRowClass }
];
const pagination = ref({ sortBy: 'id', descending: true, rowsPerPage: 20 });
@@ -192,9 +193,37 @@ const domainActive = ref(true);
const isCreate = ref(true);
let editId = ref(0);
const shareDg = ref(false);
const shareDomain = ref<IDomainOwnerDisplay>();
const shareDomain = ref<IDomainOwnerDisplay>({} as IDomainOwnerDisplay);
const getDomain = async () => {
const activeOptions = [
{ value: 0, label: '全状態' },
{ value: 1, label: '使用' },
{ value: 2, label: '未使用'}
]
const activeFilter = ref(activeOptions[0]);
const activeFilterUpdate = (option: {value: number}) => {
switch (option.value) {
case 1:
getDomain((row) => row.domainActive)
break;
case 2:
getDomain((row) => !row.domainActive)
break;
default:
getDomain()
break;
}
}
const actionList = [
{ label: '編集', icon: 'edit_note', action: editRow },
{ label: '利用権限設定', icon: 'person_add_alt', action: openShareDg },
{ separator: true },
{ label: '削除', icon: 'delete_outline', class: 'text-red', action: removeRow },
];
const getDomain = async (filter?: (row: IDomainOwnerDisplay) => boolean) => {
loading.value = true;
const { data } = await api.get<{data:IDomain[]}>(`api/domains`);
rows.value = data.data.map((item) => {
@@ -217,7 +246,7 @@ const getDomain = async () => {
isSuperuser: item.owner.is_superuser,
}
}
});
}).filter(filter || (() => true));
loading.value = false;
}
@@ -232,7 +261,7 @@ const addRow = () => {
show.value = true;
}
const removeRow = async (row: IDomainOwnerDisplay) => {
async function removeRow(row: IDomainOwnerDisplay) {
confirm.value = true;
deleteLoadingState.value = -1;
editId.value = row.id;
@@ -253,7 +282,7 @@ const deleteDomain = () => {
deleteLoadingState.value = -1;
};
const editRow = (row) => {
function editRow(row) {
isCreate.value = false
editId.value = row.id;
// tenantid.value = row.tenantid;
@@ -307,7 +336,7 @@ const onSubmit = () => {
})
}
const openShareDg = (row: IDomainOwnerDisplay|number) => {
function openShareDg(row: IDomainOwnerDisplay|number) {
if (typeof row === 'number') {
row = rows.value.find(item => item.id === row) as IDomainOwnerDisplay;
}
@@ -315,7 +344,7 @@ const openShareDg = (row: IDomainOwnerDisplay|number) => {
shareDg.value = true;
};
const closeShareDg = () => {
function closeShareDg() {
shareDg.value = false;
}
@@ -333,8 +362,18 @@ const onReset = () => {
}
</script>
<style lang="scss">
.filter-header .q-field__native {
font-size: 12px;
font-weight: 500;
}
.filter-header .q-icon {
width: 12px;
}
.q-table td.inactive-row {
color: #aaa;
background-color: #fafafa;
}
.q-table tr > td.inactive-row:last-child {
color: inherit;
}
</style>

View File

@@ -29,14 +29,14 @@
<template v-slot:item="props">
<div class="q-pa-sm">
<domain-card :item="props.row">
<domain-card :item="props.row" :active-id="activeDomainId">
<template v-slot:actions>
<q-card-actions align="right">
<q-chip v-if="isActive(props.row.id)" outline color="teal" text-color="white" icon="done">
<q-chip class="no-border" v-if="isActive(props.row.id)" outline color="primary" text-color="white" icon="done">
既定
</q-chip>
<q-btn flat v-else :loading="activeDomainLoadingId === props.row.id" :disable="deleteDomainLoadingId === props.row.id" @click="activeDomain(props.row)">既定にする</q-btn>
<q-btn flat :disable="isNotOwner(props.row.id) || activeDomainLoadingId === props.row.id" :text-color="isNotOwner(props.row.id)?'grey':''" :loading="deleteDomainLoadingId === props.row.id" @click="clickDeleteConfirm(props.row)">削除</q-btn>
<q-btn flat :disable="isNotOwner(props.row.owner.id) || activeDomainLoadingId === props.row.id" :text-color="isNotOwner(props.row.owner.id)?'grey':''" :loading="deleteDomainLoadingId === props.row.id" @click="clickDeleteConfirm(props.row)">削除</q-btn>
</q-card-actions>
</template>
</domain-card>
@@ -70,6 +70,7 @@
import { ref, onMounted, computed } from 'vue'
import { api } from 'boot/axios';
import { useAuthStore } from 'stores/useAuthStore';
import { IDomainOwnerDisplay } from '../types/DomainTypes';
import ShowDialog from 'components/ShowDialog.vue';
import DomainCard from 'components/UserDomain/DomainCard.vue';
@@ -77,7 +78,7 @@ import DomainSelect from 'components/DomainSelect.vue';
const authStore = useAuthStore();
const pagination = ref({ sortBy: 'id', rowsPerPage: 0 });
const rows = ref([] as any[]);
const rows = ref<IDomainOwnerDisplay[]>([]);
const rowIds = new Set<string>();
const initLoading = ref(true);
const addUserDomainLoading = ref(false);
@@ -88,7 +89,7 @@ const columns = [
{ name: 'id' },
{ name: 'name', required: true, label: 'Name', align: 'left', field: 'name', sortable: true },
{ name: 'url', align: 'center', label: 'Domain', field: 'url', sortable: true },
{ name: 'kintoneuser', label: 'User', field: 'kintoneuser', sortable: true },
{ name: 'kintoneuser', label: 'User', field: 'user', sortable: true },
];
const userDomainTableFilter = ref();
@@ -162,7 +163,7 @@ const isActive = computed(() => (id: number) => {
});
const isNotOwner = computed(() => (ownerId: string) => {
return ownerId == authStore.userId;
return ownerId !== authStore.userId;
});
const getDomain = async (userId? : string) => {
@@ -171,16 +172,27 @@ const getDomain = async (userId? : string) => {
activeDomainId.value = resp?.data?.data?.id;
const domainResult = userId ? await api.get(`api/domain?userId=${userId}`) : await api.get(`api/domain`);
const domains = domainResult.data as any[];
rows.value = domains.reduce((acc, item) => {
rows.value = domains.sort((a, b) => a.id - b.id).reduce((acc, item) => {
rowIds.add(item.id);
if (item.is_active) {
acc.push({
id: item.id,
tenantid: item.tenantid,
domainActive: item.is_active,
name: item.name,
url: item.url,
kintoneuser: item.kintoneuser,
ownerEmail: item.owner.email,
owner: item.owner.first_name + ' ' + item.owner.last_name,
user: item.kintoneuser,
password: item.kintonepwd,
owner: {
id: item.owner.id,
firstName: item.owner.first_name,
lastName: item.owner.last_name,
fullNameSearch: (item.owner.last_name + item.owner.first_name).toLowerCase(),
fullName: item.owner.last_name + ' ' + item.owner.first_name,
email: item.owner.email,
isActive: item.owner.is_active,
isSuperuser: item.owner.is_superuser,
}
})
}
return acc;