refactor(配置管理): 重构配置绑定组件结构并添加布尔类型支持

将原有配置绑定组件重构为泛型抽象基类,统一处理不同类型配置的绑定逻辑。新增BooleanConfig支持布尔类型配置,并优化了LocalDateTimeConfig的转换器处理。

重构后的AbstractConfigBounder提供通用属性绑定和变更处理,子类只需实现特定类型的双向绑定逻辑。同时调整了相关UI界面,添加了布尔配置开关控件。
This commit is contained in:
2025-08-28 15:09:54 +08:00
parent c69d3f1af2
commit c793c0925e
10 changed files with 238 additions and 127 deletions

View File

@@ -1,11 +1,13 @@
package com.ecep.contract.manager.cloud.u8; package com.ecep.contract.manager.cloud.u8;
import org.controlsfx.control.ToggleSwitch;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.ecep.contract.manager.cloud.u8.ctx.CompanyCtx; import com.ecep.contract.manager.cloud.u8.ctx.CompanyCtx;
import com.ecep.contract.manager.cloud.u8.ctx.ContractCtx; import com.ecep.contract.manager.cloud.u8.ctx.ContractCtx;
import com.ecep.contract.manager.ds.other.BooleanConfig;
import com.ecep.contract.manager.ds.other.LocalDateConfig; import com.ecep.contract.manager.ds.other.LocalDateConfig;
import com.ecep.contract.manager.ds.other.LocalDateTimeConfig; import com.ecep.contract.manager.ds.other.LocalDateTimeConfig;
import com.ecep.contract.manager.ds.other.StringConfig; import com.ecep.contract.manager.ds.other.StringConfig;
@@ -33,29 +35,37 @@ public class YongYouU8ConfigWindowController extends BaseController {
@FXML @FXML
private TextField sync_elapse; private TextField sync_elapse;
@FXML
private ToggleSwitch use_latest_id;
LocalDateConfig config1 = new LocalDateConfig(CompanyCtx.AUTO_CREATE_COMPANY_AFTER);
LocalDateTimeConfig config2 = new LocalDateTimeConfig(ContractCtx.KEY_SYNC_BY_LATEST_DATE);
StringConfig config3 = new StringConfig(ContractCtx.KEY_SYNC_BY_LATEST_ID);
StringConfig config4 = new StringConfig(ContractCtx.KEY_SYNC_ELAPSE);
BooleanConfig config5 = new BooleanConfig(ContractCtx.KEY_SYNC_USE_LATEST_ID);
@Override @Override
public void onShown(WindowEvent windowEvent) { public void onShown(WindowEvent windowEvent) {
super.onShown(windowEvent); super.onShown(windowEvent);
getTitle().set("用友U8配置"); getTitle().set("用友U8配置");
LocalDateConfig config1 = new LocalDateConfig(CompanyCtx.AUTO_CREATE_COMPANY_AFTER); auto_create_company_after.setConverter(getCurrentEmployee().getLocalDateStringConverter());
config1.setPicker(auto_create_company_after); config1.setControl(auto_create_company_after);
config1.initialize(); config1.initialize();
LocalDateTimeConfig config2 = new LocalDateTimeConfig(ContractCtx.KEY_SYNC_BY_LATEST_DATE); config2.setControl(contract_latest_date);
config2.setTextField(contract_latest_date); config2.setControlConverter(getCurrentEmployee().getLocalDateTimeStringConverter());
config2.setConverter(getCurrentEmployee().getLocalDateTimeStringConverter());
config2.initialize(); config2.initialize();
StringConfig config3 = new StringConfig(ContractCtx.KEY_SYNC_BY_LATEST_ID); config3.setControl(contract_latest_id);
config3.setTextField(contract_latest_id);
config3.initialize(); config3.initialize();
StringConfig config4 = new StringConfig(ContractCtx.KEY_SYNC_ELAPSE); config4.setControl(sync_elapse);
config4.setTextField(sync_elapse);
config4.initialize(); config4.initialize();
config5.setControl(use_latest_id);
config5.initialize();
} }
} }

View File

@@ -104,21 +104,18 @@ public class ContractCtx extends AbstractYongYouU8Ctx {
private InventoryCtx inventoryCtx; private InventoryCtx inventoryCtx;
@Setter @Setter
private ProjectCtx projectCtx; private ProjectCtx projectCtx;
@Getter @Getter
@Setter @Setter
private int vendorEntityUpdateDelayDays = 1; private int vendorEntityUpdateDelayDays = 1;
@Getter @Getter
@Setter @Setter
private int customerEntityUpdateDelayDays = 1; private int customerEntityUpdateDelayDays = 1;
public ContractService getContractService() { public ContractService getContractService() {
if (contractService == null) { if (contractService == null) {
contractService = getBean(ContractService.class); contractService = getBean(ContractService.class);
} }
return contractService; return contractService;
} }
ContractItemService getContractItemService() { ContractItemService getContractItemService() {
if (contractItemService == null) { if (contractItemService == null) {
contractItemService = getBean(ContractItemService.class); contractItemService = getBean(ContractItemService.class);

View File

@@ -145,18 +145,6 @@ public class ContractService implements ViewModelService<Contract, ContractViewM
return contractRepository.findByProjectIdAndPayWay(project.getId(), ContractPayWay.RECEIVE).orElse(null); return contractRepository.findByProjectIdAndPayWay(project.getId(), ContractPayWay.RECEIVE).orElse(null);
} }
/**
* 更新合同最新ID和日期
*/
public void updateLatestIdAndDate(Integer latestId, LocalDateTime latestDate) {
if (latestId != null) {
confService.set(ContractCtx.KEY_SYNC_BY_LATEST_ID, String.valueOf(latestId));
}
if (latestDate != null) {
confService.set(ContractCtx.KEY_SYNC_BY_LATEST_DATE, latestDate.toString());
}
}
public File getBasePath() { public File getBasePath() {
return new File(confService.getString(CONTRACT_BASE_PATH)); return new File(confService.getString(CONTRACT_BASE_PATH));
} }

View File

@@ -1,5 +1,6 @@
package com.ecep.contract.manager.ds.other; package com.ecep.contract.manager.ds.other;
import java.time.LocalDateTime;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import com.ecep.contract.manager.Desktop; import com.ecep.contract.manager.Desktop;
@@ -7,13 +8,37 @@ import com.ecep.contract.manager.SpringApp;
import com.ecep.contract.manager.ds.other.model.SysConf; import com.ecep.contract.manager.ds.other.model.SysConf;
import com.ecep.contract.manager.ds.other.service.SysConfService; import com.ecep.contract.manager.ds.other.service.SysConfService;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.Control;
import lombok.Getter;
import lombok.Setter; import lombok.Setter;
public abstract class AbstractConfigBounder implements ConfigBounder { public abstract class AbstractConfigBounder<T> implements ConfigBounder {
@Setter @Setter
private SysConfService confService; private SysConfService confService;
String key; String key;
SysConf conf; SysConf conf;
@Getter
@Setter
Control control;
@Getter
@Setter
javafx.util.StringConverter<T> converter;
@Setter
Property<T> property;
ObjectProperty<LocalDateTime> modified = new SimpleObjectProperty<>(this, "modified");
ObjectProperty<LocalDateTime> created = new SimpleObjectProperty<>(this, "created");
public AbstractConfigBounder(String key) {
this.key = key;
}
@Override @Override
public CompletableFuture<Void> runAsync(Runnable runnable) { public CompletableFuture<Void> runAsync(Runnable runnable) {
@@ -30,4 +55,70 @@ public abstract class AbstractConfigBounder implements ConfigBounder {
} }
return confService; return confService;
} }
@Override
public void initialize() {
getControl().setDisable(true);
runAsync(this::asyncHandle);
}
protected void asyncHandle() {
conf = getConfService().findById(key);
if (conf == null) {
conf = new SysConf();
conf.setId(key);
conf.setCreated(LocalDateTime.now());
conf.setModified(LocalDateTime.now());
conf = getConfService().save(conf);
}
Property<T> property = getProperty();
property.addListener(this::propertyChanged);
bindBidirectional(property);
modified.set(conf.getModified());
created.set(conf.getCreated());
getControl().setDisable(false);
}
public Property<T> getProperty() {
if (property == null) {
T value = getConverter().fromString(conf.getValue());
property = createProperty(value);
setProperty(property);
}
return property;
}
public ObjectProperty<LocalDateTime> getModifiedProperty() {
return modified;
}
public ObjectProperty<LocalDateTime> getCreatedProperty() {
return created;
}
@SuppressWarnings("unchecked")
protected Property<T> createProperty(T value) {
if (value instanceof Boolean b) {
return (Property<T>) new SimpleBooleanProperty(b);
} else if (value instanceof Integer i) {
return (Property<T>) new SimpleIntegerProperty(i);
} else if (value instanceof Double d) {
return (Property<T>) new SimpleDoubleProperty(d);
} else if (value instanceof String s) {
return (Property<T>) new SimpleStringProperty(s);
}
return new SimpleObjectProperty<>(value);
}
abstract void bindBidirectional(Property<T> property);
void propertyChanged(ObservableValue<? extends T> observable, T oldValue,
T newValue) {
conf.setValue(getConverter().toString(newValue));
conf.setModified(LocalDateTime.now());
SysConf saved = getConfService().save(conf);
modified.set(saved.getModified());
created.set(saved.getCreated());
}
} }

View File

@@ -0,0 +1,40 @@
package com.ecep.contract.manager.ds.other;
import org.controlsfx.control.ToggleSwitch;
import javafx.beans.property.Property;
import javafx.util.StringConverter;
import javafx.util.converter.BooleanStringConverter;
public class BooleanConfig extends AbstractConfigBounder<Boolean> {
public BooleanConfig(String key) {
super(key);
}
@Override
public ToggleSwitch getControl() {
return (ToggleSwitch) super.getControl();
}
@Override
public StringConverter<Boolean> getConverter() {
StringConverter<Boolean> converter = super.getConverter();
if (converter == null) {
converter = new BooleanStringConverter();
setConverter(converter);
}
return converter;
}
@Override
protected Property<Boolean> createProperty(Boolean value) {
// fixbug when value is null
value = value == null ? false : value;
return super.createProperty(value);
}
@Override
void bindBidirectional(Property<Boolean> property) {
getControl().selectedProperty().bindBidirectional(property);
}
}

View File

@@ -3,51 +3,35 @@ package com.ecep.contract.manager.ds.other;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import com.ecep.contract.manager.ds.other.model.SysConf;
import javafx.beans.property.Property; import javafx.beans.property.Property;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.DatePicker; import javafx.scene.control.DatePicker;
import javafx.util.StringConverter;
import javafx.util.converter.LocalDateStringConverter; import javafx.util.converter.LocalDateStringConverter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@NoArgsConstructor
public class LocalDateConfig extends AbstractConfigBounder {
Property<LocalDate> property;
@Setter
javafx.util.StringConverter<LocalDate> converter;
@Setter
DatePicker picker;
public class LocalDateConfig extends AbstractConfigBounder<LocalDate> {
public LocalDateConfig(String string) { public LocalDateConfig(String string) {
key = string; super(string);
} }
@Override @Override
public void initialize() { public DatePicker getControl() {
picker.setDisable(true); return (DatePicker) super.getControl();
}
@Override
public StringConverter<LocalDate> getConverter() {
StringConverter<LocalDate> converter = super.getConverter();
if (converter == null) { if (converter == null) {
converter = new LocalDateStringConverter(DateTimeFormatter.ISO_LOCAL_DATE, null); converter = new LocalDateStringConverter(DateTimeFormatter.ISO_LOCAL_DATE, null);
setConverter(converter);
} }
picker.setConverter(converter); return converter;
runAsync(() -> {
conf = getConfService().findById(key);
LocalDate date = converter.fromString(conf.getValue());
property = new SimpleObjectProperty<>(date);
property.addListener(this::propertyChanged);
picker.valueProperty().bindBidirectional(property);
picker.setDisable(false);
});
} }
void propertyChanged(ObservableValue<? extends LocalDate> observable, LocalDate oldValue, LocalDate newValue) { @Override
conf.setValue(converter.toString(newValue)); void bindBidirectional(Property<LocalDate> property) {
SysConf saved = getConfService().save(conf); getControl().valueProperty().bindBidirectional(property);
saved.getValue();
} }
} }

View File

@@ -3,49 +3,43 @@ package com.ecep.contract.manager.ds.other;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import com.ecep.contract.manager.ds.other.model.SysConf;
import javafx.beans.property.Property; import javafx.beans.property.Property;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.TextField; import javafx.scene.control.TextField;
import javafx.util.StringConverter;
import javafx.util.converter.LocalDateTimeStringConverter; import javafx.util.converter.LocalDateTimeStringConverter;
import lombok.NoArgsConstructor; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
@NoArgsConstructor public class LocalDateTimeConfig extends AbstractConfigBounder<LocalDateTime> {
public class LocalDateTimeConfig extends AbstractConfigBounder { @Getter
Property<LocalDateTime> property;
@Setter @Setter
javafx.util.StringConverter<LocalDateTime> converter; private javafx.util.StringConverter<LocalDateTime> controlConverter;
@Setter
TextField textField;
public LocalDateTimeConfig(String string) { public LocalDateTimeConfig(String string) {
key = string; super(string);
} }
@Override @Override
public void initialize() { public TextField getControl() {
textField.setDisable(true); return (TextField) super.getControl();
}
@Override
public javafx.util.StringConverter<LocalDateTime> getConverter() {
javafx.util.StringConverter<LocalDateTime> converter = super.getConverter();
if (converter == null) { if (converter == null) {
converter = new LocalDateTimeStringConverter(DateTimeFormatter.ISO_LOCAL_DATE_TIME, null); converter = new LocalDateTimeStringConverter(DateTimeFormatter.ISO_LOCAL_DATE_TIME, null);
setConverter(converter);
} }
runAsync(() -> { return converter;
conf = getConfService().findById(key);
LocalDateTime dateTime = LocalDateTime.parse(conf.getValue());
property = new SimpleObjectProperty<>(dateTime);
property.addListener(this::propertyChanged);
textField.textProperty().bindBidirectional(property, converter);
textField.setDisable(false);
});
} }
void propertyChanged(ObservableValue<? extends LocalDateTime> observable, LocalDateTime oldValue, @Override
LocalDateTime newValue) { void bindBidirectional(Property<LocalDateTime> property) {
conf.setValue(newValue.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)); StringConverter<LocalDateTime> converter = getControlConverter();
SysConf saved = getConfService().save(conf); if (converter == null) {
saved.getValue(); converter = getConverter();
}
getControl().textProperty().bindBidirectional(property, converter);
} }
} }

View File

@@ -1,42 +1,42 @@
package com.ecep.contract.manager.ds.other; package com.ecep.contract.manager.ds.other;
import com.ecep.contract.manager.ds.other.model.SysConf;
import javafx.beans.property.Property; import javafx.beans.property.Property;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.TextField; import javafx.scene.control.TextField;
import lombok.NoArgsConstructor; import javafx.util.StringConverter;
import lombok.Setter;
@NoArgsConstructor public class StringConfig extends AbstractConfigBounder<String> {
public class StringConfig extends AbstractConfigBounder {
@Setter
private TextField textField;
String key;
SysConf conf;
Property<String> property;
public StringConfig(String string) { public StringConfig(String string) {
key = string; super(string);
} }
public void initialize() { @Override
textField.setDisable(true); public TextField getControl() {
runAsync(() -> { return (TextField) super.getControl();
conf = getConfService().findById(key);
property = new SimpleStringProperty(conf.getValue());
property.addListener(this::propertyChanged);
textField.textProperty().bindBidirectional(property);
textField.setDisable(false);
});
} }
void propertyChanged(ObservableValue<? extends String> observable, String oldValue, String newValue) { @Override
conf.setValue(newValue); public StringConverter<String> getConverter() {
SysConf saved = getConfService().save(conf); StringConverter<String> converter = super.getConverter();
saved.getValue(); if (converter == null) {
converter = new StringConverter<String>() {
@Override
public String toString(String object) {
return object;
}
@Override
public String fromString(String string) {
return string;
}
};
setConverter(converter);
}
return converter;
}
@Override
void bindBidirectional(Property<String> property) {
getControl().textProperty().bindBidirectional(property);
} }
} }

View File

@@ -1,14 +1,15 @@
package com.ecep.contract.manager.ds.other.model; package com.ecep.contract.manager.ds.other.model;
import java.time.LocalDateTime;
import org.hibernate.annotations.ColumnDefault;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.Id; import jakarta.persistence.Id;
import jakarta.persistence.Table; import jakarta.persistence.Table;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import org.hibernate.annotations.ColumnDefault;
import java.time.Instant;
@Getter @Getter
@Setter @Setter
@@ -24,9 +25,9 @@ public class SysConf {
@ColumnDefault("CURRENT_TIMESTAMP") @ColumnDefault("CURRENT_TIMESTAMP")
@Column(name = "MODIFIED") @Column(name = "MODIFIED")
private Instant modified; private LocalDateTime modified;
@Column(name = "CREATED") @Column(name = "CREATED")
private Instant created; private LocalDateTime created;
} }

View File

@@ -7,10 +7,10 @@
<?import javafx.scene.layout.GridPane?> <?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?> <?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.layout.VBox?> <?import javafx.scene.layout.VBox?>
<?import org.controlsfx.control.ToggleSwitch?>
<VBox prefHeight="400.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/22" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.ecep.contract.manager.cloud.u8.YongYouU8ConfigWindowController"> <VBox prefHeight="400.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/22" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.ecep.contract.manager.cloud.u8.YongYouU8ConfigWindowController">
<children> <children>
<Label text="用友U8配置" />
<GridPane> <GridPane>
<columnConstraints> <columnConstraints>
<ColumnConstraints halignment="RIGHT" hgrow="SOMETIMES" minWidth="50.0" prefWidth="250.0" /> <ColumnConstraints halignment="RIGHT" hgrow="SOMETIMES" minWidth="50.0" prefWidth="250.0" />
@@ -21,19 +21,25 @@
<RowConstraints fillHeight="false" minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> <RowConstraints fillHeight="false" minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints fillHeight="false" minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> <RowConstraints fillHeight="false" minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints fillHeight="false" minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> <RowConstraints fillHeight="false" minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints fillHeight="false" minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints> </rowConstraints>
<children> <children>
<Label text="同步时未创建公司开发日期在此之后的自动创建" /> <Label text="同步时公司未创建时, 公司开发日期在此之后的自动创建" GridPane.columnIndex="2" GridPane.rowIndex="1" />
<DatePicker fx:id="auto_create_company_after" GridPane.columnIndex="2" /> <DatePicker fx:id="auto_create_company_after" GridPane.columnIndex="2" />
<Label text="cloud.u8.contract.latestDate" GridPane.rowIndex="1" /> <Label text="cloud.u8.contract.latestDate" GridPane.rowIndex="4" />
<TextField fx:id="contract_latest_date" GridPane.columnIndex="2" GridPane.rowIndex="1" /> <TextField fx:id="contract_latest_date" GridPane.columnIndex="2" GridPane.rowIndex="4" />
<Label text="cloud.u8.contract.latestId" GridPane.rowIndex="2" /> <Label text="cloud.u8.contract.latestId" GridPane.rowIndex="5" />
<TextField fx:id="contract_latest_id" GridPane.columnIndex="2" GridPane.rowIndex="2" /> <TextField fx:id="contract_latest_id" GridPane.columnIndex="2" GridPane.rowIndex="5" />
<Label text="cloud.u8.sync.elapse" GridPane.rowIndex="3" /> <Label text="cloud.u8.sync.elapse" GridPane.rowIndex="6" />
<TextField fx:id="sync_elapse" GridPane.columnIndex="2" GridPane.rowIndex="3" /> <TextField fx:id="sync_elapse" GridPane.columnIndex="2" GridPane.rowIndex="6" />
<Label text="创建在此之后日期的公司" />
<ToggleSwitch fx:id="use_latest_id" GridPane.columnIndex="2" GridPane.rowIndex="2" />
<Label text="使用latestId同步" GridPane.rowIndex="2" />
<Label text="合同同步时是否使用最后更新的Id来判断更新范围否则使用最后更新的合同日期来判断更新范围" GridPane.columnIndex="2" GridPane.rowIndex="3" />
</children> </children>
</GridPane> </GridPane>
</children> </children>