# 客户端转换器类规则

## 1. 目录结构
- 所有类型转换器类必须位于 `com.ecep.contract.converter` 包下
- 按照实体类型组织，无需进一步分包

## 2. 命名规范
- 类名格式：`[EntityName]StringConverter`
- 例如：`ContractStringConverter`、`CompanyStringConverter`、`VendorStringConverter`

## 3. 继承关系
### 3.1 基础转换器类型
- 直接继承 `javafx.util.StringConverter<T>` 接口
- 适用于简单的转换场景
- 例如：
```java
public class ContractStringConverter extends StringConverter<ContractVo> {
    // 实现代码
}
```

### 3.2 增强转换器类型
- 继承自定义的 `EntityStringConverter<T>` 抽象类
- 适用于需要更多功能（如延迟初始化、搜索建议等）的场景
- 大多数业务实体转换器应使用此类型
- 例如：
```java
public class CompanyStringConverter extends EntityStringConverter<CompanyVo> {
    // 实现代码
}
```

### 3.3 枚举转换器类型
- 继承 `EnumEntityStringConverter<E extends Enum<?>, T extends BaseEnumEntity<E>>`
- 专门用于处理枚举类型实体
- 例如：
```java
public class EnumEntityStringConverter<E extends Enum<?>, T extends BaseEnumEntity<E>> extends StringConverter<T> {
    // 实现代码
}
```

## 4. 核心接口实现
### 4.1 StringConverter<T> 接口
- 所有转换器必须实现 `toString(T object)` 方法
- 所有转换器必须实现 `fromString(String string)` 方法

## 5. Spring框架集成
### 5.1 组件注解
- 增强转换器类型通常使用 `@Component` 注解标记为Spring组件
- 结合 `@Lazy` 注解实现延迟加载
- 例如：
```java
@Lazy
@Component
public class CompanyStringConverter extends EntityStringConverter<CompanyVo> {
    // 实现代码
}
```

### 5.2 依赖注入
- 使用 `@Autowired` 注解注入对应的Service实例
- 通常也需要为注入的Service添加 `@Lazy` 注解
- 例如：
```java
@Lazy
@Autowired
private CompanyService service;
```

## 6. 初始化机制
### 6.1 构造函数
- 基础转换器通常使用构造函数接收Service实例
- 例如：
```java
public ContractStringConverter(ContractService service) {
    this.service = service;
}
```

### 6.2 初始化方法
- 增强转换器使用 `@PostConstruct` 注解标记初始化方法
- 在初始化方法中配置转换器的各种功能
- 例如：
```java
@PostConstruct
private void init() {
    setInitialized(project -> service.findById(project.getId()));
    setSuggestion(service::search);
    setFromString(service::findByName);
}
```

## 7. EntityStringConverter 特性配置
继承自 EntityStringConverter 的增强转换器可以配置以下特性：

### 7.1 对象初始化
- 使用 `setInitialized(Function<T, T> initialized)` 配置对象延迟初始化逻辑
- 通常调用Service的findById方法
- 例如：`setInitialized(project -> service.findById(project.getId()))`

### 7.2 搜索建议
- 使用 `setSuggestion(Function<String, List<T>> suggestion)` 配置自动补全数据提供方法
- 通常调用Service的search方法
- 例如：`setSuggestion(service::search)`

### 7.3 字符串转换
- 使用 `setFromString(Function<String, T> fromString)` 配置从字符串到对象的转换逻辑
- 通常调用Service的findByName方法
- 例如：`setFromString(service::findByName)`

### 7.4 格式化器
- 使用 `setFormater(Function<T, String> formater)` 配置自定义格式化逻辑
- 用于自定义对象的字符串表示
- 例如：`setFormater(contract -> contract.getCode() + " " + contract.getName())`

## 8. 核心方法实现规范
### 8.1 toString(T object) 方法
- 将对象转换为可读的字符串表示
- 必须处理null情况
- 通常返回对象的名称、编码或其他关键标识
- 对于基础转换器：
```java
@Override
public String toString(ContractVo cc) {
    return cc.getCode() + " " + cc.getName();
}
```
- 对于枚举转换器：
```java
@Override
public String toString(T object) {
    return object == null ? "" : object.getValue();
}
```

### 8.2 fromString(String string) 方法
- 从字符串还原对象实例
- 通常调用Service的findByName或findByCode方法
- 例如：
```java
@Override
public ContractVo fromString(String string) {
    return service.findByCode(string);
}
```
- 对于某些场景（如枚举转换器），可能返回null

## 9. 与Service类的关联
### 9.1 Service中的转换器创建
- 在Service类中通常会创建并持有对应的StringConverter实例
- 实现 `getStringConverter()` 方法返回转换器实例
- 例如：
```java
@Service
@CacheConfig(cacheNames = "contract-cache")
public class ContractService extends QueryService<ContractVo, ContractViewModel> {
    
    private final ContractStringConverter stringConverter = new ContractStringConverter(this);
    
    @Override
    public StringConverter<ContractVo> getStringConverter() {
        return stringConverter;
    }
}
```

### 9.2 Service方法支持
- Service需提供 `findByName`、`findById`、`search` 等方法供转换器使用
- 这些方法通常需要添加缓存注解以提高性能

## 10. 使用场景
StringConverter主要用于以下场景：
- ComboBox、ChoiceBox等选择组件的数据绑定和显示
- 表单数据的绑定和转换
- 支持用户输入的自动补全功能
- 在表格、列表等UI组件中显示对象的可读表示

## 11. 最佳实践
- 对于大多数业务实体，优先使用继承 `EntityStringConverter<T>` 的增强转换器
- 使用Spring的依赖注入和组件管理功能
- 正确处理null值和空字符串
- 为频繁调用的Service方法添加缓存支持
- 实现合理的toString逻辑，提供有意义的对象表示

## 12. 示例代码结构
### 12.1 基础转换器示例
```java
package com.ecep.contract.converter;

import com.ecep.contract.service.ContractService;
import com.ecep.contract.vo.ContractVo;
import javafx.util.StringConverter;

public class ContractStringConverter extends StringConverter<ContractVo> {
    private final ContractService service;
    
    public ContractStringConverter(ContractService service) {
        this.service = service;
    }
    
    @Override
    public String toString(ContractVo cc) {
        return cc == null ? "" : cc.getCode() + " " + cc.getName();
    }
    
    @Override
    public ContractVo fromString(String string) {
        return string == null || string.isEmpty() ? null : service.findByCode(string);
    }
}
```

### 12.2 增强转换器示例
```java
package com.ecep.contract.converter;

import com.ecep.contract.service.CompanyService;
import com.ecep.contract.vo.CompanyVo;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

@Lazy
@Component
public class CompanyStringConverter extends EntityStringConverter<CompanyVo> {
    @Lazy
    @Autowired
    private CompanyService service;
    
    @PostConstruct
    private void init() {
        setInitialized(company -> service.findById(company.getId()));
        setSuggestion(service::search);
        setFromString(service::findByName);
    }
}
```