# Inventory 实体类说明文档

## 1. 概述

`Inventory` 类是 Contract-Manager 项目中的核心实体类，代表**存货物品清单**，用于管理和存储各类存货物品的详细信息。该类位于 `common` 模块中，实现了 `IdentityEntity`、`BasedEntity` 和 `Serializable` 接口，支持数据持久化和对象序列化。

## 2. 类结构

```java
@Getter
@Setter
@Entity
@Table(name = "INVENTORY", schema = "supplier_ms")
public class Inventory implements IdentityEntity, BasedEntity, Serializable {
    // 类实现内容
}
```

### 2.1 核心注解
- `@Getter`、`@Setter`：Lombok注解，自动生成getter和setter方法
- `@Entity`：JPA注解，表示该类是持久化实体
- `@Table`：指定对应的数据库表名为 `INVENTORY`，模式为 `supplier_ms`

## 3. 字段说明

### 3.1 基本信息
- `id`：主键，使用自增策略
- `code`：存货编码
- `name`：存货名称
- `specification`：规格型号
- `specificationLock`：规格型号是否锁定
- `nameLock`：名称是否锁定
- `unit`：单位
- `description`：描述信息

### 3.2 价格信息（嵌入式）
使用 `Price` 嵌入式类存储采购和销售价格：
- `purchasePrice`：采购价格（含税前价格、税后价格、税率）
- `salePrice`：销售价格（含税前价格、税后价格、税率）

### 3.3 重量与尺寸信息
- `weight`：产品重量
- `packagedWeight`：包装重量
- `weightUnit`：重量单位（默认：kg）
- `volumeUnit`：体积单位（默认：m³）
- `sizeUnit`：尺寸单位（默认：mm）

### 3.4 体积尺寸信息（嵌入式）
使用 `VolumeSize` 嵌入式类存储体积尺寸：
- `volumeSize`：产品体积尺寸（长、宽、高、体积）
- `packagedVolumeSize`：包装体积尺寸（长、宽、高、体积）

### 3.5 关联关系
- `catalog`：所属分类，多对一关联 `InventoryCatalog`
- `creator`：创建人，多对一关联 `Employee`
- `updater`：更新人，多对一关联 `Employee`

### 3.6 时间信息
- `createTime`：创建时间（LocalDate类型）
- `updateDate`：更新时间（LocalDateTime类型）

## 4. 接口实现

### 4.1 IdentityEntity 接口
- 实现了 `getId()` 和 `setId(Integer id)` 方法
- 重写了 `equals(Object object)` 方法，基于ID进行比较

### 4.2 BasedEntity 接口
- 实现了 `toPrettyString()` 方法，返回 "名称+规格" 格式的字符串

## 5. 关联实体类

### 5.1 InventoryCatalog
- 表示存货分类
- 与Inventory是一对多关系

### 5.2 Price
- 嵌入式类，存储价格相关信息
- 包含税率、税前价格、税后价格

### 5.3 VolumeSize
- 嵌入式类，存储体积尺寸信息
- 包含体积、长度、宽度、高度

### 5.4 Employee
- 表示系统用户/员工
- 与Inventory是多对一关系（创建人和更新人）

## 6. 数据访问层

`InventoryRepository` 接口提供了对Inventory实体的数据库访问功能：

```java
@Repository
public interface InventoryRepository extends JpaRepository<Inventory, Integer>, JpaSpecificationExecutor<Inventory> {
    Optional<Inventory> findByCode(String code);
}
```

- 继承自 `JpaRepository`，提供基本的CRUD操作
- 继承自 `JpaSpecificationExecutor`，支持动态查询条件
- 自定义方法 `findByCode`，通过编码查询存货

## 7. 服务层

### 7.1 Server模块
`server` 模块中的 `InventoryService` 实现了多个接口，提供完整的业务逻辑：

- 实现了缓存机制，提高查询效率
- 支持分页查询和动态条件搜索
- 提供通过编码查询、保存、删除等操作
- 实现了 `createNewInstance()` 方法，用于创建新的Inventory实例并设置默认值
- 实现了 `updateByVo()` 方法，支持从VO对象更新实体

### 7.2 Client模块
`client` 模块中的 `InventoryService` 主要提供客户端特定的业务逻辑：

- 创建新的InventoryVo实例
- 提供按编码和名称查询的方法
- 包含待实现的 `syncInventory()` 方法

## 8. 数据传输对象

`InventoryVo` 类用于在不同层之间传输Inventory相关数据：

- 包含与Inventory实体对应的主要字段
- 添加了 `active` 字段，表示状态
- 使用关联实体的ID替代直接引用（如 `catalogId`、`creatorId` 等）

## 9. 代码优化建议

### 9.1 数据一致性
- **问题**：`InventoryService.createNewInstance()` 方法设置了默认的税率为13%，但没有自动计算税后价格
- **建议**：
```java
public Inventory createNewInstance() {
    Inventory inventory = new Inventory();
    
    // 设置默认值
    inventory.setNameLock(false);
    inventory.setSpecificationLock(false);
    
    // 设置税率并自动计算税后价格
    float defaultTaxRate = 13;
    double defaultPrice = 0;
    
    Price purchasePrice = inventory.getPurchasePrice();
    purchasePrice.setTaxRate(defaultTaxRate);
    purchasePrice.setPreTaxPrice(defaultPrice);
    purchasePrice.setPostTaxPrice(defaultPrice * (1 + defaultTaxRate / 100));
    
    Price salePrice = inventory.getSalePrice();
    salePrice.setTaxRate(defaultTaxRate);
    salePrice.setPreTaxPrice(defaultPrice);
    salePrice.setPostTaxPrice(defaultPrice * (1 + defaultTaxRate / 100));
    
    // 其他默认值设置
    inventory.setWeightUnit("kg");
    inventory.setVolumeUnit("m³");
    inventory.setSizeUnit("mm");
    inventory.setCreateTime(LocalDate.now());
    
    return inventory;
}
```

### 9.2 时间类型一致性
- **问题**：创建时间使用 `LocalDate` 类型，更新时间使用 `LocalDateTime` 类型，类型不一致
- **建议**：统一使用 `LocalDateTime` 类型，以便更精确地记录时间信息

### 9.3 完整性验证
- **问题**：缺少对必填字段的完整性验证
- **建议**：在 `save` 方法中添加基本的验证逻辑：
```java
@Caching(evict = {
        @CacheEvict(key = "#p0.id"),
        @CacheEvict(key = "'code-'+#p0.code"),
})public Inventory save(Inventory entity) {
    // 基本验证
    if (entity == null) {
        throw new IllegalArgumentException("Inventory entity cannot be null");
    }
    if (!StringUtils.hasText(entity.getName())) {
        throw new IllegalArgumentException("Inventory name is required");
    }
    
    // 更新时间戳
    entity.setUpdateDate(LocalDateTime.now());
    
    return inventoryRepository.save(entity);
}
```

### 9.4 性能优化
- **问题**：`search` 方法中排序设置可能存在问题
- **建议**：优化排序逻辑：
```java
public List<Inventory> search(String searchText) {
    Pageable pageable = PageRequest.of(0, getSearchPageSize(), Sort.by(Sort.Direction.DESC, "id"));
    Specification<Inventory> specification = getSpecification(searchText);
    return inventoryRepository.findAll(specification, pageable).getContent();
}
```

## 10. 输入输出示例

#### 输入输出示例

**创建新存货：**
```java
// 服务端创建新存货
Inventory inventory = inventoryService.createNewInstance();
inventory.setName("笔记本电脑");
inventory.setCode("NB-001");
inventory.setSpecification("ThinkPad X1 Carbon");
inventory.setUnit("台");

// 设置价格信息
Price salePrice = inventory.getSalePrice();
salePrice.setPreTaxPrice(9999.00);
salePrice.setPostTaxPrice(9999.00 * 1.13);

// 保存
inventory = inventoryService.save(inventory);
```

**根据编码查询存货：**
```java
// 服务端查询
Inventory inventory = inventoryService.findByCode("NB-001");
System.out.println("存货名称：" + inventory.toPrettyString()); // 输出：笔记本电脑 ThinkPad X1 Carbon

// 客户端查询
InventoryVo inventoryVo = clientInventoryService.findByCode("NB-001");
System.out.println("存货ID：" + inventoryVo.getId() + ", 名称：" + inventoryVo.getName());
```

## 11. 总结

`Inventory` 实体类是Contract-Manager项目中用于管理存货信息的核心类，通过与相关实体、服务和数据访问层的协作，提供了完整的存货管理功能。该类设计合理，包含了存货的各种属性信息，并通过实现相关接口提供了标准的行为规范。通过实施建议的优化，可以进一步提高代码的健壮性、一致性和性能。