From 2d0a212718a23e9e393a0e6bbac3ffc7e69c78e0 Mon Sep 17 00:00:00 2001 From: hades Date: Thu, 5 May 2022 18:52:09 +0800 Subject: [PATCH 01/85] =?UTF-8?q?REPORT-70446=20=E4=BA=A4=E4=BA=92?= =?UTF-8?q?=E8=A7=86=E8=A7=89=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mainframe/alphafine/component/ProductNewsImagePanel.java | 2 +- .../fr/design/mainframe/alphafine/question/QuestionPane.java | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/ProductNewsImagePanel.java b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/ProductNewsImagePanel.java index e87b6f199..dd1b3c483 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/ProductNewsImagePanel.java +++ b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/ProductNewsImagePanel.java @@ -56,7 +56,7 @@ public class ProductNewsImagePanel extends JPanel { } Set readSet = DesignerEnvManager.getEnvManager().getAlphaFineConfigManager().getReadSet(); if (!readSet.contains(productNews.getId())) { - g2.drawImage(NEW_TIP_IMAGE, 0, 0, this); + g2.drawImage(NEW_TIP_IMAGE, 0, 0, (int) (NEW_TIP_IMAGE.getWidth(this) / SVGLoader.SYSTEM_SCALE), (int) (NEW_TIP_IMAGE.getHeight(this) / SVGLoader.SYSTEM_SCALE), this); } g2.setColor(BACKGROUND_COLOR); diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/question/QuestionPane.java b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/question/QuestionPane.java index 2726824b7..81617d0d8 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/question/QuestionPane.java +++ b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/question/QuestionPane.java @@ -39,7 +39,9 @@ public class QuestionPane extends JPanel { } else { g2.drawImage(QUESTION_BACKGROUND_IMAGE, 0, 0, getWidth(), getHeight(), this); } - g2.drawImage(QUESTION_IMAGE, (getWidth() - QUESTION_IMAGE.getWidth(this)) / 2, (getHeight() - QUESTION_IMAGE.getHeight(this)) / 2, this); + int imageWidth = (int) (QUESTION_IMAGE.getWidth(this) / SVGLoader.SYSTEM_SCALE); + int imageHeight = (int) (QUESTION_IMAGE.getHeight(this) / SVGLoader.SYSTEM_SCALE); + g2.drawImage(QUESTION_IMAGE, (getWidth() - imageWidth) / 2, (getHeight() - imageHeight) / 2, imageWidth, imageHeight, this); } From a1fdbf1e0d875645e174198902403aad82de77c0 Mon Sep 17 00:00:00 2001 From: kerry Date: Sat, 7 May 2022 17:12:37 +0800 Subject: [PATCH 02/85] =?UTF-8?q?REPORT-67316=20=E5=86=B3=E7=AD=96?= =?UTF-8?q?=E6=8A=A5=E8=A1=A8-=E5=AE=A2=E6=88=B7=E6=A8=A1=E6=9D=BF?= =?UTF-8?q?=E6=98=AF=E7=BB=9D=E5=AF=B9=E5=B8=83=E5=B1=80=E5=9B=BA=E5=AE=9A?= =?UTF-8?q?=E5=A4=A7=E5=B0=8F=E7=9A=84=EF=BC=8C=E5=A4=8D=E5=88=B6=E5=AE=A2?= =?UTF-8?q?=E6=88=B7=E6=A8=A1=E6=9D=BF=E9=87=8C=E7=9A=84tab=E5=88=B0?= =?UTF-8?q?=E4=B8=80=E4=B8=AA=E6=96=B0=E5=BB=BA=E7=9A=84=E7=BB=9D=E5=AF=B9?= =?UTF-8?q?=E5=B8=83=E5=B1=80=E5=9B=BA=E5=AE=9A=E5=A4=A7=E5=B0=8Ffrm?= =?UTF-8?q?=EF=BC=8C=E4=BF=9D=E5=AD=98=E5=85=B3=E9=97=AD=E5=86=8D=E6=89=93?= =?UTF-8?q?=E5=BC=80=E6=96=B0=E5=BB=BA=E6=A8=A1=E6=9D=BF=EF=BC=8Ctab?= =?UTF-8?q?=E4=B8=8B=E7=9A=84=E6=8A=A5=E8=A1=A8=E5=9D=97=E5=B0=BA=E5=AF=B8?= =?UTF-8?q?=E5=8F=98=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fr/design/mainframe/FormSelectionUtils.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/designer-form/src/main/java/com/fr/design/mainframe/FormSelectionUtils.java b/designer-form/src/main/java/com/fr/design/mainframe/FormSelectionUtils.java index 5e5740f03..68461206a 100644 --- a/designer-form/src/main/java/com/fr/design/mainframe/FormSelectionUtils.java +++ b/designer-form/src/main/java/com/fr/design/mainframe/FormSelectionUtils.java @@ -16,6 +16,7 @@ import com.fr.design.designer.creator.XWTitleLayout; import com.fr.design.designer.creator.cardlayout.XWTabFitLayout; import com.fr.design.fun.FormWidgetOptionProvider; import com.fr.design.utils.ComponentUtils; +import com.fr.design.utils.gui.LayoutUtils; import com.fr.form.main.Form; import com.fr.form.ui.Widget; import com.fr.form.ui.container.WTitleLayout; @@ -290,11 +291,25 @@ public class FormSelectionUtils { public static XCreator copyXcreator(Form form, XCreator xCreator) throws CloneNotSupportedException{ Widget copied = (Widget) xCreator.toData().clone(); XCreator copiedCreator = XCreatorUtils.createXCreator(copied, xCreator.getSize()); + //主要用来处理组件间隔和padding,保证界面上展示的组件尺寸是计算过padding和组件间隔的 + LayoutUtils.layoutContainer(copiedCreator); + traverAndAdjust(copiedCreator); ArrayList nameSpace = new ArrayList<>(); copyWidgetName(form, nameSpace, copiedCreator); return copiedCreator; } + private static void traverAndAdjust(XCreator creator) { + for (int i = 0; i < creator.getComponentCount(); i++) { + Object object = creator.getComponent(i); + if (object instanceof XCreator) { + XCreator temp = (XCreator) object; + temp.adjustCompSize(0.0); + traverAndAdjust(temp); + } + } + } + /** * 拷贝组件 * @param form 当前表单 From f2931631f0f7b431d9b2fccde552890cda885c99 Mon Sep 17 00:00:00 2001 From: kerry Date: Mon, 9 May 2022 10:20:18 +0800 Subject: [PATCH 03/85] =?UTF-8?q?REPORT-67316=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E9=87=8D=E5=A4=8D=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../design/designer/creator/XCreatorUtils.java | 17 +++++++++++++++++ .../java/com/fr/design/mainframe/FormArea.java | 16 ++-------------- .../fr/design/mainframe/FormSelectionUtils.java | 12 +----------- 3 files changed, 20 insertions(+), 25 deletions(-) diff --git a/designer-form/src/main/java/com/fr/design/designer/creator/XCreatorUtils.java b/designer-form/src/main/java/com/fr/design/designer/creator/XCreatorUtils.java index 1be6f0d33..d5b6a9581 100644 --- a/designer-form/src/main/java/com/fr/design/designer/creator/XCreatorUtils.java +++ b/designer-form/src/main/java/com/fr/design/designer/creator/XCreatorUtils.java @@ -472,4 +472,21 @@ public class XCreatorUtils { } return container; } + + + /** + * 循环遍历组件,调整组件间隙 + * @param creator + */ + public static void traverAndAdjust(XCreator creator) { + for (int i = 0; i < creator.getComponentCount(); i++) { + Object object = creator.getComponent(i); + if (object instanceof XCreator) { + XCreator temp = (XCreator) object; + temp.adjustCompSize(0.0); + traverAndAdjust(temp); + } + } + + } } \ No newline at end of file diff --git a/designer-form/src/main/java/com/fr/design/mainframe/FormArea.java b/designer-form/src/main/java/com/fr/design/mainframe/FormArea.java index d989aae21..54e412383 100644 --- a/designer-form/src/main/java/com/fr/design/mainframe/FormArea.java +++ b/designer-form/src/main/java/com/fr/design/mainframe/FormArea.java @@ -4,6 +4,7 @@ import com.fr.design.actions.UpdateAction; import com.fr.design.constants.UIConstants; import com.fr.design.designer.beans.events.DesignerEvent; import com.fr.design.designer.creator.XCreator; +import com.fr.design.designer.creator.XCreatorUtils; import com.fr.design.designer.creator.XLayoutContainer; import com.fr.design.designer.creator.XWBorderLayout; import com.fr.design.designer.creator.XWFitLayout; @@ -447,7 +448,7 @@ public class FormArea extends JComponent implements ScrollRulerComponent { XLayoutContainer root = FormArea.this.designer.getRootComponent(); if (root.acceptType(XWFitLayout.class)) { XWFitLayout layout = (XWFitLayout) root; - traverAndAdjust(layout, 0.0); + XCreatorUtils.traverAndAdjust(layout); layout.adjustCreatorsWhileSlide(0.0); // 拖动滑块,先将内部组件百分比大小计算,再计算容器大小 Dimension d = new Dimension(layout.getWidth(), layout.getHeight()); @@ -469,19 +470,6 @@ public class FormArea extends JComponent implements ScrollRulerComponent { } } - //循环遍历布局,按百分比调整子组件大小 - private void traverAndAdjust(XCreator creator, double percent) { - for (int i = 0; i < creator.getComponentCount(); i++) { - Object object = creator.getComponent(i); - if (object instanceof XCreator) { - XCreator temp = (XCreator) object; - temp.adjustCompSize(percent); - traverAndAdjust(temp, percent); - } - } - - } - /** * 增加刻度条 */ diff --git a/designer-form/src/main/java/com/fr/design/mainframe/FormSelectionUtils.java b/designer-form/src/main/java/com/fr/design/mainframe/FormSelectionUtils.java index 68461206a..331a1f3fe 100644 --- a/designer-form/src/main/java/com/fr/design/mainframe/FormSelectionUtils.java +++ b/designer-form/src/main/java/com/fr/design/mainframe/FormSelectionUtils.java @@ -293,22 +293,12 @@ public class FormSelectionUtils { XCreator copiedCreator = XCreatorUtils.createXCreator(copied, xCreator.getSize()); //主要用来处理组件间隔和padding,保证界面上展示的组件尺寸是计算过padding和组件间隔的 LayoutUtils.layoutContainer(copiedCreator); - traverAndAdjust(copiedCreator); + XCreatorUtils.traverAndAdjust(copiedCreator); ArrayList nameSpace = new ArrayList<>(); copyWidgetName(form, nameSpace, copiedCreator); return copiedCreator; } - private static void traverAndAdjust(XCreator creator) { - for (int i = 0; i < creator.getComponentCount(); i++) { - Object object = creator.getComponent(i); - if (object instanceof XCreator) { - XCreator temp = (XCreator) object; - temp.adjustCompSize(0.0); - traverAndAdjust(temp); - } - } - } /** * 拷贝组件 From cd8ff843ec4d9f7f38f69bf968cfc45660fe74a6 Mon Sep 17 00:00:00 2001 From: hades Date: Tue, 10 May 2022 09:26:45 +0800 Subject: [PATCH 04/85] =?UTF-8?q?REPORT-69846=20=E5=A4=B9=E5=9C=A8?= =?UTF-8?q?=E4=B8=AD=E9=97=B4=E7=9A=84=E5=8D=95=E5=85=83=E6=A0=BC=EF=BC=8C?= =?UTF-8?q?=E4=B8=8E=E5=85=B6=E4=BB=96=E5=8D=95=E5=85=83=E6=A0=BC=E6=A1=86?= =?UTF-8?q?=E9=80=89=E9=85=8D=E7=BD=AE=E8=BE=B9=E6=A1=86=E6=97=B6=EF=BC=8C?= =?UTF-8?q?=E5=8D=95=E5=85=83=E6=A0=BC=E8=83=8C=E6=99=AF=E9=A2=9C=E8=89=B2?= =?UTF-8?q?=E4=BC=9A=E5=8F=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/fr/design/gui/style/BackgroundPane.java | 10 ++++++++++ .../main/java/com/fr/design/gui/style/BorderPane.java | 5 ++++- .../backgroundpane/NullBackgroundQuickPane.java | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/designer-base/src/main/java/com/fr/design/gui/style/BackgroundPane.java b/designer-base/src/main/java/com/fr/design/gui/style/BackgroundPane.java index bdf96f32e..3e0f6c213 100644 --- a/designer-base/src/main/java/com/fr/design/gui/style/BackgroundPane.java +++ b/designer-base/src/main/java/com/fr/design/gui/style/BackgroundPane.java @@ -34,6 +34,11 @@ public class BackgroundPane extends AbstractBasicStylePane { //获取当前面板 protected BackgroundQuickPane currentPane = null; + /** + * 记录上次选中的面板 + */ + protected BackgroundQuickPane lastSelectedPane = null; + public BackgroundPane() { this.initComponents(); @@ -62,6 +67,9 @@ public class BackgroundPane extends AbstractBasicStylePane { @Override public void itemStateChanged(ItemEvent e) { cardlayout.show(centerPane, (String) typeComboBox.getSelectedItem()); + if (e.getStateChange() == ItemEvent.DESELECTED) { + lastSelectedPane = currentPane; + } currentPane = paneList[typeComboBox.getSelectedIndex()]; fireStateChanged(); } @@ -144,6 +152,8 @@ public class BackgroundPane extends AbstractBasicStylePane { pane.populateBean(background); typeComboBox.setSelectedIndex(i); currentPane = paneList[i]; + // 填充 初始化 + lastSelectedPane = currentPane; return; } } diff --git a/designer-base/src/main/java/com/fr/design/gui/style/BorderPane.java b/designer-base/src/main/java/com/fr/design/gui/style/BorderPane.java index 3c917ea45..6d6259393 100644 --- a/designer-base/src/main/java/com/fr/design/gui/style/BorderPane.java +++ b/designer-base/src/main/java/com/fr/design/gui/style/BorderPane.java @@ -15,6 +15,7 @@ import com.fr.design.gui.icombobox.LineComboBox; import com.fr.design.gui.ilable.UILabel; import com.fr.design.layout.TableLayout; import com.fr.design.layout.TableLayoutHelper; +import com.fr.design.mainframe.backgroundpane.NullBackgroundQuickPane; import com.fr.design.style.color.NewColorSelectBox; import com.fr.design.utils.gui.AdjustWorkBookDefaultStyleUtils; import com.fr.general.IOUtils; @@ -246,7 +247,9 @@ public class BorderPane extends AbstractBasicStylePane implements GlobalNameObse style = AdjustWorkBookDefaultStyleUtils.adjustCellElement(Style.DEFAULT_STYLE); } - if (backgroundPane.currentPane.isBackgroundChange()) { + boolean isSetNullBackground = backgroundPane.currentPane instanceof NullBackgroundQuickPane + && !(backgroundPane.lastSelectedPane instanceof NullBackgroundQuickPane); + if (backgroundPane.currentPane.isBackgroundChange() || isSetNullBackground) { style = style.deriveBackground(backgroundPane.update()); } if (BORDER_SET.contains(globalNameListener.getGlobalName())) { diff --git a/designer-base/src/main/java/com/fr/design/mainframe/backgroundpane/NullBackgroundQuickPane.java b/designer-base/src/main/java/com/fr/design/mainframe/backgroundpane/NullBackgroundQuickPane.java index 9a86a38c1..44a46d60a 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/backgroundpane/NullBackgroundQuickPane.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/backgroundpane/NullBackgroundQuickPane.java @@ -35,7 +35,7 @@ public class NullBackgroundQuickPane extends BackgroundQuickPane { @Override public boolean isBackgroundChange() { - return true; + return false; } /** From ee8ccd76c73933d1287b69b922f0463e18be2e54 Mon Sep 17 00:00:00 2001 From: kerry Date: Thu, 19 May 2022 15:43:28 +0800 Subject: [PATCH 05/85] =?UTF-8?q?REPORT-67489=20tab=E6=A0=87=E9=A2=98?= =?UTF-8?q?=E9=AB=98=E5=BA=A6=E4=BF=AE=E6=94=B9=EF=BC=8C=E5=B8=83=E5=B1=80?= =?UTF-8?q?=E5=86=85=E9=83=A8=E7=BB=84=E4=BB=B6=E5=A4=A7=E5=B0=8F=E4=B8=8D?= =?UTF-8?q?=E4=BC=9A=E9=80=82=E5=BA=94=E7=BC=A9=E6=94=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../layout/FRCardMainBorderLayoutAdapter.java | 11 +++++++++++ .../fr/design/designer/creator/XWAbsoluteLayout.java | 11 +++-------- .../creator/cardlayout/XWCardMainBorderLayout.java | 12 ++++++++++++ .../designer/creator/cardlayout/XWCardTagLayout.java | 1 - 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/designer-form/src/main/java/com/fr/design/designer/beans/adapters/layout/FRCardMainBorderLayoutAdapter.java b/designer-form/src/main/java/com/fr/design/designer/beans/adapters/layout/FRCardMainBorderLayoutAdapter.java index 13910f7d4..906742d29 100644 --- a/designer-form/src/main/java/com/fr/design/designer/beans/adapters/layout/FRCardMainBorderLayoutAdapter.java +++ b/designer-form/src/main/java/com/fr/design/designer/beans/adapters/layout/FRCardMainBorderLayoutAdapter.java @@ -3,6 +3,7 @@ package com.fr.design.designer.beans.adapters.layout; import com.fr.design.designer.beans.models.AddingModel; import com.fr.design.designer.creator.XCreator; import com.fr.design.designer.creator.XLayoutContainer; +import com.fr.design.designer.creator.cardlayout.XWCardMainBorderLayout; import com.fr.design.designer.creator.cardlayout.XWCardTagLayout; import com.fr.design.dialog.FineJOptionPane; import com.fr.design.file.HistoryTemplateListPane; @@ -10,6 +11,7 @@ import com.fr.design.form.layout.FRBorderLayout; import com.fr.design.mainframe.FormDesigner; import com.fr.design.mainframe.JTemplate; import com.fr.design.mainframe.WidgetPropertyPane; +import com.fr.design.utils.gui.LayoutUtils; import com.fr.general.ComparatorUtils; import java.awt.BorderLayout; @@ -41,6 +43,15 @@ public class FRCardMainBorderLayoutAdapter extends FRBorderLayoutAdapter { if (!beyondBounds) { super.fix(creator); } + + //对于Tab布局,内部的某个组件尺寸改变后,需要重新layout下其他关联的组件 + XLayoutContainer xLayoutContainer = creator.getTopLayout(); + if (xLayoutContainer.acceptType(XWCardMainBorderLayout.class)) { + XWCardMainBorderLayout cardMainBorderLayout = (XWCardMainBorderLayout) xLayoutContainer; + LayoutUtils.layoutContainer(cardMainBorderLayout); + cardMainBorderLayout.updateBoundsWidget(); + } + } private boolean calculateBeyondBounds(XCreator creator) { diff --git a/designer-form/src/main/java/com/fr/design/designer/creator/XWAbsoluteLayout.java b/designer-form/src/main/java/com/fr/design/designer/creator/XWAbsoluteLayout.java index 5bec34d37..0067a50f5 100644 --- a/designer-form/src/main/java/com/fr/design/designer/creator/XWAbsoluteLayout.java +++ b/designer-form/src/main/java/com/fr/design/designer/creator/XWAbsoluteLayout.java @@ -7,7 +7,7 @@ import com.fr.base.GraphHelper; import com.fr.design.designer.beans.LayoutAdapter; import com.fr.design.designer.beans.adapters.layout.FRAbsoluteLayoutAdapter; import com.fr.design.designer.beans.location.Direction; -import com.fr.design.designer.creator.cardlayout.XWTabFitLayout; +import com.fr.design.designer.creator.cardlayout.XWCardMainBorderLayout; import com.fr.design.designer.properties.mobile.MobileBooKMarkUsePropertyUI; import com.fr.design.form.layout.FRAbsoluteLayout; import com.fr.design.form.util.FormDesignerUtils; @@ -34,7 +34,6 @@ import java.awt.Rectangle; import java.awt.event.ContainerEvent; import java.awt.event.MouseEvent; import java.beans.IntrospectionException; -import java.util.ArrayList; import java.util.HashMap; /** @@ -119,12 +118,8 @@ public class XWAbsoluteLayout extends XLayoutContainer { ((XWAbsoluteLayout) xCreator).updateBoundsWidget(); } // 如果子组件时tab布局,则tab布局内部的组件的wiget也要更新,否则保存后重新打开大小不对 - ArrayList childrenList = xCreator.getTargetChildrenList(); - if (!childrenList.isEmpty()) { - for (int i = 0; i < childrenList.size(); i++) { - XWTabFitLayout tabLayout = (XWTabFitLayout) childrenList.get(i); - tabLayout.updateBoundsWidget(); - } + if (xCreator.acceptType(XWCardMainBorderLayout.class)){ + ((XWCardMainBorderLayout) xCreator).updateBoundsWidget(); } } diff --git a/designer-form/src/main/java/com/fr/design/designer/creator/cardlayout/XWCardMainBorderLayout.java b/designer-form/src/main/java/com/fr/design/designer/creator/cardlayout/XWCardMainBorderLayout.java index e09b31fb0..081b5072a 100644 --- a/designer-form/src/main/java/com/fr/design/designer/creator/cardlayout/XWCardMainBorderLayout.java +++ b/designer-form/src/main/java/com/fr/design/designer/creator/cardlayout/XWCardMainBorderLayout.java @@ -17,6 +17,7 @@ import com.fr.design.designer.creator.XWidgetCreator; import com.fr.design.mainframe.CoverReportPane; import com.fr.design.mainframe.EditingMouseListener; import com.fr.design.mainframe.FormDesigner; +import com.fr.design.utils.gui.LayoutUtils; import com.fr.form.event.Listener; import com.fr.form.ui.CardSwitchButton; import com.fr.form.ui.LayoutBorderStyle; @@ -425,6 +426,7 @@ public class XWCardMainBorderLayout extends XWBorderLayout { } xwCardTitleLayout.setBounds(parentBounds); this.addCardPart((XWCardLayout)this.getComponent(0)); + LayoutUtils.layoutContainer(this); } @Override @@ -448,4 +450,14 @@ public class XWCardMainBorderLayout extends XWBorderLayout { public void setShowOuterShadowBorder(boolean showOuterShadowBorder) { this.showOuterShadowBorder = showOuterShadowBorder; } + + public void updateBoundsWidget() { + ArrayList childrenList = this.getTargetChildrenList(); + if (!childrenList.isEmpty()) { + for (int i = 0; i < childrenList.size(); i++) { + XWTabFitLayout tabLayout = (XWTabFitLayout) childrenList.get(i); + tabLayout.updateBoundsWidget(); + } + } + } } diff --git a/designer-form/src/main/java/com/fr/design/designer/creator/cardlayout/XWCardTagLayout.java b/designer-form/src/main/java/com/fr/design/designer/creator/cardlayout/XWCardTagLayout.java index ec070ad86..dbffab21a 100644 --- a/designer-form/src/main/java/com/fr/design/designer/creator/cardlayout/XWCardTagLayout.java +++ b/designer-form/src/main/java/com/fr/design/designer/creator/cardlayout/XWCardTagLayout.java @@ -278,7 +278,6 @@ public class XWCardTagLayout extends XWHorizontalBoxLayout { @Override public void doLayout() { - setTabsAndAdjust(); //设置布局 super.doLayout(); } From bb28d112b9ebaa70e474d6f6af235b3e724776b7 Mon Sep 17 00:00:00 2001 From: pengda Date: Fri, 20 May 2022 11:12:36 +0800 Subject: [PATCH 06/85] =?UTF-8?q?REPORT-70955=20=E8=AE=B8=E5=A4=9A?= =?UTF-8?q?=E5=9C=BA=E6=99=AF=E4=B8=8D=E6=94=AF=E6=8C=81=E5=8D=83=E5=88=86?= =?UTF-8?q?=E6=AF=94=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/fr/design/gui/style/FormatPane.java | 17 ++++++++++++++--- .../com/fr/design/gui/style/TextFormatPane.java | 7 +++++-- .../java/com/fr/design/style/FormatPane.java | 15 +++++++++++++++ 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/designer-base/src/main/java/com/fr/design/gui/style/FormatPane.java b/designer-base/src/main/java/com/fr/design/gui/style/FormatPane.java index ba68ab36f..aeac730c9 100644 --- a/designer-base/src/main/java/com/fr/design/gui/style/FormatPane.java +++ b/designer-base/src/main/java/com/fr/design/gui/style/FormatPane.java @@ -24,12 +24,22 @@ import com.fr.general.ComparatorUtils; import com.fr.stable.StringUtils; -import java.math.BigDecimal; +import java.awt.BorderLayout; +import java.awt.CardLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.FontMetrics; +import java.awt.Graphics; import java.math.RoundingMode; -import javax.swing.*; +import javax.swing.BorderFactory; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.SwingConstants; +import javax.swing.UIManager; import javax.swing.border.Border; import javax.swing.border.TitledBorder; -import java.awt.*; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.text.Format; @@ -54,6 +64,7 @@ public class FormatPane extends AbstractBasicStylePane implements GlobalNameObse private static final Integer[] TYPES = new Integer[]{ FormatContents.NULL, FormatContents.NUMBER, FormatContents.CURRENCY, FormatContents.PERCENT, + FormatContents.THOUSANDTHS, FormatContents.SCIENTIFIC, FormatContents.DATE, FormatContents.TIME, FormatContents.TEXT}; diff --git a/designer-base/src/main/java/com/fr/design/gui/style/TextFormatPane.java b/designer-base/src/main/java/com/fr/design/gui/style/TextFormatPane.java index a477cb16c..0b8e8287a 100644 --- a/designer-base/src/main/java/com/fr/design/gui/style/TextFormatPane.java +++ b/designer-base/src/main/java/com/fr/design/gui/style/TextFormatPane.java @@ -58,7 +58,7 @@ public class TextFormatPane extends AbstractBasicStylePane implements GlobalName private static final Integer[] TYPES = new Integer[]{ FormatContents.NULL, FormatContents.NUMBER, FormatContents.CURRENCY, FormatContents.PERCENT, - FormatContents.SCIENTIFIC, + FormatContents.THOUSANDTHS, FormatContents.SCIENTIFIC, FormatContents.DATE, FormatContents.TIME, FormatContents.TEXT}; @@ -228,6 +228,9 @@ public class TextFormatPane extends AbstractBasicStylePane implements GlobalName this.roundingBox.setSelected(((CoreDecimalFormat) format).getRoundingMode().equals(RoundingMode.HALF_UP)); } else if (pattern.indexOf("E") > 0) { setPatternComboBoxAndList(FormatContents.SCIENTIFIC, pattern); + } else if (pattern.indexOf("‰") > 0) { + setPatternComboBoxAndList(FormatContents.THOUSANDTHS, pattern); + this.roundingBox.setSelected(((CoreDecimalFormat) format).getRoundingMode().equals(RoundingMode.HALF_UP)); } else { setPatternComboBoxAndList(FormatContents.NUMBER, pattern); } @@ -345,7 +348,7 @@ public class TextFormatPane extends AbstractBasicStylePane implements GlobalName } setTextFieldVisible(!isTextOrNull()); setPreviewLabelVisible(!isTextOrNull()); - setRoundingBoxVisible(getFormatContents() == FormatContents.PERCENT); + setRoundingBoxVisible(getFormatContents() == FormatContents.PERCENT || getFormatContents() == FormatContents.THOUSANDTHS); } } diff --git a/designer-base/src/main/java/com/fr/design/style/FormatPane.java b/designer-base/src/main/java/com/fr/design/style/FormatPane.java index d1679561d..3f5d8fa0f 100644 --- a/designer-base/src/main/java/com/fr/design/style/FormatPane.java +++ b/designer-base/src/main/java/com/fr/design/style/FormatPane.java @@ -52,6 +52,7 @@ public class FormatPane extends BasicPane { private UIRadioButton numberRadioButton; private UIRadioButton currencyRadioButton; private UIRadioButton percentRadioButton; + private UIRadioButton thousandthsRadioButton; private UIRadioButton scientificRadioButton; private UIRadioButton dateRadioButton; private UIRadioButton timeRadioButton; @@ -99,6 +100,7 @@ public class FormatPane extends BasicPane { categoryButtonGroup.add(numberRadioButton); categoryButtonGroup.add(currencyRadioButton); categoryButtonGroup.add(percentRadioButton); + categoryButtonGroup.add(thousandthsRadioButton); categoryButtonGroup.add(scientificRadioButton); categoryButtonGroup.add(dateRadioButton); categoryButtonGroup.add(timeRadioButton); @@ -108,6 +110,7 @@ public class FormatPane extends BasicPane { leftControlPane.add(this.createRadioCenterPane(numberRadioButton)); leftControlPane.add(this.createRadioCenterPane(currencyRadioButton)); leftControlPane.add(this.createRadioCenterPane(percentRadioButton)); + leftControlPane.add(this.createRadioCenterPane(thousandthsRadioButton)); leftControlPane.add(this.createRadioCenterPane(scientificRadioButton)); leftControlPane.add(this.createRadioCenterPane(dateRadioButton)); leftControlPane.add(this.createRadioCenterPane(timeRadioButton)); @@ -136,6 +139,8 @@ public class FormatPane extends BasicPane { currencyRadioButton.setMnemonic('C'); percentRadioButton = new UIRadioButton(FormatField.getInstance().getName(FormatContents.PERCENT)); percentRadioButton.setMnemonic('P'); + thousandthsRadioButton = new UIRadioButton(FormatField.getInstance().getName(FormatContents.THOUSANDTHS)); + thousandthsRadioButton.setMnemonic('Q'); scientificRadioButton = new UIRadioButton(FormatField.getInstance().getName(FormatContents.SCIENTIFIC)); scientificRadioButton.setMnemonic('S'); dateRadioButton = new UIRadioButton(FormatField.getInstance().getName(FormatContents.DATE)); @@ -149,6 +154,7 @@ public class FormatPane extends BasicPane { numberRadioButton.addActionListener(radioActionListener); currencyRadioButton.addActionListener(radioActionListener); percentRadioButton.addActionListener(radioActionListener); + thousandthsRadioButton.addActionListener(radioActionListener); scientificRadioButton.addActionListener(radioActionListener); dateRadioButton.addActionListener(radioActionListener); timeRadioButton.addActionListener(radioActionListener); @@ -234,6 +240,9 @@ public class FormatPane extends BasicPane { } else if (pattern.endsWith("%")) { this.percentRadioButton.setSelected(true); this.applyRadioActionListener(this.percentRadioButton); + } else if (pattern.equals("‰")){ + this.thousandthsRadioButton.setSelected(true); + this.applyRadioActionListener(this.thousandthsRadioButton); } else if (pattern.indexOf("E") > 0) { this.scientificRadioButton.setSelected(true); this.applyRadioActionListener(this.scientificRadioButton); @@ -259,6 +268,7 @@ public class FormatPane extends BasicPane { scientificRadioButton.setEnabled(false); textRadioButton.setEnabled(false); percentRadioButton.setEnabled(false); + thousandthsRadioButton.setEnabled(false); nullRadioButton.setEnabled(false); dateRadioButton.setEnabled(false); timeRadioButton.setEnabled(false); @@ -285,6 +295,7 @@ public class FormatPane extends BasicPane { numberRadioButton.setEnabled(false); currencyRadioButton.setEnabled(false); percentRadioButton.addActionListener(radioActionListener); + thousandthsRadioButton.setEnabled(false); scientificRadioButton.setEnabled(false); dateRadioButton.setEnabled(false); timeRadioButton.setEnabled(false); @@ -333,6 +344,8 @@ public class FormatPane extends BasicPane { return FormatContents.CURRENCY; else if (percentRadioButton.isSelected()) return FormatContents.PERCENT; + else if (thousandthsRadioButton.isSelected()) + return FormatContents.THOUSANDTHS; else if (scientificRadioButton.isSelected()) return FormatContents.SCIENTIFIC; else if (dateRadioButton.isSelected()) @@ -453,6 +466,8 @@ public class FormatPane extends BasicPane { contents = FormatContents.CURRENCY; } else if (ComparatorUtils.equals(source,percentRadioButton)) { contents = FormatContents.PERCENT; + }else if (ComparatorUtils.equals(source,thousandthsRadioButton)){ + contents = FormatContents.THOUSANDTHS; } else if (ComparatorUtils.equals(source,scientificRadioButton)) { contents = FormatContents.SCIENTIFIC; } else if (ComparatorUtils.equals(source,dateRadioButton)) { From 641290016ab2fbb27de6fbdbe72e99934c76f48e Mon Sep 17 00:00:00 2001 From: zack Date: Tue, 24 May 2022 16:40:36 +0800 Subject: [PATCH 07/85] =?UTF-8?q?INO-12903=20=E5=A4=96=E7=BD=91=E6=9D=83?= =?UTF-8?q?=E9=99=90=E7=BB=86=E7=B2=92=E5=BA=A6=E6=8E=A7=E5=88=B6-?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=8D=87=E7=BA=A7+=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E7=AE=A1=E7=90=86+=E5=9B=BE=E8=A1=A8=E8=B5=84=E6=BA=90=20?= =?UTF-8?q?=E6=8E=A7=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../extra/exe/ReadUpdateOnlineExecutor.java | 11 ++++++++ .../update/ui/dialog/UpdateMainDialog.java | 13 ++++++++- .../chart/DownloadOnlineSourcesHelper.java | 28 +++++++++++++++++-- 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/designer-base/src/main/java/com/fr/design/extra/exe/ReadUpdateOnlineExecutor.java b/designer-base/src/main/java/com/fr/design/extra/exe/ReadUpdateOnlineExecutor.java index 739b84666..f9e790103 100644 --- a/designer-base/src/main/java/com/fr/design/extra/exe/ReadUpdateOnlineExecutor.java +++ b/designer-base/src/main/java/com/fr/design/extra/exe/ReadUpdateOnlineExecutor.java @@ -1,15 +1,21 @@ package com.fr.design.extra.exe; +import com.fr.design.dialog.FineJOptionPane; import com.fr.design.extra.PluginsReaderFromStore; import com.fr.design.extra.Process; +import com.fr.design.i18n.Toolkit; +import com.fr.general.SiteBlockedException; import com.fr.json.JSONArray; import com.fr.json.JSONObject; import com.fr.log.FineLoggerFactory; import com.fr.plugin.view.PluginView; import com.fr.stable.StringUtils; +import javax.swing.JOptionPane; import java.util.List; +import static com.fr.design.dialog.FineJOptionPane.OPTION_OK_CANCEL; + /** * Created by vito on 16/4/19. */ @@ -41,6 +47,11 @@ public class ReadUpdateOnlineExecutor implements Executor { jsonArray.put(jsonObject); } result = jsonArray.toString(); + } catch (SiteBlockedException e1) { + FineJOptionPane.showConfirmDialog(null, + e1.getMessage(), + Toolkit.i18nText("Fine-Design_Basic_Alert"), JOptionPane.DEFAULT_OPTION, + FineJOptionPane.WARNING_MESSAGE, null, OPTION_OK_CANCEL, null); } catch (Exception e) { FineLoggerFactory.getLogger().error(e.getMessage(), e); } diff --git a/designer-base/src/main/java/com/fr/design/update/ui/dialog/UpdateMainDialog.java b/designer-base/src/main/java/com/fr/design/update/ui/dialog/UpdateMainDialog.java index 9a849ad4e..b770290d3 100644 --- a/designer-base/src/main/java/com/fr/design/update/ui/dialog/UpdateMainDialog.java +++ b/designer-base/src/main/java/com/fr/design/update/ui/dialog/UpdateMainDialog.java @@ -56,6 +56,7 @@ import java.util.*; import java.util.List; import java.util.concurrent.ExecutionException; +import static com.fr.design.dialog.FineJOptionPane.OPTION_OK_CANCEL; import static java.nio.charset.StandardCharsets.*; import static javax.swing.JOptionPane.QUESTION_MESSAGE; @@ -350,7 +351,17 @@ public class UpdateMainDialog extends UIDialog { new SwingWorker() { @Override protected JSONObject doInBackground() throws Exception { - return new JSONObject(HttpToolbox.get(CloudCenter.getInstance().acquireUrlByKind("jar11.update"))); + try { + String url = CloudCenter.getInstance().acquireUrlByKind("jar11.update"); + return new JSONObject(HttpToolbox.get(url)); + } catch (SiteBlockedException e) { + stopLoading(); + FineJOptionPane.showConfirmDialog(null, + e.getMessage(), + Toolkit.i18nText("Fine-Design_Basic_Alert"), JOptionPane.DEFAULT_OPTION, + FineJOptionPane.WARNING_MESSAGE, null, OPTION_OK_CANCEL, null); + } + return null; } @Override diff --git a/designer-chart/src/main/java/com/fr/van/chart/DownloadOnlineSourcesHelper.java b/designer-chart/src/main/java/com/fr/van/chart/DownloadOnlineSourcesHelper.java index 33dba72f9..7fc8e7e5a 100644 --- a/designer-chart/src/main/java/com/fr/van/chart/DownloadOnlineSourcesHelper.java +++ b/designer-chart/src/main/java/com/fr/van/chart/DownloadOnlineSourcesHelper.java @@ -5,9 +5,11 @@ import com.fr.design.RestartHelper; import com.fr.design.dialog.FineJOptionPane; import com.fr.design.extra.PluginConstants; import com.fr.design.gui.ilable.UILabel; +import com.fr.design.i18n.Toolkit; import com.fr.design.utils.gui.GUICoreUtils; import com.fr.general.CloudCenter; import com.fr.general.IOUtils; +import com.fr.general.SiteBlockedException; import com.fr.general.http.HttpClient; import com.fr.plugin.chart.DownloadSourcesEvent; import com.fr.stable.CommonUtils; @@ -33,6 +35,8 @@ import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.image.BufferedImage; +import static com.fr.design.dialog.FineJOptionPane.OPTION_OK_CANCEL; + /** * Created by shine on 2017/8/21. */ @@ -69,7 +73,15 @@ public class DownloadOnlineSourcesHelper implements DownloadSourcesEvent { //本地有这个资源,不下载 return; } - httpClient = new HttpClient(CloudCenter.getInstance().acquireUrlByKind(siteKind)); + try { + httpClient = new HttpClient(CloudCenter.getInstance().acquireUrlByKind(siteKind)); + } catch (SiteBlockedException e) { + FineJOptionPane.showConfirmDialog(null, + e.getMessage(), + Toolkit.i18nText("Fine-Design_Basic_Alert"), JOptionPane.DEFAULT_OPTION, + FineJOptionPane.WARNING_MESSAGE, null, OPTION_OK_CANCEL, null); + return; + } if (httpClient.getResponseCode() != HttpURLConnection.HTTP_OK) { //服务器连不上,不下载 return; @@ -116,12 +128,22 @@ public class DownloadOnlineSourcesHelper implements DownloadSourcesEvent { private void downloadAndInstallPluginDependenceFile() { try { double currentBytesRead = 0; + result = false; for (int i = 0; i < list.size(); i++) { SiteInfo siteInfo = list.get(i); - - httpClient = new HttpClient(CloudCenter.getInstance().acquireUrlByKind(siteInfo.siteKind)); + try { + httpClient = new HttpClient(CloudCenter.getInstance().acquireUrlByKind(siteInfo.siteKind)); + } catch (SiteBlockedException e) { + FineJOptionPane.showConfirmDialog(null, + e.getMessage(), + Toolkit.i18nText("Fine-Design_Basic_Alert"), JOptionPane.DEFAULT_OPTION, + FineJOptionPane.WARNING_MESSAGE, null, OPTION_OK_CANCEL, null); + result = false; + return; + } if (httpClient.getResponseCode() == HttpURLConnection.HTTP_OK) { + result = true; String temp = StableUtils.pathJoin(PluginConstants.DOWNLOAD_PATH, PluginConstants.TEMP_FILE); File file = new File(temp); StableUtils.makesureFileExist(file); From af618b896c7f516b217cfc326f5663f529423e9e Mon Sep 17 00:00:00 2001 From: pengda Date: Wed, 25 May 2022 11:14:43 +0800 Subject: [PATCH 08/85] =?UTF-8?q?REPORT-72233=20=20=E5=9B=BE=E8=A1=A8?= =?UTF-8?q?=E6=A0=B7=E5=BC=8F=E9=85=8D=E7=BD=AE=E5=8D=83=E5=88=86=E6=AF=94?= =?UTF-8?q?=E6=A0=B7=E5=BC=8F=E5=90=8E=E6=9F=A5=E7=9C=8B=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8F=98=E6=88=90=E4=BA=86=E6=95=B0=E5=AD=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/fr/design/gui/style/FormatPane.java | 3 +++ .../src/main/java/com/fr/design/style/FormatPane.java | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/designer-base/src/main/java/com/fr/design/gui/style/FormatPane.java b/designer-base/src/main/java/com/fr/design/gui/style/FormatPane.java index aeac730c9..2485b4de4 100644 --- a/designer-base/src/main/java/com/fr/design/gui/style/FormatPane.java +++ b/designer-base/src/main/java/com/fr/design/gui/style/FormatPane.java @@ -255,6 +255,9 @@ public class FormatPane extends AbstractBasicStylePane implements GlobalNameObse } else if (pattern.indexOf("%") > 0) { setPatternComboBoxAndList(FormatContents.PERCENT, pattern); this.roundingBox.setSelected(((CoreDecimalFormat) format).getRoundingMode().equals(RoundingMode.HALF_UP)); + } else if (pattern.indexOf("‰") > 0) { + setPatternComboBoxAndList(FormatContents.THOUSANDTHS, pattern); + this.roundingBox.setSelected(((CoreDecimalFormat) format).getRoundingMode().equals(RoundingMode.HALF_UP)); } else if (pattern.indexOf("E") > 0) { setPatternComboBoxAndList(FormatContents.SCIENTIFIC, pattern); } else { diff --git a/designer-base/src/main/java/com/fr/design/style/FormatPane.java b/designer-base/src/main/java/com/fr/design/style/FormatPane.java index 3f5d8fa0f..f1d7a086b 100644 --- a/designer-base/src/main/java/com/fr/design/style/FormatPane.java +++ b/designer-base/src/main/java/com/fr/design/style/FormatPane.java @@ -240,7 +240,7 @@ public class FormatPane extends BasicPane { } else if (pattern.endsWith("%")) { this.percentRadioButton.setSelected(true); this.applyRadioActionListener(this.percentRadioButton); - } else if (pattern.equals("‰")){ + } else if (pattern.endsWith("‰")){ this.thousandthsRadioButton.setSelected(true); this.applyRadioActionListener(this.thousandthsRadioButton); } else if (pattern.indexOf("E") > 0) { From 23af43232b08e31cc03042e8b019f4ca8b1b726b Mon Sep 17 00:00:00 2001 From: zack Date: Wed, 25 May 2022 11:39:45 +0800 Subject: [PATCH 09/85] =?UTF-8?q?INO-12903=20=E5=A4=96=E7=BD=91=E6=9D=83?= =?UTF-8?q?=E9=99=90=E7=BB=86=E7=B2=92=E5=BA=A6=EF=BC=8C=E7=82=B9=E5=87=BB?= =?UTF-8?q?=E7=A1=AE=E5=AE=9A=E8=87=AA=E5=8A=A8=E8=B7=B3=E8=BD=AC=E5=B9=B3?= =?UTF-8?q?=E5=8F=B0=E8=AE=BE=E7=BD=AE=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/fr/design/dialog/FineJOptionPane.java | 24 +++++++++++++++++++ .../extra/exe/ReadUpdateOnlineExecutor.java | 17 ++++++++++--- .../update/ui/dialog/UpdateMainDialog.java | 15 +++++++++--- .../chart/DownloadOnlineSourcesHelper.java | 17 ++++++++++--- 4 files changed, 64 insertions(+), 9 deletions(-) diff --git a/designer-base/src/main/java/com/fr/design/dialog/FineJOptionPane.java b/designer-base/src/main/java/com/fr/design/dialog/FineJOptionPane.java index c64455079..4e0b4e520 100644 --- a/designer-base/src/main/java/com/fr/design/dialog/FineJOptionPane.java +++ b/designer-base/src/main/java/com/fr/design/dialog/FineJOptionPane.java @@ -8,6 +8,7 @@ import javax.swing.JDialog; import javax.swing.JOptionPane; import java.awt.Component; import java.awt.HeadlessException; +import java.awt.event.ActionListener; import java.util.HashMap; import java.util.Map; @@ -184,6 +185,29 @@ public class FineJOptionPane extends JOptionPane { messageType, icon, options, initialValue); } + /** + * 带确认+取消按钮的弹出框,并绑定确认事件 + * + * @param parentComponent 父容器 + * @param message 具体的提示消息 + * @param title 标题 + * @param icon 图标 + * @param initialValue 初始选项 + * @throws HeadlessException + */ + public static int showConfirmDialogWithOkListener(Component parentComponent, Object message, + String title, Icon icon, Object initialValue, ActionListener listener) + throws HeadlessException { + int val = showOptionDialog(parentComponent, message, title, JOptionPane.DEFAULT_OPTION, + FineJOptionPane.WARNING_MESSAGE, icon, OPTION_OK_CANCEL, initialValue); + if (val == JOptionPane.OK_OPTION && listener!=null) { + listener.actionPerformed(null); + } + return val; + } + + + /** * 指定消息内容的输入弹出框 * @param message 消息内容 diff --git a/designer-base/src/main/java/com/fr/design/extra/exe/ReadUpdateOnlineExecutor.java b/designer-base/src/main/java/com/fr/design/extra/exe/ReadUpdateOnlineExecutor.java index f9e790103..4656d8d39 100644 --- a/designer-base/src/main/java/com/fr/design/extra/exe/ReadUpdateOnlineExecutor.java +++ b/designer-base/src/main/java/com/fr/design/extra/exe/ReadUpdateOnlineExecutor.java @@ -4,6 +4,7 @@ import com.fr.design.dialog.FineJOptionPane; import com.fr.design.extra.PluginsReaderFromStore; import com.fr.design.extra.Process; import com.fr.design.i18n.Toolkit; +import com.fr.design.utils.DesignUtils; import com.fr.general.SiteBlockedException; import com.fr.json.JSONArray; import com.fr.json.JSONObject; @@ -12,6 +13,8 @@ import com.fr.plugin.view.PluginView; import com.fr.stable.StringUtils; import javax.swing.JOptionPane; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.util.List; import static com.fr.design.dialog.FineJOptionPane.OPTION_OK_CANCEL; @@ -48,10 +51,18 @@ public class ReadUpdateOnlineExecutor implements Executor { } result = jsonArray.toString(); } catch (SiteBlockedException e1) { - FineJOptionPane.showConfirmDialog(null, + FineJOptionPane.showConfirmDialogWithOkListener(null, e1.getMessage(), - Toolkit.i18nText("Fine-Design_Basic_Alert"), JOptionPane.DEFAULT_OPTION, - FineJOptionPane.WARNING_MESSAGE, null, OPTION_OK_CANCEL, null); + Toolkit.i18nText("Fine-Design_Basic_Alert"), null, null, + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + DesignUtils.visitEnvServerByParameters( + "#management/system/normal", + null, + null); + } + }); } catch (Exception e) { FineLoggerFactory.getLogger().error(e.getMessage(), e); } diff --git a/designer-base/src/main/java/com/fr/design/update/ui/dialog/UpdateMainDialog.java b/designer-base/src/main/java/com/fr/design/update/ui/dialog/UpdateMainDialog.java index b770290d3..deda005c3 100644 --- a/designer-base/src/main/java/com/fr/design/update/ui/dialog/UpdateMainDialog.java +++ b/designer-base/src/main/java/com/fr/design/update/ui/dialog/UpdateMainDialog.java @@ -24,6 +24,7 @@ import com.fr.design.update.ui.widget.UpdateInfoTable; import com.fr.design.update.ui.widget.UpdateInfoTableCellRender; import com.fr.design.update.ui.widget.UpdateInfoTableModel; import com.fr.design.update.ui.widget.UpdateInfoTextAreaCellRender; +import com.fr.design.utils.DesignUtils; import com.fr.design.utils.gui.GUICoreUtils; import com.fr.general.*; import com.fr.general.http.HttpToolbox; @@ -356,10 +357,18 @@ public class UpdateMainDialog extends UIDialog { return new JSONObject(HttpToolbox.get(url)); } catch (SiteBlockedException e) { stopLoading(); - FineJOptionPane.showConfirmDialog(null, + FineJOptionPane.showConfirmDialogWithOkListener(null, e.getMessage(), - Toolkit.i18nText("Fine-Design_Basic_Alert"), JOptionPane.DEFAULT_OPTION, - FineJOptionPane.WARNING_MESSAGE, null, OPTION_OK_CANCEL, null); + Toolkit.i18nText("Fine-Design_Basic_Alert"), null, null, + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + DesignUtils.visitEnvServerByParameters( + "#management/system/normal", + null, + null); + } + }); } return null; } diff --git a/designer-chart/src/main/java/com/fr/van/chart/DownloadOnlineSourcesHelper.java b/designer-chart/src/main/java/com/fr/van/chart/DownloadOnlineSourcesHelper.java index 7fc8e7e5a..a2e43df3e 100644 --- a/designer-chart/src/main/java/com/fr/van/chart/DownloadOnlineSourcesHelper.java +++ b/designer-chart/src/main/java/com/fr/van/chart/DownloadOnlineSourcesHelper.java @@ -6,6 +6,7 @@ import com.fr.design.dialog.FineJOptionPane; import com.fr.design.extra.PluginConstants; import com.fr.design.gui.ilable.UILabel; import com.fr.design.i18n.Toolkit; +import com.fr.design.utils.DesignUtils; import com.fr.design.utils.gui.GUICoreUtils; import com.fr.general.CloudCenter; import com.fr.general.IOUtils; @@ -22,6 +23,8 @@ import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JProgressBar; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; @@ -76,10 +79,18 @@ public class DownloadOnlineSourcesHelper implements DownloadSourcesEvent { try { httpClient = new HttpClient(CloudCenter.getInstance().acquireUrlByKind(siteKind)); } catch (SiteBlockedException e) { - FineJOptionPane.showConfirmDialog(null, + FineJOptionPane.showConfirmDialogWithOkListener(null, e.getMessage(), - Toolkit.i18nText("Fine-Design_Basic_Alert"), JOptionPane.DEFAULT_OPTION, - FineJOptionPane.WARNING_MESSAGE, null, OPTION_OK_CANCEL, null); + Toolkit.i18nText("Fine-Design_Basic_Alert"), null, null, + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + DesignUtils.visitEnvServerByParameters( + "#management/system/normal", + null, + null); + } + }); return; } if (httpClient.getResponseCode() != HttpURLConnection.HTTP_OK) { From f88195d402c2ce068d6d8a0ae7c2e59ab599244a Mon Sep 17 00:00:00 2001 From: Harrison Date: Wed, 25 May 2022 15:46:50 +0800 Subject: [PATCH 10/85] =?UTF-8?q?feat:=20REPORT-70565=20=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E7=8E=AF=E5=A2=83=E7=9B=91=E6=B5=8B=EF=BC=88jar?= =?UTF-8?q?=E5=8C=85=E5=BC=82=E5=B8=B8=E3=80=81finedb=E3=80=81=E6=9D=80?= =?UTF-8?q?=E6=AF=92=E8=BD=AF=E4=BB=B6=EF=BC=89=20=E6=8F=90=E4=BA=A4?= =?UTF-8?q?=E9=83=A8=E5=88=86=E6=A3=80=E6=B5=8B=E7=9A=84=E6=A0=B8=E5=BF=83?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E5=92=8C=E9=83=A8=E5=88=86=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E3=80=82=20=E6=97=A0=20UI=20=E9=83=A8=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../base/AbstractExceptionDetector.java | 19 ++++ .../detect/base/CatchExceptionDetector.java | 44 ++++++++ .../fr/env/detect/base/DetectorBridge.java | 95 ++++++++++++++++ .../fr/env/detect/base/DetectorManager.java | 40 +++++++ .../fr/env/detect/base/ExceptionDetector.java | 25 +++++ .../detect/base/ExceptionDetectorConfig.java | 21 ++++ .../fr/env/detect/base/ThrowableBridge.java | 40 +++++++ .../com/fr/env/detect/base/package-info.java | 4 + .../fr/env/detect/bean/DetectorResult.java | 102 ++++++++++++++++++ .../fr/env/detect/bean/DetectorStatus.java | 22 ++++ .../com/fr/env/detect/bean/DetectorType.java | 88 +++++++++++++++ .../com/fr/env/detect/bean/ExceptionLog.java | 24 +++++ .../fr/env/detect/bean/ExceptionSolution.java | 24 +++++ .../com/fr/env/detect/bean/ExceptionTips.java | 19 ++++ .../java/com/fr/env/detect/bean/Message.java | 70 ++++++++++++ .../fr/env/detect/bean/SolutionAction.java | 11 ++ .../detect/exception/DetectorException.java | 26 +++++ .../fr/env/detect/impl/HsqlDirtyDetector.java | 18 ++++ .../detect/impl/JarInconsistentDetector.java | 85 +++++++++++++++ .../fr/env/detect/impl/JarLackDetector.java | 43 ++++++++ .../converter/ClassThrowableConvertor.java | 53 +++++++++ .../impl/converter/FineDbDirtyConverter.java | 33 ++++++ .../impl/converter/FineDbLockedConverter.java | 31 ++++++ .../converter/FineDbPermissionConverter.java | 33 ++++++ .../detect/thowable/ThrowableConverter.java | 28 +++++ .../detect/thowable/ThrowableLogAppender.java | 58 ++++++++++ .../env/detect/thowable/ThrowableStore.java | 36 +++++++ 27 files changed, 1092 insertions(+) create mode 100644 designer-base/src/main/java/com/fr/env/detect/base/AbstractExceptionDetector.java create mode 100644 designer-base/src/main/java/com/fr/env/detect/base/CatchExceptionDetector.java create mode 100644 designer-base/src/main/java/com/fr/env/detect/base/DetectorBridge.java create mode 100644 designer-base/src/main/java/com/fr/env/detect/base/DetectorManager.java create mode 100644 designer-base/src/main/java/com/fr/env/detect/base/ExceptionDetector.java create mode 100644 designer-base/src/main/java/com/fr/env/detect/base/ExceptionDetectorConfig.java create mode 100644 designer-base/src/main/java/com/fr/env/detect/base/ThrowableBridge.java create mode 100644 designer-base/src/main/java/com/fr/env/detect/base/package-info.java create mode 100644 designer-base/src/main/java/com/fr/env/detect/bean/DetectorResult.java create mode 100644 designer-base/src/main/java/com/fr/env/detect/bean/DetectorStatus.java create mode 100644 designer-base/src/main/java/com/fr/env/detect/bean/DetectorType.java create mode 100644 designer-base/src/main/java/com/fr/env/detect/bean/ExceptionLog.java create mode 100644 designer-base/src/main/java/com/fr/env/detect/bean/ExceptionSolution.java create mode 100644 designer-base/src/main/java/com/fr/env/detect/bean/ExceptionTips.java create mode 100644 designer-base/src/main/java/com/fr/env/detect/bean/Message.java create mode 100644 designer-base/src/main/java/com/fr/env/detect/bean/SolutionAction.java create mode 100644 designer-base/src/main/java/com/fr/env/detect/exception/DetectorException.java create mode 100644 designer-base/src/main/java/com/fr/env/detect/impl/HsqlDirtyDetector.java create mode 100644 designer-base/src/main/java/com/fr/env/detect/impl/JarInconsistentDetector.java create mode 100644 designer-base/src/main/java/com/fr/env/detect/impl/JarLackDetector.java create mode 100644 designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassThrowableConvertor.java create mode 100644 designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbDirtyConverter.java create mode 100644 designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbLockedConverter.java create mode 100644 designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbPermissionConverter.java create mode 100644 designer-base/src/main/java/com/fr/env/detect/thowable/ThrowableConverter.java create mode 100644 designer-base/src/main/java/com/fr/env/detect/thowable/ThrowableLogAppender.java create mode 100644 designer-base/src/main/java/com/fr/env/detect/thowable/ThrowableStore.java diff --git a/designer-base/src/main/java/com/fr/env/detect/base/AbstractExceptionDetector.java b/designer-base/src/main/java/com/fr/env/detect/base/AbstractExceptionDetector.java new file mode 100644 index 000000000..32d54a1d7 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/base/AbstractExceptionDetector.java @@ -0,0 +1,19 @@ +package com.fr.env.detect.base; + +import com.fr.env.detect.bean.DetectorType; + +/** + * created by Harrison on 2022/05/13 + **/ +public abstract class AbstractExceptionDetector implements ExceptionDetector { + + private DetectorType key; + + public AbstractExceptionDetector(DetectorType key) { + this.key = key; + } + + public DetectorType type() { + return key; + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/base/CatchExceptionDetector.java b/designer-base/src/main/java/com/fr/env/detect/base/CatchExceptionDetector.java new file mode 100644 index 000000000..b2e60d42c --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/base/CatchExceptionDetector.java @@ -0,0 +1,44 @@ +package com.fr.env.detect.base; + +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.DetectorType; +import com.fr.env.detect.thowable.ThrowableConverter; +import com.fr.env.detect.thowable.ThrowableStore; + +import java.util.List; + +/** + * created by Harrison on 2022/05/13 + **/ +public abstract class CatchExceptionDetector extends AbstractExceptionDetector { + + private ThrowableStore throwableStore; + + private ThrowableConverter throwableConverter; + + public CatchExceptionDetector(DetectorType key, ThrowableStore throwableStore, ThrowableConverter throwableConverter) { + super(key); + this.throwableStore = throwableStore; + this.throwableConverter = throwableConverter; + } + + public ThrowableStore getThrowableStore() { + return throwableStore; + } + + public ThrowableConverter getThrowableConverter() { + return throwableConverter; + } + + @Override + public DetectorResult detect() { + + List throwableList = throwableStore.getAll(); + for (Throwable throwable : throwableList) { + if (throwableConverter.accept(throwable)) { + return throwableConverter.convert(throwable); + } + } + return null; + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/base/DetectorBridge.java b/designer-base/src/main/java/com/fr/env/detect/base/DetectorBridge.java new file mode 100644 index 000000000..78b501784 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/base/DetectorBridge.java @@ -0,0 +1,95 @@ +package com.fr.env.detect.base; + +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.DetectorType; +import com.fr.env.detect.thowable.ThrowableLogAppender; +import com.fr.value.NotNullLazyValue; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Stream; + +/** + * created by Harrison on 2022/05/13 + **/ +public class DetectorBridge { + + public static DetectorBridge getInstance() { + + return DetectorBridgeHolder.INSTANCE; + } + + private static class DetectorBridgeHolder { + + private static final DetectorBridge INSTANCE = new DetectorBridge(); + } + + private final NotNullLazyValue throwableBridge = new NotNullLazyValue() { + @Override + protected @NotNull ThrowableBridge compute() { + + return new ThrowableBridge(); + } + }; + + private final NotNullLazyValue detectorManager = new NotNullLazyValue() { + + @Override + protected @NotNull DetectorManager compute() { + + return new DetectorManager(); + } + }; + + private final AtomicBoolean hasStarted = new AtomicBoolean(false); + + public void start() { + + if (ExceptionDetectorConfig.getInstance().isOpen()) { + // 开始注册异常处理 + ThrowableLogAppender.getInstance().enable(); + hasStarted.set(true); + } + } + + public void stop() { + + if (hasStarted.compareAndSet(true, false)) { + // 关闭异常处理 + ThrowableLogAppender.getInstance().disable(); + } + } + + /** + * 执行-检测异常 + * + * @return 能够检测出的异常情况 + */ + public Stream detect() { + + return detectorManager.getValue().detect(); + } + + /** + * 针对某一项进行检测 + * + * @param type 检测类型 + * @return 检测结果 + */ + public Optional detect(DetectorType type) { + + return detectorManager.getValue().detect(type); + } + + /** + * 执行-捕获异常 + * + * @param throwable 异常 + * @return 检测结果 + */ + public Optional convert(Throwable throwable) { + + return throwableBridge.getValue().convert(throwable); + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/base/DetectorManager.java b/designer-base/src/main/java/com/fr/env/detect/base/DetectorManager.java new file mode 100644 index 000000000..9d3ae2daa --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/base/DetectorManager.java @@ -0,0 +1,40 @@ +package com.fr.env.detect.base; + +import com.fr.env.detect.bean.DetectorType; +import com.fr.env.detect.bean.DetectorResult; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Stream; + +/** + * created by Harrison on 2022/05/24 + **/ +class DetectorManager { + + private List detectors = new ArrayList<>(); + + public void register(ExceptionDetector detector) { + + if (detector != null) { + detectors.add(detector); + } + } + + public Stream detect() { + + return detectors.stream() + .map(ExceptionDetector::detect) + .filter(Objects::nonNull); + } + + public Optional detect(DetectorType type) { + + return detectors.stream() + .filter((detector -> type == detector.type())) + .findFirst() + .map(ExceptionDetector::detect); + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/base/ExceptionDetector.java b/designer-base/src/main/java/com/fr/env/detect/base/ExceptionDetector.java new file mode 100644 index 000000000..3d6d8bc4e --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/base/ExceptionDetector.java @@ -0,0 +1,25 @@ +package com.fr.env.detect.base; + +import com.fr.env.detect.bean.DetectorType; +import com.fr.env.detect.bean.DetectorResult; + +/** + * created by Harrison on 2022/05/13 + **/ +public interface ExceptionDetector { + + /** + * 检测类型 + * + * @return TYPE + */ + DetectorType type(); + + /** + * 检测结果 + * + * @return 结果 + */ + DetectorResult detect(); + +} diff --git a/designer-base/src/main/java/com/fr/env/detect/base/ExceptionDetectorConfig.java b/designer-base/src/main/java/com/fr/env/detect/base/ExceptionDetectorConfig.java new file mode 100644 index 000000000..489d5e311 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/base/ExceptionDetectorConfig.java @@ -0,0 +1,21 @@ +package com.fr.env.detect.base; + +/** + * created by Harrison on 2022/05/13 + **/ +public class ExceptionDetectorConfig { + + + public static ExceptionDetectorConfig getInstance() { + return ExceptionDetectorConfigHolder.INSTANCE; + } + + private static class ExceptionDetectorConfigHolder { + private static final ExceptionDetectorConfig INSTANCE = new ExceptionDetectorConfig(); + } + + public boolean isOpen() { + + return true; + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/base/ThrowableBridge.java b/designer-base/src/main/java/com/fr/env/detect/base/ThrowableBridge.java new file mode 100644 index 000000000..c8ef7a02c --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/base/ThrowableBridge.java @@ -0,0 +1,40 @@ +package com.fr.env.detect.base; + +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.thowable.ThrowableConverter; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +/** + * created by Harrison on 2022/05/13 + **/ +class ThrowableBridge { + + private List throwableConverters = new ArrayList<>(); + + public void register(ThrowableConverter throwableConverter) { + + if (throwableConverter != null) { + throwableConverters.add(throwableConverter); + } + } + + /** + * 将 throwable 转化成检测的结果 + * + * @param throwable 异常 + * @return 结果 + */ + public Optional convert(Throwable throwable) { + + return throwableConverters + .stream() + .filter((e) -> e.accept(throwable)) + .map((e) -> e.convert(throwable)) + .filter(Objects::nonNull) + .findFirst(); + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/base/package-info.java b/designer-base/src/main/java/com/fr/env/detect/base/package-info.java new file mode 100644 index 000000000..12fef30e1 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/base/package-info.java @@ -0,0 +1,4 @@ +/** + * 设计器环境检测 见 {@see https://kms.fineres.com/pages/viewpage.action?pageId=388333688} + */ +package com.fr.env.detect.base; diff --git a/designer-base/src/main/java/com/fr/env/detect/bean/DetectorResult.java b/designer-base/src/main/java/com/fr/env/detect/bean/DetectorResult.java new file mode 100644 index 000000000..09850055d --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/bean/DetectorResult.java @@ -0,0 +1,102 @@ +package com.fr.env.detect.bean; + +import org.jetbrains.annotations.Nullable; + +/** + * 检测结果 + * + * created by Harrison on 2022/05/13 + **/ +public class DetectorResult { + + private DetectorType type; + + private DetectorStatus status; + + private ExceptionTips tips; + + private ExceptionSolution solution; + + private ExceptionLog exceptionLog; + + public DetectorResult(DetectorType type, ExceptionTips tips, ExceptionSolution solution, ExceptionLog exceptionLog) { + this.type = type; + this.tips = tips; + this.solution = solution; + this.exceptionLog = exceptionLog; + } + + public DetectorType getType() { + return type; + } + + public DetectorStatus getStatus() { + return status; + } + + @Nullable + public ExceptionTips getTips() { + return tips; + } + + @Nullable + public ExceptionSolution getSolution() { + return solution; + } + + @Nullable + public ExceptionLog getExceptionLog() { + return exceptionLog; + } + + public static DetectorResultBuilder builder() { + + return new DetectorResultBuilder(); + } + + + public static final class DetectorResultBuilder { + + private DetectorType type; + private DetectorStatus status; + + private ExceptionTips tips; + private ExceptionSolution solution; + private ExceptionLog exceptionLog; + + private DetectorResultBuilder() { + } + + public DetectorResultBuilder withType(DetectorType type) { + this.type = type; + return this; + } + + public DetectorResultBuilder withStatus(DetectorStatus status) { + this.status = status; + return this; + } + + public DetectorResultBuilder withTips(ExceptionTips tips) { + this.tips = tips; + return this; + } + + public DetectorResultBuilder withSolution(ExceptionSolution solution) { + this.solution = solution; + return this; + } + + public DetectorResultBuilder withExceptionLog(ExceptionLog exceptionLog) { + this.exceptionLog = exceptionLog; + return this; + } + + public DetectorResult build() { + + DetectorResult detectorResult = new DetectorResult(type, tips, solution, exceptionLog); + detectorResult.status = this.status; + return detectorResult; + } + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/bean/DetectorStatus.java b/designer-base/src/main/java/com/fr/env/detect/bean/DetectorStatus.java new file mode 100644 index 000000000..d75a82e56 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/bean/DetectorStatus.java @@ -0,0 +1,22 @@ +package com.fr.env.detect.bean; + +/** + * created by Harrison on 2022/05/25 + **/ +public enum DetectorStatus { + + /** + * 成功 + */ + SUCCESS, + + /** + * 失败 + */ + FAILED, + + /** + * 异常 + */ + UNKNOWN +} diff --git a/designer-base/src/main/java/com/fr/env/detect/bean/DetectorType.java b/designer-base/src/main/java/com/fr/env/detect/bean/DetectorType.java new file mode 100644 index 000000000..2bc71e58a --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/bean/DetectorType.java @@ -0,0 +1,88 @@ +package com.fr.env.detect.bean; + +/** + * 检测的原生数据 + * + * created by Harrison on 2022/05/13 + **/ +public enum DetectorType { + + /** + * 缺少 JAR + */ + LACK_OF_JAR(Kind.JAR, WorkType.LOCAL), + + /** + * JAR 包版本不一致 + */ + JAR_IN_CONSISTENCE(Kind.JAR, WorkType.SIMPLE), + + /** + * JAR 包冲突 + */ + JAR_CONFLICT(Kind.JAR, WorkType.REMOTE), + + + /** + * FineDB 权限问题 + */ + FINE_DB_PERMISSION(Kind.FINE_DB, WorkType.LOCAL), + + /** + * FineDB 锁 + */ + FINE_DB_LOCKED(Kind.FINE_DB, WorkType.LOCAL), + + /** + * FineDB 脏数据 + */ + FINE_DB_DIRTY(Kind.FINE_DB, WorkType.SIMPLE) + ; + + private final Kind kind; + + private final WorkType workType; + + DetectorType(Kind kind, WorkType workType) { + + this.kind = kind; + this.workType = workType; + } + + public Kind getKind() { + return kind; + } + + public WorkType getWorkType() { + return workType; + } + + public enum Kind { + + /** + * JAR 类型 + */ + JAR, + /** + * FineDB 类型 + */ + FINE_DB + } + + public enum WorkType { + + /** + * 本地 + */ + LOCAL, + /** + * 远程 + */ + REMOTE, + /** + * 全部 + */ + SIMPLE + } + +} diff --git a/designer-base/src/main/java/com/fr/env/detect/bean/ExceptionLog.java b/designer-base/src/main/java/com/fr/env/detect/bean/ExceptionLog.java new file mode 100644 index 000000000..90cdb58fd --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/bean/ExceptionLog.java @@ -0,0 +1,24 @@ +package com.fr.env.detect.bean; + +/** + * created by Harrison on 2022/05/13 + **/ +public class ExceptionLog { + + private final String errorCode; + + private final String locale; + + public ExceptionLog(String errorCode, String locale) { + this.errorCode = errorCode; + this.locale = locale; + } + + public String getErrorCode() { + return errorCode; + } + + public String getLocale() { + return locale; + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/bean/ExceptionSolution.java b/designer-base/src/main/java/com/fr/env/detect/bean/ExceptionSolution.java new file mode 100644 index 000000000..a6748e26c --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/bean/ExceptionSolution.java @@ -0,0 +1,24 @@ +package com.fr.env.detect.bean; + +/** + * created by Harrison on 2022/05/13 + **/ +public class ExceptionSolution { + + private Message message; + + private SolutionAction action; + + public ExceptionSolution(Message message, SolutionAction action) { + this.message = message; + this.action = action; + } + + public Message getMessage() { + return message; + } + + public SolutionAction getAction() { + return action; + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/bean/ExceptionTips.java b/designer-base/src/main/java/com/fr/env/detect/bean/ExceptionTips.java new file mode 100644 index 000000000..970bca989 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/bean/ExceptionTips.java @@ -0,0 +1,19 @@ +package com.fr.env.detect.bean; + +/** + * 异常提示 + * + * created by Harrison on 2022/05/13 + **/ +public class ExceptionTips { + + private Message message; + + public ExceptionTips(Message message) { + this.message = message; + } + + public Message getMessage() { + return message; + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/bean/Message.java b/designer-base/src/main/java/com/fr/env/detect/bean/Message.java new file mode 100644 index 000000000..e1d6dcd62 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/bean/Message.java @@ -0,0 +1,70 @@ +package com.fr.env.detect.bean; + +/** + * created by Harrison on 2022/05/24 + **/ +public interface Message { + + /** + * 消息类型 + * + * @return 类型 + */ + Type getType(); + + enum Type { + + /** + * 简单 + */ + SIMPLE, + + /** + * 链接 + */ + LINK + } + + class Simple implements Message { + + private String message; + + public Simple(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + @Override + public Type getType() { + return Type.SIMPLE; + } + } + + class Link implements Message { + + private String text; + + private String link; + + public Link(String text, String link) { + this.text = text; + this.link = link; + } + + public String getText() { + return text; + } + + public String getLink() { + return link; + } + + @Override + public Type getType() { + return Type.LINK; + } + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/bean/SolutionAction.java b/designer-base/src/main/java/com/fr/env/detect/bean/SolutionAction.java new file mode 100644 index 000000000..4b959053b --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/bean/SolutionAction.java @@ -0,0 +1,11 @@ +package com.fr.env.detect.bean; + +/** + * created by Harrison on 2022/05/24 + **/ +public interface SolutionAction { + + String name(); + + void run(); +} diff --git a/designer-base/src/main/java/com/fr/env/detect/exception/DetectorException.java b/designer-base/src/main/java/com/fr/env/detect/exception/DetectorException.java new file mode 100644 index 000000000..14adcab29 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/exception/DetectorException.java @@ -0,0 +1,26 @@ +package com.fr.env.detect.exception; + +/** + * created by Harrison on 2022/05/25 + **/ +public class DetectorException extends RuntimeException { + + public DetectorException() { + } + + public DetectorException(String message) { + super(message); + } + + public DetectorException(String message, Throwable cause) { + super(message, cause); + } + + public DetectorException(Throwable cause) { + super(cause); + } + + public DetectorException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/HsqlDirtyDetector.java b/designer-base/src/main/java/com/fr/env/detect/impl/HsqlDirtyDetector.java new file mode 100644 index 000000000..6afc41a66 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/impl/HsqlDirtyDetector.java @@ -0,0 +1,18 @@ +package com.fr.env.detect.impl; + +import com.fr.env.detect.base.CatchExceptionDetector; +import com.fr.env.detect.bean.DetectorType; +import com.fr.env.detect.thowable.ThrowableStore; +import com.fr.env.detect.impl.converter.FineDbDirtyConverter; + +/** + * created by Harrison on 2022/05/25 + **/ +public class HsqlDirtyDetector extends CatchExceptionDetector { + + public HsqlDirtyDetector(ThrowableStore throwableStore) { + + super(DetectorType.FINE_DB_DIRTY, throwableStore, new FineDbDirtyConverter()); + } + +} diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/JarInconsistentDetector.java b/designer-base/src/main/java/com/fr/env/detect/impl/JarInconsistentDetector.java new file mode 100644 index 000000000..0967697f6 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/impl/JarInconsistentDetector.java @@ -0,0 +1,85 @@ +package com.fr.env.detect.impl; + +import com.fr.env.detect.base.AbstractExceptionDetector; +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.DetectorType; +import com.fr.env.detect.exception.DetectorException; +import com.fr.general.build.BuildInfo; +import com.fr.general.build.BuildInfoOperator; +import com.fr.general.build.BuildInfoManager; +import com.fr.stable.StringUtils; +import com.fr.third.guava.collect.MapDifference; +import com.fr.third.guava.collect.Maps; +import com.fr.workspace.WorkContext; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * created by Harrison on 2022/05/25 + **/ +public class JarInconsistentDetector extends AbstractExceptionDetector { + + public JarInconsistentDetector() { + + super(DetectorType.JAR_IN_CONSISTENCE); + } + + @Override + public DetectorResult detect() { + + DetectorResult.DetectorResultBuilder builder = DetectorResult.builder(); + // 检测有哪些 JAR 包, 当前是否缺少对应的 JAR 包 + BuildInfoOperator buildInfoOperator = WorkContext.getCurrent().get(BuildInfoOperator.class); + List buildInfos = buildInfoOperator.getBuildInfos(); + + if (WorkContext.getCurrent().isLocal()) { + + String designerBuild = buildInfos.stream() + .filter(this::isDesignerJar) + .map(BuildInfo::getGroupBuild) + .filter(StringUtils::isNotEmpty) + .findFirst() + .orElseThrow(DetectorException::new); + + List inConsistentInfos = buildInfos.stream() + .filter((e) -> !StringUtils.equals(designerBuild, e.getGroupBuild())) + .collect(Collectors.toList()); + + } else { + + // 远程情况 + List localInfos = BuildInfoManager.getInstance().getInfos(); + Map localMap = groupBy(localInfos); + + List remoteInfos = buildInfos; + Map remoteMap = groupBy(remoteInfos); + + MapDifference difference = Maps.difference(localMap, remoteMap); + // 两边都有的 JAR 的不同之处 + Map diffInCommon = difference.entriesInCommon(); + + } + + return builder.build(); + } + + private boolean isDesignerJar(BuildInfo info) { + + return StringUtils.contains(info.getJar(), "fine-report-designer"); + } + + private Map groupBy(List localInfos) { + + Map localMap = new HashMap<>(); + for (BuildInfo localInfo : localInfos) { + String jar = localInfo.getJar(); + String groupContent = localInfo.getGroupBuild(); + localMap.put(jar, groupContent); + } + return localMap; + } + +} diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/JarLackDetector.java b/designer-base/src/main/java/com/fr/env/detect/impl/JarLackDetector.java new file mode 100644 index 000000000..86f589f30 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/impl/JarLackDetector.java @@ -0,0 +1,43 @@ +package com.fr.env.detect.impl; + +import com.fr.env.detect.base.AbstractExceptionDetector; +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.DetectorType; +import com.fr.general.build.BuildInfo; +import com.fr.general.build.BuildInfoOperator; +import com.fr.stable.StringUtils; +import com.fr.workspace.WorkContext; + +import java.util.List; + +/** + * created by Harrison on 2022/05/24 + **/ +public class JarLackDetector extends AbstractExceptionDetector { + + public JarLackDetector() { + super(DetectorType.LACK_OF_JAR); + } + + @Override + public DetectorResult detect() { + + DetectorResult.DetectorResultBuilder builder = DetectorResult.builder(); + + // 检测有哪些 JAR 包, 当前是否缺少对应的 JAR 包 + BuildInfoOperator buildInfoOperator = WorkContext.getCurrent().get(BuildInfoOperator.class); + List buildInfos = buildInfoOperator.getBuildInfos(); + for (BuildInfo buildInfo : buildInfos) { + String jar = buildInfo.getJar(); + String buildContent = buildInfo.getGroupBuild(); + boolean isLack = StringUtils.isEmpty(buildContent); + if (isLack) { + // 处理信息 + + } + } + + return builder.build(); + + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassThrowableConvertor.java b/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassThrowableConvertor.java new file mode 100644 index 000000000..7fdd8bbe3 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassThrowableConvertor.java @@ -0,0 +1,53 @@ +package com.fr.env.detect.impl.converter; + +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.thowable.ThrowableConverter; + +import javax.el.MethodNotFoundException; +import java.util.HashSet; +import java.util.Set; + +/** + * 类抛出异常的转换 + * created by Harrison on 2022/05/24 + **/ +public class ClassThrowableConvertor implements ThrowableConverter { + + private Set> throwableClassSet = new HashSet<>(); + + private Set> throwableMethodSet = new HashSet<>(); + + public ClassThrowableConvertor() { + + // 类异常 + this.throwableClassSet.add(ClassNotFoundException.class); + this.throwableClassSet.add(NoClassDefFoundError.class); + this.throwableClassSet.add(ClassCastException.class); + this.throwableClassSet.add(IncompatibleClassChangeError.class); + + // 方法异常 + this.throwableMethodSet.add(MethodNotFoundException.class); + this.throwableMethodSet.add(NoSuchMethodException.class); + this.throwableMethodSet.add(NoSuchMethodError.class); + } + + @Override + public boolean accept(Throwable throwable) { + + Throwable sign = throwable; + while (sign != null) { + if (throwableClassSet.contains(sign.getClass()) || throwableMethodSet.contains(sign.getClass())) { + return true; + } + sign = sign.getCause(); + } + return false; + } + + @Override + public DetectorResult convert(Throwable throwable) { + + // todo + return null; + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbDirtyConverter.java b/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbDirtyConverter.java new file mode 100644 index 000000000..d23aa9915 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbDirtyConverter.java @@ -0,0 +1,33 @@ +package com.fr.env.detect.impl.converter; + +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.thowable.ThrowableConverter; +import com.fr.third.org.hibernate.exception.GenericJDBCException; +import org.jetbrains.annotations.Nullable; + +/** + * created by Harrison on 2022/05/25 + **/ +public class FineDbDirtyConverter implements ThrowableConverter { + + @Override + public boolean accept(Throwable throwable) { + + Throwable sign = throwable; + while (sign != null) { + if (sign instanceof GenericJDBCException) { + return true; + } + sign = sign.getCause(); + } + return false; + } + + @Override + public @Nullable DetectorResult convert(Throwable throwable) { + + // 检测 Config + // todo + return null; + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbLockedConverter.java b/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbLockedConverter.java new file mode 100644 index 000000000..8924d688a --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbLockedConverter.java @@ -0,0 +1,31 @@ +package com.fr.env.detect.impl.converter; + +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.thowable.ThrowableConverter; +import com.fr.third.org.hsqldb.HsqlException; + +/** + * created by Harrison on 2022/05/24 + **/ +public class FineDbLockedConverter implements ThrowableConverter { + + @Override + public boolean accept(Throwable throwable) { + + Throwable sign = throwable; + while (sign != null) { + if (sign instanceof HsqlException) { + return true; + } + sign = sign.getCause(); + } + return false; + } + + @Override + public DetectorResult convert(Throwable throwable) { + + // todo + return null; + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbPermissionConverter.java b/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbPermissionConverter.java new file mode 100644 index 000000000..21722ebb4 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbPermissionConverter.java @@ -0,0 +1,33 @@ +package com.fr.env.detect.impl.converter; + +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.thowable.ThrowableConverter; +import com.fr.third.org.hsqldb.HsqlException; + +/** + * HSQL 下的权限检测 + * + * created by Harrison on 2022/05/24 + **/ +public class FineDbPermissionConverter implements ThrowableConverter { + + @Override + public boolean accept(Throwable throwable) { + + Throwable sign = throwable; + while (sign != null) { + if (sign instanceof HsqlException) { + return true; + } + sign = sign.getCause(); + } + return false; + } + + @Override + public DetectorResult convert(Throwable throwable) { + + // todo + return null; + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/thowable/ThrowableConverter.java b/designer-base/src/main/java/com/fr/env/detect/thowable/ThrowableConverter.java new file mode 100644 index 000000000..6843cb2a8 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/thowable/ThrowableConverter.java @@ -0,0 +1,28 @@ +package com.fr.env.detect.thowable; + +import com.fr.env.detect.bean.DetectorResult; +import org.jetbrains.annotations.Nullable; + +/** + * created by Harrison on 2022/05/13 + **/ +public interface ThrowableConverter { + + /** + * 是否支持该异常 + * + * @param throwable 异常 + * @return 是/否 + */ + boolean accept(Throwable throwable); + + /** + * 将异常转化为结果 + * + * @param throwable 异常 + * @return 转化结果 + */ + @Nullable + DetectorResult convert(Throwable throwable); + +} diff --git a/designer-base/src/main/java/com/fr/env/detect/thowable/ThrowableLogAppender.java b/designer-base/src/main/java/com/fr/env/detect/thowable/ThrowableLogAppender.java new file mode 100644 index 000000000..64cd3ce7b --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/thowable/ThrowableLogAppender.java @@ -0,0 +1,58 @@ +package com.fr.env.detect.thowable; + +import com.fr.log.FineLoggerFactory; +import com.fr.log.LogHandler; +import com.fr.third.apache.logging.log4j.Level; +import com.fr.third.apache.logging.log4j.core.Filter; +import com.fr.third.apache.logging.log4j.core.Layout; +import com.fr.third.apache.logging.log4j.core.LogEvent; +import com.fr.third.apache.logging.log4j.core.appender.AbstractAppender; +import com.fr.third.apache.logging.log4j.core.config.Property; + +import java.io.Serializable; + +/** + * created by Harrison on 2022/05/13 + **/ +public class ThrowableLogAppender extends AbstractAppender { + + public ThrowableLogAppender(String name, Filter filter, Layout layout, boolean ignoreExceptions, Property[] properties) { + super(name, filter, layout, ignoreExceptions, properties); + } + + public static ThrowableLogAppender getInstance() { + return ExceptionLogAppenderHolder.INSTANCE; + } + + private static class ExceptionLogAppenderHolder { + private static final ThrowableLogAppender INSTANCE = new ThrowableLogAppender("exception-detect", null, null, false, null); + } + + private LogHandler logHandler = toHandler(); + + @Override + public void append(LogEvent logEvent) { + + if (logEvent.getLevel() == Level.ERROR) { + Throwable thrown = logEvent.getThrown(); + ThrowableStore.getInstance().add(thrown); + } + } + + private LogHandler toHandler() { + + final ThrowableLogAppender appender = this; + return () -> appender; + } + + + public void enable() { + + FineLoggerFactory.getLogger().addLogAppender(logHandler); + } + + public void disable() { + + FineLoggerFactory.getLogger().removeLogAppender(logHandler); + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/thowable/ThrowableStore.java b/designer-base/src/main/java/com/fr/env/detect/thowable/ThrowableStore.java new file mode 100644 index 000000000..346e21899 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/thowable/ThrowableStore.java @@ -0,0 +1,36 @@ +package com.fr.env.detect.thowable; + +import java.util.ArrayList; +import java.util.List; + +/** + * 异常存储中心 + * + * created by Harrison on 2022/05/13 + **/ +public class ThrowableStore { + + public static ThrowableStore getInstance() { + return ThrowableStoreHolder.INSTANCE; + } + + private static class ThrowableStoreHolder { + private static final ThrowableStore INSTANCE = new ThrowableStore(); + } + + private List exceptions = new ArrayList<>(); + + public void add(Throwable throwable) { + if (throwable != null) { + exceptions.add(throwable); + } + } + + public List getAll() { + return exceptions; + } + + public void reset() { + exceptions.clear(); + } +} From 8ce102bfdfe94a326b3b6326c3fa2c8236f1a883 Mon Sep 17 00:00:00 2001 From: Yvan Date: Thu, 26 May 2022 15:38:03 +0800 Subject: [PATCH 11/85] =?UTF-8?q?REPORT-70481=20=E8=BF=9C=E7=A8=8B?= =?UTF-8?q?=E8=AE=BE=E8=AE=A1=E5=88=87=E6=8D=A2=E6=A0=A1=E9=AA=8C=E6=9C=BA?= =?UTF-8?q?=E5=88=B6=E4=BC=98=E5=8C=96=20=E3=80=90=E9=97=AE=E9=A2=98?= =?UTF-8?q?=E5=8E=9F=E5=9B=A0=E3=80=91rt=20=E3=80=90=E6=94=B9=E5=8A=A8?= =?UTF-8?q?=E6=80=9D=E8=B7=AF=E3=80=91=E8=AF=A6=E8=A7=81=E5=BC=80=E5=8F=91?= =?UTF-8?q?=E6=96=87=E6=A1=A3=E2=80=94=E2=80=94https://kms.fineres.com/pag?= =?UTF-8?q?es/viewpage.action=3FpageId=3D412521906=20=E3=80=90review?= =?UTF-8?q?=E5=BB=BA=E8=AE=AE=E3=80=91review=E7=9A=84=E6=97=B6=E5=80=99?= =?UTF-8?q?=E9=BA=BB=E7=83=A6=E9=A1=BA=E4=BE=BF=E7=BB=99=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E8=AF=84=E5=AE=A1=E4=B8=8B=EF=BC=8C=E8=B0=A2=E8=B0=A2~?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../actions/file/ExitDesignerAction.java | 10 ++++- .../design/actions/file/PreferencePane.java | 7 +++- .../design/file/HistoryTemplateListCache.java | 7 ++-- .../fr/design/file/SaveSomeTemplatePane.java | 12 +++++- .../com/fr/design/lock/LockInfoDialog.java | 6 ++- .../com/fr/design/mainframe/JTemplate.java | 38 +++++++++++++++++-- .../com/fr/design/utils/TemplateUtils.java | 20 +++++++--- .../worker/save/SaveFailureHandler.java | 37 +++++++++++++++--- .../main/java/com/fr/file/StashedFILE.java | 8 ++++ 9 files changed, 122 insertions(+), 23 deletions(-) diff --git a/designer-base/src/main/java/com/fr/design/actions/file/ExitDesignerAction.java b/designer-base/src/main/java/com/fr/design/actions/file/ExitDesignerAction.java index 1ca29a042..5925e250f 100644 --- a/designer-base/src/main/java/com/fr/design/actions/file/ExitDesignerAction.java +++ b/designer-base/src/main/java/com/fr/design/actions/file/ExitDesignerAction.java @@ -3,6 +3,7 @@ */ package com.fr.design.actions.file; +import com.fr.design.file.SaveSomeTemplatePane; import com.fr.design.mainframe.TemplateSavingChecker; import java.awt.event.ActionEvent; @@ -28,9 +29,14 @@ public class ExitDesignerAction extends UpdateAction { * @param e 事件 */ public void actionPerformed(ActionEvent e) { + // 检查是否有正在保存的模板 if (!TemplateSavingChecker.check()) { return; } - DesignerContext.getDesignerFrame().exit(); + // 提示用户保存模板 + SaveSomeTemplatePane saveSomeTemplatePane = new SaveSomeTemplatePane(true); + if (saveSomeTemplatePane.showSavePane()) { + DesignerContext.getDesignerFrame().exit(); + } } -} \ No newline at end of file +} diff --git a/designer-base/src/main/java/com/fr/design/actions/file/PreferencePane.java b/designer-base/src/main/java/com/fr/design/actions/file/PreferencePane.java index 9c12c491c..5c403e8cf 100644 --- a/designer-base/src/main/java/com/fr/design/actions/file/PreferencePane.java +++ b/designer-base/src/main/java/com/fr/design/actions/file/PreferencePane.java @@ -9,6 +9,7 @@ import com.fr.design.dialog.BasicPane; import com.fr.design.dialog.DialogActionAdapter; import com.fr.design.dialog.DialogActionListener; import com.fr.design.editor.editor.IntegerEditor; +import com.fr.design.file.SaveSomeTemplatePane; import com.fr.design.gui.frpane.UITabbedPane; import com.fr.design.gui.ibutton.UIButton; import com.fr.design.gui.ibutton.UIColorButton; @@ -900,7 +901,11 @@ public class PreferencePane extends BasicPane { null ); if (rv == JOptionPane.OK_OPTION) { - RestartHelper.restart(); + // 重启前需要保存下模板 + SaveSomeTemplatePane saveSomeTempaltePane = new SaveSomeTemplatePane(true, SwingUtilities.getWindowAncestor(this)); + if (saveSomeTempaltePane.showSavePane()) { + RestartHelper.restart(); + } } } diff --git a/designer-base/src/main/java/com/fr/design/file/HistoryTemplateListCache.java b/designer-base/src/main/java/com/fr/design/file/HistoryTemplateListCache.java index d6b12efa6..37c8043cf 100644 --- a/designer-base/src/main/java/com/fr/design/file/HistoryTemplateListCache.java +++ b/designer-base/src/main/java/com/fr/design/file/HistoryTemplateListCache.java @@ -353,7 +353,7 @@ public class HistoryTemplateListCache implements CallbackEvent { int size = historyList.size(); for (int i = 0; i < size; i++) { JTemplate template = historyList.get(i); - FILE file = template.templateToStashFile(); + FILE file = template.templateToStashFile4Envchange(); if (file != null) { stashFILEMap.put(i, file); } @@ -382,7 +382,8 @@ public class HistoryTemplateListCache implements CallbackEvent { } FineLoggerFactory.getLogger().info("{} is being reloaded", stashedFile.getName()); JTemplate template = historyList.get(i); - template.refreshResource(stashedFile); + // 切换环境后,刷新资源并且将暂存的FILE替换到template中 + template.refreshResourceAndEditingFILE(stashedFile); } catch (Exception e) { FineLoggerFactory.getLogger().error(e.getMessage(), e); } @@ -430,7 +431,7 @@ public class HistoryTemplateListCache implements CallbackEvent { FILE file = template.getEditingFILE(); boolean needReload = context == null || needReloadTemplate(context, template); if (needReload) { - FILE stashFile = template.templateToStashFile(); + FILE stashFile = template.templateToStashFile4Other(); if (stashFile != null) { FineLoggerFactory.getLogger().info("{} is being reloaded", file.getName()); template.refreshResource(stashFile); diff --git a/designer-base/src/main/java/com/fr/design/file/SaveSomeTemplatePane.java b/designer-base/src/main/java/com/fr/design/file/SaveSomeTemplatePane.java index 7fef2e379..af2d1d2a1 100644 --- a/designer-base/src/main/java/com/fr/design/file/SaveSomeTemplatePane.java +++ b/designer-base/src/main/java/com/fr/design/file/SaveSomeTemplatePane.java @@ -17,7 +17,6 @@ import com.fr.design.mainframe.JTemplate; import com.fr.general.ComparatorUtils; import com.fr.log.FineLoggerFactory; -import com.fr.stable.ProductConstants; import javax.swing.*; import javax.swing.border.EmptyBorder; @@ -43,9 +42,18 @@ public class SaveSomeTemplatePane extends BasicPane { private boolean isJudgeCurrentEditingTemplate = true; public SaveSomeTemplatePane(boolean isNeedTojudgeCurrent) { + this(isNeedTojudgeCurrent, DesignerContext.getDesignerFrame()); + } + + /** + * 支持自定义设置 dialog的父窗口 + * @param isNeedTojudgeCurrent + * @param parent + */ + public SaveSomeTemplatePane(boolean isNeedTojudgeCurrent, Window parent) { this.setLayout(FRGUIPaneFactory.createBorderLayout()); if (this.dialog == null) { - this.dialog = this.showSmallWindow(DesignerContext.getDesignerFrame(), new DialogActionAdapter() { + this.dialog = this.showSmallWindow(parent, new DialogActionAdapter() { @Override public void doOk() { for (int i = 0; i < templateCheckBoxes.length; i++) { diff --git a/designer-base/src/main/java/com/fr/design/lock/LockInfoDialog.java b/designer-base/src/main/java/com/fr/design/lock/LockInfoDialog.java index a0fd544d5..003215ca6 100644 --- a/designer-base/src/main/java/com/fr/design/lock/LockInfoDialog.java +++ b/designer-base/src/main/java/com/fr/design/lock/LockInfoDialog.java @@ -99,7 +99,11 @@ public class LockInfoDialog extends JDialog { return; } final String selectedFilePath = StableUtils.pathJoin(ProjectConstants.REPORTLETS_NAME, TemplateTreePane.getInstance().getFilePath()); - TemplateUtils.createAndOpenTemplate(Toolkit.i18nText("Fine_Design_Template_Lock_Copy"), new FileNodeFILE(new FileNode(selectedFilePath, false)), true); + TemplateUtils.createAndOpenTemplate( + Toolkit.i18nText("Fine_Design_Template_Lock_Copy"), + new FileNodeFILE(new FileNode(selectedFilePath, false)), + false, + true); } }); cancelButton.addActionListener(new ActionListener() { diff --git a/designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java b/designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java index aacbd2fbf..ff5a2ed50 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java @@ -442,14 +442,35 @@ public abstract class JTemplate> stopListenThemeConfig(); } - public FILE templateToStashFile() { + /** + * 用于 切换工作目录 时的模板资源暂存 + * @return + */ + public FILE templateToStashFile4Envchange() { + return templateToStashFile(true); + } + + /** + * 用于 其它情况 的模板资源暂存 + * 例如 插件插件安装/启用 + * @return + */ + public FILE templateToStashFile4Other() { + return templateToStashFile(false); + } + + private FILE templateToStashFile(boolean envChange) { FILE file = this.getEditingFILE(); try { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); BaseBook target = this.getTarget(); if (target != null) { target.export(outputStream); - return new StashedFILE(file, outputStream.toByteArray(), template.suffix()); + return envChange && file.isEnvFile() && !isSaved()? + // 切换工作目录时,未保存的环境文件转化为与环境无关的内存文件,再创建暂存文件 + new StashedFILE(new MemFILE(file.getName()), outputStream.toByteArray(), template.suffix()) : + // 其它情况下,直接创建暂存文件 + new StashedFILE(file, outputStream.toByteArray(), template.suffix()); } // 如果 target == null 那么这个模板是被模板内存优化功能处理过的,不用处理 } catch (Exception e) { @@ -492,6 +513,17 @@ public abstract class JTemplate> } + /** + * 刷新 模板资源 和 EditingFILE + * 仅在切换工作目录,reload模板时使用 + * @param file + */ + public void refreshResourceAndEditingFILE(FILE file) { + + this.editingFILE = file instanceof StashedFILE ? ((StashedFILE) file).getInsideFILE() : file; + refreshResource(file); + } + private void setTargetByFile(FILE file) { T newTemplate = JTemplateFactory.asIOFile(file, this.suffix()); if (newTemplate != null) { @@ -1921,4 +1953,4 @@ public abstract class JTemplate> DesignerUIModeConfig.getInstance().setAbsoluteMeasureUIMode(); } -} \ No newline at end of file +} diff --git a/designer-base/src/main/java/com/fr/design/utils/TemplateUtils.java b/designer-base/src/main/java/com/fr/design/utils/TemplateUtils.java index a84c260c3..c4cb64db0 100644 --- a/designer-base/src/main/java/com/fr/design/utils/TemplateUtils.java +++ b/designer-base/src/main/java/com/fr/design/utils/TemplateUtils.java @@ -27,7 +27,16 @@ import java.io.OutputStream; */ public class TemplateUtils { - public static void createAndOpenTemplate(String prefix, FILE file, boolean needOpen) { + /** + * 创建新的模板文件并打开模板 + * @param prefix 模板文件名称前缀 + * @param file 模板文件 + * @param createByEditingTemplate 是否根据 当前编辑模板 来创建新模板 + * 为true时以CurrentEditingTemplate为准创建新模板 + * 为false时以传入的File文件为准创建新模板,此文件可以不是编辑状态 + * @param openNewTemplate 是否需要在创建后打开模板 + */ + public static void createAndOpenTemplate(String prefix, FILE file, boolean createByEditingTemplate, boolean openNewTemplate) { String fileName = file.getName(); String oldPath = file.getPath(); int indexOfLastDot = fileName.lastIndexOf(CoreConstants.DOT); @@ -49,19 +58,18 @@ public class TemplateUtils { if (isOk(result)) { file = fileChooserPane.getSelectedFILE(); - _createAndOpenTemplate(file, oldPath, needOpen); + _createAndOpenTemplate(file, oldPath, createByEditingTemplate, openNewTemplate); } - } - private static void _createAndOpenTemplate(FILE file, String oldPath, boolean needOpen){ + private static void _createAndOpenTemplate(FILE file, String oldPath, boolean createByEditingTemplate, boolean openNewTemplate){ new SwingWorker() { @Override protected Void doInBackground() throws Exception { byte[] content = new byte[0]; - if (!needOpen) { + if (createByEditingTemplate) { // 从当前编辑模板中生成备份文件 JTemplate template = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); content = template.exportData(); @@ -95,7 +103,7 @@ public class TemplateUtils { protected void done() { try { get(); - if (needOpen) { + if (openNewTemplate) { DesignerContext.getDesignerFrame().openTemplate(file); } // 备份成功刷新下目录树 展示出来备份的模板 diff --git a/designer-base/src/main/java/com/fr/design/worker/save/SaveFailureHandler.java b/designer-base/src/main/java/com/fr/design/worker/save/SaveFailureHandler.java index 5d7beb36a..080b9ddbc 100644 --- a/designer-base/src/main/java/com/fr/design/worker/save/SaveFailureHandler.java +++ b/designer-base/src/main/java/com/fr/design/worker/save/SaveFailureHandler.java @@ -22,6 +22,7 @@ import javax.swing.JOptionPane; * @version 11.0 * Created by hades on 2021/12/7 */ +@SuppressWarnings("all") public class SaveFailureHandler implements ThrowableHandler { private static final SaveFailureHandler INSTANCE = new SaveFailureHandler(); @@ -70,7 +71,7 @@ public class SaveFailureHandler implements ThrowableHandler { @Override public boolean process(Throwable e) { if (e.getCause() instanceof UnLockedException || e instanceof UnLockedException) { - processByBack(Toolkit.i18nText("Fine_Design_Template_Has_Been_UnLocked")); + processUnLocked(Toolkit.i18nText("Fine_Design_Template_Has_Been_UnLocked")); return true; } return false; @@ -81,7 +82,7 @@ public class SaveFailureHandler implements ThrowableHandler { @Override public boolean process(Throwable e) { if (e.getCause() instanceof InconsistentLockException || e instanceof InconsistentLockException) { - processByBack(Toolkit.i18nText("Fine_Design_Template_Save_Failed_By_Lock_Inconsistency")); + processInconsistentLock(Toolkit.i18nText("Fine_Design_Template_Save_Failed_By_Lock_Inconsistency")); return true; } return false; @@ -102,7 +103,7 @@ public class SaveFailureHandler implements ThrowableHandler { }; - protected void processByBack(String tip) { + protected void processUnLocked(String tip) { int option = FineJOptionPane.showOptionDialog(DesignerContext.getDesignerFrame(), tip, Toolkit.i18nText("Fine-Design_Basic_Alert"), @@ -113,10 +114,36 @@ public class SaveFailureHandler implements ThrowableHandler { if (option == JOptionPane.YES_OPTION) { JTemplate template = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); if (template != null) { - TemplateUtils.createAndOpenTemplate(Toolkit.i18nText("Fine_Design_Template_Backup"), new FileNodeFILE(new FileNode(template.getPath(), false)), false); + TemplateUtils.createAndOpenTemplate( + Toolkit.i18nText("Fine_Design_Template_Backup"), + new FileNodeFILE(new FileNode(template.getPath(), false)), + true, + false); + } + } + } + + protected void processInconsistentLock(String tip) { + int option = FineJOptionPane.showOptionDialog(DesignerContext.getDesignerFrame(), + tip, + Toolkit.i18nText("Fine-Design_Basic_Alert"), + JOptionPane.YES_NO_OPTION, + JOptionPane.WARNING_MESSAGE, + IOUtils.readIcon("/com/fr/design/images/warnings/warning32.png"), + new Object[] {Toolkit.i18nText("Fine_Design_Template_SaveAs_Backup"), Toolkit.i18nText("Fine-Design_Basic_Button_Cancel")}, null); + if (option == JOptionPane.YES_OPTION) { + JTemplate template = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); + if (template != null) { + TemplateUtils.createAndOpenTemplate( + Toolkit.i18nText("Fine_Design_Template_Backup"), + new FileNodeFILE(new FileNode(template.getPath(), false)), + true, + true); + // 创建并打开备份模板后,关闭原模板 + HistoryTemplateListCache.getInstance().closeSelectedReport(template); } } } } -} \ No newline at end of file +} diff --git a/designer-base/src/main/java/com/fr/file/StashedFILE.java b/designer-base/src/main/java/com/fr/file/StashedFILE.java index 48f00f95b..a73dd612d 100644 --- a/designer-base/src/main/java/com/fr/file/StashedFILE.java +++ b/designer-base/src/main/java/com/fr/file/StashedFILE.java @@ -87,6 +87,14 @@ public class StashedFILE extends AbstractFILE { return false; } + /** + * 获取内部FILE对象 + * @return + */ + public FILE getInsideFILE() { + return file; + } + @Override public String toString() { return FILEFactory.MEM_PREFIX + getName(); From 4c93226a2408d2d052965ddfa2dc73383424d9f7 Mon Sep 17 00:00:00 2001 From: Yvan Date: Thu, 26 May 2022 18:33:37 +0800 Subject: [PATCH 12/85] =?UTF-8?q?REPORT-70481=20=E8=BF=9C=E7=A8=8B?= =?UTF-8?q?=E8=AE=BE=E8=AE=A1=E5=88=87=E6=8D=A2=E6=A0=A1=E9=AA=8C=E6=9C=BA?= =?UTF-8?q?=E5=88=B6=E4=BC=98=E5=8C=96=201.=20=E5=A2=9E=E5=8A=A0=E5=88=87?= =?UTF-8?q?=E6=8D=A2=E5=B7=A5=E4=BD=9C=E7=9B=AE=E5=BD=95=E5=89=8D=EF=BC=8C?= =?UTF-8?q?=E5=BC=B9=E7=AA=97=E6=8F=90=E7=A4=BA=E7=94=A8=E6=88=B7=E4=BF=9D?= =?UTF-8?q?=E5=AD=98=E7=9A=84=E9=80=BB=E8=BE=91=202.=20=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/fr/design/EnvChangeEntrance.java | 3 +++ .../main/java/com/fr/design/actions/file/SwitchExistEnv.java | 3 +++ .../src/main/java/com/fr/design/mainframe/JTemplate.java | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/designer-base/src/main/java/com/fr/design/EnvChangeEntrance.java b/designer-base/src/main/java/com/fr/design/EnvChangeEntrance.java index cd9c835f3..60554ef7d 100644 --- a/designer-base/src/main/java/com/fr/design/EnvChangeEntrance.java +++ b/designer-base/src/main/java/com/fr/design/EnvChangeEntrance.java @@ -13,6 +13,7 @@ import com.fr.design.env.DesignerWorkspaceType; import com.fr.design.env.RemoteDesignerWorkspaceInfo; import com.fr.design.env.RemoteWorkspace; import com.fr.design.file.HistoryTemplateListCache; +import com.fr.design.file.SaveSomeTemplatePane; import com.fr.design.file.TemplateTreePane; import com.fr.design.i18n.Toolkit; import com.fr.design.mainframe.DesignerContext; @@ -82,6 +83,8 @@ public class EnvChangeEntrance { private boolean envListOkAction(EnvListPane envListPane, PopTipStrategy strategy) { final String selectedName = envListPane.updateEnvManager(); + SaveSomeTemplatePane saveSomeTemplatePane = new SaveSomeTemplatePane(true, SwingUtilities.getWindowAncestor(envListPane)); + saveSomeTemplatePane.showSavePane(); return switch2Env(selectedName, strategy); } diff --git a/designer-base/src/main/java/com/fr/design/actions/file/SwitchExistEnv.java b/designer-base/src/main/java/com/fr/design/actions/file/SwitchExistEnv.java index c59a92b97..b47d5ef1e 100644 --- a/designer-base/src/main/java/com/fr/design/actions/file/SwitchExistEnv.java +++ b/designer-base/src/main/java/com/fr/design/actions/file/SwitchExistEnv.java @@ -5,6 +5,7 @@ import com.fr.design.EnvChangeEntrance; import com.fr.design.actions.UpdateAction; import com.fr.design.env.DesignerWorkspaceInfo; import com.fr.design.file.HistoryTemplateListCache; +import com.fr.design.file.SaveSomeTemplatePane; import com.fr.design.mainframe.JTemplate; import com.fr.design.menu.KeySetUtils; import com.fr.design.menu.MenuDef; @@ -66,6 +67,8 @@ public class SwitchExistEnv extends MenuDef { // 打开配置目录面板 EnvChangeEntrance.getInstance().chooseEnv(envName); } else { + SaveSomeTemplatePane saveSomeTemplatePane = new SaveSomeTemplatePane(true); + saveSomeTemplatePane.showSavePane(); EnvChangeEntrance.getInstance().switch2Env(envName); } } diff --git a/designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java b/designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java index ff5a2ed50..d11b8bf1d 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java @@ -519,7 +519,7 @@ public abstract class JTemplate> * @param file */ public void refreshResourceAndEditingFILE(FILE file) { - + // 这里替换EditingFILE是为了在切换工作目录后,将模板文件对象设置成环境无关文件对象 this.editingFILE = file instanceof StashedFILE ? ((StashedFILE) file).getInsideFILE() : file; refreshResource(file); } From 1741b2705b1b896172eb18eb6ed216a5810ad710 Mon Sep 17 00:00:00 2001 From: Yvan Date: Fri, 27 May 2022 10:24:42 +0800 Subject: [PATCH 13/85] =?UTF-8?q?REPORT-70481=20=E8=BF=9C=E7=A8=8B?= =?UTF-8?q?=E8=AE=BE=E8=AE=A1=E5=88=87=E6=8D=A2=E6=A0=A1=E9=AA=8C=E6=9C=BA?= =?UTF-8?q?=E5=88=B6=E4=BC=98=E5=8C=96=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=EF=BC=8C=E9=81=BF=E5=85=8D=E7=9B=B4=E6=8E=A5=E4=BF=AE?= =?UTF-8?q?=E6=94=B9templateToStashFile=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../design/file/HistoryTemplateListCache.java | 2 +- .../com/fr/design/mainframe/JTemplate.java | 31 +++++++++---------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/designer-base/src/main/java/com/fr/design/file/HistoryTemplateListCache.java b/designer-base/src/main/java/com/fr/design/file/HistoryTemplateListCache.java index 37c8043cf..cb38ea8bd 100644 --- a/designer-base/src/main/java/com/fr/design/file/HistoryTemplateListCache.java +++ b/designer-base/src/main/java/com/fr/design/file/HistoryTemplateListCache.java @@ -431,7 +431,7 @@ public class HistoryTemplateListCache implements CallbackEvent { FILE file = template.getEditingFILE(); boolean needReload = context == null || needReloadTemplate(context, template); if (needReload) { - FILE stashFile = template.templateToStashFile4Other(); + FILE stashFile = template.templateToStashFile(); if (stashFile != null) { FineLoggerFactory.getLogger().info("{} is being reloaded", file.getName()); template.refreshResource(stashFile); diff --git a/designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java b/designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java index d11b8bf1d..b8e5468db 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java @@ -447,36 +447,35 @@ public abstract class JTemplate> * @return */ public FILE templateToStashFile4Envchange() { - return templateToStashFile(true); + FILE file = this.getEditingFILE(); + if (file.isEnvFile() && !isSaved()) { + // 切换工作目录时,存在未保存的环境文件时,将其转化为与环境无关的内存文件,再创建暂存文件 + return new StashedFILE(new MemFILE(file.getName()), exportBaseBook2ByteArray(), template.suffix()); + } else { + // 其它情况下,直接创建暂存文件 + return templateToStashFile(); + } } - /** - * 用于 其它情况 的模板资源暂存 - * 例如 插件插件安装/启用 - * @return - */ - public FILE templateToStashFile4Other() { - return templateToStashFile(false); + public FILE templateToStashFile() { + FILE file = this.getEditingFILE(); + return new StashedFILE(file, exportBaseBook2ByteArray(), template.suffix()); } - private FILE templateToStashFile(boolean envChange) { - FILE file = this.getEditingFILE(); + private byte[] exportBaseBook2ByteArray() { try { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); BaseBook target = this.getTarget(); if (target != null) { target.export(outputStream); - return envChange && file.isEnvFile() && !isSaved()? - // 切换工作目录时,未保存的环境文件转化为与环境无关的内存文件,再创建暂存文件 - new StashedFILE(new MemFILE(file.getName()), outputStream.toByteArray(), template.suffix()) : - // 其它情况下,直接创建暂存文件 - new StashedFILE(file, outputStream.toByteArray(), template.suffix()); + return outputStream.toByteArray(); } // 如果 target == null 那么这个模板是被模板内存优化功能处理过的,不用处理 } catch (Exception e) { + FineLoggerFactory.getLogger().error("Export BaseBook to Byte Array Failed"); FineLoggerFactory.getLogger().error(e.getMessage(), e); } - return null; + return new byte[0]; } From 677d12a1f28b1ae263f780aaf0adc9b6c347c56a Mon Sep 17 00:00:00 2001 From: Yvan Date: Fri, 27 May 2022 14:48:09 +0800 Subject: [PATCH 14/85] =?UTF-8?q?REPORT-70481=20=E8=BF=9C=E7=A8=8B?= =?UTF-8?q?=E8=AE=BE=E8=AE=A1=E5=88=87=E6=8D=A2=E6=A0=A1=E9=AA=8C=E6=9C=BA?= =?UTF-8?q?=E5=88=B6=E4=BC=98=E5=8C=96=20=E8=AE=BE=E8=AE=A1=E5=99=A8?= =?UTF-8?q?=E9=80=89=E9=A1=B9-=E4=BF=AE=E6=94=B9=E8=AF=AD=E8=A8=80?= =?UTF-8?q?=E5=90=8E=EF=BC=8C=E4=BC=98=E5=85=88=E8=B7=B3=E5=87=BA=E4=BF=9D?= =?UTF-8?q?=E5=AD=98=E6=8F=90=E7=A4=BA=E5=BC=B9=E7=AA=97=EF=BC=8C=E5=86=8D?= =?UTF-8?q?=E8=B7=B3=E5=87=BA=E9=87=8D=E5=90=AF=E8=AE=BE=E8=AE=A1=E5=99=A8?= =?UTF-8?q?=E6=8F=90=E7=A4=BA=E5=BC=B9=E7=AA=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/fr/design/actions/file/PreferencePane.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/designer-base/src/main/java/com/fr/design/actions/file/PreferencePane.java b/designer-base/src/main/java/com/fr/design/actions/file/PreferencePane.java index 5c403e8cf..100bd5965 100644 --- a/designer-base/src/main/java/com/fr/design/actions/file/PreferencePane.java +++ b/designer-base/src/main/java/com/fr/design/actions/file/PreferencePane.java @@ -890,6 +890,9 @@ public class PreferencePane extends BasicPane { if (!languageChanged) { return; } + // 重启弹窗出现之前提示用户保存模板 + SaveSomeTemplatePane saveSomeTempaltePane = new SaveSomeTemplatePane(true, SwingUtilities.getWindowAncestor(this)); + saveSomeTempaltePane.showSavePane(); int rv = JOptionPane.showOptionDialog( null, i18nText("Fine-Design_Basic_Language_Change_Successful"), @@ -901,11 +904,7 @@ public class PreferencePane extends BasicPane { null ); if (rv == JOptionPane.OK_OPTION) { - // 重启前需要保存下模板 - SaveSomeTemplatePane saveSomeTempaltePane = new SaveSomeTemplatePane(true, SwingUtilities.getWindowAncestor(this)); - if (saveSomeTempaltePane.showSavePane()) { - RestartHelper.restart(); - } + RestartHelper.restart(); } } From 3ad0c989609e54225ee943fae0959aab98c995ae Mon Sep 17 00:00:00 2001 From: Yvan Date: Fri, 27 May 2022 16:15:22 +0800 Subject: [PATCH 15/85] =?UTF-8?q?REPORT-70481=20=E8=BF=9C=E7=A8=8B?= =?UTF-8?q?=E8=AE=BE=E8=AE=A1=E5=88=87=E6=8D=A2=E6=A0=A1=E9=AA=8C=E6=9C=BA?= =?UTF-8?q?=E5=88=B6=E4=BC=98=E5=8C=96=20=E3=80=90=E9=97=AE=E9=A2=98?= =?UTF-8?q?=E5=8E=9F=E5=9B=A0=E3=80=91=E4=BA=A7=E5=93=81=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E8=AE=BE=E8=AE=A1=20=E3=80=90=E6=94=B9=E5=8A=A8=E6=80=9D?= =?UTF-8?q?=E8=B7=AF=E3=80=91=E5=88=87=E6=8D=A2=E5=B7=A5=E4=BD=9C=E7=9B=AE?= =?UTF-8?q?=E5=BD=95=E6=97=B6=E8=B7=B3=E5=87=BA=E7=9A=84=E4=BF=9D=E5=AD=98?= =?UTF-8?q?=E5=BC=B9=E7=AA=97=EF=BC=8C=E5=9C=A8=E7=94=A8=E6=88=B7=E5=8F=96?= =?UTF-8?q?=E6=B6=88=E4=BF=9D=E5=AD=98=E5=90=8E=EF=BC=8C=E4=B8=AD=E6=96=AD?= =?UTF-8?q?=E5=88=87=E6=8D=A2=E5=B7=A5=E4=BD=9C=E7=9B=AE=E5=BD=95=E5=8A=A8?= =?UTF-8?q?=E4=BD=9C=20=E3=80=90review=E5=BB=BA=E8=AE=AE=E3=80=91=E6=97=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/fr/design/EnvChangeEntrance.java | 7 +++++-- .../java/com/fr/design/actions/file/SwitchExistEnv.java | 6 ++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/designer-base/src/main/java/com/fr/design/EnvChangeEntrance.java b/designer-base/src/main/java/com/fr/design/EnvChangeEntrance.java index 60554ef7d..7d2fc6106 100644 --- a/designer-base/src/main/java/com/fr/design/EnvChangeEntrance.java +++ b/designer-base/src/main/java/com/fr/design/EnvChangeEntrance.java @@ -83,8 +83,6 @@ public class EnvChangeEntrance { private boolean envListOkAction(EnvListPane envListPane, PopTipStrategy strategy) { final String selectedName = envListPane.updateEnvManager(); - SaveSomeTemplatePane saveSomeTemplatePane = new SaveSomeTemplatePane(true, SwingUtilities.getWindowAncestor(envListPane)); - saveSomeTemplatePane.showSavePane(); return switch2Env(selectedName, strategy); } @@ -391,6 +389,11 @@ public class EnvChangeEntrance { @Override public void doOk() { + SaveSomeTemplatePane saveSomeTemplatePane = new SaveSomeTemplatePane(true, SwingUtilities.getWindowAncestor(envListPane)); + if (!saveSomeTemplatePane.showSavePane()) { + // 用户取消保存时,取消切换目录操作 + return; + } boolean changeResult = envListOkAction(envListPane, PopTipStrategy.LATER); // 切换完成后清理密码 updateNotRememberPwdEnv(); diff --git a/designer-base/src/main/java/com/fr/design/actions/file/SwitchExistEnv.java b/designer-base/src/main/java/com/fr/design/actions/file/SwitchExistEnv.java index b47d5ef1e..33761a92e 100644 --- a/designer-base/src/main/java/com/fr/design/actions/file/SwitchExistEnv.java +++ b/designer-base/src/main/java/com/fr/design/actions/file/SwitchExistEnv.java @@ -68,8 +68,10 @@ public class SwitchExistEnv extends MenuDef { EnvChangeEntrance.getInstance().chooseEnv(envName); } else { SaveSomeTemplatePane saveSomeTemplatePane = new SaveSomeTemplatePane(true); - saveSomeTemplatePane.showSavePane(); - EnvChangeEntrance.getInstance().switch2Env(envName); + if (saveSomeTemplatePane.showSavePane()) { + // 用户模板保存后,才进行切换目录操作 + EnvChangeEntrance.getInstance().switch2Env(envName); + } } } From 08c0be64ee889ca2777ef631e13fd8108a65b6fd Mon Sep 17 00:00:00 2001 From: hades Date: Fri, 27 May 2022 17:01:17 +0800 Subject: [PATCH 16/85] =?UTF-8?q?REPORT-70562=20=E5=AF=BC=E5=87=BA?= =?UTF-8?q?=E5=AD=97=E4=BD=93=E5=AD=97=E5=8F=B7=E9=97=AE=E9=A2=98-?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/fr/design/gui/ilable/ActionLabel.java | 11 ++- .../fr/design/report/ExportUniversalPane.java | 78 +++++++++++++++++++ .../design/report/ReportExportAttrPane.java | 13 ++++ 3 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 designer-realize/src/main/java/com/fr/design/report/ExportUniversalPane.java diff --git a/designer-base/src/main/java/com/fr/design/gui/ilable/ActionLabel.java b/designer-base/src/main/java/com/fr/design/gui/ilable/ActionLabel.java index ebccd24f4..85d73515e 100644 --- a/designer-base/src/main/java/com/fr/design/gui/ilable/ActionLabel.java +++ b/designer-base/src/main/java/com/fr/design/gui/ilable/ActionLabel.java @@ -13,11 +13,16 @@ import java.awt.event.MouseEvent; */ public class ActionLabel extends UILabel { private ActionListener actionListener; + private Color color; public ActionLabel(String text) { - super(text); + this(text, Color.blue); + } - this.setForeground(Color.blue); + public ActionLabel(String text, Color color) { + super(text); + this.color = color; + this.setForeground(color); this.addMouseListener(mouseInputAdapter); this.addMouseMotionListener(mouseInputAdapter); } @@ -33,7 +38,7 @@ public class ActionLabel extends UILabel { public void paintComponent(Graphics _gfx) { super.paintComponent(_gfx); - _gfx.setColor(Color.blue); + _gfx.setColor(this.color); _gfx.drawLine(0, this.getHeight() - 1, this.getWidth(), this.getHeight() - 1); } diff --git a/designer-realize/src/main/java/com/fr/design/report/ExportUniversalPane.java b/designer-realize/src/main/java/com/fr/design/report/ExportUniversalPane.java new file mode 100644 index 000000000..c95d37205 --- /dev/null +++ b/designer-realize/src/main/java/com/fr/design/report/ExportUniversalPane.java @@ -0,0 +1,78 @@ +package com.fr.design.report; + +import com.fr.base.CustomConfig; +import com.fr.config.Configuration; +import com.fr.design.constants.UIConstants; +import com.fr.design.dialog.BasicPane; +import com.fr.design.gui.icheckbox.UICheckBox; +import com.fr.design.gui.ilable.ActionLabel; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.i18n.Toolkit; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.utils.BrowseUtils; +import com.fr.transaction.Configurations; +import com.fr.transaction.WorkerFacade; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.BorderFactory; +import javax.swing.JPanel; + +/** + * @author hades + * @version 11.0 + * Created by hades on 2022/5/26 + */ +public class ExportUniversalPane extends BasicPane { + + private UICheckBox specialCharacterExport; + + public ExportUniversalPane() { + this.setLayout(FRGUIPaneFactory.createBorderLayout()); + this.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + JPanel outerNorthPane =FRGUIPaneFactory.createTitledBorderPane(Toolkit.i18nText("Fine-Design_Report_Universal_Export_Config")); + JPanel northPane = FRGUIPaneFactory.createY_AXISBoxInnerContainer_M_Pane(); + JPanel specialCharacterExportPane =FRGUIPaneFactory.createNormalFlowInnerContainer_M_Pane(); + specialCharacterExport = new UICheckBox(Toolkit.i18nText("Fine-Design_Report_Universal_Export_Special_Character")); + specialCharacterExport.setSelected(true); + specialCharacterExportPane.add(specialCharacterExport); + northPane.add(specialCharacterExportPane); + JPanel labelPane = new JPanel(new BorderLayout()); + labelPane.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 0)); + UILabel centerLabel = new UILabel(Toolkit.i18nText("Fine-Design_Report_Universal_Export_Special_Character_Tip")); + centerLabel.setForeground(Color.GRAY); + ActionLabel rightLabel = new ActionLabel(Toolkit.i18nText("Fine-Design_Report_Universal_Export_More_Alternative_Fonts"), UIConstants.FLESH_BLUE); + rightLabel.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + BrowseUtils.browser("http://www.baidu.com"); + } + }); + labelPane.add(centerLabel, BorderLayout.CENTER); + labelPane.add(rightLabel, BorderLayout.EAST); + northPane.add(labelPane); + outerNorthPane.add(northPane); + this.add(outerNorthPane); + } + + @Override + protected String title4PopupWindow() { + return "ExportUniversalPane"; + } + + public void populate() { + this.specialCharacterExport.setSelected(CustomConfig.getInstance().isOptimizedSpecialCharacterExport()); + } + + public void update() { + Configurations.modify(new WorkerFacade(CustomConfig.class) { + @Override + public void run() { + CustomConfig.getInstance().setOptimizedSpecialCharacterExport(specialCharacterExport.isSelected()); + } + }); + } + + +} diff --git a/designer-realize/src/main/java/com/fr/design/report/ReportExportAttrPane.java b/designer-realize/src/main/java/com/fr/design/report/ReportExportAttrPane.java index a284764d2..2de520d27 100644 --- a/designer-realize/src/main/java/com/fr/design/report/ReportExportAttrPane.java +++ b/designer-realize/src/main/java/com/fr/design/report/ReportExportAttrPane.java @@ -5,6 +5,7 @@ import com.fr.design.beans.BasicStorePane; import com.fr.design.dialog.BasicPane; import com.fr.design.fun.ExportAttrTabProvider; import com.fr.design.gui.frpane.UITabbedPane; +import com.fr.design.i18n.Toolkit; import com.fr.io.attr.ReportExportAttr; import javax.swing.*; @@ -18,12 +19,15 @@ public class ReportExportAttrPane extends BasicPane { private ExcelExportPane excelExportPane; private PDFExportPane pdfExportPane; private WordExportPane wordExportPane; + private ExportUniversalPane exportUniversalPane; private List> paneList; public ReportExportAttrPane() { uiTabbedPane = new UITabbedPane(); this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); this.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + exportUniversalPane = new ExportUniversalPane(); + uiTabbedPane.addTab(Toolkit.i18nText("Fine-Design_Report_Universal_Export"), exportUniversalPane); excelExportPane = new ExcelExportPane(); uiTabbedPane.addTab("Excel", excelExportPane); pdfExportPane = new PDFExportPane(); @@ -56,6 +60,10 @@ public class ReportExportAttrPane extends BasicPane { reportExportAttr = new ReportExportAttr(); } + if (this.exportUniversalPane != null) { + this.exportUniversalPane.populate(); + } + if (this.excelExportPane != null) { this.excelExportPane.populate(reportExportAttr.getExcelExportAttr()); } @@ -76,6 +84,11 @@ public class ReportExportAttrPane extends BasicPane { public ReportExportAttr update() { ReportExportAttr reportExportAttr = new ReportExportAttr(); + + if (this.exportUniversalPane != null) { + this.exportUniversalPane.update(); + } + if (this.excelExportPane != null) { reportExportAttr.setExcelExportAttr(this.excelExportPane.update()); } From 1479f0a771758997565f04bccbf88543d0e4247a Mon Sep 17 00:00:00 2001 From: hades Date: Fri, 27 May 2022 17:48:56 +0800 Subject: [PATCH 17/85] =?UTF-8?q?REPORT-70562=20=E5=B8=AE=E5=8A=A9?= =?UTF-8?q?=E6=96=87=E6=A1=A3=20url=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/fr/design/report/ExportUniversalPane.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/designer-realize/src/main/java/com/fr/design/report/ExportUniversalPane.java b/designer-realize/src/main/java/com/fr/design/report/ExportUniversalPane.java index c95d37205..3714d66a2 100644 --- a/designer-realize/src/main/java/com/fr/design/report/ExportUniversalPane.java +++ b/designer-realize/src/main/java/com/fr/design/report/ExportUniversalPane.java @@ -10,6 +10,7 @@ import com.fr.design.gui.ilable.UILabel; import com.fr.design.i18n.Toolkit; import com.fr.design.layout.FRGUIPaneFactory; import com.fr.design.utils.BrowseUtils; +import com.fr.general.CloudCenter; import com.fr.transaction.Configurations; import com.fr.transaction.WorkerFacade; import java.awt.BorderLayout; @@ -26,6 +27,8 @@ import javax.swing.JPanel; */ public class ExportUniversalPane extends BasicPane { + private static final String HELP_URL = CloudCenter.getInstance().acquireUrlByKind("help.alt_font.zh_CN", "https://help.fanruan.com/finereport/doc-view-4707.html"); + private UICheckBox specialCharacterExport; public ExportUniversalPane() { @@ -46,7 +49,7 @@ public class ExportUniversalPane extends BasicPane { rightLabel.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - BrowseUtils.browser("http://www.baidu.com"); + BrowseUtils.browser(HELP_URL); } }); labelPane.add(centerLabel, BorderLayout.CENTER); From 540480bf660f7d91d0ec689833d9a2707da89651 Mon Sep 17 00:00:00 2001 From: Harrison Date: Thu, 26 May 2022 09:51:02 +0800 Subject: [PATCH 18/85] =?UTF-8?q?feat:=20REPORT-70565=20=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E7=8E=AF=E5=A2=83=E7=9B=91=E6=B5=8B=EF=BC=88jar?= =?UTF-8?q?=E5=8C=85=E5=BC=82=E5=B8=B8=E3=80=81finedb=E3=80=81=E6=9D=80?= =?UTF-8?q?=E6=AF=92=E8=BD=AF=E4=BB=B6=EF=BC=89=20=E5=9F=BA=E6=9C=AC?= =?UTF-8?q?=E5=AE=8C=E6=88=90=E6=89=80=E6=9C=89=E7=9A=84=E4=B8=9A=E5=8A=A1?= =?UTF-8?q?=E9=80=BB=E8=BE=91=E3=80=82=E5=87=86=E5=A4=87=E5=A4=84=E7=90=86?= =?UTF-8?q?=20UI=20=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../base/AbstractExceptionDetector.java | 8 +- .../fr/env/detect/base/DetectorBridge.java | 45 +++-- .../fr/env/detect/base/DetectorConstants.java | 11 ++ .../com/fr/env/detect/base/DetectorUtil.java | 16 ++ .../fr/env/detect/base/ExceptionDetector.java | 4 +- .../fr/env/detect/bean/DetectorResult.java | 40 ++--- .../fr/env/detect/bean/DetectorStatus.java | 22 --- .../com/fr/env/detect/bean/DetectorType.java | 57 +++++- .../fr/env/detect/bean/ExceptionSolution.java | 7 +- .../exception/intelli/package-info.java | 4 + .../fr/env/detect/impl/HsqlDirtyDetector.java | 6 +- .../detect/impl/JarInconsistentDetector.java | 60 +++++-- .../fr/env/detect/impl/JarLackDetector.java | 130 ++++++++++++-- .../impl/converter/ClassConfictConvertor.java | 165 ++++++++++++++++++ .../converter/ClassThrowableConvertor.java | 53 ------ .../impl/converter/FineDbDirtyConverter.java | 70 +++++++- .../impl/converter/FineDbLockedConverter.java | 22 ++- .../converter/FineDbPermissionConverter.java | 22 ++- .../converter/ClassConfictConvertorTest.java | 23 +++ 19 files changed, 605 insertions(+), 160 deletions(-) create mode 100644 designer-base/src/main/java/com/fr/env/detect/base/DetectorConstants.java create mode 100644 designer-base/src/main/java/com/fr/env/detect/base/DetectorUtil.java delete mode 100644 designer-base/src/main/java/com/fr/env/detect/bean/DetectorStatus.java create mode 100644 designer-base/src/main/java/com/fr/env/detect/exception/intelli/package-info.java create mode 100644 designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassConfictConvertor.java delete mode 100644 designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassThrowableConvertor.java create mode 100644 designer-base/src/test/java/com/fr/env/detect/impl/converter/ClassConfictConvertorTest.java diff --git a/designer-base/src/main/java/com/fr/env/detect/base/AbstractExceptionDetector.java b/designer-base/src/main/java/com/fr/env/detect/base/AbstractExceptionDetector.java index 32d54a1d7..5414ba648 100644 --- a/designer-base/src/main/java/com/fr/env/detect/base/AbstractExceptionDetector.java +++ b/designer-base/src/main/java/com/fr/env/detect/base/AbstractExceptionDetector.java @@ -7,13 +7,13 @@ import com.fr.env.detect.bean.DetectorType; **/ public abstract class AbstractExceptionDetector implements ExceptionDetector { - private DetectorType key; + private DetectorType type; - public AbstractExceptionDetector(DetectorType key) { - this.key = key; + public AbstractExceptionDetector(DetectorType type) { + this.type = type; } public DetectorType type() { - return key; + return type; } } diff --git a/designer-base/src/main/java/com/fr/env/detect/base/DetectorBridge.java b/designer-base/src/main/java/com/fr/env/detect/base/DetectorBridge.java index 78b501784..8cbef25ab 100644 --- a/designer-base/src/main/java/com/fr/env/detect/base/DetectorBridge.java +++ b/designer-base/src/main/java/com/fr/env/detect/base/DetectorBridge.java @@ -2,6 +2,13 @@ package com.fr.env.detect.base; import com.fr.env.detect.bean.DetectorResult; import com.fr.env.detect.bean.DetectorType; +import com.fr.env.detect.impl.HsqlDirtyDetector; +import com.fr.env.detect.impl.JarInconsistentDetector; +import com.fr.env.detect.impl.JarLackDetector; +import com.fr.env.detect.impl.converter.ClassConfictConvertor; +import com.fr.env.detect.impl.converter.FineDbDirtyConverter; +import com.fr.env.detect.impl.converter.FineDbLockedConverter; +import com.fr.env.detect.impl.converter.FineDbPermissionConverter; import com.fr.env.detect.thowable.ThrowableLogAppender; import com.fr.value.NotNullLazyValue; import org.jetbrains.annotations.NotNull; @@ -28,8 +35,13 @@ public class DetectorBridge { private final NotNullLazyValue throwableBridge = new NotNullLazyValue() { @Override protected @NotNull ThrowableBridge compute() { - - return new ThrowableBridge(); + + ThrowableBridge bridge = new ThrowableBridge(); + bridge.register(new ClassConfictConvertor()); + bridge.register(new FineDbDirtyConverter()); + bridge.register(new FineDbLockedConverter()); + bridge.register(new FineDbPermissionConverter()); + return bridge; } }; @@ -37,8 +49,12 @@ public class DetectorBridge { @Override protected @NotNull DetectorManager compute() { - - return new DetectorManager(); + + DetectorManager manager = new DetectorManager(); + manager.register(new HsqlDirtyDetector()); + manager.register(new JarInconsistentDetector()); + manager.register(new JarLackDetector()); + return manager; } }; @@ -62,24 +78,25 @@ public class DetectorBridge { } /** - * 执行-检测异常 + * 针对某一项进行检测 + * 主要用于手动检测时 * - * @return 能够检测出的异常情况 + * @param type 检测类型 + * @return 检测结果 */ - public Stream detect() { - - return detectorManager.getValue().detect(); + public Optional detect(DetectorType type) { + + return detectorManager.getValue().detect(type); } /** - * 针对某一项进行检测 + * 执行-检测异常 * - * @param type 检测类型 - * @return 检测结果 + * @return 能够检测出的异常情况 */ - public Optional detect(DetectorType type) { + public Stream detect() { - return detectorManager.getValue().detect(type); + return detectorManager.getValue().detect(); } /** diff --git a/designer-base/src/main/java/com/fr/env/detect/base/DetectorConstants.java b/designer-base/src/main/java/com/fr/env/detect/base/DetectorConstants.java new file mode 100644 index 000000000..fce9d5a88 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/base/DetectorConstants.java @@ -0,0 +1,11 @@ +package com.fr.env.detect.base; + +/** + * created by Harrison on 2022/05/25 + **/ +public class DetectorConstants { + + public static final String JAR_HELP_LINK = ""; + + public static final String FINE_DB_HELP_LINK = ""; +} diff --git a/designer-base/src/main/java/com/fr/env/detect/base/DetectorUtil.java b/designer-base/src/main/java/com/fr/env/detect/base/DetectorUtil.java new file mode 100644 index 000000000..33aa0e98f --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/base/DetectorUtil.java @@ -0,0 +1,16 @@ +package com.fr.env.detect.base; + +import com.fr.general.build.BuildInfo; +import com.fr.stable.StringUtils; + +/** + * created by Harrison on 2022/05/25 + **/ +public class DetectorUtil { + + public static boolean isDesignerJar(BuildInfo info) { + + return StringUtils.contains(info.getJar(), "fine-report-designer"); + } + +} diff --git a/designer-base/src/main/java/com/fr/env/detect/base/ExceptionDetector.java b/designer-base/src/main/java/com/fr/env/detect/base/ExceptionDetector.java index 3d6d8bc4e..c874b1979 100644 --- a/designer-base/src/main/java/com/fr/env/detect/base/ExceptionDetector.java +++ b/designer-base/src/main/java/com/fr/env/detect/base/ExceptionDetector.java @@ -1,7 +1,8 @@ package com.fr.env.detect.base; -import com.fr.env.detect.bean.DetectorType; import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.DetectorType; +import org.jetbrains.annotations.Nullable; /** * created by Harrison on 2022/05/13 @@ -20,6 +21,7 @@ public interface ExceptionDetector { * * @return 结果 */ + @Nullable DetectorResult detect(); } diff --git a/designer-base/src/main/java/com/fr/env/detect/bean/DetectorResult.java b/designer-base/src/main/java/com/fr/env/detect/bean/DetectorResult.java index 09850055d..86d6c0e0c 100644 --- a/designer-base/src/main/java/com/fr/env/detect/bean/DetectorResult.java +++ b/designer-base/src/main/java/com/fr/env/detect/bean/DetectorResult.java @@ -11,29 +11,20 @@ public class DetectorResult { private DetectorType type; - private DetectorStatus status; - private ExceptionTips tips; private ExceptionSolution solution; - private ExceptionLog exceptionLog; - - public DetectorResult(DetectorType type, ExceptionTips tips, ExceptionSolution solution, ExceptionLog exceptionLog) { + public DetectorResult(DetectorType type, ExceptionTips tips, ExceptionSolution solution) { this.type = type; this.tips = tips; this.solution = solution; - this.exceptionLog = exceptionLog; } public DetectorType getType() { return type; } - public DetectorStatus getStatus() { - return status; - } - @Nullable public ExceptionTips getTips() { return tips; @@ -44,11 +35,6 @@ public class DetectorResult { return solution; } - @Nullable - public ExceptionLog getExceptionLog() { - return exceptionLog; - } - public static DetectorResultBuilder builder() { return new DetectorResultBuilder(); @@ -58,11 +44,9 @@ public class DetectorResult { public static final class DetectorResultBuilder { private DetectorType type; - private DetectorStatus status; private ExceptionTips tips; private ExceptionSolution solution; - private ExceptionLog exceptionLog; private DetectorResultBuilder() { } @@ -72,8 +56,10 @@ public class DetectorResult { return this; } - public DetectorResultBuilder withStatus(DetectorStatus status) { - this.status = status; + public DetectorResultBuilder withTips(String message) { + + Message.Simple simple = new Message.Simple(message); + this.tips = new ExceptionTips(simple); return this; } @@ -82,21 +68,21 @@ public class DetectorResult { return this; } - public DetectorResultBuilder withSolution(ExceptionSolution solution) { - this.solution = solution; + public DetectorResultBuilder withSolution(String content, String link) { + + Message.Link message = new Message.Link(content, link); + this.solution = new ExceptionSolution(message, null); return this; } - public DetectorResultBuilder withExceptionLog(ExceptionLog exceptionLog) { - this.exceptionLog = exceptionLog; + public DetectorResultBuilder withSolution(ExceptionSolution solution) { + this.solution = solution; return this; } public DetectorResult build() { - - DetectorResult detectorResult = new DetectorResult(type, tips, solution, exceptionLog); - detectorResult.status = this.status; - return detectorResult; + + return new DetectorResult(type, tips, solution); } } } diff --git a/designer-base/src/main/java/com/fr/env/detect/bean/DetectorStatus.java b/designer-base/src/main/java/com/fr/env/detect/bean/DetectorStatus.java deleted file mode 100644 index d75a82e56..000000000 --- a/designer-base/src/main/java/com/fr/env/detect/bean/DetectorStatus.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.fr.env.detect.bean; - -/** - * created by Harrison on 2022/05/25 - **/ -public enum DetectorStatus { - - /** - * 成功 - */ - SUCCESS, - - /** - * 失败 - */ - FAILED, - - /** - * 异常 - */ - UNKNOWN -} diff --git a/designer-base/src/main/java/com/fr/env/detect/bean/DetectorType.java b/designer-base/src/main/java/com/fr/env/detect/bean/DetectorType.java index 2bc71e58a..979b35501 100644 --- a/designer-base/src/main/java/com/fr/env/detect/bean/DetectorType.java +++ b/designer-base/src/main/java/com/fr/env/detect/bean/DetectorType.java @@ -2,6 +2,8 @@ package com.fr.env.detect.bean; /** * 检测的原生数据 + * 其实这里可以继续拆分到不同的逻辑下面的, 比如实际生成 DetectorResult 的地方 {@link com.fr.env.detect.base.ExceptionDetector}。 + * 不过目前没有必要。先这样处理。 * * created by Harrison on 2022/05/13 **/ @@ -10,43 +12,70 @@ public enum DetectorType { /** * 缺少 JAR */ - LACK_OF_JAR(Kind.JAR, WorkType.LOCAL), + LACK_OF_JAR(Kind.JAR, WorkType.LOCAL, + "Fine_Design_Basic_Jar_Lack", + "Fine_Design_Basic_Jar_Solution", + "Fine_Design_Basic_Log_Jar_Lack"), + /** * JAR 包版本不一致 */ - JAR_IN_CONSISTENCE(Kind.JAR, WorkType.SIMPLE), + JAR_IN_CONSISTENCE(Kind.JAR, WorkType.SIMPLE, + "Fine_Design_Basic_Jar_InConsistent", + "Fine_Design_Basic_Jar_Solution", + "Fine_Design_Basic_Log_Jar_InConsistent"), /** * JAR 包冲突 */ - JAR_CONFLICT(Kind.JAR, WorkType.REMOTE), - + JAR_CONFLICT(Kind.JAR, WorkType.REMOTE, + "Fine_Design_Basic_Jar_Problem", + "Fine_Design_Basic_Jar_Solution", + "Fine_Design_Basic_Log_Jar_Problem"), /** * FineDB 权限问题 */ - FINE_DB_PERMISSION(Kind.FINE_DB, WorkType.LOCAL), + FINE_DB_PERMISSION(Kind.FINE_DB, WorkType.LOCAL, + "Fine_Design_Basic_FineDB_Permission_Occupied", + "Fine_Design_Basic_FineDB_Solution", + "Fine_Design_Basic_Log_FineDB_Permission_Occupied"), /** * FineDB 锁 */ - FINE_DB_LOCKED(Kind.FINE_DB, WorkType.LOCAL), + FINE_DB_LOCKED(Kind.FINE_DB, WorkType.LOCAL, + "Fine_Design_Basic_FineDB_Locked", + "Fine_Design_Basic_FineDB_Solution", + "Fine_Design_Basic_Log_FineDB_Locked"), /** * FineDB 脏数据 */ - FINE_DB_DIRTY(Kind.FINE_DB, WorkType.SIMPLE) + FINE_DB_DIRTY(Kind.FINE_DB, WorkType.SIMPLE, + "Fine_Design_Basic_FineDB_Dirty_Data", + "Fine_Design_Basic_FineDB_Dirty_Solution", + "Fine_Design_Basic_Log_FineDB_Dirty_Data") ; private final Kind kind; private final WorkType workType; - DetectorType(Kind kind, WorkType workType) { + private final String tipsLocale; + + private final String solutionLocale; + + private final String logLocale; + + DetectorType(Kind kind, WorkType workType, String tipsLocale, String solutionLocale, String logLocale) { this.kind = kind; this.workType = workType; + this.tipsLocale = tipsLocale; + this.solutionLocale = solutionLocale; + this.logLocale = logLocale; } public Kind getKind() { @@ -57,6 +86,18 @@ public enum DetectorType { return workType; } + public String getTipsLocale() { + return tipsLocale; + } + + public String getSolutionLocale() { + return solutionLocale; + } + + public String getLogLocale() { + return logLocale; + } + public enum Kind { /** diff --git a/designer-base/src/main/java/com/fr/env/detect/bean/ExceptionSolution.java b/designer-base/src/main/java/com/fr/env/detect/bean/ExceptionSolution.java index a6748e26c..796d78c8d 100644 --- a/designer-base/src/main/java/com/fr/env/detect/bean/ExceptionSolution.java +++ b/designer-base/src/main/java/com/fr/env/detect/bean/ExceptionSolution.java @@ -1,5 +1,7 @@ package com.fr.env.detect.bean; +import org.jetbrains.annotations.Nullable; + /** * created by Harrison on 2022/05/13 **/ @@ -7,9 +9,10 @@ public class ExceptionSolution { private Message message; + @Nullable private SolutionAction action; - public ExceptionSolution(Message message, SolutionAction action) { + public ExceptionSolution(Message message, @Nullable SolutionAction action) { this.message = message; this.action = action; } @@ -18,7 +21,7 @@ public class ExceptionSolution { return message; } - public SolutionAction getAction() { + public @Nullable SolutionAction getAction() { return action; } } diff --git a/designer-base/src/main/java/com/fr/env/detect/exception/intelli/package-info.java b/designer-base/src/main/java/com/fr/env/detect/exception/intelli/package-info.java new file mode 100644 index 000000000..f11ad3f7e --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/exception/intelli/package-info.java @@ -0,0 +1,4 @@ +/** + * 国际化相关的异常 + */ +package com.fr.env.detect.exception.intelli; \ No newline at end of file diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/HsqlDirtyDetector.java b/designer-base/src/main/java/com/fr/env/detect/impl/HsqlDirtyDetector.java index 6afc41a66..4d3645858 100644 --- a/designer-base/src/main/java/com/fr/env/detect/impl/HsqlDirtyDetector.java +++ b/designer-base/src/main/java/com/fr/env/detect/impl/HsqlDirtyDetector.java @@ -2,14 +2,18 @@ package com.fr.env.detect.impl; import com.fr.env.detect.base.CatchExceptionDetector; import com.fr.env.detect.bean.DetectorType; -import com.fr.env.detect.thowable.ThrowableStore; import com.fr.env.detect.impl.converter.FineDbDirtyConverter; +import com.fr.env.detect.thowable.ThrowableStore; /** * created by Harrison on 2022/05/25 **/ public class HsqlDirtyDetector extends CatchExceptionDetector { + public HsqlDirtyDetector() { + this(ThrowableStore.getInstance()); + } + public HsqlDirtyDetector(ThrowableStore throwableStore) { super(DetectorType.FINE_DB_DIRTY, throwableStore, new FineDbDirtyConverter()); diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/JarInconsistentDetector.java b/designer-base/src/main/java/com/fr/env/detect/impl/JarInconsistentDetector.java index 0967697f6..4a3e4aafa 100644 --- a/designer-base/src/main/java/com/fr/env/detect/impl/JarInconsistentDetector.java +++ b/designer-base/src/main/java/com/fr/env/detect/impl/JarInconsistentDetector.java @@ -1,20 +1,30 @@ package com.fr.env.detect.impl; +import com.fr.common.util.Collections; +import com.fr.design.i18n.Toolkit; import com.fr.env.detect.base.AbstractExceptionDetector; +import com.fr.env.detect.base.DetectorConstants; +import com.fr.env.detect.base.DetectorUtil; import com.fr.env.detect.bean.DetectorResult; import com.fr.env.detect.bean.DetectorType; +import com.fr.env.detect.bean.ExceptionSolution; +import com.fr.env.detect.bean.ExceptionTips; +import com.fr.env.detect.bean.Message; import com.fr.env.detect.exception.DetectorException; import com.fr.general.build.BuildInfo; -import com.fr.general.build.BuildInfoOperator; import com.fr.general.build.BuildInfoManager; -import com.fr.stable.StringUtils; +import com.fr.general.build.BuildInfoOperator; +import com.fr.general.build.impl.BuildInfoOperatorImpl; +import com.fr.log.FineLoggerFactory; import com.fr.third.guava.collect.MapDifference; import com.fr.third.guava.collect.Maps; +import com.fr.third.org.apache.commons.lang3.StringUtils; import com.fr.workspace.WorkContext; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; /** @@ -31,14 +41,15 @@ public class JarInconsistentDetector extends AbstractExceptionDetector { public DetectorResult detect() { DetectorResult.DetectorResultBuilder builder = DetectorResult.builder(); - // 检测有哪些 JAR 包, 当前是否缺少对应的 JAR 包 - BuildInfoOperator buildInfoOperator = WorkContext.getCurrent().get(BuildInfoOperator.class); - List buildInfos = buildInfoOperator.getBuildInfos(); if (WorkContext.getCurrent().isLocal()) { + // 本地的获取方式 + BuildInfoOperator operator = new BuildInfoOperatorImpl(); + List buildInfos = operator.getBuildInfos(); + String designerBuild = buildInfos.stream() - .filter(this::isDesignerJar) + .filter(DetectorUtil::isDesignerJar) .map(BuildInfo::getGroupBuild) .filter(StringUtils::isNotEmpty) .findFirst() @@ -48,8 +59,27 @@ public class JarInconsistentDetector extends AbstractExceptionDetector { .filter((e) -> !StringUtils.equals(designerBuild, e.getGroupBuild())) .collect(Collectors.toList()); + if (Collections.isEmpty(inConsistentInfos)) { + return null; + } + + List inConsistentJars = inConsistentInfos.stream() + .map(BuildInfo::getJar) + .collect(Collectors.toList()); + String message = StringUtils.join(",", inConsistentJars); + + String tipsMessage = Toolkit.i18nText("Fine_Design_Basic_Detect_Local") + Toolkit.i18nText(type().getTipsLocale()) + "\n" + message; + builder.withTips(new ExceptionTips(new Message.Simple(tipsMessage))) + .withSolution(new ExceptionSolution(new Message.Link(Toolkit.i18nText(type().getSolutionLocale()), DetectorConstants.JAR_HELP_LINK), null)); + + FineLoggerFactory.getLogger().error(type().getLogLocale() + message); + } else { + // 检测有哪些 JAR 包, 当前是否缺少对应的 JAR 包 + BuildInfoOperator buildInfoOperator = WorkContext.getCurrent().get(BuildInfoOperator.class); + List buildInfos = buildInfoOperator.getBuildInfos(); + // 远程情况 List localInfos = BuildInfoManager.getInstance().getInfos(); Map localMap = groupBy(localInfos); @@ -58,19 +88,25 @@ public class JarInconsistentDetector extends AbstractExceptionDetector { Map remoteMap = groupBy(remoteInfos); MapDifference difference = Maps.difference(localMap, remoteMap); - // 两边都有的 JAR 的不同之处 Map diffInCommon = difference.entriesInCommon(); + + if (diffInCommon.isEmpty()) { + return null; + } + + Set inConsistentJars = diffInCommon.keySet(); + String message = StringUtils.join(",", inConsistentJars); + + Message.Simple tipsMessage = new Message.Simple(Toolkit.i18nText("Fine_Design_Basic_Detect_Server") + Toolkit.i18nText(type().getTipsLocale()) + "\n" + message); + builder.withTips(new ExceptionTips(tipsMessage)) + .withSolution(new ExceptionSolution(new Message.Link(Toolkit.i18nText(type().getSolutionLocale()), DetectorConstants.JAR_HELP_LINK), null)); + } return builder.build(); } - private boolean isDesignerJar(BuildInfo info) { - - return StringUtils.contains(info.getJar(), "fine-report-designer"); - } - private Map groupBy(List localInfos) { Map localMap = new HashMap<>(); diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/JarLackDetector.java b/designer-base/src/main/java/com/fr/env/detect/impl/JarLackDetector.java index 86f589f30..ecad03e12 100644 --- a/designer-base/src/main/java/com/fr/env/detect/impl/JarLackDetector.java +++ b/designer-base/src/main/java/com/fr/env/detect/impl/JarLackDetector.java @@ -1,14 +1,32 @@ package com.fr.env.detect.impl; +import com.fr.common.util.Collections; +import com.fr.common.util.Strings; import com.fr.env.detect.base.AbstractExceptionDetector; +import com.fr.env.detect.base.DetectorConstants; +import com.fr.env.detect.base.DetectorUtil; import com.fr.env.detect.bean.DetectorResult; import com.fr.env.detect.bean.DetectorType; +import com.fr.env.detect.bean.ExceptionSolution; +import com.fr.env.detect.bean.ExceptionTips; +import com.fr.env.detect.bean.Message; import com.fr.general.build.BuildInfo; import com.fr.general.build.BuildInfoOperator; -import com.fr.stable.StringUtils; +import com.fr.general.build.impl.BuildInfoOperatorImpl; +import com.fr.locale.InterProviderFactory; +import com.fr.log.FineLoggerFactory; +import com.fr.stable.CommonUtils; +import com.fr.stable.StableUtils; +import com.fr.third.guava.collect.Lists; +import com.fr.third.org.apache.commons.lang3.StringUtils; import com.fr.workspace.WorkContext; +import org.jetbrains.annotations.NotNull; +import java.io.File; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; /** * created by Harrison on 2022/05/24 @@ -16,28 +34,120 @@ import java.util.List; public class JarLackDetector extends AbstractExceptionDetector { public JarLackDetector() { + super(DetectorType.LACK_OF_JAR); } @Override public DetectorResult detect() { + // 不支持远程 + if (!WorkContext.getCurrent().isLocal()) { + return null; + } + DetectorResult.DetectorResultBuilder builder = DetectorResult.builder(); // 检测有哪些 JAR 包, 当前是否缺少对应的 JAR 包 - BuildInfoOperator buildInfoOperator = WorkContext.getCurrent().get(BuildInfoOperator.class); + BuildInfoOperator buildInfoOperator = new BuildInfoOperatorImpl(); List buildInfos = buildInfoOperator.getBuildInfos(); - for (BuildInfo buildInfo : buildInfos) { - String jar = buildInfo.getJar(); - String buildContent = buildInfo.getGroupBuild(); - boolean isLack = StringUtils.isEmpty(buildContent); - if (isLack) { - // 处理信息 - - } + List lackInfos = buildInfos.stream() + .filter(this::isLackInfo) + .collect(Collectors.toList()); + + if (Collections.isEmpty(lackInfos)) { + return null; } + Message message = tipsMessage(lackInfos); + builder.withTips(new ExceptionTips(message)) + .withSolution( + new ExceptionSolution( + new Message.Link(InterProviderFactory.getProvider().getLocText(type().getSolutionLocale()), DetectorConstants.JAR_HELP_LINK), + null)); + + logException(lackInfos); + return builder.build(); + } + + private void logException(List lackInfos) { + + List jarInfos = lackInfos.stream() + .map(BuildInfo::getJar) + .collect(Collectors.toList()); + String message = StringUtils.join(",", jarInfos); + FineLoggerFactory.getLogger().error(type().getLogLocale() + message); + } + + private boolean isLackInfo(BuildInfo e) { + return StringUtils.isNotEmpty(e.getGroupBuild()); + } + + private Message tipsMessage(List infos) { + + String installHome = StableUtils.getInstallHome(); + String homeLibPath = installHome + File.pathSeparator + "lib"; + String webLibPath = CommonUtils.join(new String[]{installHome, "webapps", "webroot", "WEB-INF", "lib"}, File.pathSeparator); + + Map> libMap = groupByPath(infos, homeLibPath, webLibPath); + + StringBuilder sb = new StringBuilder(); + + List homeLibs = libMap.get(homeLibPath); + if (!Collections.isEmpty(homeLibs)) { + sb.append(homeLibPath).append(":"); + sb.append(StringUtils.join("、", homeLibs)); + } + + List webLibs = libMap.get(webLibPath); + if (!Collections.isEmpty(webLibs)) { + if (sb.length() != 0) { + sb.append("\n"); + } + sb.append(Strings.join("、", webLibs)); + } + + String mainContent = sb.toString(); + + DetectorType type = this.type(); + String header = InterProviderFactory.getProvider().getLocText(type.getTipsLocale()); + return new Message.Simple(header + "\n" + mainContent); + } + + @NotNull + private Map> groupByPath(List infos, String homeLibPath, String webLibPath) { + Map> libMap = new HashMap<>(); + for (BuildInfo info : infos) { + String key; + if (inHomeLib(info)) { + key = homeLibPath; + } else { + key = webLibPath; + } + libMap.compute(key, (keyA, value) -> { + if (Collections.isEmpty(value)) { + value = Lists.newArrayList(info.getJar()); + } else { + value.add(info.getJar()); + } + return value; + }); + } + return libMap; } + + /** + * 在 %FR_HOME%\lib + * 否则都是在 %FR_HOME%\webapps\webroot\WEB-INF\lib + * + * @param info JAR 信息 + * @return 是否位于 HOME\LIB + */ + private boolean inHomeLib(BuildInfo info) { + + return DetectorUtil.isDesignerJar(info); + } + } diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassConfictConvertor.java b/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassConfictConvertor.java new file mode 100644 index 000000000..dd26dc50f --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassConfictConvertor.java @@ -0,0 +1,165 @@ +package com.fr.env.detect.impl.converter; + +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.thowable.ThrowableConverter; +import com.fr.stable.resource.ResourceLoader; + +import javax.el.MethodNotFoundException; +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 类抛出异常的转换 + * 原则:可以找不到,但不要误报 + * + * created by Harrison on 2022/05/24 + **/ +public class ClassConfictConvertor implements ThrowableConverter { + + private Map, ClassNameConverter> throwableMap = new HashMap<>(); + + private static final Pattern JAR_PATTERN = Pattern.compile("([\\w+-\\.]*\\.jar)"); + + public ClassConfictConvertor() { + + // 类异常 + this.throwableMap.put(ClassNotFoundException.class, Converter.CLASS); + this.throwableMap.put(NoClassDefFoundError.class, Converter.CLASS); + this.throwableMap.put(ClassCastException.class, Converter.CLASS); + this.throwableMap.put(IncompatibleClassChangeError.class, Converter.CLASS); + + // 方法异常 + this.throwableMap.put(MethodNotFoundException.class, Converter.METHOD); + this.throwableMap.put(NoSuchMethodException.class, Converter.METHOD); + this.throwableMap.put(NoSuchMethodError.class, Converter.METHOD); + } + + @Override + public boolean accept(Throwable throwable) { + + Throwable sign = throwable; + while (sign != null) { + if (throwableMap.containsKey(sign.getClass())) { + return true; + } + sign = sign.getCause(); + } + return false; + } + + @Override + public DetectorResult convert(Throwable throwable) { + + Iterable classNames = Collections.emptyList(); + Throwable sign = throwable; + while (sign != null) { + if (throwableMap.containsKey(sign.getClass())) { + classNames = throwableMap.get(sign.getClass()) + .converter(throwable); + } + sign = sign.getCause(); + } + + for (String className : classNames) { + String classFile = "/" + className.replaceAll("\\.", "/") + ".class"; + try { + Enumeration urls = ResourceLoader.getResources(classFile, this.getClass()); + List urlList = new ArrayList<>(); + while (urls.hasMoreElements()) { + URL url = urls.nextElement(); + urlList.add(url); + } + Set allPath = new HashSet<>(); + for (URL url : urlList) { + String file = url.getFile(); + Matcher matcher = JAR_PATTERN.matcher(url.getFile()); + if (matcher.find()) { + String jar = matcher.group(); + allPath.add(jar); + } else { + boolean containsClasses = file.contains("classes"); + if (containsClasses) { + allPath.add("classes"); + } + } + } + } catch (IOException ignore) { + } + } + return null; + } + + private interface ClassNameConverter { + + /** + * 将异常解析为类名,可能有多个 + * + * @param throwable 异常 + * @return 类名 + */ + Iterable converter(Throwable throwable); + } + + public enum Converter implements ClassNameConverter { + + /** + * 类匹配 + */ + CLASS { + + /** + * 匹配 111.222.333 + * 至少有一个 111. + * 至少有一个 333 + */ + private final Pattern CLASS_PATTERN = Pattern.compile("((\\w+\\.)+(\\w+))"); + + @Override + public Iterable converter(Throwable throwable) { + + String message = throwable.getMessage(); + Matcher matcher = CLASS_PATTERN.matcher(message); + List names = new ArrayList<>(); + while (matcher.find()) { + String className = matcher.group(); + names.add(className); + } + return names; + } + }, + + /** + * 方法匹配 + */ + METHOD { + + /** + * 后面有 .method()xxxx + */ + private final Pattern METHOD_PATTERN = Pattern.compile("((\\w+\\.)+(\\w+))(\\.\\w+\\(\\))"); + + @Override + public Iterable converter(Throwable throwable) { + + String message = throwable.getMessage(); + Matcher matcher = METHOD_PATTERN.matcher(message); + List names = new ArrayList<>(); + while (matcher.find()) { + String className = matcher.group(); + names.add(className); + } + return names; + } + } + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassThrowableConvertor.java b/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassThrowableConvertor.java deleted file mode 100644 index 7fdd8bbe3..000000000 --- a/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassThrowableConvertor.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.fr.env.detect.impl.converter; - -import com.fr.env.detect.bean.DetectorResult; -import com.fr.env.detect.thowable.ThrowableConverter; - -import javax.el.MethodNotFoundException; -import java.util.HashSet; -import java.util.Set; - -/** - * 类抛出异常的转换 - * created by Harrison on 2022/05/24 - **/ -public class ClassThrowableConvertor implements ThrowableConverter { - - private Set> throwableClassSet = new HashSet<>(); - - private Set> throwableMethodSet = new HashSet<>(); - - public ClassThrowableConvertor() { - - // 类异常 - this.throwableClassSet.add(ClassNotFoundException.class); - this.throwableClassSet.add(NoClassDefFoundError.class); - this.throwableClassSet.add(ClassCastException.class); - this.throwableClassSet.add(IncompatibleClassChangeError.class); - - // 方法异常 - this.throwableMethodSet.add(MethodNotFoundException.class); - this.throwableMethodSet.add(NoSuchMethodException.class); - this.throwableMethodSet.add(NoSuchMethodError.class); - } - - @Override - public boolean accept(Throwable throwable) { - - Throwable sign = throwable; - while (sign != null) { - if (throwableClassSet.contains(sign.getClass()) || throwableMethodSet.contains(sign.getClass())) { - return true; - } - sign = sign.getCause(); - } - return false; - } - - @Override - public DetectorResult convert(Throwable throwable) { - - // todo - return null; - } -} diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbDirtyConverter.java b/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbDirtyConverter.java index d23aa9915..962768eb4 100644 --- a/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbDirtyConverter.java +++ b/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbDirtyConverter.java @@ -1,10 +1,23 @@ package com.fr.env.detect.impl.converter; +import com.fr.config.ConfigContext; +import com.fr.config.Configuration; +import com.fr.design.i18n.Toolkit; +import com.fr.env.detect.base.DetectorConstants; import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.DetectorType; +import com.fr.env.detect.bean.ExceptionSolution; +import com.fr.env.detect.bean.Message; +import com.fr.env.detect.bean.SolutionAction; import com.fr.env.detect.thowable.ThrowableConverter; +import com.fr.log.FineLoggerFactory; import com.fr.third.org.hibernate.exception.GenericJDBCException; import org.jetbrains.annotations.Nullable; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + /** * created by Harrison on 2022/05/25 **/ @@ -15,7 +28,7 @@ public class FineDbDirtyConverter implements ThrowableConverter { Throwable sign = throwable; while (sign != null) { - if (sign instanceof GenericJDBCException) { + if (sign.getClass() == GenericJDBCException.class) { return true; } sign = sign.getCause(); @@ -27,7 +40,60 @@ public class FineDbDirtyConverter implements ThrowableConverter { public @Nullable DetectorResult convert(Throwable throwable) { // 检测 Config - // todo + Throwable sign = throwable; + while (sign.getClass() != GenericJDBCException.class) { + sign = sign.getCause(); + } + + Map configMap = getAllConfigurationClassName(); + StackTraceElement[] stackTrace = sign.getStackTrace(); + for (StackTraceElement stackTraceElement : stackTrace) { + String className = stackTraceElement.getClassName(); + if (configMap.containsKey(className)) { + + DetectorType detectorType = DetectorType.FINE_DB_DIRTY; + DetectorResult.DetectorResultBuilder builder = DetectorResult.builder() + .withType(detectorType); + + String tableName = configMap.get(className); + String tipsLocale = Toolkit.i18nText(detectorType.getTipsLocale(), tableName); + + String solutionLocale = detectorType.getSolutionLocale(); + ExceptionSolution exceptionSolution = new ExceptionSolution(new Message.Link(solutionLocale, DetectorConstants.FINE_DB_HELP_LINK), new SolutionAction() { + @Override + public String name() { + // todo + return null; + } + + @Override + public void run() { + + } + }); + builder.withTips(tipsLocale) + .withSolution(exceptionSolution); + + String errorMsg = Toolkit.i18nText(detectorType.getLogLocale(), tableName); + FineLoggerFactory.getLogger().error(errorMsg); + return builder.build(); + } + } + return null; } + + private Map getAllConfigurationClassName() { + + Map configMap = new HashMap<>(); + Iterator tableNames = ConfigContext.getConfigNames(); + while (tableNames.hasNext()) { + String tableName = tableNames.next(); + Class configClass = ConfigContext.getConfigClass(tableName); + String className = configClass.getName(); + configMap.put(className, tableName); + } + return configMap; + + } } diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbLockedConverter.java b/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbLockedConverter.java index 8924d688a..509cb30f3 100644 --- a/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbLockedConverter.java +++ b/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbLockedConverter.java @@ -1,6 +1,9 @@ package com.fr.env.detect.impl.converter; +import com.fr.design.i18n.Toolkit; +import com.fr.env.detect.base.DetectorConstants; import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.DetectorType; import com.fr.env.detect.thowable.ThrowableConverter; import com.fr.third.org.hsqldb.HsqlException; @@ -14,7 +17,7 @@ public class FineDbLockedConverter implements ThrowableConverter { Throwable sign = throwable; while (sign != null) { - if (sign instanceof HsqlException) { + if (sign.getClass() == HsqlException.class) { return true; } sign = sign.getCause(); @@ -25,7 +28,22 @@ public class FineDbLockedConverter implements ThrowableConverter { @Override public DetectorResult convert(Throwable throwable) { - // todo + Throwable sign = throwable; + while (sign.getClass() != HsqlException.class) { + sign = sign.getCause(); + } + + String message = sign.getMessage(); + if (message.contains("lock")) { + DetectorType type = DetectorType.FINE_DB_LOCKED; + String tipsLocale = type.getTipsLocale(); + + return DetectorResult.builder() + .withType(type) + .withTips(Toolkit.i18nText(tipsLocale)) + .withSolution(Toolkit.i18nText(type.getSolutionLocale()), DetectorConstants.FINE_DB_HELP_LINK) + .build(); + } return null; } } diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbPermissionConverter.java b/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbPermissionConverter.java index 21722ebb4..7a7407ea9 100644 --- a/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbPermissionConverter.java +++ b/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbPermissionConverter.java @@ -1,6 +1,9 @@ package com.fr.env.detect.impl.converter; +import com.fr.design.i18n.Toolkit; +import com.fr.env.detect.base.DetectorConstants; import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.DetectorType; import com.fr.env.detect.thowable.ThrowableConverter; import com.fr.third.org.hsqldb.HsqlException; @@ -16,7 +19,7 @@ public class FineDbPermissionConverter implements ThrowableConverter { Throwable sign = throwable; while (sign != null) { - if (sign instanceof HsqlException) { + if (sign.getClass() == HsqlException.class) { return true; } sign = sign.getCause(); @@ -27,7 +30,22 @@ public class FineDbPermissionConverter implements ThrowableConverter { @Override public DetectorResult convert(Throwable throwable) { - // todo + Throwable sign = throwable; + while (sign.getClass() != HsqlException.class) { + sign = sign.getCause(); + } + + String message = sign.getMessage(); + if (message.contains("permission")) { + DetectorType type = DetectorType.FINE_DB_PERMISSION; + String tipsLocale = type.getTipsLocale(); + + return DetectorResult.builder() + .withType(type) + .withTips(Toolkit.i18nText(tipsLocale)) + .withSolution(Toolkit.i18nText(type.getSolutionLocale()), DetectorConstants.FINE_DB_HELP_LINK) + .build(); + } return null; } } diff --git a/designer-base/src/test/java/com/fr/env/detect/impl/converter/ClassConfictConvertorTest.java b/designer-base/src/test/java/com/fr/env/detect/impl/converter/ClassConfictConvertorTest.java new file mode 100644 index 000000000..bf43c5560 --- /dev/null +++ b/designer-base/src/test/java/com/fr/env/detect/impl/converter/ClassConfictConvertorTest.java @@ -0,0 +1,23 @@ +package com.fr.env.detect.impl.converter; + +import org.junit.Test; + +public class ClassConfictConvertorTest { + + @Test + public void testInnerFinder() { + + ClassNotFoundException ex1 = new ClassNotFoundException("Class 111.222.333 not found"); + Iterable names = ClassConfictConvertor.Converter.CLASS.converter(ex1); + + System.out.println(); + } + + @Test + public void testConverter() { + + ClassNotFoundException ex1 = new ClassNotFoundException("com.zaxxer.hikari.HikariConfig"); + ClassConfictConvertor convertor = new ClassConfictConvertor(); + convertor.convert(ex1); + } +} \ No newline at end of file From c96607630031c702830d0bd2c9ff385673632847 Mon Sep 17 00:00:00 2001 From: Harrison Date: Fri, 27 May 2022 15:21:31 +0800 Subject: [PATCH 19/85] =?UTF-8?q?feat:=20REPORT-70565=20=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E7=8E=AF=E5=A2=83=E7=9B=91=E6=B5=8B=EF=BC=88jar?= =?UTF-8?q?=E5=8C=85=E5=BC=82=E5=B8=B8=E3=80=81finedb=E3=80=81=E6=9D=80?= =?UTF-8?q?=E6=AF=92=E8=BD=AF=E4=BB=B6=EF=BC=89=20=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E6=A0=B8=E5=BF=83=E9=80=BB=E8=BE=91=E3=80=82=20=E9=81=87?= =?UTF-8?q?=E5=88=B0=E4=B8=8D=E6=98=8E=E7=A1=AE=E7=9A=84=E5=BC=82=E5=B8=B8?= =?UTF-8?q?=EF=BC=8C=20=E5=88=99=E5=B0=86=E5=BC=82=E5=B8=B8=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E8=BF=9B=E6=9D=A5=E5=90=8E=EF=BC=8C=E8=BF=9B=E8=A1=8C?= =?UTF-8?q?=E5=8C=B9=E9=85=8D=E6=A3=80=E6=B5=8B=20=E9=81=87=E5=88=B0?= =?UTF-8?q?=E6=98=8E=E7=A1=AE=E7=9A=84=E5=BC=82=E5=B8=B8=EF=BC=8C=E9=80=9A?= =?UTF-8?q?=E8=BF=87=20DetectorType=20=E8=BF=9B=E8=A1=8C=E5=8C=B9=E9=85=8D?= =?UTF-8?q?=E5=A4=84=E7=90=86=20=E7=BB=9F=E4=B8=80=E6=A3=80=E6=B5=8B?= =?UTF-8?q?=E6=97=B6=EF=BC=8C=E7=9B=B4=E6=8E=A5=E8=B0=83=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detect/base/CatchExceptionDetector.java | 17 +- .../fr/env/detect/base/DetectorBridge.java | 48 +- .../fr/env/detect/base/DetectorManager.java | 15 +- .../fr/env/detect/base/ExceptionDetector.java | 2 - .../fr/env/detect/bean/DetectorResult.java | 81 ++- .../fr/env/detect/bean/DetectorStatus.java | 17 + .../com/fr/env/detect/bean/DetectorType.java | 36 +- .../com/fr/env/detect/bean/ExceptionLog.java | 30 +- .../java/com/fr/env/detect/bean/Message.java | 15 +- ...Detector.java => FineDbDirtyDetector.java} | 6 +- .../env/detect/impl/FineDbLockedDetector.java | 16 + .../detect/impl/FineDbPermissionDetector.java | 15 + .../env/detect/impl/JarConflictDetector.java | 15 + .../detect/impl/JarInconsistentDetector.java | 51 +- .../fr/env/detect/impl/JarLackDetector.java | 27 +- .../impl/converter/ClassConfictConvertor.java | 1 + .../impl/converter/FineDbDirtyConverter.java | 16 +- .../impl/converter/FineDbLockedConverter.java | 13 +- .../converter/FineDbPermissionConverter.java | 8 +- .../fr/env/detect/ui/EnvDetectorDialog.java | 595 ++++++++++++++++++ 20 files changed, 916 insertions(+), 108 deletions(-) create mode 100644 designer-base/src/main/java/com/fr/env/detect/bean/DetectorStatus.java rename designer-base/src/main/java/com/fr/env/detect/impl/{HsqlDirtyDetector.java => FineDbDirtyDetector.java} (73%) create mode 100644 designer-base/src/main/java/com/fr/env/detect/impl/FineDbLockedDetector.java create mode 100644 designer-base/src/main/java/com/fr/env/detect/impl/FineDbPermissionDetector.java create mode 100644 designer-base/src/main/java/com/fr/env/detect/impl/JarConflictDetector.java create mode 100644 designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java diff --git a/designer-base/src/main/java/com/fr/env/detect/base/CatchExceptionDetector.java b/designer-base/src/main/java/com/fr/env/detect/base/CatchExceptionDetector.java index b2e60d42c..3eeac6e46 100644 --- a/designer-base/src/main/java/com/fr/env/detect/base/CatchExceptionDetector.java +++ b/designer-base/src/main/java/com/fr/env/detect/base/CatchExceptionDetector.java @@ -16,8 +16,14 @@ public abstract class CatchExceptionDetector extends AbstractExceptionDetector { private ThrowableConverter throwableConverter; - public CatchExceptionDetector(DetectorType key, ThrowableStore throwableStore, ThrowableConverter throwableConverter) { - super(key); + public CatchExceptionDetector(DetectorType type, ThrowableConverter throwableConverter) { + super(type); + this.throwableStore = ThrowableStore.getInstance(); + this.throwableConverter = throwableConverter; + } + + public CatchExceptionDetector(DetectorType type, ThrowableStore throwableStore, ThrowableConverter throwableConverter) { + super(type); this.throwableStore = throwableStore; this.throwableConverter = throwableConverter; } @@ -36,7 +42,12 @@ public abstract class CatchExceptionDetector extends AbstractExceptionDetector { List throwableList = throwableStore.getAll(); for (Throwable throwable : throwableList) { if (throwableConverter.accept(throwable)) { - return throwableConverter.convert(throwable); + + DetectorResult result = throwableConverter.convert(throwable); + if (result == null) { + result = DetectorResult.normal(type()); + } + return result; } } return null; diff --git a/designer-base/src/main/java/com/fr/env/detect/base/DetectorBridge.java b/designer-base/src/main/java/com/fr/env/detect/base/DetectorBridge.java index 8cbef25ab..031df59a2 100644 --- a/designer-base/src/main/java/com/fr/env/detect/base/DetectorBridge.java +++ b/designer-base/src/main/java/com/fr/env/detect/base/DetectorBridge.java @@ -2,14 +2,14 @@ package com.fr.env.detect.base; import com.fr.env.detect.bean.DetectorResult; import com.fr.env.detect.bean.DetectorType; -import com.fr.env.detect.impl.HsqlDirtyDetector; +import com.fr.env.detect.impl.FineDbDirtyDetector; +import com.fr.env.detect.impl.FineDbLockedDetector; +import com.fr.env.detect.impl.FineDbPermissionDetector; +import com.fr.env.detect.impl.JarConflictDetector; import com.fr.env.detect.impl.JarInconsistentDetector; import com.fr.env.detect.impl.JarLackDetector; -import com.fr.env.detect.impl.converter.ClassConfictConvertor; -import com.fr.env.detect.impl.converter.FineDbDirtyConverter; -import com.fr.env.detect.impl.converter.FineDbLockedConverter; -import com.fr.env.detect.impl.converter.FineDbPermissionConverter; import com.fr.env.detect.thowable.ThrowableLogAppender; +import com.fr.env.detect.thowable.ThrowableStore; import com.fr.value.NotNullLazyValue; import org.jetbrains.annotations.NotNull; @@ -32,28 +32,20 @@ public class DetectorBridge { private static final DetectorBridge INSTANCE = new DetectorBridge(); } - private final NotNullLazyValue throwableBridge = new NotNullLazyValue() { - @Override - protected @NotNull ThrowableBridge compute() { - - ThrowableBridge bridge = new ThrowableBridge(); - bridge.register(new ClassConfictConvertor()); - bridge.register(new FineDbDirtyConverter()); - bridge.register(new FineDbLockedConverter()); - bridge.register(new FineDbPermissionConverter()); - return bridge; - } - }; - private final NotNullLazyValue detectorManager = new NotNullLazyValue() { @Override protected @NotNull DetectorManager compute() { DetectorManager manager = new DetectorManager(); - manager.register(new HsqlDirtyDetector()); + + manager.register(new FineDbDirtyDetector()); + manager.register(new FineDbPermissionDetector()); + manager.register(new FineDbLockedDetector()); + manager.register(new JarInconsistentDetector()); manager.register(new JarLackDetector()); + manager.register(new JarConflictDetector()); return manager; } }; @@ -90,7 +82,8 @@ public class DetectorBridge { } /** - * 执行-检测异常 + * 异常检测 + * 对异常统一检测 * * @return 能够检测出的异常情况 */ @@ -100,13 +93,22 @@ public class DetectorBridge { } /** - * 执行-捕获异常 + * 异常检测 + * 当遇到异常时,且异常难以处理,直接导致服务器启动失败时,调用 + * 将异常添加进来,统一检测 * * @param throwable 异常 * @return 检测结果 */ - public Optional convert(Throwable throwable) { + public Stream detect(Throwable throwable) { + + // 先清理一下 + ThrowableStore.getInstance().reset(); - return throwableBridge.getValue().convert(throwable); + ThrowableStore.getInstance().add(throwable); + Stream result = detect(); + ThrowableStore.getInstance().reset(); + + return result; } } diff --git a/designer-base/src/main/java/com/fr/env/detect/base/DetectorManager.java b/designer-base/src/main/java/com/fr/env/detect/base/DetectorManager.java index 9d3ae2daa..76c2d2dd1 100644 --- a/designer-base/src/main/java/com/fr/env/detect/base/DetectorManager.java +++ b/designer-base/src/main/java/com/fr/env/detect/base/DetectorManager.java @@ -1,7 +1,7 @@ package com.fr.env.detect.base; -import com.fr.env.detect.bean.DetectorType; import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.DetectorType; import java.util.ArrayList; import java.util.List; @@ -25,16 +25,25 @@ class DetectorManager { public Stream detect() { - return detectors.stream() + Stream results = detectors.stream() .map(ExceptionDetector::detect) .filter(Objects::nonNull); + + // 记录一下日志 + results.forEach(DetectorResult::log); + return results; } public Optional detect(DetectorType type) { - return detectors.stream() + Optional result = detectors.stream() .filter((detector -> type == detector.type())) .findFirst() .map(ExceptionDetector::detect); + + // 记录日志 + result.ifPresent(DetectorResult::log); + + return result; } } diff --git a/designer-base/src/main/java/com/fr/env/detect/base/ExceptionDetector.java b/designer-base/src/main/java/com/fr/env/detect/base/ExceptionDetector.java index c874b1979..7713ef739 100644 --- a/designer-base/src/main/java/com/fr/env/detect/base/ExceptionDetector.java +++ b/designer-base/src/main/java/com/fr/env/detect/base/ExceptionDetector.java @@ -2,7 +2,6 @@ package com.fr.env.detect.base; import com.fr.env.detect.bean.DetectorResult; import com.fr.env.detect.bean.DetectorType; -import org.jetbrains.annotations.Nullable; /** * created by Harrison on 2022/05/13 @@ -21,7 +20,6 @@ public interface ExceptionDetector { * * @return 结果 */ - @Nullable DetectorResult detect(); } diff --git a/designer-base/src/main/java/com/fr/env/detect/bean/DetectorResult.java b/designer-base/src/main/java/com/fr/env/detect/bean/DetectorResult.java index 86d6c0e0c..aadccd091 100644 --- a/designer-base/src/main/java/com/fr/env/detect/bean/DetectorResult.java +++ b/designer-base/src/main/java/com/fr/env/detect/bean/DetectorResult.java @@ -1,7 +1,5 @@ package com.fr.env.detect.bean; -import org.jetbrains.annotations.Nullable; - /** * 检测结果 * @@ -11,42 +9,76 @@ public class DetectorResult { private DetectorType type; + private DetectorStatus status; + private ExceptionTips tips; private ExceptionSolution solution; - public DetectorResult(DetectorType type, ExceptionTips tips, ExceptionSolution solution) { + private ExceptionLog log; + + private DetectorResult(DetectorType type) { + this.type = type; + } + + private DetectorResult(DetectorType type, ExceptionTips tips, ExceptionSolution solution, ExceptionLog log) { + this.type = type; this.tips = tips; this.solution = solution; + this.log = log; + } + + public static DetectorResult normal(DetectorType type) { + + DetectorResult result = new DetectorResult(type); + result.status = DetectorStatus.NORMAL; + return result; + } + + public static DetectorResult exception(DetectorType type, ExceptionTips tips, ExceptionSolution solution, ExceptionLog log) { + + DetectorResult result = new DetectorResult(type, tips, solution, log); + result.status = DetectorStatus.EXCEPTION; + return result; + } + + public DetectorStatus getStatus() { + return status; } public DetectorType getType() { return type; } - @Nullable public ExceptionTips getTips() { return tips; } - @Nullable public ExceptionSolution getSolution() { return solution; } - public static DetectorResultBuilder builder() { + public void log() { - return new DetectorResultBuilder(); + if (log != null) { + log.log(); + } } + public static DetectorResultBuilder builder() { + + return new DetectorResultBuilder() + .withStatus(DetectorStatus.EXCEPTION); + } public static final class DetectorResultBuilder { private DetectorType type; - + private DetectorStatus status; private ExceptionTips tips; private ExceptionSolution solution; + private ExceptionLog log; private DetectorResultBuilder() { } @@ -56,6 +88,16 @@ public class DetectorResult { return this; } + public DetectorResultBuilder withStatus(DetectorStatus status) { + this.status = status; + return this; + } + + public DetectorResultBuilder withTips(ExceptionTips tips) { + this.tips = tips; + return this; + } + public DetectorResultBuilder withTips(String message) { Message.Simple simple = new Message.Simple(message); @@ -63,26 +105,33 @@ public class DetectorResult { return this; } - public DetectorResultBuilder withTips(ExceptionTips tips) { - this.tips = tips; + public DetectorResultBuilder withSolution(ExceptionSolution solution) { + this.solution = solution; return this; } - - public DetectorResultBuilder withSolution(String content, String link) { + public DetectorResultBuilder withSolution(String content, String link) { + Message.Link message = new Message.Link(content, link); this.solution = new ExceptionSolution(message, null); return this; } - public DetectorResultBuilder withSolution(ExceptionSolution solution) { - this.solution = solution; + public DetectorResultBuilder withLog(String log, Object... args) { + + this.log = ExceptionLog.create(log, args); + return this; + } + + public DetectorResultBuilder withLog(ExceptionLog log) { + this.log = log; return this; } public DetectorResult build() { - - return new DetectorResult(type, tips, solution); + DetectorResult detectorResult = new DetectorResult(type, tips, solution, log); + detectorResult.status = this.status; + return detectorResult; } } } diff --git a/designer-base/src/main/java/com/fr/env/detect/bean/DetectorStatus.java b/designer-base/src/main/java/com/fr/env/detect/bean/DetectorStatus.java new file mode 100644 index 000000000..6a12015e9 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/bean/DetectorStatus.java @@ -0,0 +1,17 @@ +package com.fr.env.detect.bean; + +/** + * created by Harrison on 2022/05/27 + **/ +public enum DetectorStatus { + + /** + * 正常 + */ + NORMAL, + + /** + * 异常 + */ + EXCEPTION, +} diff --git a/designer-base/src/main/java/com/fr/env/detect/bean/DetectorType.java b/designer-base/src/main/java/com/fr/env/detect/bean/DetectorType.java index 979b35501..1dc8e4d62 100644 --- a/designer-base/src/main/java/com/fr/env/detect/bean/DetectorType.java +++ b/designer-base/src/main/java/com/fr/env/detect/bean/DetectorType.java @@ -1,5 +1,7 @@ package com.fr.env.detect.bean; +import com.fr.design.i18n.Toolkit; + /** * 检测的原生数据 * 其实这里可以继续拆分到不同的逻辑下面的, 比如实际生成 DetectorResult 的地方 {@link com.fr.env.detect.base.ExceptionDetector}。 @@ -13,6 +15,7 @@ public enum DetectorType { * 缺少 JAR */ LACK_OF_JAR(Kind.JAR, WorkType.LOCAL, + "Fine_Design_Basic_Jar_Lacked_Desc", "Fine_Design_Basic_Jar_Lack", "Fine_Design_Basic_Jar_Solution", "Fine_Design_Basic_Log_Jar_Lack"), @@ -22,6 +25,7 @@ public enum DetectorType { * JAR 包版本不一致 */ JAR_IN_CONSISTENCE(Kind.JAR, WorkType.SIMPLE, + "Fine_Design_Basic_Jar_InConsistent_Desc", "Fine_Design_Basic_Jar_InConsistent", "Fine_Design_Basic_Jar_Solution", "Fine_Design_Basic_Log_Jar_InConsistent"), @@ -30,6 +34,7 @@ public enum DetectorType { * JAR 包冲突 */ JAR_CONFLICT(Kind.JAR, WorkType.REMOTE, + "Fine_Design_Basic_Jar_Problem_Desc", "Fine_Design_Basic_Jar_Problem", "Fine_Design_Basic_Jar_Solution", "Fine_Design_Basic_Log_Jar_Problem"), @@ -38,6 +43,7 @@ public enum DetectorType { * FineDB 权限问题 */ FINE_DB_PERMISSION(Kind.FINE_DB, WorkType.LOCAL, + "Fine_Design_Basic_FineDB_Permission_Occupied_Desc", "Fine_Design_Basic_FineDB_Permission_Occupied", "Fine_Design_Basic_FineDB_Solution", "Fine_Design_Basic_Log_FineDB_Permission_Occupied"), @@ -46,6 +52,7 @@ public enum DetectorType { * FineDB 锁 */ FINE_DB_LOCKED(Kind.FINE_DB, WorkType.LOCAL, + "Fine_Design_Basic_FineDB_Locked_Desc", "Fine_Design_Basic_FineDB_Locked", "Fine_Design_Basic_FineDB_Solution", "Fine_Design_Basic_Log_FineDB_Locked"), @@ -54,6 +61,7 @@ public enum DetectorType { * FineDB 脏数据 */ FINE_DB_DIRTY(Kind.FINE_DB, WorkType.SIMPLE, + "Fine_Design_Basic_FineDB_Dirty_Data_Desc", "Fine_Design_Basic_FineDB_Dirty_Data", "Fine_Design_Basic_FineDB_Dirty_Solution", "Fine_Design_Basic_Log_FineDB_Dirty_Data") @@ -63,21 +71,32 @@ public enum DetectorType { private final WorkType workType; + private final String descLocale; + private final String tipsLocale; private final String solutionLocale; private final String logLocale; - DetectorType(Kind kind, WorkType workType, String tipsLocale, String solutionLocale, String logLocale) { + DetectorType(Kind kind, WorkType workType, String descLocale, String tipsLocale, String solutionLocale, String logLocale) { this.kind = kind; this.workType = workType; + this.descLocale = descLocale; this.tipsLocale = tipsLocale; this.solutionLocale = solutionLocale; this.logLocale = logLocale; } + public String getDescription() { + return Toolkit.i18nText(descLocale); + } + + public String getDescLocale() { + return descLocale; + } + public Kind getKind() { return kind; } @@ -103,11 +122,22 @@ public enum DetectorType { /** * JAR 类型 */ - JAR, + JAR("Fine_Design_Basic_Jar_Kind_Desc"), + /** * FineDB 类型 */ - FINE_DB + FINE_DB("Fine_Design_Basic_FineDB_Kind_Desc"); + + private final String locale; + + Kind(String locale) { + this.locale = locale; + } + + public String getDescription() { + return Toolkit.i18nText(this.locale); + } } public enum WorkType { diff --git a/designer-base/src/main/java/com/fr/env/detect/bean/ExceptionLog.java b/designer-base/src/main/java/com/fr/env/detect/bean/ExceptionLog.java index 90cdb58fd..521e90345 100644 --- a/designer-base/src/main/java/com/fr/env/detect/bean/ExceptionLog.java +++ b/designer-base/src/main/java/com/fr/env/detect/bean/ExceptionLog.java @@ -1,24 +1,36 @@ package com.fr.env.detect.bean; +import com.fr.log.FineLoggerFactory; + /** * created by Harrison on 2022/05/13 **/ public class ExceptionLog { - private final String errorCode; + private final String template; + + private final Object[] args; + + private ExceptionLog(String template, Object... args) { + this.template = template; + this.args = args; + } + + public static ExceptionLog create(String template, Object... args) { + + return new ExceptionLog(template, args); + } - private final String locale; + public void log() { - public ExceptionLog(String errorCode, String locale) { - this.errorCode = errorCode; - this.locale = locale; + FineLoggerFactory.getLogger().error(template, args); } - public String getErrorCode() { - return errorCode; + public String getTemplate() { + return template; } - public String getLocale() { - return locale; + public Object[] getArgs() { + return args; } } diff --git a/designer-base/src/main/java/com/fr/env/detect/bean/Message.java b/designer-base/src/main/java/com/fr/env/detect/bean/Message.java index e1d6dcd62..2acdec8b8 100644 --- a/designer-base/src/main/java/com/fr/env/detect/bean/Message.java +++ b/designer-base/src/main/java/com/fr/env/detect/bean/Message.java @@ -12,6 +12,13 @@ public interface Message { */ Type getType(); + /** + * 返回内容 + * + * @return 内容 + */ + String get(); + enum Type { /** @@ -33,7 +40,8 @@ public interface Message { this.message = message; } - public String getMessage() { + @Override + public String get() { return message; } @@ -54,6 +62,11 @@ public interface Message { this.link = link; } + @Override + public String get() { + return getText(); + } + public String getText() { return text; } diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/HsqlDirtyDetector.java b/designer-base/src/main/java/com/fr/env/detect/impl/FineDbDirtyDetector.java similarity index 73% rename from designer-base/src/main/java/com/fr/env/detect/impl/HsqlDirtyDetector.java rename to designer-base/src/main/java/com/fr/env/detect/impl/FineDbDirtyDetector.java index 4d3645858..1b3459786 100644 --- a/designer-base/src/main/java/com/fr/env/detect/impl/HsqlDirtyDetector.java +++ b/designer-base/src/main/java/com/fr/env/detect/impl/FineDbDirtyDetector.java @@ -8,13 +8,13 @@ import com.fr.env.detect.thowable.ThrowableStore; /** * created by Harrison on 2022/05/25 **/ -public class HsqlDirtyDetector extends CatchExceptionDetector { +public class FineDbDirtyDetector extends CatchExceptionDetector { - public HsqlDirtyDetector() { + public FineDbDirtyDetector() { this(ThrowableStore.getInstance()); } - public HsqlDirtyDetector(ThrowableStore throwableStore) { + public FineDbDirtyDetector(ThrowableStore throwableStore) { super(DetectorType.FINE_DB_DIRTY, throwableStore, new FineDbDirtyConverter()); } diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/FineDbLockedDetector.java b/designer-base/src/main/java/com/fr/env/detect/impl/FineDbLockedDetector.java new file mode 100644 index 000000000..c312ce328 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/impl/FineDbLockedDetector.java @@ -0,0 +1,16 @@ +package com.fr.env.detect.impl; + +import com.fr.env.detect.base.CatchExceptionDetector; +import com.fr.env.detect.bean.DetectorType; +import com.fr.env.detect.impl.converter.FineDbLockedConverter; + +/** + * created by Harrison on 2022/05/26 + **/ +public class FineDbLockedDetector extends CatchExceptionDetector { + + public FineDbLockedDetector() { + super(DetectorType.FINE_DB_LOCKED, new FineDbLockedConverter()); + } + +} diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/FineDbPermissionDetector.java b/designer-base/src/main/java/com/fr/env/detect/impl/FineDbPermissionDetector.java new file mode 100644 index 000000000..4e3a18ced --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/impl/FineDbPermissionDetector.java @@ -0,0 +1,15 @@ +package com.fr.env.detect.impl; + +import com.fr.env.detect.base.CatchExceptionDetector; +import com.fr.env.detect.bean.DetectorType; +import com.fr.env.detect.impl.converter.FineDbPermissionConverter; + +/** + * created by Harrison on 2022/05/26 + **/ +public class FineDbPermissionDetector extends CatchExceptionDetector { + + public FineDbPermissionDetector() { + super(DetectorType.FINE_DB_PERMISSION, new FineDbPermissionConverter()); + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/JarConflictDetector.java b/designer-base/src/main/java/com/fr/env/detect/impl/JarConflictDetector.java new file mode 100644 index 000000000..39a3bc4e0 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/impl/JarConflictDetector.java @@ -0,0 +1,15 @@ +package com.fr.env.detect.impl; + +import com.fr.env.detect.base.CatchExceptionDetector; +import com.fr.env.detect.bean.DetectorType; +import com.fr.env.detect.impl.converter.ClassConfictConvertor; + +/** + * created by Harrison on 2022/05/26 + **/ +public class JarConflictDetector extends CatchExceptionDetector { + + public JarConflictDetector() { + super(DetectorType.JAR_CONFLICT, new ClassConfictConvertor()); + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/JarInconsistentDetector.java b/designer-base/src/main/java/com/fr/env/detect/impl/JarInconsistentDetector.java index 4a3e4aafa..dfb880a9e 100644 --- a/designer-base/src/main/java/com/fr/env/detect/impl/JarInconsistentDetector.java +++ b/designer-base/src/main/java/com/fr/env/detect/impl/JarInconsistentDetector.java @@ -7,15 +7,14 @@ import com.fr.env.detect.base.DetectorConstants; import com.fr.env.detect.base.DetectorUtil; import com.fr.env.detect.bean.DetectorResult; import com.fr.env.detect.bean.DetectorType; +import com.fr.env.detect.bean.ExceptionLog; import com.fr.env.detect.bean.ExceptionSolution; import com.fr.env.detect.bean.ExceptionTips; import com.fr.env.detect.bean.Message; -import com.fr.env.detect.exception.DetectorException; import com.fr.general.build.BuildInfo; import com.fr.general.build.BuildInfoManager; import com.fr.general.build.BuildInfoOperator; import com.fr.general.build.impl.BuildInfoOperatorImpl; -import com.fr.log.FineLoggerFactory; import com.fr.third.guava.collect.MapDifference; import com.fr.third.guava.collect.Maps; import com.fr.third.org.apache.commons.lang3.StringUtils; @@ -24,6 +23,7 @@ import com.fr.workspace.WorkContext; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -37,42 +37,57 @@ public class JarInconsistentDetector extends AbstractExceptionDetector { super(DetectorType.JAR_IN_CONSISTENCE); } + /** + * 本地 + * 先获取 Designer, 然后对其他的 JAR 信息匹配 + *

+ * 远程 + * 两边一一对应匹配 + * + * @return 结果 + */ @Override public DetectorResult detect() { - DetectorResult.DetectorResultBuilder builder = DetectorResult.builder(); - if (WorkContext.getCurrent().isLocal()) { // 本地的获取方式 BuildInfoOperator operator = new BuildInfoOperatorImpl(); List buildInfos = operator.getBuildInfos(); - String designerBuild = buildInfos.stream() + // 获取设计器的 build + Optional designerBuild = buildInfos.stream() .filter(DetectorUtil::isDesignerJar) .map(BuildInfo::getGroupBuild) .filter(StringUtils::isNotEmpty) - .findFirst() - .orElseThrow(DetectorException::new); + .findFirst(); + + // 如果 build + if (!designerBuild.isPresent()) { + return DetectorResult.normal(type()); + } + // 获取所有的不一致的 build List inConsistentInfos = buildInfos.stream() - .filter((e) -> !StringUtils.equals(designerBuild, e.getGroupBuild())) + .filter((e) -> !StringUtils.equals(designerBuild.get(), e.getGroupBuild())) .collect(Collectors.toList()); + // 没有直接返回 if (Collections.isEmpty(inConsistentInfos)) { - return null; + return DetectorResult.normal(type()); } + // 有的话 List inConsistentJars = inConsistentInfos.stream() .map(BuildInfo::getJar) .collect(Collectors.toList()); String message = StringUtils.join(",", inConsistentJars); - String tipsMessage = Toolkit.i18nText("Fine_Design_Basic_Detect_Local") + Toolkit.i18nText(type().getTipsLocale()) + "\n" + message; - builder.withTips(new ExceptionTips(new Message.Simple(tipsMessage))) - .withSolution(new ExceptionSolution(new Message.Link(Toolkit.i18nText(type().getSolutionLocale()), DetectorConstants.JAR_HELP_LINK), null)); - FineLoggerFactory.getLogger().error(type().getLogLocale() + message); + return DetectorResult.exception(type(), + new ExceptionTips(new Message.Simple(tipsMessage)), + new ExceptionSolution(new Message.Link(Toolkit.i18nText(type().getSolutionLocale()), DetectorConstants.JAR_HELP_LINK), null), + ExceptionLog.create(type().getLogLocale() + message)); } else { @@ -91,20 +106,20 @@ public class JarInconsistentDetector extends AbstractExceptionDetector { Map diffInCommon = difference.entriesInCommon(); if (diffInCommon.isEmpty()) { - return null; + return DetectorResult.normal(type()); } Set inConsistentJars = diffInCommon.keySet(); String message = StringUtils.join(",", inConsistentJars); - Message.Simple tipsMessage = new Message.Simple(Toolkit.i18nText("Fine_Design_Basic_Detect_Server") + Toolkit.i18nText(type().getTipsLocale()) + "\n" + message); - builder.withTips(new ExceptionTips(tipsMessage)) - .withSolution(new ExceptionSolution(new Message.Link(Toolkit.i18nText(type().getSolutionLocale()), DetectorConstants.JAR_HELP_LINK), null)); + return DetectorResult.exception(type(), + new ExceptionTips(tipsMessage), + new ExceptionSolution(new Message.Link(Toolkit.i18nText(type().getSolutionLocale()), DetectorConstants.JAR_HELP_LINK), null), + ExceptionLog.create(type().getLogLocale() + message)); } - return builder.build(); } private Map groupBy(List localInfos) { diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/JarLackDetector.java b/designer-base/src/main/java/com/fr/env/detect/impl/JarLackDetector.java index ecad03e12..654029da7 100644 --- a/designer-base/src/main/java/com/fr/env/detect/impl/JarLackDetector.java +++ b/designer-base/src/main/java/com/fr/env/detect/impl/JarLackDetector.java @@ -7,6 +7,7 @@ import com.fr.env.detect.base.DetectorConstants; import com.fr.env.detect.base.DetectorUtil; import com.fr.env.detect.bean.DetectorResult; import com.fr.env.detect.bean.DetectorType; +import com.fr.env.detect.bean.ExceptionLog; import com.fr.env.detect.bean.ExceptionSolution; import com.fr.env.detect.bean.ExceptionTips; import com.fr.env.detect.bean.Message; @@ -14,7 +15,6 @@ import com.fr.general.build.BuildInfo; import com.fr.general.build.BuildInfoOperator; import com.fr.general.build.impl.BuildInfoOperatorImpl; import com.fr.locale.InterProviderFactory; -import com.fr.log.FineLoggerFactory; import com.fr.stable.CommonUtils; import com.fr.stable.StableUtils; import com.fr.third.guava.collect.Lists; @@ -43,11 +43,9 @@ public class JarLackDetector extends AbstractExceptionDetector { // 不支持远程 if (!WorkContext.getCurrent().isLocal()) { - return null; + return DetectorResult.normal(type()); } - DetectorResult.DetectorResultBuilder builder = DetectorResult.builder(); - // 检测有哪些 JAR 包, 当前是否缺少对应的 JAR 包 BuildInfoOperator buildInfoOperator = new BuildInfoOperatorImpl(); List buildInfos = buildInfoOperator.getBuildInfos(); @@ -56,28 +54,25 @@ public class JarLackDetector extends AbstractExceptionDetector { .collect(Collectors.toList()); if (Collections.isEmpty(lackInfos)) { - return null; + return DetectorResult.normal(type()); } - Message message = tipsMessage(lackInfos); - builder.withTips(new ExceptionTips(message)) - .withSolution( - new ExceptionSolution( - new Message.Link(InterProviderFactory.getProvider().getLocText(type().getSolutionLocale()), DetectorConstants.JAR_HELP_LINK), - null)); - - logException(lackInfos); + Message tipsMsg = tipsMessage(lackInfos); + ExceptionLog exceptionLog = exceptionLog(lackInfos); - return builder.build(); + return DetectorResult.exception(type(), new ExceptionTips(tipsMsg), + new ExceptionSolution( + new Message.Link(InterProviderFactory.getProvider().getLocText(type().getSolutionLocale()), DetectorConstants.JAR_HELP_LINK), + null), exceptionLog); } - private void logException(List lackInfos) { + private ExceptionLog exceptionLog(List lackInfos) { List jarInfos = lackInfos.stream() .map(BuildInfo::getJar) .collect(Collectors.toList()); String message = StringUtils.join(",", jarInfos); - FineLoggerFactory.getLogger().error(type().getLogLocale() + message); + return ExceptionLog.create(type().getLogLocale() + message); } private boolean isLackInfo(BuildInfo e) { diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassConfictConvertor.java b/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassConfictConvertor.java index dd26dc50f..f2c8c5b75 100644 --- a/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassConfictConvertor.java +++ b/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassConfictConvertor.java @@ -93,6 +93,7 @@ public class ClassConfictConvertor implements ThrowableConverter { } } } + } catch (IOException ignore) { } } diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbDirtyConverter.java b/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbDirtyConverter.java index 962768eb4..f61d16913 100644 --- a/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbDirtyConverter.java +++ b/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbDirtyConverter.java @@ -10,7 +10,6 @@ import com.fr.env.detect.bean.ExceptionSolution; import com.fr.env.detect.bean.Message; import com.fr.env.detect.bean.SolutionAction; import com.fr.env.detect.thowable.ThrowableConverter; -import com.fr.log.FineLoggerFactory; import com.fr.third.org.hibernate.exception.GenericJDBCException; import org.jetbrains.annotations.Nullable; @@ -19,6 +18,8 @@ import java.util.Iterator; import java.util.Map; /** + * 脏数据检测 + * * created by Harrison on 2022/05/25 **/ public class FineDbDirtyConverter implements ThrowableConverter { @@ -36,6 +37,14 @@ public class FineDbDirtyConverter implements ThrowableConverter { return false; } + /** + * 根据堆栈,确认是否是从配置中发出来的异常 + * 如果是,则找到对应的配置的表, + * 输出信息 + * + * @param throwable 异常 + * @return 检测结果 + */ @Override public @Nullable DetectorResult convert(Throwable throwable) { @@ -72,10 +81,9 @@ public class FineDbDirtyConverter implements ThrowableConverter { } }); builder.withTips(tipsLocale) - .withSolution(exceptionSolution); + .withSolution(exceptionSolution) + .withLog(Toolkit.i18nText(detectorType.getLogLocale(), tableName)); - String errorMsg = Toolkit.i18nText(detectorType.getLogLocale(), tableName); - FineLoggerFactory.getLogger().error(errorMsg); return builder.build(); } } diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbLockedConverter.java b/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbLockedConverter.java index 509cb30f3..09cc9b62f 100644 --- a/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbLockedConverter.java +++ b/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbLockedConverter.java @@ -25,6 +25,12 @@ public class FineDbLockedConverter implements ThrowableConverter { return false; } + /** + * 检测 FineDB 是否锁住 + * + * @param throwable 异常 + * @return 结果 + */ @Override public DetectorResult convert(Throwable throwable) { @@ -33,15 +39,16 @@ public class FineDbLockedConverter implements ThrowableConverter { sign = sign.getCause(); } + DetectorType type = DetectorType.FINE_DB_LOCKED; + String message = sign.getMessage(); if (message.contains("lock")) { - DetectorType type = DetectorType.FINE_DB_LOCKED; - String tipsLocale = type.getTipsLocale(); return DetectorResult.builder() .withType(type) - .withTips(Toolkit.i18nText(tipsLocale)) + .withTips(Toolkit.i18nText(type.getTipsLocale())) .withSolution(Toolkit.i18nText(type.getSolutionLocale()), DetectorConstants.FINE_DB_HELP_LINK) + .withLog(Toolkit.i18nText(type.getLogLocale())) .build(); } return null; diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbPermissionConverter.java b/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbPermissionConverter.java index 7a7407ea9..8052f8164 100644 --- a/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbPermissionConverter.java +++ b/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbPermissionConverter.java @@ -35,15 +35,15 @@ public class FineDbPermissionConverter implements ThrowableConverter { sign = sign.getCause(); } + DetectorType type = DetectorType.FINE_DB_PERMISSION; String message = sign.getMessage(); if (message.contains("permission")) { - DetectorType type = DetectorType.FINE_DB_PERMISSION; - String tipsLocale = type.getTipsLocale(); - + return DetectorResult.builder() .withType(type) - .withTips(Toolkit.i18nText(tipsLocale)) + .withTips(Toolkit.i18nText(type.getTipsLocale())) .withSolution(Toolkit.i18nText(type.getSolutionLocale()), DetectorConstants.FINE_DB_HELP_LINK) + .withLog(type.getLogLocale()) .build(); } return null; diff --git a/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java new file mode 100644 index 000000000..d5a0c45ea --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java @@ -0,0 +1,595 @@ +package com.fr.env.detect.ui; + +import com.fr.base.svg.IconUtils; +import com.fr.design.components.notification.NotificationAction; +import com.fr.design.components.notification.NotificationDialog; +import com.fr.design.components.notification.NotificationDialogProperties; +import com.fr.design.components.notification.NotificationMessage; +import com.fr.design.components.notification.NotificationModel; +import com.fr.design.components.notification.NotificationType; +import com.fr.design.components.table.TablePanel; +import com.fr.design.constants.DesignerColor; +import com.fr.design.gui.ibutton.UIButton; +import com.fr.design.gui.ibutton.UIButtonUI; +import com.fr.design.gui.icheckbox.UICheckBox; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.i18n.Toolkit; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.ui.util.UIUtil; +import com.fr.design.utils.gui.GUICoreUtils; +import com.fr.design.utils.gui.GUIPaintUtils; +import com.fr.env.detect.base.DetectorBridge; +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.DetectorStatus; +import com.fr.env.detect.bean.DetectorType; +import com.fr.env.detect.bean.ExceptionSolution; +import com.fr.env.detect.bean.ExceptionTips; +import com.fr.env.detect.bean.Message; +import com.fr.env.detect.bean.SolutionAction; +import com.fr.third.guava.collect.Lists; +import org.jetbrains.annotations.NotNull; + +import javax.swing.BorderFactory; +import javax.swing.ImageIcon; +import javax.swing.JDialog; +import javax.swing.JPanel; +import javax.swing.SwingWorker; +import javax.swing.plaf.ButtonUI; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * created by Harrison on 2022/05/26 + **/ +public class EnvDetectorDialog extends JDialog { + + private static final ImageIcon LOADING_ICON = getLoadingIcon(); + + private JPanel body; + + private final JPanel headerPanel; + + private final TablePanel tablePanel; + + private final JPanel tailPanel; + + /* 数据 model */ + + private EnvDetectorModel model = new EnvDetectorModel(); + + /* 流程 model */ + + /** + * 默认是第一个要检测 + */ + private int currentDetectIndex = 0; + + private EnvDetectorButtonStatus buttonStatus = EnvDetectorButtonStatus.START; + + private SwingWorker detectWorker = null; + + public EnvDetectorDialog(Frame owner) { + super(owner); + + configProperties(); + + this.body = FRGUIPaneFactory.createBorderLayout_L_Pane(); + Color backgroundColor = new Color(240, 240, 243, 1); + this.body.setBackground( backgroundColor); + + this.headerPanel = createHeaderPanel(); + body.add(headerPanel, BorderLayout.NORTH); + + this.tablePanel = createTablePanel(); + body.add(tablePanel, BorderLayout.CENTER); + + /* tailPanel*/ + this.tailPanel = createTailPanel(); + body.add(tailPanel, BorderLayout.SOUTH); + + add(body); + + Dimension preferredSize = body.getPreferredSize(); + setSize(preferredSize); + + repaint(); + pack(); + + GUICoreUtils.centerWindow(this); + } + + @NotNull + private JPanel createHeaderPanel() { + + JPanel headerPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + headerPanel.setBorder(BorderFactory.createEmptyBorder(5, 0, 12, 0)); + UIButton detectButton = new UIButton(buttonStatus.getDesc()) { + @Override + public ButtonUI getUI() { + + return new UIButtonUI() { + @Override + protected void doExtraPainting(UIButton b, Graphics2D g2d, int w, int h, String selectedRoles) { + if (isPressed(b) && b.isPressedPainted()) { + GUIPaintUtils.fillPressed(g2d, 0, 0, w, h, b.isRoundBorder(), b.getRectDirection(), b.isDoneAuthorityEdited(selectedRoles), + DesignerColor.Button.Primary.PRESSED); + } else if (isRollOver(b)) { + GUIPaintUtils.fillRollOver(g2d, 0, 0, w, h, b.isRoundBorder(), b.getRectDirection(), b.isDoneAuthorityEdited(selectedRoles), b.isPressedPainted(), + DesignerColor.Button.Primary.HOVER); + } else if (b.isNormalPainted()) { + GUIPaintUtils.fillNormal(g2d, 0, 0, w, h, b.isRoundBorder(), b.getRectDirection(), b.isDoneAuthorityEdited(selectedRoles), b.isPressedPainted(), + DesignerColor.Button.Primary.NORMAL); + } + } + }; + } + }; + detectButton.setForeground(Color.WHITE); + detectButton.addActionListener(event -> { + if (buttonStatus.isNotExecuting()) { + startDetecting(detectButton); + } else { + stopDetecting(detectButton); + } + }); + detectButton.setPreferredSize(new Dimension(68, 20)); + detectButton.setBorderPainted(false); + detectButton.setContentAreaFilled(false); + headerPanel.add(detectButton, BorderLayout.WEST); + + return headerPanel; + } + + private void startDetecting(UIButton detectButton) { + + // 执行前 + buttonStatus = buttonStatus.next(); + UIUtil.invokeLaterIfNeeded(() -> detectButton.setText(buttonStatus.getDesc())); + detectWorker = new SwingWorker() { + + @Override + protected Void doInBackground() throws Exception { + List items = model.getItems(); + // 执行刷新 + for (int i = currentDetectIndex; i < items.size(); i++) { + + // 看一下是否关闭了, 有可能已经关闭了。 + if (buttonStatus.isNotExecuting()) { + return null; + } + + // 刷新一下面板-开始执行啦 + UIUtil.invokeLaterIfNeeded(EnvDetectorDialog.this::refresh); + + EnvDetectorItem item = items.get(i); + DetectorType type = item.getType(); + + // 执行检测, UI-当前在检测中 + Optional detect = UIUtil.waitUntil( + () -> DetectorBridge.getInstance().detect(type), + 1000, TimeUnit.MILLISECONDS); + // 获取结果,更新UI + detect.ifPresent(item::setResult); + + // 只有还在运行中,才会真正的刷新面板 + if (buttonStatus.isExecuting()) { + // 在刷新一下面板 + UIUtil.invokeLaterIfNeeded(EnvDetectorDialog.this::refresh); + currentDetectIndex++; + } + + } + return null; + } + + @Override + protected void done() { + + if (buttonStatus.isExecuting()) { + // 执行结束 + buttonStatus = buttonStatus.next(); + UIUtil.invokeLaterIfNeeded(() -> detectButton.setText(buttonStatus.getDesc())); + } + } + }; + // 开始执行 + detectWorker.execute(); + } + + private void stopDetecting(UIButton detectButton) { + + buttonStatus = buttonStatus.next(); + + // 先停止 + detectWorker.cancel(false); + // 更改-UI + // 执行中 + UIUtil.invokeLaterIfNeeded(() -> { + // 刷新按钮 + detectButton.setText(buttonStatus.getDesc()); + // 刷新面板 + refresh(); + }); + } + + + @NotNull + private TablePanel createTablePanel() { + + TablePanel tablePanel = new TablePanel(18, 3); + tablePanel.updateHeaders(new String[] { + Toolkit.i18nText("Fine-Design_Basic_Detect_Kind"), + Toolkit.i18nText("Fine-Design_Basic_Detect_Item"), + Toolkit.i18nText("Fine-Design_Basic_Detect_Result")}); + + updateTable(tablePanel); + + return tablePanel; + } + + private void updateTable(TablePanel tablePanel) { + + Map> itemMap = model.getItemMap(); + + // 行号, 这边更新是通过 行/列 。 不是索引 + int row = 1; + for (Map.Entry> entry : itemMap.entrySet()) { + + DetectorType.Kind kind = entry.getKey(); + List items = entry.getValue(); + for (int i = 0; i < items.size(); i++) { + if (i == 0) { + tablePanel.updateCell(row, 1, kind.getDescription()); + } + EnvDetectorItem item = items.get(i); + tablePanel.updateCell(row, 2, new UILabel(item.getDescription())); + DetectorResult result = item.getResult(); + + int detectRow = currentDetectIndex + 1; + + if (result == null) { + // 处于非正在检测状态 或者 索引不等于当前行号的时候 + UILabel label; + if (buttonStatus.isExecuting() && detectRow == row) { + // 正在检测索引 + label = new UILabel(LOADING_ICON, UILabel.LEADING); + } else { + label = new UILabel("-"); + } + tablePanel.updateCell(row, 3, label); + } else { + Component resultComponent = createResultComponent(result); + tablePanel.updateCell(row, 3, resultComponent); + } + row++; + } + } + } + + private static ImageIcon getLoadingIcon() { + + URL resource = EnvDetectorDialog.class.getResource("/com/fr/design/standard/loading/loading-120.gif"); + if (resource == null) { + return null; + } + ImageIcon loadingIcon = new ImageIcon(resource); + int width = 16; + int height = 16; + loadingIcon.setImage(loadingIcon.getImage().getScaledInstance(width, height, Image.SCALE_DEFAULT)); + return loadingIcon; + } + + private Component createResultComponent(DetectorResult result) { + + JPanel statusPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + if (result.getStatus() == DetectorStatus.NORMAL) { + statusPanel.add(new UILabel(IconUtils.readIcon("/com/fr/design/standard/reminder/reminder_success.svg")), BorderLayout.WEST); + statusPanel.add(new UILabel(Toolkit.i18nText("Fine-Design_Basic_Normal")), BorderLayout.CENTER); + } else { + statusPanel.add(new UILabel(IconUtils.readIcon("/com/fr/design/standard/reminder/reminder_error.svg")), BorderLayout.WEST); + statusPanel.add(new UILabel(Toolkit.i18nText("Fine-Design_Basic_Exception")), BorderLayout.CENTER); + UILabel detailLabel = new UILabel(Toolkit.i18nText("Fine_Designer_Look_Detail")); + detailLabel.setForeground(new Color(65, 155, 249)); + detailLabel.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent mouseEvent) { + NotificationDialogProperties properties = new NotificationDialogProperties((Frame) EnvDetectorDialog.this.getOwner(), Toolkit.i18nText("Fine-Design_Basic_Detect_Notification_Title")); + Stream results = model.getResults(); + List notificationModels = results + .filter(Objects::nonNull) + .filter((e) -> e.getStatus() == DetectorStatus.EXCEPTION) + .map(EnvDetectorDialog.this::convertDetectorResult) + .collect(Collectors.toList()); + + NotificationDialog dialog = new NotificationDialog(properties, notificationModels); + dialog.open(); + } + }); + statusPanel.add(detailLabel); + } + return statusPanel; + } + + private NotificationModel convertDetectorResult(DetectorResult result) { + + List messages = new ArrayList<>(); + + Function> convert2NotificationMsg = message -> { + + NotificationMessage notificationMessage = null; + if (message != null) { + Message.Type type = message.getType(); + switch (type) { + case SIMPLE: + notificationMessage = (new NotificationMessage.SimpleMessage(message.get())); + break; + case LINK: + Message.Link linkMsg = (Message.Link) message; + notificationMessage = new NotificationMessage.LinkMessage(linkMsg.getText(), linkMsg.getLink()); + break; + default: + break; + } + } + return Optional.ofNullable(notificationMessage); + }; + + ExceptionTips tips = result.getTips(); + convert2NotificationMsg + .apply(tips.getMessage()) + .ifPresent(messages::add); + + ExceptionSolution solution = result.getSolution(); + convert2NotificationMsg.apply(solution.getMessage()) + .ifPresent(messages::add); + + NotificationAction notificationAction = null; + SolutionAction solutionAction = solution.getAction(); + if (solutionAction != null) { + notificationAction = new NotificationAction() { + @Override + public String name() { + return solutionAction.name(); + } + + @Override + public void run(Object... args) { + solutionAction.run(); + } + }; + } + + return new NotificationModel(NotificationType.WARNING, notificationAction, messages); + } + + private void refresh() { + + updateTable(this.tablePanel); + pack(); + repaint(); + } + + @NotNull + private JPanel createTailPanel() { + + JPanel tailPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + tailPanel.setBorder(BorderFactory.createEmptyBorder(20, 0, 0, 0)); + + JPanel configPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + { + UICheckBox checkBox = new UICheckBox(); + configPanel.add(checkBox, BorderLayout.WEST); + UILabel description = new UILabel(Toolkit.i18nText("Fine-Design_Basic_Detect_Switch")); + configPanel.add(description, BorderLayout.EAST); + } + tailPanel.add(configPanel, BorderLayout.WEST); + + JPanel actionsPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + actionsPanel.setLayout(FRGUIPaneFactory.createM_BorderLayout()); + { + UIButton confirmButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Confirm")); + confirmButton.addActionListener((e) -> { + setVisible(false); + dispose(); + // 配置处理 + // todo + }); + actionsPanel.add(confirmButton, BorderLayout.WEST); + + UIButton cancelButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Cancel")); + cancelButton.addActionListener((e) -> { + setVisible(false); + dispose(); + }); + actionsPanel.add(cancelButton, BorderLayout.EAST); + } + tailPanel.add(actionsPanel, BorderLayout.EAST); + return tailPanel; + } + + private void configProperties() { + + setTitle(Toolkit.i18nText("Fine-Design_Basic_Detect_Title")); + setModal(false); + setFocusable(false); + setAutoRequestFocus(false); + setResizable(false); + } + + /** + * 数据 model + * + * kind-list_item + */ + private class EnvDetectorModel { + + private Map> itemMap = new LinkedHashMap<>(); + + public EnvDetectorModel() { + + itemMap.put(DetectorType.Kind.JAR, Lists.newArrayList(new EnvDetectorItem(DetectorType.LACK_OF_JAR), new EnvDetectorItem(DetectorType.JAR_IN_CONSISTENCE), new EnvDetectorItem(DetectorType.JAR_CONFLICT))); + + itemMap.put(DetectorType.Kind.FINE_DB, Lists.newArrayList(new EnvDetectorItem(DetectorType.FINE_DB_LOCKED), new EnvDetectorItem(DetectorType.FINE_DB_PERMISSION), new EnvDetectorItem(DetectorType.FINE_DB_DIRTY))); + } + + public void update(DetectorType type, DetectorResult result) { + + List items = itemMap.get(type.getKind()); + items.stream() + .filter((e) -> e.getType() == type) + .findFirst() + .ifPresent((e) -> {e.setResult(result);}); + } + + public Stream getResults() { + + return getItems().stream() + .map(EnvDetectorItem::getResult); + } + + public List getItems() { + + return itemMap.values().stream() + .flatMap(Collection::stream) + .collect(Collectors.toList()); + } + + public Map> getItemMap() { + + return this.itemMap; + } + + } + + private class EnvDetectorItem { + + private DetectorType type; + + private DetectorResult result; + + public EnvDetectorItem(DetectorType detectorType) { + this.type = detectorType; + } + + public DetectorType getType() { + return type; + } + + public String getKind() { + return this.type.getKind().getDescription(); + } + + public String getDescription() { + return this.type.getDescription(); + } + + public DetectorResult getResult() { + return result; + } + + public void setResult(DetectorResult result) { + this.result = result; + } + } + + /** + * 按钮的当前状态 + */ + private enum EnvDetectorButtonStatus { + + /** + * 开始 -> 停止 + */ + START("Fine-Design_Basic_Detect_Start") { + @Override + public EnvDetectorButtonStatus next() { + return STOP; + } + }, + + /** + * 停止 -> 继续 + */ + STOP("Fine-Design_Basic_Detect_Stop") { + @Override + public EnvDetectorButtonStatus next() { + return CONTINUE; + } + }, + + /** + * 继续 -> 停止 + */ + CONTINUE("Fine-Design_Basic_Detect_Continue") { + @Override + public EnvDetectorButtonStatus next() { + return STOP; + } + }, + + /** + * 重新 -> 停止 + */ + A_NEW("Fine-Design_Basic_Detect_A_New") { + @Override + public EnvDetectorButtonStatus next() { + return STOP; + } + } + + ; + + private String descLocale; + + EnvDetectorButtonStatus(String descLocale) { + + this.descLocale = descLocale; + } + + public String getDesc() { + + return Toolkit.i18nText(descLocale); + } + + /** + * 在执行中 + * + * @return 是/否 + */ + public boolean isExecuting() { + + return this == EnvDetectorButtonStatus.STOP; + }; + + /** + * 不在执行中 + * + * @return 是/否 + */ + public boolean isNotExecuting() { + + return !isExecuting(); + } + + public abstract EnvDetectorButtonStatus next(); + } +} From ec5c5d09b2e6a51e3acb4a90994d7dd6bd2b640e Mon Sep 17 00:00:00 2001 From: Harrison Date: Fri, 27 May 2022 15:25:43 +0800 Subject: [PATCH 20/85] =?UTF-8?q?feat:=20REPORT-70565=20=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E7=8E=AF=E5=A2=83=E7=9B=91=E6=B5=8B=EF=BC=88jar?= =?UTF-8?q?=E5=8C=85=E5=BC=82=E5=B8=B8=E3=80=81finedb=E3=80=81=E6=9D=80?= =?UTF-8?q?=E6=AF=92=E8=BD=AF=E4=BB=B6=EF=BC=89=20UI=E9=9D=A2=E6=9D=BF+?= =?UTF-8?q?=E9=83=A8=E5=88=86=E8=B5=84=E6=BA=90=201-=E6=8F=90=E4=BE=9B?= =?UTF-8?q?=E9=83=A8=E5=88=86=20Utils,=20=E5=B8=AE=E5=8A=A9=E8=B0=83?= =?UTF-8?q?=E8=AF=95=20UI=202-=E6=8A=BD=E8=B1=A1=20notification=203-?= =?UTF-8?q?=E6=8A=BD=E8=B1=A1=20table=204-=E5=AE=8C=E6=88=90=20envDetect?= =?UTF-8?q?=20->=20notification=20=E7=9A=84=E8=81=94=E7=B3=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fr/base/function/ThrowableRunnable.java | 24 ++ .../notification/NotificationAction.java | 21 ++ .../notification/NotificationDialog.java | 336 ++++++++++++++++++ .../NotificationDialogProperties.java | 39 ++ .../notification/NotificationMessage.java | 88 +++++ .../notification/NotificationModel.java | 39 ++ .../notification/NotificationType.java | 16 + .../components/page/PageControlModel.java | 78 ++++ .../components/page/PageControlPanel.java | 150 ++++++++ .../design/components/table/TablePanel.java | 180 ++++++++++ .../fr/design/constants/DesignerColor.java | 24 ++ .../design/dialog/link/MessageWithLink.java | 45 +-- .../java/com/fr/design/ui/util/UIUtil.java | 29 ++ .../com/fr/design/utils/DevDebugUtil.java | 12 + .../java/com/fr/design/utils/DevUtil.java | 49 +++ .../java/com/fr/design/utils/LinkStrUtil.java | 55 +++ .../standard/arrow/arrow_disable_left.svg | 3 + .../standard/arrow/arrow_disable_right.svg | 3 + .../standard/arrow/arrow_enable_left.svg | 3 + .../standard/arrow/arrow_enable_right.svg | 3 + .../design/standard/loading/loading-120.gif | Bin 0 -> 174858 bytes .../fr/design/standard/loading/loading-64.gif | Bin 0 -> 90641 bytes .../standard/reminder/reminder_error.svg | 4 + .../standard/reminder/reminder_success.svg | 3 + .../standard/reminder/reminder_warning.svg | 5 + .../notification/NotificationDialogTest.java | 43 +++ 26 files changed, 1232 insertions(+), 20 deletions(-) create mode 100644 designer-base/src/main/java/com/fr/base/function/ThrowableRunnable.java create mode 100644 designer-base/src/main/java/com/fr/design/components/notification/NotificationAction.java create mode 100644 designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java create mode 100644 designer-base/src/main/java/com/fr/design/components/notification/NotificationDialogProperties.java create mode 100644 designer-base/src/main/java/com/fr/design/components/notification/NotificationMessage.java create mode 100644 designer-base/src/main/java/com/fr/design/components/notification/NotificationModel.java create mode 100644 designer-base/src/main/java/com/fr/design/components/notification/NotificationType.java create mode 100644 designer-base/src/main/java/com/fr/design/components/page/PageControlModel.java create mode 100644 designer-base/src/main/java/com/fr/design/components/page/PageControlPanel.java create mode 100644 designer-base/src/main/java/com/fr/design/components/table/TablePanel.java create mode 100644 designer-base/src/main/java/com/fr/design/constants/DesignerColor.java create mode 100644 designer-base/src/main/java/com/fr/design/utils/DevDebugUtil.java create mode 100644 designer-base/src/main/java/com/fr/design/utils/DevUtil.java create mode 100644 designer-base/src/main/java/com/fr/design/utils/LinkStrUtil.java create mode 100755 designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_disable_left.svg create mode 100755 designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_disable_right.svg create mode 100755 designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_enable_left.svg create mode 100755 designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_enable_right.svg create mode 100644 designer-base/src/main/resources/com/fr/design/standard/loading/loading-120.gif create mode 100644 designer-base/src/main/resources/com/fr/design/standard/loading/loading-64.gif create mode 100755 designer-base/src/main/resources/com/fr/design/standard/reminder/reminder_error.svg create mode 100755 designer-base/src/main/resources/com/fr/design/standard/reminder/reminder_success.svg create mode 100755 designer-base/src/main/resources/com/fr/design/standard/reminder/reminder_warning.svg create mode 100644 designer-base/src/test/java/com/fr/design/components/notification/NotificationDialogTest.java diff --git a/designer-base/src/main/java/com/fr/base/function/ThrowableRunnable.java b/designer-base/src/main/java/com/fr/base/function/ThrowableRunnable.java new file mode 100644 index 000000000..c89ad2115 --- /dev/null +++ b/designer-base/src/main/java/com/fr/base/function/ThrowableRunnable.java @@ -0,0 +1,24 @@ +package com.fr.base.function; + +import com.fr.log.FineLoggerFactory; + +/** + * 可抛出异常的 Runnable + * + * created by Harrison on 2022/05/24 + **/ +public interface ThrowableRunnable { + + void run() throws T; + + static Runnable toRunnable(ThrowableRunnable runnable) { + + return () -> { + try { + runnable.run(); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + }; + } +} diff --git a/designer-base/src/main/java/com/fr/design/components/notification/NotificationAction.java b/designer-base/src/main/java/com/fr/design/components/notification/NotificationAction.java new file mode 100644 index 000000000..d2f56976e --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/components/notification/NotificationAction.java @@ -0,0 +1,21 @@ +package com.fr.design.components.notification; + +/** + * created by Harrison on 2022/05/24 + **/ +public interface NotificationAction { + + /** + * 行为名 + * + * @return 名称 + */ + String name(); + + /** + * 行为动作 + * + * @param args 参数 + */ + void run(Object... args); +} diff --git a/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java b/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java new file mode 100644 index 000000000..f7182ddca --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java @@ -0,0 +1,336 @@ +package com.fr.design.components.notification; + +import com.fr.base.function.ThrowableRunnable; +import com.fr.base.svg.IconUtils; +import com.fr.design.components.page.PageControlModel; +import com.fr.design.components.page.PageControlPanel; +import com.fr.design.dialog.link.MessageWithLink; +import com.fr.design.gui.ibutton.UIButton; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.i18n.Toolkit; +import com.fr.design.layout.FRGUIPaneFactory; + +import javax.swing.BorderFactory; +import javax.swing.Icon; +import javax.swing.JComponent; +import javax.swing.JDialog; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.ScrollPaneConstants; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Container; +import java.awt.Desktop; +import java.awt.Dimension; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.net.URI; +import java.util.Arrays; +import java.util.List; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +/** + * 右下角的提醒 异常提醒 + * + * created by Harrison on 2022/05/24 + **/ +public class NotificationDialog extends JDialog { + + private Dimension contentSize = new Dimension(300, 100); + private Dimension buttonDimension = new Dimension(68, 20); + + private NotificationDialogProperties properties; + + /* 数据 model */ + + private List notificationModels; + private PageControlModel pageControlModel; + + private JPanel body; + private JPanel headerPanel; + private JPanel contentPanel; + private JPanel tailPanel; + + public NotificationDialog(NotificationDialogProperties properties, List notificationModels) { + + super(properties.getOwner()); + setTitle(properties.getTitle()); + this.properties = properties; + + this.notificationModels = notificationModels; + this.pageControlModel = new PageControlModel(0, this.notificationModels.size()); + + initComponents(); + } + + public void initComponents() { + + //UI 配置 + configProperties(); + + this.body = FRGUIPaneFactory.createBorderLayout_L_Pane(); + body.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + + //首行 + layoutHeaderPanel(); + + //消息内容 + layoutContentPanel(); + + //查看详情 + layoutTailPanel(); + + add(body); + + Dimension dimension = body.getPreferredSize(); + setSize(dimension.width, dimension.height); + + Container parent = getParent(); + setLocation((parent.getWidth() - dimension.width - 30 + parent.getX()), + parent.getY() + parent.getHeight() - dimension.height - 30); + + } + + public void open() { + + setVisible(true); + } + + private void configProperties() { + + setModal(properties.isModal()); + setFocusable(false); + setAutoRequestFocus(false); + setResizable(false); + } + + protected JPanel createHeaderPanel() { + + return null; + } + + /** + * 内容 + * + * @return 内容面板 + */ + protected JPanel createContentPanel() { + + JPanel contentPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + contentPanel.setBorder(BorderFactory.createEmptyBorder(8, 10, 8, 10)); + contentPanel.setName("contentPanel"); + + NotificationModel model = getCurrentModel(); + + UILabel icon = new UILabel(getIconForType(model.getType())); + icon.setPreferredSize(new Dimension(16, 16)); + JPanel iconPanel = FRGUIPaneFactory.createBorderLayout_L_Pane(); + iconPanel.setBorder(BorderFactory.createEmptyBorder(0, 5, 10, 8)); + iconPanel.add(icon, BorderLayout.NORTH); + + contentPanel.add(iconPanel, BorderLayout.WEST); + + + NotificationMessage[] messages = model.getMessages(); + List messageComponents = Arrays.stream(messages) + .map((messageModel) -> { + if (messageModel.getType() == NotificationMessage.Type.LINK) { + NotificationMessage.LinkMessage linkMessage = (NotificationMessage.LinkMessage) messageModel; + return new MessageWithLink(linkMessage.format(), ThrowableRunnable.toRunnable(() -> { + Desktop.getDesktop().browse(URI.create(linkMessage.getLink())); + })); + } + return new UILabel(messageModel.format()); + }) + .collect(Collectors.toList()); + + // 竖向排列 + JPanel messageSummaryPanel = new JPanel(); + messageSummaryPanel.setLayout(new GridLayout(messageComponents.size(), 1)); + messageComponents.forEach(messageSummaryPanel::add); + + JPanel centerPanel = FRGUIPaneFactory.createBorderLayout_L_Pane(); + centerPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 20)); + + JScrollPane jScrollPane = new JScrollPane(messageSummaryPanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + jScrollPane.setBorder(BorderFactory.createEmptyBorder()); + centerPanel.add(jScrollPane, BorderLayout.CENTER); + centerPanel.setPreferredSize(contentSize); + + contentPanel.add(centerPanel, BorderLayout.CENTER); + + return contentPanel; + } + + /** + * 行动 + * + * UI布局 + * /翻页/不再提醒/提醒行为/我知道了 + * + * @return 行动面板 + */ + protected JPanel createTailPanel() { + + JPanel tailPanel = FRGUIPaneFactory.createBorderLayout_L_Pane(); + tailPanel.setName("tailPanel"); + + // 翻页按钮效果 + PageControlPanel pageControlPanel = new PageControlPanel(pageControlModel); + + pageControlPanel.actions(new Runnable() { + @Override + public void run() { + pageControlModel = pageControlPanel.performPrevious(); + refresh(); + } + }, new Runnable() { + @Override + public void run() { + pageControlModel = pageControlPanel.performNext(); + refresh(); + } + }); + + tailPanel.add(pageControlPanel, BorderLayout.WEST); + + // 行为效果 + JPanel actionsPanel = FRGUIPaneFactory.createBorderLayout_M_Pane(); + + { + actionsPanel.setBorder(BorderFactory.createEmptyBorder()); + actionsPanel.setName("actionsPanel"); + + UILabel notReminder = new UILabel(); + notReminder.setText(Toolkit.i18nText("Fine-Design_Basic_Not_Reminder")); + notReminder.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + // todo + // 点击事件 + destroy(); + } + }); + Color color = new Color(65, 155, 249); + notReminder.setForeground(color); + actionsPanel.add(notReminder, BorderLayout.WEST); + + JPanel buttonPanel = FRGUIPaneFactory.createBorderLayout_M_Pane(); + buttonPanel.setBorder(BorderFactory.createEmptyBorder()); + + // real-action + NotificationModel currentModel = getCurrentModel(); + NotificationAction action = currentModel.getAction(); + if (action != null) { + UIButton actionButton = new UIButton(action.name()); + actionButton.setPreferredSize(buttonDimension); + actionButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + action.run(); + } + }); + buttonPanel.add(actionButton, BorderLayout.WEST); + } + + UIButton knowButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Know")); + knowButton.setPreferredSize(buttonDimension); + knowButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (pageControlModel.isLast()) { + destroy(); + return; + } + pageControlModel = pageControlPanel.performNext(); + refresh(); + } + }); + buttonPanel.add(knowButton, BorderLayout.EAST); + + actionsPanel.add(buttonPanel, BorderLayout.EAST); + } + + tailPanel.add(actionsPanel, BorderLayout.EAST); + + return tailPanel; + } + + private void refresh() { + + layoutContentPanel(); + + layoutTailPanel(); + + this.repaint(); + } + + private void layoutHeaderPanel() { + + this.headerPanel = layoutPanel(this.headerPanel, this::createHeaderPanel, BorderLayout.NORTH); + } + + private void layoutTailPanel() { + + this.tailPanel = layoutPanel(this.tailPanel, this::createTailPanel, BorderLayout.SOUTH); + } + + private void layoutContentPanel() { + + this.contentPanel = layoutPanel(this.contentPanel, this::createContentPanel, BorderLayout.CENTER); + } + + private JPanel layoutPanel(JPanel oldPanel, Supplier supplier, Object constraints){ + + if (oldPanel != null) { + this.body.remove(oldPanel); + } + JPanel newPanel = supplier.get(); + if (newPanel != null) { + this.body.add(newPanel, constraints); + } + return newPanel; + } + + private NotificationModel getCurrentModel() { + + int index = pageControlModel.getIndex(); + return notificationModels.get(index); + } + + protected Icon getIconForType(NotificationType type) { + + String iconPath; + switch (type) { + case ERROR: + iconPath = "/com/fr/design/standard/reminder/reminder_error.svg"; + break; + case INFO: + iconPath = "/com/fr/design/standard/reminder/reminder_success.svg"; + break; + case WARNING: + iconPath = "/com/fr/design/standard/reminder/reminder_warning.svg"; + break; + default: + return null; + } + return IconUtils.readIcon(iconPath); + } + + private void destroy() { + + setVisible(false); + dispose(); + } + + @Override + public void dispose() { + + super.dispose(); + // todo + } +} diff --git a/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialogProperties.java b/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialogProperties.java new file mode 100644 index 000000000..5bcff6715 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialogProperties.java @@ -0,0 +1,39 @@ +package com.fr.design.components.notification; + +import java.awt.Frame; + +/** + * 通知会话的属性 + * + * created by Harrison on 2022/05/24 + **/ +public class NotificationDialogProperties { + + private Frame owner; + + private String title; + + private boolean modal; + + public NotificationDialogProperties(Frame owner, String title) { + this.owner = owner; + this.title = title; + this.modal = false; + } + + public void setModal(boolean modal) { + this.modal = modal; + } + + public Frame getOwner() { + return owner; + } + + public String getTitle() { + return title; + } + + public boolean isModal() { + return modal; + } +} diff --git a/designer-base/src/main/java/com/fr/design/components/notification/NotificationMessage.java b/designer-base/src/main/java/com/fr/design/components/notification/NotificationMessage.java new file mode 100644 index 000000000..26dba1297 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/components/notification/NotificationMessage.java @@ -0,0 +1,88 @@ +package com.fr.design.components.notification; + +import com.fr.design.utils.LinkStrUtil; + +/** + * created by Harrison on 2022/05/24 + **/ +public interface NotificationMessage { + + /** + * 格式化 + * + * @return 通知信息 + */ + String format(); + + /** + * 类型 + * + * @return 类型 + */ + Type getType(); + + enum Type { + + /** + * 简单型 + */ + SIMPLE, + + /** + * 链接 + */ + LINK + } + + class SimpleMessage implements NotificationMessage { + + private String text; + + public SimpleMessage(String text) { + this.text = text; + } + + @Override + public String format() { + return text; + } + + @Override + public Type getType() { + return Type.SIMPLE; + } + } + + class LinkMessage implements NotificationMessage { + + private String text; + + private String link; + + public LinkMessage(String text, String link) { + + this.text = text; + this.link = link; + } + + public String getText() { + return text; + } + + public String getLink() { + return link; + } + + @Override + public String format() { + + return LinkStrUtil.generateHtmlTag(text); + } + + @Override + public Type getType() { + + return Type.LINK; + } + } +} diff --git a/designer-base/src/main/java/com/fr/design/components/notification/NotificationModel.java b/designer-base/src/main/java/com/fr/design/components/notification/NotificationModel.java new file mode 100644 index 000000000..86d5a9edf --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/components/notification/NotificationModel.java @@ -0,0 +1,39 @@ +package com.fr.design.components.notification; + +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class NotificationModel { + + private final NotificationType type; + private final NotificationMessage[] messages; + + @Nullable + private final NotificationAction action; + + public NotificationModel(NotificationType type, @Nullable NotificationAction action, List messages) { + this(type, action, messages.toArray(new NotificationMessage[0])); + } + + public NotificationModel(NotificationType type, @Nullable NotificationAction action, NotificationMessage... messages) { + + this.type = type; + this.messages = messages; + this.action = action; + } + + public NotificationType getType() { + return type; + } + + public NotificationMessage[] getMessages() { + return messages == null ? new NotificationMessage[0] : messages; + } + + public @Nullable NotificationAction getAction() { + + return action; + } + +} diff --git a/designer-base/src/main/java/com/fr/design/components/notification/NotificationType.java b/designer-base/src/main/java/com/fr/design/components/notification/NotificationType.java new file mode 100644 index 000000000..0f5ab7b34 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/components/notification/NotificationType.java @@ -0,0 +1,16 @@ +package com.fr.design.components.notification; + +/** + * 提醒类型 + * 决定图标种类 + * + * created by Harrison on 2022/05/27 + **/ +public enum NotificationType { + + INFO, + + ERROR, + + WARNING +} diff --git a/designer-base/src/main/java/com/fr/design/components/page/PageControlModel.java b/designer-base/src/main/java/com/fr/design/components/page/PageControlModel.java new file mode 100644 index 000000000..3d024fb05 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/components/page/PageControlModel.java @@ -0,0 +1,78 @@ +package com.fr.design.components.page; + +/** + * created by Harrison on 2022/05/26 + **/ +public class PageControlModel { + + /** + * 当前索引 + * + * = (页数-1) + */ + private int index; + + /** + * 总页数 + */ + private int summary; + + public PageControlModel(int index, int summary) { + this.index = index; + this.summary = summary; + } + + public PageControlModel() { + } + + public PageControlModel previous() { + + this.index--; + return this; + } + + public PageControlModel next() { + + this.index++; + return this; + } + + /** + * 页数 + * index+1 + * + * @return 页数 + */ + public int getNumber() { + return index + 1; + } + + public boolean isFirst() { + return getNumber() == 1; + } + + public boolean isLast() { + return getNumber() == getSummary(); + } + + public int getIndex() { + return index; + } + + public void setIndex(int index) { + this.index = index; + } + + public int getSummary() { + return summary; + } + + public void setSummary(int summary) { + this.summary = summary; + } + + public String toContent() { + + return getNumber() + "/" + this.summary; + } +} diff --git a/designer-base/src/main/java/com/fr/design/components/page/PageControlPanel.java b/designer-base/src/main/java/com/fr/design/components/page/PageControlPanel.java new file mode 100644 index 000000000..0059f9125 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/components/page/PageControlPanel.java @@ -0,0 +1,150 @@ +package com.fr.design.components.page; + +import com.fr.base.svg.IconUtils; +import com.fr.design.gui.ibutton.UIButton; +import com.fr.design.gui.ilable.UILabel; + +import javax.swing.BorderFactory; +import javax.swing.JPanel; +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.function.Function; + +/** + * 翻页组件 + * + * created by Harrison on 2022/05/26 + **/ +public class PageControlPanel extends JPanel { + + private static final long serialVersionUID = 8140501834691131305L; + + private static final Dimension PAGE_CONTROL_BUTTON_DIMENSION = new Dimension(20, 20); + + private UIButton previous; + private Runnable previousAction; + + private UILabel content; + private PageControlModel model; + + private UIButton next; + private Runnable nextAction; + + public PageControlPanel(PageControlModel model) { + + this.model = model; + setBorder(BorderFactory.createEmptyBorder()); + setLayout(new BorderLayout(6, 0)); + + this.previous = new UIButton(); + previous.setPreferredSize(PAGE_CONTROL_BUTTON_DIMENSION); + previous.setIcon(IconUtils.readIcon("/com/fr/design/standard/arrow/arrow_enable_left.svg")); + previous.setDisabledIcon(IconUtils.readIcon("/com/fr/design/standard/arrow/arrow_disable_left.svg")); + previous.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + performAction(previousAction); + } + }); + + this.add(previous, BorderLayout.WEST); + + this.content = new UILabel(model.toContent()); + this.add(content, BorderLayout.CENTER); + + next = new UIButton(); + next.setPreferredSize(PAGE_CONTROL_BUTTON_DIMENSION); + next.setIcon(IconUtils.readIcon("/com/fr/design/standard/arrow/arrow_enable_right.svg")); + next.setDisabledIcon(IconUtils.readIcon("/com/fr/design/standard/arrow/arrow_disable_right.svg")); + next.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + performAction(nextAction); + } + }); + this.add(next, BorderLayout.EAST); + + refresh(); + } + + public PageControlModel performPrevious() { + + update(PageControlModel::previous); + return this.model; + } + + public PageControlModel performNext() { + + update(PageControlModel::next); + return this.model; + } + + public PageControlModel getModel() { + + return this.model; + } + + public void update(PageControlModel model) { + + this.model.setIndex(model.getIndex()); + this.model.setSummary(model.getSummary()); + refresh(); + } + + public void update(Function updateAction) { + + PageControlModel newModel = updateAction.apply(this.model); + update(newModel); + refresh(); + } + + public void refresh() { + + this.content.setText(this.model.toContent()); + this.content.repaint(); + + this.previous.setEnabled(true); + this.next.setEnabled(true); + if (model.getNumber() == 1) { + // 禁用上一个 + disableButton(this.previous); + // 禁用next + if (model.getNumber() == model.getSummary()) { + disableButton(this.next); + } + return; + } + + // 禁用next + if (model.getNumber() == model.getSummary()) { + disableButton(this.next); + } + } + + private void enable(UIButton button) { + + button.setEnabled(true); + } + + private void disableButton(UIButton button) { + + button.setEnabled(false); + } + + private void performAction(Runnable action) { + + if (action != null) { + action.run(); + refresh(); + } + } + + public void actions(Runnable previousAction, Runnable nextAction) { + + this.previousAction = previousAction; + this.nextAction = nextAction; + } + +} diff --git a/designer-base/src/main/java/com/fr/design/components/table/TablePanel.java b/designer-base/src/main/java/com/fr/design/components/table/TablePanel.java new file mode 100644 index 000000000..a1a9492f8 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/components/table/TablePanel.java @@ -0,0 +1,180 @@ +package com.fr.design.components.table; + +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.third.org.apache.commons.lang3.ArrayUtils; + +import javax.swing.BorderFactory; +import javax.swing.JPanel; +import javax.swing.JSeparator; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.GridLayout; + +/** + * 表头 + * 内容 + * + * 适用于需要一个表格的 Panel + * + * created by Harrison on 2022/05/26 + **/ +public class TablePanel extends JPanel { + + private static final Color DEFAULT_HEADER_COLOR = new Color(232, 232, 233); + + private static final Color DEFAULT_ODD_ROW_COLOR = new Color(245, 245, 247); + + private static final Color DEFAULT_EVEN_ROW_COLOR = Color.WHITE; + + private JPanel headerPanel; + + private JPanel[] headerItemPanels; + + private JPanel contentPanel; + + private JPanel[][] cellPanels; + + public TablePanel(int row, int column) { + + setLayout(FRGUIPaneFactory.createBorderLayout()); + + /* header 部分 */ + + this.headerPanel = new JPanel(); + headerPanel.setLayout(FRGUIPaneFactory.createNColumnGridLayout(column)); + headerPanel.setName("header-panel"); + headerPanel.setPreferredSize(new Dimension(640, 24)); + + // border + headerPanel.setBorder(BorderFactory.createLineBorder(new Color(218, 218, 221))); + syncHeaderColor(headerPanel); + + headerItemPanels = new JPanel[column]; + for (int i = 0; i < column; i++) { + JPanel headerItemWrapper = FRGUIPaneFactory.createBorderLayout_S_Pane(); + syncHeaderColor(headerItemWrapper); + headerItemWrapper.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 10)); + + JPanel headerItemPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + headerItemPanel.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0)); + headerItemPanels[i] = headerItemPanel; + + UILabel label = new UILabel(); + syncHeaderColor(label); + + headerItemPanel.add(new UILabel(), BorderLayout.CENTER); + + headerItemWrapper.add(headerItemPanel, BorderLayout.WEST); + if (i != column - 1) { + JSeparator separator = new JSeparator(JSeparator.VERTICAL); + separator.setBackground(new Color(218, 218, 221)); + headerItemWrapper.add(separator, BorderLayout.EAST); + } + headerPanel.add(headerItemWrapper); + } + + /* content 部分 */ + + contentPanel = new JPanel(); + + contentPanel.setLayout(new GridLayout(row, 1)); + contentPanel.setBorder(BorderFactory.createLineBorder(new Color(218, 218, 221))); + + cellPanels = new JPanel[row][column]; + for (int i = 0; i < row; i++) { + + JPanel rowPanel = new JPanel(); + // 获取行号 + Color rowColor = getRowColorByRowNumber(i + 1); + rowPanel.setBackground(rowColor); + rowPanel.setLayout(FRGUIPaneFactory.createNColumnGridLayout(column)); + rowPanel.setName("row-" + i); + rowPanel.setBorder(BorderFactory.createEmptyBorder()); + rowPanel.setPreferredSize(new Dimension(640, 24)); + + for (int j = 0; j < column; j++) { + + JPanel rowItemPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + rowItemPanel.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0)); + rowItemPanel.setName("rowItemPanel-"+ i + "-" + j); + final UILabel empty = new UILabel(); + empty.setPreferredSize(new Dimension(210, 24)); + rowItemPanel.setBackground(rowPanel.getBackground()); + rowItemPanel.add(empty, BorderLayout.CENTER); + + rowPanel.add(rowItemPanel); + cellPanels[i][j] = rowItemPanel; + } + + contentPanel.add(rowPanel); + } + + add(headerPanel, BorderLayout.NORTH); + add(contentPanel, BorderLayout.SOUTH); + } + + /** + * 获取行的颜色 + * + * @param row 行号 + * @return 颜色 + */ + private Color getRowColorByRowNumber(int row) { + + Color rowColor; + if (row % 2 != 0) { + rowColor = DEFAULT_EVEN_ROW_COLOR; + } else { + rowColor = DEFAULT_ODD_ROW_COLOR; + } + return rowColor; + } + + public void updateHeaders(String[] headers) { + + for (int i = 0; i < headers.length; i++) { + String header = headers[i]; + UILabel headerContent = new UILabel(header); + JPanel headerItemPanel = headerItemPanels[i]; + if (ArrayUtils.getLength(headerItemPanel.getComponents()) == 1) { + headerItemPanel.remove(0); + } + headerItemPanel.add(headerContent); + syncHeaderColor(headerItemPanel); + } + } + + public void updateCell(int row, int column, Component component) { + + int x = row - 1; + int y = column - 1; + + JPanel cellPanel = this.cellPanels[x][y]; + if (ArrayUtils.getLength(cellPanel.getComponents()) == 1) { + cellPanel.remove(0); + } + cellPanel.add(component); + } + + public void updateCell(int row, int column, String value) { + + UILabel cellContent = new UILabel(value); + syncCellColor(row, cellContent); + this.updateCell(row, column, cellContent); + } + + private void syncHeaderColor(Component component) { + + component.setBackground(DEFAULT_HEADER_COLOR); + } + + private void syncCellColor(int row, Component component) { + + Color rowColor = getRowColorByRowNumber(row); + component.setBackground(rowColor); + } + +} diff --git a/designer-base/src/main/java/com/fr/design/constants/DesignerColor.java b/designer-base/src/main/java/com/fr/design/constants/DesignerColor.java new file mode 100644 index 000000000..0ece84cca --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/constants/DesignerColor.java @@ -0,0 +1,24 @@ +package com.fr.design.constants; + +import java.awt.Color; + +/** + * 见 设计器规范 + * 将相关的逻辑抽象过来 + * 如果后面更改的话, 可以统一修改 + * 如果换版本,可以换成 v2 这种类推 + * + * created by Harrison on 2022/05/26 + **/ +public interface DesignerColor { + + interface Button { + + interface Primary { + + Color PRESSED = new Color(29, 122, 220); + Color HOVER = new Color(84, 165, 249); + Color NORMAL = new Color(65, 155, 249); + } + } +} diff --git a/designer-base/src/main/java/com/fr/design/dialog/link/MessageWithLink.java b/designer-base/src/main/java/com/fr/design/dialog/link/MessageWithLink.java index 2e839d27c..a6746d227 100644 --- a/designer-base/src/main/java/com/fr/design/dialog/link/MessageWithLink.java +++ b/designer-base/src/main/java/com/fr/design/dialog/link/MessageWithLink.java @@ -1,6 +1,6 @@ package com.fr.design.dialog.link; -import com.fr.design.gui.ilable.UILabel; +import com.fr.design.utils.LinkStrUtil; import com.fr.log.FineLoggerFactory; import com.fr.stable.StringUtils; @@ -12,6 +12,8 @@ import java.awt.Desktop; import java.awt.Font; import java.net.URI; +import static com.fr.design.utils.LinkStrUtil.LABEL; + /** * 用来构建JOptionPane带超链的消息提示 * @@ -21,8 +23,13 @@ import java.net.URI; */ public class MessageWithLink extends JEditorPane { - private static final UILabel LABEL = new UILabel(); - + public MessageWithLink(String htmlText, Runnable action) { + + super("text/html", htmlText); + initListener(action); + setEditable(false); + setBorder(null); + } public MessageWithLink(String message, String linkName, String link) { this(message, linkName, link, LABEL.getBackground(), LABEL.getFont()); @@ -49,36 +56,34 @@ public class MessageWithLink extends JEditorPane { } public MessageWithLink(String frontMessage, String linkName, String link, String backMessage, Color backgroundColor, Font font, Color fontColor) { - super("text/html", "" + frontMessage + "" + linkName + "" + backMessage + ""); + + super("text/html", "" + frontMessage + "" + linkName + "" + backMessage + ""); initListener(link); setEditable(false); setBorder(null); } - - protected void initListener(String link) { + + public void initListener(Runnable runnable) { + addHyperlinkListener(new HyperlinkListener() { @Override public void hyperlinkUpdate(HyperlinkEvent e) { if (e.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED)) { - try { - Desktop.getDesktop().browse(URI.create(link)); - } catch (Exception exception) { - FineLoggerFactory.getLogger().error(exception.getMessage(), exception); - } + runnable.run(); } } }); } - private static StringBuilder generateStyle(Color backgroundColor, Font font, Color fontColor) { - // 构建相同风格样式 - StringBuilder style = new StringBuilder("font-family:" + font.getFamily() + ";"); - style.append("font-weight:").append(font.isBold() ? "bold" : "normal").append(";"); - style.append("font-size:").append(font.getSize()).append("pt;"); - style.append("color:rgb(").append(fontColor.getRed()).append(",").append(fontColor.getGreen()).append(",").append(fontColor.getBlue()).append(");"); - style.append("background-color: rgb(").append(backgroundColor.getRed()).append(",").append(backgroundColor.getGreen()).append(",").append(backgroundColor.getBlue()).append(");"); - - return style; + protected void initListener(String link) { + + initListener(() -> { + try { + Desktop.getDesktop().browse(URI.create(link)); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + }); } } diff --git a/designer-base/src/main/java/com/fr/design/ui/util/UIUtil.java b/designer-base/src/main/java/com/fr/design/ui/util/UIUtil.java index b7583d0dd..86093a6d8 100644 --- a/designer-base/src/main/java/com/fr/design/ui/util/UIUtil.java +++ b/designer-base/src/main/java/com/fr/design/ui/util/UIUtil.java @@ -1,9 +1,12 @@ package com.fr.design.ui.util; import com.fr.log.FineLoggerFactory; +import com.fr.third.guava.base.Stopwatch; +import com.fr.third.guava.base.Supplier; import org.jetbrains.annotations.NotNull; import javax.swing.SwingUtilities; +import java.util.concurrent.TimeUnit; /** * 一些常用的 GUI 工具。 @@ -52,4 +55,30 @@ public class UIUtil { } } } + + /** + * 有些时候,交互上需要有一些等待的效果, + * 如果没有等待到, 会让交互失去作用。 + * + * @param supplier 结果 + * @param timeout 超时 + * @param timeUnit 单位 + * @return 结果 + */ + public static T waitUntil(Supplier supplier, long timeout, TimeUnit timeUnit) { + + Stopwatch st = Stopwatch.createStarted(); + T result = supplier.get(); + long elapsed = st.elapsed(timeUnit); + long minus = timeout - elapsed; + if (minus > 0) { + long value = timeUnit.toMillis(minus); + try { + Thread.sleep(value); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + } + return result; + } } diff --git a/designer-base/src/main/java/com/fr/design/utils/DevDebugUtil.java b/designer-base/src/main/java/com/fr/design/utils/DevDebugUtil.java new file mode 100644 index 000000000..2125455d3 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/utils/DevDebugUtil.java @@ -0,0 +1,12 @@ +package com.fr.design.utils; + +/** + * created by Harrison on 2022/05/26 + **/ +public class DevDebugUtil { + + public static void main(String[] args) { + + org.swingexplorer.Launcher.main(new String[]{"com.fr.design.utils.DevUtil"}); + } +} diff --git a/designer-base/src/main/java/com/fr/design/utils/DevUtil.java b/designer-base/src/main/java/com/fr/design/utils/DevUtil.java new file mode 100644 index 000000000..caf6fc966 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/utils/DevUtil.java @@ -0,0 +1,49 @@ +package com.fr.design.utils; + +import com.fr.design.ui.util.UIUtil; +import com.fr.env.detect.ui.EnvDetectorDialog; + +import javax.swing.JFrame; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Toolkit; +import java.util.function.Consumer; + +/** + * 设计器的开发时工具 + * 帮助进行 UI 页面的调试 + * + * created by Harrison on 2022/05/23 + **/ +public class DevUtil { + + public static void show(Consumer consumer) { + + DesignUtils.initLookAndFeel(); + + UIUtil.invokeLaterIfNeeded(() -> { + + JFrame frame = new JFrame("dev-util"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + Dimension dimension = Toolkit.getDefaultToolkit().getScreenSize(); + frame.setSize(dimension); + + consumer.accept(frame); + + frame.setVisible(true); + }); + + } + + public static void main(String[] args) { + + DevUtil.show(new Consumer() { + @Override + public void accept(Frame frame) { + + EnvDetectorDialog envDetectorDialog = new EnvDetectorDialog(frame); + envDetectorDialog.setVisible(true); + } + }); + } +} diff --git a/designer-base/src/main/java/com/fr/design/utils/LinkStrUtil.java b/designer-base/src/main/java/com/fr/design/utils/LinkStrUtil.java new file mode 100644 index 000000000..08cd2614f --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/utils/LinkStrUtil.java @@ -0,0 +1,55 @@ +package com.fr.design.utils; + +import com.fr.design.gui.ilable.UILabel; +import com.fr.stable.StringUtils; + +import java.awt.Color; +import java.awt.Font; + +/** + * created by Harrison on 2022/05/24 + **/ +public class LinkStrUtil { + + public static final UILabel LABEL = new UILabel(); + + public static String generateHtmlTag(String html) { + + String defaultStyle = generateDefaultStyle(); + return generateHtmlTag(defaultStyle, html); + } + + public static String generateHtmlTag(String style, String html) { + + if (StringUtils.isEmpty(style)) { + throw new NullPointerException("style"); + } + if (StringUtils.isEmpty(html)) { + throw new NullPointerException("html"); + } + return "" + html + ""; + } + + public static String generateLinkTag(String link, String text) { + + return "" + text + ""; + } + + public static String generateStyle(Color backgroundColor, Font font, Color fontColor) { + + // 构建相同风格样式 + StringBuilder style = new StringBuilder("font-family:" + font.getFamily() + ";"); + + style.append("font-weight:").append(font.isBold() ? "bold" : "normal").append(";"); + style.append("font-size:").append(font.getSize()).append("pt;"); + style.append("color:rgb(").append(fontColor.getRed()).append(",").append(fontColor.getGreen()).append(",").append(fontColor.getBlue()).append(");"); + style.append("background-color: rgb(").append(backgroundColor.getRed()).append(",").append(backgroundColor.getGreen()).append(",").append(backgroundColor.getBlue()).append(");"); + + return style.toString(); + } + + public static String generateDefaultStyle() { + + return generateStyle(LABEL.getBackground(), LABEL.getFont(), LABEL.getForeground()); + } +} diff --git a/designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_disable_left.svg b/designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_disable_left.svg new file mode 100755 index 000000000..0a20cf97a --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_disable_left.svg @@ -0,0 +1,3 @@ + + + diff --git a/designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_disable_right.svg b/designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_disable_right.svg new file mode 100755 index 000000000..9d111eff9 --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_disable_right.svg @@ -0,0 +1,3 @@ + + + diff --git a/designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_enable_left.svg b/designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_enable_left.svg new file mode 100755 index 000000000..4ab4c796e --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_enable_left.svg @@ -0,0 +1,3 @@ + + + diff --git a/designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_enable_right.svg b/designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_enable_right.svg new file mode 100755 index 000000000..040db18bd --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_enable_right.svg @@ -0,0 +1,3 @@ + + + diff --git a/designer-base/src/main/resources/com/fr/design/standard/loading/loading-120.gif b/designer-base/src/main/resources/com/fr/design/standard/loading/loading-120.gif new file mode 100644 index 0000000000000000000000000000000000000000..99f95a0fa82c6bfc805c298eb3f10a5ecae508ee GIT binary patch literal 174858 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%0AQs0YwYUndsW0e+mDE&)T1idEML%rv>1;*> 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

+ * created by Harrison on 2022/05/23 + **/ +public class DevUtils { + + private static void prepare() { + + DaoContext.setEntityDao(new LocalEntityDao()); + DaoContext.setClassHelperDao(new LocalClassHelperDao()); + DaoContext.setXmlEntityDao(new LocalXmlEntityDao()); + Configurations.setHelper(new LocalConfigurationHelper()); + } + + public static void show(Consumer consumer) { + + DesignUtils.initLookAndFeel(); + + UIUtil.invokeLaterIfNeeded(() -> { + + JFrame frame = new JFrame("dev-util"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + Dimension dimension = Toolkit.getDefaultToolkit().getScreenSize(); + frame.setSize(dimension); + + consumer.accept(frame); + + frame.setVisible(true); + }); + + } + + public static void main(String[] args) { + + DevUtils.prepare(); + DevUtils.show(new Consumer() { + @Override + public void accept(Frame frame) { + + DetectorErrorDialog dialog = new DetectorErrorDialog(frame, Lists.newArrayList(DetectorResult.exception(DetectorType.FINE_DB_PERMISSION, + ExceptionTips.create("test"), + ExceptionSolution.create("111test222", "www.baidu.com", null), + ExceptionLog.create("log")))); + dialog.setVisible(true); + } + }); + } +} diff --git a/designer-base/src/main/java/com/fr/design/utils/LinkStrUtil.java b/designer-base/src/main/java/com/fr/design/utils/LinkStrUtils.java similarity index 98% rename from designer-base/src/main/java/com/fr/design/utils/LinkStrUtil.java rename to designer-base/src/main/java/com/fr/design/utils/LinkStrUtils.java index 08cd2614f..d02c9abff 100644 --- a/designer-base/src/main/java/com/fr/design/utils/LinkStrUtil.java +++ b/designer-base/src/main/java/com/fr/design/utils/LinkStrUtils.java @@ -9,7 +9,7 @@ import java.awt.Font; /** * created by Harrison on 2022/05/24 **/ -public class LinkStrUtil { +public class LinkStrUtils { public static final UILabel LABEL = new UILabel(); From 2d20d14a04f81786939013502fdd76fd38c2d60e Mon Sep 17 00:00:00 2001 From: Harrison Date: Sun, 29 May 2022 15:45:54 +0800 Subject: [PATCH 23/85] =?UTF-8?q?feat:=20REPORT-70565=20=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E7=8E=AF=E5=A2=83=E7=9B=91=E6=B5=8B=EF=BC=88jar?= =?UTF-8?q?=E5=8C=85=E5=BC=82=E5=B8=B8=E3=80=81finedb=E3=80=81=E6=9D=80?= =?UTF-8?q?=E6=AF=92=E8=BD=AF=E4=BB=B6=EF=BC=89=201-=E6=A2=B3=E7=90=86?= =?UTF-8?q?=E9=9C=80=E8=A6=81=E8=BF=9B=E8=A1=8C=E6=A3=80=E6=B5=8B=E7=9A=84?= =?UTF-8?q?=E9=80=BB=E8=BE=91=202-=E6=B7=BB=E5=8A=A0=E5=8D=95=E5=85=83?= =?UTF-8?q?=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/fr/exit/DesignerExiter.java | 46 ++++++++++ .../main/java/com/fr/start/BaseDesigner.java | 3 +- .../notification/NotificationDialogTest.java | 4 +- ...t.java => ClassConflictConvertorTest.java} | 6 +- .../detect/ui/DetectorErrorDialogTest.java | 8 ++ .../env/detect/ui/EnvDetectorDialogTest.java | 37 +++++++++ .../src/main/java/com/fr/start/Designer.java | 22 ++++- .../fr/start/LifecycleFatalErrorHandler.java | 83 +++++++++++++------ .../main/java/com/fr/start/MainDesigner.java | 2 + 9 files changed, 176 insertions(+), 35 deletions(-) rename designer-base/src/test/java/com/fr/env/detect/impl/converter/{ClassConfictConvertorTest.java => ClassConflictConvertorTest.java} (68%) create mode 100644 designer-base/src/test/java/com/fr/env/detect/ui/DetectorErrorDialogTest.java create mode 100644 designer-base/src/test/java/com/fr/env/detect/ui/EnvDetectorDialogTest.java diff --git a/designer-base/src/main/java/com/fr/exit/DesignerExiter.java b/designer-base/src/main/java/com/fr/exit/DesignerExiter.java index cda2e015b..e1a93a46c 100644 --- a/designer-base/src/main/java/com/fr/exit/DesignerExiter.java +++ b/designer-base/src/main/java/com/fr/exit/DesignerExiter.java @@ -1,10 +1,24 @@ package com.fr.exit; +import com.fr.common.util.Collections; +import com.fr.design.RestartHelper; +import com.fr.design.dialog.ErrorDialog; import com.fr.design.env.DesignerWorkspaceGenerator; +import com.fr.design.i18n.Toolkit; +import com.fr.design.mainframe.DesignerContext; +import com.fr.design.mainframe.DesignerFrame; +import com.fr.design.mainframe.messagecollect.StartErrorMessageCollector; +import com.fr.design.mainframe.messagecollect.entity.DesignerErrorMessage; import com.fr.design.monitor.DesignerLifecycleMonitorContext; +import com.fr.env.detect.EnvDetectorCenter; +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.ui.DetectorErrorDialog; +import com.fr.log.FineLoggerFactory; import com.fr.process.engine.core.FineProcessContext; import com.fr.process.engine.core.FineProcessEngineEvent; +import java.util.List; + /** * @author hades * @version 10.0 @@ -17,6 +31,38 @@ public class DesignerExiter { public static DesignerExiter getInstance() { return INSTANCE; } + + public void exit(Throwable throwable) { + + List results = EnvDetectorCenter.getInstance().terminate(throwable); + + if (Collections.isEmpty(results)) { + DesignerFrame designerFrame = DesignerContext.getDesignerFrame(); + DetectorErrorDialog errorDialog = new DetectorErrorDialog(designerFrame, results); + errorDialog.setVisible(true); + } else { + FineLoggerFactory.getLogger().error(throwable.getMessage(), throwable); + StartErrorMessageCollector.getInstance().record(DesignerErrorMessage.UNEXCEPTED_START_FAILED.getId(), + DesignerErrorMessage.UNEXCEPTED_START_FAILED.getMessage(), + throwable.getMessage()); + ErrorDialog dialog = new ErrorDialog(null, Toolkit.i18nText("Fine-Design_Error_Start_Apology_Message"), + Toolkit.i18nText("Fine-Design_Error_Start_Report"), + throwable.getMessage()) { + @Override + protected void okEvent() { + dispose(); + DesignerExiter.getInstance().execute(); + } + + @Override + protected void restartEvent() { + dispose(); + RestartHelper.restart(); + } + }; + dialog.setVisible(true); + } + } public void execute() { DesignerLifecycleMonitorContext.getMonitor().beforeStop(); diff --git a/designer-base/src/main/java/com/fr/start/BaseDesigner.java b/designer-base/src/main/java/com/fr/start/BaseDesigner.java index 360e9cdfa..95f27442d 100644 --- a/designer-base/src/main/java/com/fr/start/BaseDesigner.java +++ b/designer-base/src/main/java/com/fr/start/BaseDesigner.java @@ -32,7 +32,6 @@ import com.fr.process.ProcessEventPipe; import com.fr.process.engine.core.CarryMessageEvent; import com.fr.process.engine.core.FineProcessContext; import com.fr.stable.OperatingSystem; - import com.fr.start.event.LazyStartupEvent; import com.fr.workspace.base.WorkspaceStatus; @@ -138,7 +137,7 @@ public abstract class BaseDesigner extends ToolBarMenuDock { if (!isException) { showDesignerFrame(true); } else { - DesignerExiter.getInstance().execute(); + DesignerExiter.getInstance().exit(e); } } } diff --git a/designer-base/src/test/java/com/fr/design/components/notification/NotificationDialogTest.java b/designer-base/src/test/java/com/fr/design/components/notification/NotificationDialogTest.java index fec7e8069..1ba115c78 100644 --- a/designer-base/src/test/java/com/fr/design/components/notification/NotificationDialogTest.java +++ b/designer-base/src/test/java/com/fr/design/components/notification/NotificationDialogTest.java @@ -1,6 +1,6 @@ package com.fr.design.components.notification; -import com.fr.design.utils.DevUtil; +import com.fr.design.utils.DevUtils; import com.fr.third.guava.collect.Lists; import java.awt.Frame; @@ -15,7 +15,7 @@ public class NotificationDialogTest { public static void testShow() { - DevUtil.show(new Consumer() { + DevUtils.show(new Consumer() { @Override public void accept(Frame frame) { diff --git a/designer-base/src/test/java/com/fr/env/detect/impl/converter/ClassConfictConvertorTest.java b/designer-base/src/test/java/com/fr/env/detect/impl/converter/ClassConflictConvertorTest.java similarity index 68% rename from designer-base/src/test/java/com/fr/env/detect/impl/converter/ClassConfictConvertorTest.java rename to designer-base/src/test/java/com/fr/env/detect/impl/converter/ClassConflictConvertorTest.java index bf43c5560..cdc789b01 100644 --- a/designer-base/src/test/java/com/fr/env/detect/impl/converter/ClassConfictConvertorTest.java +++ b/designer-base/src/test/java/com/fr/env/detect/impl/converter/ClassConflictConvertorTest.java @@ -2,13 +2,13 @@ package com.fr.env.detect.impl.converter; import org.junit.Test; -public class ClassConfictConvertorTest { +public class ClassConflictConvertorTest { @Test public void testInnerFinder() { ClassNotFoundException ex1 = new ClassNotFoundException("Class 111.222.333 not found"); - Iterable names = ClassConfictConvertor.Converter.CLASS.converter(ex1); + Iterable names = ClassConflictConvertor.Converter.CLASS.converter(ex1); System.out.println(); } @@ -17,7 +17,7 @@ public class ClassConfictConvertorTest { public void testConverter() { ClassNotFoundException ex1 = new ClassNotFoundException("com.zaxxer.hikari.HikariConfig"); - ClassConfictConvertor convertor = new ClassConfictConvertor(); + ClassConflictConvertor convertor = new ClassConflictConvertor(); convertor.convert(ex1); } } \ No newline at end of file diff --git a/designer-base/src/test/java/com/fr/env/detect/ui/DetectorErrorDialogTest.java b/designer-base/src/test/java/com/fr/env/detect/ui/DetectorErrorDialogTest.java new file mode 100644 index 000000000..42d7923b8 --- /dev/null +++ b/designer-base/src/test/java/com/fr/env/detect/ui/DetectorErrorDialogTest.java @@ -0,0 +1,8 @@ +package com.fr.env.detect.ui; + +import static org.junit.Assert.*; + +public class DetectorErrorDialogTest { + + +} \ No newline at end of file diff --git a/designer-base/src/test/java/com/fr/env/detect/ui/EnvDetectorDialogTest.java b/designer-base/src/test/java/com/fr/env/detect/ui/EnvDetectorDialogTest.java new file mode 100644 index 000000000..fd63b1714 --- /dev/null +++ b/designer-base/src/test/java/com/fr/env/detect/ui/EnvDetectorDialogTest.java @@ -0,0 +1,37 @@ +package com.fr.env.detect.ui; + +import com.fr.design.utils.DevUtils; +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.DetectorStatus; +import com.fr.env.detect.bean.DetectorType; + +import java.awt.Frame; +import java.util.function.Consumer; + +public class EnvDetectorDialogTest { + + public static void main(String[] args) { + + testShow(); + } + + private static void testShow() { + + DevUtils.show(new Consumer() { + @Override + public void accept(Frame frame) { + + EnvDetectorModel envDetectorModel = new EnvDetectorModel(); + envDetectorModel.update(DetectorType.JAR_CONFLICT, DetectorResult.builder() + .withTips("test") + .withSolution("test", "abc") + .withStatus(DetectorStatus.EXCEPTION) + .build()); + + EnvDetectorDialog envDetectorDialog = new EnvDetectorDialog(frame, envDetectorModel); + envDetectorDialog.setVisible(true); + } + }); + } + +} \ No newline at end of file diff --git a/designer-realize/src/main/java/com/fr/start/Designer.java b/designer-realize/src/main/java/com/fr/start/Designer.java index 1f8d181ce..c2ec299ea 100644 --- a/designer-realize/src/main/java/com/fr/start/Designer.java +++ b/designer-realize/src/main/java/com/fr/start/Designer.java @@ -1,6 +1,7 @@ package com.fr.start; import com.fr.design.os.impl.SupportOSImpl; +import com.fr.exit.DesignerExiter; import com.fr.log.FineLoggerFactory; /** @@ -22,11 +23,26 @@ public class Designer { // 创建进程 DesignerLauncher.getInstance().start(args); } - } catch (Exception e) { - runNonGuardianDesigner(args); - FineLoggerFactory.getLogger().error(e.getMessage(), e); + } catch (Throwable ex1) { + try { + FineLoggerFactory.getLogger().error(ex1.getMessage(), ex1); + runNonGuardianDesigner(args); + } catch (Throwable ex2) { + // 异常退出 + exitExceptionally(ex2); + } } } + + /** + * 异常退出 + * + * @param throwable 异常 + */ + private static void exitExceptionally(Throwable throwable) { + + DesignerExiter.getInstance().exit(throwable); + } /** * 启动非守护设计器 diff --git a/designer-realize/src/main/java/com/fr/start/LifecycleFatalErrorHandler.java b/designer-realize/src/main/java/com/fr/start/LifecycleFatalErrorHandler.java index 446253e90..2a9103f4a 100644 --- a/designer-realize/src/main/java/com/fr/start/LifecycleFatalErrorHandler.java +++ b/designer-realize/src/main/java/com/fr/start/LifecycleFatalErrorHandler.java @@ -2,11 +2,15 @@ package com.fr.start; import com.fr.common.report.ReportState; import com.fr.design.RestartHelper; -import com.fr.design.dialog.ErrorDialog; import com.fr.design.dialog.FineJOptionPane; import com.fr.design.i18n.Toolkit; import com.fr.design.mainframe.messagecollect.StartErrorMessageCollector; import com.fr.design.mainframe.messagecollect.entity.DesignerErrorMessage; +import com.fr.env.detect.EnvDetectorCenter; +import com.fr.env.detect.base.DetectorBridge; +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.DetectorStatus; +import com.fr.env.detect.bean.DetectorType; import com.fr.exit.DesignerExiter; import com.fr.general.IOUtils; import com.fr.io.utils.ResourceIOUtils; @@ -19,7 +23,7 @@ import com.fr.stable.lifecycle.ErrorTypeHelper; import com.fr.stable.lifecycle.FineLifecycleFatalError; import com.fr.stable.project.ProjectConstants; -import javax.swing.*; +import javax.swing.JOptionPane; import java.util.HashMap; import java.util.Map; @@ -60,16 +64,22 @@ public class LifecycleFatalErrorHandler { * finedb处理 */ enum FineDBHandler implements Handler { - + + /** + * 自检测 + */ SELF { @Override public void handle(FineLifecycleFatalError fatal) { + + String showText = generateShowText(fatal); + StartErrorMessageCollector.getInstance().record(DesignerErrorMessage.FINEDB_PROBLEM.getId(), DesignerErrorMessage.FINEDB_PROBLEM.getMessage(), fatal.getMessage()); FineLoggerFactory.getLogger().error(DesignerErrorMessage.FINEDB_PROBLEM.getId() + ": " + DesignerErrorMessage.FINEDB_PROBLEM.getMessage()); int result = FineJOptionPane.showOptionDialog(null, - Toolkit.i18nText("Fine-Design_Error_Finedb_Backup_Reset"), + showText, Toolkit.i18nText("Fine-Design_Basic_Error_Tittle"), JOptionPane.YES_NO_OPTION, JOptionPane.ERROR_MESSAGE, @@ -95,8 +105,45 @@ public class LifecycleFatalErrorHandler { DesignerExiter.getInstance().execute(); } } - + + /** + * 生成展示信息 + * + * @param fatal 异常 + * @return 文本 + */ + private String generateShowText(FineLifecycleFatalError fatal) { + + // todo 其实这里的交互还是有问题, 为什么在锁住和没权限的场景下,要重置 FineDB 呢。 + DetectorResult detectorResult = DetectorBridge.getInstance().detect(DetectorType.FINE_DB_LOCKED, fatal); + if (detectorResult.getStatus() == DetectorStatus.NORMAL) { + detectorResult = DetectorBridge.getInstance().detect(DetectorType.FINE_DB_PERMISSION, fatal); + } + if (detectorResult.getStatus() == DetectorStatus.NORMAL) { + detectorResult = DetectorBridge.getInstance().detect(DetectorType.FINE_DB_DIRTY, fatal); + } + + String message; + + DetectorType type = detectorResult.getType(); + switch (type) { + case FINE_DB_LOCKED: + message = Toolkit.i18nText("Fine-Design_Error_Finedb_Locked_Backup_Reset"); + break; + case FINE_DB_PERMISSION: + message = Toolkit.i18nText("Fine-Design_Error_Finedb_Permission_Backup_Reset"); + break; + case FINE_DB_DIRTY: + message = Toolkit.i18nText("Fine-Design_Error_Finedb_Dirty_Backup_Reset"); + break; + default: + message = Toolkit.i18nText("Fine-Design_Error_Finedb_Backup_Reset"); + } + return message; + } + private void afterBackupFailed() { + FineJOptionPane.showMessageDialog(null, Toolkit.i18nText("Fine-Design_Error_Finedb_Backup_Reset_Result", ResourceIOUtils.getRealPath(StableUtils.pathJoin(ProjectConstants.EMBED_DB_DIRECTORY, ProjectConstants.FINE_DB_NAME))), @@ -111,29 +158,15 @@ public class LifecycleFatalErrorHandler { * 默认处理 */ enum DefaultHandler implements Handler { + + /** + * 自处理 + */ SELF { @Override public void handle(FineLifecycleFatalError fatal) { - FineLoggerFactory.getLogger().error(fatal.getMessage(), fatal); - StartErrorMessageCollector.getInstance().record(DesignerErrorMessage.UNEXCEPTED_START_FAILED.getId(), - DesignerErrorMessage.UNEXCEPTED_START_FAILED.getMessage(), - fatal.getMessage()); - ErrorDialog dialog = new ErrorDialog(null, Toolkit.i18nText("Fine-Design_Error_Start_Apology_Message"), - Toolkit.i18nText("Fine-Design_Error_Start_Report"), - fatal.getMessage()) { - @Override - protected void okEvent() { - dispose(); - DesignerExiter.getInstance().execute(); - } - - @Override - protected void restartEvent() { - dispose(); - RestartHelper.restart(); - } - }; - dialog.setVisible(true); + + EnvDetectorCenter.getInstance().terminate(fatal); } } } diff --git a/designer-realize/src/main/java/com/fr/start/MainDesigner.java b/designer-realize/src/main/java/com/fr/start/MainDesigner.java index f60003c9a..09d740683 100644 --- a/designer-realize/src/main/java/com/fr/start/MainDesigner.java +++ b/designer-realize/src/main/java/com/fr/start/MainDesigner.java @@ -46,6 +46,7 @@ import com.fr.design.share.SharableManager; import com.fr.design.ui.util.UIUtil; import com.fr.design.utils.concurrent.ThreadFactoryBuilder; import com.fr.design.utils.gui.GUICoreUtils; +import com.fr.env.detect.EnvDetectorCenter; import com.fr.env.utils.DesignerInteractionHistory; import com.fr.event.Event; import com.fr.event.EventDispatcher; @@ -110,6 +111,7 @@ public class MainDesigner extends BaseDesigner { */ public static void main(String[] args) { + EnvDetectorCenter.getInstance().init(); showSplash(); DeepLinkManager.getInstance().start(args); StopWatch watch = new StopWatch(); From 9457aebed665d355f852ceb3127503356ba73aa4 Mon Sep 17 00:00:00 2001 From: Harrison Date: Mon, 30 May 2022 14:38:03 +0800 Subject: [PATCH 24/85] =?UTF-8?q?feat:=20REPORT-70565=20=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E7=8E=AF=E5=A2=83=E7=9B=91=E6=B5=8B=EF=BC=88jar?= =?UTF-8?q?=E5=8C=85=E5=BC=82=E5=B8=B8=E3=80=81finedb=E3=80=81=E6=9D=80?= =?UTF-8?q?=E6=AF=92=E8=BD=AF=E4=BB=B6=EF=BC=89=20=E8=B0=83=E6=95=B4?= =?UTF-8?q?=E4=B8=80=E4=B8=8B=E7=8E=AF=E5=A2=83=E6=A3=80=E6=B5=8B=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E7=9A=84=E5=86=85=E5=AE=B9=201-=E5=9B=BD=E9=99=85?= =?UTF-8?q?=E5=8C=96=E6=95=88=E6=9E=9C\=E9=80=9A=E8=BF=87=20html=20?= =?UTF-8?q?=E5=B0=81=E8=A3=85=20text=20=E5=AE=9E=E7=8E=B0=E6=9B=BF?= =?UTF-8?q?=E6=8D=A2=E9=80=BB=E8=BE=91=202-=E8=A1=A5=E5=85=85=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=203-=E6=B7=BB=E5=8A=A0=20EnvPrepare=20=E6=B3=A8?= =?UTF-8?q?=E5=85=A5=E5=88=B0=E7=8E=AF=E5=A2=83=E4=B8=AD=EF=BC=8C=E7=94=A8?= =?UTF-8?q?=E6=9D=A5=E5=B8=AE=E5=8A=A9=E7=8E=AF=E5=A2=83=E5=90=AF=E5=8A=A8?= =?UTF-8?q?=E6=97=B6=E7=9A=84=E4=B8=80=E4=BA=9B=E7=9B=B8=E5=85=B3=E9=92=A9?= =?UTF-8?q?=E5=AD=90=E7=9A=84=E5=90=AF=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/fr/env/detect/EnvDetectorCenter.java | 58 ++++++++------- .../java/com/fr/env/detect/EnvPrepare.java | 21 ++++++ .../fr/env/detect/base/DetectorBridge.java | 13 ++-- .../fr/env/detect/base/DetectorManager.java | 10 ++- .../com/fr/env/detect/base/DetectorUtil.java | 70 +++++++++++++------ .../fr/env/detect/base/EnvDetectorConfig.java | 59 ++++++++++++++++ .../detect/base/ExceptionDetectorConfig.java | 34 --------- .../detect/impl/JarInconsistentDetector.java | 12 ++-- .../fr/env/detect/impl/JarLackDetector.java | 34 +++++---- .../converter/ClassConflictConvertor.java | 25 ++++--- .../impl/converter/FineDbDirtyConverter.java | 46 ++++-------- .../impl/converter/FineDbLockedConverter.java | 5 +- .../converter/FineDbPermissionConverter.java | 5 +- .../detect/thowable/ThrowableLogAppender.java | 17 +++-- .../fr/env/detect/ui/DetectorErrorDialog.java | 8 +-- .../fr/env/detect/ui/EnvDetectorAction.java | 28 ++++++++ .../fr/env/detect/ui/EnvDetectorDialog.java | 8 +-- .../com/fr/env/detect/ui/EnvDetectorItem.java | 2 + .../fr/env/detect/ui/EnvDetectorModel.java | 17 ++++- .../resources/com/fr/env/detect/detect.svg | 3 + .../com/fr/env/detect/detect_normal.svg | 3 + 21 files changed, 314 insertions(+), 164 deletions(-) create mode 100644 designer-base/src/main/java/com/fr/env/detect/EnvPrepare.java create mode 100644 designer-base/src/main/java/com/fr/env/detect/base/EnvDetectorConfig.java delete mode 100644 designer-base/src/main/java/com/fr/env/detect/base/ExceptionDetectorConfig.java create mode 100644 designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorAction.java create mode 100644 designer-base/src/main/resources/com/fr/env/detect/detect.svg create mode 100644 designer-base/src/main/resources/com/fr/env/detect/detect_normal.svg diff --git a/designer-base/src/main/java/com/fr/env/detect/EnvDetectorCenter.java b/designer-base/src/main/java/com/fr/env/detect/EnvDetectorCenter.java index 5a524e5b5..98938a4f8 100644 --- a/designer-base/src/main/java/com/fr/env/detect/EnvDetectorCenter.java +++ b/designer-base/src/main/java/com/fr/env/detect/EnvDetectorCenter.java @@ -1,5 +1,6 @@ package com.fr.env.detect; +import com.fr.common.util.Collections; import com.fr.design.components.notification.NotificationDialog; import com.fr.design.components.notification.NotificationDialogProperties; import com.fr.design.components.notification.NotificationModel; @@ -28,6 +29,9 @@ import java.util.stream.Collectors; import java.util.stream.Stream; /** + * 环境检测中心 + * 如果环境检测 -> + * * created by Harrison on 2022/05/27 **/ public class EnvDetectorCenter { @@ -42,6 +46,9 @@ public class EnvDetectorCenter { private final AtomicReference PROCESS = new AtomicReference<>(); + /** + * 初始化 + */ public void init() { // 默认是启动 @@ -58,18 +65,11 @@ public class EnvDetectorCenter { } }); - // 切换工作目录 - EventDispatcher.listen(WorkspaceEvent.BeforeSwitch, new Listener() { - @Override - public void on(Event event, Workspace param) { - PROCESS.set(DetectorProcess.ENV_SWITCH); - start(); - } - }); + // 切换完成后的监听 EventDispatcher.listen(WorkspaceEvent.AfterSwitch, new Listener() { @Override public void on(Event event, Workspace param) { - if (isSameProcess(DetectorProcess.ENV_SWITCH)) { + if (isSameProcess(DetectorProcess.DESIGN_LAUNCH)) { stop(); } } @@ -108,33 +108,44 @@ public class EnvDetectorCenter { return PROCESS.compareAndSet(process, null); } + /** + * 启动 + */ public void start() { DetectorBridge.getInstance().start(); } + /** + * 关闭 + */ public void stop() { - Stream resultStream = DetectorBridge.getInstance().detect(); - - // 展示效果 - NotificationDialogProperties properties = new NotificationDialogProperties(DesignerContext.getDesignerFrame(), Toolkit.i18nText("Fine-Design_Basic_Detect_Notification_Title")); - List notificationModels = resultStream - .filter(Objects::nonNull) - .filter((e) -> e.getStatus() == DetectorStatus.EXCEPTION) - .map(DetectorUtil::convert2Notification) - .collect(Collectors.toList()); - // 一分钟后执行 DelayHelper.delayCall(EnvDetectorCenter.class.getName(), () -> { + + Stream resultStream = DetectorBridge.getInstance().detect(); + + // 结束 + DetectorBridge.getInstance().stop(); + + // 展示效果 + NotificationDialogProperties properties = new NotificationDialogProperties(DesignerContext.getDesignerFrame(), Toolkit.i18nText("Fine-Design_Basic_Detect_Notification_Title")); + List notificationModels = resultStream + .filter(Objects::nonNull) + .filter((e) -> e.getStatus() == DetectorStatus.EXCEPTION) + .map(DetectorUtil::convert2Notification) + .collect(Collectors.toList()); + if (Collections.isEmpty(notificationModels)) { + return; + } + UIUtil.invokeLaterIfNeeded(() -> { NotificationDialog dialog = new NotificationDialog(properties, notificationModels); dialog.open(); }); - }, 1, TimeUnit.MINUTES); + }, 30, TimeUnit.SECONDS); - // 结束 - DetectorBridge.getInstance().stop(); } /** @@ -146,10 +157,9 @@ public class EnvDetectorCenter { public List terminate(Throwable throwable) { Stream resultStream = DetectorBridge.getInstance().detect(throwable); - List results = resultStream + return resultStream .filter((e) -> e.getStatus() == DetectorStatus.EXCEPTION) .collect(Collectors.toList()); - return results; } private enum DetectorProcess { diff --git a/designer-base/src/main/java/com/fr/env/detect/EnvPrepare.java b/designer-base/src/main/java/com/fr/env/detect/EnvPrepare.java new file mode 100644 index 000000000..eedc591db --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/EnvPrepare.java @@ -0,0 +1,21 @@ +package com.fr.env.detect; + +import com.fr.module.Activator; + +/** + * 设计器环境准备 + * + * created by Harrison on 2022/05/29 + **/ +public class EnvPrepare extends Activator { + + @Override + public void start() { + EnvDetectorCenter.getInstance().init(); + } + + @Override + public void stop() { + + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/base/DetectorBridge.java b/designer-base/src/main/java/com/fr/env/detect/base/DetectorBridge.java index 534fa7abf..0d5b60bc0 100644 --- a/designer-base/src/main/java/com/fr/env/detect/base/DetectorBridge.java +++ b/designer-base/src/main/java/com/fr/env/detect/base/DetectorBridge.java @@ -17,6 +17,9 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Stream; /** + * 检测器桥接逻辑 + * [detect-core] - bridge - ui + * * created by Harrison on 2022/05/13 **/ public class DetectorBridge { @@ -53,7 +56,7 @@ public class DetectorBridge { public void start() { - if (!hasStarted.get() && ExceptionDetectorConfig.getInstance().isOpen()) { + if (!hasStarted.get() && EnvDetectorConfig.getInstance().isEnabled()) { // 开始注册异常处理 ThrowableLogAppender.getInstance().enable(); hasStarted.set(true); @@ -91,11 +94,10 @@ public class DetectorBridge { @NotNull public DetectorResult detect(DetectorType type, Throwable throwable) { - // 先清理一下 - ThrowableStore.getInstance().reset(); - ThrowableStore.getInstance().add(throwable); + DetectorResult result = detect(type); + ThrowableStore.getInstance().reset(); return result; @@ -123,9 +125,6 @@ public class DetectorBridge { */ public Stream detect(Throwable throwable) { - // 先清理一下 - ThrowableStore.getInstance().reset(); - ThrowableStore.getInstance().add(throwable); Stream result = detect(); ThrowableStore.getInstance().reset(); diff --git a/designer-base/src/main/java/com/fr/env/detect/base/DetectorManager.java b/designer-base/src/main/java/com/fr/env/detect/base/DetectorManager.java index ced502129..bc65870da 100644 --- a/designer-base/src/main/java/com/fr/env/detect/base/DetectorManager.java +++ b/designer-base/src/main/java/com/fr/env/detect/base/DetectorManager.java @@ -7,9 +7,12 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.stream.Collectors; import java.util.stream.Stream; /** + * 检测器中心 + * * created by Harrison on 2022/05/24 **/ class DetectorManager { @@ -29,9 +32,10 @@ class DetectorManager { .map(ExceptionDetector::detect) .filter(Objects::nonNull); - // 记录一下日志 - results.forEach(DetectorResult::log); - return results; + List resultList = results.collect(Collectors.toList()); + resultList.forEach(DetectorResult::log); + + return resultList.stream(); } public DetectorResult detect(DetectorType type) { diff --git a/designer-base/src/main/java/com/fr/env/detect/base/DetectorUtil.java b/designer-base/src/main/java/com/fr/env/detect/base/DetectorUtil.java index 12c51d19b..fdfccf216 100644 --- a/designer-base/src/main/java/com/fr/env/detect/base/DetectorUtil.java +++ b/designer-base/src/main/java/com/fr/env/detect/base/DetectorUtil.java @@ -7,6 +7,7 @@ import com.fr.design.components.notification.NotificationModel; import com.fr.design.components.notification.NotificationType; import com.fr.design.dialog.link.MessageWithLink; import com.fr.design.gui.ilable.UILabel; +import com.fr.design.utils.LinkStrUtils; import com.fr.env.detect.bean.DetectorResult; import com.fr.env.detect.bean.ExceptionSolution; import com.fr.env.detect.bean.ExceptionTips; @@ -29,11 +30,26 @@ import java.util.function.Function; **/ public class DetectorUtil { + /** + * 是否是设计器的 jar + * + * @param info 信息 + * @return 是/否 + */ public static boolean isDesignerJar(BuildInfo info) { - + + if (info == null) { + return false; + } return StringUtils.contains(info.getJar(), "fine-report-designer"); } + /** + * 将结果转化为提醒的数据 + * + * @param result 结果 + * @return 数据 + */ public static NotificationModel convert2Notification(DetectorResult result) { List messages = new ArrayList<>(); @@ -59,34 +75,46 @@ public class DetectorUtil { }; ExceptionTips tips = result.getTips(); - convert2NotificationMsg - .apply(tips.getMessage()) - .ifPresent(messages::add); + if (tips != null) { + convert2NotificationMsg + .apply(tips.getMessage()) + .ifPresent(messages::add); + } ExceptionSolution solution = result.getSolution(); - convert2NotificationMsg.apply(solution.getMessage()) - .ifPresent(messages::add); + if (solution != null) { + convert2NotificationMsg.apply(solution.getMessage()) + .ifPresent(messages::add); + } NotificationAction notificationAction = null; - SolutionAction solutionAction = solution.getAction(); - if (solutionAction != null) { - notificationAction = new NotificationAction() { - @Override - public String name() { - return solutionAction.name(); - } - - @Override - public void run(Object... args) { - solutionAction.run(); - } - }; + if (solution != null) { + SolutionAction solutionAction = solution.getAction(); + if (solutionAction != null) { + notificationAction = new NotificationAction() { + @Override + public String name() { + return solutionAction.name(); + } + + @Override + public void run(Object... args) { + solutionAction.run(); + } + }; + } } return new NotificationModel(NotificationType.WARNING, notificationAction, messages); } - public static JComponent convert2Component(@NotNull Message message) { + /** + * 将信息转化为展示的组件 + * + * @param message 信息 + * @return 组件 + */ + public static JComponent convert2TextComponent(@NotNull Message message) { if (message.getType() == Message.Type.LINK) { Message.Link linkMsg = (Message.Link) message; @@ -94,6 +122,6 @@ public class DetectorUtil { Desktop.getDesktop().browse(URI.create(linkMsg.getLink())); })); } - return new UILabel(message.get()); + return new UILabel(LinkStrUtils.generateHtmlTag(message.get())); } } diff --git a/designer-base/src/main/java/com/fr/env/detect/base/EnvDetectorConfig.java b/designer-base/src/main/java/com/fr/env/detect/base/EnvDetectorConfig.java new file mode 100644 index 000000000..caafdbe46 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/base/EnvDetectorConfig.java @@ -0,0 +1,59 @@ +package com.fr.env.detect.base; + +import com.fr.design.DesignerEnvManager; +import com.fr.stable.xml.XMLPrintWriter; +import com.fr.stable.xml.XMLable; +import com.fr.stable.xml.XMLableReader; + +/** + * created by Harrison on 2022/05/13 + **/ +public class EnvDetectorConfig implements XMLable { + + public static final String XML_TAG = "EnvDetectorConfig"; + + private static final long serialVersionUID = -8170289826729582122L; + + private static final EnvDetectorConfig INSTANCE = new EnvDetectorConfig(); + + public static EnvDetectorConfig getInstance() { + + return INSTANCE; + } + + private boolean enabled = true; + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + save(); + } + + private void save() { + + DesignerEnvManager.getEnvManager(false).saveXMLFile(); + } + + @Override + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + + @Override + public void readXML(XMLableReader reader) { + if (reader.isAttr()) { + this.setEnabled(reader.getAttrAsBoolean("isEnabled", true)); + } + } + + @Override + public void writeXML(XMLPrintWriter writer) { + writer.startTAG(XML_TAG); + writer.attr("isEnabled", this.isEnabled()); + writer.end(); + } + +} diff --git a/designer-base/src/main/java/com/fr/env/detect/base/ExceptionDetectorConfig.java b/designer-base/src/main/java/com/fr/env/detect/base/ExceptionDetectorConfig.java deleted file mode 100644 index 724e5e8d3..000000000 --- a/designer-base/src/main/java/com/fr/env/detect/base/ExceptionDetectorConfig.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.fr.env.detect.base; - -import com.fr.config.ConfigContext; -import com.fr.config.DefaultConfiguration; -import com.fr.config.holder.Conf; -import com.fr.config.holder.factory.Holders; - -/** - * created by Harrison on 2022/05/13 - **/ -public class ExceptionDetectorConfig extends DefaultConfiguration { - - private static volatile ExceptionDetectorConfig instance = null; - - public static ExceptionDetectorConfig getInstance() { - - if (instance == null) { - instance = ConfigContext.getConfigInstance(ExceptionDetectorConfig.class); - } - return instance; - } - - private final Conf open = Holders.simple(true); - - public void setOpen(boolean open) { - - this.open.set(open); - } - - public boolean isOpen() { - - return open.get(); - } -} diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/JarInconsistentDetector.java b/designer-base/src/main/java/com/fr/env/detect/impl/JarInconsistentDetector.java index 6012be3d3..679671997 100644 --- a/designer-base/src/main/java/com/fr/env/detect/impl/JarInconsistentDetector.java +++ b/designer-base/src/main/java/com/fr/env/detect/impl/JarInconsistentDetector.java @@ -33,6 +33,8 @@ import java.util.stream.Collectors; **/ public class JarInconsistentDetector extends AbstractExceptionDetector { + public static final String SEPARATOR = ","; + public JarInconsistentDetector() { super(DetectorType.JAR_IN_CONSISTENCE); @@ -81,14 +83,14 @@ public class JarInconsistentDetector extends AbstractExceptionDetector { Set inConsistentJars = diffInCommon.keySet(); - String message = StringUtils.join(",", inConsistentJars); - Message.Simple tipsMessage = new Message.Simple(Toolkit.i18nText("Fine_Design_Basic_Detect_Server") + Toolkit.i18nText(type().getTipsLocale()) + "\n" + message); + String message = StringUtils.join(inConsistentJars, SEPARATOR); + Message.Simple tipsMessage = new Message.Simple(Toolkit.i18nText("Fine_Design_Basic_Detect_Server") + Toolkit.i18nText(type().getTipsLocale()) + message); return DetectorResult.exception(type(), new ExceptionTips(tipsMessage), new ExceptionSolution(new Message.Link(Toolkit.i18nText(type().getSolutionLocale(),DetectorConstants.JAR_HELP_LINK) , DetectorConstants.JAR_HELP_LINK), null), - ExceptionLog.create(type().getLogLocale() + message)); + ExceptionLog.create(Toolkit.i18nText(type().getLogLocale()) + message)); } @NotNull @@ -124,8 +126,8 @@ public class JarInconsistentDetector extends AbstractExceptionDetector { List inConsistentJars = inConsistentInfos.stream() .map(BuildInfo::getJar) .collect(Collectors.toList()); - String message = StringUtils.join(",", inConsistentJars); - String tipsMessage = Toolkit.i18nText("Fine_Design_Basic_Detect_Local") + Toolkit.i18nText(type().getTipsLocale()) + "\n" + message; + String message = StringUtils.join(inConsistentJars, SEPARATOR); + String tipsMessage = Toolkit.i18nText("Fine_Design_Basic_Detect_Local") + Toolkit.i18nText(type().getTipsLocale()) + message; return DetectorResult.exception(type(), new ExceptionTips(new Message.Simple(tipsMessage)), diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/JarLackDetector.java b/designer-base/src/main/java/com/fr/env/detect/impl/JarLackDetector.java index 2175b8bb1..74c908000 100644 --- a/designer-base/src/main/java/com/fr/env/detect/impl/JarLackDetector.java +++ b/designer-base/src/main/java/com/fr/env/detect/impl/JarLackDetector.java @@ -1,7 +1,7 @@ package com.fr.env.detect.impl; import com.fr.common.util.Collections; -import com.fr.common.util.Strings; +import com.fr.design.i18n.Toolkit; import com.fr.env.detect.base.AbstractExceptionDetector; import com.fr.env.detect.base.DetectorConstants; import com.fr.env.detect.base.DetectorUtil; @@ -14,7 +14,6 @@ import com.fr.env.detect.bean.Message; import com.fr.general.build.BuildInfo; import com.fr.general.build.BuildInfoOperator; import com.fr.general.build.impl.BuildInfoOperatorImpl; -import com.fr.locale.InterProviderFactory; import com.fr.third.guava.collect.Lists; import com.fr.third.org.apache.commons.lang3.StringUtils; import com.fr.workspace.WorkContext; @@ -30,6 +29,11 @@ import java.util.stream.Collectors; **/ public class JarLackDetector extends AbstractExceptionDetector { + public static final String SEPARATOR = "、"; + public static final String BR_TAG = "
"; + public static final String WEB_LIB_PATH = "%FR_HOME%\\webapps\\webroot\\WEB-INF\\lib:"; + public static final String FR_HOME_LIB = "%FR_HOME%\\lib:"; + public JarLackDetector() { super(DetectorType.LACK_OF_JAR); @@ -57,9 +61,10 @@ public class JarLackDetector extends AbstractExceptionDetector { Message tipsMsg = tipsMessage(lackInfos); ExceptionLog exceptionLog = exceptionLog(lackInfos); - return DetectorResult.exception(type(), new ExceptionTips(tipsMsg), + return DetectorResult.exception(type(), + new ExceptionTips(tipsMsg), new ExceptionSolution( - new Message.Link(InterProviderFactory.getProvider().getLocText(type().getSolutionLocale()), DetectorConstants.JAR_HELP_LINK), + new Message.Link(Toolkit.i18nText(type().getSolutionLocale()), DetectorConstants.JAR_HELP_LINK), null), exceptionLog); } @@ -68,8 +73,8 @@ public class JarLackDetector extends AbstractExceptionDetector { List jarInfos = lackInfos.stream() .map(BuildInfo::getJar) .collect(Collectors.toList()); - String message = StringUtils.join(",", jarInfos); - return ExceptionLog.create(type().getLogLocale() + message); + String message = StringUtils.join(jarInfos, ","); + return ExceptionLog.create(Toolkit.i18nText(type().getLogLocale()) + message); } private boolean isLackInfo(BuildInfo e) { @@ -78,8 +83,8 @@ public class JarLackDetector extends AbstractExceptionDetector { private Message tipsMessage(List infos) { - String webLibPath = "%FR_HOME%\\webapps\\webroot\\WEB-INF\\lib:"; - String homeLibPath = "%FR_HOME%\\lib:"; + String webLibPath = WEB_LIB_PATH; + String homeLibPath = FR_HOME_LIB; Map> libMap = groupByPath(infos, homeLibPath, webLibPath); @@ -87,23 +92,24 @@ public class JarLackDetector extends AbstractExceptionDetector { List homeLibs = libMap.get(homeLibPath); if (!Collections.isEmpty(homeLibs)) { - sb.append(homeLibPath).append(":"); - sb.append(StringUtils.join("、", homeLibs)); + sb.append(homeLibPath); + sb.append(StringUtils.join(homeLibs, SEPARATOR)); } List webLibs = libMap.get(webLibPath); if (!Collections.isEmpty(webLibs)) { if (sb.length() != 0) { - sb.append("\n"); + sb.append(BR_TAG); } - sb.append(Strings.join("、", webLibs)); + sb.append(webLibPath); + sb.append(StringUtils.join(webLibs, SEPARATOR)); } String mainContent = sb.toString(); DetectorType type = this.type(); - String header = InterProviderFactory.getProvider().getLocText(type.getTipsLocale()); - return new Message.Simple(header + "\n" + mainContent); + String header = Toolkit.i18nText(type.getTipsLocale()); + return new Message.Simple(header + mainContent); } @NotNull diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassConflictConvertor.java b/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassConflictConvertor.java index 4fa33f301..3432d976d 100644 --- a/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassConflictConvertor.java +++ b/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassConflictConvertor.java @@ -1,6 +1,5 @@ package com.fr.env.detect.impl.converter; -import com.fr.common.util.Strings; import com.fr.design.i18n.Toolkit; import com.fr.env.detect.base.DetectorConstants; import com.fr.env.detect.bean.DetectorResult; @@ -10,6 +9,8 @@ import com.fr.env.detect.bean.ExceptionSolution; import com.fr.env.detect.bean.ExceptionTips; import com.fr.env.detect.thowable.ThrowableConverter; import com.fr.stable.resource.ResourceLoader; +import com.fr.third.org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; import javax.el.MethodNotFoundException; import java.io.IOException; @@ -33,13 +34,15 @@ import java.util.regex.Pattern; **/ public class ClassConflictConvertor implements ThrowableConverter { - private Map, ClassNameConverter> throwableMap = new HashMap<>(); - + public static final String CLASSES = "classes"; + public static final String SEPARATOR = "、"; /** * 获取对应的 JAR 包名称 */ private static final Pattern JAR_NAME_PATTERN = Pattern.compile("([\\w+-\\.]*\\.jar)"); + private final Map, ClassNameConverter> throwableMap = new HashMap<>(); + public ClassConflictConvertor() { // 类异常 @@ -82,7 +85,7 @@ public class ClassConflictConvertor implements ThrowableConverter { Set allPath = new HashSet<>(); for (String className : classNames) { - String classFile = "/" + className.replaceAll("\\.", "/") + ".class"; + String classFile = convertClass2Path(className); try { Enumeration urls = ResourceLoader.getResources(classFile, this.getClass()); List urlList = new ArrayList<>(); @@ -97,9 +100,9 @@ public class ClassConflictConvertor implements ThrowableConverter { String jar = matcher.group(); allPath.add(jar); } else { - boolean containsClasses = file.contains("classes"); + boolean containsClasses = file.contains(CLASSES); if (containsClasses) { - allPath.add("classes"); + allPath.add(CLASSES); } } } @@ -107,15 +110,21 @@ public class ClassConflictConvertor implements ThrowableConverter { } } - String msg = Strings.join("、", allPath); + String msg = StringUtils.join(allPath, SEPARATOR); DetectorType type = DetectorType.JAR_CONFLICT; return DetectorResult.exception(type, - ExceptionTips.create(Toolkit.i18nText(type.getTipsLocale()) + msg), + ExceptionTips.create(Toolkit.i18nText(type.getTipsLocale(), msg)), ExceptionSolution.create(Toolkit.i18nText(type.getSolutionLocale()), DetectorConstants.JAR_HELP_LINK, null), ExceptionLog.create(Toolkit.i18nText(type.getLogLocale()), msg)); } + @NotNull + private String convertClass2Path(String className) { + + return "/" + className.replaceAll("\\.", "/") + ".class"; + } + private interface ClassNameConverter { /** diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbDirtyConverter.java b/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbDirtyConverter.java index fe114b253..4550cde4a 100644 --- a/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbDirtyConverter.java +++ b/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbDirtyConverter.java @@ -19,9 +19,7 @@ import com.fr.third.org.hibernate.exception.GenericJDBCException; import org.jetbrains.annotations.Nullable; import javax.swing.JOptionPane; -import java.util.HashMap; import java.util.Iterator; -import java.util.Map; /** * 脏数据检测 @@ -53,24 +51,22 @@ public class FineDbDirtyConverter implements ThrowableConverter { */ @Override public @Nullable DetectorResult convert(Throwable throwable) { - - // 检测 Config - Throwable sign = throwable; - while (sign.getClass() != GenericJDBCException.class) { - sign = sign.getCause(); - } - - Map configMap = getAllConfigurationClassName(); - StackTraceElement[] stackTrace = sign.getStackTrace(); - for (StackTraceElement stackTraceElement : stackTrace) { - String className = stackTraceElement.getClassName(); - if (configMap.containsKey(className)) { + Iterator tableNames = ConfigContext.getConfigNames(); + while (tableNames.hasNext()) { + String tableName = tableNames.next(); + Class configClass = ConfigContext.getConfigClass(tableName); + Configuration configuration = ConfigContext.getConfigInstance(configClass); + try { + + // 尝试获取每一个值 + configuration.mirror(); + } catch (Throwable e) { + DetectorType detectorType = DetectorType.FINE_DB_DIRTY; DetectorResult.DetectorResultBuilder builder = DetectorResult.builder() .withType(detectorType); - String tableName = configMap.get(className); String tipsLocale = Toolkit.i18nText(detectorType.getTipsLocale(), tableName); String solutionLocale = detectorType.getSolutionLocale(); @@ -79,7 +75,7 @@ public class FineDbDirtyConverter implements ThrowableConverter { public String name() { return Toolkit.i18nText("Fine-Design_Basic_Reset_Immediately"); } - + @Override public void run() { boolean success = false; @@ -90,6 +86,7 @@ public class FineDbDirtyConverter implements ThrowableConverter { } catch (Exception e) { FineLoggerFactory.getLogger().error(e.getMessage(), e); } + // todo 最好的逻辑是,这里应该和 UI 隔离开的 if (!success) { FineJOptionPane.showMessageDialog(null, Toolkit.i18nText("Fine-Design_Error_Finedb_Backup_Reset_Result", @@ -106,21 +103,8 @@ public class FineDbDirtyConverter implements ThrowableConverter { return builder.build(); } } - - return null; + + return DetectorResult.normal(DetectorType.FINE_DB_DIRTY); } - private Map getAllConfigurationClassName() { - - Map configMap = new HashMap<>(); - Iterator tableNames = ConfigContext.getConfigNames(); - while (tableNames.hasNext()) { - String tableName = tableNames.next(); - Class configClass = ConfigContext.getConfigClass(tableName); - String className = configClass.getName(); - configMap.put(className, tableName); - } - return configMap; - - } } diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbLockedConverter.java b/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbLockedConverter.java index 22f600104..8a5e74f0b 100644 --- a/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbLockedConverter.java +++ b/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbLockedConverter.java @@ -5,6 +5,7 @@ import com.fr.env.detect.base.DetectorConstants; import com.fr.env.detect.bean.DetectorResult; import com.fr.env.detect.bean.DetectorType; import com.fr.env.detect.thowable.ThrowableConverter; +import com.fr.third.org.apache.commons.lang3.StringUtils; import com.fr.third.org.hsqldb.HsqlException; /** @@ -12,6 +13,8 @@ import com.fr.third.org.hsqldb.HsqlException; **/ public class FineDbLockedConverter implements ThrowableConverter { + public static final String LOCK_FILE = "lockFile"; + @Override public boolean accept(Throwable throwable) { @@ -42,7 +45,7 @@ public class FineDbLockedConverter implements ThrowableConverter { DetectorType type = DetectorType.FINE_DB_LOCKED; String message = sign.getMessage(); - if (message.contains("lock")) { + if (StringUtils.containsIgnoreCase(message, LOCK_FILE)) { return DetectorResult.builder() .withType(type) diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbPermissionConverter.java b/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbPermissionConverter.java index 00775706d..dd350abdf 100644 --- a/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbPermissionConverter.java +++ b/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbPermissionConverter.java @@ -5,6 +5,7 @@ import com.fr.env.detect.base.DetectorConstants; import com.fr.env.detect.bean.DetectorResult; import com.fr.env.detect.bean.DetectorType; import com.fr.env.detect.thowable.ThrowableConverter; +import com.fr.third.org.apache.commons.lang3.StringUtils; import com.fr.third.org.hsqldb.HsqlException; /** @@ -14,6 +15,8 @@ import com.fr.third.org.hsqldb.HsqlException; **/ public class FineDbPermissionConverter implements ThrowableConverter { + public static final String PERMISSION = "permission"; + @Override public boolean accept(Throwable throwable) { @@ -37,7 +40,7 @@ public class FineDbPermissionConverter implements ThrowableConverter { DetectorType type = DetectorType.FINE_DB_PERMISSION; String message = sign.getMessage(); - if (message.contains("permission")) { + if (StringUtils.containsIgnoreCase(message, PERMISSION)) { return DetectorResult.builder() .withType(type) diff --git a/designer-base/src/main/java/com/fr/env/detect/thowable/ThrowableLogAppender.java b/designer-base/src/main/java/com/fr/env/detect/thowable/ThrowableLogAppender.java index 64cd3ce7b..14210b8fb 100644 --- a/designer-base/src/main/java/com/fr/env/detect/thowable/ThrowableLogAppender.java +++ b/designer-base/src/main/java/com/fr/env/detect/thowable/ThrowableLogAppender.java @@ -25,24 +25,33 @@ public class ThrowableLogAppender extends AbstractAppender { } private static class ExceptionLogAppenderHolder { - private static final ThrowableLogAppender INSTANCE = new ThrowableLogAppender("exception-detect", null, null, false, null); + private static final ThrowableLogAppender INSTANCE = new ThrowableLogAppender("exception-detect-appender", null, null, false, null); } - private LogHandler logHandler = toHandler(); + private final LogHandler logHandler = toHandler(); @Override public void append(LogEvent logEvent) { if (logEvent.getLevel() == Level.ERROR) { Throwable thrown = logEvent.getThrown(); - ThrowableStore.getInstance().add(thrown); + if (thrown != null) { + ThrowableStore.getInstance().add(thrown); + } } } private LogHandler toHandler() { final ThrowableLogAppender appender = this; - return () -> appender; + appender.start(); + LogHandler handler = new LogHandler() { + @Override + public ThrowableLogAppender getHandler() { + return appender; + } + }; + return handler; } diff --git a/designer-base/src/main/java/com/fr/env/detect/ui/DetectorErrorDialog.java b/designer-base/src/main/java/com/fr/env/detect/ui/DetectorErrorDialog.java index 96e8dfa3f..9b85c61af 100644 --- a/designer-base/src/main/java/com/fr/env/detect/ui/DetectorErrorDialog.java +++ b/designer-base/src/main/java/com/fr/env/detect/ui/DetectorErrorDialog.java @@ -5,7 +5,6 @@ import com.fr.design.gui.ibutton.UIButton; import com.fr.design.gui.ilable.UILabel; import com.fr.design.i18n.Toolkit; import com.fr.design.layout.FRGUIPaneFactory; -import com.fr.design.layout.VerticalFlowLayout; import com.fr.design.utils.ColorUtils; import com.fr.design.utils.gui.GUICoreUtils; import com.fr.env.detect.base.DetectorUtil; @@ -34,6 +33,7 @@ import java.util.List; /** * 未知错误框 * todo 其实这里可以将多个 error-dialog 抽象在一起的。 时间不够, 简单写写 + * 见 {@link com.fr.design.dialog.ErrorDialog} * * created by Harrison on 2022/05/29 **/ @@ -63,19 +63,19 @@ public class DetectorErrorDialog extends JDialog implements ActionListener { UILabel detailDesc = new UILabel(Toolkit.i18nText("Fine-Design_Problem_Detail_Message")); centerPane.add(detailDesc, BorderLayout.NORTH); - JPanel detailPanel = FRGUIPaneFactory.createVerticalFlowLayout_Pane(true, VerticalFlowLayout.TOP, 0, 10); + JPanel detailPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); detailPanel.setBorder(BorderFactory.createEmptyBorder(0, 20, 10, 10)); for (DetectorResult result : results) { ExceptionTips tips = result.getTips(); if (tips != null) { Message tipsMsg = tips.getMessage(); - detailPanel.add(DetectorUtil.convert2Component(tipsMsg)); + detailPanel.add(DetectorUtil.convert2TextComponent(tipsMsg), BorderLayout.NORTH); } ExceptionSolution solution = result.getSolution(); if (solution != null) { Message solutionMsg = solution.getMessage(); - detailPanel.add(DetectorUtil.convert2Component(solutionMsg)); + detailPanel.add(DetectorUtil.convert2TextComponent(solutionMsg), BorderLayout.CENTER); } } diff --git a/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorAction.java b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorAction.java new file mode 100644 index 000000000..7992648f0 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorAction.java @@ -0,0 +1,28 @@ +package com.fr.env.detect.ui; + +import com.fr.design.actions.UpdateAction; +import com.fr.design.i18n.Toolkit; +import com.fr.design.mainframe.DesignerContext; + +import java.awt.event.ActionEvent; + +/** + * 工具栏里面的行为 + * + * created by Harrison on 2022/05/29 + **/ +public class EnvDetectorAction extends UpdateAction { + + public EnvDetectorAction() { + + this.setName(Toolkit.i18nText("Fine-Design_Basic_Detect_Toolbar_Title")); + this.setSmallIcon("com/fr/env/detect/detect_normal.svg"); + } + + @Override + public void actionPerformed(ActionEvent e) { + + EnvDetectorDialog dialog = new EnvDetectorDialog(DesignerContext.getDesignerFrame()); + dialog.setVisible(true); + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java index a5cea1323..64d6f4168 100644 --- a/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java +++ b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java @@ -17,7 +17,7 @@ import com.fr.design.utils.gui.GUICoreUtils; import com.fr.design.utils.gui.GUIPaintUtils; import com.fr.env.detect.base.DetectorBridge; import com.fr.env.detect.base.DetectorUtil; -import com.fr.env.detect.base.ExceptionDetectorConfig; +import com.fr.env.detect.base.EnvDetectorConfig; import com.fr.env.detect.bean.DetectorResult; import com.fr.env.detect.bean.DetectorStatus; import com.fr.env.detect.bean.DetectorType; @@ -79,7 +79,7 @@ public class EnvDetectorDialog extends JDialog { /* config model */ - private boolean detectOpen = ExceptionDetectorConfig.getInstance().isOpen(); + private boolean detectOpen = EnvDetectorConfig.getInstance().isEnabled(); public EnvDetectorDialog(Frame owner) { this(owner, null); @@ -213,7 +213,7 @@ public class EnvDetectorDialog extends JDialog { if (buttonStatus.isExecuting()) { // 执行结束 - buttonStatus = buttonStatus.next(); + buttonStatus = EnvDetectorButtonStatus.A_NEW; UIUtil.invokeLaterIfNeeded(() -> detectButton.setText(buttonStatus.getDesc())); } } @@ -366,7 +366,7 @@ public class EnvDetectorDialog extends JDialog { setVisible(false); dispose(); // 配置处理 - ExceptionDetectorConfig.getInstance().setOpen(this.detectOpen); + EnvDetectorConfig.getInstance().setEnabled(this.detectOpen); }); actionsPanel.add(confirmButton, BorderLayout.WEST); diff --git a/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorItem.java b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorItem.java index 6b5b1159c..0f9075313 100644 --- a/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorItem.java +++ b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorItem.java @@ -4,6 +4,8 @@ import com.fr.env.detect.bean.DetectorResult; import com.fr.env.detect.bean.DetectorType; /** + * 环境检测条目 + * * created by Harrison on 2022/05/27 **/ public class EnvDetectorItem { diff --git a/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorModel.java b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorModel.java index 8a698f3c3..457734d0b 100644 --- a/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorModel.java +++ b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorModel.java @@ -12,17 +12,28 @@ import java.util.stream.Collectors; import java.util.stream.Stream; /** + * 环境检测数据格式 + * * created by Harrison on 2022/05/27 **/ public class EnvDetectorModel { - private Map> itemMap = new LinkedHashMap<>(); + /** + * 类型 -> list [{type-result}] + */ + private final Map> itemMap = new LinkedHashMap<>(); public EnvDetectorModel() { - itemMap.put(DetectorType.Kind.JAR, Lists.newArrayList(new EnvDetectorItem(DetectorType.LACK_OF_JAR), new EnvDetectorItem(DetectorType.JAR_IN_CONSISTENCE), new EnvDetectorItem(DetectorType.JAR_CONFLICT))); + itemMap.put(DetectorType.Kind.JAR, Lists.newArrayList( + new EnvDetectorItem(DetectorType.LACK_OF_JAR), + new EnvDetectorItem(DetectorType.JAR_IN_CONSISTENCE), + new EnvDetectorItem(DetectorType.JAR_CONFLICT))); - itemMap.put(DetectorType.Kind.FINE_DB, Lists.newArrayList(new EnvDetectorItem(DetectorType.FINE_DB_LOCKED), new EnvDetectorItem(DetectorType.FINE_DB_PERMISSION), new EnvDetectorItem(DetectorType.FINE_DB_DIRTY))); + itemMap.put(DetectorType.Kind.FINE_DB, Lists.newArrayList( + new EnvDetectorItem(DetectorType.FINE_DB_LOCKED), + new EnvDetectorItem(DetectorType.FINE_DB_PERMISSION), + new EnvDetectorItem(DetectorType.FINE_DB_DIRTY))); } public void update(DetectorType type, DetectorResult result) { diff --git a/designer-base/src/main/resources/com/fr/env/detect/detect.svg b/designer-base/src/main/resources/com/fr/env/detect/detect.svg new file mode 100644 index 000000000..b279a649f --- /dev/null +++ b/designer-base/src/main/resources/com/fr/env/detect/detect.svg @@ -0,0 +1,3 @@ + + + diff --git a/designer-base/src/main/resources/com/fr/env/detect/detect_normal.svg b/designer-base/src/main/resources/com/fr/env/detect/detect_normal.svg new file mode 100644 index 000000000..b279a649f --- /dev/null +++ b/designer-base/src/main/resources/com/fr/env/detect/detect_normal.svg @@ -0,0 +1,3 @@ + + + From 58dc6d403194ecd245fa8ffaab414729b16f34a2 Mon Sep 17 00:00:00 2001 From: Harrison Date: Mon, 30 May 2022 14:42:39 +0800 Subject: [PATCH 25/85] =?UTF-8?q?feat:=20REPORT-70565=20=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E7=8E=AF=E5=A2=83=E7=9B=91=E6=B5=8B=EF=BC=88jar?= =?UTF-8?q?=E5=8C=85=E5=BC=82=E5=B8=B8=E3=80=81finedb=E3=80=81=E6=9D=80?= =?UTF-8?q?=E6=AF=92=E8=BD=AF=E4=BB=B6=EF=BC=89=201-=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E9=80=80=E5=87=BA=E6=97=B6=E7=9A=84=E9=80=BB=E8=BE=91=E5=B1=95?= =?UTF-8?q?=E7=A4=BA=202-=E6=B7=BB=E5=8A=A0=20fatalError=20=E7=9A=84?= =?UTF-8?q?=E6=A3=80=E6=B5=8B=203-=E5=B7=A5=E5=85=B7=E6=A0=8F=E6=95=88?= =?UTF-8?q?=E6=9E=9C=204-=E4=BC=98=E5=8C=96=E4=B8=80=E4=B8=8B=20MessageWit?= =?UTF-8?q?hLink=20=E7=9A=84=E4=BD=BF=E7=94=A8=E6=96=B9=E5=BC=8F=205-?= =?UTF-8?q?=E8=A1=A5=E5=85=85=E8=B6=85=E9=93=BE=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/fr/design/DesignerEnvManager.java | 20 +++++++ .../notification/NotificationDialog.java | 27 ++++++--- .../design/dialog/link/MessageWithLink.java | 12 +++- .../mainframe/toolbar/ToolBarMenuDock.java | 5 +- .../{DevDebugUtil.java => DevDebugUtils.java} | 4 +- .../java/com/fr/design/utils/DevUtils.java | 13 ----- .../java/com/fr/env/detect/EnvPrepare.java | 5 ++ .../fr/env/detect/base/DetectorConstants.java | 4 +- .../main/java/com/fr/exit/DesignerExiter.java | 58 +++++++++++-------- .../notification/NotificationDialogTest.java | 2 +- .../detect/ui/DetectorErrorDialogTest.java | 27 ++++++++- .../fr/start/LifecycleFatalErrorHandler.java | 30 +++------- .../main/java/com/fr/start/MainDesigner.java | 2 - 13 files changed, 131 insertions(+), 78 deletions(-) rename designer-base/src/main/java/com/fr/design/utils/{DevDebugUtil.java => DevDebugUtils.java} (79%) diff --git a/designer-base/src/main/java/com/fr/design/DesignerEnvManager.java b/designer-base/src/main/java/com/fr/design/DesignerEnvManager.java index 3694c6f83..958d1fa65 100644 --- a/designer-base/src/main/java/com/fr/design/DesignerEnvManager.java +++ b/designer-base/src/main/java/com/fr/design/DesignerEnvManager.java @@ -29,6 +29,7 @@ import com.fr.design.style.color.ColorSelectConfigManager; import com.fr.design.update.push.DesignerPushUpdateConfigManager; import com.fr.design.utils.DesignUtils; import com.fr.design.utils.DesignerPort; +import com.fr.env.detect.base.EnvDetectorConfig; import com.fr.exit.DesignerExiter; import com.fr.file.FILEFactory; import com.fr.general.ComparatorUtils; @@ -168,6 +169,12 @@ public class DesignerEnvManager implements XMLReadable, XMLWriter { private boolean embedServerLazyStartup = false; //最近使用的颜色 private ColorSelectConfigManager configManager = new ColorSelectConfigManager(); + + /** + * 环境检测配置 + */ + private EnvDetectorConfig envDetectorConfig = EnvDetectorConfig.getInstance(); + /** * alphafine */ @@ -1820,6 +1827,8 @@ public class DesignerEnvManager implements XMLReadable, XMLWriter { readActiveStatus(reader); } else if (ComparatorUtils.equals(CAS_PARAS, name)) { readHttpsParas(reader); + } else if (name.equals(EnvDetectorConfig.XML_TAG)) { + readEnvDetectorConfig(reader); } else if (name.equals("AlphaFineConfigManager")) { readAlphaFineAttr(reader); } else if (name.equals("RecentColors")) { @@ -1856,6 +1865,10 @@ public class DesignerEnvManager implements XMLReadable, XMLWriter { private void readAlphaFineAttr(XMLableReader reader) { reader.readXMLObject(this.alphaFineConfigManager = AlphaFineConfigManager.getInstance()); } + + private void readEnvDetectorConfig(XMLableReader reader) { + reader.readXMLObject(this.envDetectorConfig); + } private void readHttpsParas(XMLableReader reader) { String tempVal; @@ -2070,6 +2083,7 @@ public class DesignerEnvManager implements XMLReadable, XMLWriter { writeActiveStatus(writer); writeHttpsParas(writer); writeAlphaFineAttr(writer); + writeEnvDetectorConfig(writer); writeRecentColor(writer); writeOpenDebug(writer); writeDesignerPushUpdateAttr(writer); @@ -2113,6 +2127,12 @@ public class DesignerEnvManager implements XMLReadable, XMLWriter { writer.end(); } } + + private void writeEnvDetectorConfig(XMLPrintWriter writer) { + if (this.envDetectorConfig != null) { + this.envDetectorConfig.writeXML(writer); + } + } //写入uuid private void writeUUID(XMLPrintWriter writer) { diff --git a/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java b/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java index 42dac12c9..9c83ff1e2 100644 --- a/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java +++ b/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java @@ -10,7 +10,8 @@ import com.fr.design.gui.ilable.UILabel; import com.fr.design.i18n.Toolkit; import com.fr.design.layout.FRGUIPaneFactory; import com.fr.design.layout.VerticalFlowLayout; -import com.fr.env.detect.base.ExceptionDetectorConfig; +import com.fr.design.utils.LinkStrUtils; +import com.fr.env.detect.base.EnvDetectorConfig; import javax.swing.BorderFactory; import javax.swing.Icon; @@ -134,7 +135,9 @@ public class NotificationDialog extends JDialog { iconPanel.add(icon, BorderLayout.NORTH); contentPanel.add(iconPanel, BorderLayout.WEST); - + + JPanel centerPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + centerPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 20)); NotificationMessage[] messages = model.getMessages(); List messageComponents = Arrays.stream(messages) @@ -145,19 +148,27 @@ public class NotificationDialog extends JDialog { Desktop.getDesktop().browse(URI.create(linkMessage.getLink())); })); } - return new UILabel(messageModel.format()); + return new UILabel(LinkStrUtils.generateHtmlTag(messageModel.format())); + }) + .peek((component) -> { + Dimension preferredSize = component.getPreferredSize(); + double componentWidth = preferredSize.getWidth(); + double componentHeight = preferredSize.getHeight(); + double widthFactor = Math.ceil(componentWidth / 300); + double heightFactor = Math.ceil(componentHeight / 15); + int realHeight = (int) (heightFactor + widthFactor - 1) * 15; + component.setPreferredSize(new Dimension(300, realHeight)); + }) .collect(Collectors.toList()); // 竖向排列 JPanel messageSummaryPanel = FRGUIPaneFactory.createVerticalFlowLayout_Pane(true, VerticalFlowLayout.TOP, 0, 0); messageComponents.forEach(messageSummaryPanel::add); - - JPanel centerPanel = FRGUIPaneFactory.createBorderLayout_L_Pane(); - centerPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 20)); - + JScrollPane jScrollPane = new JScrollPane(messageSummaryPanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); jScrollPane.setBorder(BorderFactory.createEmptyBorder()); + centerPanel.add(jScrollPane, BorderLayout.CENTER); centerPanel.setPreferredSize(contentSize); @@ -211,7 +222,7 @@ public class NotificationDialog extends JDialog { @Override public void mouseClicked(MouseEvent e) { // 配置处理 - ExceptionDetectorConfig.getInstance().setOpen(false); + EnvDetectorConfig.getInstance().setEnabled(false); // 点击事件 destroy(); } diff --git a/designer-base/src/main/java/com/fr/design/dialog/link/MessageWithLink.java b/designer-base/src/main/java/com/fr/design/dialog/link/MessageWithLink.java index 0d6bd2348..d412fb682 100644 --- a/designer-base/src/main/java/com/fr/design/dialog/link/MessageWithLink.java +++ b/designer-base/src/main/java/com/fr/design/dialog/link/MessageWithLink.java @@ -23,10 +23,16 @@ import static com.fr.design.utils.LinkStrUtils.LABEL; */ public class MessageWithLink extends JEditorPane { + /** + * 直接放入 html 内容 + * 如果有超链接标签, 如 的话,将会自动点击匹配 url + * + * @param htmlText html内容 + */ public MessageWithLink(String htmlText) { super("text/html", htmlText); - + initListener(); setEditable(false); setBorder(null); } @@ -71,7 +77,7 @@ public class MessageWithLink extends JEditorPane { setBorder(null); } - public void initListener() { + protected void initListener() { addHyperlinkListener(hyperlinkEvent -> { try { @@ -85,7 +91,7 @@ public class MessageWithLink extends JEditorPane { }); } - public void initListener(Runnable runnable) { + protected void initListener(Runnable runnable) { addHyperlinkListener(e -> { if (e.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED)) { diff --git a/designer-base/src/main/java/com/fr/design/mainframe/toolbar/ToolBarMenuDock.java b/designer-base/src/main/java/com/fr/design/mainframe/toolbar/ToolBarMenuDock.java index ba9aadcc5..229933736 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/toolbar/ToolBarMenuDock.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/toolbar/ToolBarMenuDock.java @@ -64,6 +64,7 @@ import com.fr.design.os.impl.SupportOSImpl; import com.fr.design.remote.action.RemoteDesignAuthManagerAction; import com.fr.design.update.actions.SoftwareUpdateAction; import com.fr.design.utils.ThemeUtils; +import com.fr.env.detect.ui.EnvDetectorAction; import com.fr.general.ComparatorUtils; import com.fr.general.GeneralContext; import com.fr.general.locale.LocaleAction; @@ -572,7 +573,9 @@ public abstract class ToolBarMenuDock { if (AlphaFineConfigManager.isALPHALicAvailable()) { shortCuts.add(new AlphaFineAction()); } - + + shortCuts.add(new EnvDetectorAction()); + shortCuts.add(SeparatorDef.DEFAULT); if (DesignerEnvManager.getEnvManager().isOpenDebug()) { OSSupportCenter.buildAction(objects -> shortCuts.add(new FineUIAction()), SupportOSImpl.FINEUI); diff --git a/designer-base/src/main/java/com/fr/design/utils/DevDebugUtil.java b/designer-base/src/main/java/com/fr/design/utils/DevDebugUtils.java similarity index 79% rename from designer-base/src/main/java/com/fr/design/utils/DevDebugUtil.java rename to designer-base/src/main/java/com/fr/design/utils/DevDebugUtils.java index 2125455d3..af1d984ea 100644 --- a/designer-base/src/main/java/com/fr/design/utils/DevDebugUtil.java +++ b/designer-base/src/main/java/com/fr/design/utils/DevDebugUtils.java @@ -3,10 +3,10 @@ package com.fr.design.utils; /** * created by Harrison on 2022/05/26 **/ -public class DevDebugUtil { +public class DevDebugUtils { public static void main(String[] args) { - org.swingexplorer.Launcher.main(new String[]{"com.fr.design.utils.DevUtil"}); + org.swingexplorer.Launcher.main(new String[]{"com.fr.design.utils.DevUtils"}); } } diff --git a/designer-base/src/main/java/com/fr/design/utils/DevUtils.java b/designer-base/src/main/java/com/fr/design/utils/DevUtils.java index 85918f84b..2d8827755 100644 --- a/designer-base/src/main/java/com/fr/design/utils/DevUtils.java +++ b/designer-base/src/main/java/com/fr/design/utils/DevUtils.java @@ -5,13 +5,6 @@ import com.fr.config.dao.impl.LocalClassHelperDao; import com.fr.config.dao.impl.LocalEntityDao; import com.fr.config.dao.impl.LocalXmlEntityDao; import com.fr.design.ui.util.UIUtil; -import com.fr.env.detect.bean.DetectorResult; -import com.fr.env.detect.bean.DetectorType; -import com.fr.env.detect.bean.ExceptionLog; -import com.fr.env.detect.bean.ExceptionSolution; -import com.fr.env.detect.bean.ExceptionTips; -import com.fr.env.detect.ui.DetectorErrorDialog; -import com.fr.third.guava.collect.Lists; import com.fr.transaction.Configurations; import com.fr.transaction.LocalConfigurationHelper; @@ -61,12 +54,6 @@ public class DevUtils { DevUtils.show(new Consumer() { @Override public void accept(Frame frame) { - - DetectorErrorDialog dialog = new DetectorErrorDialog(frame, Lists.newArrayList(DetectorResult.exception(DetectorType.FINE_DB_PERMISSION, - ExceptionTips.create("test"), - ExceptionSolution.create("111test222", "www.baidu.com", null), - ExceptionLog.create("log")))); - dialog.setVisible(true); } }); } diff --git a/designer-base/src/main/java/com/fr/env/detect/EnvPrepare.java b/designer-base/src/main/java/com/fr/env/detect/EnvPrepare.java index eedc591db..258d579aa 100644 --- a/designer-base/src/main/java/com/fr/env/detect/EnvPrepare.java +++ b/designer-base/src/main/java/com/fr/env/detect/EnvPrepare.java @@ -4,6 +4,11 @@ import com.fr.module.Activator; /** * 设计器环境准备 + * 更多的是一些钩子,需要在环境启动、切换时进行处理 + * 使用监听 {@link com.fr.workspace.WorkspaceEvent} 只能满足 + * before -> stop -> start -> after + * 现在支持 => + * before -> stop -> prepare -> start -> after * * created by Harrison on 2022/05/29 **/ diff --git a/designer-base/src/main/java/com/fr/env/detect/base/DetectorConstants.java b/designer-base/src/main/java/com/fr/env/detect/base/DetectorConstants.java index fce9d5a88..be1036f0d 100644 --- a/designer-base/src/main/java/com/fr/env/detect/base/DetectorConstants.java +++ b/designer-base/src/main/java/com/fr/env/detect/base/DetectorConstants.java @@ -5,7 +5,7 @@ package com.fr.env.detect.base; **/ public class DetectorConstants { - public static final String JAR_HELP_LINK = ""; + public static final String JAR_HELP_LINK = "https://help.fanruan.com/finereport/doc-view-4700.html?source=3"; - public static final String FINE_DB_HELP_LINK = ""; + public static final String FINE_DB_HELP_LINK = "https://help.fanruan.com/finereport/doc-view-4701.html?source=3"; } diff --git a/designer-base/src/main/java/com/fr/exit/DesignerExiter.java b/designer-base/src/main/java/com/fr/exit/DesignerExiter.java index e1a93a46c..7337ff7c7 100644 --- a/designer-base/src/main/java/com/fr/exit/DesignerExiter.java +++ b/designer-base/src/main/java/com/fr/exit/DesignerExiter.java @@ -33,37 +33,47 @@ public class DesignerExiter { } public void exit(Throwable throwable) { + + FineLoggerFactory.getLogger().error(throwable.getMessage(), throwable); List results = EnvDetectorCenter.getInstance().terminate(throwable); if (Collections.isEmpty(results)) { - DesignerFrame designerFrame = DesignerContext.getDesignerFrame(); - DetectorErrorDialog errorDialog = new DetectorErrorDialog(designerFrame, results); - errorDialog.setVisible(true); + // 为空,则 + showOldExitDialog(throwable); } else { - FineLoggerFactory.getLogger().error(throwable.getMessage(), throwable); - StartErrorMessageCollector.getInstance().record(DesignerErrorMessage.UNEXCEPTED_START_FAILED.getId(), - DesignerErrorMessage.UNEXCEPTED_START_FAILED.getMessage(), - throwable.getMessage()); - ErrorDialog dialog = new ErrorDialog(null, Toolkit.i18nText("Fine-Design_Error_Start_Apology_Message"), - Toolkit.i18nText("Fine-Design_Error_Start_Report"), - throwable.getMessage()) { - @Override - protected void okEvent() { - dispose(); - DesignerExiter.getInstance().execute(); - } - - @Override - protected void restartEvent() { - dispose(); - RestartHelper.restart(); - } - }; - dialog.setVisible(true); + showNewExitDialog(results); } } - + + private void showNewExitDialog(List results) { + DesignerFrame designerFrame = DesignerContext.getDesignerFrame(); + DetectorErrorDialog errorDialog = new DetectorErrorDialog(designerFrame, results); + errorDialog.setVisible(true); + } + + private void showOldExitDialog(Throwable throwable) { + StartErrorMessageCollector.getInstance().record(DesignerErrorMessage.UNEXCEPTED_START_FAILED.getId(), + DesignerErrorMessage.UNEXCEPTED_START_FAILED.getMessage(), + throwable.getMessage()); + ErrorDialog dialog = new ErrorDialog(null, Toolkit.i18nText("Fine-Design_Error_Start_Apology_Message"), + Toolkit.i18nText("Fine-Design_Error_Start_Report"), + throwable.getMessage()) { + @Override + protected void okEvent() { + dispose(); + DesignerExiter.getInstance().execute(); + } + + @Override + protected void restartEvent() { + dispose(); + RestartHelper.restart(); + } + }; + dialog.setVisible(true); + } + public void execute() { DesignerLifecycleMonitorContext.getMonitor().beforeStop(); beforeExit(); diff --git a/designer-base/src/test/java/com/fr/design/components/notification/NotificationDialogTest.java b/designer-base/src/test/java/com/fr/design/components/notification/NotificationDialogTest.java index 1ba115c78..1536dd262 100644 --- a/designer-base/src/test/java/com/fr/design/components/notification/NotificationDialogTest.java +++ b/designer-base/src/test/java/com/fr/design/components/notification/NotificationDialogTest.java @@ -31,7 +31,7 @@ public class NotificationDialogTest { public void run(Object... args) { System.out.println("1111"); } - }, new NotificationMessage.LinkMessage("display model2 test", "abc")); + }, new NotificationMessage.LinkMessage("1111 2222 33333333 4444 555 6666 66555 888 999 333
3333", ""),new NotificationMessage.LinkMessage("display model2 test", "abc")); NotificationDialogProperties properties = new NotificationDialogProperties(frame, "test"); NotificationDialog dialog = new NotificationDialog(properties, Lists.newArrayList(model1, model2)); diff --git a/designer-base/src/test/java/com/fr/env/detect/ui/DetectorErrorDialogTest.java b/designer-base/src/test/java/com/fr/env/detect/ui/DetectorErrorDialogTest.java index 42d7923b8..b54288ea5 100644 --- a/designer-base/src/test/java/com/fr/env/detect/ui/DetectorErrorDialogTest.java +++ b/designer-base/src/test/java/com/fr/env/detect/ui/DetectorErrorDialogTest.java @@ -1,8 +1,33 @@ package com.fr.env.detect.ui; +import com.fr.design.utils.DevUtils; +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.DetectorType; +import com.fr.env.detect.bean.ExceptionLog; +import com.fr.env.detect.bean.ExceptionSolution; +import com.fr.env.detect.bean.ExceptionTips; +import com.fr.third.guava.collect.Lists; + +import java.awt.Frame; +import java.util.function.Consumer; + import static org.junit.Assert.*; public class DetectorErrorDialogTest { - + + public static void main(String[] args) { + + DevUtils.show(new Consumer() { + @Override + public void accept(Frame frame) { + + DetectorErrorDialog dialog = new DetectorErrorDialog(frame, Lists.newArrayList(DetectorResult.exception(DetectorType.FINE_DB_PERMISSION, + ExceptionTips.create("test"), + ExceptionSolution.create("111test222", "www.baidu.com", null), + ExceptionLog.create("log")))); + dialog.setVisible(true); + } + }); + } } \ No newline at end of file diff --git a/designer-realize/src/main/java/com/fr/start/LifecycleFatalErrorHandler.java b/designer-realize/src/main/java/com/fr/start/LifecycleFatalErrorHandler.java index 2a9103f4a..e4c5d73f9 100644 --- a/designer-realize/src/main/java/com/fr/start/LifecycleFatalErrorHandler.java +++ b/designer-realize/src/main/java/com/fr/start/LifecycleFatalErrorHandler.java @@ -116,30 +116,18 @@ public class LifecycleFatalErrorHandler { // todo 其实这里的交互还是有问题, 为什么在锁住和没权限的场景下,要重置 FineDB 呢。 DetectorResult detectorResult = DetectorBridge.getInstance().detect(DetectorType.FINE_DB_LOCKED, fatal); - if (detectorResult.getStatus() == DetectorStatus.NORMAL) { - detectorResult = DetectorBridge.getInstance().detect(DetectorType.FINE_DB_PERMISSION, fatal); + if (detectorResult.getStatus() == DetectorStatus.EXCEPTION) { + return Toolkit.i18nText("Fine-Design_Error_Finedb_Locked_Backup_Reset"); } - if (detectorResult.getStatus() == DetectorStatus.NORMAL) { - detectorResult = DetectorBridge.getInstance().detect(DetectorType.FINE_DB_DIRTY, fatal); + detectorResult = DetectorBridge.getInstance().detect(DetectorType.FINE_DB_PERMISSION, fatal); + if (detectorResult.getStatus() == DetectorStatus.EXCEPTION) { + return Toolkit.i18nText("Fine-Design_Error_Finedb_Permission_Backup_Reset"); } - - String message; - - DetectorType type = detectorResult.getType(); - switch (type) { - case FINE_DB_LOCKED: - message = Toolkit.i18nText("Fine-Design_Error_Finedb_Locked_Backup_Reset"); - break; - case FINE_DB_PERMISSION: - message = Toolkit.i18nText("Fine-Design_Error_Finedb_Permission_Backup_Reset"); - break; - case FINE_DB_DIRTY: - message = Toolkit.i18nText("Fine-Design_Error_Finedb_Dirty_Backup_Reset"); - break; - default: - message = Toolkit.i18nText("Fine-Design_Error_Finedb_Backup_Reset"); + detectorResult = DetectorBridge.getInstance().detect(DetectorType.FINE_DB_DIRTY, fatal); + if (detectorResult.getStatus() == DetectorStatus.EXCEPTION) { + return Toolkit.i18nText("Fine-Design_Error_Finedb_Dirty_Backup_Reset"); } - return message; + return Toolkit.i18nText("Fine-Design_Error_Finedb_Backup_Reset"); } private void afterBackupFailed() { diff --git a/designer-realize/src/main/java/com/fr/start/MainDesigner.java b/designer-realize/src/main/java/com/fr/start/MainDesigner.java index 09d740683..f60003c9a 100644 --- a/designer-realize/src/main/java/com/fr/start/MainDesigner.java +++ b/designer-realize/src/main/java/com/fr/start/MainDesigner.java @@ -46,7 +46,6 @@ import com.fr.design.share.SharableManager; import com.fr.design.ui.util.UIUtil; import com.fr.design.utils.concurrent.ThreadFactoryBuilder; import com.fr.design.utils.gui.GUICoreUtils; -import com.fr.env.detect.EnvDetectorCenter; import com.fr.env.utils.DesignerInteractionHistory; import com.fr.event.Event; import com.fr.event.EventDispatcher; @@ -111,7 +110,6 @@ public class MainDesigner extends BaseDesigner { */ public static void main(String[] args) { - EnvDetectorCenter.getInstance().init(); showSplash(); DeepLinkManager.getInstance().start(args); StopWatch watch = new StopWatch(); From 3276b22b864b20afdb1db8225540ae108f9f60db Mon Sep 17 00:00:00 2001 From: Harrison Date: Mon, 30 May 2022 15:06:27 +0800 Subject: [PATCH 26/85] =?UTF-8?q?feat:=20REPORT-70565=20=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E7=8E=AF=E5=A2=83=E7=9B=91=E6=B5=8B=EF=BC=88jar?= =?UTF-8?q?=E5=8C=85=E5=BC=82=E5=B8=B8=E3=80=81finedb=E3=80=81=E6=9D=80?= =?UTF-8?q?=E6=AF=92=E8=BD=AF=E4=BB=B6=EF=BC=89=20=E9=81=97=E7=95=99?= =?UTF-8?q?=E9=97=AE=E9=A2=98=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/fr/exit/DesignerExiter.java | 9 ++++++--- .../java/com/fr/start/LifecycleFatalErrorHandler.java | 5 ++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/designer-base/src/main/java/com/fr/exit/DesignerExiter.java b/designer-base/src/main/java/com/fr/exit/DesignerExiter.java index 7337ff7c7..3acb3cf8a 100644 --- a/designer-base/src/main/java/com/fr/exit/DesignerExiter.java +++ b/designer-base/src/main/java/com/fr/exit/DesignerExiter.java @@ -35,7 +35,12 @@ public class DesignerExiter { public void exit(Throwable throwable) { FineLoggerFactory.getLogger().error(throwable.getMessage(), throwable); + + StartErrorMessageCollector.getInstance().record(DesignerErrorMessage.UNEXCEPTED_START_FAILED.getId(), + DesignerErrorMessage.UNEXCEPTED_START_FAILED.getMessage(), + throwable.getMessage()); + // 尝试进行检测 List results = EnvDetectorCenter.getInstance().terminate(throwable); if (Collections.isEmpty(results)) { @@ -53,9 +58,7 @@ public class DesignerExiter { } private void showOldExitDialog(Throwable throwable) { - StartErrorMessageCollector.getInstance().record(DesignerErrorMessage.UNEXCEPTED_START_FAILED.getId(), - DesignerErrorMessage.UNEXCEPTED_START_FAILED.getMessage(), - throwable.getMessage()); + ErrorDialog dialog = new ErrorDialog(null, Toolkit.i18nText("Fine-Design_Error_Start_Apology_Message"), Toolkit.i18nText("Fine-Design_Error_Start_Report"), throwable.getMessage()) { diff --git a/designer-realize/src/main/java/com/fr/start/LifecycleFatalErrorHandler.java b/designer-realize/src/main/java/com/fr/start/LifecycleFatalErrorHandler.java index e4c5d73f9..37f608ee1 100644 --- a/designer-realize/src/main/java/com/fr/start/LifecycleFatalErrorHandler.java +++ b/designer-realize/src/main/java/com/fr/start/LifecycleFatalErrorHandler.java @@ -6,7 +6,6 @@ import com.fr.design.dialog.FineJOptionPane; import com.fr.design.i18n.Toolkit; import com.fr.design.mainframe.messagecollect.StartErrorMessageCollector; import com.fr.design.mainframe.messagecollect.entity.DesignerErrorMessage; -import com.fr.env.detect.EnvDetectorCenter; import com.fr.env.detect.base.DetectorBridge; import com.fr.env.detect.bean.DetectorResult; import com.fr.env.detect.bean.DetectorStatus; @@ -153,8 +152,8 @@ public class LifecycleFatalErrorHandler { SELF { @Override public void handle(FineLifecycleFatalError fatal) { - - EnvDetectorCenter.getInstance().terminate(fatal); + + DesignerExiter.getInstance().exit(fatal); } } } From 91993924559069e73e9e3efb7d091474aab78f22 Mon Sep 17 00:00:00 2001 From: Harrison Date: Mon, 30 May 2022 16:21:31 +0800 Subject: [PATCH 27/85] =?UTF-8?q?feat:=20REPORT-70565=20=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E7=8E=AF=E5=A2=83=E7=9B=91=E6=B5=8B=EF=BC=88jar?= =?UTF-8?q?=E5=8C=85=E5=BC=82=E5=B8=B8=E3=80=81finedb=E3=80=81=E6=9D=80?= =?UTF-8?q?=E6=AF=92=E8=BD=AF=E4=BB=B6=EF=BC=89=20=E8=A1=A5=E5=85=85?= =?UTF-8?q?=E6=B3=A8=E9=87=8A=EF=BC=8C=E5=B0=86=20devUtils=20=E7=A7=BB?= =?UTF-8?q?=E5=88=B0=20main=20=E9=87=8C=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fr/design/components/notification/NotificationDialog.java | 1 + .../java/com/fr/design/components/page/PageControlPanel.java | 1 + .../{main => test}/java/com/fr/design/utils/DevDebugUtils.java | 0 .../src/{main => test}/java/com/fr/design/utils/DevUtils.java | 0 4 files changed, 2 insertions(+) rename designer-base/src/{main => test}/java/com/fr/design/utils/DevDebugUtils.java (100%) rename designer-base/src/{main => test}/java/com/fr/design/utils/DevUtils.java (100%) diff --git a/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java b/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java index 9c83ff1e2..5956cc9e3 100644 --- a/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java +++ b/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java @@ -37,6 +37,7 @@ import java.util.stream.Collectors; /** * 右下角的提醒 异常提醒 + * 相关使用方式见 提醒组件 * * created by Harrison on 2022/05/24 **/ diff --git a/designer-base/src/main/java/com/fr/design/components/page/PageControlPanel.java b/designer-base/src/main/java/com/fr/design/components/page/PageControlPanel.java index 0059f9125..cc8502889 100644 --- a/designer-base/src/main/java/com/fr/design/components/page/PageControlPanel.java +++ b/designer-base/src/main/java/com/fr/design/components/page/PageControlPanel.java @@ -14,6 +14,7 @@ import java.util.function.Function; /** * 翻页组件 + * 相关使用方式见 翻页组件 * * created by Harrison on 2022/05/26 **/ diff --git a/designer-base/src/main/java/com/fr/design/utils/DevDebugUtils.java b/designer-base/src/test/java/com/fr/design/utils/DevDebugUtils.java similarity index 100% rename from designer-base/src/main/java/com/fr/design/utils/DevDebugUtils.java rename to designer-base/src/test/java/com/fr/design/utils/DevDebugUtils.java diff --git a/designer-base/src/main/java/com/fr/design/utils/DevUtils.java b/designer-base/src/test/java/com/fr/design/utils/DevUtils.java similarity index 100% rename from designer-base/src/main/java/com/fr/design/utils/DevUtils.java rename to designer-base/src/test/java/com/fr/design/utils/DevUtils.java From 48786bde20001f2e315280224f07303d4822050b Mon Sep 17 00:00:00 2001 From: "Yuan.Wang" <1536296691@qq.com> Date: Mon, 30 May 2022 20:16:43 +0800 Subject: [PATCH 28/85] =?UTF-8?q?REPORT-70850=20=E4=B8=8B=E6=8B=89?= =?UTF-8?q?=E6=A0=91=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../itree/refreshabletree/TreeRootPane.java | 10 ++++++++- .../ui/designer/TreeEditorDefinePane.java | 21 ++++++++++++------- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/designer-base/src/main/java/com/fr/design/gui/itree/refreshabletree/TreeRootPane.java b/designer-base/src/main/java/com/fr/design/gui/itree/refreshabletree/TreeRootPane.java index bb754716f..a12ce0bcf 100644 --- a/designer-base/src/main/java/com/fr/design/gui/itree/refreshabletree/TreeRootPane.java +++ b/designer-base/src/main/java/com/fr/design/gui/itree/refreshabletree/TreeRootPane.java @@ -38,6 +38,10 @@ public class TreeRootPane extends BasicPane { checkTypePane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); loadTypeCheckBox = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Widget_Load_By_Async")); loadTypeCheckBox.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + loadTypeCheckBox.addItemListener(e -> { + UICheckBox checkBox = (UICheckBox) e.getSource(); + doLoadTypeChange(checkBox.isSelected()); + }); loadTypePane.add(loadTypeCheckBox); this.add(loadTypePane); @@ -57,7 +61,11 @@ public class TreeRootPane extends BasicPane { this.add(returnFullPathPane); } - + + private void doLoadTypeChange(boolean selected) { + //给埋点插件提供一个方法,埋埋点用 + } + @Override protected String title4PopupWindow() { return "tree"; diff --git a/designer-form/src/main/java/com/fr/design/widget/ui/designer/TreeEditorDefinePane.java b/designer-form/src/main/java/com/fr/design/widget/ui/designer/TreeEditorDefinePane.java index e4d99802c..d039b001d 100644 --- a/designer-form/src/main/java/com/fr/design/widget/ui/designer/TreeEditorDefinePane.java +++ b/designer-form/src/main/java/com/fr/design/widget/ui/designer/TreeEditorDefinePane.java @@ -4,18 +4,16 @@ import com.fr.design.data.DataCreatorUI; import com.fr.design.designer.IntervalConstants; import com.fr.design.designer.creator.XCreator; import com.fr.design.gui.icheckbox.UICheckBox; - import com.fr.design.gui.ilable.UILabel; import com.fr.design.gui.itree.refreshabletree.TreeRootPane; import com.fr.design.layout.TableLayout; import com.fr.design.layout.TableLayoutHelper; - import com.fr.design.mainframe.widget.accessibles.AccessibleTreeModelEditor; import com.fr.form.ui.TreeEditor; - -import javax.swing.*; -import java.awt.*; +import javax.swing.BorderFactory; +import javax.swing.JPanel; +import java.awt.Component; /* @@ -35,13 +33,16 @@ public class TreeEditorDefinePane extends CustomWritableRepeatEditorPane { + UICheckBox checkBox = (UICheckBox) e.getSource(); + doLoadTypeChange(checkBox.isSelected()); + }); returnLeaf = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Widget_Return_Leaf")); returnLeaf.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); @@ -63,13 +64,17 @@ public class TreeEditorDefinePane extends CustomWritableRepeatEditorPane Date: Tue, 31 May 2022 12:15:13 +0800 Subject: [PATCH 29/85] =?UTF-8?q?REPORT-70746=20=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E7=9A=84=E6=98=BE=E7=A4=BA=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E4=BC=98=E5=8C=96=201=E3=80=81=E4=BF=AE=E6=94=B9url=EF=BC=8C?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=B6=85=E9=93=BE=202=E3=80=81=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E5=9F=8B=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../update/actions/NewFeatureAction.java | 29 ++++++ .../update/ui/dialog/UpdateMainDialog.java | 94 +++++++++++-------- 2 files changed, 83 insertions(+), 40 deletions(-) create mode 100644 designer-base/src/main/java/com/fr/design/update/actions/NewFeatureAction.java diff --git a/designer-base/src/main/java/com/fr/design/update/actions/NewFeatureAction.java b/designer-base/src/main/java/com/fr/design/update/actions/NewFeatureAction.java new file mode 100644 index 000000000..ac7c01cd0 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/update/actions/NewFeatureAction.java @@ -0,0 +1,29 @@ +package com.fr.design.update.actions; + + +import com.fr.general.CloudCenter; +import com.fr.log.FineLoggerFactory; + +import java.awt.Desktop; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.net.URI; + + +/** + * 帮助-更新升级 + * 点击查看新特性 + * */ +public class NewFeatureAction implements ActionListener { + + private String url = CloudCenter.getInstance().acquireConf("fr.latest.update.detail", "https://help.fanruan.com/finereport/doc-view-4699.html"); + + @Override + public void actionPerformed(ActionEvent e) { + try { + Desktop.getDesktop().browse(new URI(url)); + } catch (Exception ex) { + FineLoggerFactory.getLogger().error(ex.getMessage()); + } + } +} diff --git a/designer-base/src/main/java/com/fr/design/update/ui/dialog/UpdateMainDialog.java b/designer-base/src/main/java/com/fr/design/update/ui/dialog/UpdateMainDialog.java index deda005c3..29ed3a47e 100644 --- a/designer-base/src/main/java/com/fr/design/update/ui/dialog/UpdateMainDialog.java +++ b/designer-base/src/main/java/com/fr/design/update/ui/dialog/UpdateMainDialog.java @@ -9,6 +9,7 @@ import com.fr.design.dialog.FineJOptionPane; import com.fr.design.dialog.UIDialog; import com.fr.design.gui.ibutton.UIButton; import com.fr.design.gui.icontainer.UIScrollPane; +import com.fr.design.gui.ilable.ActionLabel; import com.fr.design.gui.ilable.UILabel; import com.fr.design.gui.itextfield.UITextField; import com.fr.design.i18n.Toolkit; @@ -16,6 +17,7 @@ import com.fr.design.layout.TableLayout; import com.fr.design.layout.TableLayoutHelper; import com.fr.design.mainframe.DesignerContext; import com.fr.design.update.actions.FileProcess; +import com.fr.design.update.actions.NewFeatureAction; import com.fr.design.update.domain.UpdateInfoCachePropertyManager; import com.fr.design.update.utils.UpdateFileUtils; import com.fr.design.update.ui.widget.LoadingLabel; @@ -57,7 +59,6 @@ import java.util.*; import java.util.List; import java.util.concurrent.ExecutionException; -import static com.fr.design.dialog.FineJOptionPane.OPTION_OK_CANCEL; import static java.nio.charset.StandardCharsets.*; import static javax.swing.JOptionPane.QUESTION_MESSAGE; @@ -185,7 +186,7 @@ public class UpdateMainDialog extends UIDialog { double[] rowUpdateContentPaneSize = {TableLayout.PREFERRED}; double[] columnUpdateContentPaneSize = {TableLayout.PREFERRED, TableLayout.FILL, TableLayout.PREFERRED}; double[] rowUpdateSubContentPaneSize = {UPDATE_CONTENT_PANE_ROW_SIZE, TableLayout.PREFERRED, TableLayout.PREFERRED, TableLayout.PREFERRED, UPDATE_CONTENT_PANE_ROW_SIZE}; - double[] columnUpdateSubContentPaneSize = {UPDATE_CONTENT_PANE_COLUMN_SIZE, TableLayout.FILL, TableLayout.PREFERRED}; + double[] columnUpdateSubContentPaneSize = {UPDATE_CONTENT_PANE_COLUMN_SIZE, TableLayout.PREFERRED, TableLayout.PREFERRED}; double[] columnUpdateSubContentPaneLabelSize = {UPDATE_CONTENT_PANE_LABEL_COLUMN_SIZE, TableLayout.PREFERRED}; JPanel jarUpdateContentPane = new JPanel(); @@ -196,7 +197,8 @@ public class UpdateMainDialog extends UIDialog { new Component[]{new UILabel(), new UILabel(), new UILabel()}, new Component[]{new UILabel(), updateVersionReminderPane, new UILabel()}, new Component[]{new UILabel(), initPaneContent(Color.WHITE, rowUpdateContentPaneSize, columnUpdateSubContentPaneLabelSize, new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Updater_JAR_Version")), jarCurrentLabel), new UILabel()}, - new Component[]{new UILabel(), initPaneContent(Color.WHITE, rowUpdateContentPaneSize, columnUpdateSubContentPaneLabelSize, new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Updater_Latest_JAR")), loadingLabel), new UILabel()}, + new Component[]{new UILabel(), initPaneContent(Color.WHITE, rowUpdateContentPaneSize, columnUpdateSubContentPaneLabelSize, new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Updater_Latest_JAR")), loadingLabel), + getNewFeatureActionLabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Latest_Feature_Detail"))}, new Component[]{new UILabel(), new UILabel(), new UILabel()} }, rowUpdateSubContentPaneSize, columnUpdateSubContentPaneSize, LayoutConstants.VGAP_LARGE); jarUpdateContentPane2.setBackground(Color.WHITE); @@ -417,8 +419,8 @@ public class UpdateMainDialog extends UIDialog { if (downloadFileConfig == null) { throw new Exception("network error."); } - HttpGet get = new HttpGet(CloudCenter.getInstance().acquireUrlByKind("changelog10") + "&start=" + lastUpdateCacheTime + "&end=" + getLatestJARTimeStr()); - httpClient = HttpToolbox.getHttpClient(CloudCenter.getInstance().acquireUrlByKind("changelog10") + "&start=" + lastUpdateCacheTime + "&end=" + getLatestJARTimeStr()); + HttpGet get = new HttpGet(CloudCenter.getInstance().acquireUrlByKind("updatelog") + "&start=" + lastUpdateCacheTime + "&end=" + getLatestJARTimeStr()); + httpClient = HttpToolbox.getHttpClient(CloudCenter.getInstance().acquireUrlByKind("updatelog") + "&start=" + lastUpdateCacheTime + "&end=" + getLatestJARTimeStr()); response = httpClient.execute(get); String responseText = CommonIOUtils.inputStream2String(response.getEntity().getContent(),EncodeConstants.ENCODING_UTF_8).trim(); JSONArray array = JSONArray.create(); @@ -608,41 +610,7 @@ public class UpdateMainDialog extends UIDialog { * jar包更新按钮监听器 */ private void addActionListenerForUpdateBtn() { - updateButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - String[] option = {Toolkit.i18nText("Fine-Design_Report_Yes"), Toolkit.i18nText("Fine-Design_Report_No")}; - int a = JOptionPane.showOptionDialog(getParent(), Toolkit.i18nText("Fine-Design_Update_Info_Information"), - Toolkit.i18nText("Fine-Design_Update_Info_Title"),JOptionPane.YES_NO_OPTION, QUESTION_MESSAGE, UIManager.getIcon("OptionPane.warningIcon"), option, 1); - if (a == 0) { - progressBar.setVisible(true); - progressBar.setString(Toolkit.i18nText("Fine-Design_Update_Info_Wait_Message")); - UpdateCallBack callBack = new UpdateProgressCallBack(progressBar); - updateButton.setEnabled(false); - updateLabel.setVisible(false); - RestoreResultDialog.deletePreviousPropertyFile(); - final String installLib = StableUtils.pathJoin(StableUtils.getInstallHome(), ProjectConstants.LOGS_NAME, UpdateConstants.INSTALL_LIB); - final JFrame frame = DesignerContext.getDesignerFrame(); - final RestartHelper helper = new RestartHelper(); - FineProcessContext.getParentPipe().fire(FineProcessEngineEvent.DESTROY); - new FileProcess(callBack) { - @Override - public void onDownloadSuccess() { - progressBar.setVisible(false); - deleteForDesignerUpdate(installLib); - helper.restartForUpdate(frame); - } - @Override - public void onDownloadFailed() { - progressBar.setVisible(false); - deleteForDesignerUpdate(installLib); - FineJOptionPane.showMessageDialog(getParent(), Toolkit.i18nText("Fine-Design_Update_Info_Failed_Message")); - helper.restartForUpdate(frame); - } - }.execute(); - } - } - }); + updateButton.addActionListener(new UpdateAction()); } private void deleteForDesignerUpdate(String installLib) { @@ -688,6 +656,13 @@ public class UpdateMainDialog extends UIDialog { return false; } + + private ActionLabel getNewFeatureActionLabel(final String text){ + ActionLabel actionLabel = new ActionLabel(text); + actionLabel.addActionListener(new NewFeatureAction()); + return actionLabel; + } + /** * 显示窗口 */ @@ -705,4 +680,43 @@ public class UpdateMainDialog extends UIDialog { @Override public void checkValid() throws Exception { } + + + + private class UpdateAction implements ActionListener { + @Override + public void actionPerformed(ActionEvent e) { + String[] option = {Toolkit.i18nText("Fine-Design_Report_Yes"), Toolkit.i18nText("Fine-Design_Report_No")}; + int a = JOptionPane.showOptionDialog(getParent(), Toolkit.i18nText("Fine-Design_Update_Info_Information"), + Toolkit.i18nText("Fine-Design_Update_Info_Title"),JOptionPane.YES_NO_OPTION, QUESTION_MESSAGE, UIManager.getIcon("OptionPane.warningIcon"), option, 1); + if (a == 0) { + progressBar.setVisible(true); + progressBar.setString(Toolkit.i18nText("Fine-Design_Update_Info_Wait_Message")); + UpdateCallBack callBack = new UpdateProgressCallBack(progressBar); + updateButton.setEnabled(false); + updateLabel.setVisible(false); + RestoreResultDialog.deletePreviousPropertyFile(); + final String installLib = StableUtils.pathJoin(StableUtils.getInstallHome(), ProjectConstants.LOGS_NAME, UpdateConstants.INSTALL_LIB); + final JFrame frame = DesignerContext.getDesignerFrame(); + final RestartHelper helper = new RestartHelper(); + FineProcessContext.getParentPipe().fire(FineProcessEngineEvent.DESTROY); + new FileProcess(callBack) { + @Override + public void onDownloadSuccess() { + progressBar.setVisible(false); + deleteForDesignerUpdate(installLib); + helper.restartForUpdate(frame); + } + @Override + public void onDownloadFailed() { + progressBar.setVisible(false); + deleteForDesignerUpdate(installLib); + FineJOptionPane.showMessageDialog(getParent(), Toolkit.i18nText("Fine-Design_Update_Info_Failed_Message")); + helper.restartForUpdate(frame); + } + }.execute(); + } + + } + } } \ No newline at end of file From 5916c1d49e3bb24421745a11699842a39d9551b9 Mon Sep 17 00:00:00 2001 From: "Link.Zhao" Date: Tue, 31 May 2022 15:28:10 +0800 Subject: [PATCH 30/85] =?UTF-8?q?REPORT-70746=20=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E7=9A=84=E6=98=BE=E7=A4=BA=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E4=BC=98=E5=8C=96=201=E3=80=81=E4=BF=AE=E6=94=B9=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E8=B0=83=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/fr/design/update/actions/NewFeatureAction.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/designer-base/src/main/java/com/fr/design/update/actions/NewFeatureAction.java b/designer-base/src/main/java/com/fr/design/update/actions/NewFeatureAction.java index ac7c01cd0..cf98501c1 100644 --- a/designer-base/src/main/java/com/fr/design/update/actions/NewFeatureAction.java +++ b/designer-base/src/main/java/com/fr/design/update/actions/NewFeatureAction.java @@ -1,6 +1,7 @@ package com.fr.design.update.actions; +import com.fr.design.utils.BrowseUtils; import com.fr.general.CloudCenter; import com.fr.log.FineLoggerFactory; @@ -21,7 +22,7 @@ public class NewFeatureAction implements ActionListener { @Override public void actionPerformed(ActionEvent e) { try { - Desktop.getDesktop().browse(new URI(url)); + BrowseUtils.browser(url); } catch (Exception ex) { FineLoggerFactory.getLogger().error(ex.getMessage()); } From 755632a44e5767aeb2852487407273537da16cdf Mon Sep 17 00:00:00 2001 From: Yvan Date: Tue, 31 May 2022 17:49:20 +0800 Subject: [PATCH 31/85] =?UTF-8?q?REPORT-72728=20=E8=BF=9C=E7=A8=8B?= =?UTF-8?q?=E5=88=87=E6=8D=A2=E6=A0=A1=E9=AA=8C=E6=9C=BA=E5=88=B6=E4=BC=98?= =?UTF-8?q?=E5=8C=96-=E5=88=87=E6=8D=A2=E8=AF=AD=E8=A8=80=E8=A7=A6?= =?UTF-8?q?=E5=8F=91=E4=BF=9D=E5=AD=98=E5=BC=B9=E7=AA=97=EF=BC=8C=E7=82=B9?= =?UTF-8?q?=E5=87=BB=E5=8F=96=E6=B6=88/=E5=85=B3=E9=97=AD=EF=BC=8C?= =?UTF-8?q?=E4=BB=8D=E8=A7=A6=E5=8F=91=E9=87=8D=E5=90=AF=E5=BC=B9=E7=AA=97?= =?UTF-8?q?=20=E3=80=90=E9=97=AE=E9=A2=98=E5=8E=9F=E5=9B=A0=E3=80=91?= =?UTF-8?q?=E4=B9=8B=E5=89=8D=E5=AE=9E=E7=8E=B0=E7=9A=84=E6=98=AF=E4=B8=8D?= =?UTF-8?q?=E7=AE=A1=E4=BF=9D=E5=AD=98=E6=88=90=E5=8A=9F=E8=BF=98=E6=98=AF?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5=EF=BC=8C=E9=83=BD=E4=BC=9A=E7=BB=A7=E7=BB=AD?= =?UTF-8?q?=E8=B5=B0=E9=87=8D=E5=90=AF=E5=BC=B9=E7=AA=97=E7=9A=84=E9=80=BB?= =?UTF-8?q?=E8=BE=91=20=E3=80=90=E6=94=B9=E5=8A=A8=E6=80=9D=E8=B7=AF?= =?UTF-8?q?=E3=80=91=E4=BF=AE=E6=94=B9=E4=B8=BA=E4=BF=9D=E5=AD=98=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5=E6=97=B6=E7=9B=B4=E6=8E=A5return=20=E3=80=90review?= =?UTF-8?q?=E5=BB=BA=E8=AE=AE=E3=80=91=E6=97=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/fr/design/actions/file/PreferencePane.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/designer-base/src/main/java/com/fr/design/actions/file/PreferencePane.java b/designer-base/src/main/java/com/fr/design/actions/file/PreferencePane.java index 100bd5965..9f15abee7 100644 --- a/designer-base/src/main/java/com/fr/design/actions/file/PreferencePane.java +++ b/designer-base/src/main/java/com/fr/design/actions/file/PreferencePane.java @@ -892,7 +892,10 @@ public class PreferencePane extends BasicPane { } // 重启弹窗出现之前提示用户保存模板 SaveSomeTemplatePane saveSomeTempaltePane = new SaveSomeTemplatePane(true, SwingUtilities.getWindowAncestor(this)); - saveSomeTempaltePane.showSavePane(); + if (!saveSomeTempaltePane.showSavePane()) { + // 保存失败时,直接返回 + return; + } int rv = JOptionPane.showOptionDialog( null, i18nText("Fine-Design_Basic_Language_Change_Successful"), From 31adf7261729f1f281eea0ade0497e7340d44709 Mon Sep 17 00:00:00 2001 From: Yvan Date: Wed, 1 Jun 2022 14:13:14 +0800 Subject: [PATCH 32/85] =?UTF-8?q?REPORT-72766=20=E4=BF=9D=E5=AD=98?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5=EF=BC=8C=E5=B7=B2=E9=80=89=E6=8B=A9=E7=9A=84?= =?UTF-8?q?=E5=8A=A8=E4=BD=9C=E5=BA=94=E4=B8=AD=E6=96=AD=20=E3=80=90?= =?UTF-8?q?=E9=97=AE=E9=A2=98=E5=8E=9F=E5=9B=A0=E3=80=91=E4=BB=A5=E5=89=8D?= =?UTF-8?q?=E7=9A=84=E9=80=BB=E8=BE=91=E6=98=AF=E5=8F=91=E7=8E=B0=E4=BA=86?= =?UTF-8?q?=E6=9C=89=E6=A8=A1=E6=9D=BF=E6=B2=A1=E4=BF=9D=E5=AD=98=EF=BC=8C?= =?UTF-8?q?=E6=89=80=E4=BB=A5=E8=B7=B3=E5=87=BA=E4=BF=9D=E5=AD=98=E5=BC=B9?= =?UTF-8?q?=E7=AA=97=20=201.=20=E4=BF=9D=E5=AD=98=E5=BC=B9=E7=AA=97?= =?UTF-8?q?=E6=97=B6=EF=BC=8C=E9=80=89=E6=8B=A9=E5=85=B3=E9=97=AD=E6=88=96?= =?UTF-8?q?=E5=8F=96=E6=B6=88=EF=BC=8C=E8=A7=86=E4=B8=BA"=E6=95=B4?= =?UTF-8?q?=E4=BD=93=E4=BF=9D=E5=AD=98=E7=BB=93=E6=9E=9C"=E7=9A=84?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5=20=202.=20=E5=8F=AA=E8=A6=81=E5=9C=A8?= =?UTF-8?q?=E4=BF=9D=E5=AD=98=E5=BC=B9=E7=AA=97=E4=B8=AD=E9=80=89=E6=8B=A9?= =?UTF-8?q?=E4=BA=86=E7=A1=AE=E5=AE=9A=EF=BC=8C=E4=B8=8D=E7=AE=A1=E5=90=8E?= =?UTF-8?q?=E9=9D=A2=E6=98=AF=E8=B7=B3=E5=87=BA=E4=BF=9D=E5=AD=98=E8=B7=AF?= =?UTF-8?q?=E5=BE=84=E5=BC=B9=E7=AA=97=EF=BC=8C=E8=BF=98=E6=98=AF=E4=BF=9D?= =?UTF-8?q?=E5=AD=98=E6=96=87=E4=BB=B6=E6=9C=AC=E8=BA=AB=E7=9A=84=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=E6=88=90=E5=8A=9F=E6=88=96=E8=80=85=E5=A4=B1=E8=B4=A5?= =?UTF-8?q?=EF=BC=8C=E9=83=BD=E8=A7=86=E4=B8=BA"=E6=95=B4=E4=BD=93?= =?UTF-8?q?=E4=BF=9D=E5=AD=98=E7=BB=93=E6=9E=9C"=E7=9A=84=E6=88=90?= =?UTF-8?q?=E5=8A=9F=20=20=E7=84=B6=E5=90=8E=E7=94=A8"=E6=95=B4=E4=BD=93?= =?UTF-8?q?=E4=BF=9D=E5=AD=98=E7=BB=93=E6=9E=9C"=E6=9D=A5=E5=86=B3?= =?UTF-8?q?=E5=AE=9A=E5=90=8E=E7=BB=AD=E5=8A=A8=E4=BD=9C=E6=98=AF=E4=B8=8D?= =?UTF-8?q?=E6=98=AF=E8=A6=81=E4=B8=AD=E6=96=AD=E6=97=B6=EF=BC=8C=E4=BC=9A?= =?UTF-8?q?=E5=87=BA=E7=8E=B0=E6=A8=A1=E6=9D=BF=E6=B2=A1=E6=9C=89=E7=9C=9F?= =?UTF-8?q?=E6=AD=A3=E7=9A=84=E4=BF=9D=E5=AD=98=EF=BC=8C=E7=84=B6=E5=90=8E?= =?UTF-8?q?=E8=AE=BE=E8=AE=A1=E5=99=A8=E5=85=B3=E9=97=AD=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98=20=E3=80=90=E6=94=B9=E5=8A=A8=E6=80=9D=E8=B7=AF?= =?UTF-8?q?=E3=80=91=E4=B8=8E=E4=BA=A7=E5=93=81=E6=B2=9F=E9=80=9A=E5=90=8E?= =?UTF-8?q?=EF=BC=8C=E7=A1=AE=E5=AE=9A=E7=BB=9F=E4=B8=80=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E4=B8=BA=EF=BC=8C=E5=BD=93=E5=87=BA=E7=8E=B0=E4=BB=BB=E4=B8=80?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF=E4=BF=9D=E5=AD=98=E5=A4=B1=E8=B4=A5=E6=97=B6?= =?UTF-8?q?=EF=BC=8C=E8=A7=86=E4=B8=BA"=E6=95=B4=E4=BD=93=E4=BF=9D?= =?UTF-8?q?=E5=AD=98=E7=BB=93=E6=9E=9C"=E7=9A=84=E5=A4=B1=E8=B4=A5?= =?UTF-8?q?=EF=BC=8C=E4=B8=94=E4=BF=9D=E5=AD=98=E5=A4=B1=E8=B4=A5=E7=9A=84?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF=E4=B8=8D=E4=BC=9A=E5=BD=B1=E5=93=8D=E6=AD=A3?= =?UTF-8?q?=E5=B8=B8=E6=A8=A1=E6=9D=BF=E7=9A=84=E4=BF=9D=E5=AD=98=20?= =?UTF-8?q?=E3=80=90review=E5=BB=BA=E8=AE=AE=E3=80=91=E6=97=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/fr/design/file/SaveSomeTemplatePane.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/designer-base/src/main/java/com/fr/design/file/SaveSomeTemplatePane.java b/designer-base/src/main/java/com/fr/design/file/SaveSomeTemplatePane.java index af2d1d2a1..6192649ab 100644 --- a/designer-base/src/main/java/com/fr/design/file/SaveSomeTemplatePane.java +++ b/designer-base/src/main/java/com/fr/design/file/SaveSomeTemplatePane.java @@ -56,12 +56,13 @@ public class SaveSomeTemplatePane extends BasicPane { this.dialog = this.showSmallWindow(parent, new DialogActionAdapter() { @Override public void doOk() { + isAllSaved = true; for (int i = 0; i < templateCheckBoxes.length; i++) { if (templateCheckBoxes[i].isSelected()) { - saveSelectedTemplate(unSavedTemplate.get(i)); + // 当存在模板保存失败时,视为整体的isAllSaved失败 + isAllSaved = saveSelectedTemplate(unSavedTemplate.get(i)) && isAllSaved; } } - isAllSaved = true; } public void doCancel() { @@ -188,12 +189,13 @@ public class SaveSomeTemplatePane extends BasicPane { } - private void saveSelectedTemplate(JTemplate specifiedTemplate) { + private boolean saveSelectedTemplate(JTemplate specifiedTemplate) { if (!specifiedTemplate.isSaved()) { specifiedTemplate.stopEditing(); - specifiedTemplate.saveTemplate(); + return specifiedTemplate.saveTemplate(); } FineLoggerFactory.getLogger().info( com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Template_Already_Saved", specifiedTemplate.getEditingFILE().getName())); + return true; } From 9d6e8bae92e02cb4e10bb36076ae441d869d5ee7 Mon Sep 17 00:00:00 2001 From: Yvan Date: Wed, 1 Jun 2022 16:14:47 +0800 Subject: [PATCH 33/85] =?UTF-8?q?REPORT-72783=20=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E5=85=B3=E9=97=AD=E6=97=B6=EF=BC=8C=E5=8D=95=E6=A8=A1?= =?UTF-8?q?=E6=9D=BF=E5=92=8C=E5=A4=9A=E6=A8=A1=E6=9D=BF=E4=BF=9D=E5=AD=98?= =?UTF-8?q?=E5=BC=B9=E7=AA=97=E9=80=BB=E8=BE=91=E7=BB=9F=E4=B8=80=20?= =?UTF-8?q?=E3=80=90=E9=97=AE=E9=A2=98=E5=8E=9F=E5=9B=A0=E3=80=91=E4=BA=A7?= =?UTF-8?q?=E5=93=81=E6=B2=9F=E9=80=9A=EF=BC=8C=E9=9C=80=E8=A6=81=E7=BB=9F?= =?UTF-8?q?=E4=B8=80=E4=B8=80=E4=B8=8B=EF=BC=8C=E5=85=8D=E5=BE=97=E7=BB=B4?= =?UTF-8?q?=E6=8A=A4=E5=A4=9A=E5=A5=97=20=E3=80=90=E6=94=B9=E5=8A=A8?= =?UTF-8?q?=E6=80=9D=E8=B7=AF=E3=80=91=E7=BB=9F=E4=B8=80=E6=88=90=E5=A4=9A?= =?UTF-8?q?=E6=A8=A1=E7=89=88=E4=BF=9D=E5=AD=98=E7=9A=84=E5=BC=B9=E7=AA=97?= =?UTF-8?q?=20=E3=80=90review=E5=BB=BA=E8=AE=AE=E3=80=91=E6=97=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fr/design/file/SaveSomeTemplatePane.java | 17 ----------------- .../com/fr/design/mainframe/DesignerFrame.java | 15 ++++----------- 2 files changed, 4 insertions(+), 28 deletions(-) diff --git a/designer-base/src/main/java/com/fr/design/file/SaveSomeTemplatePane.java b/designer-base/src/main/java/com/fr/design/file/SaveSomeTemplatePane.java index 6192649ab..72945b31c 100644 --- a/designer-base/src/main/java/com/fr/design/file/SaveSomeTemplatePane.java +++ b/designer-base/src/main/java/com/fr/design/file/SaveSomeTemplatePane.java @@ -198,23 +198,6 @@ public class SaveSomeTemplatePane extends BasicPane { return true; } - - public int saveLastOneTemplate() { - JTemplate specifiedTemplate = HistoryTemplateListPane.getInstance().getCurrentEditingTemplate(); - if (!specifiedTemplate.isALLSaved()) { - specifiedTemplate.stopEditing(); - int returnVal = FineJOptionPane.showConfirmDialog(DesignerContext.getDesignerFrame(), com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Utils_Would_You_Like_To_Save") + " \"" + specifiedTemplate.getEditingFILE() + "\" ?", - com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Confirm"), JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE); - if (returnVal == JOptionPane.YES_OPTION) { - specifiedTemplate.saveTemplate(); - FineLoggerFactory.getLogger().info(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Template_Already_Saved", specifiedTemplate.getEditingFILE().getName())); - } - return returnVal; - } - return JOptionPane.YES_OPTION; - } - - protected String title4PopupWindow() { return com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Save"); } diff --git a/designer-base/src/main/java/com/fr/design/mainframe/DesignerFrame.java b/designer-base/src/main/java/com/fr/design/mainframe/DesignerFrame.java index 812cb86b1..cd9b60be4 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/DesignerFrame.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/DesignerFrame.java @@ -173,16 +173,9 @@ public class DesignerFrame extends JFrame implements JTemplateActionListener, Ta //关闭前当前模板 停止编辑 HistoryTemplateListCache.getInstance().getCurrentEditingTemplate().stopEditing(); SaveSomeTemplatePane saveSomeTempaltePane = new SaveSomeTemplatePane(true); - // 只有一个文件未保存时 - if (HistoryTemplateListCache.getInstance().getHistoryCount() == 1) { - int choose = saveSomeTempaltePane.saveLastOneTemplate(); - if (choose != JOptionPane.CANCEL_OPTION) { - DesignerFrame.this.exit(); - } - } else { - if (saveSomeTempaltePane.showSavePane()) { - DesignerFrame.this.exit(); - } + // 全部保存成功才退出 + if (saveSomeTempaltePane.showSavePane()) { + DesignerFrame.this.exit(); } } @@ -1225,4 +1218,4 @@ public class DesignerFrame extends JFrame implements JTemplateActionListener, Ta public void setServerConfig(boolean serverConfig) { this.serverConfig = serverConfig; } -} \ No newline at end of file +} From 893095bd90311a34ef0b49238d7ab020caeffcd0 Mon Sep 17 00:00:00 2001 From: Yvan Date: Wed, 1 Jun 2022 16:29:35 +0800 Subject: [PATCH 34/85] =?UTF-8?q?REPORT-72783=20=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E5=85=B3=E9=97=AD=E6=97=B6=EF=BC=8C=E5=8D=95=E6=A8=A1?= =?UTF-8?q?=E6=9D=BF=E5=92=8C=E5=A4=9A=E6=A8=A1=E6=9D=BF=E4=BF=9D=E5=AD=98?= =?UTF-8?q?=E5=BC=B9=E7=AA=97=E9=80=BB=E8=BE=91=E7=BB=9F=E4=B8=80=20?= =?UTF-8?q?=E6=94=B9=E4=B8=8B=E5=8F=98=E9=87=8F=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/fr/design/mainframe/DesignerFrame.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/designer-base/src/main/java/com/fr/design/mainframe/DesignerFrame.java b/designer-base/src/main/java/com/fr/design/mainframe/DesignerFrame.java index cd9b60be4..4751649b8 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/DesignerFrame.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/DesignerFrame.java @@ -172,9 +172,9 @@ public class DesignerFrame extends JFrame implements JTemplateActionListener, Ta } //关闭前当前模板 停止编辑 HistoryTemplateListCache.getInstance().getCurrentEditingTemplate().stopEditing(); - SaveSomeTemplatePane saveSomeTempaltePane = new SaveSomeTemplatePane(true); + SaveSomeTemplatePane saveSomeTemplatePane = new SaveSomeTemplatePane(true); // 全部保存成功才退出 - if (saveSomeTempaltePane.showSavePane()) { + if (saveSomeTemplatePane.showSavePane()) { DesignerFrame.this.exit(); } } From 30058b7a4a5e424443ef83326e3ef416c4690715 Mon Sep 17 00:00:00 2001 From: Yvan Date: Wed, 1 Jun 2022 17:39:29 +0800 Subject: [PATCH 35/85] =?UTF-8?q?REPORT-72816=20=E8=BF=9C=E7=A8=8B?= =?UTF-8?q?=E5=88=87=E6=8D=A2=E6=A0=A1=E9=AA=8C=E6=9C=BA=E5=88=B6=E4=BC=98?= =?UTF-8?q?=E5=8C=96-=E6=89=93=E5=BC=80=E4=B8=80=E5=BC=A0=E6=A8=A1?= =?UTF-8?q?=E6=9D=BF=EF=BC=8C=E5=B7=A5=E4=BD=9C=E7=9B=AE=E5=BD=95A->B->A?= =?UTF-8?q?=EF=BC=8C=E4=BF=9D=E5=AD=98=E6=A8=A1=E6=9D=BF=E4=BC=9A=E8=A7=A6?= =?UTF-8?q?=E5=8F=91=E5=A4=87=E4=BB=BD=E6=8F=90=E7=A4=BA=20=E3=80=90?= =?UTF-8?q?=E9=97=AE=E9=A2=98=E5=8E=9F=E5=9B=A0=E3=80=91=E4=B9=8B=E5=89=8D?= =?UTF-8?q?=E7=9A=84=E8=BF=AD=E4=BB=A3=E5=8F=AA=E5=A4=84=E7=90=86=E4=BA=86?= =?UTF-8?q?=E6=9C=AA=E4=BF=9D=E5=AD=98=E7=9A=84=E6=A8=A1=E6=9D=BF=20?= =?UTF-8?q?=E3=80=90=E6=94=B9=E5=8A=A8=E6=80=9D=E8=B7=AF=E3=80=91=E4=B8=8D?= =?UTF-8?q?=E5=8C=BA=E5=88=86=E6=98=AF=E5=90=A6=E4=BF=9D=E5=AD=98=EF=BC=8C?= =?UTF-8?q?=E5=8F=AA=E8=A6=81=E6=98=AF=E7=8E=AF=E5=A2=83=E6=96=87=E4=BB=B6?= =?UTF-8?q?=EF=BC=8C=E9=83=BD=E5=81=9A=E4=B8=80=E6=AC=A1=E8=BD=AC=E5=8C=96?= =?UTF-8?q?=20=E3=80=90review=E5=BB=BA=E8=AE=AE=E3=80=91=E6=97=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/fr/design/mainframe/JTemplate.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java b/designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java index b8e5468db..c086ddac9 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java @@ -448,8 +448,8 @@ public abstract class JTemplate> */ public FILE templateToStashFile4Envchange() { FILE file = this.getEditingFILE(); - if (file.isEnvFile() && !isSaved()) { - // 切换工作目录时,存在未保存的环境文件时,将其转化为与环境无关的内存文件,再创建暂存文件 + if (file.isEnvFile()) { + // 切换工作目录时,模板锁信息被清除,因此这里需要将环境相关模板文件转化为与环境无关的内存模板文件,再创建暂存文件 return new StashedFILE(new MemFILE(file.getName()), exportBaseBook2ByteArray(), template.suffix()); } else { // 其它情况下,直接创建暂存文件 From 09c7d6c6c414d0850ad2d58d6e226b005a8bb795 Mon Sep 17 00:00:00 2001 From: Harrison Date: Wed, 1 Jun 2022 18:23:20 +0800 Subject: [PATCH 36/85] =?UTF-8?q?REPORT-72831=20=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E7=8E=AF=E5=A2=83=E6=A3=80=E6=B5=8B-=E8=AE=BE?= =?UTF-8?q?=E8=AE=A1=E5=99=A8=E6=AD=A3=E5=B8=B8=E7=9A=84=EF=BC=8C=E4=BD=86?= =?UTF-8?q?=E6=98=AF=E6=8A=9B=E5=87=BA=E5=BC=82=E5=B8=B8=E4=BA=86=201-?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=8A=9B=E5=87=BA=E5=BC=82=E5=B8=B8=EF=BC=8C?= =?UTF-8?q?=E5=86=99=E5=8F=8D=E4=BA=86=202-=E4=BF=AE=E6=94=B9=E4=B8=80?= =?UTF-8?q?=E4=B8=AA=20Activator=20=E7=9A=84=E5=90=8D=E5=AD=97=E5=92=8C?= =?UTF-8?q?=E5=90=AF=E5=8A=A8=E9=A1=B9=203-=E4=BF=AE=E6=94=B9=20=E9=87=8D?= =?UTF-8?q?=E6=96=B0=E6=A3=80=E6=B5=8B=E7=9A=84=E6=97=B6=E5=80=99=E7=9A=84?= =?UTF-8?q?=20UI=20=E7=95=8C=E9=9D=A2=204-=E4=BF=AE=E5=A4=8D=E5=B0=8F?= =?UTF-8?q?=E9=83=A8=E5=88=86=E8=87=AA=E5=B7=B1=E5=8F=91=E7=8E=B0=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/fr/env/{detect => }/EnvPrepare.java | 3 ++- .../com/fr/env/detect/EnvDetectorCenter.java | 23 +++++++++++++++---- .../fr/env/detect/base/DetectorBridge.java | 5 ++++ .../fr/env/detect/impl/JarLackDetector.java | 3 ++- .../converter/ClassConflictConvertor.java | 9 ++++++-- .../fr/env/detect/ui/EnvDetectorDialog.java | 15 +++++++++++- 6 files changed, 48 insertions(+), 10 deletions(-) rename designer-base/src/main/java/com/fr/env/{detect => }/EnvPrepare.java (89%) diff --git a/designer-base/src/main/java/com/fr/env/detect/EnvPrepare.java b/designer-base/src/main/java/com/fr/env/EnvPrepare.java similarity index 89% rename from designer-base/src/main/java/com/fr/env/detect/EnvPrepare.java rename to designer-base/src/main/java/com/fr/env/EnvPrepare.java index 258d579aa..999088e27 100644 --- a/designer-base/src/main/java/com/fr/env/detect/EnvPrepare.java +++ b/designer-base/src/main/java/com/fr/env/EnvPrepare.java @@ -1,5 +1,6 @@ -package com.fr.env.detect; +package com.fr.env; +import com.fr.env.detect.EnvDetectorCenter; import com.fr.module.Activator; /** diff --git a/designer-base/src/main/java/com/fr/env/detect/EnvDetectorCenter.java b/designer-base/src/main/java/com/fr/env/detect/EnvDetectorCenter.java index 98938a4f8..e9ed96cf8 100644 --- a/designer-base/src/main/java/com/fr/env/detect/EnvDetectorCenter.java +++ b/designer-base/src/main/java/com/fr/env/detect/EnvDetectorCenter.java @@ -10,6 +10,7 @@ import com.fr.design.mainframe.DesignerContext; import com.fr.design.ui.util.UIUtil; import com.fr.env.detect.base.DetectorBridge; import com.fr.env.detect.base.DetectorUtil; +import com.fr.env.detect.base.EnvDetectorConfig; import com.fr.env.detect.bean.DetectorResult; import com.fr.env.detect.bean.DetectorStatus; import com.fr.event.Event; @@ -50,6 +51,14 @@ public class EnvDetectorCenter { * 初始化 */ public void init() { + + // 重置逻辑 + DetectorBridge.getInstance().reset(); + + // 如果已经启动了,则不再启动 + if (PROCESS.get() != null) { + return; + } // 默认是启动 PROCESS.set(DetectorProcess.DESIGN_LAUNCH); @@ -121,13 +130,17 @@ public class EnvDetectorCenter { */ public void stop() { - // 一分钟后执行 + // 结束 + DetectorBridge.getInstance().stop(); + + // 30s后执行 DelayHelper.delayCall(EnvDetectorCenter.class.getName(), () -> { - + + // 如果当前没开启,则直接返回 + if (!EnvDetectorConfig.getInstance().isEnabled()) { + return; + } Stream resultStream = DetectorBridge.getInstance().detect(); - - // 结束 - DetectorBridge.getInstance().stop(); // 展示效果 NotificationDialogProperties properties = new NotificationDialogProperties(DesignerContext.getDesignerFrame(), Toolkit.i18nText("Fine-Design_Basic_Detect_Notification_Title")); diff --git a/designer-base/src/main/java/com/fr/env/detect/base/DetectorBridge.java b/designer-base/src/main/java/com/fr/env/detect/base/DetectorBridge.java index 0d5b60bc0..50bca7fd1 100644 --- a/designer-base/src/main/java/com/fr/env/detect/base/DetectorBridge.java +++ b/designer-base/src/main/java/com/fr/env/detect/base/DetectorBridge.java @@ -71,6 +71,11 @@ public class DetectorBridge { } } + public void reset() { + + ThrowableStore.getInstance().reset(); + } + /** * 针对某一项进行检测 * 主要用于手动检测时 diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/JarLackDetector.java b/designer-base/src/main/java/com/fr/env/detect/impl/JarLackDetector.java index 74c908000..8c85da202 100644 --- a/designer-base/src/main/java/com/fr/env/detect/impl/JarLackDetector.java +++ b/designer-base/src/main/java/com/fr/env/detect/impl/JarLackDetector.java @@ -78,7 +78,8 @@ public class JarLackDetector extends AbstractExceptionDetector { } private boolean isLackInfo(BuildInfo e) { - return StringUtils.isNotEmpty(e.getGroupBuild()); + + return StringUtils.isEmpty(e.getGroupBuild()); } private Message tipsMessage(List infos) { diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassConflictConvertor.java b/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassConflictConvertor.java index 3432d976d..ab2b16ed9 100644 --- a/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassConflictConvertor.java +++ b/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassConflictConvertor.java @@ -110,13 +110,18 @@ public class ClassConflictConvertor implements ThrowableConverter { } } + // 如果少于两个,则不需要提示 + if (allPath.size() < 2) { + return null; + } + String msg = StringUtils.join(allPath, SEPARATOR); DetectorType type = DetectorType.JAR_CONFLICT; return DetectorResult.exception(type, - ExceptionTips.create(Toolkit.i18nText(type.getTipsLocale(), msg)), + ExceptionTips.create(Toolkit.i18nText(type.getTipsLocale()) + msg), ExceptionSolution.create(Toolkit.i18nText(type.getSolutionLocale()), DetectorConstants.JAR_HELP_LINK, null), - ExceptionLog.create(Toolkit.i18nText(type.getLogLocale()), msg)); + ExceptionLog.create(Toolkit.i18nText(type.getLogLocale()) + msg)); } @NotNull diff --git a/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java index 64d6f4168..96120e317 100644 --- a/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java +++ b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java @@ -166,7 +166,11 @@ public class EnvDetectorDialog extends JDialog { } private void startDetecting(UIButton detectButton) { - + + // 重新检测的时候需要处理一些逻辑 + if (buttonStatus == EnvDetectorButtonStatus.A_NEW) { + reInit(); + } // 执行前 buttonStatus = buttonStatus.next(); UIUtil.invokeLaterIfNeeded(() -> detectButton.setText(buttonStatus.getDesc())); @@ -222,6 +226,15 @@ public class EnvDetectorDialog extends JDialog { detectWorker.execute(); } + private void reInit() { + currentDetectIndex = 0; + for (EnvDetectorItem e : model.getItems()) { + e.setResult(null); + } + // 刷新一下面板-开始执行啦 + UIUtil.invokeLaterIfNeeded(EnvDetectorDialog.this::refresh); + } + private void stopDetecting(UIButton detectButton) { buttonStatus = buttonStatus.next(); From 16eea506d74a530781979852aff0ce699f124014 Mon Sep 17 00:00:00 2001 From: Harrison Date: Thu, 2 Jun 2022 09:24:49 +0800 Subject: [PATCH 37/85] =?UTF-8?q?REPORT-72831=20=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E7=8E=AF=E5=A2=83=E6=A3=80=E6=B5=8B-=E8=AE=BE?= =?UTF-8?q?=E8=AE=A1=E5=99=A8=E6=AD=A3=E5=B8=B8=E7=9A=84=EF=BC=8C=E4=BD=86?= =?UTF-8?q?=E6=98=AF=E6=8A=9B=E5=87=BA=E5=BC=82=E5=B8=B8=E4=BA=86=20?= =?UTF-8?q?=E9=81=97=E6=BC=8F=E4=B8=80=E9=83=A8=E5=88=86=EF=BC=8C=E5=90=88?= =?UTF-8?q?=E4=B8=80=E4=B8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/fr/env/detect/impl/JarInconsistentDetector.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/JarInconsistentDetector.java b/designer-base/src/main/java/com/fr/env/detect/impl/JarInconsistentDetector.java index 679671997..b13195067 100644 --- a/designer-base/src/main/java/com/fr/env/detect/impl/JarInconsistentDetector.java +++ b/designer-base/src/main/java/com/fr/env/detect/impl/JarInconsistentDetector.java @@ -54,7 +54,6 @@ public class JarInconsistentDetector extends AbstractExceptionDetector { if (WorkContext.getCurrent().isLocal()) { return detectLocal(); - } else { return detectLocalAndRemote(); } @@ -75,13 +74,14 @@ public class JarInconsistentDetector extends AbstractExceptionDetector { Map remoteMap = groupBy(remoteInfos); MapDifference difference = Maps.difference(localMap, remoteMap); - Map diffInCommon = difference.entriesInCommon(); + // 获取本地远程不一样的部分 + Map> diffs = difference.entriesDiffering(); - if (diffInCommon.isEmpty()) { + if (diffs.isEmpty()) { return DetectorResult.normal(type()); } - Set inConsistentJars = diffInCommon.keySet(); + Set inConsistentJars = diffs.keySet(); String message = StringUtils.join(inConsistentJars, SEPARATOR); Message.Simple tipsMessage = new Message.Simple(Toolkit.i18nText("Fine_Design_Basic_Detect_Server") + Toolkit.i18nText(type().getTipsLocale()) + message); From 1a1b76e6ec98eac0e56ccbba9908a80571858f60 Mon Sep 17 00:00:00 2001 From: Harrison Date: Thu, 2 Jun 2022 12:49:30 +0800 Subject: [PATCH 38/85] =?UTF-8?q?REPORT-72831=20=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E7=8E=AF=E5=A2=83=E6=A3=80=E6=B5=8B-=E8=AE=BE?= =?UTF-8?q?=E8=AE=A1=E5=99=A8=E6=AD=A3=E5=B8=B8=E7=9A=84=EF=BC=8C=E4=BD=86?= =?UTF-8?q?=E6=98=AF=E6=8A=9B=E5=87=BA=E5=BC=82=E5=B8=B8=E4=BA=86=20?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E8=BF=99=E9=83=A8=E5=88=86=E6=9C=89=E4=BA=9B?= =?UTF-8?q?=E9=97=AE=E9=A2=98=E3=80=82=20core.xml=20=E9=87=8C=E9=9D=A2?= =?UTF-8?q?=E7=9A=84=20log=20=E5=8A=A0=E8=BD=BD=E5=90=8E=EF=BC=8C=E4=BC=9A?= =?UTF-8?q?=E8=A6=86=E7=9B=96=E8=BF=99=E9=87=8C=E7=9A=84=20log=20=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E3=80=82=20=E6=B7=BB=E5=8A=A0=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E3=80=82=E6=8F=90=E4=BE=9B=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E7=9A=84=20appender?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/fr/env/EnvPrepare.java | 2 +- .../com/fr/env/detect/EnvDetectorCenter.java | 121 +++++++++++------- .../detect/thowable/ThrowableLogAppender.java | 8 +- 3 files changed, 82 insertions(+), 49 deletions(-) diff --git a/designer-base/src/main/java/com/fr/env/EnvPrepare.java b/designer-base/src/main/java/com/fr/env/EnvPrepare.java index 999088e27..288d2f09c 100644 --- a/designer-base/src/main/java/com/fr/env/EnvPrepare.java +++ b/designer-base/src/main/java/com/fr/env/EnvPrepare.java @@ -22,6 +22,6 @@ public class EnvPrepare extends Activator { @Override public void stop() { - + EnvDetectorCenter.getInstance().destroy(); } } diff --git a/designer-base/src/main/java/com/fr/env/detect/EnvDetectorCenter.java b/designer-base/src/main/java/com/fr/env/detect/EnvDetectorCenter.java index e9ed96cf8..070d63d85 100644 --- a/designer-base/src/main/java/com/fr/env/detect/EnvDetectorCenter.java +++ b/designer-base/src/main/java/com/fr/env/detect/EnvDetectorCenter.java @@ -37,6 +37,43 @@ import java.util.stream.Stream; **/ public class EnvDetectorCenter { + private final Listener AFTER_START_LISTENER = new Listener() { + @Override + public void on(Event event, Null param) { + if (isSameProcess(DetectorProcess.SERVER_LAUNCH)) { + stop(); + } + } + }; + private final Listener BEFORE_START_LISTENER = new Listener() { + @Override + public void on(Event event, Null param) { + PROCESS.set(DetectorProcess.SERVER_LAUNCH); + start(); + } + }; + + + private final Listener START_UP_COMPLETE_LISTENER = new Listener() { + + @Override + public void on(Event event, Null param) { + if (isSameProcess(DetectorProcess.DESIGN_LAUNCH)) { + stop(); + } + } + }; + private final Listener AFTER_SWITCH_LISTENER = new Listener() { + @Override + public void on(Event event, Workspace param) { + if (isSameProcess(DetectorProcess.DESIGN_LAUNCH)) { + stop(); + } + } + }; + + private final AtomicReference PROCESS = new AtomicReference<>(); + public static EnvDetectorCenter getInstance() { return EnvDetectorCenterHolder.INSTANCE; } @@ -45,62 +82,36 @@ public class EnvDetectorCenter { private static final EnvDetectorCenter INSTANCE = new EnvDetectorCenter(); } - private final AtomicReference PROCESS = new AtomicReference<>(); - /** * 初始化 */ public void init() { - - // 重置逻辑 - DetectorBridge.getInstance().reset(); // 如果已经启动了,则不再启动 if (PROCESS.get() != null) { return; } - + + start(); // 默认是启动 PROCESS.set(DetectorProcess.DESIGN_LAUNCH); - start(); - - // 添加启动完成监听 - EventDispatcher.listen(DesignerLaunchStatus.STARTUP_COMPLETE, new Listener() { - @Override - public void on(Event event, Null param) { - if (isSameProcess(DetectorProcess.DESIGN_LAUNCH)) { - stop(); - } - } - }); - - // 切换完成后的监听 - EventDispatcher.listen(WorkspaceEvent.AfterSwitch, new Listener() { - @Override - public void on(Event event, Workspace param) { - if (isSameProcess(DetectorProcess.DESIGN_LAUNCH)) { - stop(); - } - } - }); - - // 打开内置服务器 - EventDispatcher.listen(EmbedServerEvent.BeforeStart, new Listener() { - @Override - public void on(Event event, Null param) { - PROCESS.set(DetectorProcess.SERVER_LAUNCH); - start(); - } - }); - EventDispatcher.listen(EmbedServerEvent.AfterStart, new Listener() { - @Override - public void on(Event event, Null param) { - if (isSameProcess(DetectorProcess.SERVER_LAUNCH)) { - stop(); - } - } - }); + + listen(); + } + + + /** + * 销毁,一般用在模块关闭中 + */ + public void destroy() { + + stopListen(); + // 重置内容 + DetectorBridge.getInstance().reset(); + // 关闭逻辑 + DetectorBridge.getInstance().stop(); + PROCESS.set(null); } /** @@ -175,6 +186,28 @@ public class EnvDetectorCenter { .collect(Collectors.toList()); } + + private void listen() { + + // 添加启动完成监听 + EventDispatcher.listen(DesignerLaunchStatus.STARTUP_COMPLETE, START_UP_COMPLETE_LISTENER); + + // 切换完成后的监听 + EventDispatcher.listen(WorkspaceEvent.AfterSwitch, AFTER_SWITCH_LISTENER); + + // 内置服务器监听 + EventDispatcher.listen(EmbedServerEvent.BeforeStart, BEFORE_START_LISTENER); + EventDispatcher.listen(EmbedServerEvent.AfterStart, AFTER_START_LISTENER); + } + + private void stopListen() { + + EventDispatcher.stopListen(START_UP_COMPLETE_LISTENER); + EventDispatcher.stopListen(AFTER_SWITCH_LISTENER); + EventDispatcher.stopListen(BEFORE_START_LISTENER); + EventDispatcher.stopListen(AFTER_START_LISTENER); + } + private enum DetectorProcess { /** diff --git a/designer-base/src/main/java/com/fr/env/detect/thowable/ThrowableLogAppender.java b/designer-base/src/main/java/com/fr/env/detect/thowable/ThrowableLogAppender.java index 14210b8fb..5b471f337 100644 --- a/designer-base/src/main/java/com/fr/env/detect/thowable/ThrowableLogAppender.java +++ b/designer-base/src/main/java/com/fr/env/detect/thowable/ThrowableLogAppender.java @@ -1,6 +1,6 @@ package com.fr.env.detect.thowable; -import com.fr.log.FineLoggerFactory; +import com.fr.general.FRLogger; import com.fr.log.LogHandler; import com.fr.third.apache.logging.log4j.Level; import com.fr.third.apache.logging.log4j.core.Filter; @@ -57,11 +57,11 @@ public class ThrowableLogAppender extends AbstractAppender { public void enable() { - FineLoggerFactory.getLogger().addLogAppender(logHandler); + FRLogger.getLogger().addExtendLogAppender(logHandler); } public void disable() { - - FineLoggerFactory.getLogger().removeLogAppender(logHandler); + + FRLogger.getLogger().removeExtendLogAppender(logHandler); } } From 0989763254d90c5d96d16fd1dc4825e9191e9dac Mon Sep 17 00:00:00 2001 From: Harrison Date: Thu, 2 Jun 2022 14:17:19 +0800 Subject: [PATCH 39/85] =?UTF-8?q?catch=20=E4=BD=8F=E5=BC=82=E5=B8=B8?= =?UTF-8?q?=EF=BC=8C=E9=98=B2=E6=AD=A2=E8=BF=99=E4=B8=80=E5=9D=97=E6=AD=BB?= =?UTF-8?q?=E5=BE=AA=E7=8E=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../env/detect/thowable/ThrowableLogAppender.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/designer-base/src/main/java/com/fr/env/detect/thowable/ThrowableLogAppender.java b/designer-base/src/main/java/com/fr/env/detect/thowable/ThrowableLogAppender.java index 5b471f337..bcb4736e1 100644 --- a/designer-base/src/main/java/com/fr/env/detect/thowable/ThrowableLogAppender.java +++ b/designer-base/src/main/java/com/fr/env/detect/thowable/ThrowableLogAppender.java @@ -32,12 +32,15 @@ public class ThrowableLogAppender extends AbstractAppender { @Override public void append(LogEvent logEvent) { - - if (logEvent.getLevel() == Level.ERROR) { - Throwable thrown = logEvent.getThrown(); - if (thrown != null) { - ThrowableStore.getInstance().add(thrown); + + try { + if (logEvent.getLevel() == Level.ERROR) { + Throwable thrown = logEvent.getThrown(); + if (thrown != null) { + ThrowableStore.getInstance().add(thrown); + } } + } catch (Throwable ignore) { } } From 9fc439adf8afbfc4c7637c7eaaa01ae1a39340e2 Mon Sep 17 00:00:00 2001 From: Yvan Date: Thu, 2 Jun 2022 16:22:27 +0800 Subject: [PATCH 40/85] =?UTF-8?q?REPORT-72900=20=E5=8E=BB=E9=99=A4RPC-?= =?UTF-8?q?=E4=B8=AD=E5=8F=B0=E5=9F=BA=E7=A1=80=E6=A8=A1=E5=9D=97=20?= =?UTF-8?q?=E3=80=90=E9=97=AE=E9=A2=98=E5=8E=9F=E5=9B=A0=E3=80=91rt=20?= =?UTF-8?q?=E3=80=90=E6=94=B9=E5=8A=A8=E6=80=9D=E8=B7=AF=E3=80=91=E8=AF=A6?= =?UTF-8?q?=E8=A7=81=E5=BC=80=E5=8F=91=E6=96=87=E6=A1=A3=E2=80=94=E2=80=94?= =?UTF-8?q?https://kms.fineres.com/pages/viewpage.action=3FpageId=3D416066?= =?UTF-8?q?483=20=E3=80=90review=E5=BB=BA=E8=AE=AE=E3=80=91=E5=A4=A7?= =?UTF-8?q?=E9=83=A8=E5=88=86=E7=9A=84=E7=B1=BB=E4=BF=AE=E6=94=B9=E6=98=AF?= =?UTF-8?q?=E5=9B=A0=E4=B8=BA=E6=94=B9=E4=BA=86=E7=B1=BB=E5=90=8D=E5=92=8C?= =?UTF-8?q?=E5=8C=85=E5=90=8D=EF=BC=8Creview=E7=9A=84=E8=AF=9D=E5=8F=AF?= =?UTF-8?q?=E4=BB=A5=E4=B8=BB=E8=A6=81=E7=9C=8B=E4=B8=8Bcom.fr.data.impl.e?= =?UTF-8?q?scapesql=E4=B8=8B=E9=9D=A2=E7=9A=84=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../preview/sql/PreviewPerformedSqlPane.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/preview/sql/PreviewPerformedSqlPane.java b/designer-base/src/main/java/com/fr/design/data/datapane/preview/sql/PreviewPerformedSqlPane.java index c0239c8fa..ee9ee2ec0 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/preview/sql/PreviewPerformedSqlPane.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/preview/sql/PreviewPerformedSqlPane.java @@ -4,7 +4,7 @@ import com.fr.base.Parameter; import com.fr.base.ParameterHelper; import com.fr.base.ParameterMapNameSpace; import com.fr.data.impl.DBTableData; -import com.fr.data.impl.EscapeSqlHelper; +import com.fr.data.impl.escapesql.EscapeSqlHelperManager; import com.fr.data.operator.DataOperator; import com.fr.decision.webservice.v10.config.ConfigService; import com.fr.design.dialog.DialogActionAdapter; @@ -189,7 +189,7 @@ public class PreviewPerformedSqlPane extends JDialog implements ActionListener { Parameter[] paras = processParameters(tableData, calculator); // 所有被转义参数的集合 refreshEscapeSqlHelper(); - Set specialCharParam = EscapeSqlHelper.getInstance().getSpecialCharParam(paras); + Set specialCharParam = EscapeSqlHelperManager.getInstance().getSpecialCharParam(paras); // 将参数转义等 Set tableDataProviders = getTableDataProviders(); for (TableDataProvider provider : tableDataProviders) { @@ -228,10 +228,10 @@ public class PreviewPerformedSqlPane extends JDialog implements ActionListener { } private static void refreshEscapeSqlHelper() { - EscapeSqlHelper.getInstance().setUseForbidWord(ConfigService.getInstance().getPSIConfig().isUseForbidWord()); - EscapeSqlHelper.getInstance().setSelectedForbidWord(ConfigService.getInstance().getPSIConfig().getSelectedForbidWord()); - EscapeSqlHelper.getInstance().setUseEscapeSpecialChar(ConfigService.getInstance().getPSIConfig().isUseEscapeSpecialChar()); - EscapeSqlHelper.getInstance().setSelectedSpecialChar(ConfigService.getInstance().getPSIConfig().getSelectedSpecialChar()); + EscapeSqlHelperManager.getInstance().setUseForbidWord(ConfigService.getInstance().getPSIConfig().isUseForbidWord()); + EscapeSqlHelperManager.getInstance().setSelectedForbidWord(ConfigService.getInstance().getPSIConfig().getSelectedForbidWord()); + EscapeSqlHelperManager.getInstance().setUseEscapeSpecialChar(ConfigService.getInstance().getPSIConfig().isUseEscapeSpecialChar()); + EscapeSqlHelperManager.getInstance().setSelectedSpecialChar(ConfigService.getInstance().getPSIConfig().getSelectedSpecialChar()); } private static boolean isShowSpecialCharSqlPane(List specialCharParamIndex) { @@ -284,7 +284,7 @@ public class PreviewPerformedSqlPane extends JDialog implements ActionListener { if (classManagerProvider == null) { return new HashSet<>(); } - return classManagerProvider.getArray(TableDataProvider.XML_TAG, EscapeSqlHelper.getInstance()); + return classManagerProvider.getArray(TableDataProvider.XML_TAG, EscapeSqlHelperManager.getInstance()); } @Override From 8049e9b981fdf90b8f6e6652e9c85bdfa2f54b86 Mon Sep 17 00:00:00 2001 From: pengda Date: Mon, 6 Jun 2022 14:18:57 +0800 Subject: [PATCH 41/85] =?UTF-8?q?REPORT-71925=20&=20REPORT-71908=20?= =?UTF-8?q?=E9=83=A8=E5=88=86=E6=95=B0=E6=8D=AE=E6=83=85=E5=86=B5=E4=B8=8B?= =?UTF-8?q?=E7=BB=93=E6=9E=9C=E4=B8=8D=E7=AC=A6=E5=90=88=E9=A2=84=E6=9C=9F?= =?UTF-8?q?&=E7=BB=84=E5=90=88=E5=9B=BE=E9=85=8D=E7=BD=AE=E6=9D=A1?= =?UTF-8?q?=E4=BB=B6=E5=B1=9E=E6=80=A7=E6=B1=87=E6=80=BB=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E5=80=BC=EF=BC=8C=E8=AE=BE=E7=BD=AE=E5=90=8E=E6=9D=A1=E4=BB=B6?= =?UTF-8?q?=E5=B1=9E=E6=80=A7=E5=86=85=E5=AE=B9=E8=A2=AB=E6=B8=85=E7=A9=BA?= =?UTF-8?q?=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../design/data/datapane/SummaryMethodComboBox.java | 11 +++++++---- .../series/SeriesCondition/ChartConditionPane.java | 5 ++++- .../ColSelectedWithSummaryMethodEditor.java | 6 ++++-- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/SummaryMethodComboBox.java b/designer-base/src/main/java/com/fr/design/data/datapane/SummaryMethodComboBox.java index 7aee00e24..5e4cc2cd4 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/SummaryMethodComboBox.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/SummaryMethodComboBox.java @@ -40,10 +40,13 @@ public class SummaryMethodComboBox extends UIComboBox { * 更新公式选择. */ public void populateBean(AbstractDataFunction function) { - for (int i = 0; i < CLASS_ARRAY.length; i++) { - if (function != null && ComparatorUtils.equals(function.getClass(), CLASS_ARRAY[i])) { - setSelectedIndex(i); - break; + if (function != null) { + for (int i = 0; i < CLASS_ARRAY.length; i++) { + if (this.getModel() != null && this.getModel().getSize() > i + && ComparatorUtils.equals(function.getClass(), CLASS_ARRAY[i])) { + setSelectedIndex(i); + break; + } } } } diff --git a/designer-chart/src/main/java/com/fr/design/chart/series/SeriesCondition/ChartConditionPane.java b/designer-chart/src/main/java/com/fr/design/chart/series/SeriesCondition/ChartConditionPane.java index 73ee26234..4085c7f10 100644 --- a/designer-chart/src/main/java/com/fr/design/chart/series/SeriesCondition/ChartConditionPane.java +++ b/designer-chart/src/main/java/com/fr/design/chart/series/SeriesCondition/ChartConditionPane.java @@ -81,7 +81,10 @@ public class ChartConditionPane extends LiteConditionPane { return this; } }); - conditionValuePane = ValueEditorPaneFactory.createAllValueEditorPane(); + Editor[] editors = ValueEditorPaneFactory.allEditors(); + ColSelectedWithSummaryMethodEditor colSelectedWithSummaryMethodEditor = new ColSelectedWithSummaryMethodEditor(); + Editor[] allEditors = ArrayUtils.add(editors,colSelectedWithSummaryMethodEditor); + conditionValuePane = ValueEditorPaneFactory.createValueEditorPane(allEditors,StringUtils.EMPTY,StringUtils.EMPTY); conditionKeyComboBox.setPreferredSize(new Dimension(175, conditionKeyComboBox.getPreferredSize().height)); conditionOPComboBox.setPreferredSize(new Dimension(80, 20)); Component[][] components = { diff --git a/designer-chart/src/main/java/com/fr/design/chart/series/SeriesCondition/ColSelectedWithSummaryMethodEditor.java b/designer-chart/src/main/java/com/fr/design/chart/series/SeriesCondition/ColSelectedWithSummaryMethodEditor.java index a949e9ecd..875bc705a 100644 --- a/designer-chart/src/main/java/com/fr/design/chart/series/SeriesCondition/ColSelectedWithSummaryMethodEditor.java +++ b/designer-chart/src/main/java/com/fr/design/chart/series/SeriesCondition/ColSelectedWithSummaryMethodEditor.java @@ -38,11 +38,13 @@ public class ColSelectedWithSummaryMethodEditor extends Editor Date: Mon, 6 Jun 2022 16:50:50 +0800 Subject: [PATCH 42/85] =?UTF-8?q?REPORT-71925=20&=20REPORT-71908=20?= =?UTF-8?q?=E9=83=A8=E5=88=86=E6=95=B0=E6=8D=AE=E6=83=85=E5=86=B5=E4=B8=8B?= =?UTF-8?q?=E7=BB=93=E6=9E=9C=E4=B8=8D=E7=AC=A6=E5=90=88=E9=A2=84=E6=9C=9F?= =?UTF-8?q?&=E7=BB=84=E5=90=88=E5=9B=BE=E9=85=8D=E7=BD=AE=E6=9D=A1?= =?UTF-8?q?=E4=BB=B6=E5=B1=9E=E6=80=A7=E6=B1=87=E6=80=BB=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E5=80=BC=EF=BC=8C=E8=AE=BE=E7=BD=AE=E5=90=8E=E6=9D=A1=E4=BB=B6?= =?UTF-8?q?=E5=B1=9E=E6=80=A7=E5=86=85=E5=AE=B9=E8=A2=AB=E6=B8=85=E7=A9=BA?= =?UTF-8?q?=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SeriesCondition/ColSelectedWithSummaryMethodEditor.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/designer-chart/src/main/java/com/fr/design/chart/series/SeriesCondition/ColSelectedWithSummaryMethodEditor.java b/designer-chart/src/main/java/com/fr/design/chart/series/SeriesCondition/ColSelectedWithSummaryMethodEditor.java index 875bc705a..5388684e7 100644 --- a/designer-chart/src/main/java/com/fr/design/chart/series/SeriesCondition/ColSelectedWithSummaryMethodEditor.java +++ b/designer-chart/src/main/java/com/fr/design/chart/series/SeriesCondition/ColSelectedWithSummaryMethodEditor.java @@ -38,13 +38,11 @@ public class ColSelectedWithSummaryMethodEditor extends Editor Date: Wed, 8 Jun 2022 15:48:58 +0800 Subject: [PATCH 43/85] =?UTF-8?q?REPORT-72900=20=E5=8E=BB=E9=99=A4RPC-?= =?UTF-8?q?=E4=B8=AD=E5=8F=B0=E5=9F=BA=E7=A1=80=E6=A8=A1=E5=9D=97=20?= =?UTF-8?q?=E3=80=90=E9=97=AE=E9=A2=98=E5=8E=9F=E5=9B=A0=E3=80=91EscapeSql?= =?UTF-8?q?Helper=E8=BF=99=E4=B8=AA=E7=B1=BB=E4=B9=8B=E5=89=8D=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E4=BA=86=E5=8C=85=E5=90=8D=E5=92=8C=E7=B1=BB=E5=90=8D?= =?UTF-8?q?=EF=BC=8C=E4=BD=86=E6=98=AF=E8=A2=AB=E4=B8=80=E9=83=A8=E5=88=86?= =?UTF-8?q?=E6=9C=AA=E4=B8=8A=E6=9E=B6=E5=95=86=E5=9F=8E=E7=9A=84=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=EF=BC=88=E6=8F=90=E4=BA=A4pr=E6=97=B6=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E5=85=BC=E5=AE=B9=E6=80=A7=E6=A3=80=E6=B5=8B=E4=B8=8D?= =?UTF-8?q?=E5=87=BA=E6=9D=A5=EF=BC=89=E4=BD=BF=E7=94=A8=E4=BA=86=EF=BC=8C?= =?UTF-8?q?=E5=AF=BC=E8=87=B4=E4=BA=86=E4=BE=9D=E8=B5=96=E9=94=99=E8=AF=AF?= =?UTF-8?q?=20=E3=80=90=E6=94=B9=E5=8A=A8=E6=80=9D=E8=B7=AF=E3=80=91?= =?UTF-8?q?=E6=8A=8A=E5=8C=85=E5=90=8D=E5=92=8C=E7=B1=BB=E5=90=8D=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E5=9B=9E=E9=80=80=E4=B8=8B=20=E3=80=90review=E5=BB=BA?= =?UTF-8?q?=E8=AE=AE=E3=80=91=E6=97=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../preview/sql/PreviewPerformedSqlPane.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/preview/sql/PreviewPerformedSqlPane.java b/designer-base/src/main/java/com/fr/design/data/datapane/preview/sql/PreviewPerformedSqlPane.java index ee9ee2ec0..c0239c8fa 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/preview/sql/PreviewPerformedSqlPane.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/preview/sql/PreviewPerformedSqlPane.java @@ -4,7 +4,7 @@ import com.fr.base.Parameter; import com.fr.base.ParameterHelper; import com.fr.base.ParameterMapNameSpace; import com.fr.data.impl.DBTableData; -import com.fr.data.impl.escapesql.EscapeSqlHelperManager; +import com.fr.data.impl.EscapeSqlHelper; import com.fr.data.operator.DataOperator; import com.fr.decision.webservice.v10.config.ConfigService; import com.fr.design.dialog.DialogActionAdapter; @@ -189,7 +189,7 @@ public class PreviewPerformedSqlPane extends JDialog implements ActionListener { Parameter[] paras = processParameters(tableData, calculator); // 所有被转义参数的集合 refreshEscapeSqlHelper(); - Set specialCharParam = EscapeSqlHelperManager.getInstance().getSpecialCharParam(paras); + Set specialCharParam = EscapeSqlHelper.getInstance().getSpecialCharParam(paras); // 将参数转义等 Set tableDataProviders = getTableDataProviders(); for (TableDataProvider provider : tableDataProviders) { @@ -228,10 +228,10 @@ public class PreviewPerformedSqlPane extends JDialog implements ActionListener { } private static void refreshEscapeSqlHelper() { - EscapeSqlHelperManager.getInstance().setUseForbidWord(ConfigService.getInstance().getPSIConfig().isUseForbidWord()); - EscapeSqlHelperManager.getInstance().setSelectedForbidWord(ConfigService.getInstance().getPSIConfig().getSelectedForbidWord()); - EscapeSqlHelperManager.getInstance().setUseEscapeSpecialChar(ConfigService.getInstance().getPSIConfig().isUseEscapeSpecialChar()); - EscapeSqlHelperManager.getInstance().setSelectedSpecialChar(ConfigService.getInstance().getPSIConfig().getSelectedSpecialChar()); + EscapeSqlHelper.getInstance().setUseForbidWord(ConfigService.getInstance().getPSIConfig().isUseForbidWord()); + EscapeSqlHelper.getInstance().setSelectedForbidWord(ConfigService.getInstance().getPSIConfig().getSelectedForbidWord()); + EscapeSqlHelper.getInstance().setUseEscapeSpecialChar(ConfigService.getInstance().getPSIConfig().isUseEscapeSpecialChar()); + EscapeSqlHelper.getInstance().setSelectedSpecialChar(ConfigService.getInstance().getPSIConfig().getSelectedSpecialChar()); } private static boolean isShowSpecialCharSqlPane(List specialCharParamIndex) { @@ -284,7 +284,7 @@ public class PreviewPerformedSqlPane extends JDialog implements ActionListener { if (classManagerProvider == null) { return new HashSet<>(); } - return classManagerProvider.getArray(TableDataProvider.XML_TAG, EscapeSqlHelperManager.getInstance()); + return classManagerProvider.getArray(TableDataProvider.XML_TAG, EscapeSqlHelper.getInstance()); } @Override From 5c6a55fda293ba7a488477abfe13233d995eba71 Mon Sep 17 00:00:00 2001 From: hades Date: Wed, 8 Jun 2022 19:34:17 +0800 Subject: [PATCH 44/85] =?UTF-8?q?REPORT-72570=20=E5=AF=8C=E6=96=87?= =?UTF-8?q?=E6=9C=AC=E5=AF=BC=E5=87=BA=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../design/form/util/FontTransformUtil.java | 8 +++ .../fr/design/form/util/HtmlPaintUtils.java | 42 ++++++++++++++++ .../design/form/util/HtmlPaintUtilsTest.java | 49 +++++++++++++++++++ .../cell/editor/RichTextCellEditor.java | 9 +++- .../design/cell/editor/RichTextToolBar.java | 4 +- 5 files changed, 108 insertions(+), 4 deletions(-) create mode 100644 designer-form/src/main/java/com/fr/design/form/util/HtmlPaintUtils.java create mode 100644 designer-form/src/test/java/com/fr/design/form/util/HtmlPaintUtilsTest.java diff --git a/designer-form/src/main/java/com/fr/design/form/util/FontTransformUtil.java b/designer-form/src/main/java/com/fr/design/form/util/FontTransformUtil.java index 769707ebe..eab2a64bb 100644 --- a/designer-form/src/main/java/com/fr/design/form/util/FontTransformUtil.java +++ b/designer-form/src/main/java/com/fr/design/form/util/FontTransformUtil.java @@ -5,6 +5,7 @@ import com.fr.design.ExtraDesignClassManager; import com.fr.design.fun.FormAdaptiveConfigUIProcessor; import com.fr.stable.Constants; import com.fr.stable.unit.PT; +import java.math.BigDecimal; /** @@ -43,4 +44,11 @@ public class FontTransformUtil { return value * (double) Constants.DEFAULT_FONT_PAINT_RESOLUTION / (double) getDesignerFontResolution(); } + + public static int roundUp(double num) { + String numStr = Double.toString(num); + numStr = new BigDecimal(numStr).setScale(0, BigDecimal.ROUND_HALF_UP).toString(); + return Integer.valueOf(numStr); + } + } diff --git a/designer-form/src/main/java/com/fr/design/form/util/HtmlPaintUtils.java b/designer-form/src/main/java/com/fr/design/form/util/HtmlPaintUtils.java new file mode 100644 index 000000000..f8620f52d --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/form/util/HtmlPaintUtils.java @@ -0,0 +1,42 @@ +package com.fr.design.form.util; + +import com.fr.base.Style; +import com.fr.general.FRFont; +import com.fr.log.FineLoggerFactory; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 富文本导出工具栏 + * + * @author hades + * @version 11.0 + * Created by hades on 2022/5/19 + */ +public class HtmlPaintUtils { + + + private static final Pattern FONT_SIZE_PATTERN = Pattern.compile(Pattern.quote("font-size:") + "(.*?)" + Pattern.quote("px")); + + /** + * 设置单元格字体为富文本中的最大字体 + * + * @param style + */ + public static Style deriveMaxFontFromRichChar(Style style, String html) { + int maxSize = style.getFRFont().getSize(); + Matcher matcher = FONT_SIZE_PATTERN.matcher(html); + while (matcher.find()) { + String value = matcher.group(1); + try { + double pxSize = Double.parseDouble(value); + int ptSize = FontTransformUtil.roundUp(FontTransformUtil.px2pt(pxSize)); + maxSize = Math.max(maxSize, ptSize); + } catch (Throwable e) { + FineLoggerFactory.getLogger().debug(e.getMessage(), e); + } + } + FRFont cellFont = style.getFRFont(); + return style.deriveFRFont(cellFont.applySize(maxSize)); + } +} diff --git a/designer-form/src/test/java/com/fr/design/form/util/HtmlPaintUtilsTest.java b/designer-form/src/test/java/com/fr/design/form/util/HtmlPaintUtilsTest.java new file mode 100644 index 000000000..445e865ab --- /dev/null +++ b/designer-form/src/test/java/com/fr/design/form/util/HtmlPaintUtilsTest.java @@ -0,0 +1,49 @@ +package com.fr.design.form.util; + +import com.fr.base.Style; +import junit.framework.TestCase; +import org.junit.Assert; + + +/** + * @author hades + * @version 11.0 + * Created by hades on 2022/5/25 + */ +public class HtmlPaintUtilsTest extends TestCase { + + public void testDeriveMaxFontFromRichChar() { + // 富文本字体size更大 + String testHtml0 = "这是一条测试数据"; + Style style0 = Style.DEFAULT_STYLE; + Assert.assertEquals(16, HtmlPaintUtils.deriveMaxFontFromRichChar(style0, testHtml0).getFRFont().getSize()); + + // 单元格字体size更大 + String testHtml1 = "这是一条测试数据"; + Style style1 = Style.DEFAULT_STYLE; + int oldFontSize = style1.getFRFont().getSize(); + Assert.assertEquals(oldFontSize, HtmlPaintUtils.deriveMaxFontFromRichChar(style1, testHtml1).getFRFont().getSize()); + + // 富文本字体size更大 不同文本 有不同size + String testHtml2 = "这是一条测试数"; + Style style2 = Style.DEFAULT_STYLE; + Assert.assertEquals(23, HtmlPaintUtils.deriveMaxFontFromRichChar(style2, testHtml2).getFRFont().getSize()); + + + // 异常场景1 + String testHtml3 = "xxxx奇怪的格式xxxx"; + Style style3 = Style.DEFAULT_STYLE; + oldFontSize = style1.getFRFont().getSize(); + Assert.assertEquals(oldFontSize, HtmlPaintUtils.deriveMaxFontFromRichChar(style3, testHtml3).getFRFont().getSize()); + + // 异常场景2 + String testHtml4 = "这是一条测试数据"; + Style style4 = Style.DEFAULT_STYLE; + oldFontSize = style1.getFRFont().getSize(); + Assert.assertEquals(oldFontSize, HtmlPaintUtils.deriveMaxFontFromRichChar(style4, testHtml4).getFRFont().getSize()); + + + } + + +} \ No newline at end of file diff --git a/designer-realize/src/main/java/com/fr/design/cell/editor/RichTextCellEditor.java b/designer-realize/src/main/java/com/fr/design/cell/editor/RichTextCellEditor.java index 101928135..1d2a5eda2 100644 --- a/designer-realize/src/main/java/com/fr/design/cell/editor/RichTextCellEditor.java +++ b/designer-realize/src/main/java/com/fr/design/cell/editor/RichTextCellEditor.java @@ -1,5 +1,8 @@ package com.fr.design.cell.editor; +import com.fr.design.mainframe.DesignerContext; +import com.fr.general.GeneralUtils; +import com.fr.design.form.util.HtmlPaintUtils; import java.awt.Component; import javax.swing.SwingUtilities; @@ -42,6 +45,11 @@ public class RichTextCellEditor extends AbstractCellEditor implements @Override public void doOk() { RichTextCellEditor.this.fireEditingStopped(); + CellElement newCellElement = parentTplEC.getTemplateCellElement(cellElement.getColumn(), cellElement.getRow()); + if (cellElement.getCellGUIAttr().isShowAsHTML()) { + newCellElement.setStyle(HtmlPaintUtils.deriveMaxFontFromRichChar(newCellElement.getStyle(), GeneralUtils.objectToString(newCellElement.getValue()))); + DesignerContext.getDesignerFrame().refreshToolbar(); + } } @Override @@ -49,7 +57,6 @@ public class RichTextCellEditor extends AbstractCellEditor implements RichTextCellEditor.this.fireEditingCanceled(); } }); - richTextDialog.addDialogActionListener(this); this.richTextPane.populate(parentTplEC, cellElement); setShowAsHtml(cellElement); diff --git a/designer-realize/src/main/java/com/fr/design/cell/editor/RichTextToolBar.java b/designer-realize/src/main/java/com/fr/design/cell/editor/RichTextToolBar.java index f95681be3..74654cd2e 100644 --- a/designer-realize/src/main/java/com/fr/design/cell/editor/RichTextToolBar.java +++ b/designer-realize/src/main/java/com/fr/design/cell/editor/RichTextToolBar.java @@ -366,9 +366,7 @@ public class RichTextToolBar extends BasicPane { }; private int roundUp(double num) { - String numStr = Double.toString(num); - numStr = new BigDecimal(numStr).setScale(0, BigDecimal.ROUND_HALF_UP).toString(); - return Integer.valueOf(numStr); + return FontTransformUtil.roundUp(num); } private CaretListener textCareListener = new CaretListener() { From 84b25012aa7274e98760215fb09477b2276146ca Mon Sep 17 00:00:00 2001 From: "Yuan.Wang" <1536296691@qq.com> Date: Thu, 9 Jun 2022 15:23:57 +0800 Subject: [PATCH 45/85] =?UTF-8?q?REPORT-70850=20=E4=B8=8B=E6=8B=89?= =?UTF-8?q?=E6=A0=91=E4=BC=98=E5=8C=96=E5=9F=8B=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fr/design/gui/frpane/TreeSettingPane.java | 2 +- .../itree/refreshabletree/TreeRootPane.java | 169 ++++++++++-------- .../ui/designer/TreeEditorDefinePane.java | 41 ++++- 3 files changed, 136 insertions(+), 76 deletions(-) diff --git a/designer-base/src/main/java/com/fr/design/gui/frpane/TreeSettingPane.java b/designer-base/src/main/java/com/fr/design/gui/frpane/TreeSettingPane.java index 77d3aa583..1f059d625 100644 --- a/designer-base/src/main/java/com/fr/design/gui/frpane/TreeSettingPane.java +++ b/designer-base/src/main/java/com/fr/design/gui/frpane/TreeSettingPane.java @@ -79,7 +79,7 @@ public class TreeSettingPane extends BasicPane implements DataCreatorUI { cardChanged(0); } - private void cardChanged(int index) { + private void cardChanged(Integer index) { this.remove(controlPane); this.remove(autoBuildPane); diff --git a/designer-base/src/main/java/com/fr/design/gui/itree/refreshabletree/TreeRootPane.java b/designer-base/src/main/java/com/fr/design/gui/itree/refreshabletree/TreeRootPane.java index a12ce0bcf..5ed3fb3ae 100644 --- a/designer-base/src/main/java/com/fr/design/gui/itree/refreshabletree/TreeRootPane.java +++ b/designer-base/src/main/java/com/fr/design/gui/itree/refreshabletree/TreeRootPane.java @@ -8,83 +8,112 @@ import com.fr.design.layout.FRGUIPaneFactory; import javax.swing.BorderFactory; import javax.swing.BoxLayout; import javax.swing.JPanel; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; public class TreeRootPane extends BasicPane { - - // 是否支持多选(checkBoxTree) - //private JCheckBox multipleSelection; - private UICheckBox checkTypeCheckBox; - - // richer:加载的方式,支持异步加载和完全加载 - private UICheckBox loadTypeCheckBox; - - private UICheckBox layerTypeCheckBox; - - private UICheckBox returnFullPathCheckBox ; - - public TreeRootPane() { - this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); - - JPanel checkTypePane = FRGUIPaneFactory.createBoxFlowInnerContainer_S_Pane_First0(); - checkTypePane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); - checkTypeCheckBox = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Tree_Mutiple_Selection_Or_Not")); - checkTypeCheckBox.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); - - checkTypePane.add(checkTypeCheckBox); - this.add(checkTypePane); - - JPanel loadTypePane = FRGUIPaneFactory.createBoxFlowInnerContainer_S_Pane_First0(); - checkTypePane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); - loadTypeCheckBox = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Widget_Load_By_Async")); - loadTypeCheckBox.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); - loadTypeCheckBox.addItemListener(e -> { - UICheckBox checkBox = (UICheckBox) e.getSource(); - doLoadTypeChange(checkBox.isSelected()); - }); - - loadTypePane.add(loadTypeCheckBox); - this.add(loadTypePane); - - JPanel leafSelectPane = FRGUIPaneFactory.createBoxFlowInnerContainer_S_Pane_First0(); - checkTypePane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); - leafSelectPane.add(layerTypeCheckBox = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Tree_Select_Leaf_Only"))); - layerTypeCheckBox.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); - - this.add(leafSelectPane); + + // 是否支持多选(checkBoxTree) + //private JCheckBox multipleSelection; + private UICheckBox checkTypeCheckBox; + + // richer:加载的方式,支持异步加载和完全加载 + private UICheckBox loadTypeCheckBox; + + private UICheckBox layerTypeCheckBox; + + private UICheckBox returnFullPathCheckBox; + + public TreeRootPane() { + this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + + JPanel checkTypePane = FRGUIPaneFactory.createBoxFlowInnerContainer_S_Pane_First0(); + checkTypePane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + checkTypeCheckBox = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Tree_Mutiple_Selection_Or_Not")); + checkTypeCheckBox.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + + checkTypePane.add(checkTypeCheckBox); + this.add(checkTypePane); + + JPanel loadTypePane = FRGUIPaneFactory.createBoxFlowInnerContainer_S_Pane_First0(); + checkTypePane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + loadTypeCheckBox = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Widget_Load_By_Async")); + loadTypeCheckBox.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + loadTypeCheckBoxAddListener(); + + loadTypePane.add(loadTypeCheckBox); + this.add(loadTypePane); + + JPanel leafSelectPane = FRGUIPaneFactory.createBoxFlowInnerContainer_S_Pane_First0(); + checkTypePane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + leafSelectPane.add(layerTypeCheckBox = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Tree_Select_Leaf_Only"))); + layerTypeCheckBox.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + + this.add(leafSelectPane); JPanel returnFullPathPane = FRGUIPaneFactory.createBoxFlowInnerContainer_S_Pane_First0(); - checkTypePane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); - returnFullPathPane.add(returnFullPathCheckBox = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Tree_Return_Full_Path"))); - returnFullPathCheckBox.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); - - this.add(returnFullPathPane); - - } - - private void doLoadTypeChange(boolean selected) { - //给埋点插件提供一个方法,埋埋点用 - } - - @Override - protected String title4PopupWindow() { - return "tree"; - } - - public void populate(TreeAttr treeAttr) { - checkTypeCheckBox.setSelected(treeAttr.isMultipleSelection()); - loadTypeCheckBox.setSelected(treeAttr.isAjax()); - layerTypeCheckBox.setSelected(treeAttr.isSelectLeafOnly()); + checkTypePane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + returnFullPathPane.add(returnFullPathCheckBox = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Tree_Return_Full_Path"))); + returnFullPathCheckBox.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + + this.add(returnFullPathPane); + + } + + private void loadTypeCheckBoxAddListener() { + loadTypeCheckBox.addMouseListener(new MouseListener() { + @Override + public void mouseClicked(MouseEvent e) { + UICheckBox checkBox = (UICheckBox) e.getSource(); + doLoadTypeChange(checkBox.isSelected()); + } + + @Override + public void mousePressed(MouseEvent e) { + + } + + @Override + public void mouseReleased(MouseEvent e) { + + } + + @Override + public void mouseEntered(MouseEvent e) { + + } + + @Override + public void mouseExited(MouseEvent e) { + + } + }); + } + + private void doLoadTypeChange(Boolean selected) { + //给埋点插件提供一个方法,埋埋点用 + } + + @Override + protected String title4PopupWindow() { + return "tree"; + } + + public void populate(TreeAttr treeAttr) { + checkTypeCheckBox.setSelected(treeAttr.isMultipleSelection()); + loadTypeCheckBox.setSelected(treeAttr.isAjax()); + layerTypeCheckBox.setSelected(treeAttr.isSelectLeafOnly()); returnFullPathCheckBox.setSelected(treeAttr.isReturnFullPath()); - } + } - public TreeAttr update() { - TreeAttr treeAttr = new TreeAttr(); - treeAttr.setMultipleSelection(checkTypeCheckBox.isSelected()); - treeAttr.setAjax(loadTypeCheckBox.isSelected()); - treeAttr.setSelectLeafOnly(layerTypeCheckBox.isSelected()); + public TreeAttr update() { + TreeAttr treeAttr = new TreeAttr(); + treeAttr.setMultipleSelection(checkTypeCheckBox.isSelected()); + treeAttr.setAjax(loadTypeCheckBox.isSelected()); + treeAttr.setSelectLeafOnly(layerTypeCheckBox.isSelected()); treeAttr.setReturnFullPath(returnFullPathCheckBox.isSelected()); - return treeAttr; - } + return treeAttr; + } } diff --git a/designer-form/src/main/java/com/fr/design/widget/ui/designer/TreeEditorDefinePane.java b/designer-form/src/main/java/com/fr/design/widget/ui/designer/TreeEditorDefinePane.java index d039b001d..b59742217 100644 --- a/designer-form/src/main/java/com/fr/design/widget/ui/designer/TreeEditorDefinePane.java +++ b/designer-form/src/main/java/com/fr/design/widget/ui/designer/TreeEditorDefinePane.java @@ -14,6 +14,8 @@ import com.fr.form.ui.TreeEditor; import javax.swing.BorderFactory; import javax.swing.JPanel; import java.awt.Component; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; /* @@ -39,10 +41,7 @@ public class TreeEditorDefinePane extends CustomWritableRepeatEditorPane { - UICheckBox checkBox = (UICheckBox) e.getSource(); - doLoadTypeChange(checkBox.isSelected()); - }); + loadAsyncAddListener(); returnLeaf = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Widget_Return_Leaf")); returnLeaf.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); @@ -64,7 +63,39 @@ public class TreeEditorDefinePane extends CustomWritableRepeatEditorPane Date: Thu, 9 Jun 2022 18:30:35 +0800 Subject: [PATCH 46/85] =?UTF-8?q?REPORT-72377=20=E6=8E=A7=E4=BB=B6-?= =?UTF-8?q?=E4=B8=8B=E6=8B=89=E6=A1=86=E8=81=94=E5=8A=A8=E5=85=B6=E4=BB=96?= =?UTF-8?q?=E6=8E=A7=E4=BB=B6-=E5=80=BC=E8=81=94=E5=8A=A8-FR11=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F=E4=B8=8B=E9=83=A8=E5=88=86=E6=8E=A7=E4=BB=B6=E6=B2=A1?= =?UTF-8?q?=E6=9C=89=E8=81=94=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../itree/refreshabletree/TreeRootPane.java | 23 ++----------------- .../ui/designer/TreeEditorDefinePane.java | 23 ++----------------- 2 files changed, 4 insertions(+), 42 deletions(-) diff --git a/designer-base/src/main/java/com/fr/design/gui/itree/refreshabletree/TreeRootPane.java b/designer-base/src/main/java/com/fr/design/gui/itree/refreshabletree/TreeRootPane.java index 5ed3fb3ae..b5dae36a6 100644 --- a/designer-base/src/main/java/com/fr/design/gui/itree/refreshabletree/TreeRootPane.java +++ b/designer-base/src/main/java/com/fr/design/gui/itree/refreshabletree/TreeRootPane.java @@ -8,6 +8,7 @@ import com.fr.design.layout.FRGUIPaneFactory; import javax.swing.BorderFactory; import javax.swing.BoxLayout; import javax.swing.JPanel; +import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; @@ -62,32 +63,12 @@ public class TreeRootPane extends BasicPane { } private void loadTypeCheckBoxAddListener() { - loadTypeCheckBox.addMouseListener(new MouseListener() { + loadTypeCheckBox.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { UICheckBox checkBox = (UICheckBox) e.getSource(); doLoadTypeChange(checkBox.isSelected()); } - - @Override - public void mousePressed(MouseEvent e) { - - } - - @Override - public void mouseReleased(MouseEvent e) { - - } - - @Override - public void mouseEntered(MouseEvent e) { - - } - - @Override - public void mouseExited(MouseEvent e) { - - } }); } diff --git a/designer-form/src/main/java/com/fr/design/widget/ui/designer/TreeEditorDefinePane.java b/designer-form/src/main/java/com/fr/design/widget/ui/designer/TreeEditorDefinePane.java index b59742217..7a5718ccf 100644 --- a/designer-form/src/main/java/com/fr/design/widget/ui/designer/TreeEditorDefinePane.java +++ b/designer-form/src/main/java/com/fr/design/widget/ui/designer/TreeEditorDefinePane.java @@ -14,6 +14,7 @@ import com.fr.form.ui.TreeEditor; import javax.swing.BorderFactory; import javax.swing.JPanel; import java.awt.Component; +import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; @@ -65,32 +66,12 @@ public class TreeEditorDefinePane extends CustomWritableRepeatEditorPane Date: Thu, 9 Jun 2022 18:31:15 +0800 Subject: [PATCH 47/85] =?UTF-8?q?REPORT-72377=20=E6=8E=A7=E4=BB=B6-?= =?UTF-8?q?=E4=B8=8B=E6=8B=89=E6=A1=86=E8=81=94=E5=8A=A8=E5=85=B6=E4=BB=96?= =?UTF-8?q?=E6=8E=A7=E4=BB=B6-=E5=80=BC=E8=81=94=E5=8A=A8-FR11=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F=E4=B8=8B=E9=83=A8=E5=88=86=E6=8E=A7=E4=BB=B6=E6=B2=A1?= =?UTF-8?q?=E6=9C=89=E8=81=94=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../itree/refreshabletree/TreeRootPane.java | 18 +++++++--------- .../ui/designer/TreeEditorDefinePane.java | 21 +++++++------------ 2 files changed, 14 insertions(+), 25 deletions(-) diff --git a/designer-base/src/main/java/com/fr/design/gui/itree/refreshabletree/TreeRootPane.java b/designer-base/src/main/java/com/fr/design/gui/itree/refreshabletree/TreeRootPane.java index b5dae36a6..a848b06d6 100644 --- a/designer-base/src/main/java/com/fr/design/gui/itree/refreshabletree/TreeRootPane.java +++ b/designer-base/src/main/java/com/fr/design/gui/itree/refreshabletree/TreeRootPane.java @@ -41,7 +41,13 @@ public class TreeRootPane extends BasicPane { checkTypePane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); loadTypeCheckBox = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Widget_Load_By_Async")); loadTypeCheckBox.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); - loadTypeCheckBoxAddListener(); + loadTypeCheckBox.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + UICheckBox checkBox = (UICheckBox) e.getSource(); + doLoadTypeChange(checkBox.isSelected()); + } + }); loadTypePane.add(loadTypeCheckBox); this.add(loadTypePane); @@ -62,16 +68,6 @@ public class TreeRootPane extends BasicPane { } - private void loadTypeCheckBoxAddListener() { - loadTypeCheckBox.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - UICheckBox checkBox = (UICheckBox) e.getSource(); - doLoadTypeChange(checkBox.isSelected()); - } - }); - } - private void doLoadTypeChange(Boolean selected) { //给埋点插件提供一个方法,埋埋点用 } diff --git a/designer-form/src/main/java/com/fr/design/widget/ui/designer/TreeEditorDefinePane.java b/designer-form/src/main/java/com/fr/design/widget/ui/designer/TreeEditorDefinePane.java index 7a5718ccf..9746c605d 100644 --- a/designer-form/src/main/java/com/fr/design/widget/ui/designer/TreeEditorDefinePane.java +++ b/designer-form/src/main/java/com/fr/design/widget/ui/designer/TreeEditorDefinePane.java @@ -42,8 +42,13 @@ public class TreeEditorDefinePane extends CustomWritableRepeatEditorPane Date: Fri, 10 Jun 2022 11:05:08 +0800 Subject: [PATCH 48/85] =?UTF-8?q?REPORT-70850=20=E4=B8=8B=E6=8B=89?= =?UTF-8?q?=E6=A0=91=E4=BC=98=E5=8C=96=E5=9F=8B=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fr/design/gui/frpane/TreeSettingPane.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/designer-base/src/main/java/com/fr/design/gui/frpane/TreeSettingPane.java b/designer-base/src/main/java/com/fr/design/gui/frpane/TreeSettingPane.java index 1f059d625..59ea03ae2 100644 --- a/designer-base/src/main/java/com/fr/design/gui/frpane/TreeSettingPane.java +++ b/designer-base/src/main/java/com/fr/design/gui/frpane/TreeSettingPane.java @@ -62,13 +62,12 @@ public class TreeSettingPane extends BasicPane implements DataCreatorUI { UILabel buildWayLabel = new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Build_Way") + " :"); buildWayPanel.add(buildWayLabel); buildBox = new UIComboBox(buildWay); - buildBox.addItemListener(new ItemListener() { - - @Override - public void itemStateChanged(ItemEvent e) { - cardChanged(buildBox.getSelectedIndex()); - } - }); + buildBox.addItemListener(e -> { + cardChanged(buildBox.getSelectedIndex()); + if (e.getStateChange() == ItemEvent.SELECTED) { + doBuildBoxSelect(buildBox.getSelectedIndex()); + } + }); buildWayPanel.add(buildBox); controlPane = new JTreeControlPane(new NameableCreator[]{treeNode}, @@ -79,7 +78,10 @@ public class TreeSettingPane extends BasicPane implements DataCreatorUI { cardChanged(0); } - private void cardChanged(Integer index) { + private void doBuildBoxSelect(Integer selectedIndex) { + } + + private void cardChanged(int index) { this.remove(controlPane); this.remove(autoBuildPane); From c564a875cf7b4bb24eb86aa93cf73b7e0995be58 Mon Sep 17 00:00:00 2001 From: Harrison Date: Fri, 10 Jun 2022 11:39:31 +0800 Subject: [PATCH 49/85] =?UTF-8?q?REPORT-73262=E3=80=90=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E7=8E=AF=E5=A2=83=E7=9B=91=E6=B5=8B=E3=80=91=E5=8F=B3?= =?UTF-8?q?=E4=B8=8B=E8=A7=92=E5=BC=B9=E7=AA=97=E6=96=87=E5=AD=97=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E4=B8=8D=E5=85=A8=20=E6=B5=8B=E8=AF=95=E5=8F=B3?= =?UTF-8?q?=E4=B8=8B=E8=A7=92=E5=BC=B9=E7=AA=97=E6=96=87=E5=AD=97=E7=9A=84?= =?UTF-8?q?=E5=B1=95=E7=A4=BA=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notification/NotificationDialog.java | 22 +++++++++++++++---- .../notification/NotificationDialogTest.java | 2 +- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java b/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java index 5956cc9e3..05a3149b8 100644 --- a/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java +++ b/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java @@ -32,6 +32,7 @@ import java.awt.event.MouseEvent; import java.net.URI; import java.util.Arrays; import java.util.List; +import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -151,18 +152,31 @@ public class NotificationDialog extends JDialog { } return new UILabel(LinkStrUtils.generateHtmlTag(messageModel.format())); }) + .collect(Collectors.toList()); + + Function calStandardWidth = e -> e > 4 ? 280 : 300; + + int widthUnit = messageComponents.stream() + .map((component) -> { + Dimension preferredSize = component.getPreferredSize(); + return preferredSize.getHeight(); + }) + .reduce(Double::sum) + .map(calStandardWidth) + .orElse(300); + + messageComponents = messageComponents.stream() .peek((component) -> { Dimension preferredSize = component.getPreferredSize(); double componentWidth = preferredSize.getWidth(); double componentHeight = preferredSize.getHeight(); - double widthFactor = Math.ceil(componentWidth / 300); double heightFactor = Math.ceil(componentHeight / 15); + double widthFactor = Math.ceil(componentWidth / widthUnit); int realHeight = (int) (heightFactor + widthFactor - 1) * 15; - component.setPreferredSize(new Dimension(300, realHeight)); - + component.setPreferredSize(new Dimension(widthUnit, realHeight)); }) .collect(Collectors.toList()); - + // 竖向排列 JPanel messageSummaryPanel = FRGUIPaneFactory.createVerticalFlowLayout_Pane(true, VerticalFlowLayout.TOP, 0, 0); messageComponents.forEach(messageSummaryPanel::add); diff --git a/designer-base/src/test/java/com/fr/design/components/notification/NotificationDialogTest.java b/designer-base/src/test/java/com/fr/design/components/notification/NotificationDialogTest.java index 1536dd262..b4ae29397 100644 --- a/designer-base/src/test/java/com/fr/design/components/notification/NotificationDialogTest.java +++ b/designer-base/src/test/java/com/fr/design/components/notification/NotificationDialogTest.java @@ -31,7 +31,7 @@ public class NotificationDialogTest { public void run(Object... args) { System.out.println("1111"); } - }, new NotificationMessage.LinkMessage("1111 2222 33333333 4444 555 6666 66555 888 999 333
3333", ""),new NotificationMessage.LinkMessage("display model2 test", "abc")); + }, new NotificationMessage.LinkMessage("1111 2222 33333333 4444 555 6666 66555 888 999 333
3333
444
555
", ""),new NotificationMessage.LinkMessage("display model2 test", "abc")); NotificationDialogProperties properties = new NotificationDialogProperties(frame, "test"); NotificationDialog dialog = new NotificationDialog(properties, Lists.newArrayList(model1, model2)); From 8561268d74d5934248b9095310f9ed8d39f760b3 Mon Sep 17 00:00:00 2001 From: Harrison Date: Fri, 10 Jun 2022 14:13:56 +0800 Subject: [PATCH 50/85] =?UTF-8?q?REPORT-73335=E3=80=90=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E7=8E=AF=E5=A2=83=E7=9B=91=E6=B5=8B=E3=80=91=E6=89=8B?= =?UTF-8?q?=E5=8A=A8=E6=A3=80=E6=B5=8B=E5=AE=8C=E6=88=90=E5=90=8E=EF=BC=8C?= =?UTF-8?q?=E4=BA=A4=E4=BA=92=E6=96=87=E5=AD=97=E6=8F=90=E7=A4=BA=E4=B8=A2?= =?UTF-8?q?=E5=A4=B1=20=E5=92=8C=E4=BA=A7=E5=93=81=E6=B2=9F=E9=80=9A?= =?UTF-8?q?=EF=BC=8C=E4=BF=AE=E5=A4=8D=E4=BA=A4=E4=BA=92=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fr/env/detect/ui/EnvDetectorDialog.java | 62 +++++++++++++++++-- 1 file changed, 56 insertions(+), 6 deletions(-) diff --git a/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java index 96120e317..2cde3dd89 100644 --- a/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java +++ b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java @@ -54,9 +54,11 @@ public class EnvDetectorDialog extends JDialog { private static final ImageIcon LOADING_ICON = getLoadingIcon(); public static final int TIMEOUT = 1000; - private JPanel body; + private final JPanel body; private final JPanel headerPanel; + private UIButton detectButton; + private JPanel resultSummaryPane; private final TablePanel tablePanel; @@ -128,7 +130,7 @@ public class EnvDetectorDialog extends JDialog { JPanel headerPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); headerPanel.setBorder(BorderFactory.createEmptyBorder(5, 0, 12, 0)); - UIButton detectButton = new UIButton(buttonStatus.getDesc()) { + this.detectButton = new UIButton(buttonStatus.getDesc()) { @Override public ButtonUI getUI() { @@ -152,7 +154,7 @@ public class EnvDetectorDialog extends JDialog { detectButton.setForeground(Color.WHITE); detectButton.addActionListener(event -> { if (buttonStatus.isNotExecuting()) { - startDetecting(detectButton); + startDetecting(); } else { stopDetecting(detectButton); } @@ -165,7 +167,7 @@ public class EnvDetectorDialog extends JDialog { return headerPanel; } - private void startDetecting(UIButton detectButton) { + private void startDetecting() { // 重新检测的时候需要处理一些逻辑 if (buttonStatus == EnvDetectorButtonStatus.A_NEW) { @@ -173,7 +175,7 @@ public class EnvDetectorDialog extends JDialog { } // 执行前 buttonStatus = buttonStatus.next(); - UIUtil.invokeLaterIfNeeded(() -> detectButton.setText(buttonStatus.getDesc())); + UIUtil.invokeLaterIfNeeded(EnvDetectorDialog.this::refreshHeaderPanel); detectWorker = new SwingWorker() { @Override @@ -218,7 +220,7 @@ public class EnvDetectorDialog extends JDialog { if (buttonStatus.isExecuting()) { // 执行结束 buttonStatus = EnvDetectorButtonStatus.A_NEW; - UIUtil.invokeLaterIfNeeded(() -> detectButton.setText(buttonStatus.getDesc())); + UIUtil.invokeLaterIfNeeded(EnvDetectorDialog.this::refreshHeaderPanel); } } }; @@ -251,6 +253,42 @@ public class EnvDetectorDialog extends JDialog { }); } + private void updateHeaderPanel() { + + // 刷新按钮 + detectButton.setText(buttonStatus.getDesc()); + if (buttonStatus == EnvDetectorButtonStatus.A_NEW) { + this.resultSummaryPane = new JPanel(); + this.resultSummaryPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0)); + this.resultSummaryPane.setLayout(new BorderLayout(5, 0)); + Boolean success = model.getResults() + .map((e) -> { + if (e.getStatus() == DetectorStatus.NORMAL) { + return Boolean.TRUE; + } + return Boolean.FALSE; + }).reduce((a, b) -> a && b) + .orElse(Boolean.FALSE); + + if (success) { + resultSummaryPane.add(new UILabel(Toolkit.i18nText("Fine-Design_Basic_Detect_Result_Label")), BorderLayout.WEST); + UILabel successLabel = new UILabel(Toolkit.i18nText("Fine-Design_Basic_Detect_Result_Success")); + successLabel.setForeground(Color.GREEN); + resultSummaryPane.add(successLabel, BorderLayout.CENTER); + } else { + resultSummaryPane.add(new UILabel(Toolkit.i18nText("Fine-Design_Basic_Exception")), BorderLayout.WEST); + UILabel resultLabel = new UILabel(Toolkit.i18nText("Fine-Design_Basic_Detect_Result_Error")); + resultLabel.setForeground(Color.RED); + resultSummaryPane.add(resultLabel, BorderLayout.CENTER); + } + this.headerPanel.add(BorderLayout.CENTER, resultSummaryPane); + } else { + if (resultSummaryPane != null) { + this.headerPanel.remove(resultSummaryPane); + } + } + } + /* table */ @@ -394,6 +432,13 @@ public class EnvDetectorDialog extends JDialog { return tailPanel; } + private void refreshHeaderPanel() { + + updateHeaderPanel(); + pack(); + repaint(); + } + private void refresh() { updateTable(this.tablePanel); @@ -504,4 +549,9 @@ public class EnvDetectorDialog extends JDialog { public abstract EnvDetectorButtonStatus next(); } + + private class EnvDetectorHeaderPanel extends JPanel { + + + } } From d567cf0a69979d6cf17918ff90c9c427f59affc8 Mon Sep 17 00:00:00 2001 From: Harrison Date: Fri, 10 Jun 2022 14:14:45 +0800 Subject: [PATCH 51/85] =?UTF-8?q?REPORT-73292=E3=80=90=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E7=8E=AF=E5=A2=83=E7=9B=91=E6=B5=8B=E3=80=91=E7=BC=BA?= =?UTF-8?q?=E5=B0=91=E7=9A=84jar=E5=9C=A8=E7=89=88=E6=9C=AC=E4=B8=8D?= =?UTF-8?q?=E4=B8=80=E8=87=B4=E4=B8=AD=E4=B9=9F=E4=BC=9A=E8=A2=AB=E6=A3=80?= =?UTF-8?q?=E6=B5=8B=E5=88=B0=20=E5=A4=84=E7=90=86=E4=B8=8D=E4=B8=80?= =?UTF-8?q?=E8=87=B4=E7=9A=84=E9=80=BB=E8=BE=91=EF=BC=8C=E4=B9=8B=E5=89=8D?= =?UTF-8?q?=E5=BF=98=E4=BA=86=E5=88=A4=E7=A9=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fr/env/detect/impl/JarInconsistentDetector.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/JarInconsistentDetector.java b/designer-base/src/main/java/com/fr/env/detect/impl/JarInconsistentDetector.java index b13195067..e10f18052 100644 --- a/designer-base/src/main/java/com/fr/env/detect/impl/JarInconsistentDetector.java +++ b/designer-base/src/main/java/com/fr/env/detect/impl/JarInconsistentDetector.java @@ -114,7 +114,11 @@ public class JarInconsistentDetector extends AbstractExceptionDetector { // 获取所有的不一致的 build List inConsistentInfos = buildInfos.stream() - .filter((e) -> !StringUtils.equals(designerBuild.get(), e.getGroupBuild())) + .filter((e) -> { + // 不为空,且不相等 + return StringUtils.isNotEmpty(e.getGroupBuild()) + && !StringUtils.equals(designerBuild.get(), e.getGroupBuild()); + }) .collect(Collectors.toList()); // 没有直接返回 @@ -141,7 +145,9 @@ public class JarInconsistentDetector extends AbstractExceptionDetector { for (BuildInfo localInfo : localInfos) { String jar = localInfo.getJar(); String groupContent = localInfo.getGroupBuild(); - localMap.put(jar, groupContent); + if (StringUtils.isNotEmpty(groupContent)) { + localMap.put(jar, groupContent); + } } return localMap; } From 6bcad8f5f71afc625755d7c75009ac3d818956e4 Mon Sep 17 00:00:00 2001 From: Harrison Date: Fri, 10 Jun 2022 14:15:39 +0800 Subject: [PATCH 52/85] =?UTF-8?q?REPORT-73331=E3=80=90=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E7=8E=AF=E5=A2=83=E7=9B=91=E6=B5=8B=E3=80=91=E8=BF=9C?= =?UTF-8?q?=E7=A8=8B=E7=8E=AF=E5=A2=83=E7=BC=BA=E5=B0=91jar=EF=BC=8C?= =?UTF-8?q?=E6=B2=A1=E6=9C=89=E6=A3=80=E6=B5=8B=E5=88=B0=E5=BC=82=E5=B8=B8?= =?UTF-8?q?=20=E4=BF=AE=E5=A4=8D=E8=BF=9C=E7=A8=8B=E6=97=B6=EF=BC=8C?= =?UTF-8?q?=E7=BC=BA=E5=B0=91=20JAR=20=E7=9A=84=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fr/env/detect/impl/JarLackDetector.java | 47 +++++++++++++++---- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/JarLackDetector.java b/designer-base/src/main/java/com/fr/env/detect/impl/JarLackDetector.java index 8c85da202..a769921b0 100644 --- a/designer-base/src/main/java/com/fr/env/detect/impl/JarLackDetector.java +++ b/designer-base/src/main/java/com/fr/env/detect/impl/JarLackDetector.java @@ -12,6 +12,7 @@ import com.fr.env.detect.bean.ExceptionSolution; import com.fr.env.detect.bean.ExceptionTips; import com.fr.env.detect.bean.Message; import com.fr.general.build.BuildInfo; +import com.fr.general.build.BuildInfoManager; import com.fr.general.build.BuildInfoOperator; import com.fr.general.build.impl.BuildInfoOperatorImpl; import com.fr.third.guava.collect.Lists; @@ -22,6 +23,8 @@ import org.jetbrains.annotations.NotNull; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.function.Predicate; import java.util.stream.Collectors; /** @@ -42,17 +45,38 @@ public class JarLackDetector extends AbstractExceptionDetector { @Override public DetectorResult detect() { - // 不支持远程 + List lackInfos; + + // 远程 if (!WorkContext.getCurrent().isLocal()) { - return DetectorResult.normal(type()); + // 检测有哪些 JAR 包, 当前是否缺少对应的 JAR 包 + BuildInfoOperator buildInfoOperator = WorkContext.getCurrent().get(BuildInfoOperator.class); + // 远程情况 + List remoteInfos = buildInfoOperator.getBuildInfos(); + // 本地情况 + List localInfos = BuildInfoManager.getInstance().getInfos(); + + Set remoteSet = remoteInfos.stream() + .filter(this::isExistInfo) + .map(BuildInfo::getJar) + .collect(Collectors.toSet()); + + Predicate remoteNotContains = (e) -> !remoteSet.contains(e.getJar()); + + lackInfos = localInfos.stream() + .filter(this::isExistInfo) + .filter(remoteNotContains) + .collect(Collectors.toList()); + + } else { + // 本地 + // 检测有哪些 JAR 包, 当前是否缺少对应的 JAR 包 + BuildInfoOperator buildInfoOperator = new BuildInfoOperatorImpl(); + List buildInfos = buildInfoOperator.getBuildInfos(); + lackInfos = buildInfos.stream() + .filter(this::isLackInfo) + .collect(Collectors.toList()); } - - // 检测有哪些 JAR 包, 当前是否缺少对应的 JAR 包 - BuildInfoOperator buildInfoOperator = new BuildInfoOperatorImpl(); - List buildInfos = buildInfoOperator.getBuildInfos(); - List lackInfos = buildInfos.stream() - .filter(this::isLackInfo) - .collect(Collectors.toList()); if (Collections.isEmpty(lackInfos)) { return DetectorResult.normal(type()); @@ -77,6 +101,11 @@ public class JarLackDetector extends AbstractExceptionDetector { return ExceptionLog.create(Toolkit.i18nText(type().getLogLocale()) + message); } + private boolean isExistInfo(BuildInfo e) { + + return !isLackInfo(e); + } + private boolean isLackInfo(BuildInfo e) { return StringUtils.isEmpty(e.getGroupBuild()); From 639d5f6d8e504edfe0601e92bcfa61e33762cda9 Mon Sep 17 00:00:00 2001 From: Harrison Date: Fri, 10 Jun 2022 14:47:22 +0800 Subject: [PATCH 53/85] =?UTF-8?q?REPORT-73391=E3=80=90=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E7=8E=AF=E5=A2=83=E7=9B=91=E6=B5=8B=E3=80=91=E7=BC=BA?= =?UTF-8?q?=E5=B0=91fine-activator-11.0=EF=BC=8C=E9=A2=84=E6=9C=9F?= =?UTF-8?q?=E5=A4=96=E5=90=AF=E5=8A=A8=E7=9A=84=E5=BC=B9=E7=AA=97=E6=B2=A1?= =?UTF-8?q?=E6=9C=89=E6=8D=95=E8=8E=B7=20=E5=AE=88=E6=8A=A4=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B=E7=9A=84=E6=A3=80=E6=B5=8B=E9=80=BB=E8=BE=91=E5=A4=84?= =?UTF-8?q?=E7=90=86=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/fr/start/FineDesigner.java | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/designer-realize/src/main/java/com/fr/start/FineDesigner.java b/designer-realize/src/main/java/com/fr/start/FineDesigner.java index a3e202308..a1bd3591c 100644 --- a/designer-realize/src/main/java/com/fr/start/FineDesigner.java +++ b/designer-realize/src/main/java/com/fr/start/FineDesigner.java @@ -1,5 +1,7 @@ package com.fr.start; +import com.fr.exit.DesignerExiter; +import com.fr.process.engine.FineProcessUtils; import com.fr.process.engine.core.FineProcessEntry; /** @@ -10,5 +12,20 @@ import com.fr.process.engine.core.FineProcessEntry; * Created by hades on 2020/3/24 */ public class FineDesigner extends FineProcessEntry { - + + public static void main(String[] args) { + + FineDesigner fineDesigner = new FineDesigner(); + FineProcessUtils.run(fineDesigner, args); + } + + @Override + public void run(String[] args) { + + try { + super.run(args); + } catch (Throwable throwable) { + DesignerExiter.getInstance().exit(throwable); + } + } } From 8cbd251beb00a8c0bf1207334a318bca3497dd0e Mon Sep 17 00:00:00 2001 From: Harrison Date: Fri, 10 Jun 2022 15:36:18 +0800 Subject: [PATCH 54/85] =?UTF-8?q?=20REPORT-73312=20=E3=80=90=E8=AE=BE?= =?UTF-8?q?=E8=AE=A1=E5=99=A8=E7=8E=AF=E5=A2=83=E7=9B=91=E6=B5=8B=E3=80=91?= =?UTF-8?q?=E8=BF=9C=E7=A8=8B=E5=88=87=E6=8D=A2=E6=97=B6=EF=BC=8Cfine-repo?= =?UTF-8?q?rt-designer-11.0=E4=B8=80=E5=AE=9A=E4=BC=9A=E8=A7=A6=E5=8F=91?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E4=B8=8D=E4=B8=80=E8=87=B4=E7=9A=84=E6=A3=80?= =?UTF-8?q?=E6=B5=8B=20=20=E8=BF=87=E6=BB=A4=E6=8E=89=20designer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/fr/env/detect/impl/JarLackDetector.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/JarLackDetector.java b/designer-base/src/main/java/com/fr/env/detect/impl/JarLackDetector.java index a769921b0..82b906270 100644 --- a/designer-base/src/main/java/com/fr/env/detect/impl/JarLackDetector.java +++ b/designer-base/src/main/java/com/fr/env/detect/impl/JarLackDetector.java @@ -65,6 +65,8 @@ public class JarLackDetector extends AbstractExceptionDetector { lackInfos = localInfos.stream() .filter(this::isExistInfo) + // 不是设计器的 JAR + .filter((e) -> !DetectorUtil.isDesignerJar(e)) .filter(remoteNotContains) .collect(Collectors.toList()); From 293fd4ff876010afb0e9245d1a26eb24e0495881 Mon Sep 17 00:00:00 2001 From: Harrison Date: Fri, 10 Jun 2022 15:37:14 +0800 Subject: [PATCH 55/85] =?UTF-8?q?REPORT-73396=E3=80=90=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E7=8E=AF=E5=A2=83=E7=9B=91=E6=B5=8B=E3=80=91=E5=88=A0?= =?UTF-8?q?=E9=99=A4fine-decision-11.0=EF=BC=8C=E8=A7=A6=E5=8F=91=E7=9A=84?= =?UTF-8?q?=E6=98=AF=E9=87=8D=E7=BD=AEfinedb=20=E5=BD=93=E9=81=87=E5=88=B0?= =?UTF-8?q?=20finedb=20=E5=BC=82=E5=B8=B8=E5=8D=B4=E6=98=AF=E7=BC=BA?= =?UTF-8?q?=E5=B0=91=20JAR=20=E5=AF=BC=E8=87=B4=E7=9A=84=E6=97=B6=E5=80=99?= =?UTF-8?q?=EF=BC=8C=E4=BA=A4=E4=BA=92=E8=A1=A5=E5=85=85=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fr/start/LifecycleFatalErrorHandler.java | 64 +++++++++++++------ 1 file changed, 45 insertions(+), 19 deletions(-) diff --git a/designer-realize/src/main/java/com/fr/start/LifecycleFatalErrorHandler.java b/designer-realize/src/main/java/com/fr/start/LifecycleFatalErrorHandler.java index 37f608ee1..fbe8f1962 100644 --- a/designer-realize/src/main/java/com/fr/start/LifecycleFatalErrorHandler.java +++ b/designer-realize/src/main/java/com/fr/start/LifecycleFatalErrorHandler.java @@ -1,15 +1,19 @@ package com.fr.start; import com.fr.common.report.ReportState; +import com.fr.common.util.Collections; import com.fr.design.RestartHelper; import com.fr.design.dialog.FineJOptionPane; import com.fr.design.i18n.Toolkit; +import com.fr.design.mainframe.DesignerContext; +import com.fr.design.mainframe.DesignerFrame; import com.fr.design.mainframe.messagecollect.StartErrorMessageCollector; import com.fr.design.mainframe.messagecollect.entity.DesignerErrorMessage; import com.fr.env.detect.base.DetectorBridge; import com.fr.env.detect.bean.DetectorResult; import com.fr.env.detect.bean.DetectorStatus; import com.fr.env.detect.bean.DetectorType; +import com.fr.env.detect.ui.DetectorErrorDialog; import com.fr.exit.DesignerExiter; import com.fr.general.IOUtils; import com.fr.io.utils.ResourceIOUtils; @@ -17,14 +21,19 @@ import com.fr.log.FineLoggerFactory; import com.fr.process.engine.core.CarryMessageEvent; import com.fr.process.engine.core.FineProcessContext; import com.fr.stable.StableUtils; +import com.fr.stable.StringUtils; import com.fr.stable.lifecycle.ErrorType; import com.fr.stable.lifecycle.ErrorTypeHelper; import com.fr.stable.lifecycle.FineLifecycleFatalError; import com.fr.stable.project.ProjectConstants; import javax.swing.JOptionPane; +import java.util.EnumMap; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * @author hades @@ -68,10 +77,30 @@ public class LifecycleFatalErrorHandler { * 自检测 */ SELF { + final EnumMap solutionMap = new EnumMap<>(DetectorType.class); + + { + solutionMap.put(DetectorType.FINE_DB_LOCKED, "Fine-Design_Error_Finedb_Dirty_Backup_Reset"); + solutionMap.put(DetectorType.FINE_DB_PERMISSION, "Fine-Design_Error_Finedb_Permission_Backup_Reset"); + solutionMap.put(DetectorType.FINE_DB_DIRTY, "Fine-Design_Error_Finedb_Dirty_Backup_Reset"); + } + @Override public void handle(FineLifecycleFatalError fatal) { - String showText = generateShowText(fatal); + Stream resultStream = DetectorBridge.getInstance().detect(fatal); + List results = resultStream + .filter((e) -> e.getStatus() == DetectorStatus.EXCEPTION) + .collect(Collectors.toList()); + + String showText = generateShowText(results); + // 如果还是异常,则抛出预期外的错误 + if (StringUtils.isEmpty(showText)) { + DesignerFrame designerFrame = DesignerContext.getDesignerFrame(); + DetectorErrorDialog errorDialog = new DetectorErrorDialog(designerFrame, results); + errorDialog.setVisible(true); + return; + } StartErrorMessageCollector.getInstance().record(DesignerErrorMessage.FINEDB_PROBLEM.getId(), DesignerErrorMessage.FINEDB_PROBLEM.getMessage(), @@ -107,26 +136,23 @@ public class LifecycleFatalErrorHandler { /** * 生成展示信息 - * - * @param fatal 异常 - * @return 文本 */ - private String generateShowText(FineLifecycleFatalError fatal) { - - // todo 其实这里的交互还是有问题, 为什么在锁住和没权限的场景下,要重置 FineDB 呢。 - DetectorResult detectorResult = DetectorBridge.getInstance().detect(DetectorType.FINE_DB_LOCKED, fatal); - if (detectorResult.getStatus() == DetectorStatus.EXCEPTION) { - return Toolkit.i18nText("Fine-Design_Error_Finedb_Locked_Backup_Reset"); - } - detectorResult = DetectorBridge.getInstance().detect(DetectorType.FINE_DB_PERMISSION, fatal); - if (detectorResult.getStatus() == DetectorStatus.EXCEPTION) { - return Toolkit.i18nText("Fine-Design_Error_Finedb_Permission_Backup_Reset"); - } - detectorResult = DetectorBridge.getInstance().detect(DetectorType.FINE_DB_DIRTY, fatal); - if (detectorResult.getStatus() == DetectorStatus.EXCEPTION) { - return Toolkit.i18nText("Fine-Design_Error_Finedb_Dirty_Backup_Reset"); + private String generateShowText(List results) { + + String showText = StringUtils.EMPTY; + if (Collections.isEmpty(results)) { + showText = Toolkit.i18nText("Fine-Design_Error_Finedb_Backup_Reset"); + } else { + for (DetectorResult result : results) { + DetectorType type = result.getType(); + String solutionLocale = solutionMap.get(type); + if (StringUtils.isNotEmpty(solutionLocale)) { + showText = Toolkit.i18nText(solutionLocale); + break; + } + } } - return Toolkit.i18nText("Fine-Design_Error_Finedb_Backup_Reset"); + return showText; } private void afterBackupFailed() { From 0e4183ca6460dbf065530aeeabed421fa3db6cb4 Mon Sep 17 00:00:00 2001 From: Harrison Date: Fri, 10 Jun 2022 16:27:05 +0800 Subject: [PATCH 56/85] =?UTF-8?q?REPORT-73318=E3=80=90=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E7=8E=AF=E5=A2=83=E7=9B=91=E6=B5=8B=E3=80=91=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E5=99=A8finedb=E6=9C=89=E8=84=8F=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=EF=BC=8C=E8=AE=BE=E8=AE=A1=E5=99=A8=E8=BF=9C=E7=A8=8B=E5=88=87?= =?UTF-8?q?=E6=8D=A2=EF=BC=8C=E8=BF=87=E7=A8=8B=E5=BE=88=E6=85=A2=E4=B8=94?= =?UTF-8?q?=E6=B2=A1=E6=9C=89=E8=87=AA=E5=8A=A8=E7=9B=91=E6=B5=8B=E5=BC=B9?= =?UTF-8?q?=E7=AA=97=20=E5=A4=84=E7=90=86=E5=88=87=E6=8D=A2=E8=BF=9C?= =?UTF-8?q?=E7=A8=8B=E6=97=B6=E4=B8=8D=E7=94=9F=E6=95=88=E7=9A=84=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/fr/env/detect/EnvDetectorCenter.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/designer-base/src/main/java/com/fr/env/detect/EnvDetectorCenter.java b/designer-base/src/main/java/com/fr/env/detect/EnvDetectorCenter.java index 070d63d85..e73aa673b 100644 --- a/designer-base/src/main/java/com/fr/env/detect/EnvDetectorCenter.java +++ b/designer-base/src/main/java/com/fr/env/detect/EnvDetectorCenter.java @@ -18,6 +18,7 @@ import com.fr.event.EventDispatcher; import com.fr.event.Listener; import com.fr.event.Null; import com.fr.start.server.EmbedServerEvent; +import com.fr.task.Once; import com.fr.update.delay.DelayHelper; import com.fr.workspace.Workspace; import com.fr.workspace.WorkspaceEvent; @@ -72,6 +73,14 @@ public class EnvDetectorCenter { } }; + private final Once launchOnce = new Once(() -> { + + // 添加启动完成监听 + EventDispatcher.listen(DesignerLaunchStatus.STARTUP_COMPLETE, START_UP_COMPLETE_LISTENER); + // 切换完成后的监听 + EventDispatcher.listen(WorkspaceEvent.AfterSwitch, AFTER_SWITCH_LISTENER); + }); + private final AtomicReference PROCESS = new AtomicReference<>(); public static EnvDetectorCenter getInstance() { @@ -96,6 +105,7 @@ public class EnvDetectorCenter { // 默认是启动 PROCESS.set(DetectorProcess.DESIGN_LAUNCH); + launchOnce.run(); listen(); } @@ -189,12 +199,6 @@ public class EnvDetectorCenter { private void listen() { - // 添加启动完成监听 - EventDispatcher.listen(DesignerLaunchStatus.STARTUP_COMPLETE, START_UP_COMPLETE_LISTENER); - - // 切换完成后的监听 - EventDispatcher.listen(WorkspaceEvent.AfterSwitch, AFTER_SWITCH_LISTENER); - // 内置服务器监听 EventDispatcher.listen(EmbedServerEvent.BeforeStart, BEFORE_START_LISTENER); EventDispatcher.listen(EmbedServerEvent.AfterStart, AFTER_START_LISTENER); @@ -202,8 +206,6 @@ public class EnvDetectorCenter { private void stopListen() { - EventDispatcher.stopListen(START_UP_COMPLETE_LISTENER); - EventDispatcher.stopListen(AFTER_SWITCH_LISTENER); EventDispatcher.stopListen(BEFORE_START_LISTENER); EventDispatcher.stopListen(AFTER_START_LISTENER); } From 945994e032ef138fd802e0f3b2a6ef917a097b8d Mon Sep 17 00:00:00 2001 From: Harrison Date: Fri, 10 Jun 2022 16:31:28 +0800 Subject: [PATCH 57/85] =?UTF-8?q?REPORT-73403=E3=80=90=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E7=8E=AF=E5=A2=83=E7=9B=91=E6=B5=8B=E3=80=91jar?= =?UTF-8?q?=E5=BC=82=E5=B8=B8=E5=BC=B9=E7=AA=97=EF=BC=8C%FR=5FHOME%\lib?= =?UTF-8?q?=E5=92=8C\WEB-INF\lib=E4=B8=8B=E7=9A=84jar=E9=9C=80=E8=A6=81?= =?UTF-8?q?=E5=8C=BA=E5=88=86=E6=98=BE=E7=A4=BA=20=E9=80=82=E9=85=8D?= =?UTF-8?q?=E4=BA=A4=E4=BA=92=E7=9A=84=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fr/env/detect/base/DetectorConstants.java | 7 ++++ .../com/fr/env/detect/base/DetectorUtil.java | 42 ++++++++++++++++++- .../fr/env/detect/impl/JarLackDetector.java | 35 +++------------- .../converter/ClassConflictConvertor.java | 20 +++++---- 4 files changed, 67 insertions(+), 37 deletions(-) diff --git a/designer-base/src/main/java/com/fr/env/detect/base/DetectorConstants.java b/designer-base/src/main/java/com/fr/env/detect/base/DetectorConstants.java index be1036f0d..4e4592dcd 100644 --- a/designer-base/src/main/java/com/fr/env/detect/base/DetectorConstants.java +++ b/designer-base/src/main/java/com/fr/env/detect/base/DetectorConstants.java @@ -8,4 +8,11 @@ public class DetectorConstants { public static final String JAR_HELP_LINK = "https://help.fanruan.com/finereport/doc-view-4700.html?source=3"; public static final String FINE_DB_HELP_LINK = "https://help.fanruan.com/finereport/doc-view-4701.html?source=3"; + + public static final String SEPARATOR = "、"; + public static final String BR_TAG = "
"; + + public static final String WEB_LIB_PATH = "%FR_HOME%\\webapps\\webroot\\WEB-INF\\lib:"; + + public static final String FR_HOME_LIB = "%FR_HOME%\\lib:"; } diff --git a/designer-base/src/main/java/com/fr/env/detect/base/DetectorUtil.java b/designer-base/src/main/java/com/fr/env/detect/base/DetectorUtil.java index fdfccf216..fd6c33607 100644 --- a/designer-base/src/main/java/com/fr/env/detect/base/DetectorUtil.java +++ b/designer-base/src/main/java/com/fr/env/detect/base/DetectorUtil.java @@ -1,6 +1,7 @@ package com.fr.env.detect.base; import com.fr.base.function.ThrowableRunnable; +import com.fr.common.util.Collections; import com.fr.design.components.notification.NotificationAction; import com.fr.design.components.notification.NotificationMessage; import com.fr.design.components.notification.NotificationModel; @@ -14,7 +15,7 @@ import com.fr.env.detect.bean.ExceptionTips; import com.fr.env.detect.bean.Message; import com.fr.env.detect.bean.SolutionAction; import com.fr.general.build.BuildInfo; -import com.fr.stable.StringUtils; +import com.fr.third.org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import javax.swing.JComponent; @@ -22,9 +23,15 @@ import java.awt.Desktop; import java.net.URI; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.function.Function; +import static com.fr.env.detect.base.DetectorConstants.BR_TAG; +import static com.fr.env.detect.base.DetectorConstants.FR_HOME_LIB; +import static com.fr.env.detect.base.DetectorConstants.SEPARATOR; +import static com.fr.env.detect.base.DetectorConstants.WEB_LIB_PATH; + /** * created by Harrison on 2022/05/25 **/ @@ -124,4 +131,37 @@ public class DetectorUtil { } return new UILabel(LinkStrUtils.generateHtmlTag(message.get())); } + + /** + * 将 lib 转化成合适的格式 + * %FR_HOME%/lib + * %FR_HOME%/webapps/webroot/WEB-INF/lib + * + * @param libMap jar 路径, key为前缀 + * @return 信息 + */ + public static String concatLibFormatMsg(Map> libMap) { + + String webLibPath = WEB_LIB_PATH; + String homeLibPath = FR_HOME_LIB; + + StringBuilder sb = new StringBuilder(); + + List homeLibs = libMap.get(homeLibPath); + if (!Collections.isEmpty(homeLibs)) { + sb.append(homeLibPath); + sb.append(StringUtils.join(homeLibs, SEPARATOR)); + } + + List webLibs = libMap.get(webLibPath); + if (!Collections.isEmpty(webLibs)) { + if (sb.length() != 0) { + sb.append(BR_TAG); + } + sb.append(webLibPath); + sb.append(StringUtils.join(webLibs, SEPARATOR)); + } + return sb.toString(); + } + } diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/JarLackDetector.java b/designer-base/src/main/java/com/fr/env/detect/impl/JarLackDetector.java index 82b906270..322c15d50 100644 --- a/designer-base/src/main/java/com/fr/env/detect/impl/JarLackDetector.java +++ b/designer-base/src/main/java/com/fr/env/detect/impl/JarLackDetector.java @@ -27,16 +27,14 @@ import java.util.Set; import java.util.function.Predicate; import java.util.stream.Collectors; +import static com.fr.env.detect.base.DetectorConstants.FR_HOME_LIB; +import static com.fr.env.detect.base.DetectorConstants.WEB_LIB_PATH; + /** * created by Harrison on 2022/05/24 **/ public class JarLackDetector extends AbstractExceptionDetector { - public static final String SEPARATOR = "、"; - public static final String BR_TAG = "
"; - public static final String WEB_LIB_PATH = "%FR_HOME%\\webapps\\webroot\\WEB-INF\\lib:"; - public static final String FR_HOME_LIB = "%FR_HOME%\\lib:"; - public JarLackDetector() { super(DetectorType.LACK_OF_JAR); @@ -115,33 +113,12 @@ public class JarLackDetector extends AbstractExceptionDetector { private Message tipsMessage(List infos) { - String webLibPath = WEB_LIB_PATH; - String homeLibPath = FR_HOME_LIB; - - Map> libMap = groupByPath(infos, homeLibPath, webLibPath); - - StringBuilder sb = new StringBuilder(); - - List homeLibs = libMap.get(homeLibPath); - if (!Collections.isEmpty(homeLibs)) { - sb.append(homeLibPath); - sb.append(StringUtils.join(homeLibs, SEPARATOR)); - } - - List webLibs = libMap.get(webLibPath); - if (!Collections.isEmpty(webLibs)) { - if (sb.length() != 0) { - sb.append(BR_TAG); - } - sb.append(webLibPath); - sb.append(StringUtils.join(webLibs, SEPARATOR)); - } - - String mainContent = sb.toString(); + Map> libMap = groupByPath(infos, FR_HOME_LIB, WEB_LIB_PATH); + String content = DetectorUtil.concatLibFormatMsg(libMap); DetectorType type = this.type(); String header = Toolkit.i18nText(type.getTipsLocale()); - return new Message.Simple(header + mainContent); + return new Message.Simple(header + content); } @NotNull diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassConflictConvertor.java b/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassConflictConvertor.java index ab2b16ed9..ef5be1339 100644 --- a/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassConflictConvertor.java +++ b/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassConflictConvertor.java @@ -2,6 +2,7 @@ package com.fr.env.detect.impl.converter; import com.fr.design.i18n.Toolkit; import com.fr.env.detect.base.DetectorConstants; +import com.fr.env.detect.base.DetectorUtil; import com.fr.env.detect.bean.DetectorResult; import com.fr.env.detect.bean.DetectorType; import com.fr.env.detect.bean.ExceptionLog; @@ -9,7 +10,6 @@ import com.fr.env.detect.bean.ExceptionSolution; import com.fr.env.detect.bean.ExceptionTips; import com.fr.env.detect.thowable.ThrowableConverter; import com.fr.stable.resource.ResourceLoader; -import com.fr.third.org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import javax.el.MethodNotFoundException; @@ -83,6 +83,10 @@ public class ClassConflictConvertor implements ThrowableConverter { sign = sign.getCause(); } + Map> libMap = new HashMap<>(); + libMap.put(DetectorConstants.FR_HOME_LIB, new ArrayList<>()); + libMap.put(DetectorConstants.WEB_LIB_PATH, new ArrayList<>()); + Set allPath = new HashSet<>(); for (String className : classNames) { String classFile = convertClass2Path(className); @@ -98,12 +102,14 @@ public class ClassConflictConvertor implements ThrowableConverter { Matcher matcher = JAR_NAME_PATTERN.matcher(url.getFile()); if (matcher.find()) { String jar = matcher.group(); - allPath.add(jar); - } else { - boolean containsClasses = file.contains(CLASSES); - if (containsClasses) { - allPath.add(CLASSES); + List libPath = null; + if (file.contains("WEB-INF")) { + libPath = libMap.get(DetectorConstants.WEB_LIB_PATH); + } else { + libPath = libMap.get(DetectorConstants.FR_HOME_LIB); } + libPath.add(jar); + allPath.add(jar); } } } catch (IOException ignore) { @@ -115,7 +121,7 @@ public class ClassConflictConvertor implements ThrowableConverter { return null; } - String msg = StringUtils.join(allPath, SEPARATOR); + String msg = DetectorUtil.concatLibFormatMsg(libMap); DetectorType type = DetectorType.JAR_CONFLICT; return DetectorResult.exception(type, From 3910005fe8381e8d8c586b217a4b5b3f8de1f798 Mon Sep 17 00:00:00 2001 From: Harrison Date: Fri, 10 Jun 2022 16:41:25 +0800 Subject: [PATCH 58/85] =?UTF-8?q?=E6=97=A0=20JIRA=20=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=EF=BC=8C=E8=A1=A5=E5=85=85=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fr/design/components/notification/NotificationDialog.java | 4 +++- .../java/com/fr/env/detect/impl/JarInconsistentDetector.java | 1 + designer-realize/src/main/java/com/fr/start/FineDesigner.java | 1 + .../main/java/com/fr/start/LifecycleFatalErrorHandler.java | 2 +- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java b/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java index 05a3149b8..14fb6d007 100644 --- a/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java +++ b/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java @@ -154,7 +154,9 @@ public class NotificationDialog extends JDialog { }) .collect(Collectors.toList()); - Function calStandardWidth = e -> e > 4 ? 280 : 300; + // 当高度 / 15(每个字的大小) 大于 4 时,就会出现滚动条。 + // 当出现滚动条时,需要将内部的宽度限制为 280, 否则会展示不出来 + Function calStandardWidth = heightFactor -> heightFactor > 4 ? 280 : 300; int widthUnit = messageComponents.stream() .map((component) -> { diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/JarInconsistentDetector.java b/designer-base/src/main/java/com/fr/env/detect/impl/JarInconsistentDetector.java index e10f18052..8cfc988a9 100644 --- a/designer-base/src/main/java/com/fr/env/detect/impl/JarInconsistentDetector.java +++ b/designer-base/src/main/java/com/fr/env/detect/impl/JarInconsistentDetector.java @@ -145,6 +145,7 @@ public class JarInconsistentDetector extends AbstractExceptionDetector { for (BuildInfo localInfo : localInfos) { String jar = localInfo.getJar(); String groupContent = localInfo.getGroupBuild(); + // 不一致的 JAR 检测,忽视缺少的情况 if (StringUtils.isNotEmpty(groupContent)) { localMap.put(jar, groupContent); } diff --git a/designer-realize/src/main/java/com/fr/start/FineDesigner.java b/designer-realize/src/main/java/com/fr/start/FineDesigner.java index a1bd3591c..e70575708 100644 --- a/designer-realize/src/main/java/com/fr/start/FineDesigner.java +++ b/designer-realize/src/main/java/com/fr/start/FineDesigner.java @@ -25,6 +25,7 @@ public class FineDesigner extends FineProcessEntry { try { super.run(args); } catch (Throwable throwable) { + // 守护进程启动时,需要捕获异常。并且退出。 DesignerExiter.getInstance().exit(throwable); } } diff --git a/designer-realize/src/main/java/com/fr/start/LifecycleFatalErrorHandler.java b/designer-realize/src/main/java/com/fr/start/LifecycleFatalErrorHandler.java index fbe8f1962..92b8ac6c6 100644 --- a/designer-realize/src/main/java/com/fr/start/LifecycleFatalErrorHandler.java +++ b/designer-realize/src/main/java/com/fr/start/LifecycleFatalErrorHandler.java @@ -94,7 +94,7 @@ public class LifecycleFatalErrorHandler { .collect(Collectors.toList()); String showText = generateShowText(results); - // 如果还是异常,则抛出预期外的错误 + // 如果还是异常,说明并不是 DB 的异常,抛出预期外的错误 if (StringUtils.isEmpty(showText)) { DesignerFrame designerFrame = DesignerContext.getDesignerFrame(); DetectorErrorDialog errorDialog = new DetectorErrorDialog(designerFrame, results); From 52fb4b227e87a9166d03059bef1765dff7ec2176 Mon Sep 17 00:00:00 2001 From: Harrison Date: Fri, 10 Jun 2022 17:50:30 +0800 Subject: [PATCH 59/85] =?UTF-8?q?REPORT-73403=E3=80=90=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E7=8E=AF=E5=A2=83=E7=9B=91=E6=B5=8B=E3=80=91jar?= =?UTF-8?q?=E5=BC=82=E5=B8=B8=E5=BC=B9=E7=AA=97=EF=BC=8C%FR=5FHOME%\lib?= =?UTF-8?q?=E5=92=8C\WEB-INF\lib=E4=B8=8B=E7=9A=84jar=E9=9C=80=E8=A6=81?= =?UTF-8?q?=E5=8C=BA=E5=88=86=E6=98=BE=E7=A4=BA=20=E6=8A=BD=E8=B1=A1?= =?UTF-8?q?=E4=B8=80=E4=B8=AA=E5=B8=B8=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fr/env/detect/impl/converter/ClassConflictConvertor.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassConflictConvertor.java b/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassConflictConvertor.java index ef5be1339..b1f9e507d 100644 --- a/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassConflictConvertor.java +++ b/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassConflictConvertor.java @@ -41,6 +41,8 @@ public class ClassConflictConvertor implements ThrowableConverter { */ private static final Pattern JAR_NAME_PATTERN = Pattern.compile("([\\w+-\\.]*\\.jar)"); + private static final String WEB_INF_STRING = "WEB-INF"; + private final Map, ClassNameConverter> throwableMap = new HashMap<>(); public ClassConflictConvertor() { @@ -103,7 +105,7 @@ public class ClassConflictConvertor implements ThrowableConverter { if (matcher.find()) { String jar = matcher.group(); List libPath = null; - if (file.contains("WEB-INF")) { + if (file.contains(WEB_INF_STRING)) { libPath = libMap.get(DetectorConstants.WEB_LIB_PATH); } else { libPath = libMap.get(DetectorConstants.FR_HOME_LIB); From f539d9293a904db0449e11a6afa6ba59d4a74151 Mon Sep 17 00:00:00 2001 From: Harrison Date: Fri, 10 Jun 2022 18:19:01 +0800 Subject: [PATCH 60/85] =?UTF-8?q?REPORT-73403=E3=80=90=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E7=8E=AF=E5=A2=83=E7=9B=91=E6=B5=8B=E3=80=91jar?= =?UTF-8?q?=E5=BC=82=E5=B8=B8=E5=BC=B9=E7=AA=97=EF=BC=8C%FR=5FHOME%\lib?= =?UTF-8?q?=E5=92=8C\WEB-INF\lib=E4=B8=8B=E7=9A=84jar=E9=9C=80=E8=A6=81?= =?UTF-8?q?=E5=8C=BA=E5=88=86=E6=98=BE=E7=A4=BA=20=E5=A4=84=E7=90=86?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E7=9A=84=E9=AD=94=E6=9C=AF=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notification/NotificationDialog.java | 39 ++++++++++++++----- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java b/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java index 14fb6d007..4df430b06 100644 --- a/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java +++ b/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java @@ -44,8 +44,26 @@ import java.util.stream.Collectors; **/ public class NotificationDialog extends JDialog { - private Dimension contentSize = new Dimension(300, 100); - private Dimension buttonDimension = new Dimension(68, 20); + /** + * 通知框的内部高度 + */ + private static final int CONTENT_INNER_HEIGHT = 60; + /** + * 通知框如果出现滚动条后的内部宽度 + */ + private static final int CONTENT_SCROLL_WIDTH = 280; + + private static final int CONTENT_WIDTH = 300; + private static final int CONTENT_HEIGHT = 100; + /** + * 通知框的外部宽高 + */ + private static final Dimension CONTENT_SIZE = new Dimension(CONTENT_WIDTH, CONTENT_HEIGHT); + private static final Dimension BUTTON_DIMENSION = new Dimension(68, 20); + + private static final UILabel SIGN_LABEL = new UILabel("#"); + + private static final Dimension SIGN_LABEL_DIMENSION = SIGN_LABEL.getPreferredSize(); private NotificationDialogProperties properties; @@ -154,9 +172,9 @@ public class NotificationDialog extends JDialog { }) .collect(Collectors.toList()); - // 当高度 / 15(每个字的大小) 大于 4 时,就会出现滚动条。 + // 当高度 大于 60 时,就会出现滚动条。 // 当出现滚动条时,需要将内部的宽度限制为 280, 否则会展示不出来 - Function calStandardWidth = heightFactor -> heightFactor > 4 ? 280 : 300; + Function calStandardWidth = height -> height > CONTENT_INNER_HEIGHT ? CONTENT_SCROLL_WIDTH : CONTENT_WIDTH; int widthUnit = messageComponents.stream() .map((component) -> { @@ -165,16 +183,17 @@ public class NotificationDialog extends JDialog { }) .reduce(Double::sum) .map(calStandardWidth) - .orElse(300); + .orElse(CONTENT_WIDTH); messageComponents = messageComponents.stream() .peek((component) -> { + int size = component.getFont().getSize(); Dimension preferredSize = component.getPreferredSize(); double componentWidth = preferredSize.getWidth(); double componentHeight = preferredSize.getHeight(); - double heightFactor = Math.ceil(componentHeight / 15); + double heightFactor = Math.ceil(componentHeight / SIGN_LABEL_DIMENSION.getHeight()); double widthFactor = Math.ceil(componentWidth / widthUnit); - int realHeight = (int) (heightFactor + widthFactor - 1) * 15; + int realHeight = (int)Math.ceil(heightFactor + widthFactor - 1) * (int)(Math.ceil(SIGN_LABEL_DIMENSION.getHeight())); component.setPreferredSize(new Dimension(widthUnit, realHeight)); }) .collect(Collectors.toList()); @@ -187,7 +206,7 @@ public class NotificationDialog extends JDialog { jScrollPane.setBorder(BorderFactory.createEmptyBorder()); centerPanel.add(jScrollPane, BorderLayout.CENTER); - centerPanel.setPreferredSize(contentSize); + centerPanel.setPreferredSize(CONTENT_SIZE); contentPanel.add(centerPanel, BorderLayout.CENTER); @@ -256,7 +275,7 @@ public class NotificationDialog extends JDialog { NotificationAction action = currentModel.getAction(); if (action != null) { UIButton actionButton = new UIButton(action.name()); - actionButton.setPreferredSize(buttonDimension); + actionButton.setPreferredSize(BUTTON_DIMENSION); actionButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -267,7 +286,7 @@ public class NotificationDialog extends JDialog { } UIButton knowButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Know")); - knowButton.setPreferredSize(buttonDimension); + knowButton.setPreferredSize(BUTTON_DIMENSION); knowButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { From f6cf059be1768012a407c0b40f051b711bcd5af2 Mon Sep 17 00:00:00 2001 From: Harrison Date: Fri, 10 Jun 2022 18:21:51 +0800 Subject: [PATCH 61/85] =?UTF-8?q?=E6=97=A0=20JIRA=20=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=EF=BC=8C=E8=A1=A5=E5=85=85=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/notification/NotificationDialog.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java b/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java index 4df430b06..4be51f1a1 100644 --- a/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java +++ b/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java @@ -61,8 +61,13 @@ public class NotificationDialog extends JDialog { private static final Dimension CONTENT_SIZE = new Dimension(CONTENT_WIDTH, CONTENT_HEIGHT); private static final Dimension BUTTON_DIMENSION = new Dimension(68, 20); + /** + * 标记 LABEL, 没有作用 + */ private static final UILabel SIGN_LABEL = new UILabel("#"); - + /** + * 确认一个 LABEL 的宽高 + */ private static final Dimension SIGN_LABEL_DIMENSION = SIGN_LABEL.getPreferredSize(); private NotificationDialogProperties properties; @@ -187,7 +192,6 @@ public class NotificationDialog extends JDialog { messageComponents = messageComponents.stream() .peek((component) -> { - int size = component.getFont().getSize(); Dimension preferredSize = component.getPreferredSize(); double componentWidth = preferredSize.getWidth(); double componentHeight = preferredSize.getHeight(); From 038f97c796b72f85beaf8dfc004acea99750ae87 Mon Sep 17 00:00:00 2001 From: "Yuan.Wang" <1536296691@qq.com> Date: Mon, 13 Jun 2022 10:22:11 +0800 Subject: [PATCH 62/85] =?UTF-8?q?REPORT-70850=20=E4=B8=8B=E6=8B=89?= =?UTF-8?q?=E6=A0=91=E4=BC=98=E5=8C=96=E5=9F=8B=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/fr/design/gui/frpane/TreeSettingPane.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/designer-base/src/main/java/com/fr/design/gui/frpane/TreeSettingPane.java b/designer-base/src/main/java/com/fr/design/gui/frpane/TreeSettingPane.java index 59ea03ae2..da1862f3d 100644 --- a/designer-base/src/main/java/com/fr/design/gui/frpane/TreeSettingPane.java +++ b/designer-base/src/main/java/com/fr/design/gui/frpane/TreeSettingPane.java @@ -62,12 +62,6 @@ public class TreeSettingPane extends BasicPane implements DataCreatorUI { UILabel buildWayLabel = new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Build_Way") + " :"); buildWayPanel.add(buildWayLabel); buildBox = new UIComboBox(buildWay); - buildBox.addItemListener(e -> { - cardChanged(buildBox.getSelectedIndex()); - if (e.getStateChange() == ItemEvent.SELECTED) { - doBuildBoxSelect(buildBox.getSelectedIndex()); - } - }); buildWayPanel.add(buildBox); controlPane = new JTreeControlPane(new NameableCreator[]{treeNode}, @@ -78,7 +72,12 @@ public class TreeSettingPane extends BasicPane implements DataCreatorUI { cardChanged(0); } - private void doBuildBoxSelect(Integer selectedIndex) { + @Override + public void checkValid() throws Exception { + doBuildBoxSelect(buildBox.getSelectedIndex()); + } + + private void doBuildBoxSelect(Integer selectedIndex) { } private void cardChanged(int index) { From ffa640087adb37e5cf8e68d380787ce52ffe364b Mon Sep 17 00:00:00 2001 From: "Yuan.Wang" <1536296691@qq.com> Date: Mon, 13 Jun 2022 14:13:45 +0800 Subject: [PATCH 63/85] =?UTF-8?q?REPORT-70850=20=E4=B8=8B=E6=8B=89?= =?UTF-8?q?=E6=A0=91=E4=BC=98=E5=8C=96=E5=9F=8B=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/fr/design/gui/frpane/TreeSettingPane.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/designer-base/src/main/java/com/fr/design/gui/frpane/TreeSettingPane.java b/designer-base/src/main/java/com/fr/design/gui/frpane/TreeSettingPane.java index da1862f3d..b187a8bfa 100644 --- a/designer-base/src/main/java/com/fr/design/gui/frpane/TreeSettingPane.java +++ b/designer-base/src/main/java/com/fr/design/gui/frpane/TreeSettingPane.java @@ -62,6 +62,9 @@ public class TreeSettingPane extends BasicPane implements DataCreatorUI { UILabel buildWayLabel = new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Build_Way") + " :"); buildWayPanel.add(buildWayLabel); buildBox = new UIComboBox(buildWay); + buildBox.addItemListener(e -> { + cardChanged(buildBox.getSelectedIndex()); + }); buildWayPanel.add(buildBox); controlPane = new JTreeControlPane(new NameableCreator[]{treeNode}, From 5d277b9835f9a10ca15f3df2910819e91a7c543b Mon Sep 17 00:00:00 2001 From: Harrison Date: Mon, 13 Jun 2022 14:15:43 +0800 Subject: [PATCH 64/85] =?UTF-8?q?REPORT-73262=E3=80=90=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E7=8E=AF=E5=A2=83=E7=9B=91=E6=B5=8B=E3=80=91=E5=8F=B3?= =?UTF-8?q?=E4=B8=8B=E8=A7=92=E5=BC=B9=E7=AA=97=E6=96=87=E5=AD=97=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E4=B8=8D=E5=85=A8=20rt,=20=E4=BD=BF=E7=94=A8=20scroll?= =?UTF-8?q?Panel=20=E7=9A=84=E6=97=B6=E5=80=99=E9=9C=80=E8=A6=81=E8=AE=A1?= =?UTF-8?q?=E7=AE=97=E5=AE=BD=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../design/components/notification/NotificationDialog.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java b/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java index 4be51f1a1..a4935b30c 100644 --- a/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java +++ b/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java @@ -184,7 +184,10 @@ public class NotificationDialog extends JDialog { int widthUnit = messageComponents.stream() .map((component) -> { Dimension preferredSize = component.getPreferredSize(); - return preferredSize.getHeight(); + double width = preferredSize.getWidth(); + double widthFactor = Math.ceil(width / CONTENT_WIDTH); + // 这里的高度是没有限制宽度的,如果限制宽度,高度会变更,所以这里需要加上宽度的影响 + return preferredSize.getHeight() + widthFactor * SIGN_LABEL_DIMENSION.getHeight(); }) .reduce(Double::sum) .map(calStandardWidth) From 3e054ef021cf59695dad58c9518a1ab818dd3869 Mon Sep 17 00:00:00 2001 From: Harrison Date: Mon, 13 Jun 2022 15:44:35 +0800 Subject: [PATCH 65/85] =?UTF-8?q?REPORT-73403=E3=80=90=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E7=8E=AF=E5=A2=83=E7=9B=91=E6=B5=8B=E3=80=91jar?= =?UTF-8?q?=E5=BC=82=E5=B8=B8=E5=BC=B9=E7=AA=97=EF=BC=8C%FR=5FHOME%\lib?= =?UTF-8?q?=E5=92=8C\WEB-INF\lib=E4=B8=8B=E7=9A=84jar=E9=9C=80=E8=A6=81?= =?UTF-8?q?=E5=8C=BA=E5=88=86=E6=98=BE=E7=A4=BA=20Jar=20=E5=BC=82=E5=B8=B8?= =?UTF-8?q?=E9=83=A8=E5=88=86=E5=A4=84=E7=90=86=E4=B8=80=E4=B8=8B=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../converter/ClassConflictConvertor.java | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassConflictConvertor.java b/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassConflictConvertor.java index b1f9e507d..57c97326e 100644 --- a/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassConflictConvertor.java +++ b/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassConflictConvertor.java @@ -9,12 +9,15 @@ import com.fr.env.detect.bean.ExceptionLog; import com.fr.env.detect.bean.ExceptionSolution; import com.fr.env.detect.bean.ExceptionTips; import com.fr.env.detect.thowable.ThrowableConverter; +import com.fr.stable.EncodeConstants; import com.fr.stable.resource.ResourceLoader; import org.jetbrains.annotations.NotNull; import javax.el.MethodNotFoundException; +import java.io.File; import java.io.IOException; import java.net.URL; +import java.net.URLDecoder; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; @@ -42,6 +45,9 @@ public class ClassConflictConvertor implements ThrowableConverter { private static final Pattern JAR_NAME_PATTERN = Pattern.compile("([\\w+-\\.]*\\.jar)"); private static final String WEB_INF_STRING = "WEB-INF"; + private static final String JAR_URL_SUFFIX = ".jar!"; + private static final String JAR_FILE_SUFFIX = ".jar"; + private static final String FILE_URL_PREFIX = "file:"; private final Map, ClassNameConverter> throwableMap = new HashMap<>(); @@ -101,17 +107,19 @@ public class ClassConflictConvertor implements ThrowableConverter { } for (URL url : urlList) { String file = url.getFile(); - Matcher matcher = JAR_NAME_PATTERN.matcher(url.getFile()); - if (matcher.find()) { - String jar = matcher.group(); - List libPath = null; - if (file.contains(WEB_INF_STRING)) { - libPath = libMap.get(DetectorConstants.WEB_LIB_PATH); - } else { - libPath = libMap.get(DetectorConstants.FR_HOME_LIB); + String decodeFileStr = URLDecoder.decode(file, EncodeConstants.ENCODING_UTF_8); + if (decodeFileStr.contains(JAR_URL_SUFFIX)) { + String jarPath = decodeFileStr.substring(FILE_URL_PREFIX.length(), decodeFileStr.indexOf(JAR_URL_SUFFIX) + JAR_FILE_SUFFIX.length()); + String jar = new File(jarPath).getName(); + if (allPath.add(jar)) { + List libPath; + if (file.contains(WEB_INF_STRING)) { + libPath = libMap.get(DetectorConstants.WEB_LIB_PATH); + } else { + libPath = libMap.get(DetectorConstants.FR_HOME_LIB); + } + libPath.add(jar); } - libPath.add(jar); - allPath.add(jar); } } } catch (IOException ignore) { From 09ecf087f32b212edc78f2ae4844a2929fb98a9b Mon Sep 17 00:00:00 2001 From: Harrison Date: Mon, 13 Jun 2022 16:49:23 +0800 Subject: [PATCH 66/85] =?UTF-8?q?REPORT-73335=E3=80=90=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E7=8E=AF=E5=A2=83=E7=9B=91=E6=B5=8B=E3=80=91=E6=89=8B?= =?UTF-8?q?=E5=8A=A8=E6=A3=80=E6=B5=8B=E5=AE=8C=E6=88=90=E5=90=8E=EF=BC=8C?= =?UTF-8?q?=E4=BA=A4=E4=BA=92=E6=96=87=E5=AD=97=E6=8F=90=E7=A4=BA=E4=B8=A2?= =?UTF-8?q?=E5=A4=B1=20=E4=BF=AE=E4=B8=80=E4=B8=8B=E4=BA=A4=E4=BA=92?= =?UTF-8?q?=E4=B8=8A=E7=9A=84=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java index 2cde3dd89..acfc345d4 100644 --- a/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java +++ b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java @@ -276,7 +276,7 @@ public class EnvDetectorDialog extends JDialog { successLabel.setForeground(Color.GREEN); resultSummaryPane.add(successLabel, BorderLayout.CENTER); } else { - resultSummaryPane.add(new UILabel(Toolkit.i18nText("Fine-Design_Basic_Exception")), BorderLayout.WEST); + resultSummaryPane.add(new UILabel(Toolkit.i18nText("Fine-Design_Basic_Detect_Result_Label")), BorderLayout.WEST); UILabel resultLabel = new UILabel(Toolkit.i18nText("Fine-Design_Basic_Detect_Result_Error")); resultLabel.setForeground(Color.RED); resultSummaryPane.add(resultLabel, BorderLayout.CENTER); From c54effde51d1a2aef3f55939493fc16d59679b70 Mon Sep 17 00:00:00 2001 From: Harrison Date: Mon, 13 Jun 2022 16:53:16 +0800 Subject: [PATCH 67/85] =?UTF-8?q?REPORT-73335=E3=80=90=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E7=8E=AF=E5=A2=83=E7=9B=91=E6=B5=8B=E3=80=91=E6=89=8B?= =?UTF-8?q?=E5=8A=A8=E6=A3=80=E6=B5=8B=E5=AE=8C=E6=88=90=E5=90=8E=EF=BC=8C?= =?UTF-8?q?=E4=BA=A4=E4=BA=92=E6=96=87=E5=AD=97=E6=8F=90=E7=A4=BA=E4=B8=A2?= =?UTF-8?q?=E5=A4=B1=20=E4=BF=AE=E4=B8=80=E4=B8=8B=20UI=20=E7=95=8C?= =?UTF-8?q?=E9=9D=A2=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/fr/env/detect/ui/EnvDetectorDialog.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java index acfc345d4..25689ad20 100644 --- a/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java +++ b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java @@ -52,7 +52,9 @@ import java.util.stream.Stream; public class EnvDetectorDialog extends JDialog { private static final ImageIcon LOADING_ICON = getLoadingIcon(); - public static final int TIMEOUT = 1000; + private static final int TIMEOUT = 1000; + + private static final Color SUCCESS_COLOR = new Color(22, 193, 83); private final JPanel body; @@ -273,7 +275,7 @@ public class EnvDetectorDialog extends JDialog { if (success) { resultSummaryPane.add(new UILabel(Toolkit.i18nText("Fine-Design_Basic_Detect_Result_Label")), BorderLayout.WEST); UILabel successLabel = new UILabel(Toolkit.i18nText("Fine-Design_Basic_Detect_Result_Success")); - successLabel.setForeground(Color.GREEN); + successLabel.setForeground(SUCCESS_COLOR); resultSummaryPane.add(successLabel, BorderLayout.CENTER); } else { resultSummaryPane.add(new UILabel(Toolkit.i18nText("Fine-Design_Basic_Detect_Result_Label")), BorderLayout.WEST); From 4d01785829fe604bfa9828a1b0c8435a69536975 Mon Sep 17 00:00:00 2001 From: Harrison Date: Mon, 13 Jun 2022 17:35:30 +0800 Subject: [PATCH 68/85] =?UTF-8?q?REPORT-73396=E3=80=90=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E7=8E=AF=E5=A2=83=E7=9B=91=E6=B5=8B=E3=80=91=E5=88=A0?= =?UTF-8?q?=E9=99=A4fine-decision-11.0=EF=BC=8C=E8=A7=A6=E5=8F=91=E7=9A=84?= =?UTF-8?q?=E6=98=AF=E9=87=8D=E7=BD=AEfinedb=20=E9=80=9A=E8=BF=87=E5=AE=88?= =?UTF-8?q?=E6=8A=A4=E8=BF=9B=E7=A8=8B=EF=BC=8C=E5=A4=84=E7=90=86=E4=B8=80?= =?UTF-8?q?=E4=B8=8B=E8=AE=BE=E8=AE=A1=E5=99=A8=E5=90=AF=E5=8A=A8=E4=B8=8D?= =?UTF-8?q?=E4=BA=86=E4=B8=94=E6=B2=A1=E6=9C=89=E7=9B=B4=E6=8E=A5=E6=8A=A5?= =?UTF-8?q?=E9=94=99=E7=9A=84=E5=9C=BA=E6=99=AF=E3=80=82=20=EF=BC=88mac=20?= =?UTF-8?q?=E4=B8=8B=E8=BF=98=E6=98=AF=E6=9C=89=E9=97=AE=E9=A2=98=EF=BC=8C?= =?UTF-8?q?=E5=AE=88=E6=8A=A4=E8=BF=9B=E7=A8=8B=E4=B8=8D=E6=94=AF=E6=8C=81?= =?UTF-8?q?=20mac?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/fr/env/detect/EnvDetectorCenter.java | 13 +++- .../main/java/com/fr/exit/DesignerExiter.java | 66 +++++++++++++++++-- .../com/fr/start/DesignerSubListener.java | 6 +- .../com/fr/start/DesignerSuperListener.java | 3 +- 4 files changed, 80 insertions(+), 8 deletions(-) diff --git a/designer-base/src/main/java/com/fr/env/detect/EnvDetectorCenter.java b/designer-base/src/main/java/com/fr/env/detect/EnvDetectorCenter.java index e73aa673b..37e01f788 100644 --- a/designer-base/src/main/java/com/fr/env/detect/EnvDetectorCenter.java +++ b/designer-base/src/main/java/com/fr/env/detect/EnvDetectorCenter.java @@ -54,7 +54,6 @@ public class EnvDetectorCenter { } }; - private final Listener START_UP_COMPLETE_LISTENER = new Listener() { @Override @@ -196,6 +195,18 @@ public class EnvDetectorCenter { .collect(Collectors.toList()); } + /** + * 预期外的终止 + * + * @return 检测结果 + */ + public List terminateUnexpectedly() { + + Stream resultStream = DetectorBridge.getInstance().detect(); + return resultStream + .filter((e) -> e.getStatus() == DetectorStatus.EXCEPTION) + .collect(Collectors.toList()); + } private void listen() { diff --git a/designer-base/src/main/java/com/fr/exit/DesignerExiter.java b/designer-base/src/main/java/com/fr/exit/DesignerExiter.java index 3acb3cf8a..babadc5fe 100644 --- a/designer-base/src/main/java/com/fr/exit/DesignerExiter.java +++ b/designer-base/src/main/java/com/fr/exit/DesignerExiter.java @@ -17,7 +17,9 @@ import com.fr.log.FineLoggerFactory; import com.fr.process.engine.core.FineProcessContext; import com.fr.process.engine.core.FineProcessEngineEvent; +import java.util.ArrayList; import java.util.List; +import java.util.function.Supplier; /** * @author hades @@ -32,16 +34,43 @@ public class DesignerExiter { return INSTANCE; } + /** + * 预期外的退出 + * 首先检测是否有检测到的异常。如果没有,则运行默认行为 + * + * @param defaultAction 默认行为 + */ + public void exitUnexpectedly(Runnable defaultAction) { + + // 尝试进行检测 + List results = runAndGet(() -> EnvDetectorCenter.getInstance().terminateUnexpectedly(), ArrayList::new); + try { + if (!Collections.isEmpty(results)) { + showNewExitDialog(results); + } + } finally { + // 正常的话上面会直接退出, system.exit(0) + // 只有异常,或者不命中,才会走到这里 + defaultAction.run(); + } + } + public void exit(Throwable throwable) { - FineLoggerFactory.getLogger().error(throwable.getMessage(), throwable); + doThrowableAction(() -> { + FineLoggerFactory.getLogger().error(throwable.getMessage(), throwable); + }, () -> { + throwable.printStackTrace(System.err); + }); - StartErrorMessageCollector.getInstance().record(DesignerErrorMessage.UNEXCEPTED_START_FAILED.getId(), - DesignerErrorMessage.UNEXCEPTED_START_FAILED.getMessage(), - throwable.getMessage()); + doThrowableAction(() -> { + StartErrorMessageCollector.getInstance().record(DesignerErrorMessage.UNEXCEPTED_START_FAILED.getId(), + DesignerErrorMessage.UNEXCEPTED_START_FAILED.getMessage(), + throwable.getMessage()); + }); // 尝试进行检测 - List results = EnvDetectorCenter.getInstance().terminate(throwable); + List results = runAndGet(() -> EnvDetectorCenter.getInstance().terminate(throwable), ArrayList::new); if (Collections.isEmpty(results)) { // 为空,则 @@ -89,4 +118,31 @@ public class DesignerExiter { private void beforeExit() { DesignerWorkspaceGenerator.stop(); } + + /* 忽视异常的调用方法 */ + + private void doThrowableAction(Runnable runnable) { + doThrowableAction(runnable, null); + } + + private void doThrowableAction(Runnable runnable, Runnable defaultRunnable) { + + try { + runnable.run(); + } catch (Throwable ignore) { + if (defaultRunnable != null) { + defaultRunnable.run(); + } + } + } + + private T runAndGet(Supplier supplier, Supplier defaultCallable) { + + try { + return supplier.get(); + } catch (Exception ignore) { + return defaultCallable.get(); + } + } + } diff --git a/designer-realize/src/main/java/com/fr/start/DesignerSubListener.java b/designer-realize/src/main/java/com/fr/start/DesignerSubListener.java index c76e6b072..52d364f6c 100644 --- a/designer-realize/src/main/java/com/fr/start/DesignerSubListener.java +++ b/designer-realize/src/main/java/com/fr/start/DesignerSubListener.java @@ -4,6 +4,7 @@ import com.fr.design.mainframe.DesignerContext; import com.fr.event.Event; import com.fr.event.Listener; import com.fr.event.Null; +import com.fr.exit.DesignerExiter; import com.fr.process.engine.core.CarryMessageEvent; import com.fr.process.engine.core.FineProcessContext; import com.fr.process.engine.core.FineProcessEngineEvent; @@ -31,7 +32,10 @@ public class DesignerSubListener { @Override public void on(Event event, Null param) { if (DesignerContext.getDesignerFrame() == null || !DesignerContext.getDesignerFrame().isShowing()) { - FineProcessContext.getParentPipe().fire(new CarryMessageEvent(DesignerProcessType.INSTANCE.obtain())); + + DesignerExiter.getInstance().exitUnexpectedly(() -> { + FineProcessContext.getParentPipe().fire(new CarryMessageEvent(DesignerProcessType.INSTANCE.obtain())); + }); } } }); diff --git a/designer-realize/src/main/java/com/fr/start/DesignerSuperListener.java b/designer-realize/src/main/java/com/fr/start/DesignerSuperListener.java index f5c917464..2f2530e59 100644 --- a/designer-realize/src/main/java/com/fr/start/DesignerSuperListener.java +++ b/designer-realize/src/main/java/com/fr/start/DesignerSuperListener.java @@ -31,7 +31,7 @@ import java.util.concurrent.TimeUnit; public class DesignerSuperListener { private static final DesignerSuperListener INSTANCE = new DesignerSuperListener(); - private static final int ONCE_DELAY = 180; + private static final int ONCE_DELAY = 30; private static final int FIXED_DELAY = 0; private static final int FIXED_FREQ = 2; @@ -95,6 +95,7 @@ public class DesignerSuperListener { public void run() { cancel = true; ProcessEventPipe pipe = process.getPipe(); + // 确认设计器是否启动完成 pipe.fire(FineProcessEngineEvent.READY); if (StringUtils.isNotEmpty(pipe.info())) { frameReport(); From 5398db8af809b7aaaba73621a0c6052cc3efabdd Mon Sep 17 00:00:00 2001 From: Harrison Date: Mon, 13 Jun 2022 17:36:42 +0800 Subject: [PATCH 69/85] =?UTF-8?q?REPORT-73396=E3=80=90=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E7=8E=AF=E5=A2=83=E7=9B=91=E6=B5=8B=E3=80=91=E5=88=A0?= =?UTF-8?q?=E9=99=A4fine-decision-11.0=EF=BC=8C=E8=A7=A6=E5=8F=91=E7=9A=84?= =?UTF-8?q?=E6=98=AF=E9=87=8D=E7=BD=AEfinedb=20=E6=81=A2=E5=A4=8D=E4=B8=80?= =?UTF-8?q?=E4=B8=AA=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/fr/start/DesignerSuperListener.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/designer-realize/src/main/java/com/fr/start/DesignerSuperListener.java b/designer-realize/src/main/java/com/fr/start/DesignerSuperListener.java index 2f2530e59..ee4b26502 100644 --- a/designer-realize/src/main/java/com/fr/start/DesignerSuperListener.java +++ b/designer-realize/src/main/java/com/fr/start/DesignerSuperListener.java @@ -31,7 +31,7 @@ import java.util.concurrent.TimeUnit; public class DesignerSuperListener { private static final DesignerSuperListener INSTANCE = new DesignerSuperListener(); - private static final int ONCE_DELAY = 30; + private static final int ONCE_DELAY = 180; private static final int FIXED_DELAY = 0; private static final int FIXED_FREQ = 2; From 9bbe1311120a18f4fa8664ff792bdc2c744fab58 Mon Sep 17 00:00:00 2001 From: "Link.Zhao" Date: Tue, 14 Jun 2022 11:18:16 +0800 Subject: [PATCH 70/85] =?UTF-8?q?Revert=20"REPORT-70746=20=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E6=97=A5=E5=BF=97=E7=9A=84=E6=98=BE=E7=A4=BA=E9=80=BB?= =?UTF-8?q?=E8=BE=91=E4=BC=98=E5=8C=96"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 5916c1d49e3bb24421745a11699842a39d9551b9. --- .../java/com/fr/design/update/actions/NewFeatureAction.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/designer-base/src/main/java/com/fr/design/update/actions/NewFeatureAction.java b/designer-base/src/main/java/com/fr/design/update/actions/NewFeatureAction.java index cf98501c1..ac7c01cd0 100644 --- a/designer-base/src/main/java/com/fr/design/update/actions/NewFeatureAction.java +++ b/designer-base/src/main/java/com/fr/design/update/actions/NewFeatureAction.java @@ -1,7 +1,6 @@ package com.fr.design.update.actions; -import com.fr.design.utils.BrowseUtils; import com.fr.general.CloudCenter; import com.fr.log.FineLoggerFactory; @@ -22,7 +21,7 @@ public class NewFeatureAction implements ActionListener { @Override public void actionPerformed(ActionEvent e) { try { - BrowseUtils.browser(url); + Desktop.getDesktop().browse(new URI(url)); } catch (Exception ex) { FineLoggerFactory.getLogger().error(ex.getMessage()); } From 37fb54bb84f15d642693a3d10f1ffb4726444f0b Mon Sep 17 00:00:00 2001 From: "Link.Zhao" Date: Tue, 14 Jun 2022 11:18:16 +0800 Subject: [PATCH 71/85] =?UTF-8?q?Revert=20"REPORT-70746=20=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E6=97=A5=E5=BF=97=E7=9A=84=E6=98=BE=E7=A4=BA=E9=80=BB?= =?UTF-8?q?=E8=BE=91=E4=BC=98=E5=8C=96"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 50c19560f2127f96840246e7da17ab43da6fd793. --- .../update/actions/NewFeatureAction.java | 29 ------ .../update/ui/dialog/UpdateMainDialog.java | 94 ++++++++----------- 2 files changed, 40 insertions(+), 83 deletions(-) delete mode 100644 designer-base/src/main/java/com/fr/design/update/actions/NewFeatureAction.java diff --git a/designer-base/src/main/java/com/fr/design/update/actions/NewFeatureAction.java b/designer-base/src/main/java/com/fr/design/update/actions/NewFeatureAction.java deleted file mode 100644 index ac7c01cd0..000000000 --- a/designer-base/src/main/java/com/fr/design/update/actions/NewFeatureAction.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.fr.design.update.actions; - - -import com.fr.general.CloudCenter; -import com.fr.log.FineLoggerFactory; - -import java.awt.Desktop; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.net.URI; - - -/** - * 帮助-更新升级 - * 点击查看新特性 - * */ -public class NewFeatureAction implements ActionListener { - - private String url = CloudCenter.getInstance().acquireConf("fr.latest.update.detail", "https://help.fanruan.com/finereport/doc-view-4699.html"); - - @Override - public void actionPerformed(ActionEvent e) { - try { - Desktop.getDesktop().browse(new URI(url)); - } catch (Exception ex) { - FineLoggerFactory.getLogger().error(ex.getMessage()); - } - } -} diff --git a/designer-base/src/main/java/com/fr/design/update/ui/dialog/UpdateMainDialog.java b/designer-base/src/main/java/com/fr/design/update/ui/dialog/UpdateMainDialog.java index 29ed3a47e..deda005c3 100644 --- a/designer-base/src/main/java/com/fr/design/update/ui/dialog/UpdateMainDialog.java +++ b/designer-base/src/main/java/com/fr/design/update/ui/dialog/UpdateMainDialog.java @@ -9,7 +9,6 @@ import com.fr.design.dialog.FineJOptionPane; import com.fr.design.dialog.UIDialog; import com.fr.design.gui.ibutton.UIButton; import com.fr.design.gui.icontainer.UIScrollPane; -import com.fr.design.gui.ilable.ActionLabel; import com.fr.design.gui.ilable.UILabel; import com.fr.design.gui.itextfield.UITextField; import com.fr.design.i18n.Toolkit; @@ -17,7 +16,6 @@ import com.fr.design.layout.TableLayout; import com.fr.design.layout.TableLayoutHelper; import com.fr.design.mainframe.DesignerContext; import com.fr.design.update.actions.FileProcess; -import com.fr.design.update.actions.NewFeatureAction; import com.fr.design.update.domain.UpdateInfoCachePropertyManager; import com.fr.design.update.utils.UpdateFileUtils; import com.fr.design.update.ui.widget.LoadingLabel; @@ -59,6 +57,7 @@ import java.util.*; import java.util.List; import java.util.concurrent.ExecutionException; +import static com.fr.design.dialog.FineJOptionPane.OPTION_OK_CANCEL; import static java.nio.charset.StandardCharsets.*; import static javax.swing.JOptionPane.QUESTION_MESSAGE; @@ -186,7 +185,7 @@ public class UpdateMainDialog extends UIDialog { double[] rowUpdateContentPaneSize = {TableLayout.PREFERRED}; double[] columnUpdateContentPaneSize = {TableLayout.PREFERRED, TableLayout.FILL, TableLayout.PREFERRED}; double[] rowUpdateSubContentPaneSize = {UPDATE_CONTENT_PANE_ROW_SIZE, TableLayout.PREFERRED, TableLayout.PREFERRED, TableLayout.PREFERRED, UPDATE_CONTENT_PANE_ROW_SIZE}; - double[] columnUpdateSubContentPaneSize = {UPDATE_CONTENT_PANE_COLUMN_SIZE, TableLayout.PREFERRED, TableLayout.PREFERRED}; + double[] columnUpdateSubContentPaneSize = {UPDATE_CONTENT_PANE_COLUMN_SIZE, TableLayout.FILL, TableLayout.PREFERRED}; double[] columnUpdateSubContentPaneLabelSize = {UPDATE_CONTENT_PANE_LABEL_COLUMN_SIZE, TableLayout.PREFERRED}; JPanel jarUpdateContentPane = new JPanel(); @@ -197,8 +196,7 @@ public class UpdateMainDialog extends UIDialog { new Component[]{new UILabel(), new UILabel(), new UILabel()}, new Component[]{new UILabel(), updateVersionReminderPane, new UILabel()}, new Component[]{new UILabel(), initPaneContent(Color.WHITE, rowUpdateContentPaneSize, columnUpdateSubContentPaneLabelSize, new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Updater_JAR_Version")), jarCurrentLabel), new UILabel()}, - new Component[]{new UILabel(), initPaneContent(Color.WHITE, rowUpdateContentPaneSize, columnUpdateSubContentPaneLabelSize, new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Updater_Latest_JAR")), loadingLabel), - getNewFeatureActionLabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Latest_Feature_Detail"))}, + new Component[]{new UILabel(), initPaneContent(Color.WHITE, rowUpdateContentPaneSize, columnUpdateSubContentPaneLabelSize, new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Updater_Latest_JAR")), loadingLabel), new UILabel()}, new Component[]{new UILabel(), new UILabel(), new UILabel()} }, rowUpdateSubContentPaneSize, columnUpdateSubContentPaneSize, LayoutConstants.VGAP_LARGE); jarUpdateContentPane2.setBackground(Color.WHITE); @@ -419,8 +417,8 @@ public class UpdateMainDialog extends UIDialog { if (downloadFileConfig == null) { throw new Exception("network error."); } - HttpGet get = new HttpGet(CloudCenter.getInstance().acquireUrlByKind("updatelog") + "&start=" + lastUpdateCacheTime + "&end=" + getLatestJARTimeStr()); - httpClient = HttpToolbox.getHttpClient(CloudCenter.getInstance().acquireUrlByKind("updatelog") + "&start=" + lastUpdateCacheTime + "&end=" + getLatestJARTimeStr()); + HttpGet get = new HttpGet(CloudCenter.getInstance().acquireUrlByKind("changelog10") + "&start=" + lastUpdateCacheTime + "&end=" + getLatestJARTimeStr()); + httpClient = HttpToolbox.getHttpClient(CloudCenter.getInstance().acquireUrlByKind("changelog10") + "&start=" + lastUpdateCacheTime + "&end=" + getLatestJARTimeStr()); response = httpClient.execute(get); String responseText = CommonIOUtils.inputStream2String(response.getEntity().getContent(),EncodeConstants.ENCODING_UTF_8).trim(); JSONArray array = JSONArray.create(); @@ -610,7 +608,41 @@ public class UpdateMainDialog extends UIDialog { * jar包更新按钮监听器 */ private void addActionListenerForUpdateBtn() { - updateButton.addActionListener(new UpdateAction()); + updateButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + String[] option = {Toolkit.i18nText("Fine-Design_Report_Yes"), Toolkit.i18nText("Fine-Design_Report_No")}; + int a = JOptionPane.showOptionDialog(getParent(), Toolkit.i18nText("Fine-Design_Update_Info_Information"), + Toolkit.i18nText("Fine-Design_Update_Info_Title"),JOptionPane.YES_NO_OPTION, QUESTION_MESSAGE, UIManager.getIcon("OptionPane.warningIcon"), option, 1); + if (a == 0) { + progressBar.setVisible(true); + progressBar.setString(Toolkit.i18nText("Fine-Design_Update_Info_Wait_Message")); + UpdateCallBack callBack = new UpdateProgressCallBack(progressBar); + updateButton.setEnabled(false); + updateLabel.setVisible(false); + RestoreResultDialog.deletePreviousPropertyFile(); + final String installLib = StableUtils.pathJoin(StableUtils.getInstallHome(), ProjectConstants.LOGS_NAME, UpdateConstants.INSTALL_LIB); + final JFrame frame = DesignerContext.getDesignerFrame(); + final RestartHelper helper = new RestartHelper(); + FineProcessContext.getParentPipe().fire(FineProcessEngineEvent.DESTROY); + new FileProcess(callBack) { + @Override + public void onDownloadSuccess() { + progressBar.setVisible(false); + deleteForDesignerUpdate(installLib); + helper.restartForUpdate(frame); + } + @Override + public void onDownloadFailed() { + progressBar.setVisible(false); + deleteForDesignerUpdate(installLib); + FineJOptionPane.showMessageDialog(getParent(), Toolkit.i18nText("Fine-Design_Update_Info_Failed_Message")); + helper.restartForUpdate(frame); + } + }.execute(); + } + } + }); } private void deleteForDesignerUpdate(String installLib) { @@ -656,13 +688,6 @@ public class UpdateMainDialog extends UIDialog { return false; } - - private ActionLabel getNewFeatureActionLabel(final String text){ - ActionLabel actionLabel = new ActionLabel(text); - actionLabel.addActionListener(new NewFeatureAction()); - return actionLabel; - } - /** * 显示窗口 */ @@ -680,43 +705,4 @@ public class UpdateMainDialog extends UIDialog { @Override public void checkValid() throws Exception { } - - - - private class UpdateAction implements ActionListener { - @Override - public void actionPerformed(ActionEvent e) { - String[] option = {Toolkit.i18nText("Fine-Design_Report_Yes"), Toolkit.i18nText("Fine-Design_Report_No")}; - int a = JOptionPane.showOptionDialog(getParent(), Toolkit.i18nText("Fine-Design_Update_Info_Information"), - Toolkit.i18nText("Fine-Design_Update_Info_Title"),JOptionPane.YES_NO_OPTION, QUESTION_MESSAGE, UIManager.getIcon("OptionPane.warningIcon"), option, 1); - if (a == 0) { - progressBar.setVisible(true); - progressBar.setString(Toolkit.i18nText("Fine-Design_Update_Info_Wait_Message")); - UpdateCallBack callBack = new UpdateProgressCallBack(progressBar); - updateButton.setEnabled(false); - updateLabel.setVisible(false); - RestoreResultDialog.deletePreviousPropertyFile(); - final String installLib = StableUtils.pathJoin(StableUtils.getInstallHome(), ProjectConstants.LOGS_NAME, UpdateConstants.INSTALL_LIB); - final JFrame frame = DesignerContext.getDesignerFrame(); - final RestartHelper helper = new RestartHelper(); - FineProcessContext.getParentPipe().fire(FineProcessEngineEvent.DESTROY); - new FileProcess(callBack) { - @Override - public void onDownloadSuccess() { - progressBar.setVisible(false); - deleteForDesignerUpdate(installLib); - helper.restartForUpdate(frame); - } - @Override - public void onDownloadFailed() { - progressBar.setVisible(false); - deleteForDesignerUpdate(installLib); - FineJOptionPane.showMessageDialog(getParent(), Toolkit.i18nText("Fine-Design_Update_Info_Failed_Message")); - helper.restartForUpdate(frame); - } - }.execute(); - } - - } - } } \ No newline at end of file From c29935fbf82c47d30dffc81fda9443f2a7d49de4 Mon Sep 17 00:00:00 2001 From: Harrison Date: Tue, 14 Jun 2022 13:36:53 +0800 Subject: [PATCH 72/85] =?UTF-8?q?REPORT-73262=E3=80=90=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E7=8E=AF=E5=A2=83=E7=9B=91=E6=B5=8B=E3=80=91=E5=8F=B3?= =?UTF-8?q?=E4=B8=8B=E8=A7=92=E5=BC=B9=E7=AA=97=E6=96=87=E5=AD=97=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E4=B8=8D=E5=85=A8=20=E5=8F=B3=E8=BE=B9=E7=9A=84?= =?UTF-8?q?=E5=A4=A7=E5=B0=8F=20Border=20=E5=A4=AA=E5=A4=A7=E4=BA=86?= =?UTF-8?q?=EF=BC=8C=20=E6=9B=B4=E6=8D=A2=E4=B8=80=E4=B8=8B=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fr/design/components/notification/NotificationDialog.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java b/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java index a4935b30c..d9a1cf0c2 100644 --- a/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java +++ b/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java @@ -162,7 +162,7 @@ public class NotificationDialog extends JDialog { contentPanel.add(iconPanel, BorderLayout.WEST); JPanel centerPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); - centerPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 20)); + centerPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 5)); NotificationMessage[] messages = model.getMessages(); List messageComponents = Arrays.stream(messages) From fd35564b2155aabab1d6130e3bed96950ff34b0b Mon Sep 17 00:00:00 2001 From: Harrison Date: Tue, 14 Jun 2022 13:38:08 +0800 Subject: [PATCH 73/85] =?UTF-8?q?REPORT-73557=E3=80=90=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E7=8E=AF=E5=A2=83=E7=9B=91=E6=B5=8B=E3=80=91=E9=A2=84?= =?UTF-8?q?=E6=9C=9F=E5=A4=96=E7=9A=84=E5=90=AF=E5=8A=A8=E5=BC=B9=E7=AA=97?= =?UTF-8?q?=E4=BA=A4=E4=BA=92=E4=B8=8E=E8=AE=BE=E8=AE=A1=E7=A8=BF=E4=B8=8D?= =?UTF-8?q?=E4=B8=80=E8=87=B4=201-=E6=B7=BB=E5=8A=A0=E5=9B=BE=E6=A0=87=202?= =?UTF-8?q?-=E5=A4=84=E7=90=86=20html=20=E7=9A=84=20style,=20=E4=BD=BF?= =?UTF-8?q?=E8=83=8C=E6=99=AF=E4=BF=9D=E6=8C=81=E4=B8=80=E8=87=B4=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/fr/design/utils/LinkStrUtils.java | 8 ++++ .../com/fr/env/detect/base/DetectorUtil.java | 5 +-- .../fr/env/detect/ui/DetectorErrorDialog.java | 41 ++++++++++++++----- .../reminder/reminder_warning_window.svg | 9 ++++ 4 files changed, 49 insertions(+), 14 deletions(-) create mode 100644 designer-base/src/main/resources/com/fr/design/standard/reminder/reminder_warning_window.svg diff --git a/designer-base/src/main/java/com/fr/design/utils/LinkStrUtils.java b/designer-base/src/main/java/com/fr/design/utils/LinkStrUtils.java index d02c9abff..b7f8217b4 100644 --- a/designer-base/src/main/java/com/fr/design/utils/LinkStrUtils.java +++ b/designer-base/src/main/java/com/fr/design/utils/LinkStrUtils.java @@ -3,6 +3,7 @@ package com.fr.design.utils; import com.fr.design.gui.ilable.UILabel; import com.fr.stable.StringUtils; +import javax.swing.JComponent; import java.awt.Color; import java.awt.Font; @@ -13,6 +14,13 @@ public class LinkStrUtils { public static final UILabel LABEL = new UILabel(); + public static UILabel generateLabel(String html, JComponent templateLabel) { + + String style = generateStyle(templateLabel.getBackground(), templateLabel.getFont(), templateLabel.getForeground()); + String fullHtml = generateHtmlTag(style, html); + return new UILabel(fullHtml); + } + public static String generateHtmlTag(String html) { String defaultStyle = generateDefaultStyle(); diff --git a/designer-base/src/main/java/com/fr/env/detect/base/DetectorUtil.java b/designer-base/src/main/java/com/fr/env/detect/base/DetectorUtil.java index fd6c33607..b9253ed4c 100644 --- a/designer-base/src/main/java/com/fr/env/detect/base/DetectorUtil.java +++ b/designer-base/src/main/java/com/fr/env/detect/base/DetectorUtil.java @@ -7,7 +7,6 @@ import com.fr.design.components.notification.NotificationMessage; import com.fr.design.components.notification.NotificationModel; import com.fr.design.components.notification.NotificationType; import com.fr.design.dialog.link.MessageWithLink; -import com.fr.design.gui.ilable.UILabel; import com.fr.design.utils.LinkStrUtils; import com.fr.env.detect.bean.DetectorResult; import com.fr.env.detect.bean.ExceptionSolution; @@ -121,7 +120,7 @@ public class DetectorUtil { * @param message 信息 * @return 组件 */ - public static JComponent convert2TextComponent(@NotNull Message message) { + public static JComponent convert2TextComponent(@NotNull Message message, JComponent template) { if (message.getType() == Message.Type.LINK) { Message.Link linkMsg = (Message.Link) message; @@ -129,7 +128,7 @@ public class DetectorUtil { Desktop.getDesktop().browse(URI.create(linkMsg.getLink())); })); } - return new UILabel(LinkStrUtils.generateHtmlTag(message.get())); + return LinkStrUtils.generateLabel(message.get(), template); } /** diff --git a/designer-base/src/main/java/com/fr/env/detect/ui/DetectorErrorDialog.java b/designer-base/src/main/java/com/fr/env/detect/ui/DetectorErrorDialog.java index 9b85c61af..43a2f00b9 100644 --- a/designer-base/src/main/java/com/fr/env/detect/ui/DetectorErrorDialog.java +++ b/designer-base/src/main/java/com/fr/env/detect/ui/DetectorErrorDialog.java @@ -1,5 +1,6 @@ package com.fr.env.detect.ui; +import com.fr.base.svg.IconUtils; import com.fr.design.RestartHelper; import com.fr.design.gui.ibutton.UIButton; import com.fr.design.gui.ilable.UILabel; @@ -47,36 +48,54 @@ public class DetectorErrorDialog extends JDialog implements ActionListener { super(parent, true); JPanel northPane = FRGUIPaneFactory.createBorderLayout_L_Pane(); + JPanel headerPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); + headerPane.setLayout(new BorderLayout(15, 0)); + + UILabel iconLabel = new UILabel(IconUtils.readIcon("/com/fr/design/standard/reminder/reminder_warning_window.svg")); + headerPane.add(iconLabel, BorderLayout.WEST); + JPanel messagePane = FRGUIPaneFactory.createVerticalFlowLayout_S_Pane(true); + { + UILabel boldHeader = new UILabel(Toolkit.i18nText("Fine-Design_Error_Start_Apology_Message")); + Font font = FRFont.getInstance(boldHeader.getFont().getFontName(), Font.PLAIN, 16); + boldHeader.setFont(font); + messagePane.add(boldHeader); + + UILabel description = new UILabel(Toolkit.i18nText("Fine-Design_Send_Report_To_Us")); + messagePane.add(description); + } + headerPane.add(messagePane, BorderLayout.CENTER); - UILabel boldHeader = new UILabel(Toolkit.i18nText("Fine-Design_Error_Start_Apology_Message")); - Font font = FRFont.getInstance(boldHeader.getFont().getFontName(), Font.BOLD, 20); - boldHeader.setFont(font); - messagePane.add(boldHeader); - - UILabel description = new UILabel(Toolkit.i18nText("Fine-Design_Send_Report_To_Us")); - messagePane.add(description); - northPane.add(messagePane); + northPane.add(headerPane); JPanel centerPane = FRGUIPaneFactory.createBorderLayout_L_Pane(); + centerPane.setLayout(new BorderLayout(0, 5)); UILabel detailDesc = new UILabel(Toolkit.i18nText("Fine-Design_Problem_Detail_Message")); centerPane.add(detailDesc, BorderLayout.NORTH); JPanel detailPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); detailPanel.setBorder(BorderFactory.createEmptyBorder(0, 20, 10, 10)); + detailPanel.setLayout(new BorderLayout(0, 8)); for (DetectorResult result : results) { + JPanel detailItemPanel = FRGUIPaneFactory.createBorderLayout_L_Pane(); + detailItemPanel.setLayout(new BorderLayout(0, 8)); ExceptionTips tips = result.getTips(); + + UILabel template = new UILabel(); + template.setBackground(Color.white); + if (tips != null) { Message tipsMsg = tips.getMessage(); - detailPanel.add(DetectorUtil.convert2TextComponent(tipsMsg), BorderLayout.NORTH); + detailItemPanel.add(DetectorUtil.convert2TextComponent(tipsMsg, template), BorderLayout.NORTH); } ExceptionSolution solution = result.getSolution(); if (solution != null) { Message solutionMsg = solution.getMessage(); - detailPanel.add(DetectorUtil.convert2TextComponent(solutionMsg), BorderLayout.CENTER); + detailItemPanel.add(DetectorUtil.convert2TextComponent(solutionMsg, template), BorderLayout.CENTER); } + detailPanel.add(detailItemPanel, BorderLayout.CENTER); } JScrollPane detailPanelWrapper = new JScrollPane(detailPanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); @@ -110,7 +129,7 @@ public class DetectorErrorDialog extends JDialog implements ActionListener { this.add(northPane, BorderLayout.NORTH); this.add(centerPane, BorderLayout.CENTER); this.add(southPane, BorderLayout.SOUTH); - this.setSize(new Dimension(600, 500)); + this.setSize(new Dimension(650, 500)); GUICoreUtils.centerWindow(this); } diff --git a/designer-base/src/main/resources/com/fr/design/standard/reminder/reminder_warning_window.svg b/designer-base/src/main/resources/com/fr/design/standard/reminder/reminder_warning_window.svg new file mode 100644 index 000000000..66dde5923 --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/reminder/reminder_warning_window.svg @@ -0,0 +1,9 @@ + + + + + + + + + From d828819b5fc926823ed9b9a1d0d67a4e8e2970cd Mon Sep 17 00:00:00 2001 From: Harrison Date: Tue, 14 Jun 2022 14:49:31 +0800 Subject: [PATCH 74/85] =?UTF-8?q?REPORT-73283=E3=80=90=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E7=8E=AF=E5=A2=83=E7=9B=91=E6=B5=8B=E3=80=91=E4=B8=AA?= =?UTF-8?q?=E5=88=ABjar=E5=BC=82=E5=B8=B8=E6=97=B6=EF=BC=8C=E4=B8=8D?= =?UTF-8?q?=E4=BC=9A=E8=A7=A6=E5=8F=91=E8=87=AA=E5=8A=A8=E6=A3=80=E6=B5=8B?= =?UTF-8?q?=20=E6=94=B9=E4=B8=80=E4=B8=AA=E5=9B=BD=E9=99=85=E5=8C=96?= =?UTF-8?q?=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/fr/env/detect/impl/JarInconsistentDetector.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/JarInconsistentDetector.java b/designer-base/src/main/java/com/fr/env/detect/impl/JarInconsistentDetector.java index 8cfc988a9..2a744046a 100644 --- a/designer-base/src/main/java/com/fr/env/detect/impl/JarInconsistentDetector.java +++ b/designer-base/src/main/java/com/fr/env/detect/impl/JarInconsistentDetector.java @@ -136,7 +136,7 @@ public class JarInconsistentDetector extends AbstractExceptionDetector { return DetectorResult.exception(type(), new ExceptionTips(new Message.Simple(tipsMessage)), new ExceptionSolution(new Message.Link(Toolkit.i18nText(type().getSolutionLocale()), DetectorConstants.JAR_HELP_LINK), null), - ExceptionLog.create(type().getLogLocale() + message)); + ExceptionLog.create(Toolkit.i18nText(type().getLogLocale()) + message)); } private Map groupBy(List localInfos) { From 866989a42deb32177e2a35154822482dfa5d47db Mon Sep 17 00:00:00 2001 From: pengda Date: Tue, 14 Jun 2022 15:10:21 +0800 Subject: [PATCH 75/85] =?UTF-8?q?REPORT-71908=20=E7=BB=84=E5=90=88?= =?UTF-8?q?=E5=9B=BE=E9=85=8D=E7=BD=AE=E6=9D=A1=E4=BB=B6=E5=B1=9E=E6=80=A7?= =?UTF-8?q?=E6=B1=87=E6=80=BB=E5=AD=97=E6=AE=B5=E5=80=BC=EF=BC=8C=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E5=90=8E=E6=9D=A1=E4=BB=B6=E5=B1=9E=E6=80=A7=E5=86=85?= =?UTF-8?q?=E5=AE=B9=E8=A2=AB=E6=B8=85=E7=A9=BA=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../other/VanChartCustomPlotConditionAttrTabPane.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/designer-chart/src/main/java/com/fr/van/chart/custom/other/VanChartCustomPlotConditionAttrTabPane.java b/designer-chart/src/main/java/com/fr/van/chart/custom/other/VanChartCustomPlotConditionAttrTabPane.java index 8c2056857..c9b45162f 100644 --- a/designer-chart/src/main/java/com/fr/van/chart/custom/other/VanChartCustomPlotConditionAttrTabPane.java +++ b/designer-chart/src/main/java/com/fr/van/chart/custom/other/VanChartCustomPlotConditionAttrTabPane.java @@ -92,7 +92,11 @@ public class VanChartCustomPlotConditionAttrTabPane extends VanChartCustomPlotTa CustomPlotType plotType = CustomPlotFactory.getCustomType(chartPlot); VanChartRichEditorPane.refreshCustomChartTableFieldNames(chart, plotType); - ColSelectedWithSummaryMethodEditor.refreshCustomChartTableFieldNames(chart,plotType); + ColSelectedWithSummaryMethodEditor.refreshCustomChartTableFieldNames(chart, plotType); + JPanel selectedPane = paneList.get(index); + if (chart != null && chartPlot != null && selectedPane instanceof VanChartConditionAttrPane) { + ((VanChartConditionAttrPane) selectedPane).populateBean(chartPlot); + } } } From c469663ed61f693e3171c08f11f8359f73a531e0 Mon Sep 17 00:00:00 2001 From: hades Date: Tue, 14 Jun 2022 16:03:28 +0800 Subject: [PATCH 76/85] =?UTF-8?q?REPORT-71476=20=E6=96=B0=E5=BB=BA?= =?UTF-8?q?=E4=BF=9D=E5=AD=98=E5=90=8E=EF=BC=8C=E7=9B=AE=E5=BD=95=E6=B2=A1?= =?UTF-8?q?=E6=9C=89=E9=80=89=E4=B8=AD=E6=A8=A1=E6=9D=BF=EF=BC=8C=E9=87=8D?= =?UTF-8?q?=E5=91=BD=E5=90=8D=E4=B8=8D=E5=BA=94=E8=AF=A5=E9=AB=98=E4=BA=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/fr/design/mainframe/JTemplate.java | 1 + 1 file changed, 1 insertion(+) diff --git a/designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java b/designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java index c086ddac9..a7e89c434 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java @@ -1769,6 +1769,7 @@ public abstract class JTemplate> refreshToolArea(); } DesignerFrameFileDealerPane.getInstance().refresh(); + DesignerFrameFileDealerPane.getInstance().stateChange(); } }); return worker; From 0f8406f3c4442ec7b11f352163f5eefe4a0a9731 Mon Sep 17 00:00:00 2001 From: Harrison Date: Tue, 14 Jun 2022 16:13:03 +0800 Subject: [PATCH 77/85] =?UTF-8?q?REPORT-73557=E3=80=90=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E7=8E=AF=E5=A2=83=E7=9B=91=E6=B5=8B=E3=80=91=E9=A2=84?= =?UTF-8?q?=E6=9C=9F=E5=A4=96=E7=9A=84=E5=90=AF=E5=8A=A8=E5=BC=B9=E7=AA=97?= =?UTF-8?q?=E4=BA=A4=E4=BA=92=E4=B8=8E=E8=AE=BE=E8=AE=A1=E7=A8=BF=E4=B8=8D?= =?UTF-8?q?=E4=B8=80=E8=87=B4=201=E3=80=81=E4=BF=AE=E6=94=B9=E4=B8=80?= =?UTF-8?q?=E4=B8=8B=E5=AD=97=E4=BD=93=E7=9A=84=E5=AE=9E=E7=8E=B0=EF=BC=8C?= =?UTF-8?q?=E4=B8=8D=E7=84=B6=E5=92=8C=E5=85=B6=E4=BB=96=E7=9A=84=E5=AD=97?= =?UTF-8?q?=E4=BD=93=E4=B8=8D=E7=BB=9F=E4=B8=80=E3=80=82=202=E3=80=81?= =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=E4=B8=80=E4=B8=8B=20UI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/fr/env/detect/ui/DetectorErrorDialog.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/designer-base/src/main/java/com/fr/env/detect/ui/DetectorErrorDialog.java b/designer-base/src/main/java/com/fr/env/detect/ui/DetectorErrorDialog.java index 43a2f00b9..e636524c8 100644 --- a/designer-base/src/main/java/com/fr/env/detect/ui/DetectorErrorDialog.java +++ b/designer-base/src/main/java/com/fr/env/detect/ui/DetectorErrorDialog.java @@ -7,6 +7,7 @@ import com.fr.design.gui.ilable.UILabel; import com.fr.design.i18n.Toolkit; import com.fr.design.layout.FRGUIPaneFactory; import com.fr.design.utils.ColorUtils; +import com.fr.design.utils.DesignUtils; import com.fr.design.utils.gui.GUICoreUtils; import com.fr.env.detect.base.DetectorUtil; import com.fr.env.detect.bean.DetectorResult; @@ -14,7 +15,6 @@ import com.fr.env.detect.bean.ExceptionSolution; import com.fr.env.detect.bean.ExceptionTips; import com.fr.env.detect.bean.Message; import com.fr.exit.DesignerExiter; -import com.fr.general.FRFont; import javax.swing.BorderFactory; import javax.swing.JDialog; @@ -39,13 +39,17 @@ import java.util.List; * created by Harrison on 2022/05/29 **/ public class DetectorErrorDialog extends JDialog implements ActionListener { - + + public static final float FONT_BOLD_HEIGHT = 16f; private UIButton okButton; private UIButton restartButton; public DetectorErrorDialog(Frame parent, List results) { super(parent, true); + + // 可能是还没初始化 UI 的时候出现的问题,初始化一下 UI + DesignUtils.initLookAndFeel(); JPanel northPane = FRGUIPaneFactory.createBorderLayout_L_Pane(); JPanel headerPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); @@ -57,8 +61,9 @@ public class DetectorErrorDialog extends JDialog implements ActionListener { JPanel messagePane = FRGUIPaneFactory.createVerticalFlowLayout_S_Pane(true); { UILabel boldHeader = new UILabel(Toolkit.i18nText("Fine-Design_Error_Start_Apology_Message")); - Font font = FRFont.getInstance(boldHeader.getFont().getFontName(), Font.PLAIN, 16); - boldHeader.setFont(font); + Font font = boldHeader.getFont(); + Font boldFont = font.deriveFont(FONT_BOLD_HEIGHT); + boldHeader.setFont(boldFont); messagePane.add(boldHeader); UILabel description = new UILabel(Toolkit.i18nText("Fine-Design_Send_Report_To_Us")); From 652f8ac9a5aaf174fa7591bfe28a7dd77131751a Mon Sep 17 00:00:00 2001 From: hades Date: Tue, 14 Jun 2022 16:33:19 +0800 Subject: [PATCH 78/85] =?UTF-8?q?REPORT-73532=20F0017=E5=9F=8B=E7=82=B9?= =?UTF-8?q?=E6=96=B0=E5=A2=9Econtent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../alphafine/component/ProductNewsList.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/ProductNewsList.java b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/ProductNewsList.java index 9e4d1c402..c4614a29e 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/ProductNewsList.java +++ b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/ProductNewsList.java @@ -4,6 +4,7 @@ import com.fr.design.DesignerEnvManager; import com.fr.design.mainframe.alphafine.AlphaFineConstants; import com.fr.design.mainframe.alphafine.AlphaFineHelper; import com.fr.design.mainframe.alphafine.model.ProductNews; +import com.fr.design.utils.BrowseUtils; import com.fr.log.FineLoggerFactory; import java.awt.Cursor; @@ -63,15 +64,15 @@ public class ProductNewsList extends JList { private void dealWithClick() { ProductNews productNews = getSelectedValue(); - try { - Desktop.getDesktop().browse(new URI(productNews.getUrl())); - } catch (Exception e) { - FineLoggerFactory.getLogger().error(e.getMessage(), e); - } + jumpToUrl(productNews.getUrl()); DesignerEnvManager.getEnvManager().getAlphaFineConfigManager().getReadSet().add(productNews.getId()); AlphaFineHelper.getAlphaFineDialog().repaint(); } + private void jumpToUrl(String url) { + BrowseUtils.browser(url); + } + public int getHoverIndex() { return hoverIndex; } From 2d322f3ad24d3f1bc7bb04685232f0a15af0684e Mon Sep 17 00:00:00 2001 From: Harrison Date: Tue, 14 Jun 2022 16:58:40 +0800 Subject: [PATCH 79/85] =?UTF-8?q?REPORT-73586=E3=80=90=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E7=8E=AF=E5=A2=83=E7=9B=91=E6=B5=8B=E3=80=91=E8=BF=9C?= =?UTF-8?q?=E7=A8=8B=E8=BF=9E=E6=8E=A5=E5=A4=B1=E8=B4=A5=E5=90=8E=EF=BC=8C?= =?UTF-8?q?=E8=A7=A6=E5=8F=91=E6=89=8B=E5=8A=A8=E6=A3=80=E6=B5=8B=EF=BC=8C?= =?UTF-8?q?=E6=A3=80=E6=B5=8B=E5=A4=B1=E8=B4=A5=20=E5=92=8C=E4=BA=A7?= =?UTF-8?q?=E5=93=81=E6=B2=9F=E9=80=9A=EF=BC=8C=E6=B7=BB=E5=8A=A0=E4=BA=A4?= =?UTF-8?q?=E4=BA=92=E5=AD=98=E5=9C=A8=E6=A3=80=E6=B5=8B=E8=BF=87=E7=A8=8B?= =?UTF-8?q?=E7=9A=84=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fr/env/detect/base/DetectorBridge.java | 9 +- .../fr/env/detect/bean/DetectorResult.java | 6 ++ .../fr/env/detect/bean/DetectorStatus.java | 5 ++ .../detect/thowable/ThrowableLogAppender.java | 2 + .../fr/env/detect/ui/EnvDetectorDialog.java | 82 +++++++++++-------- 5 files changed, 67 insertions(+), 37 deletions(-) diff --git a/designer-base/src/main/java/com/fr/env/detect/base/DetectorBridge.java b/designer-base/src/main/java/com/fr/env/detect/base/DetectorBridge.java index 50bca7fd1..765ad1eda 100644 --- a/designer-base/src/main/java/com/fr/env/detect/base/DetectorBridge.java +++ b/designer-base/src/main/java/com/fr/env/detect/base/DetectorBridge.java @@ -10,6 +10,7 @@ import com.fr.env.detect.impl.JarInconsistentDetector; import com.fr.env.detect.impl.JarLackDetector; import com.fr.env.detect.thowable.ThrowableLogAppender; import com.fr.env.detect.thowable.ThrowableStore; +import com.fr.log.FineLoggerFactory; import com.fr.value.NotNullLazyValue; import org.jetbrains.annotations.NotNull; @@ -86,7 +87,13 @@ public class DetectorBridge { @NotNull public DetectorResult detect(DetectorType type) { - return detectorManager.getValue().detect(type); + try { + return detectorManager.getValue().detect(type); + } catch (Exception e) { + FineLoggerFactory.getLogger().error("detect failed", e); + // 返回成功的信息, 别影响前端的展示。 + return DetectorResult.unknown(type); + } } /** diff --git a/designer-base/src/main/java/com/fr/env/detect/bean/DetectorResult.java b/designer-base/src/main/java/com/fr/env/detect/bean/DetectorResult.java index 2b45ecbc1..aa9a007bb 100644 --- a/designer-base/src/main/java/com/fr/env/detect/bean/DetectorResult.java +++ b/designer-base/src/main/java/com/fr/env/detect/bean/DetectorResult.java @@ -31,6 +31,12 @@ public class DetectorResult { this.log = log; } + public static DetectorResult unknown(DetectorType type) { + DetectorResult result = new DetectorResult(type); + result.status = DetectorStatus.UNKNOWN; + return result; + } + public static DetectorResult normal(DetectorType type) { DetectorResult result = new DetectorResult(type); diff --git a/designer-base/src/main/java/com/fr/env/detect/bean/DetectorStatus.java b/designer-base/src/main/java/com/fr/env/detect/bean/DetectorStatus.java index 6a12015e9..b7e5df818 100644 --- a/designer-base/src/main/java/com/fr/env/detect/bean/DetectorStatus.java +++ b/designer-base/src/main/java/com/fr/env/detect/bean/DetectorStatus.java @@ -14,4 +14,9 @@ public enum DetectorStatus { * 异常 */ EXCEPTION, + + /** + * 未知 + */ + UNKNOWN, } diff --git a/designer-base/src/main/java/com/fr/env/detect/thowable/ThrowableLogAppender.java b/designer-base/src/main/java/com/fr/env/detect/thowable/ThrowableLogAppender.java index bcb4736e1..3348052f4 100644 --- a/designer-base/src/main/java/com/fr/env/detect/thowable/ThrowableLogAppender.java +++ b/designer-base/src/main/java/com/fr/env/detect/thowable/ThrowableLogAppender.java @@ -60,6 +60,8 @@ public class ThrowableLogAppender extends AbstractAppender { public void enable() { + // 初始化一下,别出问题 + logHandler.getHandler().start(); FRLogger.getLogger().addExtendLogAppender(logHandler); } diff --git a/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java index 25689ad20..ed03d78cc 100644 --- a/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java +++ b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java @@ -21,6 +21,7 @@ import com.fr.env.detect.base.EnvDetectorConfig; import com.fr.env.detect.bean.DetectorResult; import com.fr.env.detect.bean.DetectorStatus; import com.fr.env.detect.bean.DetectorType; +import com.fr.log.FineLoggerFactory; import org.jetbrains.annotations.NotNull; import javax.swing.BorderFactory; @@ -55,6 +56,7 @@ public class EnvDetectorDialog extends JDialog { private static final int TIMEOUT = 1000; private static final Color SUCCESS_COLOR = new Color(22, 193, 83); + private static final Color DETAIL_FONT_COLOR = new Color(65, 155, 249); private final JPanel body; @@ -177,7 +179,7 @@ public class EnvDetectorDialog extends JDialog { } // 执行前 buttonStatus = buttonStatus.next(); - UIUtil.invokeLaterIfNeeded(EnvDetectorDialog.this::refreshHeaderPanel); + UIUtil.invokeLaterIfNeeded(EnvDetectorDialog.this::refreshHeader); detectWorker = new SwingWorker() { @Override @@ -192,7 +194,7 @@ public class EnvDetectorDialog extends JDialog { } // 刷新一下面板-开始执行啦 - UIUtil.invokeLaterIfNeeded(EnvDetectorDialog.this::refresh); + UIUtil.invokeLaterIfNeeded(EnvDetectorDialog.this::refreshBody); EnvDetectorItem item = items.get(i); DetectorType type = item.getType(); @@ -208,7 +210,7 @@ public class EnvDetectorDialog extends JDialog { // 只有还在运行中,才会真正的刷新面板 if (buttonStatus.isExecuting()) { // 在刷新一下面板 - UIUtil.invokeLaterIfNeeded(EnvDetectorDialog.this::refresh); + UIUtil.invokeLaterIfNeeded(EnvDetectorDialog.this::refreshBody); currentDetectIndex++; } @@ -218,11 +220,16 @@ public class EnvDetectorDialog extends JDialog { @Override protected void done() { - - if (buttonStatus.isExecuting()) { - // 执行结束 - buttonStatus = EnvDetectorButtonStatus.A_NEW; - UIUtil.invokeLaterIfNeeded(EnvDetectorDialog.this::refreshHeaderPanel); + + try { + this.get(); + if (buttonStatus.isExecuting()) { + // 执行结束 + buttonStatus = EnvDetectorButtonStatus.A_NEW; + UIUtil.invokeLaterIfNeeded(EnvDetectorDialog.this::refreshHeader); + } + } catch (Exception e) { + FineLoggerFactory.getLogger().error("detect failed", e); } } }; @@ -236,7 +243,7 @@ public class EnvDetectorDialog extends JDialog { e.setResult(null); } // 刷新一下面板-开始执行啦 - UIUtil.invokeLaterIfNeeded(EnvDetectorDialog.this::refresh); + UIUtil.invokeLaterIfNeeded(EnvDetectorDialog.this::refreshBody); } private void stopDetecting(UIButton detectButton) { @@ -251,7 +258,7 @@ public class EnvDetectorDialog extends JDialog { // 刷新按钮 detectButton.setText(buttonStatus.getDesc()); // 刷新面板 - refresh(); + refreshBody(); }); } @@ -265,7 +272,7 @@ public class EnvDetectorDialog extends JDialog { this.resultSummaryPane.setLayout(new BorderLayout(5, 0)); Boolean success = model.getResults() .map((e) -> { - if (e.getStatus() == DetectorStatus.NORMAL) { + if (e != null && e.getStatus() == DetectorStatus.NORMAL) { return Boolean.TRUE; } return Boolean.FALSE; @@ -360,31 +367,34 @@ public class EnvDetectorDialog extends JDialog { infoPanel.add(new UILabel(Toolkit.i18nText("Fine-Design_Basic_Exception")), BorderLayout.CENTER); } statusPanel.add(infoPanel, BorderLayout.WEST); - - JPanel detailPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); - { - detailPanel.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0)); - UILabel detailLabel = new UILabel(Toolkit.i18nText("Fine_Designer_Look_Detail")); - detailLabel.setForeground(new Color(65, 155, 249)); - detailLabel.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent mouseEvent) { - NotificationDialogProperties properties = new NotificationDialogProperties((Frame) EnvDetectorDialog.this.getOwner(), Toolkit.i18nText("Fine-Design_Basic_Detect_Notification_Title")); - Stream results = model.getResults(); - List notificationModels = results - .filter(Objects::nonNull) - .filter((e) -> e.getStatus() == DetectorStatus.EXCEPTION) - .map(DetectorUtil::convert2Notification) - .collect(Collectors.toList()); - - NotificationDialog dialog = new NotificationDialog(properties, notificationModels); - dialog.open(); - } - }); - detailPanel.add(detailLabel, BorderLayout.CENTER); + + // 如果结果是检测出的异常,则出现详细信息。 + if (result.getStatus() == DetectorStatus.EXCEPTION) { + JPanel detailPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + { + detailPanel.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0)); + UILabel detailLabel = new UILabel(Toolkit.i18nText("Fine_Designer_Look_Detail")); + detailLabel.setForeground(DETAIL_FONT_COLOR); + detailLabel.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent mouseEvent) { + NotificationDialogProperties properties = new NotificationDialogProperties((Frame) EnvDetectorDialog.this.getOwner(), Toolkit.i18nText("Fine-Design_Basic_Detect_Notification_Title")); + Stream results = model.getResults(); + List notificationModels = results + .filter(Objects::nonNull) + .filter((e) -> e.getStatus() == DetectorStatus.EXCEPTION) + .map(DetectorUtil::convert2Notification) + .collect(Collectors.toList()); + + NotificationDialog dialog = new NotificationDialog(properties, notificationModels); + dialog.open(); + } + }); + detailPanel.add(detailLabel, BorderLayout.CENTER); + } + statusPanel.add(detailPanel, BorderLayout.CENTER); } - statusPanel.add(detailPanel, BorderLayout.CENTER); } return statusPanel; } @@ -434,14 +444,14 @@ public class EnvDetectorDialog extends JDialog { return tailPanel; } - private void refreshHeaderPanel() { + private void refreshHeader() { updateHeaderPanel(); pack(); repaint(); } - private void refresh() { + private void refreshBody() { updateTable(this.tablePanel); pack(); From 750e5ea5435f03d92996d93d298e0d8eea65c8ce Mon Sep 17 00:00:00 2001 From: Harrison Date: Tue, 14 Jun 2022 17:21:33 +0800 Subject: [PATCH 80/85] =?UTF-8?q?REPORT-73586=E3=80=90=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E7=8E=AF=E5=A2=83=E7=9B=91=E6=B5=8B=E3=80=91=E8=BF=9C?= =?UTF-8?q?=E7=A8=8B=E8=BF=9E=E6=8E=A5=E5=A4=B1=E8=B4=A5=E5=90=8E=EF=BC=8C?= =?UTF-8?q?=E8=A7=A6=E5=8F=91=E6=89=8B=E5=8A=A8=E6=A3=80=E6=B5=8B=EF=BC=8C?= =?UTF-8?q?=E6=A3=80=E6=B5=8B=E5=A4=B1=E8=B4=A5=20=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/fr/env/detect/base/DetectorBridge.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/designer-base/src/main/java/com/fr/env/detect/base/DetectorBridge.java b/designer-base/src/main/java/com/fr/env/detect/base/DetectorBridge.java index 765ad1eda..798e0ae04 100644 --- a/designer-base/src/main/java/com/fr/env/detect/base/DetectorBridge.java +++ b/designer-base/src/main/java/com/fr/env/detect/base/DetectorBridge.java @@ -91,7 +91,7 @@ public class DetectorBridge { return detectorManager.getValue().detect(type); } catch (Exception e) { FineLoggerFactory.getLogger().error("detect failed", e); - // 返回成功的信息, 别影响前端的展示。 + // 返回未知错误信息 return DetectorResult.unknown(type); } } From 55249c518b2aaed896314820e07f5c3227967a17 Mon Sep 17 00:00:00 2001 From: hades Date: Tue, 14 Jun 2022 17:27:37 +0800 Subject: [PATCH 81/85] =?UTF-8?q?REPORT-73532=20F0017=E5=9F=8B=E7=82=B9?= =?UTF-8?q?=E6=96=B0=E5=A2=9Econtent=20=E5=A4=84=E7=90=86=E5=85=BC?= =?UTF-8?q?=E5=AE=B9=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mainframe/alphafine/component/ProductNewsList.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/ProductNewsList.java b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/ProductNewsList.java index c4614a29e..a47322e1c 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/ProductNewsList.java +++ b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/ProductNewsList.java @@ -33,7 +33,7 @@ public class ProductNewsList extends JList { @Override public void mouseClicked(MouseEvent e) { if (e.getClickCount() == AlphaFineConstants.DEFAULT_CLICK_COUNT) { - dealWithClick(); + dealWithClick(getSelectedValue().getUrl()); } } @@ -62,17 +62,13 @@ public class ProductNewsList extends JList { this(new DefaultListModel<>()); } - private void dealWithClick() { + private void dealWithClick(String url) { ProductNews productNews = getSelectedValue(); - jumpToUrl(productNews.getUrl()); + BrowseUtils.browser(url); DesignerEnvManager.getEnvManager().getAlphaFineConfigManager().getReadSet().add(productNews.getId()); AlphaFineHelper.getAlphaFineDialog().repaint(); } - private void jumpToUrl(String url) { - BrowseUtils.browser(url); - } - public int getHoverIndex() { return hoverIndex; } From 79d3169cc1eca6ed31f4d76869e901a33922e9c5 Mon Sep 17 00:00:00 2001 From: hades Date: Tue, 14 Jun 2022 17:37:21 +0800 Subject: [PATCH 82/85] =?UTF-8?q?REPORT-73532=20F0017=E5=9F=8B=E7=82=B9?= =?UTF-8?q?=E6=96=B0=E5=A2=9Econtent=20=E5=A4=84=E7=90=86=E5=85=BC?= =?UTF-8?q?=E5=AE=B9=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../alphafine/component/ProductNewsList.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/ProductNewsList.java b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/ProductNewsList.java index a47322e1c..9e4d1c402 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/ProductNewsList.java +++ b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/ProductNewsList.java @@ -4,7 +4,6 @@ import com.fr.design.DesignerEnvManager; import com.fr.design.mainframe.alphafine.AlphaFineConstants; import com.fr.design.mainframe.alphafine.AlphaFineHelper; import com.fr.design.mainframe.alphafine.model.ProductNews; -import com.fr.design.utils.BrowseUtils; import com.fr.log.FineLoggerFactory; import java.awt.Cursor; @@ -33,7 +32,7 @@ public class ProductNewsList extends JList { @Override public void mouseClicked(MouseEvent e) { if (e.getClickCount() == AlphaFineConstants.DEFAULT_CLICK_COUNT) { - dealWithClick(getSelectedValue().getUrl()); + dealWithClick(); } } @@ -62,9 +61,13 @@ public class ProductNewsList extends JList { this(new DefaultListModel<>()); } - private void dealWithClick(String url) { + private void dealWithClick() { ProductNews productNews = getSelectedValue(); - BrowseUtils.browser(url); + try { + Desktop.getDesktop().browse(new URI(productNews.getUrl())); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } DesignerEnvManager.getEnvManager().getAlphaFineConfigManager().getReadSet().add(productNews.getId()); AlphaFineHelper.getAlphaFineDialog().repaint(); } From 83e8378b0cd25ef502681d8aac9d0cf76dcf891e Mon Sep 17 00:00:00 2001 From: hades Date: Tue, 14 Jun 2022 17:41:22 +0800 Subject: [PATCH 83/85] =?UTF-8?q?REPORT-73532=20=E6=94=B9=E4=B8=8B?= =?UTF-8?q?=E8=B7=B3=E8=BD=AC=E9=A2=84=E8=A7=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mainframe/alphafine/component/ProductNewsList.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/ProductNewsList.java b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/ProductNewsList.java index 9e4d1c402..1a8e0a2a2 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/ProductNewsList.java +++ b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/ProductNewsList.java @@ -4,6 +4,7 @@ import com.fr.design.DesignerEnvManager; import com.fr.design.mainframe.alphafine.AlphaFineConstants; import com.fr.design.mainframe.alphafine.AlphaFineHelper; import com.fr.design.mainframe.alphafine.model.ProductNews; +import com.fr.design.utils.BrowseUtils; import com.fr.log.FineLoggerFactory; import java.awt.Cursor; @@ -63,11 +64,7 @@ public class ProductNewsList extends JList { private void dealWithClick() { ProductNews productNews = getSelectedValue(); - try { - Desktop.getDesktop().browse(new URI(productNews.getUrl())); - } catch (Exception e) { - FineLoggerFactory.getLogger().error(e.getMessage(), e); - } + BrowseUtils.browser(productNews.getUrl()); DesignerEnvManager.getEnvManager().getAlphaFineConfigManager().getReadSet().add(productNews.getId()); AlphaFineHelper.getAlphaFineDialog().repaint(); } From 0c20e0c051f72b7ee5d19f5fd6006b8c3c116ab1 Mon Sep 17 00:00:00 2001 From: Harrison Date: Tue, 14 Jun 2022 20:57:46 +0800 Subject: [PATCH 84/85] =?UTF-8?q?REPORT-73318=E3=80=90=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E7=8E=AF=E5=A2=83=E7=9B=91=E6=B5=8B=E3=80=91=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E5=99=A8finedb=E6=9C=89=E8=84=8F=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=EF=BC=8C=E8=AE=BE=E8=AE=A1=E5=99=A8=E8=BF=9C=E7=A8=8B=E5=88=87?= =?UTF-8?q?=E6=8D=A2=EF=BC=8C=E8=BF=87=E7=A8=8B=E5=BE=88=E6=85=A2=E4=B8=94?= =?UTF-8?q?=E6=B2=A1=E6=9C=89=E8=87=AA=E5=8A=A8=E7=9B=91=E6=B5=8B=E5=BC=B9?= =?UTF-8?q?=E7=AA=97=201-=E7=AE=80=E5=8C=96=E6=93=8D=E4=BD=9C=EF=BC=8C?= =?UTF-8?q?=E4=B8=8D=E5=8C=BA=E5=88=86=20=E8=AE=BE=E8=AE=A1=E5=99=A8/?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E5=99=A8=20=E5=90=AF=E5=8A=A8=E3=80=82?= =?UTF-8?q?=E5=8F=AA=E5=9C=A8=E8=AE=BE=E8=AE=A1=E5=99=A8=E5=90=AF=E5=8A=A8?= =?UTF-8?q?=E5=90=8E=E8=BF=9B=E8=A1=8C=E6=A3=80=E6=B5=8B=202-=E5=88=87?= =?UTF-8?q?=E6=8D=A2=E7=8E=AF=E5=A2=83=E7=9A=84=E6=A3=80=E6=B5=8B=E3=80=82?= =?UTF-8?q?=E5=88=87=E6=8D=A2=E7=8E=AF=E5=A2=83=E5=90=8E=E4=B8=8A=E6=AC=A1?= =?UTF-8?q?=E7=9A=84=E4=BB=BB=E5=8A=A1=E9=9C=80=E8=A6=81=E5=81=9C=E6=AD=A2?= =?UTF-8?q?=E6=89=A7=E8=A1=8C=E3=80=82=203-FineDB=20=E6=A3=80=E6=B5=8B?= =?UTF-8?q?=E4=BB=8E=E6=8D=95=E8=8E=B7=E6=A3=80=E6=B5=8B=EF=BC=8C=E5=88=87?= =?UTF-8?q?=E6=8D=A2=E5=88=B0=E5=85=A8=E9=87=8F=E6=A3=80=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/fr/env/detect/EnvDetectorCenter.java | 111 ++++++++---------- .../env/detect/impl/FineDbDirtyDetector.java | 100 ++++++++++++++-- .../impl/converter/FineDbDirtyConverter.java | 110 ----------------- 3 files changed, 138 insertions(+), 183 deletions(-) delete mode 100644 designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbDirtyConverter.java diff --git a/designer-base/src/main/java/com/fr/env/detect/EnvDetectorCenter.java b/designer-base/src/main/java/com/fr/env/detect/EnvDetectorCenter.java index 37e01f788..a566caa84 100644 --- a/designer-base/src/main/java/com/fr/env/detect/EnvDetectorCenter.java +++ b/designer-base/src/main/java/com/fr/env/detect/EnvDetectorCenter.java @@ -17,58 +17,42 @@ import com.fr.event.Event; import com.fr.event.EventDispatcher; import com.fr.event.Listener; import com.fr.event.Null; -import com.fr.start.server.EmbedServerEvent; +import com.fr.stable.core.UUID; import com.fr.task.Once; import com.fr.update.delay.DelayHelper; import com.fr.workspace.Workspace; import com.fr.workspace.WorkspaceEvent; +import java.util.HashSet; import java.util.List; import java.util.Objects; +import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; /** * 环境检测中心 - * 如果环境检测 -> * * created by Harrison on 2022/05/27 **/ public class EnvDetectorCenter { - private final Listener AFTER_START_LISTENER = new Listener() { - @Override - public void on(Event event, Null param) { - if (isSameProcess(DetectorProcess.SERVER_LAUNCH)) { - stop(); - } - } - }; - private final Listener BEFORE_START_LISTENER = new Listener() { - @Override - public void on(Event event, Null param) { - PROCESS.set(DetectorProcess.SERVER_LAUNCH); - start(); - } - }; - private final Listener START_UP_COMPLETE_LISTENER = new Listener() { @Override public void on(Event event, Null param) { - if (isSameProcess(DetectorProcess.DESIGN_LAUNCH)) { - stop(); - } + // startup 的监听,执行一次即可 + EventDispatcher.stopListen(this); + stop(); } }; private final Listener AFTER_SWITCH_LISTENER = new Listener() { @Override public void on(Event event, Workspace param) { - if (isSameProcess(DetectorProcess.DESIGN_LAUNCH)) { - stop(); - } + stop(); } }; @@ -82,6 +66,13 @@ public class EnvDetectorCenter { private final AtomicReference PROCESS = new AtomicReference<>(); + /** + * 当前还有什么动作未执行 + * 如果切换环境,这里的动作是要被清空的 + * 从而防止切换环境后, 上一个动作的环境延续过来 + */ + private final Set pendingActions = new HashSet<>(); + public static EnvDetectorCenter getInstance() { return EnvDetectorCenterHolder.INSTANCE; } @@ -105,7 +96,6 @@ public class EnvDetectorCenter { PROCESS.set(DetectorProcess.DESIGN_LAUNCH); launchOnce.run(); - listen(); } @@ -113,8 +103,10 @@ public class EnvDetectorCenter { * 销毁,一般用在模块关闭中 */ public void destroy() { + + // 清空 + pendingActions.clear(); - stopListen(); // 重置内容 DetectorBridge.getInstance().reset(); // 关闭逻辑 @@ -123,20 +115,6 @@ public class EnvDetectorCenter { PROCESS.set(null); } - /** - * 当前的流程符合预期 - * 什么情况下不符合? - * design.start -> server.start -> design.end -> server.end - * 这个时候 design.end 就不会触发,等待服务器启动后才触发 - * - * @param process 检测流程 - * @return 是 / 否 - */ - private boolean isSameProcess(DetectorProcess process) { - - return PROCESS.compareAndSet(process, null); - } - /** * 启动 */ @@ -153,13 +131,23 @@ public class EnvDetectorCenter { // 结束 DetectorBridge.getInstance().stop(); - // 30s后执行 - DelayHelper.delayCall(EnvDetectorCenter.class.getName(), () -> { + // id值 + String uuid = UUID.randomUUID().toString(); + // 确认当前的 action 是否有效 + Supplier validAction = () -> pendingActions.contains(uuid); + // 30s后执行 + Runnable detectorAction = () -> { + // 如果当前没开启,则直接返回 if (!EnvDetectorConfig.getInstance().isEnabled()) { return; } + + // 如果当前不包含这个 id, 就不执行 + if (!validAction.get()) { + return; + } Stream resultStream = DetectorBridge.getInstance().detect(); // 展示效果 @@ -174,10 +162,25 @@ public class EnvDetectorCenter { } UIUtil.invokeLaterIfNeeded(() -> { + + // 如果当前不包含这个 id, 就不执行 + if (!validAction.get()) { + return; + } NotificationDialog dialog = new NotificationDialog(properties, notificationModels); dialog.open(); }); - }, 30, TimeUnit.SECONDS); + + // 执行完移除 + pendingActions.remove(uuid); + + }; + + // 添加 + pendingActions.add(uuid); + + + DelayHelper.delayCall(EnvDetectorCenter.class.getName(), detectorAction, 30, TimeUnit.SECONDS); } @@ -208,18 +211,6 @@ public class EnvDetectorCenter { .collect(Collectors.toList()); } - private void listen() { - - // 内置服务器监听 - EventDispatcher.listen(EmbedServerEvent.BeforeStart, BEFORE_START_LISTENER); - EventDispatcher.listen(EmbedServerEvent.AfterStart, AFTER_START_LISTENER); - } - - private void stopListen() { - - EventDispatcher.stopListen(BEFORE_START_LISTENER); - EventDispatcher.stopListen(AFTER_START_LISTENER); - } private enum DetectorProcess { @@ -227,15 +218,5 @@ public class EnvDetectorCenter { * 设计器启动 */ DESIGN_LAUNCH, - - /** - * 服务器启动 - */ - SERVER_LAUNCH, - - /** - * 环境切换 - */ - ENV_SWITCH } } diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/FineDbDirtyDetector.java b/designer-base/src/main/java/com/fr/env/detect/impl/FineDbDirtyDetector.java index 1b3459786..6e33eda43 100644 --- a/designer-base/src/main/java/com/fr/env/detect/impl/FineDbDirtyDetector.java +++ b/designer-base/src/main/java/com/fr/env/detect/impl/FineDbDirtyDetector.java @@ -1,22 +1,106 @@ package com.fr.env.detect.impl; -import com.fr.env.detect.base.CatchExceptionDetector; +import com.fr.config.ConfigContext; +import com.fr.config.Configuration; +import com.fr.design.dialog.FineJOptionPane; +import com.fr.design.i18n.Toolkit; +import com.fr.env.detect.base.AbstractExceptionDetector; +import com.fr.env.detect.base.DetectorConstants; +import com.fr.env.detect.bean.DetectorResult; import com.fr.env.detect.bean.DetectorType; -import com.fr.env.detect.impl.converter.FineDbDirtyConverter; -import com.fr.env.detect.thowable.ThrowableStore; +import com.fr.env.detect.bean.ExceptionSolution; +import com.fr.env.detect.bean.Message; +import com.fr.env.detect.bean.SolutionAction; +import com.fr.io.utils.ResourceIOUtils; +import com.fr.log.FineLoggerFactory; +import com.fr.stable.StableUtils; +import com.fr.stable.project.ProjectConstants; +import com.fr.third.org.hibernate.exception.GenericJDBCException; + +import javax.swing.JOptionPane; +import java.util.Iterator; +import java.util.function.Function; /** * created by Harrison on 2022/05/25 **/ -public class FineDbDirtyDetector extends CatchExceptionDetector { +public class FineDbDirtyDetector extends AbstractExceptionDetector { public FineDbDirtyDetector() { - this(ThrowableStore.getInstance()); + + super(DetectorType.FINE_DB_DIRTY); } - public FineDbDirtyDetector(ThrowableStore throwableStore) { + @Override + public DetectorResult detect() { - super(DetectorType.FINE_DB_DIRTY, throwableStore, new FineDbDirtyConverter()); - } + Iterator tableNames = ConfigContext.getConfigNames(); + while (tableNames.hasNext()) { + String tableName = tableNames.next(); + Class configClass = ConfigContext.getConfigClass(tableName); + Configuration configuration = ConfigContext.getConfigInstance(configClass); + try { + + // 尝试获取每一个值 + configuration.mirror(); + } catch (Throwable e) { + + Function isDirtyExFunction = throwable -> { + while (throwable != null) { + if (throwable instanceof GenericJDBCException) { + return true; + } + throwable = throwable.getCause(); + } + return false; + }; + boolean isDirtyEx = isDirtyExFunction.apply(e); + + if (isDirtyEx) { + DetectorType detectorType = DetectorType.FINE_DB_DIRTY; + DetectorResult.DetectorResultBuilder builder = DetectorResult + .builder() + .withType(detectorType); + String tipsLocale = Toolkit.i18nText(detectorType.getTipsLocale(), tableName); + + String solutionLocale = detectorType.getSolutionLocale(); + ExceptionSolution exceptionSolution = new ExceptionSolution(new Message.Link(Toolkit.i18nText(solutionLocale, DetectorConstants.FINE_DB_HELP_LINK), DetectorConstants.FINE_DB_HELP_LINK), new SolutionAction() { + @Override + public String name() { + return Toolkit.i18nText("Fine-Design_Basic_Reset_Immediately"); + } + + @Override + public void run() { + boolean success = false; + try { + ResourceIOUtils.copy(StableUtils.pathJoin(ProjectConstants.EMBED_DB_DIRECTORY, ProjectConstants.FINE_DB_NAME), + StableUtils.pathJoin(ProjectConstants.EMBED_DB_DIRECTORY, ProjectConstants.FINE_DB_BAK_NAME)); + success = ResourceIOUtils.delete(StableUtils.pathJoin(ProjectConstants.EMBED_DB_DIRECTORY, ProjectConstants.FINE_DB_NAME)); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + // todo 最好的逻辑是,这里应该和 UI 隔离开的 + if (!success) { + FineJOptionPane.showMessageDialog(null, + Toolkit.i18nText("Fine-Design_Error_Finedb_Backup_Reset_Result", + ResourceIOUtils.getRealPath(StableUtils.pathJoin(ProjectConstants.EMBED_DB_DIRECTORY, ProjectConstants.FINE_DB_NAME))), + Toolkit.i18nText("Fine-Design_Basic_Error"), + JOptionPane.ERROR_MESSAGE); + } + } + }); + builder.withTips(tipsLocale) + .withSolution(exceptionSolution) + .withLog(Toolkit.i18nText(detectorType.getLogLocale(), tableName)); + + return builder.build(); + } + + } + } + + return DetectorResult.normal(DetectorType.FINE_DB_DIRTY); + } } diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbDirtyConverter.java b/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbDirtyConverter.java deleted file mode 100644 index 4550cde4a..000000000 --- a/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbDirtyConverter.java +++ /dev/null @@ -1,110 +0,0 @@ -package com.fr.env.detect.impl.converter; - -import com.fr.config.ConfigContext; -import com.fr.config.Configuration; -import com.fr.design.dialog.FineJOptionPane; -import com.fr.design.i18n.Toolkit; -import com.fr.env.detect.base.DetectorConstants; -import com.fr.env.detect.bean.DetectorResult; -import com.fr.env.detect.bean.DetectorType; -import com.fr.env.detect.bean.ExceptionSolution; -import com.fr.env.detect.bean.Message; -import com.fr.env.detect.bean.SolutionAction; -import com.fr.env.detect.thowable.ThrowableConverter; -import com.fr.io.utils.ResourceIOUtils; -import com.fr.log.FineLoggerFactory; -import com.fr.stable.StableUtils; -import com.fr.stable.project.ProjectConstants; -import com.fr.third.org.hibernate.exception.GenericJDBCException; -import org.jetbrains.annotations.Nullable; - -import javax.swing.JOptionPane; -import java.util.Iterator; - -/** - * 脏数据检测 - * - * created by Harrison on 2022/05/25 - **/ -public class FineDbDirtyConverter implements ThrowableConverter { - - @Override - public boolean accept(Throwable throwable) { - - Throwable sign = throwable; - while (sign != null) { - if (sign.getClass() == GenericJDBCException.class) { - return true; - } - sign = sign.getCause(); - } - return false; - } - - /** - * 根据堆栈,确认是否是从配置中发出来的异常 - * 如果是,则找到对应的配置的表, - * 输出信息 - * - * @param throwable 异常 - * @return 检测结果 - */ - @Override - public @Nullable DetectorResult convert(Throwable throwable) { - - Iterator tableNames = ConfigContext.getConfigNames(); - while (tableNames.hasNext()) { - String tableName = tableNames.next(); - Class configClass = ConfigContext.getConfigClass(tableName); - Configuration configuration = ConfigContext.getConfigInstance(configClass); - try { - - // 尝试获取每一个值 - configuration.mirror(); - } catch (Throwable e) { - - DetectorType detectorType = DetectorType.FINE_DB_DIRTY; - DetectorResult.DetectorResultBuilder builder = DetectorResult.builder() - .withType(detectorType); - - String tipsLocale = Toolkit.i18nText(detectorType.getTipsLocale(), tableName); - - String solutionLocale = detectorType.getSolutionLocale(); - ExceptionSolution exceptionSolution = new ExceptionSolution(new Message.Link(Toolkit.i18nText(solutionLocale, DetectorConstants.FINE_DB_HELP_LINK), DetectorConstants.FINE_DB_HELP_LINK), new SolutionAction() { - @Override - public String name() { - return Toolkit.i18nText("Fine-Design_Basic_Reset_Immediately"); - } - - @Override - public void run() { - boolean success = false; - try { - ResourceIOUtils.copy(StableUtils.pathJoin(ProjectConstants.EMBED_DB_DIRECTORY, ProjectConstants.FINE_DB_NAME), - StableUtils.pathJoin(ProjectConstants.EMBED_DB_DIRECTORY, ProjectConstants.FINE_DB_BAK_NAME)); - success = ResourceIOUtils.delete(StableUtils.pathJoin(ProjectConstants.EMBED_DB_DIRECTORY, ProjectConstants.FINE_DB_NAME)); - } catch (Exception e) { - FineLoggerFactory.getLogger().error(e.getMessage(), e); - } - // todo 最好的逻辑是,这里应该和 UI 隔离开的 - if (!success) { - FineJOptionPane.showMessageDialog(null, - Toolkit.i18nText("Fine-Design_Error_Finedb_Backup_Reset_Result", - ResourceIOUtils.getRealPath(StableUtils.pathJoin(ProjectConstants.EMBED_DB_DIRECTORY, ProjectConstants.FINE_DB_NAME))), - Toolkit.i18nText("Fine-Design_Basic_Error"), - JOptionPane.ERROR_MESSAGE); - } - } - }); - builder.withTips(tipsLocale) - .withSolution(exceptionSolution) - .withLog(Toolkit.i18nText(detectorType.getLogLocale(), tableName)); - - return builder.build(); - } - } - - return DetectorResult.normal(DetectorType.FINE_DB_DIRTY); - } - -} From 36779698ebaf51dd45bec9c5c8c6735402e95822 Mon Sep 17 00:00:00 2001 From: Harrison Date: Wed, 15 Jun 2022 11:45:44 +0800 Subject: [PATCH 85/85] =?UTF-8?q?REPORT-73318=E3=80=90=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E7=8E=AF=E5=A2=83=E7=9B=91=E6=B5=8B=E3=80=91=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E5=99=A8finedb=E6=9C=89=E8=84=8F=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=EF=BC=8C=E8=AE=BE=E8=AE=A1=E5=99=A8=E8=BF=9C=E7=A8=8B=E5=88=87?= =?UTF-8?q?=E6=8D=A2=EF=BC=8C=E8=BF=87=E7=A8=8B=E5=BE=88=E6=85=A2=E4=B8=94?= =?UTF-8?q?=E6=B2=A1=E6=9C=89=E8=87=AA=E5=8A=A8=E7=9B=91=E6=B5=8B=E5=BC=B9?= =?UTF-8?q?=E7=AA=97=201-=E8=A1=A5=E5=85=85=E6=97=A5=E5=BF=97=202-?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=B8=AA=E9=97=AE=E9=A2=98=EF=BC=8C?= =?UTF-8?q?=E8=A6=81=E6=89=A7=E8=A1=8C=E5=AE=8C=E4=B9=8B=E5=90=8E=E7=A7=BB?= =?UTF-8?q?=E9=99=A4=EF=BC=8C=E4=B8=8D=E7=84=B6=E5=9C=A8=20UI=20=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B=E4=B8=AD=E4=B8=8D=E6=89=A7=E8=A1=8C=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/fr/env/detect/EnvDetectorCenter.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/designer-base/src/main/java/com/fr/env/detect/EnvDetectorCenter.java b/designer-base/src/main/java/com/fr/env/detect/EnvDetectorCenter.java index a566caa84..edaf3d0b3 100644 --- a/designer-base/src/main/java/com/fr/env/detect/EnvDetectorCenter.java +++ b/designer-base/src/main/java/com/fr/env/detect/EnvDetectorCenter.java @@ -17,6 +17,7 @@ import com.fr.event.Event; import com.fr.event.EventDispatcher; import com.fr.event.Listener; import com.fr.event.Null; +import com.fr.log.FineLoggerFactory; import com.fr.stable.core.UUID; import com.fr.task.Once; import com.fr.update.delay.DelayHelper; @@ -46,12 +47,14 @@ public class EnvDetectorCenter { public void on(Event event, Null param) { // startup 的监听,执行一次即可 EventDispatcher.stopListen(this); + FineLoggerFactory.getLogger().debug("startup complete, start detecting env"); stop(); } }; private final Listener AFTER_SWITCH_LISTENER = new Listener() { @Override public void on(Event event, Workspace param) { + FineLoggerFactory.getLogger().debug("switch env complete, start detecting env"); stop(); } }; @@ -158,6 +161,7 @@ public class EnvDetectorCenter { .map(DetectorUtil::convert2Notification) .collect(Collectors.toList()); if (Collections.isEmpty(notificationModels)) { + FineLoggerFactory.getLogger().debug("detector not found any exception"); return; } @@ -169,10 +173,10 @@ public class EnvDetectorCenter { } NotificationDialog dialog = new NotificationDialog(properties, notificationModels); dialog.open(); + + // 执行完移除 + pendingActions.remove(uuid); }); - - // 执行完移除 - pendingActions.remove(uuid); };

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 z z!~>RI@yDl+t;p8}h%RURX~6=4#=9f1&?(-_PJ`JUdjt<85AkdVWnirLj=K*Vy{c+4 zuI+WZF)>=^Zp=_gg6Lq>10>dXP=QXZBbZF7&21jatlN)B=V4?ts1q+9kic`_KS$bM z;yhkBfgpaJvR(ELK$3yvUJ^SzLOz()ADR{{LE$@`@|9Q9OilPlrsZyF7qg+scYJ3b?PWR!O%`qREmb4T8l-wWBy7QzKr1)rHiU|d{8kgE zRq=8k@CklE|DD=vy{i$A2PdIbd+De)=_dH8y#%~u-i<75^en(O&~5=(Ig53+#%H-L z$wE`mf=e?s9r$_{+mi%)c=dtX#$7rOQx7dN0zTCmzEka88WJwyv+|ksbv;^r@6TRP z)iqXu>P_2;*eCI*cq8YC?N8GY0#?c5e~Qn?RwV!`H0`F7X|}}x9Js{HvU)EqB8kth zV^-MaipSA8!(0O4j{MlKj2gcoay(-OuqEs$i}=M6vVur`vxtXq@8yW&{m3}~c0H}1 zCJ-&?*NO7j^V-|T2rh*>M)BiM3j4={pXTZZ3~$y6MY=oc0`DL{2wd;5KaSZ#&drt_ z?x^7!Bt7bZfpLj!Z>iqz8HK3G@s@eT)Y;n>P@rkVAKy9>y7Yb+?5>~{IlnW(B#E!;D(7GwVBNE=;wiIVs-MF2tl0o&>QHG3%5VcWefPh0ushf z1t)&Z8a@pPWA#7QTCFBuJ4^ymmA0!?q_K5(g`ZrSfD-5qP)_I7bD-_NH+$?{^5E;_1G?@U@j5c=dKvT z3x2x4$ucUk`g4M-fl`ILFAa`tuKwe5up!Xc!xryLmMRqT7oDyBOdmZ?fIq0mXd4hV zt*-P_ci{K1LG;#`>ta`Fb?5_ni=3GN-AN39`)2i{M-+jlC< zc?iGH_{x~!_(od}3lfC%)csAlRstq!&YU1bYkyk&?aSXkwCvmc8UDXr$d~1Y*%Z^3 zq-p+Es6jlT^tlW9+P^D0%D|vG-u1sKI{o#7*8iuXGfetX(W&~ciVlwq{+Qj&Rdj|B zPU6Z()yENY-Q{1Piq1d6SYX}nH~$rCo;B+)3&MK+>U<-KDscaIsOkS_E49!m=(mi+ zC(*3At<>Pu!!)R(qigo+&1&?IicU!`aBlum zSJr{LI_Dey>FR_bV`W_t$>-ZU9neNs)TF3lp^IWYY@%4WL)%Z>&d@GC+H2F|CV?UC z=Im*VE+SimkJSR{&CVto5Uz?oNHP zRxu0Rl5VxViLEBXU(5I-v|=Qz-G!eg#$KCaw`bzPszXTw(z#GGgJIArl7ku746>tPiO!*jIvEOJYx#co zv=3I_$TzmfC4(e5mlT;tuU9>P!Jc8yw&Wgfl4(NuokOC@i1f$QyG^Jx4TdVjMds1U zWbY-CEcZcKy4n;eX-xOi^g>5n1X^67aQ=z2CzVMfXnJM_M>RV5ZX;(tt5wmKUF-A0 zNBe8&h|S}Zb^>EzB{aY>L&Kj(WCsppUxlB*aq1fi>Nfct6jVpJ>}Ov-X;Wg5(`Q?- z>PP>W-KX`10Pm$Esuw+n`p3XeQ0s=Bca_11P`lsVph}Gagz6Ta+eNs3iQGTlb5i}I ze}kNMiPVQCQBBAC+wa)2HU}WRCA+BQb)Jad6flZ? z-DOsuKBet<(}HHx`>v#2t~*m6V`mP5fjoDY(Sol-*0rm6J>ji_WkV7g)WqqnDkPS6 zASMⅆ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 M>!kg1GO*@<0qZIIivR!s literal 0 HcmV?d00001 diff --git a/designer-base/src/main/resources/com/fr/design/standard/loading/loading-64.gif b/designer-base/src/main/resources/com/fr/design/standard/loading/loading-64.gif new file mode 100644 index 0000000000000000000000000000000000000000..9db4c738fbb7e86f556c32f7891525ea93710773 GIT binary patch literal 90641 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 + + + diff --git a/designer-base/src/main/resources/com/fr/design/standard/reminder/reminder_success.svg b/designer-base/src/main/resources/com/fr/design/standard/reminder/reminder_success.svg new file mode 100755 index 000000000..c26d221b5 --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/reminder/reminder_success.svg @@ -0,0 +1,3 @@ + + + diff --git a/designer-base/src/main/resources/com/fr/design/standard/reminder/reminder_warning.svg b/designer-base/src/main/resources/com/fr/design/standard/reminder/reminder_warning.svg new file mode 100755 index 000000000..5b0eabe82 --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/reminder/reminder_warning.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/designer-base/src/test/java/com/fr/design/components/notification/NotificationDialogTest.java b/designer-base/src/test/java/com/fr/design/components/notification/NotificationDialogTest.java new file mode 100644 index 000000000..fec7e8069 --- /dev/null +++ b/designer-base/src/test/java/com/fr/design/components/notification/NotificationDialogTest.java @@ -0,0 +1,43 @@ +package com.fr.design.components.notification; + +import com.fr.design.utils.DevUtil; +import com.fr.third.guava.collect.Lists; + +import java.awt.Frame; +import java.util.function.Consumer; + +public class NotificationDialogTest { + + public static void main(String[] args) { + + testShow(); + } + + public static void testShow() { + + DevUtil.show(new Consumer() { + @Override + public void accept(Frame frame) { + + NotificationModel model1 = new NotificationModel(NotificationType.WARNING, null, new NotificationMessage.LinkMessage("test", "abc"), new NotificationMessage.LinkMessage("abc", "aaa")); + + NotificationModel model2 = new NotificationModel(NotificationType.INFO, new NotificationAction() { + @Override + public String name() { + return "action"; + } + + @Override + public void run(Object... args) { + System.out.println("1111"); + } + }, new NotificationMessage.LinkMessage("display model2 test", "abc")); + + NotificationDialogProperties properties = new NotificationDialogProperties(frame, "test"); + NotificationDialog dialog = new NotificationDialog(properties, Lists.newArrayList(model1, model2)); + dialog.setVisible(true); + } + }); + } + +} \ No newline at end of file From 0b02d73eb8a4f352ee1e6ae7b916877e79b482ff Mon Sep 17 00:00:00 2001 From: Harrison Date: Sun, 29 May 2022 15:45:04 +0800 Subject: [PATCH 21/85] =?UTF-8?q?feat:=20REPORT-70565=20=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E7=8E=AF=E5=A2=83=E7=9B=91=E6=B5=8B=EF=BC=88jar?= =?UTF-8?q?=E5=8C=85=E5=BC=82=E5=B8=B8=E3=80=81finedb=E3=80=81=E6=9D=80?= =?UTF-8?q?=E6=AF=92=E8=BD=AF=E4=BB=B6=EF=BC=89=201-=E8=A1=A5=E5=85=85?= =?UTF-8?q?=E8=AE=BE=E5=AE=9A=EF=BC=8C=E9=92=88=E5=AF=B9=E6=9F=90=E4=B8=80?= =?UTF-8?q?=E9=A1=B9=E7=9A=84=E6=9F=90=E4=B8=80=E4=B8=AA=E5=BC=82=E5=B8=B8?= =?UTF-8?q?=E8=BF=9B=E8=A1=8C=E6=A3=80=E6=B5=8B=202-=E6=89=80=E6=9C=89?= =?UTF-8?q?=E7=9A=84=E6=97=A5=E5=BF=97=EF=BC=8C=E5=9C=A8=E6=A3=80=E6=B5=8B?= =?UTF-8?q?=E5=90=8E=EF=BC=8C=E7=BB=9F=E4=B8=80=E8=BE=93=E5=87=BA=EF=BC=8C?= =?UTF-8?q?=E9=81=BF=E5=85=8D=E9=81=97=E5=BF=98=203-=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E4=B8=80=E4=B8=8B=E4=BB=A3=E7=A0=81=E8=B4=A8=E9=87=8F=204-?= =?UTF-8?q?=E8=A1=A5=E5=85=85=20UI=20=E9=83=A8=E5=88=86=20-=20=E5=BC=82?= =?UTF-8?q?=E5=B8=B8=E5=B1=95=E7=A4=BA=205-=E8=A1=A5=E5=85=85=20UI=20?= =?UTF-8?q?=E9=83=A8=E5=88=86=20-=20=E6=A3=80=E6=B5=8B=20model?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/fr/env/detect/EnvDetectorCenter.java | 172 +++++++++++ .../detect/base/CatchExceptionDetector.java | 2 +- .../fr/env/detect/base/DetectorBridge.java | 27 +- .../fr/env/detect/base/DetectorManager.java | 4 +- .../com/fr/env/detect/base/DetectorUtil.java | 85 +++++- .../detect/base/ExceptionDetectorConfig.java | 23 +- .../fr/env/detect/bean/DetectorResult.java | 4 + .../fr/env/detect/bean/ExceptionSolution.java | 5 + .../com/fr/env/detect/bean/ExceptionTips.java | 5 + .../detect/exception/DetectorException.java | 26 -- .../exception/intelli/package-info.java | 4 - .../env/detect/impl/JarConflictDetector.java | 4 +- .../detect/impl/JarInconsistentDetector.java | 141 ++++----- .../fr/env/detect/impl/JarLackDetector.java | 8 +- ...ertor.java => ClassConflictConvertor.java} | 30 +- .../impl/converter/FineDbDirtyConverter.java | 27 +- .../impl/converter/FineDbLockedConverter.java | 2 +- .../converter/FineDbPermissionConverter.java | 3 +- .../fr/env/detect/ui/DetectorErrorDialog.java | 135 +++++++++ .../fr/env/detect/ui/EnvDetectorDialog.java | 273 ++++++------------ .../com/fr/env/detect/ui/EnvDetectorItem.java | 38 +++ .../fr/env/detect/ui/EnvDetectorModel.java | 59 ++++ 22 files changed, 762 insertions(+), 315 deletions(-) create mode 100644 designer-base/src/main/java/com/fr/env/detect/EnvDetectorCenter.java delete mode 100644 designer-base/src/main/java/com/fr/env/detect/exception/DetectorException.java delete mode 100644 designer-base/src/main/java/com/fr/env/detect/exception/intelli/package-info.java rename designer-base/src/main/java/com/fr/env/detect/impl/converter/{ClassConfictConvertor.java => ClassConflictConvertor.java} (81%) create mode 100644 designer-base/src/main/java/com/fr/env/detect/ui/DetectorErrorDialog.java create mode 100644 designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorItem.java create mode 100644 designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorModel.java diff --git a/designer-base/src/main/java/com/fr/env/detect/EnvDetectorCenter.java b/designer-base/src/main/java/com/fr/env/detect/EnvDetectorCenter.java new file mode 100644 index 000000000..5a524e5b5 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/EnvDetectorCenter.java @@ -0,0 +1,172 @@ +package com.fr.env.detect; + +import com.fr.design.components.notification.NotificationDialog; +import com.fr.design.components.notification.NotificationDialogProperties; +import com.fr.design.components.notification.NotificationModel; +import com.fr.design.constants.DesignerLaunchStatus; +import com.fr.design.i18n.Toolkit; +import com.fr.design.mainframe.DesignerContext; +import com.fr.design.ui.util.UIUtil; +import com.fr.env.detect.base.DetectorBridge; +import com.fr.env.detect.base.DetectorUtil; +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.DetectorStatus; +import com.fr.event.Event; +import com.fr.event.EventDispatcher; +import com.fr.event.Listener; +import com.fr.event.Null; +import com.fr.start.server.EmbedServerEvent; +import com.fr.update.delay.DelayHelper; +import com.fr.workspace.Workspace; +import com.fr.workspace.WorkspaceEvent; + +import java.util.List; +import java.util.Objects; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * created by Harrison on 2022/05/27 + **/ +public class EnvDetectorCenter { + + public static EnvDetectorCenter getInstance() { + return EnvDetectorCenterHolder.INSTANCE; + } + + private static class EnvDetectorCenterHolder { + private static final EnvDetectorCenter INSTANCE = new EnvDetectorCenter(); + } + + private final AtomicReference PROCESS = new AtomicReference<>(); + + public void init() { + + // 默认是启动 + PROCESS.set(DetectorProcess.DESIGN_LAUNCH); + start(); + + // 添加启动完成监听 + EventDispatcher.listen(DesignerLaunchStatus.STARTUP_COMPLETE, new Listener() { + @Override + public void on(Event event, Null param) { + if (isSameProcess(DetectorProcess.DESIGN_LAUNCH)) { + stop(); + } + } + }); + + // 切换工作目录 + EventDispatcher.listen(WorkspaceEvent.BeforeSwitch, new Listener() { + @Override + public void on(Event event, Workspace param) { + PROCESS.set(DetectorProcess.ENV_SWITCH); + start(); + } + }); + EventDispatcher.listen(WorkspaceEvent.AfterSwitch, new Listener() { + @Override + public void on(Event event, Workspace param) { + if (isSameProcess(DetectorProcess.ENV_SWITCH)) { + stop(); + } + } + }); + + // 打开内置服务器 + EventDispatcher.listen(EmbedServerEvent.BeforeStart, new Listener() { + @Override + public void on(Event event, Null param) { + PROCESS.set(DetectorProcess.SERVER_LAUNCH); + start(); + } + }); + EventDispatcher.listen(EmbedServerEvent.AfterStart, new Listener() { + @Override + public void on(Event event, Null param) { + if (isSameProcess(DetectorProcess.SERVER_LAUNCH)) { + stop(); + } + } + }); + + } + + /** + * 当前的流程符合预期 + * 什么情况下不符合? + * design.start -> server.start -> design.end -> server.end + * 这个时候 design.end 就不会触发,等待服务器启动后才触发 + * + * @param process 检测流程 + * @return 是 / 否 + */ + private boolean isSameProcess(DetectorProcess process) { + + return PROCESS.compareAndSet(process, null); + } + + public void start() { + + DetectorBridge.getInstance().start(); + } + + public void stop() { + + Stream resultStream = DetectorBridge.getInstance().detect(); + + // 展示效果 + NotificationDialogProperties properties = new NotificationDialogProperties(DesignerContext.getDesignerFrame(), Toolkit.i18nText("Fine-Design_Basic_Detect_Notification_Title")); + List notificationModels = resultStream + .filter(Objects::nonNull) + .filter((e) -> e.getStatus() == DetectorStatus.EXCEPTION) + .map(DetectorUtil::convert2Notification) + .collect(Collectors.toList()); + + // 一分钟后执行 + DelayHelper.delayCall(EnvDetectorCenter.class.getName(), () -> { + UIUtil.invokeLaterIfNeeded(() -> { + NotificationDialog dialog = new NotificationDialog(properties, notificationModels); + dialog.open(); + }); + }, 1, TimeUnit.MINUTES); + + // 结束 + DetectorBridge.getInstance().stop(); + } + + /** + * 使用预期外的错误进行展示 + * + * @param throwable 异常 + * @return + */ + public List terminate(Throwable throwable) { + + Stream resultStream = DetectorBridge.getInstance().detect(throwable); + List results = resultStream + .filter((e) -> e.getStatus() == DetectorStatus.EXCEPTION) + .collect(Collectors.toList()); + return results; + } + + private enum DetectorProcess { + + /** + * 设计器启动 + */ + DESIGN_LAUNCH, + + /** + * 服务器启动 + */ + SERVER_LAUNCH, + + /** + * 环境切换 + */ + ENV_SWITCH + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/base/CatchExceptionDetector.java b/designer-base/src/main/java/com/fr/env/detect/base/CatchExceptionDetector.java index 3eeac6e46..0dbce9c00 100644 --- a/designer-base/src/main/java/com/fr/env/detect/base/CatchExceptionDetector.java +++ b/designer-base/src/main/java/com/fr/env/detect/base/CatchExceptionDetector.java @@ -50,6 +50,6 @@ public abstract class CatchExceptionDetector extends AbstractExceptionDetector { return result; } } - return null; + return DetectorResult.normal(type()); } } diff --git a/designer-base/src/main/java/com/fr/env/detect/base/DetectorBridge.java b/designer-base/src/main/java/com/fr/env/detect/base/DetectorBridge.java index 031df59a2..534fa7abf 100644 --- a/designer-base/src/main/java/com/fr/env/detect/base/DetectorBridge.java +++ b/designer-base/src/main/java/com/fr/env/detect/base/DetectorBridge.java @@ -13,7 +13,6 @@ import com.fr.env.detect.thowable.ThrowableStore; import com.fr.value.NotNullLazyValue; import org.jetbrains.annotations.NotNull; -import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Stream; @@ -54,7 +53,7 @@ public class DetectorBridge { public void start() { - if (ExceptionDetectorConfig.getInstance().isOpen()) { + if (!hasStarted.get() && ExceptionDetectorConfig.getInstance().isOpen()) { // 开始注册异常处理 ThrowableLogAppender.getInstance().enable(); hasStarted.set(true); @@ -76,11 +75,33 @@ public class DetectorBridge { * @param type 检测类型 * @return 检测结果 */ - public Optional detect(DetectorType type) { + @NotNull + public DetectorResult detect(DetectorType type) { return detectorManager.getValue().detect(type); } + /** + * 针对某一项 \ 某一个异常进行检测 + * + * @param type 类型 + * @param throwable 异常 + * @return 结果 + */ + @NotNull + public DetectorResult detect(DetectorType type, Throwable throwable) { + + // 先清理一下 + ThrowableStore.getInstance().reset(); + + ThrowableStore.getInstance().add(throwable); + DetectorResult result = detect(type); + ThrowableStore.getInstance().reset(); + + return result; + + } + /** * 异常检测 * 对异常统一检测 diff --git a/designer-base/src/main/java/com/fr/env/detect/base/DetectorManager.java b/designer-base/src/main/java/com/fr/env/detect/base/DetectorManager.java index 76c2d2dd1..ced502129 100644 --- a/designer-base/src/main/java/com/fr/env/detect/base/DetectorManager.java +++ b/designer-base/src/main/java/com/fr/env/detect/base/DetectorManager.java @@ -34,7 +34,7 @@ class DetectorManager { return results; } - public Optional detect(DetectorType type) { + public DetectorResult detect(DetectorType type) { Optional result = detectors.stream() .filter((detector -> type == detector.type())) @@ -44,6 +44,6 @@ class DetectorManager { // 记录日志 result.ifPresent(DetectorResult::log); - return result; + return result.orElse(DetectorResult.normal(type)); } } diff --git a/designer-base/src/main/java/com/fr/env/detect/base/DetectorUtil.java b/designer-base/src/main/java/com/fr/env/detect/base/DetectorUtil.java index 33aa0e98f..12c51d19b 100644 --- a/designer-base/src/main/java/com/fr/env/detect/base/DetectorUtil.java +++ b/designer-base/src/main/java/com/fr/env/detect/base/DetectorUtil.java @@ -1,7 +1,28 @@ package com.fr.env.detect.base; +import com.fr.base.function.ThrowableRunnable; +import com.fr.design.components.notification.NotificationAction; +import com.fr.design.components.notification.NotificationMessage; +import com.fr.design.components.notification.NotificationModel; +import com.fr.design.components.notification.NotificationType; +import com.fr.design.dialog.link.MessageWithLink; +import com.fr.design.gui.ilable.UILabel; +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.ExceptionSolution; +import com.fr.env.detect.bean.ExceptionTips; +import com.fr.env.detect.bean.Message; +import com.fr.env.detect.bean.SolutionAction; import com.fr.general.build.BuildInfo; import com.fr.stable.StringUtils; +import org.jetbrains.annotations.NotNull; + +import javax.swing.JComponent; +import java.awt.Desktop; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.function.Function; /** * created by Harrison on 2022/05/25 @@ -12,5 +33,67 @@ public class DetectorUtil { return StringUtils.contains(info.getJar(), "fine-report-designer"); } - + + public static NotificationModel convert2Notification(DetectorResult result) { + + List messages = new ArrayList<>(); + + Function> convert2NotificationMsg = message -> { + + NotificationMessage notificationMessage = null; + if (message != null) { + Message.Type type = message.getType(); + switch (type) { + case SIMPLE: + notificationMessage = (new NotificationMessage.SimpleMessage(message.get())); + break; + case LINK: + Message.Link linkMsg = (Message.Link) message; + notificationMessage = new NotificationMessage.LinkMessage(linkMsg.getText(), linkMsg.getLink()); + break; + default: + break; + } + } + return Optional.ofNullable(notificationMessage); + }; + + ExceptionTips tips = result.getTips(); + convert2NotificationMsg + .apply(tips.getMessage()) + .ifPresent(messages::add); + + ExceptionSolution solution = result.getSolution(); + convert2NotificationMsg.apply(solution.getMessage()) + .ifPresent(messages::add); + + NotificationAction notificationAction = null; + SolutionAction solutionAction = solution.getAction(); + if (solutionAction != null) { + notificationAction = new NotificationAction() { + @Override + public String name() { + return solutionAction.name(); + } + + @Override + public void run(Object... args) { + solutionAction.run(); + } + }; + } + + return new NotificationModel(NotificationType.WARNING, notificationAction, messages); + } + + public static JComponent convert2Component(@NotNull Message message) { + + if (message.getType() == Message.Type.LINK) { + Message.Link linkMsg = (Message.Link) message; + return new MessageWithLink(linkMsg.getText(), ThrowableRunnable.toRunnable(() -> { + Desktop.getDesktop().browse(URI.create(linkMsg.getLink())); + })); + } + return new UILabel(message.get()); + } } diff --git a/designer-base/src/main/java/com/fr/env/detect/base/ExceptionDetectorConfig.java b/designer-base/src/main/java/com/fr/env/detect/base/ExceptionDetectorConfig.java index 489d5e311..724e5e8d3 100644 --- a/designer-base/src/main/java/com/fr/env/detect/base/ExceptionDetectorConfig.java +++ b/designer-base/src/main/java/com/fr/env/detect/base/ExceptionDetectorConfig.java @@ -1,21 +1,34 @@ package com.fr.env.detect.base; +import com.fr.config.ConfigContext; +import com.fr.config.DefaultConfiguration; +import com.fr.config.holder.Conf; +import com.fr.config.holder.factory.Holders; + /** * created by Harrison on 2022/05/13 **/ -public class ExceptionDetectorConfig { +public class ExceptionDetectorConfig extends DefaultConfiguration { + private static volatile ExceptionDetectorConfig instance = null; public static ExceptionDetectorConfig getInstance() { - return ExceptionDetectorConfigHolder.INSTANCE; + + if (instance == null) { + instance = ConfigContext.getConfigInstance(ExceptionDetectorConfig.class); + } + return instance; } - private static class ExceptionDetectorConfigHolder { - private static final ExceptionDetectorConfig INSTANCE = new ExceptionDetectorConfig(); + private final Conf open = Holders.simple(true); + + public void setOpen(boolean open) { + + this.open.set(open); } public boolean isOpen() { - return true; + return open.get(); } } diff --git a/designer-base/src/main/java/com/fr/env/detect/bean/DetectorResult.java b/designer-base/src/main/java/com/fr/env/detect/bean/DetectorResult.java index aadccd091..2b45ecbc1 100644 --- a/designer-base/src/main/java/com/fr/env/detect/bean/DetectorResult.java +++ b/designer-base/src/main/java/com/fr/env/detect/bean/DetectorResult.java @@ -1,5 +1,7 @@ package com.fr.env.detect.bean; +import org.jetbrains.annotations.Nullable; + /** * 检测结果 * @@ -51,10 +53,12 @@ public class DetectorResult { return type; } + @Nullable public ExceptionTips getTips() { return tips; } + @Nullable public ExceptionSolution getSolution() { return solution; } diff --git a/designer-base/src/main/java/com/fr/env/detect/bean/ExceptionSolution.java b/designer-base/src/main/java/com/fr/env/detect/bean/ExceptionSolution.java index 796d78c8d..b4f549f67 100644 --- a/designer-base/src/main/java/com/fr/env/detect/bean/ExceptionSolution.java +++ b/designer-base/src/main/java/com/fr/env/detect/bean/ExceptionSolution.java @@ -12,6 +12,11 @@ public class ExceptionSolution { @Nullable private SolutionAction action; + public static ExceptionSolution create(String text, String link, SolutionAction action) { + + return new ExceptionSolution(new Message.Link(text, link), action); + } + public ExceptionSolution(Message message, @Nullable SolutionAction action) { this.message = message; this.action = action; diff --git a/designer-base/src/main/java/com/fr/env/detect/bean/ExceptionTips.java b/designer-base/src/main/java/com/fr/env/detect/bean/ExceptionTips.java index 970bca989..b7ab6c63f 100644 --- a/designer-base/src/main/java/com/fr/env/detect/bean/ExceptionTips.java +++ b/designer-base/src/main/java/com/fr/env/detect/bean/ExceptionTips.java @@ -9,6 +9,11 @@ public class ExceptionTips { private Message message; + public static ExceptionTips create(String text) { + + return new ExceptionTips(new Message.Simple(text)); + } + public ExceptionTips(Message message) { this.message = message; } diff --git a/designer-base/src/main/java/com/fr/env/detect/exception/DetectorException.java b/designer-base/src/main/java/com/fr/env/detect/exception/DetectorException.java deleted file mode 100644 index 14adcab29..000000000 --- a/designer-base/src/main/java/com/fr/env/detect/exception/DetectorException.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.fr.env.detect.exception; - -/** - * created by Harrison on 2022/05/25 - **/ -public class DetectorException extends RuntimeException { - - public DetectorException() { - } - - public DetectorException(String message) { - super(message); - } - - public DetectorException(String message, Throwable cause) { - super(message, cause); - } - - public DetectorException(Throwable cause) { - super(cause); - } - - public DetectorException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { - super(message, cause, enableSuppression, writableStackTrace); - } -} diff --git a/designer-base/src/main/java/com/fr/env/detect/exception/intelli/package-info.java b/designer-base/src/main/java/com/fr/env/detect/exception/intelli/package-info.java deleted file mode 100644 index f11ad3f7e..000000000 --- a/designer-base/src/main/java/com/fr/env/detect/exception/intelli/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 国际化相关的异常 - */ -package com.fr.env.detect.exception.intelli; \ No newline at end of file diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/JarConflictDetector.java b/designer-base/src/main/java/com/fr/env/detect/impl/JarConflictDetector.java index 39a3bc4e0..2eb38d94e 100644 --- a/designer-base/src/main/java/com/fr/env/detect/impl/JarConflictDetector.java +++ b/designer-base/src/main/java/com/fr/env/detect/impl/JarConflictDetector.java @@ -2,7 +2,7 @@ package com.fr.env.detect.impl; import com.fr.env.detect.base.CatchExceptionDetector; import com.fr.env.detect.bean.DetectorType; -import com.fr.env.detect.impl.converter.ClassConfictConvertor; +import com.fr.env.detect.impl.converter.ClassConflictConvertor; /** * created by Harrison on 2022/05/26 @@ -10,6 +10,6 @@ import com.fr.env.detect.impl.converter.ClassConfictConvertor; public class JarConflictDetector extends CatchExceptionDetector { public JarConflictDetector() { - super(DetectorType.JAR_CONFLICT, new ClassConfictConvertor()); + super(DetectorType.JAR_CONFLICT, new ClassConflictConvertor()); } } diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/JarInconsistentDetector.java b/designer-base/src/main/java/com/fr/env/detect/impl/JarInconsistentDetector.java index dfb880a9e..6012be3d3 100644 --- a/designer-base/src/main/java/com/fr/env/detect/impl/JarInconsistentDetector.java +++ b/designer-base/src/main/java/com/fr/env/detect/impl/JarInconsistentDetector.java @@ -19,6 +19,7 @@ import com.fr.third.guava.collect.MapDifference; import com.fr.third.guava.collect.Maps; import com.fr.third.org.apache.commons.lang3.StringUtils; import com.fr.workspace.WorkContext; +import org.jetbrains.annotations.NotNull; import java.util.HashMap; import java.util.List; @@ -50,76 +51,86 @@ public class JarInconsistentDetector extends AbstractExceptionDetector { public DetectorResult detect() { if (WorkContext.getCurrent().isLocal()) { - - // 本地的获取方式 - BuildInfoOperator operator = new BuildInfoOperatorImpl(); - List buildInfos = operator.getBuildInfos(); - - // 获取设计器的 build - Optional designerBuild = buildInfos.stream() - .filter(DetectorUtil::isDesignerJar) - .map(BuildInfo::getGroupBuild) - .filter(StringUtils::isNotEmpty) - .findFirst(); - - // 如果 build - if (!designerBuild.isPresent()) { - return DetectorResult.normal(type()); - } - - // 获取所有的不一致的 build - List inConsistentInfos = buildInfos.stream() - .filter((e) -> !StringUtils.equals(designerBuild.get(), e.getGroupBuild())) - .collect(Collectors.toList()); - - // 没有直接返回 - if (Collections.isEmpty(inConsistentInfos)) { - return DetectorResult.normal(type()); - } - - // 有的话 - List inConsistentJars = inConsistentInfos.stream() - .map(BuildInfo::getJar) - .collect(Collectors.toList()); - String message = StringUtils.join(",", inConsistentJars); - String tipsMessage = Toolkit.i18nText("Fine_Design_Basic_Detect_Local") + Toolkit.i18nText(type().getTipsLocale()) + "\n" + message; - - return DetectorResult.exception(type(), - new ExceptionTips(new Message.Simple(tipsMessage)), - new ExceptionSolution(new Message.Link(Toolkit.i18nText(type().getSolutionLocale()), DetectorConstants.JAR_HELP_LINK), null), - ExceptionLog.create(type().getLogLocale() + message)); + return detectLocal(); } else { - - // 检测有哪些 JAR 包, 当前是否缺少对应的 JAR 包 - BuildInfoOperator buildInfoOperator = WorkContext.getCurrent().get(BuildInfoOperator.class); - List buildInfos = buildInfoOperator.getBuildInfos(); - - // 远程情况 - List localInfos = BuildInfoManager.getInstance().getInfos(); - Map localMap = groupBy(localInfos); - - List remoteInfos = buildInfos; - Map remoteMap = groupBy(remoteInfos); - - MapDifference difference = Maps.difference(localMap, remoteMap); - Map diffInCommon = difference.entriesInCommon(); - - if (diffInCommon.isEmpty()) { - return DetectorResult.normal(type()); - } - - Set inConsistentJars = diffInCommon.keySet(); - - String message = StringUtils.join(",", inConsistentJars); - Message.Simple tipsMessage = new Message.Simple(Toolkit.i18nText("Fine_Design_Basic_Detect_Server") + Toolkit.i18nText(type().getTipsLocale()) + "\n" + message); + return detectLocalAndRemote(); + } + } - return DetectorResult.exception(type(), - new ExceptionTips(tipsMessage), - new ExceptionSolution(new Message.Link(Toolkit.i18nText(type().getSolutionLocale()), DetectorConstants.JAR_HELP_LINK), null), - ExceptionLog.create(type().getLogLocale() + message)); + @NotNull + private DetectorResult detectLocalAndRemote() { + + // 检测有哪些 JAR 包, 当前是否缺少对应的 JAR 包 + BuildInfoOperator buildInfoOperator = WorkContext.getCurrent().get(BuildInfoOperator.class); + List buildInfos = buildInfoOperator.getBuildInfos(); + + // 远程情况 + List localInfos = BuildInfoManager.getInstance().getInfos(); + Map localMap = groupBy(localInfos); + + List remoteInfos = buildInfos; + Map remoteMap = groupBy(remoteInfos); + + MapDifference difference = Maps.difference(localMap, remoteMap); + Map diffInCommon = difference.entriesInCommon(); + + if (diffInCommon.isEmpty()) { + return DetectorResult.normal(type()); } + + Set inConsistentJars = diffInCommon.keySet(); + + String message = StringUtils.join(",", inConsistentJars); + Message.Simple tipsMessage = new Message.Simple(Toolkit.i18nText("Fine_Design_Basic_Detect_Server") + Toolkit.i18nText(type().getTipsLocale()) + "\n" + message); + + return DetectorResult.exception(type(), + new ExceptionTips(tipsMessage), + new ExceptionSolution(new Message.Link(Toolkit.i18nText(type().getSolutionLocale(),DetectorConstants.JAR_HELP_LINK) + , DetectorConstants.JAR_HELP_LINK), null), + ExceptionLog.create(type().getLogLocale() + message)); + } + @NotNull + private DetectorResult detectLocal() { + + // 本地的获取方式 + BuildInfoOperator operator = new BuildInfoOperatorImpl(); + List buildInfos = operator.getBuildInfos(); + + // 获取设计器的 build + Optional designerBuild = buildInfos.stream() + .filter(DetectorUtil::isDesignerJar) + .map(BuildInfo::getGroupBuild) + .filter(StringUtils::isNotEmpty) + .findFirst(); + + // 如果 build + if (!designerBuild.isPresent()) { + return DetectorResult.normal(type()); + } + + // 获取所有的不一致的 build + List inConsistentInfos = buildInfos.stream() + .filter((e) -> !StringUtils.equals(designerBuild.get(), e.getGroupBuild())) + .collect(Collectors.toList()); + + // 没有直接返回 + if (Collections.isEmpty(inConsistentInfos)) { + return DetectorResult.normal(type()); + } + + // 有的话 + List inConsistentJars = inConsistentInfos.stream() + .map(BuildInfo::getJar) + .collect(Collectors.toList()); + String message = StringUtils.join(",", inConsistentJars); + String tipsMessage = Toolkit.i18nText("Fine_Design_Basic_Detect_Local") + Toolkit.i18nText(type().getTipsLocale()) + "\n" + message; + + return DetectorResult.exception(type(), + new ExceptionTips(new Message.Simple(tipsMessage)), + new ExceptionSolution(new Message.Link(Toolkit.i18nText(type().getSolutionLocale()), DetectorConstants.JAR_HELP_LINK), null), + ExceptionLog.create(type().getLogLocale() + message)); } private Map groupBy(List localInfos) { diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/JarLackDetector.java b/designer-base/src/main/java/com/fr/env/detect/impl/JarLackDetector.java index 654029da7..2175b8bb1 100644 --- a/designer-base/src/main/java/com/fr/env/detect/impl/JarLackDetector.java +++ b/designer-base/src/main/java/com/fr/env/detect/impl/JarLackDetector.java @@ -15,14 +15,11 @@ import com.fr.general.build.BuildInfo; import com.fr.general.build.BuildInfoOperator; import com.fr.general.build.impl.BuildInfoOperatorImpl; import com.fr.locale.InterProviderFactory; -import com.fr.stable.CommonUtils; -import com.fr.stable.StableUtils; import com.fr.third.guava.collect.Lists; import com.fr.third.org.apache.commons.lang3.StringUtils; import com.fr.workspace.WorkContext; import org.jetbrains.annotations.NotNull; -import java.io.File; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -81,9 +78,8 @@ public class JarLackDetector extends AbstractExceptionDetector { private Message tipsMessage(List infos) { - String installHome = StableUtils.getInstallHome(); - String homeLibPath = installHome + File.pathSeparator + "lib"; - String webLibPath = CommonUtils.join(new String[]{installHome, "webapps", "webroot", "WEB-INF", "lib"}, File.pathSeparator); + String webLibPath = "%FR_HOME%\\webapps\\webroot\\WEB-INF\\lib:"; + String homeLibPath = "%FR_HOME%\\lib:"; Map> libMap = groupByPath(infos, homeLibPath, webLibPath); diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassConfictConvertor.java b/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassConflictConvertor.java similarity index 81% rename from designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassConfictConvertor.java rename to designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassConflictConvertor.java index f2c8c5b75..4fa33f301 100644 --- a/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassConfictConvertor.java +++ b/designer-base/src/main/java/com/fr/env/detect/impl/converter/ClassConflictConvertor.java @@ -1,6 +1,13 @@ package com.fr.env.detect.impl.converter; +import com.fr.common.util.Strings; +import com.fr.design.i18n.Toolkit; +import com.fr.env.detect.base.DetectorConstants; import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.DetectorType; +import com.fr.env.detect.bean.ExceptionLog; +import com.fr.env.detect.bean.ExceptionSolution; +import com.fr.env.detect.bean.ExceptionTips; import com.fr.env.detect.thowable.ThrowableConverter; import com.fr.stable.resource.ResourceLoader; @@ -24,13 +31,16 @@ import java.util.regex.Pattern; * * created by Harrison on 2022/05/24 **/ -public class ClassConfictConvertor implements ThrowableConverter { +public class ClassConflictConvertor implements ThrowableConverter { private Map, ClassNameConverter> throwableMap = new HashMap<>(); - private static final Pattern JAR_PATTERN = Pattern.compile("([\\w+-\\.]*\\.jar)"); + /** + * 获取对应的 JAR 包名称 + */ + private static final Pattern JAR_NAME_PATTERN = Pattern.compile("([\\w+-\\.]*\\.jar)"); - public ClassConfictConvertor() { + public ClassConflictConvertor() { // 类异常 this.throwableMap.put(ClassNotFoundException.class, Converter.CLASS); @@ -70,6 +80,7 @@ public class ClassConfictConvertor implements ThrowableConverter { sign = sign.getCause(); } + Set allPath = new HashSet<>(); for (String className : classNames) { String classFile = "/" + className.replaceAll("\\.", "/") + ".class"; try { @@ -79,10 +90,9 @@ public class ClassConfictConvertor implements ThrowableConverter { URL url = urls.nextElement(); urlList.add(url); } - Set allPath = new HashSet<>(); for (URL url : urlList) { String file = url.getFile(); - Matcher matcher = JAR_PATTERN.matcher(url.getFile()); + Matcher matcher = JAR_NAME_PATTERN.matcher(url.getFile()); if (matcher.find()) { String jar = matcher.group(); allPath.add(jar); @@ -93,11 +103,17 @@ public class ClassConfictConvertor implements ThrowableConverter { } } } - } catch (IOException ignore) { } } - return null; + + String msg = Strings.join("、", allPath); + + DetectorType type = DetectorType.JAR_CONFLICT; + return DetectorResult.exception(type, + ExceptionTips.create(Toolkit.i18nText(type.getTipsLocale()) + msg), + ExceptionSolution.create(Toolkit.i18nText(type.getSolutionLocale()), DetectorConstants.JAR_HELP_LINK, null), + ExceptionLog.create(Toolkit.i18nText(type.getLogLocale()), msg)); } private interface ClassNameConverter { diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbDirtyConverter.java b/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbDirtyConverter.java index f61d16913..fe114b253 100644 --- a/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbDirtyConverter.java +++ b/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbDirtyConverter.java @@ -2,6 +2,7 @@ package com.fr.env.detect.impl.converter; import com.fr.config.ConfigContext; import com.fr.config.Configuration; +import com.fr.design.dialog.FineJOptionPane; import com.fr.design.i18n.Toolkit; import com.fr.env.detect.base.DetectorConstants; import com.fr.env.detect.bean.DetectorResult; @@ -10,9 +11,14 @@ import com.fr.env.detect.bean.ExceptionSolution; import com.fr.env.detect.bean.Message; import com.fr.env.detect.bean.SolutionAction; import com.fr.env.detect.thowable.ThrowableConverter; +import com.fr.io.utils.ResourceIOUtils; +import com.fr.log.FineLoggerFactory; +import com.fr.stable.StableUtils; +import com.fr.stable.project.ProjectConstants; import com.fr.third.org.hibernate.exception.GenericJDBCException; import org.jetbrains.annotations.Nullable; +import javax.swing.JOptionPane; import java.util.HashMap; import java.util.Iterator; import java.util.Map; @@ -68,16 +74,29 @@ public class FineDbDirtyConverter implements ThrowableConverter { String tipsLocale = Toolkit.i18nText(detectorType.getTipsLocale(), tableName); String solutionLocale = detectorType.getSolutionLocale(); - ExceptionSolution exceptionSolution = new ExceptionSolution(new Message.Link(solutionLocale, DetectorConstants.FINE_DB_HELP_LINK), new SolutionAction() { + ExceptionSolution exceptionSolution = new ExceptionSolution(new Message.Link(Toolkit.i18nText(solutionLocale, DetectorConstants.FINE_DB_HELP_LINK), DetectorConstants.FINE_DB_HELP_LINK), new SolutionAction() { @Override public String name() { - // todo - return null; + return Toolkit.i18nText("Fine-Design_Basic_Reset_Immediately"); } @Override public void run() { - + boolean success = false; + try { + ResourceIOUtils.copy(StableUtils.pathJoin(ProjectConstants.EMBED_DB_DIRECTORY, ProjectConstants.FINE_DB_NAME), + StableUtils.pathJoin(ProjectConstants.EMBED_DB_DIRECTORY, ProjectConstants.FINE_DB_BAK_NAME)); + success = ResourceIOUtils.delete(StableUtils.pathJoin(ProjectConstants.EMBED_DB_DIRECTORY, ProjectConstants.FINE_DB_NAME)); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + if (!success) { + FineJOptionPane.showMessageDialog(null, + Toolkit.i18nText("Fine-Design_Error_Finedb_Backup_Reset_Result", + ResourceIOUtils.getRealPath(StableUtils.pathJoin(ProjectConstants.EMBED_DB_DIRECTORY, ProjectConstants.FINE_DB_NAME))), + Toolkit.i18nText("Fine-Design_Basic_Error"), + JOptionPane.ERROR_MESSAGE); + } } }); builder.withTips(tipsLocale) diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbLockedConverter.java b/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbLockedConverter.java index 09cc9b62f..22f600104 100644 --- a/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbLockedConverter.java +++ b/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbLockedConverter.java @@ -47,7 +47,7 @@ public class FineDbLockedConverter implements ThrowableConverter { return DetectorResult.builder() .withType(type) .withTips(Toolkit.i18nText(type.getTipsLocale())) - .withSolution(Toolkit.i18nText(type.getSolutionLocale()), DetectorConstants.FINE_DB_HELP_LINK) + .withSolution(Toolkit.i18nText(type.getSolutionLocale(),DetectorConstants.FINE_DB_HELP_LINK), DetectorConstants.FINE_DB_HELP_LINK) .withLog(Toolkit.i18nText(type.getLogLocale())) .build(); } diff --git a/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbPermissionConverter.java b/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbPermissionConverter.java index 8052f8164..00775706d 100644 --- a/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbPermissionConverter.java +++ b/designer-base/src/main/java/com/fr/env/detect/impl/converter/FineDbPermissionConverter.java @@ -42,7 +42,8 @@ public class FineDbPermissionConverter implements ThrowableConverter { return DetectorResult.builder() .withType(type) .withTips(Toolkit.i18nText(type.getTipsLocale())) - .withSolution(Toolkit.i18nText(type.getSolutionLocale()), DetectorConstants.FINE_DB_HELP_LINK) + .withSolution(Toolkit.i18nText(type.getSolutionLocale(), DetectorConstants.FINE_DB_HELP_LINK), + DetectorConstants.FINE_DB_HELP_LINK) .withLog(type.getLogLocale()) .build(); } diff --git a/designer-base/src/main/java/com/fr/env/detect/ui/DetectorErrorDialog.java b/designer-base/src/main/java/com/fr/env/detect/ui/DetectorErrorDialog.java new file mode 100644 index 000000000..96e8dfa3f --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/ui/DetectorErrorDialog.java @@ -0,0 +1,135 @@ +package com.fr.env.detect.ui; + +import com.fr.design.RestartHelper; +import com.fr.design.gui.ibutton.UIButton; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.i18n.Toolkit; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.layout.VerticalFlowLayout; +import com.fr.design.utils.ColorUtils; +import com.fr.design.utils.gui.GUICoreUtils; +import com.fr.env.detect.base.DetectorUtil; +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.ExceptionSolution; +import com.fr.env.detect.bean.ExceptionTips; +import com.fr.env.detect.bean.Message; +import com.fr.exit.DesignerExiter; +import com.fr.general.FRFont; + +import javax.swing.BorderFactory; +import javax.swing.JDialog; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.ScrollPaneConstants; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.Frame; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.List; + +/** + * 未知错误框 + * todo 其实这里可以将多个 error-dialog 抽象在一起的。 时间不够, 简单写写 + * + * created by Harrison on 2022/05/29 + **/ +public class DetectorErrorDialog extends JDialog implements ActionListener { + + private UIButton okButton; + private UIButton restartButton; + + public DetectorErrorDialog(Frame parent, List results) { + + super(parent, true); + + JPanel northPane = FRGUIPaneFactory.createBorderLayout_L_Pane(); + JPanel messagePane = FRGUIPaneFactory.createVerticalFlowLayout_S_Pane(true); + + UILabel boldHeader = new UILabel(Toolkit.i18nText("Fine-Design_Error_Start_Apology_Message")); + Font font = FRFont.getInstance(boldHeader.getFont().getFontName(), Font.BOLD, 20); + boldHeader.setFont(font); + messagePane.add(boldHeader); + + UILabel description = new UILabel(Toolkit.i18nText("Fine-Design_Send_Report_To_Us")); + messagePane.add(description); + northPane.add(messagePane); + + JPanel centerPane = FRGUIPaneFactory.createBorderLayout_L_Pane(); + + UILabel detailDesc = new UILabel(Toolkit.i18nText("Fine-Design_Problem_Detail_Message")); + centerPane.add(detailDesc, BorderLayout.NORTH); + + JPanel detailPanel = FRGUIPaneFactory.createVerticalFlowLayout_Pane(true, VerticalFlowLayout.TOP, 0, 10); + detailPanel.setBorder(BorderFactory.createEmptyBorder(0, 20, 10, 10)); + + for (DetectorResult result : results) { + ExceptionTips tips = result.getTips(); + if (tips != null) { + Message tipsMsg = tips.getMessage(); + detailPanel.add(DetectorUtil.convert2Component(tipsMsg)); + } + ExceptionSolution solution = result.getSolution(); + if (solution != null) { + Message solutionMsg = solution.getMessage(); + detailPanel.add(DetectorUtil.convert2Component(solutionMsg)); + } + } + + JScrollPane detailPanelWrapper = new JScrollPane(detailPanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + ColorUtils.syncBackground(detailPanelWrapper, Color.WHITE); + centerPane.add(detailPanelWrapper, BorderLayout.CENTER); + + JPanel southPane = FRGUIPaneFactory.createBorderLayout_L_Pane(); + JPanel controlPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); + JPanel buttonPane = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 0)); + okButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Ok")); + okButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + okEvent(); + } + }); + buttonPane.add(okButton); + restartButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Restart")); + restartButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + restartEvent(); + } + }); + buttonPane.add(restartButton); + controlPane.add(buttonPane, BorderLayout.EAST); + southPane.add(controlPane); + + this.setTitle(Toolkit.i18nText("Fine-Design_Error_Start_Report")); + this.setResizable(false); + this.add(northPane, BorderLayout.NORTH); + this.add(centerPane, BorderLayout.CENTER); + this.add(southPane, BorderLayout.SOUTH); + this.setSize(new Dimension(600, 500)); + GUICoreUtils.centerWindow(this); + + } + + @Override + public void actionPerformed(ActionEvent e) { + dispose(); + } + + protected void okEvent() { + + dispose(); + DesignerExiter.getInstance().execute(); + } + + protected void restartEvent() { + + dispose(); + RestartHelper.restart(); + } + +} diff --git a/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java index d5a0c45ea..a5cea1323 100644 --- a/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java +++ b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java @@ -1,12 +1,9 @@ package com.fr.env.detect.ui; import com.fr.base.svg.IconUtils; -import com.fr.design.components.notification.NotificationAction; import com.fr.design.components.notification.NotificationDialog; import com.fr.design.components.notification.NotificationDialogProperties; -import com.fr.design.components.notification.NotificationMessage; import com.fr.design.components.notification.NotificationModel; -import com.fr.design.components.notification.NotificationType; import com.fr.design.components.table.TablePanel; import com.fr.design.constants.DesignerColor; import com.fr.design.gui.ibutton.UIButton; @@ -19,14 +16,11 @@ import com.fr.design.ui.util.UIUtil; import com.fr.design.utils.gui.GUICoreUtils; import com.fr.design.utils.gui.GUIPaintUtils; import com.fr.env.detect.base.DetectorBridge; +import com.fr.env.detect.base.DetectorUtil; +import com.fr.env.detect.base.ExceptionDetectorConfig; import com.fr.env.detect.bean.DetectorResult; import com.fr.env.detect.bean.DetectorStatus; import com.fr.env.detect.bean.DetectorType; -import com.fr.env.detect.bean.ExceptionSolution; -import com.fr.env.detect.bean.ExceptionTips; -import com.fr.env.detect.bean.Message; -import com.fr.env.detect.bean.SolutionAction; -import com.fr.third.guava.collect.Lists; import org.jetbrains.annotations.NotNull; import javax.swing.BorderFactory; @@ -45,15 +39,10 @@ import java.awt.Image; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.net.URL; -import java.util.ArrayList; -import java.util.Collection; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Optional; import java.util.concurrent.TimeUnit; -import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -63,6 +52,7 @@ import java.util.stream.Stream; public class EnvDetectorDialog extends JDialog { private static final ImageIcon LOADING_ICON = getLoadingIcon(); + public static final int TIMEOUT = 1000; private JPanel body; @@ -74,7 +64,7 @@ public class EnvDetectorDialog extends JDialog { /* 数据 model */ - private EnvDetectorModel model = new EnvDetectorModel(); + private EnvDetectorModel model; /* 流程 model */ @@ -87,11 +77,25 @@ public class EnvDetectorDialog extends JDialog { private SwingWorker detectWorker = null; + /* config model */ + + private boolean detectOpen = ExceptionDetectorConfig.getInstance().isOpen(); + public EnvDetectorDialog(Frame owner) { + this(owner, null); + } + + public EnvDetectorDialog(Frame owner, EnvDetectorModel model) { super(owner); configProperties(); + if (model == null) { + this.model = new EnvDetectorModel(); + } else { + this.model = model; + } + this.body = FRGUIPaneFactory.createBorderLayout_L_Pane(); Color backgroundColor = new Color(240, 240, 243, 1); this.body.setBackground( backgroundColor); @@ -116,6 +120,8 @@ public class EnvDetectorDialog extends JDialog { GUICoreUtils.centerWindow(this); } + + /* header */ @NotNull private JPanel createHeaderPanel() { @@ -184,12 +190,13 @@ public class EnvDetectorDialog extends JDialog { DetectorType type = item.getType(); // 执行检测, UI-当前在检测中 - Optional detect = UIUtil.waitUntil( + DetectorResult result = UIUtil.waitUntil( () -> DetectorBridge.getInstance().detect(type), - 1000, TimeUnit.MILLISECONDS); - // 获取结果,更新UI - detect.ifPresent(item::setResult); + TIMEOUT, TimeUnit.MILLISECONDS); + // 获取结果 + item.setResult(result); + // 更新UI // 只有还在运行中,才会真正的刷新面板 if (buttonStatus.isExecuting()) { // 在刷新一下面板 @@ -231,6 +238,8 @@ public class EnvDetectorDialog extends JDialog { }); } + /* table */ + @NotNull private TablePanel createTablePanel() { @@ -285,19 +294,6 @@ public class EnvDetectorDialog extends JDialog { } } - private static ImageIcon getLoadingIcon() { - - URL resource = EnvDetectorDialog.class.getResource("/com/fr/design/standard/loading/loading-120.gif"); - if (resource == null) { - return null; - } - ImageIcon loadingIcon = new ImageIcon(resource); - int width = 16; - int height = 16; - loadingIcon.setImage(loadingIcon.getImage().getScaledInstance(width, height, Image.SCALE_DEFAULT)); - return loadingIcon; - } - private Component createResultComponent(DetectorResult result) { JPanel statusPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); @@ -305,89 +301,43 @@ public class EnvDetectorDialog extends JDialog { statusPanel.add(new UILabel(IconUtils.readIcon("/com/fr/design/standard/reminder/reminder_success.svg")), BorderLayout.WEST); statusPanel.add(new UILabel(Toolkit.i18nText("Fine-Design_Basic_Normal")), BorderLayout.CENTER); } else { - statusPanel.add(new UILabel(IconUtils.readIcon("/com/fr/design/standard/reminder/reminder_error.svg")), BorderLayout.WEST); - statusPanel.add(new UILabel(Toolkit.i18nText("Fine-Design_Basic_Exception")), BorderLayout.CENTER); - UILabel detailLabel = new UILabel(Toolkit.i18nText("Fine_Designer_Look_Detail")); - detailLabel.setForeground(new Color(65, 155, 249)); - detailLabel.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent mouseEvent) { - NotificationDialogProperties properties = new NotificationDialogProperties((Frame) EnvDetectorDialog.this.getOwner(), Toolkit.i18nText("Fine-Design_Basic_Detect_Notification_Title")); - Stream results = model.getResults(); - List notificationModels = results - .filter(Objects::nonNull) - .filter((e) -> e.getStatus() == DetectorStatus.EXCEPTION) - .map(EnvDetectorDialog.this::convertDetectorResult) - .collect(Collectors.toList()); - - NotificationDialog dialog = new NotificationDialog(properties, notificationModels); - dialog.open(); - } - }); - statusPanel.add(detailLabel); - } - return statusPanel; - } - - private NotificationModel convertDetectorResult(DetectorResult result) { - - List messages = new ArrayList<>(); - - Function> convert2NotificationMsg = message -> { - - NotificationMessage notificationMessage = null; - if (message != null) { - Message.Type type = message.getType(); - switch (type) { - case SIMPLE: - notificationMessage = (new NotificationMessage.SimpleMessage(message.get())); - break; - case LINK: - Message.Link linkMsg = (Message.Link) message; - notificationMessage = new NotificationMessage.LinkMessage(linkMsg.getText(), linkMsg.getLink()); - break; - default: - break; - } + JPanel infoPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + { + infoPanel.add(new UILabel(IconUtils.readIcon("/com/fr/design/standard/reminder/reminder_error.svg")), BorderLayout.WEST); + infoPanel.add(new UILabel(Toolkit.i18nText("Fine-Design_Basic_Exception")), BorderLayout.CENTER); } - return Optional.ofNullable(notificationMessage); - }; - - ExceptionTips tips = result.getTips(); - convert2NotificationMsg - .apply(tips.getMessage()) - .ifPresent(messages::add); - - ExceptionSolution solution = result.getSolution(); - convert2NotificationMsg.apply(solution.getMessage()) - .ifPresent(messages::add); - - NotificationAction notificationAction = null; - SolutionAction solutionAction = solution.getAction(); - if (solutionAction != null) { - notificationAction = new NotificationAction() { - @Override - public String name() { - return solutionAction.name(); - } - - @Override - public void run(Object... args) { - solutionAction.run(); - } - }; + statusPanel.add(infoPanel, BorderLayout.WEST); + + JPanel detailPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + { + detailPanel.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0)); + UILabel detailLabel = new UILabel(Toolkit.i18nText("Fine_Designer_Look_Detail")); + detailLabel.setForeground(new Color(65, 155, 249)); + detailLabel.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent mouseEvent) { + NotificationDialogProperties properties = new NotificationDialogProperties((Frame) EnvDetectorDialog.this.getOwner(), Toolkit.i18nText("Fine-Design_Basic_Detect_Notification_Title")); + Stream results = model.getResults(); + List notificationModels = results + .filter(Objects::nonNull) + .filter((e) -> e.getStatus() == DetectorStatus.EXCEPTION) + .map(DetectorUtil::convert2Notification) + .collect(Collectors.toList()); + + NotificationDialog dialog = new NotificationDialog(properties, notificationModels); + dialog.open(); + } + }); + detailPanel.add(detailLabel, BorderLayout.CENTER); + } + + statusPanel.add(detailPanel, BorderLayout.CENTER); } - - return new NotificationModel(NotificationType.WARNING, notificationAction, messages); - } - - private void refresh() { - - updateTable(this.tablePanel); - pack(); - repaint(); + return statusPanel; } + /* tail */ + @NotNull private JPanel createTailPanel() { @@ -397,6 +347,11 @@ public class EnvDetectorDialog extends JDialog { JPanel configPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); { UICheckBox checkBox = new UICheckBox(); + checkBox.setSelected(this.detectOpen); + checkBox.addChangeListener(e -> { + UICheckBox source = (UICheckBox) e.getSource(); + EnvDetectorDialog.this.detectOpen = source.isSelected(); + }); configPanel.add(checkBox, BorderLayout.WEST); UILabel description = new UILabel(Toolkit.i18nText("Fine-Design_Basic_Detect_Switch")); configPanel.add(description, BorderLayout.EAST); @@ -411,7 +366,7 @@ public class EnvDetectorDialog extends JDialog { setVisible(false); dispose(); // 配置处理 - // todo + ExceptionDetectorConfig.getInstance().setOpen(this.detectOpen); }); actionsPanel.add(confirmButton, BorderLayout.WEST); @@ -426,89 +381,33 @@ public class EnvDetectorDialog extends JDialog { return tailPanel; } - private void configProperties() { - - setTitle(Toolkit.i18nText("Fine-Design_Basic_Detect_Title")); - setModal(false); - setFocusable(false); - setAutoRequestFocus(false); - setResizable(false); - } - - /** - * 数据 model - * - * kind-list_item - */ - private class EnvDetectorModel { - - private Map> itemMap = new LinkedHashMap<>(); - - public EnvDetectorModel() { - - itemMap.put(DetectorType.Kind.JAR, Lists.newArrayList(new EnvDetectorItem(DetectorType.LACK_OF_JAR), new EnvDetectorItem(DetectorType.JAR_IN_CONSISTENCE), new EnvDetectorItem(DetectorType.JAR_CONFLICT))); - - itemMap.put(DetectorType.Kind.FINE_DB, Lists.newArrayList(new EnvDetectorItem(DetectorType.FINE_DB_LOCKED), new EnvDetectorItem(DetectorType.FINE_DB_PERMISSION), new EnvDetectorItem(DetectorType.FINE_DB_DIRTY))); - } - - public void update(DetectorType type, DetectorResult result) { + private void refresh() { - List items = itemMap.get(type.getKind()); - items.stream() - .filter((e) -> e.getType() == type) - .findFirst() - .ifPresent((e) -> {e.setResult(result);}); - } - - public Stream getResults() { + updateTable(this.tablePanel); + pack(); + repaint(); + } - return getItems().stream() - .map(EnvDetectorItem::getResult); - } - - public List getItems() { - - return itemMap.values().stream() - .flatMap(Collection::stream) - .collect(Collectors.toList()); - } + private static ImageIcon getLoadingIcon() { - public Map> getItemMap() { - - return this.itemMap; + URL resource = EnvDetectorDialog.class.getResource("/com/fr/design/standard/loading/loading-120.gif"); + if (resource == null) { + return null; } - + ImageIcon loadingIcon = new ImageIcon(resource); + int width = 16; + int height = 16; + loadingIcon.setImage(loadingIcon.getImage().getScaledInstance(width, height, Image.SCALE_DEFAULT)); + return loadingIcon; } - private class EnvDetectorItem { - - private DetectorType type; - - private DetectorResult result; + private void configProperties() { - public EnvDetectorItem(DetectorType detectorType) { - this.type = detectorType; - } - - public DetectorType getType() { - return type; - } - - public String getKind() { - return this.type.getKind().getDescription(); - } - - public String getDescription() { - return this.type.getDescription(); - } - - public DetectorResult getResult() { - return result; - } - - public void setResult(DetectorResult result) { - this.result = result; - } + setTitle(Toolkit.i18nText("Fine-Design_Basic_Detect_Title")); + setModal(false); + setFocusable(false); + setAutoRequestFocus(false); + setResizable(false); } /** diff --git a/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorItem.java b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorItem.java new file mode 100644 index 000000000..6b5b1159c --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorItem.java @@ -0,0 +1,38 @@ +package com.fr.env.detect.ui; + +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.DetectorType; + +/** + * created by Harrison on 2022/05/27 + **/ +public class EnvDetectorItem { + + private DetectorType type; + + private DetectorResult result; + + public EnvDetectorItem(DetectorType detectorType) { + this.type = detectorType; + } + + public DetectorType getType() { + return type; + } + + public String getKind() { + return this.type.getKind().getDescription(); + } + + public String getDescription() { + return this.type.getDescription(); + } + + public DetectorResult getResult() { + return result; + } + + public void setResult(DetectorResult result) { + this.result = result; + } +} diff --git a/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorModel.java b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorModel.java new file mode 100644 index 000000000..8a698f3c3 --- /dev/null +++ b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorModel.java @@ -0,0 +1,59 @@ +package com.fr.env.detect.ui; + +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.DetectorType; +import com.fr.third.guava.collect.Lists; + +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * created by Harrison on 2022/05/27 + **/ +public class EnvDetectorModel { + + private Map> itemMap = new LinkedHashMap<>(); + + public EnvDetectorModel() { + + itemMap.put(DetectorType.Kind.JAR, Lists.newArrayList(new EnvDetectorItem(DetectorType.LACK_OF_JAR), new EnvDetectorItem(DetectorType.JAR_IN_CONSISTENCE), new EnvDetectorItem(DetectorType.JAR_CONFLICT))); + + itemMap.put(DetectorType.Kind.FINE_DB, Lists.newArrayList(new EnvDetectorItem(DetectorType.FINE_DB_LOCKED), new EnvDetectorItem(DetectorType.FINE_DB_PERMISSION), new EnvDetectorItem(DetectorType.FINE_DB_DIRTY))); + } + + public void update(DetectorType type, DetectorResult result) { + + List items = itemMap.get(type.getKind()); + items.stream() + .filter((e) -> e.getType() == type) + .findFirst() + .ifPresent((e) -> { + e.setResult(result); + }); + } + + public Stream getResults() { + + return getItems().stream() + .map(EnvDetectorItem::getResult); + } + + public List getItems() { + + return itemMap.values().stream() + .flatMap(Collection::stream) + .collect(Collectors.toList()); + } + + public Map> getItemMap() { + + return this.itemMap; + } + +} + + From 4f0fe26f5c93447f1d8268e66343dd3abbaef334 Mon Sep 17 00:00:00 2001 From: Harrison Date: Sun, 29 May 2022 15:45:22 +0800 Subject: [PATCH 22/85] =?UTF-8?q?feat:=20REPORT-70565=20=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=99=A8=E7=8E=AF=E5=A2=83=E7=9B=91=E6=B5=8B=EF=BC=88jar?= =?UTF-8?q?=E5=8C=85=E5=BC=82=E5=B8=B8=E3=80=81finedb=E3=80=81=E6=9D=80?= =?UTF-8?q?=E6=AF=92=E8=BD=AF=E4=BB=B6=EF=BC=89=201-=E4=BC=98=E5=8C=96=20U?= =?UTF-8?q?I=20=E9=83=A8=E5=88=86=E7=9A=84=E4=BB=A3=E7=A0=81=E8=B4=A8?= =?UTF-8?q?=E9=87=8F=202-=E6=8A=BD=E8=B1=A1=E9=83=A8=E5=88=86=E5=85=AC?= =?UTF-8?q?=E5=85=B1=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notification/NotificationDialog.java | 9 ++- .../notification/NotificationMessage.java | 4 +- .../design/components/table/TablePanel.java | 8 +- .../design/dialog/link/MessageWithLink.java | 39 +++++++--- .../java/com/fr/design/utils/ColorUtils.java | 30 ++++++++ .../java/com/fr/design/utils/DevUtil.java | 49 ------------- .../java/com/fr/design/utils/DevUtils.java | 73 +++++++++++++++++++ .../{LinkStrUtil.java => LinkStrUtils.java} | 2 +- 8 files changed, 146 insertions(+), 68 deletions(-) create mode 100644 designer-base/src/main/java/com/fr/design/utils/ColorUtils.java delete mode 100644 designer-base/src/main/java/com/fr/design/utils/DevUtil.java create mode 100644 designer-base/src/main/java/com/fr/design/utils/DevUtils.java rename designer-base/src/main/java/com/fr/design/utils/{LinkStrUtil.java => LinkStrUtils.java} (98%) diff --git a/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java b/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java index f7182ddca..42dac12c9 100644 --- a/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java +++ b/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java @@ -9,6 +9,8 @@ import com.fr.design.gui.ibutton.UIButton; import com.fr.design.gui.ilable.UILabel; import com.fr.design.i18n.Toolkit; import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.layout.VerticalFlowLayout; +import com.fr.env.detect.base.ExceptionDetectorConfig; import javax.swing.BorderFactory; import javax.swing.Icon; @@ -22,7 +24,6 @@ import java.awt.Color; import java.awt.Container; import java.awt.Desktop; import java.awt.Dimension; -import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; @@ -149,8 +150,7 @@ public class NotificationDialog extends JDialog { .collect(Collectors.toList()); // 竖向排列 - JPanel messageSummaryPanel = new JPanel(); - messageSummaryPanel.setLayout(new GridLayout(messageComponents.size(), 1)); + JPanel messageSummaryPanel = FRGUIPaneFactory.createVerticalFlowLayout_Pane(true, VerticalFlowLayout.TOP, 0, 0); messageComponents.forEach(messageSummaryPanel::add); JPanel centerPanel = FRGUIPaneFactory.createBorderLayout_L_Pane(); @@ -210,7 +210,8 @@ public class NotificationDialog extends JDialog { notReminder.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { - // todo + // 配置处理 + ExceptionDetectorConfig.getInstance().setOpen(false); // 点击事件 destroy(); } diff --git a/designer-base/src/main/java/com/fr/design/components/notification/NotificationMessage.java b/designer-base/src/main/java/com/fr/design/components/notification/NotificationMessage.java index 26dba1297..c107a0fb8 100644 --- a/designer-base/src/main/java/com/fr/design/components/notification/NotificationMessage.java +++ b/designer-base/src/main/java/com/fr/design/components/notification/NotificationMessage.java @@ -1,6 +1,6 @@ package com.fr.design.components.notification; -import com.fr.design.utils.LinkStrUtil; +import com.fr.design.utils.LinkStrUtils; /** * created by Harrison on 2022/05/24 @@ -76,7 +76,7 @@ public interface NotificationMessage { @Override public String format() { - return LinkStrUtil.generateHtmlTag(text); + return LinkStrUtils.generateHtmlTag(text); } @Override diff --git a/designer-base/src/main/java/com/fr/design/components/table/TablePanel.java b/designer-base/src/main/java/com/fr/design/components/table/TablePanel.java index a1a9492f8..317a17195 100644 --- a/designer-base/src/main/java/com/fr/design/components/table/TablePanel.java +++ b/designer-base/src/main/java/com/fr/design/components/table/TablePanel.java @@ -2,6 +2,7 @@ package com.fr.design.components.table; import com.fr.design.gui.ilable.UILabel; import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.utils.ColorUtils; import com.fr.third.org.apache.commons.lang3.ArrayUtils; import javax.swing.BorderFactory; @@ -152,6 +153,8 @@ public class TablePanel extends JPanel { int x = row - 1; int y = column - 1; + syncCellColor(row, component); + JPanel cellPanel = this.cellPanels[x][y]; if (ArrayUtils.getLength(cellPanel.getComponents()) == 1) { cellPanel.remove(0); @@ -167,13 +170,14 @@ public class TablePanel extends JPanel { } private void syncHeaderColor(Component component) { - - component.setBackground(DEFAULT_HEADER_COLOR); + + ColorUtils.syncBackground(component, DEFAULT_HEADER_COLOR); } private void syncCellColor(int row, Component component) { Color rowColor = getRowColorByRowNumber(row); + ColorUtils.syncBackground(component, rowColor); component.setBackground(rowColor); } diff --git a/designer-base/src/main/java/com/fr/design/dialog/link/MessageWithLink.java b/designer-base/src/main/java/com/fr/design/dialog/link/MessageWithLink.java index a6746d227..0d6bd2348 100644 --- a/designer-base/src/main/java/com/fr/design/dialog/link/MessageWithLink.java +++ b/designer-base/src/main/java/com/fr/design/dialog/link/MessageWithLink.java @@ -1,18 +1,18 @@ package com.fr.design.dialog.link; -import com.fr.design.utils.LinkStrUtil; +import com.fr.design.utils.LinkStrUtils; import com.fr.log.FineLoggerFactory; import com.fr.stable.StringUtils; import javax.swing.JEditorPane; import javax.swing.event.HyperlinkEvent; -import javax.swing.event.HyperlinkListener; import java.awt.Color; import java.awt.Desktop; import java.awt.Font; import java.net.URI; +import java.net.URL; -import static com.fr.design.utils.LinkStrUtil.LABEL; +import static com.fr.design.utils.LinkStrUtils.LABEL; /** * 用来构建JOptionPane带超链的消息提示 @@ -22,6 +22,14 @@ import static com.fr.design.utils.LinkStrUtil.LABEL; * Created by hades on 2020/10/23 */ public class MessageWithLink extends JEditorPane { + + public MessageWithLink(String htmlText) { + + super("text/html", htmlText); + + setEditable(false); + setBorder(null); + } public MessageWithLink(String htmlText, Runnable action) { @@ -57,20 +65,31 @@ public class MessageWithLink extends JEditorPane { public MessageWithLink(String frontMessage, String linkName, String link, String backMessage, Color backgroundColor, Font font, Color fontColor) { - super("text/html", "" + frontMessage + "" + linkName + "" + backMessage + ""); + super("text/html", "" + frontMessage + "" + linkName + "" + backMessage + ""); initListener(link); setEditable(false); setBorder(null); } + public void initListener() { + + addHyperlinkListener(hyperlinkEvent -> { + try { + if (hyperlinkEvent.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED)) { + URL url = hyperlinkEvent.getURL(); + Desktop.getDesktop().browse(url.toURI()); + } + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + }); + } + public void initListener(Runnable runnable) { - addHyperlinkListener(new HyperlinkListener() { - @Override - public void hyperlinkUpdate(HyperlinkEvent e) { - if (e.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED)) { - runnable.run(); - } + addHyperlinkListener(e -> { + if (e.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED)) { + runnable.run(); } }); } diff --git a/designer-base/src/main/java/com/fr/design/utils/ColorUtils.java b/designer-base/src/main/java/com/fr/design/utils/ColorUtils.java new file mode 100644 index 000000000..739d39472 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/utils/ColorUtils.java @@ -0,0 +1,30 @@ +package com.fr.design.utils; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.util.Arrays; + +/** + * created by Harrison on 2022/05/29 + **/ +public class ColorUtils { + + /** + * 递归的同步颜色 + * + * @param color 颜色 + */ + public static void syncBackground(Component component, Color color) { + + component.setBackground(color); + + if (component instanceof Container) { + Container container = (Container) component; + Component[] components = container.getComponents(); + if (components != null) { + Arrays.stream(components).forEach((e) -> syncBackground(e, color)); + } + } + } +} diff --git a/designer-base/src/main/java/com/fr/design/utils/DevUtil.java b/designer-base/src/main/java/com/fr/design/utils/DevUtil.java deleted file mode 100644 index caf6fc966..000000000 --- a/designer-base/src/main/java/com/fr/design/utils/DevUtil.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.fr.design.utils; - -import com.fr.design.ui.util.UIUtil; -import com.fr.env.detect.ui.EnvDetectorDialog; - -import javax.swing.JFrame; -import java.awt.Dimension; -import java.awt.Frame; -import java.awt.Toolkit; -import java.util.function.Consumer; - -/** - * 设计器的开发时工具 - * 帮助进行 UI 页面的调试 - * - * created by Harrison on 2022/05/23 - **/ -public class DevUtil { - - public static void show(Consumer consumer) { - - DesignUtils.initLookAndFeel(); - - UIUtil.invokeLaterIfNeeded(() -> { - - JFrame frame = new JFrame("dev-util"); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - Dimension dimension = Toolkit.getDefaultToolkit().getScreenSize(); - frame.setSize(dimension); - - consumer.accept(frame); - - frame.setVisible(true); - }); - - } - - public static void main(String[] args) { - - DevUtil.show(new Consumer() { - @Override - public void accept(Frame frame) { - - EnvDetectorDialog envDetectorDialog = new EnvDetectorDialog(frame); - envDetectorDialog.setVisible(true); - } - }); - } -} diff --git a/designer-base/src/main/java/com/fr/design/utils/DevUtils.java b/designer-base/src/main/java/com/fr/design/utils/DevUtils.java new file mode 100644 index 000000000..85918f84b --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/utils/DevUtils.java @@ -0,0 +1,73 @@ +package com.fr.design.utils; + +import com.fr.config.dao.DaoContext; +import com.fr.config.dao.impl.LocalClassHelperDao; +import com.fr.config.dao.impl.LocalEntityDao; +import com.fr.config.dao.impl.LocalXmlEntityDao; +import com.fr.design.ui.util.UIUtil; +import com.fr.env.detect.bean.DetectorResult; +import com.fr.env.detect.bean.DetectorType; +import com.fr.env.detect.bean.ExceptionLog; +import com.fr.env.detect.bean.ExceptionSolution; +import com.fr.env.detect.bean.ExceptionTips; +import com.fr.env.detect.ui.DetectorErrorDialog; +import com.fr.third.guava.collect.Lists; +import com.fr.transaction.Configurations; +import com.fr.transaction.LocalConfigurationHelper; + +import javax.swing.JFrame; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Toolkit; +import java.util.function.Consumer; + +/** + * 设计器的开发时工具 + * 帮助进行 UI 页面的调试 + *