diff --git a/designer-base/src/main/java/com/fr/design/actions/UpdateAction.java b/designer-base/src/main/java/com/fr/design/actions/UpdateAction.java index 048f233373..d33c04eb15 100644 --- a/designer-base/src/main/java/com/fr/design/actions/UpdateAction.java +++ b/designer-base/src/main/java/com/fr/design/actions/UpdateAction.java @@ -185,12 +185,24 @@ public abstract class UpdateAction extends ShortCut implements Action { * @param resource 图标资源路径 */ public void setSmallIcon(String resource) { + setSmallIcon(resource, true); + } + + /** + * 使用传入资源url的方式设置Icon,会自动设置_normal.svg,然后通过needDisable参数判断是否需要自动设置_disable.svg + * 因为有些地方是不需要设置灰化图标的,所以不存在灰化图标资源,这边如果一并设置,就会报错文件找不到 + * @param resource + * @param needDisable + */ + public void setSmallIcon(String resource, boolean needDisable) { if (StringUtils.equals(resource, StringUtils.EMPTY)) { this.putValue(Action.SMALL_ICON, null); return; } this.putValue(Action.SMALL_ICON, IconUtils.readIcon(resource)); - this.putValue(UpdateAction.DISABLED_ICON, IconUtils.readSVGIcon(resource, IconUtils.ICON_TYPE_DISABLED)); + if (needDisable) { + this.putValue(UpdateAction.DISABLED_ICON, IconUtils.readSVGIcon(resource, IconUtils.ICON_TYPE_DISABLED)); + } } public void setSmallIcon(Icon[] smallIcon, boolean white) { diff --git a/designer-base/src/main/java/com/fr/design/constants/UIConstants.java b/designer-base/src/main/java/com/fr/design/constants/UIConstants.java index b10d3f9e52..850a6b5aac 100644 --- a/designer-base/src/main/java/com/fr/design/constants/UIConstants.java +++ b/designer-base/src/main/java/com/fr/design/constants/UIConstants.java @@ -150,6 +150,7 @@ public interface UIConstants { public static final Color LIST_ITEM_SPLIT_LINE = new Color(0xf0f0f3); public static final Color DESIGNER_LOGIN_BACKGROUND = new Color(0xf1ad14); public static final Color DESIGNER_LOGIN_BACKGROUND_ONCLICK = new Color(0xd89600); + public static final Color CHECK_BOX_TIP_FONT_COLOR = new Color(51, 51, 52, (int)Math.round(0.5 * 255)); public static final BufferedImage DRAG_BAR = IOUtils.readImage("com/fr/design/images/control/bar.png"); public static final BufferedImage DRAG_BAR_LIGHT = IOUtils.readImage("com/fr/design/images/control/bar-light.png"); diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/preview/PreviewTableModel.java b/designer-base/src/main/java/com/fr/design/data/datapane/preview/PreviewTableModel.java index abbe3a7e08..91c8506b79 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/preview/PreviewTableModel.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/preview/PreviewTableModel.java @@ -26,6 +26,8 @@ public class PreviewTableModel extends AbstractTableModel { public IntList dateIndexs = new IntList(4); + public PreviewTableModel() {} + public PreviewTableModel(int maxRowCount) { // peter:默认必须显示错误的数据源. this(new ErrorResultSet(), maxRowCount); diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/preview/PreviewTablePane.java b/designer-base/src/main/java/com/fr/design/data/datapane/preview/PreviewTablePane.java index 5926dcb2df..6665c6f772 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/preview/PreviewTablePane.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/preview/PreviewTablePane.java @@ -3,28 +3,39 @@ */ package com.fr.design.data.datapane.preview; -import com.fr.base.BaseUtils; import com.fr.base.TableData; +import com.fr.base.svg.IconUtils; import com.fr.data.TableDataSource; +import com.fr.data.desensitize.base.AbstractDesensitizationTableData; import com.fr.data.impl.DBTableData; import com.fr.data.impl.EmbeddedTableData; import com.fr.data.impl.NameDataModel; import com.fr.data.operator.DataOperator; import com.fr.design.DesignerEnvManager; +import com.fr.design.constants.UIConstants; import com.fr.design.data.DesignTableDataManager; +import com.fr.design.data.datapane.preview.desensitization.TableDataPreviewDesensitizeManager; +import com.fr.design.data.datapane.preview.desensitization.model.DesensitizedPreviewTableModel; +import com.fr.design.data.datapane.preview.desensitization.view.setting.TableDataDesensitizationSettingPane; import com.fr.design.dialog.BasicDialog; import com.fr.design.dialog.BasicPane; +import com.fr.design.dialog.DialogActionAdapter; import com.fr.design.dialog.FineJOptionPane; import com.fr.design.file.HistoryTemplateListCache; import com.fr.design.gui.frpane.UITabbedPane; +import com.fr.design.gui.ibutton.UIToggleButton; import com.fr.design.gui.ilable.UILabel; import com.fr.design.gui.iprogressbar.AutoProgressBar; import com.fr.design.gui.itable.SortableJTable; import com.fr.design.gui.itable.TableSorter; import com.fr.design.gui.itextfield.UINumberField; +import com.fr.design.gui.itoolbar.UIToolbar; import com.fr.design.i18n.Toolkit; import com.fr.design.layout.FRGUIPaneFactory; import com.fr.design.mainframe.DesignerContext; +import com.fr.design.mainframe.JTemplate; +import com.fr.design.menu.SeparatorDef; +import com.fr.design.menu.ToolBarDef; import com.fr.design.ui.util.UIUtil; import com.fr.function.TIME; import com.fr.general.FRFont; @@ -39,6 +50,7 @@ import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; +import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import javax.swing.UIManager; import javax.swing.table.DefaultTableCellRenderer; @@ -57,12 +69,19 @@ import java.sql.Timestamp; import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.Objects; import java.util.concurrent.CancellationException; /** * august: PreviewTablePane一共提供5个共有的静态方法,用来预览。 */ public class PreviewTablePane extends BasicPane { + + private static final String DATA_DESENSITIZATION_CONFIG = Toolkit.i18nText("Fine-Design_Report_Desensitization_Config"); + private static final String LEFT_BRACKET = "("; + private static final String RIGHT_BRACKET = ")"; + private static final String COUNT = Toolkit.i18nText("Fine-Design_Report_Desensitization_Count"); + private TableData tableData; private DataModel dataModel; private UINumberField maxPreviewNumberField; @@ -78,6 +97,54 @@ public class PreviewTablePane extends BasicPane { private static PreviewTablePane THIS; private EmbeddedTableData previewTableData; + private UILabel desensitizationLabel; + + /** + * 用于refreshLabel的鼠标监听 + */ + private final MouseAdapter refreshLabelMouseAdapter = new MouseAdapter() { + boolean mouseEntered = false; + boolean buttonPressed = false; + + @Override + public void mouseEntered(MouseEvent e) { // 当鼠标进入时候调用. + mouseEntered = true; + if (!buttonPressed) { + refreshLabel.setBackground(java.awt.Color.WHITE); + refreshLabel.setOpaque(true); + refreshLabel.setBorder(BorderFactory.createLineBorder(java.awt.Color.GRAY)); + } + } + + @Override + public void mouseExited(MouseEvent e) { + mouseEntered = false; + refreshLabel.setOpaque(false); + refreshLabel.setBorder(BorderFactory.createEmptyBorder()); + } + + @Override + public void mousePressed(MouseEvent e) { + buttonPressed = true; + refreshLabel.setBackground(java.awt.Color.lightGray); + } + + @Override + public void mouseReleased(MouseEvent e) { + buttonPressed = false; + if (mouseEntered) { + refreshLabel.setBackground(java.awt.Color.WHITE); + try { + populate(tableData); + if (dataModel != null) { + setRowsLimitTableModel(); + } + } catch (Exception ignore) { + } + } + } + }; + public static final PreviewTablePane getInstance() { if (THIS == null) { THIS = new PreviewTablePane(); @@ -87,91 +154,175 @@ public class PreviewTablePane extends BasicPane { private PreviewTablePane() { this.setLayout(FRGUIPaneFactory.createBorderLayout()); + // northPane + this.add(initNorthPane(), BorderLayout.NORTH); + // centerPane + this.add(initCenterPane(), BorderLayout.CENTER); + // dialog + initDialog(); + // progressBar + initProgressBar(); + } - // elalke:预览行数 - JPanel previewNumberPanel = FRGUIPaneFactory.createNormalFlowInnerContainer_S_Pane(); - this.add(previewNumberPanel, BorderLayout.NORTH); + /** + * 初始化northPane + * + * @return + */ + private JComponent initNorthPane() { + JPanel northPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); + // 预览行数面板 + northPane.add(initPreviewNumberPane(), BorderLayout.CENTER); + // 脱敏预览设置面板 + northPane.add(initDesensitizationPane(), BorderLayout.EAST); + return northPane; + } + /** + * 初始化预览行数面板 + * + * @return + */ + private JComponent initPreviewNumberPane() { + JPanel previewNumberPanel = FRGUIPaneFactory.createNormalFlowInnerContainer_S_Pane(); + // 当前行数 JPanel currentPreviewPanel = FRGUIPaneFactory.createNormalFlowInnerContainer_S_Pane(); - previewNumberPanel.add(currentPreviewPanel); currentPreviewPanel.add(new UILabel(Toolkit.i18nText("Fine-Design_Basic_Current_Preview_Rows") + ":")); - currentRowsField = new UINumberField(); currentPreviewPanel.add(currentRowsField); currentRowsField.setEditable(false); currentRowsField.setColumns(4); currentRowsField.setInteger(true); - + // 最大行数 JPanel maxPanel = FRGUIPaneFactory.createNormalFlowInnerContainer_S_Pane(); - previewNumberPanel.add(maxPanel); maxPanel.add(new UILabel(Toolkit.i18nText("Fine-Design_Basic_Datasource_Maximum_Number_of_Preview_Rows") + ":")); - maxPreviewNumberField = new UINumberField(); maxPanel.add(maxPreviewNumberField); maxPreviewNumberField.setColumns(4); maxPreviewNumberField.setInteger(true); - - DesignerEnvManager designerEnvManager = DesignerEnvManager.getEnvManager(); - maxPreviewNumberField.setValue(designerEnvManager.getMaxNumberOrPreviewRow()); - + maxPreviewNumberField.setValue(DesignerEnvManager.getEnvManager().getMaxNumberOrPreviewRow()); maxPreviewNumberField.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent evt) { DesignerEnvManager designerEnvManager = DesignerEnvManager.getEnvManager(); designerEnvManager.setMaxNumberOrPreviewRow((int) ((UINumberField) evt.getSource()).getValue()); } }); + // 刷新按钮 + initRefreshLabel(); - Icon refreshImage = BaseUtils.readIcon("/com/fr/design/images/control/refresh.png"); - refreshLabel = new UILabel(refreshImage); + previewNumberPanel.add(currentPreviewPanel); + previewNumberPanel.add(maxPanel); previewNumberPanel.add(refreshLabel); - refreshLabel.addMouseListener(new MouseAdapter() { - boolean mouseEntered = false; - boolean buttonPressed = false; - - public void mouseEntered(MouseEvent e) { // 当鼠标进入时候调用. - mouseEntered = true; - if (!buttonPressed) { - refreshLabel.setBackground(java.awt.Color.WHITE); - refreshLabel.setOpaque(true); - refreshLabel.setBorder(BorderFactory.createLineBorder(java.awt.Color.GRAY)); - } - } - - public void mouseExited(MouseEvent e) { - mouseEntered = false; - refreshLabel.setOpaque(false); - refreshLabel.setBorder(BorderFactory.createEmptyBorder()); - } + return previewNumberPanel; + } - public void mousePressed(MouseEvent e) { - buttonPressed = true; - refreshLabel.setBackground(java.awt.Color.lightGray); - } + private void initRefreshLabel() { + Icon refreshImage = IconUtils.readIcon("/com/fr/design/images/control/refresh"); + refreshLabel = new UILabel(refreshImage); + refreshLabel.addMouseListener(refreshLabelMouseAdapter); + } - public void mouseReleased(MouseEvent e) { - buttonPressed = false; - if (mouseEntered) { - refreshLabel.setBackground(java.awt.Color.WHITE); - try { - populate(tableData); - if (dataModel != null) { - setRowsLimitTableModel(); + /** + * 初始化脱敏设置面板 + * + * @return + */ + private JComponent initDesensitizationPane() { + JPanel desensitizationPane = FRGUIPaneFactory.createNormalFlowInnerContainer_S_Pane(); + // 初始化Label + desensitizationLabel = new UILabel(DATA_DESENSITIZATION_CONFIG); + desensitizationLabel.setForeground(UIConstants.NORMAL_BLUE); + desensitizationLabel.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + TableDataDesensitizationSettingPane settingPane = new TableDataDesensitizationSettingPane((AbstractDesensitizationTableData) tableData); + settingPane.populateBean((AbstractDesensitizationTableData) tableData); + BasicDialog dialog = settingPane.showWindowWithCustomSize(SwingUtilities.getWindowAncestor(PreviewTablePane.this), new DialogActionAdapter() { + @Override + public void doOk() { + // 保存脱敏规则配置 + settingPane.updateBean(); + // 改变模板保存状态 + JTemplate editingTemplate = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); + if (Objects.nonNull(editingTemplate)) { + editingTemplate.fireTargetModified(true); } - } catch (Exception e1) { + // 刷新预览页面 + refreshTable(); } - } + + @Override + public void doCancel() { + + } + }, BasicDialog.DEFAULT); + dialog.setVisible(true); } }); + // 初始化分隔符 + ToolBarDef toolbarDef = new ToolBarDef(); + toolbarDef.addShortCut(SeparatorDef.DEFAULT); + UIToolbar toolBar = ToolBarDef.createJToolBar(); + toolBar.setBorderPainted(false); + toolbarDef.updateToolBar(toolBar); + + // 初始化预览按钮 + UIToggleButton previewToggle = new UIToggleButton(new Icon[]{IconUtils.readIcon("/com/fr/design/images/m_file/preview"), IconUtils.readIcon("/com/fr/design/images/m_file/preview")}, true); + previewToggle.setToolTipText(Toolkit.i18nText("Fine-Design_Report_Desensitization_Preview")); + previewToggle.setSelected(false); + previewToggle.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + // 切换TableModel的脱敏状态 + togglePreviewTableModelDesensitizeStatus(); + // 刷新页面,展示 + refreshTable(); + } + }); + + desensitizationPane.add(desensitizationLabel); + desensitizationPane.add(toolBar); + desensitizationPane.add(previewToggle); + return desensitizationPane; + } + + /** + * 设置脱敏设置的个数 + * + * @param model + */ + private void setDesensitizationCount(TableModel model) { + if (isDesensitizeOpened()) { + int count = model instanceof DesensitizedPreviewTableModel ? ((DesensitizedPreviewTableModel) model).getDesensitizeColumnsCount() : 0; + desensitizationLabel.setText(DATA_DESENSITIZATION_CONFIG + LEFT_BRACKET + count + RIGHT_BRACKET + COUNT); + } else { + desensitizationLabel.setText(DATA_DESENSITIZATION_CONFIG); + } + } + + /** + * 初始化centerPane + * + * @return + */ + private JComponent initCenterPane() { preveiwTable = new CopyableJTable(new TableSorter()); preveiwTable.setRowSelectionAllowed(false); preveiwTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); + return new JScrollPane(preveiwTable); + } - this.add(new JScrollPane(preveiwTable), BorderLayout.CENTER); + private void initDialog() { if (this.dialog == null) { this.dialog = this.showWindow(new JFrame()); } + } + + private void initProgressBar() { progressBar = new AutoProgressBar(this, Toolkit.i18nText("Fine-Design_Basic_Loading_Data"), "", 0, 100) { + @Override public void doMonitorCanceled() { if (getWorker() != null) { getWorker().cancel(true); @@ -182,7 +333,7 @@ public class PreviewTablePane extends BasicPane { } public AutoProgressBar getProgressBar() { - return this.progressBar; + return PreviewTablePane.progressBar; } @Override @@ -222,13 +373,19 @@ public class PreviewTablePane extends BasicPane { return this.worker; } - // elake:为预览表的columnIndex列着c色. + /** + * 为预览表的columnIndex列着色. + * + * @param columnIndex 列索引值 + * @param c 颜色 + */ private void setPreviewTableColumnColor(final int columnIndex, final Color c) { addLoadedListener(new LoadedEventListener() { @Override public void fireLoaded() { TableColumn column = preveiwTable.getColumnModel().getColumn(columnIndex); DefaultTableCellRenderer cellRenderer = new DefaultTableCellRenderer() { + @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { JComponent comp = (JComponent) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); comp.setBackground(c); @@ -249,7 +406,7 @@ public class PreviewTablePane extends BasicPane { getInstance().preveiwTable = new SortableJTable(new TableSorter()); getInstance().preveiwTable.setRowSelectionAllowed(false); getInstance().preveiwTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); - getInstance().progressBar.close(); + PreviewTablePane.progressBar.close(); getInstance().repaint(); } @@ -331,6 +488,7 @@ public class PreviewTablePane extends BasicPane { private void previewTableDataSQL() throws Exception { connectionBar = new AutoProgressBar(this, Toolkit.i18nText("Fine-Design_Basic_Utils_Now_Create_Connection"), "", 0, 100) { + @Override public void doMonitorCanceled() { getWorker().cancel(true); getDialog().setVisible(false); @@ -345,42 +503,49 @@ public class PreviewTablePane extends BasicPane { private void setPreviewTableColumnValue(final Graphics g) { for (int i = 0; i < preveiwTable.getColumnModel().getColumnCount(); i++) { TableColumn column = preveiwTable.getColumnModel().getColumn(i); - DefaultTableCellRenderer cellRenderer = new DefaultTableCellRenderer() { - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { - JComponent comp = (JComponent) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - Font f = table.getFont(); - - //默认在系统不支持 无法显示时 如自造的字 ,字体设置为空. - Font defaultShowFont = FRFont.getInstance("", f.getStyle(), f.getSize()); - if (value instanceof String) { - String str = (String) value; - for (int j = 0; j < str.length(); j++) { - char c = str.charAt(j); - if (!f.canDisplay(c)) { - table.setFont(defaultShowFont); - } + DefaultTableCellRenderer cellRenderer = getDefaultTableCellRenderer(); + column.setCellRenderer(cellRenderer); + } + } + + /** + * 默认表格格子渲染器 + * + * @return + */ + private DefaultTableCellRenderer getDefaultTableCellRenderer() { + return new DefaultTableCellRenderer() { + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + JComponent comp = (JComponent) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + Font f = table.getFont(); + + //默认在系统不支持 无法显示时 如自造的字 ,字体设置为空. + Font defaultShowFont = FRFont.getInstance("", f.getStyle(), f.getSize()); + if (value instanceof String) { + String str = (String) value; + for (int j = 0; j < str.length(); j++) { + char c = str.charAt(j); + if (!f.canDisplay(c)) { + table.setFont(defaultShowFont); } } - return comp; } - }; - column.setCellRenderer(cellRenderer); - } + return comp; + } + }; } private void setWorker() { - worker = new SwingWorker() { - protected PreviewTableModel doInBackground() throws Exception { + worker = new SwingWorker() { + + @Override + protected TableModel doInBackground() throws Exception { connectionBar.start(); try { - if (tableData instanceof DBTableData) { - boolean status = DataOperator.getInstance().testConnection(((DBTableData) tableData).getDatabase()); - if (!status) { - throw new Exception(Toolkit.i18nText("Fine-Design_Basic_Database_Connection_Failed")); - } - } + testDBTableDataConnection(tableData); } finally { // 将close操作放到EDT线程中 UIUtil.invokeLaterIfNeeded(() -> connectionBar.close()); @@ -390,22 +555,20 @@ public class PreviewTablePane extends BasicPane { // parameterInputDialog // update之后的parameters,转成一个parameterMap,用于预览TableData PreviewTableModel previewModel = new PreviewTableModel(previewTableData.createDataModel(null), (int) maxPreviewNumberField.getValue()); - for (int i = 0; i < previewTableData.getColumnCount(); i++) { - Class cls = previewTableData.getColumnClass(i); - if (cls == Date.class || cls == TIME.class || cls == Timestamp.class) { - previewModel.dateIndexs.add(i); - } + if (TableDataPreviewDesensitizeManager.getInstance().needDesensitize(tableData)) { + // 数据集预览脱敏 + previewModel = TableDataPreviewDesensitizeManager.getInstance().desensitizeTableModel(tableData, previewModel); } + dealWithPreviewTableModelColumnClass(previewModel, previewTableData); return previewModel; } + @Override public void done() { try { - PreviewTableModel model = get(); - setModel(model); - setCurrentRows(model.getRowCount()); + TableModel model = get(); + setPreviewTableModel(model); setPreviewTableColumnValue(getParent().getGraphics()); - fireLoadedListener(); } catch (Exception e) { if (!(e instanceof CancellationException)) { FineLoggerFactory.getLogger().error(e.getMessage(), e); @@ -420,6 +583,36 @@ public class PreviewTablePane extends BasicPane { }; } + /** + * 检查DBTableData连接 + * + * @param tableData + * @throws Exception + */ + private void testDBTableDataConnection(TableData tableData) throws Exception { + if (tableData instanceof DBTableData) { + boolean status = DataOperator.getInstance().testConnection(((DBTableData) tableData).getDatabase()); + if (!status) { + throw new Exception(Toolkit.i18nText("Fine-Design_Basic_Database_Connection_Failed")); + } + } + } + + /** + * 处理预览Model的列类型 + * + * @param previewModel + * @param previewTableData + */ + private void dealWithPreviewTableModelColumnClass(PreviewTableModel previewModel, EmbeddedTableData previewTableData) { + for (int i = 0; i < previewTableData.getColumnCount(); i++) { + Class cls = previewTableData.getColumnClass(i); + if (cls == Date.class || cls == TIME.class || cls == Timestamp.class) { + previewModel.dateIndexs.add(i); + } + } + } + /** * 直接预览存储过程的一个返回数据集,没有实际值和显示值 * @@ -528,9 +721,61 @@ public class PreviewTablePane extends BasicPane { } catch (Exception e) { previewModel = new PreviewTableModel((int) maxPreviewNumberField.getValue()); } - setModel(previewModel); - setCurrentRows(previewModel.getRowCount()); + setPreviewTableModel(previewModel); + + } + + /** + * 切换TableModel的展示状态 + */ + private void togglePreviewTableModelDesensitizeStatus() { + if (!isDesensitizeOpened()) { + // 未启用数据脱敏时,不需要切换 + return; + } + TableSorter tableSorter = (TableSorter) preveiwTable.getModel(); + TableModel originTableModel = tableSorter.getTableModel(); + if (originTableModel instanceof DesensitizedPreviewTableModel) { + ((DesensitizedPreviewTableModel) originTableModel).toggleNeedDesensite(); + } + } + + /** + * 刷新一下预览页面,用于切换脱敏和非脱敏时的显示 + */ + private void refreshTable() { + TableModel originTableModel = getCurrentTableModel(); + setPreviewTableModel(originTableModel); + } + + /** + * 获取当前的TableModel,已经除掉了TableSorter的包装 + * + * @return + */ + private TableModel getCurrentTableModel() { + TableSorter tableSorter = (TableSorter) preveiwTable.getModel(); + return tableSorter.getTableModel(); + } + + /** + * 设置预览TableModel + * + * @param previewTableModel + */ + private void setPreviewTableModel(TableModel previewTableModel) { + setDesensitizationCount(previewTableModel); + setModel(previewTableModel); + setCurrentRows(previewTableModel.getRowCount()); fireLoadedListener(); + } + /** + * 数据脱敏是否启用 + * @return + */ + private boolean isDesensitizeOpened() { + return tableData instanceof AbstractDesensitizationTableData && + ((AbstractDesensitizationTableData) tableData).getDesensitizeOpen(); } } diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/preview/desensitization/TableDataPreviewDesensitizeManager.java b/designer-base/src/main/java/com/fr/design/data/datapane/preview/desensitization/TableDataPreviewDesensitizeManager.java new file mode 100644 index 0000000000..e2d79fbf70 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/data/datapane/preview/desensitization/TableDataPreviewDesensitizeManager.java @@ -0,0 +1,182 @@ +package com.fr.design.data.datapane.preview.desensitization; + + +import com.fr.base.TableData; +import com.fr.data.desensitize.base.AbstractDesensitizationTableData; +import com.fr.data.desensitize.base.TableDataDesensitizationBean; +import com.fr.data.desensitize.manage.DesensitizationManager; +import com.fr.data.desensitize.util.DesentizationUtils; +import com.fr.decision.webservice.bean.user.DepartmentPostBean; +import com.fr.decision.webservice.bean.user.RoleBean; +import com.fr.decision.webservice.utils.DecisionServiceConstants; +import com.fr.decision.webservice.v10.user.CustomRoleService; +import com.fr.decision.webservice.v10.user.PositionService; +import com.fr.design.data.DesignTableDataManager; +import com.fr.design.data.datapane.preview.PreviewTableModel; +import com.fr.design.data.datapane.preview.desensitization.model.DesensitizedPreviewTableModel; +import com.fr.design.data.tabledata.wrapper.TableDataWrapper; +import com.fr.general.ComparatorUtils; +import com.fr.log.FineLoggerFactory; +import com.fr.stable.StringUtils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + + +/** + * 管理所有数据集预览过程中的脱敏计算 + * @author Yvan + * @version 11.0 + * Created by Yvan on 2022/9/14 + */ +public class TableDataPreviewDesensitizeManager implements DesensitizationManager { + + private static final String CONNECTOR = "_"; + + private TableDataPreviewDesensitizeManager() { + } + + private static class Holder { + private static final TableDataPreviewDesensitizeManager INSTANCE = new TableDataPreviewDesensitizeManager(); + } + + /** + * 获取TableDataPreviewDesensitizeManager单例 + * @return + */ + public static TableDataPreviewDesensitizeManager getInstance() { + return TableDataPreviewDesensitizeManager.Holder.INSTANCE; + } + + /** + * 判断数据集预览时是否需要脱敏,这里不需要判断roleIds,因为默认有权限编辑的人一定有权限看脱敏前后字段值 + * + * @param tableData + * @return + */ + public boolean needDesensitize(TableData tableData) { + return tableData instanceof AbstractDesensitizationTableData && + DesentizationUtils.isCollectionNotEmpty(((AbstractDesensitizationTableData) tableData).getDesensitizationBeans()) && + ((AbstractDesensitizationTableData) tableData).getDesensitizationBeans().stream().noneMatch(TableDataDesensitizationBean::invalid); + } + + /** + * 数据集预览脱敏,使用DesensitizedPreviewTableModel对当前的预览TableModel做包装 + * + * @param model + * @return + */ + public PreviewTableModel desensitizeTableModel(TableData tableData, PreviewTableModel model) { + if (needDesensitize(tableData)) { + Map desensitizationBeanMap = new LinkedHashMap<>(); + // 获取此数据集的所有脱敏信息 + Collection desensitizationBeans = ((AbstractDesensitizationTableData) tableData).getDesensitizationBeans(); + // 排个序 + List sortedBeans = desensitizationBeans.stream().sorted(Comparator.comparingInt(TableDataDesensitizationBean::getColumnIndex)).collect(Collectors.toList()); + int columnIndex = 0; + for (TableDataDesensitizationBean sortedBean : sortedBeans) { + // 当map中已包含列序号时,代表对这一列设置了不同的脱敏规则,将key + 1 + if (desensitizationBeanMap.containsKey(columnIndex)) { + columnIndex++; + } + desensitizationBeanMap.put(columnIndex, sortedBean); + } + // 包装TableModel + return new DesensitizedPreviewTableModel(model, desensitizationBeanMap); + } + return model; + } + + /** + * 通过TableData获取其列名 + * 实现逻辑有点绕,TableData本身并不能返回列名集合,只能先去获取当前模板所有数据集,然后匹配名称拿到TableDataWrapper再获取列名 + * @param tableData + * @return + */ + public List getColumnNamesByTableData(TableData tableData) { + List> editingDataSet = DesignTableDataManager.getEditingDataSet(DesignTableDataManager.getEditingTableDataSource()); + // 当前所有模板数据集Wrapper,不包括存储过程 + Map templeteDataMap = editingDataSet.get(0); + // 当前所有模板数据集Wrapper,包括存储过程 + Map dataWrapperMap = DesignTableDataManager.getAllDataSetIncludingProcedure(templeteDataMap); + // 找到匹配当前数据集名称的Wrapper + TableDataWrapper tableDataWrapper = dataWrapperMap.get(tableData.getName()); + return tableDataWrapper == null ? + new ArrayList<>() : + tableDataWrapper.calculateColumnNameList(); + } + + /** + * 这个api会返回 部门职位 + 自定义角色 的Map + * 其中 key为 "pid" + "_" + "id",value为 "ptext" + "text" + * @return + */ + public Map getAllRoles() { + Map rolesMap = new LinkedHashMap<>(); + // 处理部门职位相关的用户组 + addDepartmentAndPositionRoles2Map(rolesMap); + // 处理自定义角色相关的用户组 + addCustomRoles2Map(rolesMap); + return rolesMap; + } + + /** + * 获取所有的部门职位,按照 key为 "pid" + "_" + "id",value为 "ptext" + "text"的格式添加到参数Map中 + * @param rolesMap + */ + private void addDepartmentAndPositionRoles2Map(Map rolesMap) { + try { + List postBeans = PositionService.getInstance().getDepartmentPostNameList(); + for (DepartmentPostBean postBean : postBeans) { + String department = postBean.getpText(); + String position = postBean.getText(); + String departmentId = postBean.getpId(); + String positionId = postBean.getId(); + if (!ComparatorUtils.equals(DecisionServiceConstants.DECISION_DEP_ROOT, postBean.getId()) && StringUtils.isNotEmpty(position)) { + // 只添加部门和职位同时存在的 + rolesMap.put(mergeRoleId(departmentId, positionId), mergeRoleText(department, position)); + } + } + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e, "[Desensitization] failed to add department and position roles to map for {}", e.getMessage()); + } + } + + /** + * 获取所有的自定义角色,按照 id - name添加到参数map里 + * @param rolesMap + */ + private void addCustomRoles2Map(Map rolesMap) { + try { + List allCustomRole = CustomRoleService.getInstance().getAllCustomRoleNameList(null); + allCustomRole.forEach(roleBean -> rolesMap.put(roleBean.getId(), roleBean.getText())); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e, "[Desensitization] failed to add custom role to map for {}", e.getMessage()); + } + } + + /** + * 合并部门 + 职位的名称 + * @param departmentName + * @param positionName + * @return + */ + public String mergeRoleText(String departmentName, String positionName) { + return departmentName + positionName; + } + + /** + * 合并部门 + 职位的id + * @param departmentId + * @param positionId + * @return + */ + public String mergeRoleId(String departmentId, String positionId) { + return departmentId + CONNECTOR + positionId; + } +} diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/preview/desensitization/model/DesensitizedPreviewTableModel.java b/designer-base/src/main/java/com/fr/design/data/datapane/preview/desensitization/model/DesensitizedPreviewTableModel.java new file mode 100644 index 0000000000..066f11374b --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/data/datapane/preview/desensitization/model/DesensitizedPreviewTableModel.java @@ -0,0 +1,113 @@ +package com.fr.design.data.datapane.preview.desensitization.model; + + +import com.fr.data.desensitize.base.TableDataDesensitizationBean; +import com.fr.data.desensitize.calculate.DesensitizationCalculator; +import com.fr.data.desensitize.rule.base.DesensitizationRule; +import com.fr.design.data.datapane.preview.PreviewTableModel; + +import java.util.Map; +import java.util.Objects; + +/** + * 数据集预览TableModel的脱敏实现 + * + * @author Yvan + * @version 11.0 + * Created by Yvan on 2022/9/14 + */ +public class DesensitizedPreviewTableModel extends PreviewTableModel { + + /** + * 原始数据Model + */ + private PreviewTableModel previewTableModel; + + /** + * 脱敏后新组装的TableModel中,列序号 - 脱敏信息 对应的Map + */ + private Map desensitizationBeanMap; + + private boolean needDesensitize = false; + + public DesensitizedPreviewTableModel(PreviewTableModel previewTableModel, Map desensitizationBeanMap) { + this.previewTableModel = previewTableModel; + this.desensitizationBeanMap = desensitizationBeanMap; + } + + @Override + public String getColumnName(int column) { + int originIndex = needDesensitize && Objects.nonNull(desensitizationBeanMap.get(column)) ? + desensitizationBeanMap.get(column).getColumnIndex() : column; + return previewTableModel.getColumnName(originIndex); + } + + @Override + public int getRowCount() { + return previewTableModel.getRowCount(); + } + + @Override + public int getColumnCount() { + return needDesensitize ? + getDesensitizeColumnsCount() : + previewTableModel.getColumnCount(); + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + return needDesensitize && Objects.nonNull(desensitizationBeanMap.get(columnIndex)) ? + getDesensitizedValue(rowIndex, columnIndex) : + previewTableModel.getValueAt(rowIndex, columnIndex); + } + + /** + * 返回脱敏后的数据值 + * + * @param rowIndex + * @param columnIndex + * @return + */ + private Object getDesensitizedValue(int rowIndex, int columnIndex) { + // 先通过columnIndex找到对应原TableModel的列序号 + int originColumnIndex = desensitizationBeanMap.get(columnIndex).getColumnIndex(); + // 获取原值 + Object value = previewTableModel.getValueAt(rowIndex, originColumnIndex); + // 判空 + if (Objects.isNull(value)) { + return value; + } + // 脱敏 + String strValue = String.valueOf(value); + value = desensitizeValue(strValue, columnIndex); + return value; + } + + /** + * 计算脱敏后的value,此时columnIndex对应的脱敏信息一定不为空 + * + * @param strValue + * @param columnIndex + * @return + */ + private String desensitizeValue(String strValue, int columnIndex) { + TableDataDesensitizationBean desensitizationBean = desensitizationBeanMap.get(columnIndex); + DesensitizationRule desensitizationRule = desensitizationBean.getDesensitizationRule(); + return DesensitizationCalculator.getInstance().desensitize(strValue, desensitizationRule); + } + + /** + * 获取当前有效的脱敏字段个数 + * @return + */ + public int getDesensitizeColumnsCount() { + return desensitizationBeanMap.size(); + } + + /** + * 切换脱敏状态 + */ + public void toggleNeedDesensite() { + this.needDesensitize = !needDesensitize; + } +} diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/preview/desensitization/view/rule/DesensitizationRuleChoosePane.java b/designer-base/src/main/java/com/fr/design/data/datapane/preview/desensitization/view/rule/DesensitizationRuleChoosePane.java new file mode 100644 index 0000000000..3f8485ffa5 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/data/datapane/preview/desensitization/view/rule/DesensitizationRuleChoosePane.java @@ -0,0 +1,312 @@ +package com.fr.design.data.datapane.preview.desensitization.view.rule; + +import com.fr.base.svg.IconUtils; +import com.fr.data.desensitize.rule.DesensitizationRuleManager; +import com.fr.data.desensitize.rule.base.DesensitizationRule; +import com.fr.data.desensitize.rule.base.DesensitizationRuleSource; +import com.fr.design.dialog.BasicDialog; +import com.fr.design.dialog.DialogActionAdapter; +import com.fr.design.gui.ibutton.UIRadioButton; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.gui.itableeditorpane.UITableEditAction; +import com.fr.design.gui.itableeditorpane.UITableEditorPane; +import com.fr.design.gui.itableeditorpane.UITableModelAdapter; +import com.fr.design.i18n.Toolkit; +import com.fr.stable.StringUtils; +import com.fr.stable.collections.CollectionUtils; + +import javax.swing.AbstractCellEditor; +import javax.swing.JPanel; +import javax.swing.JTable; +import javax.swing.SwingUtilities; +import javax.swing.table.TableCellEditor; +import javax.swing.table.TableCellRenderer; +import java.awt.CardLayout; +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * 脱敏规则选择页面 + * + * @author Yvan + * @version 11.0 + * Created by Yvan on 2022/9/26 + */ +public class DesensitizationRuleChoosePane extends JPanel { + + private CardLayout cardLayout; + + private UITableEditorPane serverRuleEditPane; + + private UITableEditorPane customRuleEditPane; + + private DesensitizationRuleSource currentRuleSource; + + public DesensitizationRuleChoosePane() { + this.cardLayout = new CardLayout(); + this.setLayout(cardLayout); + serverRuleEditPane = new UITableEditorPane<>(new DesensitizationRuleChooseTableModel(this, true)); + customRuleEditPane = new UITableEditorPane<>(new DesensitizationRuleChooseTableModel(this, false)); + serverRuleEditPane.setHeaderResizing(false); + customRuleEditPane.setHeaderResizing(false); + populateDesensitizationRules(); + this.add(serverRuleEditPane, DesensitizationRuleSource.SERVER.name()); + this.add(customRuleEditPane, DesensitizationRuleSource.CUSTOM.name()); + // 默认显示平台规则 + switchPaneByRuleSource(DesensitizationRuleSource.SERVER); + } + + /** + * 通过脱敏规则来源,切换显示不同的脱敏规则Table面板 + * @param ruleSource + */ + public void switchPaneByRuleSource(DesensitizationRuleSource ruleSource) { + this.currentRuleSource = ruleSource; + this.cardLayout.show(this, ruleSource.name()); + } + + /** + * 展示当前规则 + */ + private void populateDesensitizationRules() { + serverRuleEditPane.populate(DesensitizationRuleManager.getInstance().getRules(DesensitizationRuleSource.SERVER)); + customRuleEditPane.populate(DesensitizationRuleManager.getInstance().getRules(DesensitizationRuleSource.CUSTOM)); + } + + /** + * 获取当前选中的规则 + * + * @return + */ + public DesensitizationRule getSelectedDesensitizationRule() { + switch (currentRuleSource) { + case SERVER: + return serverRuleEditPane.getTableModel().getSelectedValue(); + case CUSTOM: + return customRuleEditPane.getTableModel().getSelectedValue(); + default: + return null; + } + } + + /** + * 规则选择Table的TableModel + */ + private class DesensitizationRuleChooseTableModel extends UITableModelAdapter { + + private Component parent; + + private boolean debugActionOnly; + + protected DesensitizationRuleChooseTableModel(Component parent, boolean debugActionOnly) { + super(new String[]{ + StringUtils.EMPTY, + Toolkit.i18nText("Fine-Design_Report_Desensitization_Rule_Name"), + Toolkit.i18nText("Fine-Design_Report_Desensitization_Rule_Description") + }); + this.parent = parent; + this.debugActionOnly = debugActionOnly; + this.setColumnClass(new Class[]{ + RuleChoosePane.class, + UILabel.class, + UILabel.class + }); + this.setDefaultEditor(RuleChoosePane.class, new RuleChoosePane()); + this.setDefaultRenderer(RuleChoosePane.class, new RuleChoosePane()); + this.createTable().getColumnModel().getColumn(0).setMaxWidth(20); + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + DesensitizationRule rule = getList().get(rowIndex); + switch (columnIndex) { + case 0: + return rule.equals(getSelectedValue()); + case 1: + return rule.getRuleName(); + case 2: + return DesensitizationRule.getDescription(rule); + default: + return StringUtils.EMPTY; + } + } + + @Override + public boolean isCellEditable(int row, int col) { + return true; + } + + @Override + public UITableEditAction[] createAction() { + return debugActionOnly ? + new UITableEditAction[]{new DebugRuleAction(parent)} : + new UITableEditAction[]{new AddRuleAction(), new EditRuleAction(), new DeleteRuleAction(parent), new DebugRuleAction(parent)}; + } + + private Set getCurrentExistRuleNames(String excludeName) { + List rules = getList(); + return CollectionUtils.isEmpty(rules) ? + new LinkedHashSet<>() : + rules.stream() + .map(DesensitizationRule::getRuleName) + .filter(name -> !StringUtils.equals(name, excludeName)) + .collect(Collectors.toSet()); + } + + private class RuleChoosePane extends AbstractCellEditor implements TableCellEditor, TableCellRenderer { + + private UIRadioButton selectedButton; + + public RuleChoosePane() { + this.selectedButton = new UIRadioButton(); + } + + @Override + public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { + selectedButton.setSelected(isSelected); + return selectedButton; + } + + @Override + public Object getCellEditorValue() { + return selectedButton.isSelected(); + } + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + selectedButton.setSelected(isSelected); + return selectedButton; + } + } + + /** + * 添加规则 + */ + private class AddRuleAction extends AddTableRowAction { + + public AddRuleAction() { + this.setName(Toolkit.i18nText("Fine-Design_Report_Desensitization_Add")); + this.setSmallIcon("/com/fr/design/standard/add/add_black", false); + } + + @Override + public void actionPerformed(ActionEvent e) { + // 新增一条规则 + DesensitizationRuleEditPane editPane = new DesensitizationRuleEditPane(getCurrentExistRuleNames(StringUtils.EMPTY)); + BasicDialog basicDialog = editPane.showWindowWithCustomSize(SwingUtilities.getWindowAncestor(parent), new DialogActionAdapter() { + @Override + public void doOk() { + DesensitizationRule rule = editPane.updateBean(); + // 添加到Rule Manager中 + if (DesensitizationRule.valid(rule)) { + DesensitizationRuleManager.getInstance().addRule(rule); + } + // 添加到Table中 + addRow(rule); + fireTableDataChanged(); + } + + @Override + public void doCancel() { + super.doCancel(); + } + }, BasicDialog.DEFAULT); + basicDialog.setVisible(true); + } + } + + private class EditRuleAction extends UITableEditAction { + + public EditRuleAction() { + this.setName(Toolkit.i18nText("Fine-Design_Basic_Edit")); + this.setSmallIcon(IconUtils.readIcon("/com/fr/design/standard/edit/edit")); + } + + @Override + public void checkEnabled() { + setEnabled(table.getSelectedRow() != -1); + } + + @Override + public void actionPerformed(ActionEvent e) { + // 获取当前选中规则 + DesensitizationRule selectedValue = getSelectedValue(); + DesensitizationRuleEditPane editPane = new DesensitizationRuleEditPane(getCurrentExistRuleNames(selectedValue.getRuleName())); + editPane.populateBean(selectedValue); + BasicDialog basicDialog = editPane.showWindowWithCustomSize(SwingUtilities.getWindowAncestor(parent), new DialogActionAdapter() { + @Override + public void doOk() { + DesensitizationRule rule = editPane.updateBean(); + // 修改同步到RuleManager中 + if (DesensitizationRule.valid(rule)) { + DesensitizationRuleManager.getInstance().updateRule(rule); + } + fireTableDataChanged(); + } + + @Override + public void doCancel() { + super.doCancel(); + } + }, BasicDialog.DEFAULT); + basicDialog.setVisible(true); + } + } + + private class DeleteRuleAction extends DeleteAction { + + public DeleteRuleAction(Component parent) { + super(parent); + this.setName(Toolkit.i18nText("Fine-Design_Basic_Base_Remove")); + this.setSmallIcon("/com/fr/design/images/control/remove"); + } + + @Override + public void checkEnabled() { + setEnabled(table.getSelectedRow() != -1); + } + + @Override + public void actionPerformed(ActionEvent e) { + // 获取当前选中规则 + DesensitizationRule selectedRule = getSelectedValue(); + // 删除同步到RuleManager中 + if (DesensitizationRule.valid(selectedRule)) { + DesensitizationRuleManager.getInstance().removeRule(selectedRule); + } + super.actionPerformed(e); + } + } + + private class DebugRuleAction extends UITableEditAction { + + private Component parent; + + public DebugRuleAction(Component parent) { + this.parent = parent; + this.setName(Toolkit.i18nText("Fine-Design_Report_Desensitization_Rule_Debug")); + this.setSmallIcon("/com/fr/design/standard/debug/debug"); + } + + @Override + public void checkEnabled() { + setEnabled(table.getSelectedRow() != -1); + } + + @Override + public void actionPerformed(ActionEvent e) { + // 获取当前选中规则 + DesensitizationRule selectedRule = getSelectedValue(); + DesensitizationRuleDebugPane ruleDebugPane = new DesensitizationRuleDebugPane(selectedRule); + BasicDialog ruleDebugDialog = ruleDebugPane.showWindowWithCustomSize(SwingUtilities.getWindowAncestor(parent), null, BasicDialog.DEFAULT); + ruleDebugDialog.setVisible(true); + } + } + } + + +} diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/preview/desensitization/view/rule/DesensitizationRuleDebugPane.java b/designer-base/src/main/java/com/fr/design/data/datapane/preview/desensitization/view/rule/DesensitizationRuleDebugPane.java new file mode 100644 index 0000000000..71998b7bee --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/data/datapane/preview/desensitization/view/rule/DesensitizationRuleDebugPane.java @@ -0,0 +1,127 @@ +package com.fr.design.data.datapane.preview.desensitization.view.rule; + +import com.fr.data.desensitize.calculate.DesensitizationCalculator; +import com.fr.data.desensitize.rule.base.DesensitizationRule; +import com.fr.design.dialog.BasicPane; +import com.fr.design.dialog.FineJOptionPane; +import com.fr.design.gui.ibutton.UIButton; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.gui.itextfield.UITextField; +import com.fr.design.i18n.Toolkit; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.layout.TableLayout; +import com.fr.design.layout.TableLayoutHelper; +import com.fr.stable.StringUtils; + +import javax.swing.BorderFactory; +import javax.swing.JComponent; +import javax.swing.JPanel; +import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; + +/** + * 脱敏规则调试页 + * @author Yvan + * @version 11.0 + * Created by Yvan on 2022/9/8 + */ +public class DesensitizationRuleDebugPane extends BasicPane { + + /** + * 脱敏规则 + */ + private DesensitizationRule rule; + + public DesensitizationRuleDebugPane(DesensitizationRule rule) { + this.rule = rule; + initComponent(); + } + + private void initComponent() { + this.setLayout(FRGUIPaneFactory.createBorderLayout()); + this.add(initNorthPane(), BorderLayout.NORTH); + this.add(initCenterPane(), BorderLayout.CENTER); + } + + private JPanel initNorthPane() { + JPanel northPane = FRGUIPaneFactory.createTitledBorderPane(Toolkit.i18nText("Fine-Design_Report_Desensitization_Rule_Description")); + JPanel panel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + panel.setBorder(BorderFactory.createEmptyBorder(20, 10, 0, 0)); + UILabel desensitizationRule = new UILabel(Toolkit.i18nText("Fine-Design_Report_Desensitization_Rule_Algorithm")); + UILabel characterReplace = new UILabel(rule.getRuleType().getTypeName()); + UILabel description = new UILabel(DesensitizationRule.getDescription(rule)); + JComponent[][] components = new JComponent[][] { + {desensitizationRule, characterReplace}, + {new UILabel(), description} + }; + panel.add( + TableLayoutHelper.createCommonTableLayoutPane( + components, + new double[]{TableLayout.PREFERRED, TableLayout.PREFERRED}, + new double[]{TableLayout.PREFERRED, TableLayout.PREFERRED}, + 20), + BorderLayout.CENTER); + northPane.add(panel); + return northPane; + } + + private JPanel initCenterPane() { + JPanel centerPane = FRGUIPaneFactory.createTitledBorderPane(Toolkit.i18nText("Fine-Design_Report_Desensitization_Rule_Debug")); + JPanel panel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + panel.setBorder(BorderFactory.createEmptyBorder(20, 10, 0, 0)); + + UILabel beforeDesensitize = new UILabel(Toolkit.i18nText("Fine-Design_Report_Desensitization_Before")); + UITextField beforeDesensitizeText = new UITextField(20); + beforeDesensitizeText.setPlaceholder(Toolkit.i18nText("Fine-Design_Report_Desensitization_Enter_Content")); + UILabel afterDesensitize = new UILabel(Toolkit.i18nText("Fine-Design_Report_Desensitization_After")); + UITextField afterDesensitizeText = new UITextField(20); + afterDesensitizeText.setEditable(false); + UIButton desensitizeButton = new UIButton(Toolkit.i18nText("Fine-Design_Report_Desensitization_Debug")); + beforeDesensitizeText.addFocusListener(new FocusListener() { + @Override + public void focusGained(FocusEvent e) { + afterDesensitizeText.setText(StringUtils.EMPTY); + } + + @Override + public void focusLost(FocusEvent e) { + + } + }); + desensitizeButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + String text = beforeDesensitizeText.getText(); + if (StringUtils.isEmpty(text)) { + FineJOptionPane.showMessageDialog(DesensitizationRuleDebugPane.this, + Toolkit.i18nText("Fine-Design_Report_Desensitization_Please_Enter_Valid_Content")); + } + String desensitizedText = DesensitizationCalculator.getInstance().desensitize(text, rule); + afterDesensitizeText.setText(desensitizedText); + } + }); + + JComponent[][] components = new JComponent[][] { + {beforeDesensitize, beforeDesensitizeText, desensitizeButton}, + {afterDesensitize, afterDesensitizeText, new UILabel()} + }; + panel.add( + TableLayoutHelper.createCommonTableLayoutPane( + components, + new double[]{TableLayout.PREFERRED, TableLayout.PREFERRED}, + new double[]{TableLayout.PREFERRED, TableLayout.PREFERRED, TableLayout.PREFERRED}, + 20), + BorderLayout.CENTER); + + centerPane.add(panel); + return centerPane; + } + + @Override + protected String title4PopupWindow() { + return Toolkit.i18nText("Fine-Design_Report_Desensitization_Rule_Debug"); + } +} diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/preview/desensitization/view/rule/DesensitizationRuleEditPane.java b/designer-base/src/main/java/com/fr/design/data/datapane/preview/desensitization/view/rule/DesensitizationRuleEditPane.java new file mode 100644 index 0000000000..9c2c467fe3 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/data/datapane/preview/desensitization/view/rule/DesensitizationRuleEditPane.java @@ -0,0 +1,296 @@ +package com.fr.design.data.datapane.preview.desensitization.view.rule; + +import com.fr.data.desensitize.rule.base.DesensitizationCondition; +import com.fr.data.desensitize.rule.base.DesensitizationRule; +import com.fr.data.desensitize.rule.base.DesensitizationRuleType; +import com.fr.design.beans.BasicBeanPane; +import com.fr.design.constants.UIConstants; +import com.fr.design.event.UIObserverListener; +import com.fr.design.gui.icombobox.UIComboBox; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.gui.itextfield.UINumberField; +import com.fr.design.gui.itextfield.UITextField; +import com.fr.design.i18n.Toolkit; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.layout.TableLayout; +import com.fr.design.layout.TableLayoutHelper; +import com.fr.stable.StringUtils; + +import javax.swing.BorderFactory; +import javax.swing.JComponent; +import javax.swing.JPanel; +import java.awt.BorderLayout; +import java.awt.CardLayout; +import java.awt.Insets; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.util.Arrays; +import java.util.Set; + +/** + * 脱敏规则编辑页 + * + * @author Yvan + * @version 11.0 + * Created by Yvan on 2022/9/8 + */ +public class DesensitizationRuleEditPane extends BasicBeanPane { + /** + * 已经存在的规则名称,用于检测重名 + */ + private Set existRuleNames; + + private UITextField ruleNameTextField; + private UIComboBox ruleTypeComboBox; + private UINumberField retainFrontTextField; + private UINumberField retainBackTextField; + private UITextField firstSymbolTextField; + private UITextField secondSymbolTextField; + private CardLayout cardLayout; + private JPanel ruleConditionPane; + + + private DesensitizationRule rule = DesensitizationRule.createDefaultEmptyRule(); + + private final FocusListener retainFrontListener = new FocusListener() { + @Override + public void focusGained(FocusEvent e) { + } + + @Override + public void focusLost(FocusEvent e) { + rule.getCondition().setRetainFront((int) retainFrontTextField.getValue()); + } + }; + + private final FocusListener retainBackListener = new FocusListener() { + @Override + public void focusGained(FocusEvent e) { + } + + @Override + public void focusLost(FocusEvent e) { + rule.getCondition().setRetainBack((int) retainBackTextField.getValue()); + } + }; + + private final FocusListener firstSymbolListener = new FocusListener() { + @Override + public void focusGained(FocusEvent e) { + } + + @Override + public void focusLost(FocusEvent e) { + rule.getCondition().setSymbol(firstSymbolTextField.getText()); + } + }; + + private final FocusListener secondSymbolListener = new FocusListener() { + @Override + public void focusGained(FocusEvent e) { + } + + @Override + public void focusLost(FocusEvent e) { + rule.getCondition().setRetainFront(0); + rule.getCondition().setRetainBack(0); + rule.getCondition().setSymbol(secondSymbolTextField.getText()); + } + }; + + public DesensitizationRuleEditPane(Set existRuleNames) { + this.existRuleNames = existRuleNames; + initComponent(); + } + + /** + * 初始化面板 + */ + private void initComponent() { + this.setLayout(FRGUIPaneFactory.createLeftZeroVgapNormalHgapLayout()); + JPanel panel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + panel.setBorder(BorderFactory.createEmptyBorder(20, 10, 0, 0)); + UILabel ruleNameLabel = new UILabel(Toolkit.i18nText("Fine-Design_Report_Desensitization_Rule_Name")); + // 规则名称输入框 + initRuleNameTextField(); + UILabel ruleTypeLabel = new UILabel(Toolkit.i18nText("Fine-Design_Report_Desensitization_Rule_Algorithm")); + // 脱敏算法Type设置 + JPanel ruleTypePane = initRuleTypePane(); + // 脱敏算法Condition设置 + JPanel ruleConditionPane = initRuleConditionPane(); + JComponent[][] components = { + {ruleNameLabel, ruleNameTextField}, + {ruleTypeLabel, ruleTypePane}, + {new UILabel(), ruleConditionPane} + }; + panel.add( + TableLayoutHelper.createGapTableLayoutPane( + components, + new double[]{TableLayout.PREFERRED, TableLayout.PREFERRED, TableLayout.FILL}, + new double[]{TableLayout.FILL, TableLayout.PREFERRED}, + 20, + 20), + BorderLayout.CENTER); + this.add(panel); + } + + /** + * 初始化规则类型面板 + * @return + */ + private JPanel initRuleTypePane() { + JPanel ruleTypePane = FRGUIPaneFactory.createBorderLayout_S_Pane(); + // 脱敏算法类型下拉框 + initRuleTypeComboBox(); + ruleTypePane.add(ruleTypeComboBox, BorderLayout.CENTER); + return ruleTypePane; + } + + /** + * 初始化规则条件面板 + * @return + */ + private JPanel initRuleConditionPane() { + JPanel panel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + cardLayout = new CardLayout(); + ruleConditionPane = new JPanel(cardLayout); + // 字符替换 + JPanel characterReplacePane = FRGUIPaneFactory.createLeftFlowZeroGapBorderPane(); + UILabel retainFrontLabel = new UILabel(Toolkit.i18nText("Fine-Design_Report_Desensitization_Retain_Front") + StringUtils.BLANK); + retainFrontTextField = new UINumberField(5); + retainFrontTextField.addFocusListener(retainFrontListener); + + UILabel retainBackLabel = new UILabel(StringUtils.BLANK + Toolkit.i18nText("Fine-Design_Report_Desensitization_Count_And_Back") + StringUtils.BLANK); + retainBackTextField = new UINumberField(5); + retainBackTextField.addFocusListener(retainBackListener); + + UILabel replaceLabel = new UILabel(StringUtils.BLANK + Toolkit.i18nText("Fine-Design_Report_Desensitization_Count_And_Other_Character_Replace_By") + StringUtils.BLANK); + firstSymbolTextField = new UITextField(10); + firstSymbolTextField.addFocusListener(firstSymbolListener); + + characterReplacePane.add(retainFrontLabel); + characterReplacePane.add(retainFrontTextField); + characterReplacePane.add(retainBackLabel); + characterReplacePane.add(retainBackTextField); + characterReplacePane.add(replaceLabel); + characterReplacePane.add(firstSymbolTextField); + // 整体替换 + JPanel characterAllReplacePane = FRGUIPaneFactory.createLeftFlowZeroGapBorderPane(); + UILabel allReplaceLabel = new UILabel(Toolkit.i18nText("Fine-Design_Report_Desensitization_All_Character_Replace_By") + StringUtils.BLANK); + secondSymbolTextField = new UITextField(10); + secondSymbolTextField.addFocusListener(secondSymbolListener); + + characterAllReplacePane.add(allReplaceLabel); + characterAllReplacePane.add(secondSymbolTextField); + + ruleConditionPane.add(characterReplacePane, DesensitizationRuleType.CHARACTER_REPLACE.getTypeName()); + ruleConditionPane.add(characterAllReplacePane, DesensitizationRuleType.ALL_CHARACTERS_REPLACE.getTypeName()); + // 初始化状态为字符替换 + switchRuleConditionPane(DesensitizationRuleType.CHARACTER_REPLACE); + + panel.add(ruleConditionPane, BorderLayout.CENTER); + return panel; + } + + /** + * 切换规则类型面板 + * @param ruleType + */ + private void switchRuleConditionPane(DesensitizationRuleType ruleType) { + this.cardLayout.show(ruleConditionPane, ruleType.getTypeName()); + } + + /** + * 初始化规则类型下拉框 + */ + private void initRuleTypeComboBox() { + ruleTypeComboBox = new UIComboBox(Arrays.stream(DesensitizationRuleType.values()).map(DesensitizationRuleType::getTypeName).toArray()); + ruleTypeComboBox.setSelectedIndex(0); + ruleTypeComboBox.registerChangeListener(new UIObserverListener() { + @Override + public void doChange() { + DesensitizationRuleType ruleType = DesensitizationRuleType.matchByTypeName((String) ruleTypeComboBox.getSelectedItem()); + rule.setRuleType(ruleType); + // 修改底下的conditionPane + switchRuleConditionPane(ruleType); + } + }); + } + + /** + * 初始化规则名称输入框 + */ + private void initRuleNameTextField() { + ruleNameTextField = new UITextField(20) { + @Override + public Insets getInsets() { + return new Insets(2, 4, 0, 4); + } + }; + ruleNameTextField.setPlaceholder(Toolkit.i18nText("Fine-Design_Report_Desensitization_Please_Enter_Rule_Name")); + ruleNameTextField.setBorderPainted(true); + ruleNameTextField.addFocusListener(new FocusListener() { + @Override + public void focusGained(FocusEvent e) { + ruleNameTextField.setBorder(BorderFactory.createLineBorder(UIConstants.NORMAL_BLUE)); + ruleNameTextField.repaint(); + } + + @Override + public void focusLost(FocusEvent e) { + ruleNameTextField.setBorder(BorderFactory.createLineBorder(UIConstants.TOOLBAR_BORDER_COLOR)); + rule.setRuleName(ruleNameTextField.getText()); + ruleNameTextField.repaint(); + } + }); + } + + + @Override + public void populateBean(DesensitizationRule ob) { + this.rule = ob; + this.ruleNameTextField.setText(rule.getRuleName()); + String typeName = rule.getRuleType().getTypeName(); + this.ruleTypeComboBox.setSelectedItem(typeName); + switch (DesensitizationRuleType.matchByTypeName(typeName)) { + case CHARACTER_REPLACE: + this.retainFrontTextField.setValue(rule.getCondition().getRetainFront()); + this.retainBackTextField.setValue(rule.getCondition().getRetainBack()); + this.firstSymbolTextField.setText(rule.getCondition().getSymbol()); + break; + case ALL_CHARACTERS_REPLACE: + this.secondSymbolTextField.setText(rule.getCondition().getSymbol()); + break; + default: + } + + } + + @Override + public DesensitizationRule updateBean() { + rule.setEnable(true); + return rule; + } + + @Override + protected String title4PopupWindow() { + return Toolkit.i18nText("Fine-Design_Report_Desensitization_Custom_Config_Rules"); + } + + @Override + public void checkValid() throws Exception { + // 保存rule前检查下 + String checkMessage = StringUtils.EMPTY; + if (StringUtils.isEmpty(rule.getRuleName())) { + checkMessage = Toolkit.i18nText("Fine-Design_Report_Desensitization_Rule_Name_Cannot_Be_Empty"); + } else if (existRuleNames.contains(rule.getRuleName())) { + checkMessage = Toolkit.i18nText("Fine-Design_Report_Desensitization_Rule_Name_Cannot_Repeat"); + } else if (DesensitizationCondition.invalid(rule.getCondition())) { + checkMessage = Toolkit.i18nText("Fine-Design_Report_Desensitization_Rule_Wrong_Condition"); + } + if (StringUtils.isNotEmpty(checkMessage)) { + throw new Exception(checkMessage); + } + } + +} diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/preview/desensitization/view/rule/DesensitizationRulePane.java b/designer-base/src/main/java/com/fr/design/data/datapane/preview/desensitization/view/rule/DesensitizationRulePane.java new file mode 100644 index 0000000000..4fd8f5fef4 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/data/datapane/preview/desensitization/view/rule/DesensitizationRulePane.java @@ -0,0 +1,80 @@ +package com.fr.design.data.datapane.preview.desensitization.view.rule; + +import com.fr.data.desensitize.rule.base.DesensitizationRule; +import com.fr.data.desensitize.rule.base.DesensitizationRuleSource; +import com.fr.design.beans.BasicBeanPane; +import com.fr.design.border.UITitledBorder; +import com.fr.design.i18n.Toolkit; +import com.fr.design.layout.FRGUIPaneFactory; + +import javax.swing.JPanel; +import java.awt.BorderLayout; + +/** + * 脱敏规则展示页 + * @author Yvan + * @version 11.0 + * Created by Yvan on 2022/9/8 + */ +public class DesensitizationRulePane extends BasicBeanPane { + + /** + * 规则来源选择面板 + */ + private DesensitizationRuleSourceChoosePane ruleSourceChoosePane; + + /** + * 规则选择面板 + */ + private DesensitizationRuleChoosePane ruleChoosePane; + + /** + * 内容面板 + */ + private JPanel contentPane; + + public DesensitizationRulePane() { + this.setLayout(FRGUIPaneFactory.createBorderLayout()); + initPane(); + } + + private void initPane() { + // 内容面板 + contentPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); + contentPane.setBorder(UITitledBorder.createBorderWithTitle(Toolkit.i18nText("Fine-Design_Report_Desensitization_Rule_Choose"))); + this.add(contentPane, BorderLayout.CENTER); + // 规则来源选择Pane + ruleSourceChoosePane = new DesensitizationRuleSourceChoosePane(this); + // 规则选择Pane + ruleChoosePane = new DesensitizationRuleChoosePane(); + contentPane.add(ruleSourceChoosePane, BorderLayout.NORTH); + contentPane.add(ruleChoosePane, BorderLayout.CENTER); + } + + /** + * 处理规则来源选择面板中改动来源的事件 + * @param newRuleSource + */ + public void dealWithRuleSourceChanged(DesensitizationRuleSource newRuleSource) { + ruleChoosePane.switchPaneByRuleSource(newRuleSource); + } + + + @Override + public void populateBean(DesensitizationRule ob) { + // 这边展示当前所有规则时,不依靠外界传值,初始化的时候,从规则管理中心去获取 + } + + @Override + public DesensitizationRule updateBean() { + DesensitizationRule desensitizationRule = ruleChoosePane.getSelectedDesensitizationRule(); + return DesensitizationRule.valid(desensitizationRule) ? + desensitizationRule : + DesensitizationRule.createDefaultEmptyRule(); + } + + @Override + protected String title4PopupWindow() { + return Toolkit.i18nText("Fine-Design_Report_Desensitization_Rule"); + } +} diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/preview/desensitization/view/rule/DesensitizationRuleSourceChoosePane.java b/designer-base/src/main/java/com/fr/design/data/datapane/preview/desensitization/view/rule/DesensitizationRuleSourceChoosePane.java new file mode 100644 index 0000000000..f67512164e --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/data/datapane/preview/desensitization/view/rule/DesensitizationRuleSourceChoosePane.java @@ -0,0 +1,48 @@ +package com.fr.design.data.datapane.preview.desensitization.view.rule; + +import com.fr.data.desensitize.rule.base.DesensitizationRuleSource; +import com.fr.design.gui.ibutton.UIRadioButton; +import com.fr.design.i18n.Toolkit; + +import javax.swing.BorderFactory; +import javax.swing.ButtonGroup; +import javax.swing.JPanel; +import java.awt.FlowLayout; + +/** + * 脱敏规则来源选择页面 + * @author Yvan + * @version 11.0 + * Created by Yvan on 2022/9/26 + */ +public class DesensitizationRuleSourceChoosePane extends JPanel { + + /** + * 来源平台的按钮 + */ + private UIRadioButton serverSource; + + /** + * 来源本地的按钮 + */ + private UIRadioButton customSource; + + public DesensitizationRuleSourceChoosePane(DesensitizationRulePane desensitizationRulePane) { + this.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + this.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0)); + + serverSource = new UIRadioButton(Toolkit.i18nText("Fine-Design_Report_Desensitization_Server_Config_Rules")); + customSource = new UIRadioButton(Toolkit.i18nText("Fine-Design_Report_Desensitization_Custom_Config_Rules")); + + ButtonGroup buttonGroup = new ButtonGroup(); + buttonGroup.add(serverSource); + buttonGroup.add(customSource); + serverSource.setSelected(true); + + serverSource.registerChangeListener(() -> desensitizationRulePane.dealWithRuleSourceChanged(DesensitizationRuleSource.SERVER)); + customSource.registerChangeListener(() -> desensitizationRulePane.dealWithRuleSourceChanged(DesensitizationRuleSource.CUSTOM)); + + this.add(serverSource); + this.add(customSource); + } +} diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/preview/desensitization/view/setting/TableDataDesensitizationSettingPane.java b/designer-base/src/main/java/com/fr/design/data/datapane/preview/desensitization/view/setting/TableDataDesensitizationSettingPane.java new file mode 100644 index 0000000000..f63bcdacc3 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/data/datapane/preview/desensitization/view/setting/TableDataDesensitizationSettingPane.java @@ -0,0 +1,110 @@ +package com.fr.design.data.datapane.preview.desensitization.view.setting; + +import com.fr.data.desensitize.base.AbstractDesensitizationTableData; +import com.fr.data.desensitize.base.TableDataDesensitizationBean; +import com.fr.design.beans.BasicBeanPane; +import com.fr.design.constants.UIConstants; +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 javax.swing.JComponent; +import javax.swing.JPanel; +import java.awt.BorderLayout; +import java.util.List; + +/** + * 数据集脱敏字段设置页面 + * @author Yvan + * @version 11.0 + * Created by Yvan on 2022/9/8 + */ +public class TableDataDesensitizationSettingPane extends BasicBeanPane { + + /** + * 设置针对的数据集 + */ + private AbstractDesensitizationTableData tableData; + + private UICheckBox desensitizeOpen; + + private TableDataDesensitizationTablePane tableDataDesensitizationTablePane; + + + public TableDataDesensitizationSettingPane(AbstractDesensitizationTableData tableData) { + this.tableData = tableData; + initComponents(); + } + + private void initComponents() { + this.setLayout(FRGUIPaneFactory.createBorderLayout()); + this.add(initNorthPane(), BorderLayout.NORTH); + this.add(initCenterPane(), BorderLayout.CENTER); + } + + /** + * 初始化启用数据脱敏面板 + * @return + */ + private JComponent initNorthPane() { + JPanel northPane = FRGUIPaneFactory.createTopVerticalTitledBorderPane(Toolkit.i18nText("Fine-Design_Report_Desensitization_Config")); + this.desensitizeOpen = new UICheckBox(Toolkit.i18nText("Fine-Design_Report_Desensitization_Opened")); + northPane.add(desensitizeOpen); + UILabel tipLabel = new UILabel(Toolkit.i18nText("Fine-Design_Report_Desensitization_Opened_Tooltips")); + tipLabel.setForeground(UIConstants.CHECK_BOX_TIP_FONT_COLOR); + northPane.add(tipLabel); + return northPane; + } + + /** + * 初始化数据脱敏规则表面板 + * @return + */ + private JComponent initCenterPane() { + this.tableDataDesensitizationTablePane = new TableDataDesensitizationTablePane(tableData, this); + return tableDataDesensitizationTablePane; + } + + /** + * 是否启用脱敏 + * @return + */ + public boolean isDesensitizeOpened() { + return desensitizeOpen.isSelected(); + } + + @Override + protected String title4PopupWindow() { + return Toolkit.i18nText("Fine-Design_Report_Desensitization_Config"); + } + + @Override + public void populateBean(AbstractDesensitizationTableData tableData) { + this.tableData = tableData; + this.desensitizeOpen.setSelected(tableData.getDesensitizeOpen()); + tableDataDesensitizationTablePane.populateDesensitizationSetting(tableData); + } + + @Override + public AbstractDesensitizationTableData updateBean() { + saveDesensitizeOpened(); + saveDesensitizationBeans(tableDataDesensitizationTablePane.updateDesensitizationSetting()); + return tableData; + } + + /** + * 保存脱敏启用状态 + */ + public void saveDesensitizeOpened() { + tableData.setDesensitizeOpen(desensitizeOpen.isSelected()); + } + + /** + * 保存脱敏规则配置信息 + * @param tableDataDesensitizationBeans + */ + public void saveDesensitizationBeans(List tableDataDesensitizationBeans) { + tableData.setDesensitizationBeans(tableDataDesensitizationBeans); + } +} diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/preview/desensitization/view/setting/TableDataDesensitizationTableModel.java b/designer-base/src/main/java/com/fr/design/data/datapane/preview/desensitization/view/setting/TableDataDesensitizationTableModel.java new file mode 100644 index 0000000000..36322c3980 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/data/datapane/preview/desensitization/view/setting/TableDataDesensitizationTableModel.java @@ -0,0 +1,459 @@ +package com.fr.design.data.datapane.preview.desensitization.view.setting; + +import com.fr.data.desensitize.base.AbstractDesensitizationTableData; +import com.fr.data.desensitize.base.TableDataDesensitizationBean; +import com.fr.data.desensitize.rule.base.DesensitizationRule; +import com.fr.design.data.datapane.preview.desensitization.TableDataPreviewDesensitizeManager; +import com.fr.design.data.datapane.preview.desensitization.view.rule.DesensitizationRulePane; +import com.fr.design.dialog.BasicDialog; +import com.fr.design.dialog.DialogActionAdapter; +import com.fr.design.gui.ibutton.UIButton; +import com.fr.design.gui.icombobox.UIComboBox; +import com.fr.design.gui.icombocheckbox.UIComboCheckBox; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.gui.itableeditorpane.UITableEditAction; +import com.fr.design.gui.itableeditorpane.UITableModelAdapter; +import com.fr.design.gui.itextfield.UITextField; +import com.fr.design.i18n.Toolkit; +import com.fr.design.layout.TableLayout; +import com.fr.design.layout.TableLayoutHelper; +import com.fr.stable.StringUtils; +import com.fr.stable.collections.CollectionUtils; +import org.jetbrains.annotations.Nullable; + +import javax.swing.AbstractCellEditor; +import javax.swing.JPanel; +import javax.swing.JTable; +import javax.swing.SwingUtilities; +import javax.swing.event.CellEditorListener; +import javax.swing.event.ChangeEvent; +import javax.swing.table.TableCellEditor; +import javax.swing.table.TableCellRenderer; +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; + +/** + * 处理TableDataDesensitizationTablePane中TableEditPane的Model + * + * @author Yvan + * @version 11.0 + * Created by Yvan on 2022/9/23 + */ +public class TableDataDesensitizationTableModel extends UITableModelAdapter { + + private static final String APOSTROPHE = "..."; + + private AbstractDesensitizationTableData tableData; + + /** + * 当前数据集的所有列名 + */ + private List columnNames; + + /** + * key为用户组唯一标识(id拼接),value为用户组名称 + */ + private Map roleMap; + + private Component parent; + + private DesensitizationRuleChooser ruleChooser; + + private DesensitizationRuleDescriptionPane descriptionPane; + + public TableDataDesensitizationTableModel(AbstractDesensitizationTableData tableData, Component parent) { + // table相关 + super(new String[]{ + Toolkit.i18nText("Fine-Design_Report_Desensitization_Column"), + Toolkit.i18nText("Fine-Design_Report_Desensitization_Rule"), + StringUtils.EMPTY, + Toolkit.i18nText("Fine-Design_Report_Desensitization_Effected_Roles") + }); + // 一些数据相关 + this.tableData = tableData; + // 获取当前数据集的所有列名 + this.columnNames = TableDataPreviewDesensitizeManager.getInstance().getColumnNamesByTableData(tableData); + // 获取当前所有用户组 + this.roleMap = TableDataPreviewDesensitizeManager.getInstance().getAllRoles(); + this.parent = parent; + this.setColumnClass(new Class[]{ + // 列名选择 + ColumnNamesComboBox.class, + // 规则选择 + DesensitizationRuleChooser.class, + // 规则详情展示 + DesensitizationRuleDescriptionPane.class, + // 生效用户组选择 + EffectedRolesChooser.class + }); + this.setDefaultEditor(ColumnNamesComboBox.class, new ColumnNamesComboBox()); + this.ruleChooser = new DesensitizationRuleChooser(); + this.setDefaultEditor(DesensitizationRuleChooser.class, ruleChooser); + this.setDefaultRenderer(DesensitizationRuleChooser.class, ruleChooser); + this.descriptionPane = new DesensitizationRuleDescriptionPane(); + this.setDefaultEditor(DesensitizationRuleDescriptionPane.class, new DesensitizationRuleDescriptionPane()); + this.setDefaultEditor(EffectedRolesChooser.class, new EffectedRolesChooser()); + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + TableDataDesensitizationBean desensitizationBean = this.getList().get(rowIndex); + switch (columnIndex) { + case 0: + return getColumnNameValue(desensitizationBean); + case 1: + return desensitizationBean.getDesensitizationRule().getRuleName(); + case 2: + return DesensitizationRule.getDescription(desensitizationBean.getDesensitizationRule()); + case 3: + return matchRoleNamesByIds(desensitizationBean.getRoleIds()); + default: + return StringUtils.EMPTY; + } + } + + /** + * 根据当前选中的Bean,获取其对应的列名字段 + * @param desensitizationBean + * @return + */ + private Object getColumnNameValue(TableDataDesensitizationBean desensitizationBean) { + // 内部列名list为空时直接返回空字符串 + if (CollectionUtils.isEmpty(columnNames)) { + return StringUtils.EMPTY; + } + // bean的列字段index不对劲时,返回空字符串 + return validColumnIndex(desensitizationBean.getColumnIndex()) ? + columnNames.get(desensitizationBean.getColumnIndex()) : + StringUtils.EMPTY; + } + + /** + * 判断字段索引是否越界 + * @param columnIndex + * @return + */ + private boolean validColumnIndex(int columnIndex) { + return columnIndex >= 0 && columnIndex < columnNames.size(); + } + + /** + * 通过id匹配此用户组对应的部门职位名称(或者说自定义角色名称) + * + * @param roleIds + * @return + */ + private List matchRoleNamesByIds(Set roleIds) { + List result = new ArrayList<>(); + for (String roleId : roleIds) { + if (roleMap != null && roleMap.containsKey(roleId)) { + result.add(roleMap.get(roleId)); + } + } + return result; + } + + @Override + public boolean isCellEditable(int row, int col) { + return true; + } + + @Override + public UITableEditAction[] createAction() { + return new UITableEditAction[]{new AddDesensitizationAction(), new RemoveDesensitizationAction(parent)}; + } + + /** + * 获取当前选中的item,可能为null + * + * @return + */ + @Nullable + private TableDataDesensitizationBean getCurrentSelectBean() { + return table.getSelectedRow() == -1 ? + null : + getList().get(table.getSelectedRow()); + } + + /** + * 列名选择下拉框 + */ + private class ColumnNamesComboBox extends AbstractCellEditor implements TableCellEditor, TableCellRenderer { + + private UIComboBox columnNameComboBox; + + ColumnNamesComboBox() { + columnNameComboBox = new UIComboBox(columnNames.toArray(new String[0])); + this.addCellEditorListener(new CellEditorListener() { + @Override + public void editingStopped(ChangeEvent e) { + + TableDataDesensitizationBean desensitizationBean = getCurrentSelectBean(); + if (Objects.nonNull(desensitizationBean)) { + desensitizationBean.setColumnIndex(columnNameComboBox.getSelectedIndex()); + fireTableDataChanged(); + } + } + + @Override + public void editingCanceled(ChangeEvent e) { + + } + }); + } + + + @Override + public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { + columnNameComboBox.setSelectedIndex(getList().get(row).getColumnIndex()); + return columnNameComboBox; + } + + @Override + public Object getCellEditorValue() { + Object selectedItem = columnNameComboBox.getSelectedItem(); + return Objects.isNull(selectedItem) ? StringUtils.EMPTY : selectedItem.toString(); + } + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + columnNameComboBox.setSelectedIndex(getList().get(row).getColumnIndex()); + return columnNameComboBox; + } + } + + private class DesensitizationRuleChooser extends AbstractCellEditor implements TableCellEditor, TableCellRenderer { + /** + * 规则选择页面 + */ + private JPanel choosePane; + /** + * 规则名称 + */ + private UITextField ruleNameTextField; + /** + * 规则选择按钮 + */ + private UIButton chooseButton; + /** + * 规则 + */ + private DesensitizationRule rule; + + private ActionListener chooseRuleListener = new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + DesensitizationRulePane rulePane = new DesensitizationRulePane(); + BasicDialog ruleDialog = rulePane.showWindowWithCustomSize(SwingUtilities.getWindowAncestor(parent), new DialogActionAdapter() { + @Override + public void doOk() { + rule = rulePane.updateBean(); + TableDataDesensitizationBean desensitizationBean = getCurrentSelectBean(); + if (Objects.nonNull(desensitizationBean) && Objects.nonNull(rule)) { + desensitizationBean.setDesensitizationRule(rule); + fireTableDataChanged(); + } + rule = null; + } + }, BasicDialog.DEFAULT); + ruleDialog.setVisible(true); + } + }; + + DesensitizationRuleChooser() { + // 规则名称展示 + ruleNameTextField = new UITextField(); + ruleNameTextField.setEnabled(false); + // 规则选择按钮 + chooseButton = new UIButton(APOSTROPHE); + chooseButton.addActionListener(chooseRuleListener); + // 规则选择页面 + Component[][] templateChooserComponent = {{ruleNameTextField, chooseButton}}; + double[] rowSize = {TableLayout.PREFERRED}; + double[] columnSize = {TableLayout.FILL, 22}; + choosePane = TableLayoutHelper.createCommonTableLayoutPane(templateChooserComponent, rowSize, columnSize, 0); + this.addCellEditorListener(new CellEditorListener() { + + @Override + public void editingCanceled(ChangeEvent e) { + + } + + @Override + public void editingStopped(ChangeEvent e) { + TableDataDesensitizationBean desensitizationBean = getCurrentSelectBean(); + if (Objects.nonNull(desensitizationBean) && Objects.nonNull(rule)) { + desensitizationBean.setDesensitizationRule(rule); + fireTableDataChanged(); + } + } + }); + } + + @Override + public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { + ruleNameTextField.setText(getList().get(row).getDesensitizationRule().getRuleName()); + return choosePane; + } + + @Override + public Object getCellEditorValue() { + return ruleNameTextField.getText(); + } + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + ruleNameTextField.setText((String) value); + return choosePane; + } + } + + private class DesensitizationRuleDescriptionPane extends AbstractCellEditor implements TableCellEditor { + + private UILabel descriptionLabel; + + DesensitizationRuleDescriptionPane() { + // 规则描述 + this.descriptionLabel = new UILabel(); + } + + /** + * 根据脱敏规则信息,刷新下规则描述文字,主要用于与规则选择器的联动 + * + * @param desensitizationRule + */ + public void refreshDescription(DesensitizationRule desensitizationRule) { + this.descriptionLabel.setText(DesensitizationRule.getDescription(desensitizationRule)); + } + + @Override + public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { + refreshDescription(getList().get(row).getDesensitizationRule()); + return descriptionLabel; + } + + @Override + public Object getCellEditorValue() { + return this.descriptionLabel.getText(); + } + } + + private class EffectedRolesChooser extends AbstractCellEditor implements TableCellEditor, TableCellRenderer { + + private UIComboCheckBox rolesCheckBox; + + EffectedRolesChooser() { + this.rolesCheckBox = new UIComboCheckBox(roleMap.values().toArray(), true); + this.addCellEditorListener(new CellEditorListener() { + @Override + public void editingStopped(ChangeEvent e) { + TableDataDesensitizationBean desensitizationBean = getCurrentSelectBean(); + if (Objects.nonNull(desensitizationBean)) { + desensitizationBean.setRoleIds(generateRolesIdsBySelectedValues()); + fireTableDataChanged(); + } + } + + @Override + public void editingCanceled(ChangeEvent e) { + + } + }); + } + + @Override + public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { + rolesCheckBox.setSelectedValues(generateRolesCheckBoxSelectedValues(getList().get(row))); + return rolesCheckBox; + } + + /** + * 根据当前的规则配置信息,生成选中的rolesMap用来展示 + * @param desensitizationBean + * @return + */ + private Map generateRolesCheckBoxSelectedValues(TableDataDesensitizationBean desensitizationBean) { + Map result = new HashMap<>(roleMap.size()); + for (Map.Entry roleEntry : roleMap.entrySet()) { + String roleId = roleEntry.getKey(); + String roleName = roleEntry.getValue(); + if (desensitizationBean.getRoleIds().contains(roleId)) { + result.put(roleName, true); + } else { + result.put(roleName, false); + } + } + return result; + } + + /** + * 根据当前的RoleName选择项,生成其对应的RoleId的set存入规则配置信息 + * @return + */ + private Set generateRolesIdsBySelectedValues() { + Set result = new LinkedHashSet<>(); + Object[] selectedValues = rolesCheckBox.getSelectedValues(); + for (Object selectedValue : selectedValues) { + String selectedRoleName = (String) selectedValue; + if (roleMap.containsValue(selectedRoleName)) { + Optional> matchedEntry = roleMap.entrySet().stream().filter(entry -> StringUtils.equals(entry.getValue(), selectedRoleName)).findFirst(); + matchedEntry.ifPresent(stringStringEntry -> result.add(stringStringEntry.getKey())); + } + } + return result; + } + + @Override + public Object getCellEditorValue() { + return rolesCheckBox.getSelectedValues(); + } + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + rolesCheckBox.setSelectedValues(generateRolesCheckBoxSelectedValues(getList().get(row))); + return rolesCheckBox; + } + } + + + private class AddDesensitizationAction extends AddTableRowAction { + + public AddDesensitizationAction() { + this.setName(Toolkit.i18nText("Fine-Design_Report_Desensitization_Add")); + this.setSmallIcon("/com/fr/design/standard/add/add_black", false); + } + + @Override + public void actionPerformed(ActionEvent e) { + super.actionPerformed(e); + // 添加一条空白数据 + addRow(TableDataDesensitizationBean.createEmyptBean()); + fireTableDataChanged(); + table.getSelectionModel().setSelectionInterval(table.getRowCount() - 1, table.getRowCount() - 1); + } + } + + private class RemoveDesensitizationAction extends DeleteAction { + + public RemoveDesensitizationAction(Component component) { + super(component); + this.setName(Toolkit.i18nText("Fine-Design_Basic_Base_Remove")); + this.setSmallIcon("/com/fr/design/standard/remove/remove_red", false); + } + + @Override + public void actionPerformed(ActionEvent e) { + super.actionPerformed(e); + } + } + +} diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/preview/desensitization/view/setting/TableDataDesensitizationTablePane.java b/designer-base/src/main/java/com/fr/design/data/datapane/preview/desensitization/view/setting/TableDataDesensitizationTablePane.java new file mode 100644 index 0000000000..77546b9ef1 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/data/datapane/preview/desensitization/view/setting/TableDataDesensitizationTablePane.java @@ -0,0 +1,64 @@ +package com.fr.design.data.datapane.preview.desensitization.view.setting; + +import com.fr.data.desensitize.base.AbstractDesensitizationTableData; +import com.fr.data.desensitize.base.TableDataDesensitizationBean; +import com.fr.design.gui.itableeditorpane.UITableEditorPane; +import com.fr.design.layout.FRGUIPaneFactory; + +import javax.swing.JPanel; +import java.awt.BorderLayout; +import java.awt.Component; +import java.util.List; + +/** + * 脱敏字段设置页面中的Table + * @author Yvan + * @version 11.0 + * Created by Yvan on 2022/9/14 + */ +public class TableDataDesensitizationTablePane extends JPanel { + + /** + * 脱敏数据集 + */ + private AbstractDesensitizationTableData tableData; + + /** + * 父页面 + */ + private Component parent; + + /** + * 脱敏信息Table + */ + private UITableEditorPane editorPane; + + public TableDataDesensitizationTablePane(AbstractDesensitizationTableData tableData, Component parent) { + this.tableData = tableData; + this.parent = parent; + initComponent(); + } + + private void initComponent() { + this.setLayout(FRGUIPaneFactory.createBorderLayout()); + this.editorPane = new UITableEditorPane<>(new TableDataDesensitizationTableModel(tableData, parent)); + this.editorPane.setHeaderResizing(false); + this.add(editorPane, BorderLayout.CENTER); + } + + /** + * 展示此TableData中已配置的脱敏规则信息 + * @param tableData + */ + public void populateDesensitizationSetting(AbstractDesensitizationTableData tableData) { + this.tableData = tableData; + editorPane.populate(tableData.getDesensitizationBeans().toArray(new TableDataDesensitizationBean[0])); + } + + /** + * 获取当前对TableData的配置脱敏规则信息 + */ + public List updateDesensitizationSetting() { + return editorPane.update(); + } +} diff --git a/designer-base/src/main/resources/com/fr/design/images/control/refresh_normal.svg b/designer-base/src/main/resources/com/fr/design/images/control/refresh_normal.svg new file mode 100644 index 0000000000..e83a52740e --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/images/control/refresh_normal.svg @@ -0,0 +1,7 @@ + + + icon_刷新_normal + + + + diff --git a/designer-base/src/main/resources/com/fr/design/standard/add/add_black_normal.svg b/designer-base/src/main/resources/com/fr/design/standard/add/add_black_normal.svg new file mode 100644 index 0000000000..2c63e640bc --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/add/add_black_normal.svg @@ -0,0 +1,3 @@ + + + diff --git a/designer-base/src/main/resources/com/fr/design/standard/debug/debug_disabled.svg b/designer-base/src/main/resources/com/fr/design/standard/debug/debug_disabled.svg new file mode 100644 index 0000000000..520d126a0f --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/debug/debug_disabled.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/designer-base/src/main/resources/com/fr/design/standard/debug/debug_normal.svg b/designer-base/src/main/resources/com/fr/design/standard/debug/debug_normal.svg new file mode 100644 index 0000000000..80abccaba9 --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/debug/debug_normal.svg @@ -0,0 +1,4 @@ + + + + diff --git a/designer-base/src/main/resources/com/fr/design/standard/edit/edit_disabled.svg b/designer-base/src/main/resources/com/fr/design/standard/edit/edit_disabled.svg new file mode 100644 index 0000000000..6ac7d0b932 --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/edit/edit_disabled.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/designer-base/src/main/resources/com/fr/design/standard/edit/edit_normal.svg b/designer-base/src/main/resources/com/fr/design/standard/edit/edit_normal.svg new file mode 100644 index 0000000000..3ab4c0b297 --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/edit/edit_normal.svg @@ -0,0 +1,3 @@ + + + diff --git a/designer-base/src/main/resources/com/fr/design/standard/remove/remove_red_normal.svg b/designer-base/src/main/resources/com/fr/design/standard/remove/remove_red_normal.svg new file mode 100644 index 0000000000..74ff5ab385 --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/remove/remove_red_normal.svg @@ -0,0 +1,7 @@ + + + icon_关闭_red + + + +