# 项目详情参数配置页面设计文档

**创建时间：** 2026-03-06
**作者：** Claude Code
**状态：** 已批准

---

## 1. 概述

### 1.1 需求背景

纪检初核系统需要在项目详情页面中添加参数配置功能，允许用户为每个项目自定义模型参数配置。当前系统已有独立的模型参数配置页面（管理系统默认参数），需要将其功能复用到项目详情页面中。

### 1.2 核心需求

1. **配置模式：** 自动切换模式（修改即切换为 custom）
2. **界面布局：** 完全复用独立页面的布局（模型下拉框 + 参数表格 + 保存按钮）
3. **重置功能：** 不提供切换回默认配置的功能
4. **初始化策略：** 查询时复制（按需创建自定义参数）

### 1.3 设计原则

1. **最小改动原则：** 前端组件直接复用代码，后端只修改必要的方法
2. **自动切换原则：** 用户保存参数时自动从 default 切换到 custom
3. **按需创建原则：** 只在首次保存时创建项目自定义参数，不预复制
4. **数据隔离原则：** 项目自定义参数与系统默认参数完全独立

---

## 2. 架构设计

### 2.1 整体架构

```
┌─────────────────────────────────────────────────────────┐
│                    项目详情页面                           │
│                   detail.vue                             │
│  ┌───────────────────────────────────────────────────┐  │
│  │  菜单栏: 上传数据 | 参数配置 | 结果总览 | ...      │  │
│  └───────────────────────────────────────────────────┘  │
│  ┌───────────────────────────────────────────────────┐  │
│  │            ParamConfig 组件                        │  │
│  │  ┌─────────────────────────────────────────────┐  │  │
│  │  │  模型选择下拉框                              │  │  │
│  │  └─────────────────────────────────────────────┘  │  │
│  │  ┌─────────────────────────────────────────────┐  │  │
│  │  │  参数表格（可编辑）                          │  │  │
│  │  └─────────────────────────────────────────────┘  │  │
│  │  ┌─────────────────────────────────────────────┐  │  │
│  │  │  保存按钮                                    │  │  │
│  │  └─────────────────────────────────────────────┘  │  │
│  └───────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────┘
                          │
                          │ API 调用
                          ▼
┌─────────────────────────────────────────────────────────┐
│                  后端 CcdiModelParamController           │
│  ┌───────────────────────────────────────────────────┐  │
│  │  GET /ccdi/modelParam/modelList?projectId={id}   │  │
│  │  - 查询模型列表                                   │  │
│  └───────────────────────────────────────────────────┘  │
│  ┌───────────────────────────────────────────────────┐  │
│  │  GET /ccdi/modelParam/list?projectId={id}        │  │
│  │  - 查询模型参数列表                               │  │
│  │  - 如果 configType=default，返回系统默认参数      │  │
│  │  - 如果 configType=custom，返回项目自定义参数     │  │
│  └───────────────────────────────────────────────────┘  │
│  ┌───────────────────────────────────────────────────┐  │
│  │  POST /ccdi/modelParam/save                      │  │
│  │  - 保存参数                                       │  │
│  │  - 如果是首次保存，自动复制系统默认参数           │  │
│  │  - 更新 configType=custom                        │  │
│  └───────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────┘
                          │
                          ▼
┌─────────────────────────────────────────────────────────┐
│                  数据库 ccdi_model_param                 │
│  - projectId=0：系统默认参数                            │
│  - projectId>0：项目自定义参数                          │
└─────────────────────────────────────────────────────────┘
```

### 2.2 数据模型

**表：ccdi_model_param**

| 字段名 | 类型 | 说明 |
|--------|------|------|
| id | BIGINT | 主键ID |
| project_id | BIGINT | 项目ID（0表示默认参数） |
| model_code | VARCHAR(50) | 模型编码 |
| model_name | VARCHAR(100) | 模型名称 |
| param_code | VARCHAR(50) | 参数编码 |
| param_name | VARCHAR(100) | 监测项名称 |
| param_desc | VARCHAR(500) | 参数描述 |
| param_value | VARCHAR(200) | 参数值 |
| param_unit | VARCHAR(50) | 参数单位 |
| sort_order | INT | 排序号 |
| create_by | VARCHAR(64) | 创建者 |
| create_time | DATETIME | 创建时间 |
| update_by | VARCHAR(64) | 更新者 |
| update_time | DATETIME | 更新时间 |
| remark | VARCHAR(500) | 备注 |

**表：ccdi_project（相关字段）**

| 字段名 | 类型 | 说明 |
|--------|------|------|
| project_id | BIGINT | 项目ID |
| config_type | VARCHAR(20) | 配置方式：default-全局默认，custom-自定义 |

---

## 3. 组件设计

### 3.1 前端组件

**组件路径：** `ruoyi-ui/src/views/ccdiProject/components/detail/ParamConfig.vue`

**组件结构：**

```vue
<template>
  <div class="param-config-container">
    <!-- 模型选择区域 -->
    <div class="filter-section">
      <el-form :inline="true" :model="queryParams">
        <el-form-item label="模型名称">
          <el-select
            v-model="queryParams.modelCode"
            placeholder="请选择模型"
            @change="handleModelChange"
          >
            <el-option
              v-for="model in modelList"
              :key="model.modelCode"
              :label="model.modelName"
              :value="model.modelCode"
            />
          </el-select>
        </el-form-item>
      </el-form>
    </div>

    <!-- 参数配置表格 -->
    <div class="table-section">
      <h3>阈值参数配置</h3>
      <el-table :data="paramList" border>
        <el-table-column label="监测项" prop="paramName" width="200" />
        <el-table-column label="描述" prop="paramDesc" />
        <el-table-column label="阈值设置" width="200">
          <template #default="{ row }">
            <el-input
              v-model="row.paramValue"
              placeholder="请输入阈值"
              @input="markAsModified(row)"
            />
          </template>
        </el-table-column>
        <el-table-column label="单位" prop="paramUnit" width="120" />
      </el-table>
    </div>

    <!-- 操作按钮 -->
    <div class="button-section">
      <el-button
        type="primary"
        @click="handleSave"
        :loading="saving"
      >
        保存配置
      </el-button>
    </div>
  </div>
</template>

<script>
import { listModels, listParams, saveParams } from "@/api/ccdi/modelParam";

export default {
  name: 'ParamConfig',
  props: {
    projectId: {
      type: [String, Number],
      required: true
    },
    projectInfo: {
      type: Object,
      default: () => ({})
    }
  },
  data() {
    return {
      modelList: [],
      queryParams: {
        modelCode: undefined,
        projectId: this.projectId
      },
      paramList: [],
      saving: false
    }
  },
  watch: {
    projectId(newVal) {
      this.queryParams.projectId = newVal
      this.loadModelList()
    }
  },
  created() {
    this.loadModelList()
  },
  methods: {
    /** 加载模型列表 */
    async loadModelList() {
      try {
        const res = await listModels({ projectId: this.projectId })
        this.modelList = res.data
        if (this.modelList.length > 0) {
          this.queryParams.modelCode = this.modelList[0].modelCode
          this.loadParamList()
        }
      } catch (error) {
        this.$message.error('加载模型列表失败：' + error.message)
        console.error('加载模型列表失败', error)
      }
    },

    /** 加载参数列表 */
    async loadParamList() {
      try {
        const res = await listParams(this.queryParams)
        this.paramList = res.data
      } catch (error) {
        this.$message.error('加载参数列表失败：' + error.message)
        console.error('加载参数列表失败', error)
      }
    },

    /** 模型切换 */
    handleModelChange() {
      this.loadParamList()
    },

    /** 标记为已修改 */
    markAsModified(row) {
      row.modified = true
    },

    /** 保存配置 */
    async handleSave() {
      // 验证是否有修改
      const modifiedParams = this.paramList.filter(item => item.modified)
      if (modifiedParams.length === 0) {
        this.$message.info('没有需要保存的修改')
        return
      }

      // 验证参数值
      const invalidParams = modifiedParams.filter(
        item => !item.paramValue || item.paramValue.trim() === ''
      )
      if (invalidParams.length > 0) {
        this.$message.error('请填写所有参数值')
        return
      }

      // 构造保存数据
      const saveDTO = {
        projectId: this.projectId,
        modelCode: this.queryParams.modelCode,
        params: modifiedParams.map(item => ({
          paramCode: item.paramCode,
          paramValue: item.paramValue
        }))
      }

      // 保存
      this.saving = true
      try {
        await saveParams(saveDTO)
        this.$message.success('保存成功')
        // 清除修改标记并重新加载
        this.paramList.forEach(item => { item.modified = false })
        await this.loadParamList()
      } catch (error) {
        if (error.response && error.response.data && error.response.data.msg) {
          this.$message.error('保存失败：' + error.response.data.msg)
        } else {
          this.$message.error('保存失败：' + error.message)
        }
      } finally {
        this.saving = false
      }
    }
  }
}
</script>

<style scoped lang="scss">
.param-config-container {
  padding: 20px;
  background-color: #fff;
  min-height: 400px;
}

.filter-section {
  padding: 15px;
  background: #fff;
  border-radius: 4px;
  margin-bottom: 20px;
}

.table-section {
  padding: 20px;
  background: #fff;
  border-radius: 4px;
  margin-bottom: 20px;

  h3 {
    font-size: 16px;
    font-weight: bold;
    color: #333;
    margin: 0 0 15px 0;
  }
}

.button-section {
  padding: 15px;
  background: #fff;
  border-radius: 4px;
  text-align: left;
}
</style>
```

### 3.2 后端接口

**文件：** `CcdiModelParamServiceImpl.java`

**修改的方法：**

#### 3.2.1 selectParamList 方法

```java
@Override
public List<ModelParamVO> selectParamList(ModelParamQueryDTO queryDTO) {
    // 1. 查询项目信息
    CcdiProject project = projectMapper.selectById(queryDTO.getProjectId());

    if (project == null) {
        throw new ServiceException("项目不存在");
    }

    // 2. 根据 configType 决定查询哪组参数
    Long effectiveProjectId;
    if ("default".equals(project.getConfigType())) {
        // 使用系统默认参数
        effectiveProjectId = 0L;
    } else {
        // 使用项目自定义参数
        effectiveProjectId = queryDTO.getProjectId();
    }

    // 3. 查询参数列表
    return modelParamMapper.selectParamList(effectiveProjectId, queryDTO.getModelCode());
}
```

#### 3.2.2 saveParams 方法

```java
@Override
@Transactional(rollbackFor = Exception.class)
public void saveParams(ModelParamSaveDTO saveDTO) {
    try {
        // 1. 参数验证
        if (saveDTO.getProjectId() == null) {
            throw new ServiceException("项目ID不能为空");
        }
        if (StringUtils.isBlank(saveDTO.getModelCode())) {
            throw new ServiceException("模型编码不能为空");
        }
        if (saveDTO.getParams() == null || saveDTO.getParams().isEmpty()) {
            throw new ServiceException("参数列表不能为空");
        }

        // 2. 查询项目信息
        CcdiProject project = projectMapper.selectById(saveDTO.getProjectId());
        if (project == null) {
            throw new ServiceException("项目不存在");
        }

        // 3. 如果是首次保存（configType=default），需要复制系统默认参数
        if ("default".equals(project.getConfigType())) {
            int copiedCount = copyDefaultParamsToProject(
                saveDTO.getProjectId(),
                saveDTO.getModelCode()
            );
            if (copiedCount == 0) {
                log.warn("系统默认参数为空，projectId={}, modelCode={}",
                    saveDTO.getProjectId(), saveDTO.getModelCode());
            }

            // 更新项目配置类型为 custom
            project.setConfigType("custom");
            projectMapper.updateById(project);
        }

        // 4. 更新参数值
        for (ModelParamSaveDTO.ParamValueItem item : saveDTO.getParams()) {
            int updated = modelParamMapper.updateParamValue(
                saveDTO.getProjectId(),
                saveDTO.getModelCode(),
                item.getParamCode(),
                item.getParamValue()
            );
            if (updated == 0) {
                log.warn("参数不存在或未更新，paramCode={}", item.getParamCode());
            }
        }

    } catch (ServiceException e) {
        // 业务异常，直接抛出
        throw e;
    } catch (Exception e) {
        // 系统异常，记录日志并抛出
        log.error("保存模型参数失败", e);
        throw new ServiceException("保存模型参数失败：" + e.getMessage());
    }
}

/**
 * 复制系统默认参数到项目
 *
 * @param projectId 项目ID
 * @param modelCode 模型编码
 * @return 复制的参数数量
 */
private int copyDefaultParamsToProject(Long projectId, String modelCode) {
    // 查询系统默认参数
    List<CcdiModelParam> defaultParams = modelParamMapper.selectList(
        new LambdaQueryWrapper<CcdiModelParam>()
            .eq(CcdiModelParam::getProjectId, 0L)
            .eq(CcdiModelParam::getModelCode, modelCode)
    );

    if (defaultParams.isEmpty()) {
        return 0;
    }

    // 复制到项目
    List<CcdiModelParam> projectParams = defaultParams.stream()
        .map(param -> {
            CcdiModelParam newParam = new CcdiModelParam();
            BeanUtils.copyProperties(param, newParam);
            newParam.setId(null); // 清空ID，让数据库自动生成
            newParam.setProjectId(projectId);
            newParam.setCreateBy(null); // 清空审计字段，让 MyBatis Plus 自动填充
            newParam.setCreateTime(null);
            newParam.setUpdateBy(null);
            newParam.setUpdateTime(null);
            return newParam;
        })
        .collect(Collectors.toList());

    // 批量插入
    modelParamMapper.insertBatch(projectParams);

    return projectParams.size();
}
```

#### 3.2.3 Mapper 方法

**CcdiModelParamMapper.xml 新增：**

```xml
<!-- 更新参数值 -->
<update id="updateParamValue">
    UPDATE ccdi_model_param
    SET param_value = #{paramValue},
        update_by = NULL,
        update_time = NOW()
    WHERE project_id = #{projectId}
      AND model_code = #{modelCode}
      AND param_code = #{paramCode}
</update>

<!-- 批量插入 -->
<insert id="insertBatch" parameterType="java.util.List">
    INSERT INTO ccdi_model_param (
        project_id, model_code, model_name, param_code, param_name,
        param_desc, param_value, param_unit, sort_order,
        create_by, create_time, remark
    ) VALUES
    <foreach collection="list" item="item" separator=",">
        (
            #{item.projectId}, #{item.modelCode}, #{item.modelName},
            #{item.paramCode}, #{item.paramName}, #{item.paramDesc},
            #{item.paramValue}, #{item.paramUnit}, #{item.sortOrder},
            NULL, NOW(), #{item.remark}
        )
    </foreach>
</insert>
```

---

## 4. 数据流设计

### 4.1 查看参数配置（configType=default）

```
用户点击"参数配置"菜单
    ↓
前端调用 GET /ccdi/modelParam/modelList?projectId=123
    ↓
后端返回模型列表
    ↓
前端选择第一个模型，调用 GET /ccdi/modelParam/list?projectId=123&modelCode=MODEL_001
    ↓
后端查询项目，发现 configType=default
    ↓
后端返回系统默认参数（projectId=0）
    ↓
前端显示参数表格
```

### 4.2 查看参数配置（configType=custom）

```
用户点击"参数配置"菜单
    ↓
前端调用 GET /ccdi/modelParam/modelList?projectId=123
    ↓
后端返回模型列表
    ↓
前端选择第一个模型，调用 GET /ccdi/modelParam/list?projectId=123&modelCode=MODEL_001
    ↓
后端查询项目，发现 configType=custom
    ↓
后端返回项目自定义参数（projectId=123）
    ↓
前端显示参数表格
```

### 4.3 首次保存参数（default → custom）

```
用户修改参数值，点击"保存配置"
    ↓
前端调用 POST /ccdi/modelParam/save
{
  "projectId": 123,
  "modelCode": "MODEL_001",
  "params": [
    {"paramCode": "THRESHOLD_1", "paramValue": "100"},
    {"paramCode": "THRESHOLD_2", "paramValue": "50"}
  ]
}
    ↓
后端检查项目 configType=default
    ↓
后端执行复制操作：
  1. 查询系统默认参数（projectId=0, modelCode=MODEL_001）
  2. 复制所有参数，设置 projectId=123
  3. 批量插入到数据库
    ↓
后端更新项目的 configType=custom
    ↓
后端更新参数值：
  UPDATE ccdi_model_param
  SET param_value='100'
  WHERE project_id=123 AND model_code='MODEL_001' AND param_code='THRESHOLD_1'
    ↓
后端返回成功
    ↓
前端重新加载参数列表（此时查询的是项目自定义参数）
    ↓
前端显示成功消息
```

### 4.4 再次保存参数（configType=custom）

```
用户修改参数值，点击"保存配置"
    ↓
前端调用 POST /ccdi/modelParam/save
    ↓
后端检查项目 configType=custom
    ↓
后端跳过复制步骤，直接更新参数值
    ↓
后端返回成功
```

---

## 5. 错误处理

### 5.1 前端错误处理

**网络错误：**

```javascript
async loadParamList() {
  try {
    const res = await listParams(this.queryParams)
    this.paramList = res.data
  } catch (error) {
    this.$message.error('加载参数列表失败：' + error.message)
    console.error('加载参数列表失败', error)
  }
}
```

**保存验证：**

```javascript
async handleSave() {
  // 验证是否有修改
  const modifiedParams = this.paramList.filter(item => item.modified)
  if (modifiedParams.length === 0) {
    this.$message.info('没有需要保存的修改')
    return
  }

  // 验证参数值
  const invalidParams = modifiedParams.filter(
    item => !item.paramValue || item.paramValue.trim() === ''
  )
  if (invalidParams.length > 0) {
    this.$message.error('请填写所有参数值')
    return
  }

  // 保存
  this.saving = true
  try {
    await saveParams(saveDTO)
    this.$message.success('保存成功')
    // 清除修改标记并重新加载
    this.paramList.forEach(item => { item.modified = false })
    await this.loadParamList()
  } catch (error) {
    if (error.response && error.response.data && error.response.data.msg) {
      this.$message.error('保存失败：' + error.response.data.msg)
    } else {
      this.$message.error('保存失败：' + error.message)
    }
  } finally {
    this.saving = false
  }
}
```

### 5.2 后端错误处理

**异常处理：**

```java
@Override
@Transactional(rollbackFor = Exception.class)
public void saveParams(ModelParamSaveDTO saveDTO) {
    try {
        // 1. 参数验证
        if (saveDTO.getProjectId() == null) {
            throw new ServiceException("项目ID不能为空");
        }
        if (StringUtils.isBlank(saveDTO.getModelCode())) {
            throw new ServiceException("模型编码不能为空");
        }
        if (saveDTO.getParams() == null || saveDTO.getParams().isEmpty()) {
            throw new ServiceException("参数列表不能为空");
        }

        // 2. 查询项目信息
        CcdiProject project = projectMapper.selectById(saveDTO.getProjectId());
        if (project == null) {
            throw new ServiceException("项目不存在");
        }

        // 3. 复制默认参数（如果需要）
        if ("default".equals(project.getConfigType())) {
            int copiedCount = copyDefaultParamsToProject(
                saveDTO.getProjectId(),
                saveDTO.getModelCode()
            );
            if (copiedCount == 0) {
                log.warn("系统默认参数为空，projectId={}, modelCode={}",
                    saveDTO.getProjectId(), saveDTO.getModelCode());
            }

            // 更新项目配置类型
            project.setConfigType("custom");
            projectMapper.updateById(project);
        }

        // 4. 更新参数值
        for (ModelParamSaveDTO.ParamValueItem item : saveDTO.getParams()) {
            int updated = modelParamMapper.updateParamValue(
                saveDTO.getProjectId(),
                saveDTO.getModelCode(),
                item.getParamCode(),
                item.getParamValue()
            );
            if (updated == 0) {
                log.warn("参数不存在或未更新，paramCode={}", item.getParamCode());
            }
        }

    } catch (ServiceException e) {
        // 业务异常，直接抛出
        throw e;
    } catch (Exception e) {
        // 系统异常，记录日志并抛出
        log.error("保存模型参数失败", e);
        throw new ServiceException("保存模型参数失败：" + e.getMessage());
    }
}
```

### 5.3 错误场景处理表

| 错误场景 | 处理方式 |
|---------|---------|
| 项目不存在 | 返回 404 错误，提示"项目不存在" |
| 系统默认参数为空 | 记录警告日志，继续执行（允许项目自定义参数） |
| 参数值验证失败 | 前端拦截，不提交到后端 |
| 数据库连接失败 | 返回 500 错误，提示"系统异常，请稍后重试" |
| 事务回滚 | 自动回滚所有操作，保证数据一致性 |

---

## 6. 测试策略

### 6.1 后端单元测试

**测试类：** `CcdiModelParamServiceImplTest.java`

**测试用例：**

1. `testSelectParamList_DefaultConfig()` - 测试查询默认配置项目的参数列表
2. `testSelectParamList_CustomConfig()` - 测试查询自定义配置项目的参数列表
3. `testSaveParams_FirstTimeSave()` - 测试首次保存参数（触发 default → custom 切换）
4. `testSaveParams_SecondTimeSave()` - 测试再次保存参数（已为 custom 模式）

### 6.2 前端集成测试

**测试脚本：** `test-param-config.sh`

**测试流程：**

1. 登录获取 Token
2. 创建测试项目
3. 查询模型列表
4. 查询参数列表（default 模式）
5. 首次保存参数（触发切换）
6. 查询参数列表（custom 模式）
7. 查询项目信息（验证 configType）
8. 清理测试数据

### 6.3 手动测试清单

| 编号 | 测试场景 | 预期结果 | 通过标准 |
|------|---------|---------|---------|
| 1 | 新项目查看参数配置 | 显示系统默认参数 | 参数值与系统默认一致 |
| 2 | 新项目修改并保存参数 | 自动切换为自定义配置 | configType 变为 custom |
| 3 | 再次查看参数 | 显示项目自定义参数 | 参数值为修改后的值 |
| 4 | 再次修改参数 | 直接更新参数值 | 参数值更新成功 |
| 5 | 切换模型 | 正确加载不同模型的参数 | 参数列表正确切换 |
| 6 | 不修改任何参数点击保存 | 提示"没有需要保存的修改" | 不发起保存请求 |
| 7 | 清空参数值后保存 | 前端验证拦截 | 显示错误提示 |
| 8 | 并发保存同一参数 | 后保存的值生效 | 数据一致性 |
| 9 | 网络异常时保存 | 显示错误提示 | 不更新页面数据 |
| 10 | 项目状态为"已归档"时保存 | 根据业务规则处理 | 符合业务逻辑 |

### 6.4 性能测试

| 测试项 | 测试方法 | 性能目标 |
|--------|---------|---------|
| 查询参数列表 | 模拟 100 个项目同时查询 | 响应时间 < 500ms |
| 首次保存参数 | 模拟 50 个项目同时首次保存 | 响应时间 < 2s |
| 数据库查询性能 | EXPLAIN 分析 SQL | 使用索引，无全表扫描 |
| 并发保存 | 10 个并发请求保存同一项目 | 无死锁，数据一致 |

---

## 7. 实施计划

### 7.1 实施步骤

1. **后端开发**
   - 修改 `CcdiModelParamServiceImpl.selectParamList()` 方法
   - 修改 `CcdiModelParamServiceImpl.saveParams()` 方法
   - 新增 `copyDefaultParamsToProject()` 私有方法
   - 新增 Mapper XML 中的 `updateParamValue` 和 `insertBatch` 方法

2. **前端开发**
   - 实现 `ParamConfig.vue` 组件
   - 复用 `ccdi/modelParam.js` API 接口
   - 确保组件正确接收 `projectId` 和 `projectInfo` props

3. **测试**
   - 编写后端单元测试
   - 编写集成测试脚本
   - 执行手动测试清单

4. **文档**
   - 更新 API 文档
   - 更新用户手册

### 7.2 风险评估

| 风险项 | 影响 | 概率 | 应对措施 |
|--------|------|------|---------|
| 并发保存导致数据不一致 | 高 | 低 | 使用事务隔离，数据库行锁 |
| 系统默认参数缺失 | 中 | 低 | 记录日志，允许项目自定义 |
| 前端缓存导致参数不更新 | 低 | 中 | 保存后重新加载参数列表 |
| 大批量参数复制性能问题 | 中 | 低 | 使用批量插入，控制事务大小 |

---

## 8. 附录

### 8.1 相关文件清单

**前端文件：**
- `ruoyi-ui/src/views/ccdiProject/components/detail/ParamConfig.vue` - 参数配置组件
- `ruoyi-ui/src/api/ccdi/modelParam.js` - API 接口（已存在，无需修改）

**后端文件：**
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiModelParamServiceImpl.java` - Service 实现
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiModelParamMapper.java` - Mapper 接口
- `ccdi-project/src/main/resources/mapper/ccdi/project/CcdiModelParamMapper.xml` - Mapper XML

**测试文件：**
- `ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/CcdiModelParamServiceImplTest.java` - 单元测试
- `docs/tests/scripts/test-param-config.sh` - 集成测试脚本

### 8.2 参考文档

- 若依框架官方文档
- MyBatis Plus 官方文档
- Element UI 官方文档
- 项目 CLAUDE.md 开发规范

---

**设计完成时间：** 2026-03-06
**下一步：** 创建详细实施计划
