docs: 添加模型参数配置页面优化设计文档
This commit is contained in:
995
docs/plans/2026-03-06-model-param-config-optimization-design.md
Normal file
995
docs/plans/2026-03-06-model-param-config-optimization-design.md
Normal file
@@ -0,0 +1,995 @@
|
|||||||
|
# 模型参数配置页面优化设计文档
|
||||||
|
|
||||||
|
**文档版本:** v1.0
|
||||||
|
**创建日期:** 2026-03-06
|
||||||
|
**设计人员:** Claude Code
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一、概述
|
||||||
|
|
||||||
|
### 1.1 背景
|
||||||
|
|
||||||
|
当前模型参数配置页面采用模型下拉框切换的方式,用户需要逐个切换模型才能查看和配置不同模型的参数,操作不够便捷。本次优化旨在取消模型切换,改为在同一页面中以垂直堆叠方式展示所有模型的参数表格,提升用户体验。
|
||||||
|
|
||||||
|
### 1.2 目标
|
||||||
|
|
||||||
|
- ✅ 取消模型名称查询切换
|
||||||
|
- ✅ 在同一页面中分多个表格展示所有模型的参数
|
||||||
|
- ✅ 全局模型参数配置页面和项目内模型参数配置页面同步修改
|
||||||
|
- ✅ 统一保存机制,一次性保存所有修改
|
||||||
|
|
||||||
|
### 1.3 影响范围
|
||||||
|
|
||||||
|
**前端页面:**
|
||||||
|
- `ruoyi-ui/src/views/ccdi/modelParam/index.vue` - 全局模型参数配置页面
|
||||||
|
- `ruoyi-ui/src/views/ccdiProject/components/detail/ParamConfig.vue` - 项目内参数配置页面
|
||||||
|
|
||||||
|
**后端接口:**
|
||||||
|
- `CcdiModelParamController.java` - 新增批量查询和批量保存接口
|
||||||
|
- `ICcdiModelParamService.java` - 新增Service方法
|
||||||
|
- `CcdiModelParamServiceImpl.java` - 实现批量操作逻辑
|
||||||
|
- `CcdiModelParamMapper.java` - 新增Mapper方法
|
||||||
|
- `CcdiModelParamMapper.xml` - 新增SQL查询
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、详细设计
|
||||||
|
|
||||||
|
### 2.1 后端接口设计
|
||||||
|
|
||||||
|
#### 2.1.1 批量查询所有模型参数
|
||||||
|
|
||||||
|
**接口路径:** `GET /ccdi/modelParam/listAll`
|
||||||
|
|
||||||
|
**请求参数:**
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class ModelParamAllQueryDTO {
|
||||||
|
/** 项目ID(0表示全局配置,>0表示项目配置) */
|
||||||
|
private Long projectId;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**响应结构:**
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class ModelParamAllVO {
|
||||||
|
/** 模型列表(包含每个模型及其参数) */
|
||||||
|
private List<ModelGroupVO> models;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ModelGroupVO {
|
||||||
|
/** 模型编码 */
|
||||||
|
private String modelCode;
|
||||||
|
|
||||||
|
/** 模型名称 */
|
||||||
|
private String modelName;
|
||||||
|
|
||||||
|
/** 参数列表 */
|
||||||
|
private List<ModelParamVO> params;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**返回数据示例:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "操作成功",
|
||||||
|
"data": {
|
||||||
|
"models": [
|
||||||
|
{
|
||||||
|
"modelCode": "LARGE_TRANSACTION",
|
||||||
|
"modelName": "大额交易模型",
|
||||||
|
"params": [
|
||||||
|
{
|
||||||
|
"paramCode": "THRESHOLD_AMOUNT",
|
||||||
|
"paramName": "单笔交易金额阈值",
|
||||||
|
"paramDesc": "单笔交易金额超过此值触发预警",
|
||||||
|
"paramValue": "50000",
|
||||||
|
"paramUnit": "元",
|
||||||
|
"sortOrder": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"modelCode": "SUSPICIOUS_FOREIGN_EXCHANGE",
|
||||||
|
"modelName": "可疑外汇交易模型",
|
||||||
|
"params": [
|
||||||
|
{
|
||||||
|
"paramCode": "FREQUENCY_THRESHOLD",
|
||||||
|
"paramName": "交易频次阈值",
|
||||||
|
"paramDesc": "交易频次超过此值触发预警",
|
||||||
|
"paramValue": "10",
|
||||||
|
"paramUnit": "次/天",
|
||||||
|
"sortOrder": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"modelCode": "SUSPICIOUS_PART_TIME",
|
||||||
|
"modelName": "可疑兼职模型",
|
||||||
|
"params": [...]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2.1.2 批量保存所有模型参数
|
||||||
|
|
||||||
|
**接口路径:** `POST /ccdi/modelParam/saveAll`
|
||||||
|
|
||||||
|
**请求结构:**
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class ModelParamSaveAllDTO {
|
||||||
|
/** 项目ID */
|
||||||
|
private Long projectId;
|
||||||
|
|
||||||
|
/** 所有模型的参数修改(只包含修改过的参数) */
|
||||||
|
private List<ModelParamGroupDTO> models;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ModelParamGroupDTO {
|
||||||
|
/** 模型编码 */
|
||||||
|
private String modelCode;
|
||||||
|
|
||||||
|
/** 该模型下修改过的参数 */
|
||||||
|
private List<ParamValueItem> params;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ParamValueItem {
|
||||||
|
private String paramCode;
|
||||||
|
private String paramValue;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**请求示例:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"projectId": 1,
|
||||||
|
"models": [
|
||||||
|
{
|
||||||
|
"modelCode": "LARGE_TRANSACTION",
|
||||||
|
"params": [
|
||||||
|
{
|
||||||
|
"paramCode": "THRESHOLD_AMOUNT",
|
||||||
|
"paramValue": "60000"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"modelCode": "SUSPICIOUS_FOREIGN_EXCHANGE",
|
||||||
|
"params": [
|
||||||
|
{
|
||||||
|
"paramCode": "FREQUENCY_THRESHOLD",
|
||||||
|
"paramValue": "5"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**响应示例:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "保存成功"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**错误码说明:**
|
||||||
|
|
||||||
|
| 错误码 | 说明 |
|
||||||
|
|--------|------|
|
||||||
|
| 400 | 参数验证失败(项目ID为空、参数列表为空等) |
|
||||||
|
| 500 | 服务器内部错误(数据库操作失败等) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2.2 后端Service层设计
|
||||||
|
|
||||||
|
#### 2.2.1 Service接口新增方法
|
||||||
|
|
||||||
|
```java
|
||||||
|
public interface ICcdiModelParamService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询所有模型及其参数(按模型分组)
|
||||||
|
*
|
||||||
|
* @param projectId 项目ID(0表示全局配置)
|
||||||
|
* @return 所有模型的参数配置
|
||||||
|
*/
|
||||||
|
ModelParamAllVO selectAllParams(Long projectId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量保存所有模型的参数修改
|
||||||
|
*
|
||||||
|
* @param saveAllDTO 所有模型的参数修改数据
|
||||||
|
*/
|
||||||
|
void saveAllParams(ModelParamSaveAllDTO saveAllDTO);
|
||||||
|
|
||||||
|
// ... 保留原有的其他方法
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2.2.2 Service实现类核心逻辑
|
||||||
|
|
||||||
|
**查询所有模型参数:**
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Override
|
||||||
|
public ModelParamAllVO selectAllParams(Long projectId) {
|
||||||
|
// 1. 参数验证
|
||||||
|
if (projectId == null) {
|
||||||
|
projectId = 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 如果是项目查询,根据 configType 决定查询哪组参数
|
||||||
|
Long effectiveProjectId = projectId;
|
||||||
|
if (projectId > 0) {
|
||||||
|
CcdiProject project = projectMapper.selectById(projectId);
|
||||||
|
if (project == null) {
|
||||||
|
throw new ServiceException("项目不存在");
|
||||||
|
}
|
||||||
|
if ("default".equals(project.getConfigType())) {
|
||||||
|
effectiveProjectId = 0L;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 查询所有模型的参数
|
||||||
|
List<CcdiModelParam> allParams = modelParamMapper.selectByProjectId(effectiveProjectId);
|
||||||
|
|
||||||
|
// 4. 按模型分组
|
||||||
|
Map<String, List<CcdiModelParam>> groupedParams = allParams.stream()
|
||||||
|
.collect(Collectors.groupingBy(CcdiModelParam::getModelCode));
|
||||||
|
|
||||||
|
// 5. 转换为VO
|
||||||
|
ModelParamAllVO result = new ModelParamAllVO();
|
||||||
|
List<ModelGroupVO> models = new ArrayList<>();
|
||||||
|
|
||||||
|
groupedParams.forEach((modelCode, params) -> {
|
||||||
|
ModelGroupVO groupVO = new ModelGroupVO();
|
||||||
|
groupVO.setModelCode(modelCode);
|
||||||
|
groupVO.setModelName(params.get(0).getModelName());
|
||||||
|
|
||||||
|
List<ModelParamVO> paramVOs = params.stream()
|
||||||
|
.map(param -> {
|
||||||
|
ModelParamVO vo = new ModelParamVO();
|
||||||
|
BeanUtils.copyProperties(param, vo);
|
||||||
|
return vo;
|
||||||
|
})
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
groupVO.setParams(paramVOs);
|
||||||
|
models.add(groupVO);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 6. 按模型编码排序(保证固定顺序)
|
||||||
|
models.sort(Comparator.comparing(ModelGroupVO::getModelCode));
|
||||||
|
|
||||||
|
result.setModels(models);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**批量保存参数:**
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void saveAllParams(ModelParamSaveAllDTO saveAllDTO) {
|
||||||
|
try {
|
||||||
|
// 1. 参数验证
|
||||||
|
if (saveAllDTO.getProjectId() == null) {
|
||||||
|
throw new ServiceException("项目ID不能为空");
|
||||||
|
}
|
||||||
|
if (saveAllDTO.getModels() == null || saveAllDTO.getModels().isEmpty()) {
|
||||||
|
throw new ServiceException("参数列表不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
Long projectId = saveAllDTO.getProjectId();
|
||||||
|
|
||||||
|
// 2. 如果是项目保存,检查是否需要复制默认参数
|
||||||
|
if (projectId > 0) {
|
||||||
|
CcdiProject project = projectMapper.selectById(projectId);
|
||||||
|
if (project == null) {
|
||||||
|
throw new ServiceException("项目不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是首次保存(configType=default),需要复制所有模型的系统默认参数
|
||||||
|
if ("default".equals(project.getConfigType())) {
|
||||||
|
for (ModelParamGroupDTO modelGroup : saveAllDTO.getModels()) {
|
||||||
|
copyDefaultParamsToProject(projectId, modelGroup.getModelCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新项目配置类型为 custom
|
||||||
|
project.setConfigType("custom");
|
||||||
|
projectMapper.updateById(project);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 批量更新所有模型的参数值
|
||||||
|
String username = SecurityUtils.getUsername();
|
||||||
|
for (ModelParamGroupDTO modelGroup : saveAllDTO.getModels()) {
|
||||||
|
for (ParamValueItem item : modelGroup.getParams()) {
|
||||||
|
int updated = modelParamMapper.updateParamValue(
|
||||||
|
projectId,
|
||||||
|
modelGroup.getModelCode(),
|
||||||
|
item.getParamCode(),
|
||||||
|
item.getParamValue()
|
||||||
|
);
|
||||||
|
if (updated == 0) {
|
||||||
|
log.warn("参数不存在或未更新,modelCode={}, paramCode={}",
|
||||||
|
modelGroup.getModelCode(), item.getParamCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (ServiceException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("批量保存模型参数失败", e);
|
||||||
|
throw new ServiceException("批量保存模型参数失败:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2.3 后端Mapper层设计
|
||||||
|
|
||||||
|
#### 2.3.1 Mapper接口新增方法
|
||||||
|
|
||||||
|
```java
|
||||||
|
public interface CcdiModelParamMapper extends BaseMapper<CcdiModelParam> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据项目ID查询所有模型参数
|
||||||
|
*/
|
||||||
|
List<CcdiModelParam> selectByProjectId(@Param("projectId") Long projectId);
|
||||||
|
|
||||||
|
// ... 保留原有的其他方法
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2.3.2 Mapper XML
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<select id="selectByProjectId" resultType="CcdiModelParam">
|
||||||
|
SELECT * FROM ccdi_model_param
|
||||||
|
WHERE project_id = #{projectId}
|
||||||
|
ORDER BY model_code, sort_order
|
||||||
|
</select>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2.4 前端组件设计
|
||||||
|
|
||||||
|
#### 2.4.1 页面结构(两个页面相同布局)
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<div class="param-config-container">
|
||||||
|
<!-- 页面标题 -->
|
||||||
|
<div class="page-header">
|
||||||
|
<h2>{{ pageTitle }}</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 模型参数卡片组(垂直堆叠) -->
|
||||||
|
<div class="model-cards-container">
|
||||||
|
<div
|
||||||
|
v-for="model in modelGroups"
|
||||||
|
:key="model.modelCode"
|
||||||
|
class="model-card"
|
||||||
|
>
|
||||||
|
<!-- 模型标题 -->
|
||||||
|
<div class="model-header">
|
||||||
|
<h3>{{ model.modelName }}</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 参数表格 -->
|
||||||
|
<el-table :data="model.params" 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(model.modelCode, row)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="单位" prop="paramUnit" width="120" />
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 统一保存按钮 -->
|
||||||
|
<div class="button-section">
|
||||||
|
<el-button type="primary" @click="handleSaveAll" :loading="saving">
|
||||||
|
保存所有修改
|
||||||
|
</el-button>
|
||||||
|
<span v-if="modifiedCount > 0" class="modified-tip">
|
||||||
|
已修改 {{ modifiedCount }} 个参数
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2.4.2 核心数据结构
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// 页面标题(全局配置 vs 项目配置)
|
||||||
|
pageTitle: this.projectId ? '项目参数配置' : '全局模型参数管理',
|
||||||
|
|
||||||
|
// 模型参数数据(按模型分组)
|
||||||
|
modelGroups: [], // ModelGroupVO[]
|
||||||
|
|
||||||
|
// 修改记录(记录哪些参数被修改过)
|
||||||
|
modifiedParams: new Map(), // Map<modelCode, Set<paramCode>>
|
||||||
|
|
||||||
|
// 保存状态
|
||||||
|
saving: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2.4.3 核心方法
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
methods: {
|
||||||
|
/** 加载所有模型参数 */
|
||||||
|
async loadAllParams() {
|
||||||
|
try {
|
||||||
|
const res = await listAllParams({ projectId: this.projectId })
|
||||||
|
this.modelGroups = res.data.models
|
||||||
|
// 清空修改记录
|
||||||
|
this.modifiedParams.clear()
|
||||||
|
} catch (error) {
|
||||||
|
this.$message.error('加载参数失败:' + error.message)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 标记参数为已修改 */
|
||||||
|
markAsModified(modelCode, row) {
|
||||||
|
if (!this.modifiedParams.has(modelCode)) {
|
||||||
|
this.modifiedParams.set(modelCode, new Set())
|
||||||
|
}
|
||||||
|
this.modifiedParams.get(modelCode).add(row.paramCode)
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 保存所有修改 */
|
||||||
|
async handleSaveAll() {
|
||||||
|
// 验证是否有修改
|
||||||
|
if (this.modifiedCount === 0) {
|
||||||
|
this.$message.info('没有需要保存的修改')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构造保存数据(只包含修改过的参数)
|
||||||
|
const saveDTO = {
|
||||||
|
projectId: this.projectId,
|
||||||
|
models: []
|
||||||
|
}
|
||||||
|
|
||||||
|
this.modifiedParams.forEach((paramCodes, modelCode) => {
|
||||||
|
const modelGroup = this.modelGroups.find(m => m.modelCode === modelCode)
|
||||||
|
const modifiedParamList = modelGroup.params
|
||||||
|
.filter(p => paramCodes.has(p.paramCode))
|
||||||
|
.map(p => ({
|
||||||
|
paramCode: p.paramCode,
|
||||||
|
paramValue: p.paramValue
|
||||||
|
}))
|
||||||
|
|
||||||
|
if (modifiedParamList.length > 0) {
|
||||||
|
saveDTO.models.push({
|
||||||
|
modelCode: modelCode,
|
||||||
|
params: modifiedParamList
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 保存
|
||||||
|
this.saving = true
|
||||||
|
try {
|
||||||
|
await saveAllParams(saveDTO)
|
||||||
|
this.$message.success('保存成功')
|
||||||
|
// 清空修改记录并重新加载
|
||||||
|
this.modifiedParams.clear()
|
||||||
|
await this.loadAllParams()
|
||||||
|
} catch (error) {
|
||||||
|
this.$message.error('保存失败:' + error.message)
|
||||||
|
} finally {
|
||||||
|
this.saving = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
/** 计算已修改参数数量 */
|
||||||
|
modifiedCount() {
|
||||||
|
let count = 0
|
||||||
|
this.modifiedParams.forEach(params => {
|
||||||
|
count += params.size
|
||||||
|
})
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2.4.4 样式设计
|
||||||
|
|
||||||
|
```scss
|
||||||
|
.param-config-container {
|
||||||
|
padding: 20px;
|
||||||
|
background-color: #fff;
|
||||||
|
min-height: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding: 15px;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-cards-container {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-card {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
border: 1px solid #e4e7ed;
|
||||||
|
|
||||||
|
.model-header {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-section {
|
||||||
|
padding: 15px;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 4px;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
.modified-tip {
|
||||||
|
margin-left: 15px;
|
||||||
|
color: #909399;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2.5 前端API层设计
|
||||||
|
|
||||||
|
在 `ruoyi-ui/src/api/ccdi/modelParam.js` 中添加:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询所有模型及其参数(按模型分组)
|
||||||
|
*/
|
||||||
|
export function listAllParams(query) {
|
||||||
|
return request({
|
||||||
|
url: '/ccdi/modelParam/listAll',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量保存所有模型的参数修改
|
||||||
|
*/
|
||||||
|
export function saveAllParams(data) {
|
||||||
|
return request({
|
||||||
|
url: '/ccdi/modelParam/saveAll',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保留原有的其他API方法...
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、数据库设计
|
||||||
|
|
||||||
|
**无需修改数据库表结构**,现有的 `ccdi_model_param` 表结构已满足需求。
|
||||||
|
|
||||||
|
**现有表结构说明:**
|
||||||
|
|
||||||
|
| 字段名 | 类型 | 说明 |
|
||||||
|
|--------|------|------|
|
||||||
|
| id | BIGINT | 主键ID |
|
||||||
|
| project_id | BIGINT | 项目ID(0表示默认参数) |
|
||||||
|
| model_code | VARCHAR | 模型编码 |
|
||||||
|
| model_name | VARCHAR | 模型名称 |
|
||||||
|
| param_code | VARCHAR | 参数编码 |
|
||||||
|
| param_name | VARCHAR | 监测项名称 |
|
||||||
|
| param_desc | VARCHAR | 参数描述 |
|
||||||
|
| param_value | VARCHAR | 参数值 |
|
||||||
|
| param_unit | VARCHAR | 参数单位 |
|
||||||
|
| sort_order | INT | 排序号 |
|
||||||
|
| create_by | VARCHAR | 创建者 |
|
||||||
|
| create_time | DATETIME | 创建时间 |
|
||||||
|
| update_by | VARCHAR | 更新者 |
|
||||||
|
| update_time | DATETIME | 更新时间 |
|
||||||
|
| remark | VARCHAR | 备注 |
|
||||||
|
|
||||||
|
**索引说明:**
|
||||||
|
- 主键:`id`
|
||||||
|
- 常用查询索引:`idx_project_model` (`project_id`, `model_code`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、实现步骤
|
||||||
|
|
||||||
|
### 4.1 后端开发任务
|
||||||
|
|
||||||
|
#### 第一阶段:DTO/VO类创建
|
||||||
|
|
||||||
|
- [ ] 创建 `ModelParamAllQueryDTO.java` - 批量查询请求DTO
|
||||||
|
- [ ] 创建 `ModelParamAllVO.java` - 批量查询响应VO
|
||||||
|
- [ ] 创建 `ModelGroupVO.java` - 模型分组VO
|
||||||
|
- [ ] 创建 `ModelParamSaveAllDTO.java` - 批量保存请求DTO
|
||||||
|
- [ ] 创建 `ModelParamGroupDTO.java` - 模型参数分组DTO
|
||||||
|
|
||||||
|
#### 第二阶段:Mapper层修改
|
||||||
|
|
||||||
|
- [ ] 在 `CcdiModelParamMapper.java` 中添加 `selectByProjectId` 方法
|
||||||
|
- [ ] 在 `CcdiModelParamMapper.xml` 中添加对应的SQL查询
|
||||||
|
|
||||||
|
#### 第三阶段:Service层修改
|
||||||
|
|
||||||
|
- [ ] 在 `ICcdiModelParamService.java` 接口中添加 `selectAllParams` 方法
|
||||||
|
- [ ] 在 `ICcdiModelParamService.java` 接口中添加 `saveAllParams` 方法
|
||||||
|
- [ ] 在 `CcdiModelParamServiceImpl.java` 中实现 `selectAllParams` 方法
|
||||||
|
- [ ] 在 `CcdiModelParamServiceImpl.java` 中实现 `saveAllParams` 方法
|
||||||
|
|
||||||
|
#### 第四阶段:Controller层修改
|
||||||
|
|
||||||
|
- [ ] 在 `CcdiModelParamController.java` 中添加 `listAll` 接口(GET)
|
||||||
|
- [ ] 在 `CcdiModelParamController.java` 中添加 `saveAll` 接口(POST)
|
||||||
|
|
||||||
|
#### 第五阶段:后端测试
|
||||||
|
|
||||||
|
- [ ] 使用 Swagger 测试 `listAll` 接口
|
||||||
|
- 测试全局配置查询(projectId=0)
|
||||||
|
- 测试项目配置查询(projectId>0)
|
||||||
|
- 测试使用默认配置的项目(configType=default)
|
||||||
|
- [ ] 使用 Swagger 测试 `saveAll` 接口
|
||||||
|
- 测试全局配置保存
|
||||||
|
- 测试项目首次保存(验证参数复制逻辑)
|
||||||
|
- 测试项目二次保存
|
||||||
|
- 测试多模型同时保存
|
||||||
|
- [ ] 验证错误处理
|
||||||
|
- 参数验证失败
|
||||||
|
- 项目不存在
|
||||||
|
- 数据库异常
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4.2 前端开发任务
|
||||||
|
|
||||||
|
#### 第一阶段:API层修改
|
||||||
|
|
||||||
|
- [ ] 在 `ruoyi-ui/src/api/ccdi/modelParam.js` 中添加 `listAllParams` 方法
|
||||||
|
- [ ] 在 `ruoyi-ui/src/api/ccdi/modelParam.js` 中添加 `saveAllParams` 方法
|
||||||
|
|
||||||
|
#### 第二阶段:全局配置页面重构
|
||||||
|
|
||||||
|
- [ ] 重构 `ruoyi-ui/src/views/ccdi/modelParam/index.vue`
|
||||||
|
- 去掉模型下拉框
|
||||||
|
- 添加页面标题
|
||||||
|
- 实现垂直堆叠布局展示所有模型
|
||||||
|
- 实现参数修改跟踪
|
||||||
|
- 实现统一保存按钮
|
||||||
|
- 添加修改提示(显示已修改参数数量)
|
||||||
|
- 优化样式
|
||||||
|
|
||||||
|
#### 第三阶段:项目配置页面重构
|
||||||
|
|
||||||
|
- [ ] 重构 `ruoyi-ui/src/views/ccdiProject/components/detail/ParamConfig.vue`
|
||||||
|
- 采用与全局配置页面相同的布局和逻辑
|
||||||
|
- 适配 projectId 传递
|
||||||
|
- 适配项目信息显示
|
||||||
|
|
||||||
|
#### 第四阶段:前端测试
|
||||||
|
|
||||||
|
- [ ] 测试全局配置页面
|
||||||
|
- 页面加载是否正确显示所有模型
|
||||||
|
- 参数修改和标记是否正常
|
||||||
|
- 统一保存功能是否正常
|
||||||
|
- 修改提示是否准确
|
||||||
|
- [ ] 测试项目配置页面
|
||||||
|
- 页面加载是否正确显示所有模型
|
||||||
|
- 参数修改和保存功能是否正常
|
||||||
|
- 使用默认配置的项目是否正确显示系统参数
|
||||||
|
- 首次保存是否成功
|
||||||
|
- [ ] 测试用户体验
|
||||||
|
- 页面加载速度
|
||||||
|
- 操作流畅性
|
||||||
|
- 错误提示友好性
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 五、兼容性与迁移说明
|
||||||
|
|
||||||
|
### 5.1 向后兼容
|
||||||
|
|
||||||
|
**保留原有接口:**
|
||||||
|
- 原有的 `GET /list` 接口保留,不影响其他可能的调用方
|
||||||
|
- 原有的 `POST /save` 接口保留,继续可用
|
||||||
|
|
||||||
|
**数据库无变更:**
|
||||||
|
- 数据库表结构无修改
|
||||||
|
- 现有数据无需迁移
|
||||||
|
|
||||||
|
### 5.2 废弃说明
|
||||||
|
|
||||||
|
**功能废弃:**
|
||||||
|
- 前端的模型下拉框切换方式不再使用
|
||||||
|
- 但后端接口仍保留,以确保向后兼容
|
||||||
|
|
||||||
|
**建议:**
|
||||||
|
- 逐步迁移所有调用方到新接口
|
||||||
|
- 未来版本可以废弃旧接口
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六、性能考虑
|
||||||
|
|
||||||
|
### 6.1 查询性能
|
||||||
|
|
||||||
|
**优化措施:**
|
||||||
|
- 使用 `selectByProjectId` 一次性查询所有参数,减少数据库往返
|
||||||
|
- 在内存中按模型分组,避免多次查询
|
||||||
|
- 利用现有的 `idx_project_model` 索引
|
||||||
|
|
||||||
|
**预期性能:**
|
||||||
|
- 当前模型数量:3个
|
||||||
|
- 预计参数总数:约30个
|
||||||
|
- 单次查询时间:<50ms
|
||||||
|
- 完全满足性能要求
|
||||||
|
|
||||||
|
### 6.2 保存性能
|
||||||
|
|
||||||
|
**优化措施:**
|
||||||
|
- 只保存修改过的参数,减少数据库更新操作
|
||||||
|
- 使用事务保证数据一致性
|
||||||
|
- 批量更新,避免多次提交
|
||||||
|
|
||||||
|
**预期性能:**
|
||||||
|
- 典型修改场景:1-5个参数
|
||||||
|
- 保存时间:<100ms
|
||||||
|
- 完全满足性能要求
|
||||||
|
|
||||||
|
### 6.3 前端性能
|
||||||
|
|
||||||
|
**优化措施:**
|
||||||
|
- 使用 `v-for` 高效渲染列表
|
||||||
|
- 使用计算属性缓存已修改参数数量
|
||||||
|
- 避免不必要的重渲染
|
||||||
|
|
||||||
|
**预期性能:**
|
||||||
|
- 页面渲染时间:<200ms
|
||||||
|
- 操作响应时间:<50ms
|
||||||
|
- 完全满足用户体验要求
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 七、安全考虑
|
||||||
|
|
||||||
|
### 7.1 权限控制
|
||||||
|
|
||||||
|
**现有权限机制:**
|
||||||
|
- 使用 Spring Security + JWT 进行认证
|
||||||
|
- 基于角色的访问控制(RBAC)
|
||||||
|
- 新接口继承现有权限控制机制
|
||||||
|
|
||||||
|
**权限标识:**
|
||||||
|
- 查询:`ccdi:modelParam:list`
|
||||||
|
- 保存:`ccdi:modelParam:edit`
|
||||||
|
|
||||||
|
### 7.2 数据验证
|
||||||
|
|
||||||
|
**后端验证:**
|
||||||
|
- 使用 `@Validated` 注解进行参数验证
|
||||||
|
- 验证项目ID、模型编码、参数编码的合法性
|
||||||
|
- 验证参数值的格式和范围
|
||||||
|
|
||||||
|
**前端验证:**
|
||||||
|
- 参数值非空验证
|
||||||
|
- 参数值格式验证
|
||||||
|
|
||||||
|
### 7.3 数据一致性
|
||||||
|
|
||||||
|
**事务管理:**
|
||||||
|
- 使用 `@Transactional` 保证批量保存的原子性
|
||||||
|
- 保存失败时自动回滚
|
||||||
|
|
||||||
|
**并发控制:**
|
||||||
|
- 使用乐观锁或悲观锁(根据实际并发情况决定)
|
||||||
|
- 当前场景并发量低,无需特殊处理
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 八、测试策略
|
||||||
|
|
||||||
|
### 8.1 单元测试
|
||||||
|
|
||||||
|
**Service层测试:**
|
||||||
|
- 测试 `selectAllParams` 方法
|
||||||
|
- 测试全局配置查询
|
||||||
|
- 测试项目配置查询
|
||||||
|
- 测试使用默认配置的项目
|
||||||
|
- 测试空数据情况
|
||||||
|
- 测试 `saveAllParams` 方法
|
||||||
|
- 测试参数验证
|
||||||
|
- 测试首次保存(参数复制)
|
||||||
|
- 测试二次保存
|
||||||
|
- 测试事务回滚
|
||||||
|
|
||||||
|
### 8.2 集成测试
|
||||||
|
|
||||||
|
**API接口测试:**
|
||||||
|
- 使用 Swagger UI 进行接口测试
|
||||||
|
- 测试各种参数组合
|
||||||
|
- 测试错误场景
|
||||||
|
|
||||||
|
### 8.3 前端测试
|
||||||
|
|
||||||
|
**功能测试:**
|
||||||
|
- 测试页面加载和渲染
|
||||||
|
- 测试参数修改和标记
|
||||||
|
- 测试保存功能
|
||||||
|
- 测试错误处理
|
||||||
|
|
||||||
|
**用户体验测试:**
|
||||||
|
- 测试页面响应速度
|
||||||
|
- 测试操作流畅性
|
||||||
|
- 测试错误提示友好性
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 九、风险评估
|
||||||
|
|
||||||
|
### 9.1 技术风险
|
||||||
|
|
||||||
|
| 风险 | 概率 | 影响 | 应对措施 |
|
||||||
|
|------|------|------|----------|
|
||||||
|
| 后端接口设计不合理 | 低 | 中 | 充分设计评审,参考现有接口 |
|
||||||
|
| 前端组件复杂度高 | 低 | 低 | 采用简单清晰的组件结构 |
|
||||||
|
| 数据库查询性能差 | 极低 | 中 | 已有索引支持,数据量小 |
|
||||||
|
| 批量保存失败 | 低 | 高 | 使用事务保证原子性 |
|
||||||
|
|
||||||
|
### 9.2 业务风险
|
||||||
|
|
||||||
|
| 风险 | 概率 | 影响 | 应对措施 |
|
||||||
|
|------|------|------|----------|
|
||||||
|
| 用户不习惯新界面 | 中 | 低 | 提供用户培训,界面简洁直观 |
|
||||||
|
| 误操作导致参数错误 | 低 | 高 | 添加确认提示,记录操作日志 |
|
||||||
|
| 保存时数据丢失 | 极低 | 高 | 使用事务,添加错误处理 |
|
||||||
|
|
||||||
|
### 9.3 兼容性风险
|
||||||
|
|
||||||
|
| 风险 | 概率 | 影响 | 应对措施 |
|
||||||
|
|------|------|------|----------|
|
||||||
|
| 旧接口调用方受影响 | 低 | 低 | 保留旧接口,逐步迁移 |
|
||||||
|
| 数据库不兼容 | 极低 | 高 | 无数据库结构变更 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 十、上线计划
|
||||||
|
|
||||||
|
### 10.1 上线前准备
|
||||||
|
|
||||||
|
- [ ] 完成所有开发任务
|
||||||
|
- [ ] 完成所有测试任务
|
||||||
|
- [ ] 准备上线文档
|
||||||
|
- [ ] 准备回滚方案
|
||||||
|
|
||||||
|
### 10.2 上线步骤
|
||||||
|
|
||||||
|
1. **后端部署**
|
||||||
|
- 停止应用服务
|
||||||
|
- 部署新版本代码
|
||||||
|
- 启动应用服务
|
||||||
|
- 验证接口可用性
|
||||||
|
|
||||||
|
2. **前端部署**
|
||||||
|
- 构建前端代码
|
||||||
|
- 部署到服务器
|
||||||
|
- 清理浏览器缓存
|
||||||
|
- 验证页面可用性
|
||||||
|
|
||||||
|
3. **功能验证**
|
||||||
|
- 测试全局配置页面
|
||||||
|
- 测试项目配置页面
|
||||||
|
- 验证保存功能
|
||||||
|
- 验证数据一致性
|
||||||
|
|
||||||
|
### 10.3 上线后监控
|
||||||
|
|
||||||
|
- [ ] 监控接口响应时间
|
||||||
|
- [ ] 监控错误日志
|
||||||
|
- [ ] 收集用户反馈
|
||||||
|
- [ ] 准备问题修复
|
||||||
|
|
||||||
|
### 10.4 回滚方案
|
||||||
|
|
||||||
|
**如果出现严重问题:**
|
||||||
|
1. 前端回滚到旧版本
|
||||||
|
2. 后端回滚到旧版本(接口保留不影响)
|
||||||
|
3. 数据无需回滚(无数据库变更)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 十一、总结
|
||||||
|
|
||||||
|
本次设计采用了优化接口的方案,通过新增批量查询和批量保存接口,实现了在同一页面中展示和编辑所有模型参数的需求。设计充分考虑了性能、安全性、兼容性和可维护性,是一个可行且高效的解决方案。
|
||||||
|
|
||||||
|
**设计亮点:**
|
||||||
|
- ✅ 接口设计合理,易于理解和扩展
|
||||||
|
- ✅ 前后端分离,逻辑清晰
|
||||||
|
- ✅ 保留向后兼容,降低风险
|
||||||
|
- ✅ 性能优化,用户体验好
|
||||||
|
- ✅ 代码复用性高,可维护性好
|
||||||
|
|
||||||
|
**预期收益:**
|
||||||
|
- 🎯 提升用户体验,减少操作步骤
|
||||||
|
- 🎯 提高工作效率,一次查看所有模型
|
||||||
|
- 🎯 降低误操作风险,统一保存机制
|
||||||
|
- 🎯 代码结构更清晰,便于后续维护
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 附录
|
||||||
|
|
||||||
|
### A. 相关文档
|
||||||
|
|
||||||
|
- [若依框架官方文档](http://doc.ruoyi.vip/)
|
||||||
|
- [Element UI 组件库](https://element.eleme.cn/)
|
||||||
|
- [MyBatis Plus 官方文档](https://baomidou.com/)
|
||||||
|
|
||||||
|
### B. 变更记录
|
||||||
|
|
||||||
|
| 版本 | 日期 | 修改人 | 修改内容 |
|
||||||
|
|------|------|--------|----------|
|
||||||
|
| v1.0 | 2026-03-06 | Claude Code | 初始版本 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**文档结束**
|
||||||
Reference in New Issue
Block a user