# Design Document

## Overview

本设计实现营部及以下级别账号的消耗上报可见性过滤功能。核心思路是在 `IOrganizationService` 中添加一个新方法 `GetVisibleUnitIdsAsync`，根据用户的组织层级返回可见的单位ID列表。对于营部及以下级别，只返回本单位及其直接下级；对于师团级别，返回本单位及所有下级。

## Architecture

```mermaid
flowchart TD
    A[ReportsController] --> B[ReportingService]
    B --> C[OrganizationService]
    C --> D{User Level Check}
    D -->|Battalion/Company| E[GetVisibleUnitIdsAsync - Restricted]
    D -->|Division/Regiment| F[GetAllSubordinateIdsAsync - Full]
    E --> G[Return: Own Unit + Direct Subordinates]
    F --> H[Return: Own Unit + All Subordinates]
```

## Components and Interfaces

### 1. IOrganizationService 扩展

新增方法用于获取用户可见的单位ID列表：

```csharp
/// <summary>
/// 获取用户可见的单位ID列表
/// 营部及以下级别：本单位 + 直接下级
/// 师团级别：本单位 + 所有下级
/// </summary>
Task<IEnumerable<int>> GetVisibleUnitIdsAsync(int unitId, OrganizationalLevel userLevel);
```

### 2. IReportingService 修改

修改现有方法以支持可见性过滤：

```csharp
/// <summary>
/// 获取汇总数据（应用可见性过滤）
/// </summary>
Task<IEnumerable<AllocationDistribution>> GetAggregatedDataAsync(int unitId, OrganizationalLevel userLevel);

/// <summary>
/// 获取详细上报记录（应用可见性过滤）
/// </summary>
Task<IEnumerable<ReportResponse>> GetDetailedReportsAsync(int unitId, OrganizationalLevel userLevel);
```

### 3. ReportsController 修改

在调用服务方法时传递用户的组织层级：

```csharp
var unitLevel = GetCurrentUnitLevel();
var reports = await _reportingService.GetDetailedReportsAsync(unitId.Value, unitLevel.Value);
```

## Data Models

无需新增数据模型，使用现有的：
- `OrganizationalUnit` - 组织单位实体
- `OrganizationalLevel` - 组织层级枚举
- `AllocationDistribution` - 配额分配记录
- `ConsumptionReport` - 消耗上报记录

### 可见性规则数据流

| 用户层级 | 可见范围 | 示例 |
|---------|---------|------|
| Division (师团) | 本单位 + 所有下级 | 师团A → 团B, 团C, 营D, 营E, 连F... |
| Regiment (团) | 本单位 + 所有下级 | 团B → 营D, 营E, 连F, 连G... |
| Battalion (营) | 本单位 + 直接下级 | 营D → 连F, 连G (不含同级营E及其下级) |
| Company (连) | 仅本单位 | 连F → 仅连F |



## Correctness Properties

*A property is a characteristic or behavior that should hold true across all valid executions of a system-essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.*

### Property 1: Restricted visibility for battalion/company level users

*For any* organization hierarchy and any user at Battalion_Level or Company_Level, the visible unit IDs returned by `GetVisibleUnitIdsAsync` SHALL:
- Include the user's own unit ID
- Include only direct subordinate unit IDs (children)
- Exclude all sibling unit IDs (units with the same parent)
- Exclude all subordinates of sibling units

**Validates: Requirements 1.1, 1.2, 1.3, 3.1, 3.2**

### Property 2: Full visibility for division/regiment level users

*For any* organization hierarchy and any user at Division_Level or Regiment_Level, the visible unit IDs returned by `GetVisibleUnitIdsAsync` SHALL equal the union of the user's own unit ID and all subordinate unit IDs (as returned by `GetAllSubordinateIdsAsync`).

**Validates: Requirements 1.4, 3.3**

### Property 3: Access control respects visibility scope

*For any* user and any target unit ID, the `CanViewUnitDataAsync` method SHALL return `true` if and only if the target unit ID is contained in the user's visible unit IDs set.

**Validates: Requirements 2.3**

## Error Handling

| Scenario | Handling |
|----------|----------|
| User unit not found | Return empty visible units list |
| Invalid organizational level | Default to restricted visibility (fail-safe) |
| Database query failure | Propagate exception to controller for 500 response |

## Testing Strategy

### Property-Based Testing

使用 FsCheck 库进行属性测试（项目已使用 xUnit + FsCheck）。

**测试配置：**
- 每个属性测试运行最少 100 次迭代
- 使用自定义生成器创建有效的组织层级树

**属性测试标注格式：**
```csharp
// **Feature: consumption-report-visibility-filter, Property 1: Restricted visibility for battalion/company level users**
// **Validates: Requirements 1.1, 1.2, 1.3, 3.1, 3.2**
```

### Unit Tests

- 测试边界情况：无下级单位的营级用户
- 测试边界情况：连级用户（最底层）
- 测试师团级用户的完整可见性

### Integration Tests (Optional)

- 验证 API 端点正确应用可见性过滤
- 验证聚合数据计算使用过滤后的单位列表
