Merge branch 'feature/model-param-config' into dev
This commit is contained in:
@@ -30,6 +30,30 @@
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- spring-doc -->
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.30</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.ruoyi.ccdi.project.controller;
|
||||
|
||||
import com.ruoyi.common.annotation.Log;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.enums.BusinessType;
|
||||
import com.ruoyi.ccdi.project.domain.dto.ModelParamQueryDTO;
|
||||
import com.ruoyi.ccdi.project.domain.dto.ModelParamSaveDTO;
|
||||
import com.ruoyi.ccdi.project.domain.vo.ModelListVO;
|
||||
import com.ruoyi.ccdi.project.domain.vo.ModelParamVO;
|
||||
import com.ruoyi.ccdi.project.service.ICcdiModelParamService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 模型参数配置Controller
|
||||
*/
|
||||
@Tag(name = "模型参数配置")
|
||||
@RestController
|
||||
@RequestMapping("/ccdi/modelParam")
|
||||
public class CcdiModelParamController extends BaseController {
|
||||
|
||||
@Resource
|
||||
private ICcdiModelParamService modelParamService;
|
||||
|
||||
/**
|
||||
* 查询模型列表
|
||||
*/
|
||||
@Operation(summary = "查询模型列表")
|
||||
@GetMapping("/modelList")
|
||||
public AjaxResult listModels(@RequestParam(required = false) Long projectId) {
|
||||
List<ModelListVO> list = modelParamService.selectModelList(projectId);
|
||||
return success(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询模型参数列表
|
||||
*/
|
||||
@Operation(summary = "查询模型参数列表")
|
||||
@GetMapping("/list")
|
||||
public AjaxResult list(@Validated ModelParamQueryDTO queryDTO) {
|
||||
List<ModelParamVO> list = modelParamService.selectParamList(queryDTO);
|
||||
return success(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存模型参数(只更新阈值)
|
||||
*/
|
||||
@Operation(summary = "保存模型参数")
|
||||
@Log(title = "模型参数配置", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/save")
|
||||
public AjaxResult save(@Validated @RequestBody ModelParamSaveDTO saveDTO) {
|
||||
modelParamService.saveParams(saveDTO);
|
||||
return success("保存成功");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.ruoyi.ccdi.project.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.ruoyi.ccdi.project.domain.CcdiModelParam;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 模型参数Mapper
|
||||
*/
|
||||
public interface CcdiModelParamMapper extends BaseMapper<CcdiModelParam> {
|
||||
|
||||
/**
|
||||
* 查询指定项目和模型的参数列表
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @param modelCode 模型编码
|
||||
* @return 参数列表
|
||||
*/
|
||||
List<CcdiModelParam> selectByProjectAndModel(
|
||||
@Param("projectId") Long projectId,
|
||||
@Param("modelCode") String modelCode
|
||||
);
|
||||
|
||||
/**
|
||||
* 查询所有模型列表(去重)
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @return 模型列表
|
||||
*/
|
||||
List<CcdiModelParam> selectDistinctModels(@Param("projectId") Long projectId);
|
||||
|
||||
/**
|
||||
* 批量更新参数值(只更新param_value字段)
|
||||
*
|
||||
* @param list 参数列表
|
||||
* @return 更新数量
|
||||
*/
|
||||
int batchUpdateParamValues(@Param("list") List<CcdiModelParam> list);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.ruoyi.ccdi.project.service;
|
||||
|
||||
import com.ruoyi.ccdi.project.domain.dto.ModelParamQueryDTO;
|
||||
import com.ruoyi.ccdi.project.domain.dto.ModelParamSaveDTO;
|
||||
import com.ruoyi.ccdi.project.domain.vo.ModelListVO;
|
||||
import com.ruoyi.ccdi.project.domain.vo.ModelParamVO;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 模型参数Service
|
||||
*/
|
||||
public interface ICcdiModelParamService {
|
||||
|
||||
/**
|
||||
* 查询模型列表
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @return 模型列表
|
||||
*/
|
||||
List<ModelListVO> selectModelList(Long projectId);
|
||||
|
||||
/**
|
||||
* 查询模型参数列表
|
||||
*
|
||||
* @param queryDTO 查询条件
|
||||
* @return 参数列表
|
||||
*/
|
||||
List<ModelParamVO> selectParamList(ModelParamQueryDTO queryDTO);
|
||||
|
||||
/**
|
||||
* 保存模型参数(只更新阈值)
|
||||
*
|
||||
* @param saveDTO 保存参数
|
||||
*/
|
||||
void saveParams(ModelParamSaveDTO saveDTO);
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package com.ruoyi.ccdi.project.service.impl;
|
||||
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.utils.SecurityUtils;
|
||||
import com.ruoyi.ccdi.project.domain.CcdiModelParam;
|
||||
import com.ruoyi.ccdi.project.domain.dto.ModelParamQueryDTO;
|
||||
import com.ruoyi.ccdi.project.domain.dto.ModelParamSaveDTO;
|
||||
import com.ruoyi.ccdi.project.domain.vo.ModelListVO;
|
||||
import com.ruoyi.ccdi.project.domain.vo.ModelParamVO;
|
||||
import com.ruoyi.ccdi.project.mapper.CcdiModelParamMapper;
|
||||
import com.ruoyi.ccdi.project.service.ICcdiModelParamService;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 模型参数Service实现
|
||||
*/
|
||||
@Service
|
||||
public class CcdiModelParamServiceImpl implements ICcdiModelParamService {
|
||||
|
||||
@Resource
|
||||
private CcdiModelParamMapper modelParamMapper;
|
||||
|
||||
@Override
|
||||
public List<ModelListVO> selectModelList(Long projectId) {
|
||||
if (projectId == null) {
|
||||
projectId = 0L; // 默认查询系统级参数
|
||||
}
|
||||
|
||||
List<ModelListVO> result = new ArrayList<>();
|
||||
List<CcdiModelParam> params = modelParamMapper.selectDistinctModels(projectId);
|
||||
|
||||
params.forEach(param -> {
|
||||
ModelListVO vo = new ModelListVO();
|
||||
vo.setModelCode(param.getModelCode());
|
||||
vo.setModelName(param.getModelName());
|
||||
result.add(vo);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ModelParamVO> selectParamList(ModelParamQueryDTO queryDTO) {
|
||||
Long projectId = queryDTO.getProjectId();
|
||||
if (projectId == null) {
|
||||
projectId = 0L;
|
||||
}
|
||||
|
||||
List<CcdiModelParam> params = modelParamMapper.selectByProjectAndModel(
|
||||
projectId,
|
||||
queryDTO.getModelCode()
|
||||
);
|
||||
|
||||
List<ModelParamVO> result = new ArrayList<>();
|
||||
params.forEach(param -> {
|
||||
ModelParamVO vo = new ModelParamVO();
|
||||
BeanUtils.copyProperties(param, vo);
|
||||
result.add(vo);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void saveParams(ModelParamSaveDTO saveDTO) {
|
||||
Long projectId = saveDTO.getProjectId();
|
||||
if (projectId == null) {
|
||||
projectId = 0L;
|
||||
}
|
||||
|
||||
// 空列表校验
|
||||
if (saveDTO.getParams() == null || saveDTO.getParams().isEmpty()) {
|
||||
throw new ServiceException("参数列表不能为空");
|
||||
}
|
||||
|
||||
String username = SecurityUtils.getUsername();
|
||||
Date now = new Date();
|
||||
|
||||
// 查询现有参数
|
||||
List<CcdiModelParam> existingParams = modelParamMapper.selectByProjectAndModel(
|
||||
projectId,
|
||||
saveDTO.getModelCode()
|
||||
);
|
||||
|
||||
if (existingParams.isEmpty()) {
|
||||
throw new ServiceException("未找到模型参数配置");
|
||||
}
|
||||
|
||||
// 构建Map提升性能
|
||||
Map<String, CcdiModelParam> existingMap = existingParams.stream()
|
||||
.collect(Collectors.toMap(CcdiModelParam::getParamCode, p -> p));
|
||||
|
||||
// 准备更新列表 - 只更新 param_value 字段
|
||||
List<CcdiModelParam> updateList = new ArrayList<>();
|
||||
for (ModelParamSaveDTO.ParamItem item : saveDTO.getParams()) {
|
||||
CcdiModelParam existing = existingMap.get(item.getParamCode());
|
||||
|
||||
if (existing != null) {
|
||||
// ⚠️ 关键:只修改 param_value 字段
|
||||
CcdiModelParam updateParam = new CcdiModelParam();
|
||||
updateParam.setId(existing.getId());
|
||||
updateParam.setParamValue(item.getParamValue()); // 只更新阈值
|
||||
updateParam.setUpdateBy(username);
|
||||
updateParam.setUpdateTime(now);
|
||||
updateList.add(updateParam);
|
||||
}
|
||||
}
|
||||
|
||||
if (!updateList.isEmpty()) {
|
||||
modelParamMapper.batchUpdateParamValues(updateList);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ruoyi.ccdi.project.mapper.CcdiModelParamMapper">
|
||||
|
||||
<resultMap id="ModelParamResult" type="com.ruoyi.ccdi.project.domain.CcdiModelParam">
|
||||
<id property="id" column="id"/>
|
||||
<result property="projectId" column="project_id"/>
|
||||
<result property="modelCode" column="model_code"/>
|
||||
<result property="modelName" column="model_name"/>
|
||||
<result property="paramCode" column="param_code"/>
|
||||
<result property="paramName" column="param_name"/>
|
||||
<result property="paramDesc" column="param_desc"/>
|
||||
<result property="paramValue" column="param_value"/>
|
||||
<result property="paramUnit" column="param_unit"/>
|
||||
<result property="sortOrder" column="sort_order"/>
|
||||
<result property="createBy" column="create_by"/>
|
||||
<result property="createTime" column="create_time"/>
|
||||
<result property="updateBy" column="update_by"/>
|
||||
<result property="updateTime" column="update_time"/>
|
||||
<result property="remark" column="remark"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectModelParamVo">
|
||||
select id, project_id, model_code, model_name, param_code, param_name, param_desc,
|
||||
param_value, param_unit, sort_order, create_by, create_time, update_by, update_time, remark
|
||||
from ccdi_model_param
|
||||
</sql>
|
||||
|
||||
<select id="selectByProjectAndModel" resultMap="ModelParamResult">
|
||||
<include refid="selectModelParamVo"/>
|
||||
where project_id = #{projectId} and model_code = #{modelCode}
|
||||
order by sort_order asc
|
||||
</select>
|
||||
|
||||
<select id="selectDistinctModels" resultMap="ModelParamResult">
|
||||
select distinct model_code, model_name
|
||||
from ccdi_model_param
|
||||
where project_id = #{projectId}
|
||||
order by model_code
|
||||
</select>
|
||||
|
||||
<!-- 关键:只更新 param_value 字段,使用 CASE WHEN 批量更新 -->
|
||||
<update id="batchUpdateParamValues">
|
||||
update ccdi_model_param
|
||||
<set>
|
||||
<trim prefix="param_value = case" suffix="end,">
|
||||
<foreach collection="list" item="item">
|
||||
when id = #{item.id} then #{item.paramValue}
|
||||
</foreach>
|
||||
</trim>
|
||||
<trim prefix="update_by = case" suffix="end,">
|
||||
<foreach collection="list" item="item">
|
||||
when id = #{item.id} then #{item.updateBy}
|
||||
</foreach>
|
||||
</trim>
|
||||
update_time = sysdate()
|
||||
</set>
|
||||
where id in
|
||||
<foreach collection="list" item="item" open="(" separator="," close=")">
|
||||
#{item.id}
|
||||
</foreach>
|
||||
</update>
|
||||
|
||||
</mapper>
|
||||
325
doc/test-scripts/后端功能测试报告.md
Normal file
325
doc/test-scripts/后端功能测试报告.md
Normal file
@@ -0,0 +1,325 @@
|
||||
# 后端功能测试报告
|
||||
|
||||
## 测试环境
|
||||
- 后端地址: http://localhost:8080
|
||||
- Swagger 地址: http://localhost:8080/swagger-ui/index.html
|
||||
- 数据库: 116.62.17.81:3306/ccdi
|
||||
- 测试时间: 2026-02-26 02:03:10 (UTC)
|
||||
|
||||
## 测试账号
|
||||
- 用户名: admin
|
||||
- 密码: admin123
|
||||
- Token: `eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhZG1pbiIsImxvZ2luX3VzZXJfa2V5IjoiOTJjODUzYWUtNDZjNi00ZmQ3LWExMDEtYTA5NzRmMzlmOGNkIn0.AUiHT2p-wcETEN1rZtgP8oSdx1kHWpYUT-TZmfjECON6T-p0M94mvwN1ySJmC4yeozu4VCZm13cRvkqwzH7Teg`
|
||||
|
||||
---
|
||||
|
||||
## 测试结果
|
||||
|
||||
### 1. 登录接口测试
|
||||
|
||||
**接口:** `POST /login/test`
|
||||
|
||||
**请求:**
|
||||
```bash
|
||||
curl -X POST "http://localhost:8080/login/test" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username":"admin","password":"admin123"}'
|
||||
```
|
||||
|
||||
**响应:**
|
||||
```json
|
||||
{
|
||||
"msg": "操作成功",
|
||||
"code": 200,
|
||||
"token": "eyJhbGciOiJIUzUxMiJ9..."
|
||||
}
|
||||
```
|
||||
|
||||
**状态:** ✅ 通过
|
||||
|
||||
---
|
||||
|
||||
### 2. 查询模型列表接口
|
||||
|
||||
**接口:** `GET /ccdi/modelParam/modelList`
|
||||
|
||||
**请求:**
|
||||
```bash
|
||||
curl -X GET "http://localhost:8080/ccdi/modelParam/modelList" \
|
||||
-H "Authorization: Bearer {token}"
|
||||
```
|
||||
|
||||
**响应:**
|
||||
```json
|
||||
{
|
||||
"msg": "操作成功",
|
||||
"code": 200,
|
||||
"data": [
|
||||
{
|
||||
"modelCode": "LARGE_TRANSACTION",
|
||||
"modelName": "大额交易模型"
|
||||
},
|
||||
{
|
||||
"modelCode": "SUSPICIOUS_FOREIGN_EXCHANGE",
|
||||
"modelName": "可疑外汇交易模型"
|
||||
},
|
||||
{
|
||||
"modelCode": "SUSPICIOUS_PART_TIME",
|
||||
"modelName": "可疑兼职模型"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**验证点:**
|
||||
- ✅ 返回3个模型
|
||||
- ✅ 包含预期的模型代码和名称
|
||||
- ✅ 响应格式正确
|
||||
|
||||
**状态:** ✅ 通过
|
||||
|
||||
---
|
||||
|
||||
### 3. 查询模型参数列表接口
|
||||
|
||||
#### 3.1 大额交易模型参数查询
|
||||
|
||||
**接口:** `GET /ccdi/modelParam/list?modelCode=LARGE_TRANSACTION`
|
||||
|
||||
**响应数据摘要:**
|
||||
| 参数代码 | 参数名称 | 参数值 | 单位 |
|
||||
|---------|---------|--------|------|
|
||||
| SINGLE_TRANSACTION_AMOUNT | 单笔交易额 | 50000 | 元 |
|
||||
| CUMULATIVE_TRANSACTION_AMOUNT | 累计交易额 | 5000000 | 元 |
|
||||
| LARGE_CASH_DEPOSIT | 大额存现 | 200000 | 元 |
|
||||
| FREQUENT_CASH_DEPOSIT | 短时多次存现 | 100000 | 元/4小时 |
|
||||
| FREQUENT_TRANSFER | 频繁转账 | 10 | 次/日 |
|
||||
| TRANSFER_FREQUENCY | 转账频率 | 1000000 | 元/日 |
|
||||
|
||||
**验证点:**
|
||||
- ✅ 返回6个参数
|
||||
- ✅ 所有参数字段完整
|
||||
- ✅ 排序正确(sortOrder: 1-6)
|
||||
|
||||
**状态:** ✅ 通过
|
||||
|
||||
#### 3.2 可疑外汇交易模型参数查询
|
||||
|
||||
**接口:** `GET /ccdi/modelParam/list?modelCode=SUSPICIOUS_FOREIGN_EXCHANGE`
|
||||
|
||||
**响应数据摘要:**
|
||||
| 参数代码 | 参数名称 | 参数值 | 单位 |
|
||||
|---------|---------|--------|------|
|
||||
| SINGLE_PURCHASE_AMOUNT | 单笔购汇金额 | 50000 | 美元/笔 |
|
||||
| SINGLE_SETTLEMENT_AMOUNT | 单笔结汇金额 | 50000 | 美元/笔 |
|
||||
| CROSS_BORDER_REMITTANCE | 跨境汇款金额 | 200000 | 美元/笔 |
|
||||
| MONTHLY_PURCHASE_TOTAL | 月度购汇总额 | 100000 | 美元/月 |
|
||||
| MONTHLY_SETTLEMENT_TOTAL | 月度结汇总额 | 100000 | 美元/月 |
|
||||
| FREQUENT_FOREX_TRADE | 频繁外汇交易 | 5 | 次/日 |
|
||||
|
||||
**验证点:**
|
||||
- ✅ 返回6个参数
|
||||
- ✅ 所有参数字段完整
|
||||
|
||||
**状态:** ✅ 通过
|
||||
|
||||
#### 3.3 可疑兼职模型参数查询
|
||||
|
||||
**接口:** `GET /ccdi/modelParam/list?modelCode=SUSPICIOUS_PART_TIME`
|
||||
|
||||
**响应数据摘要:**
|
||||
| 参数代码 | 参数名称 | 参数值 | 单位 |
|
||||
|---------|---------|--------|------|
|
||||
| MONTHLY_FIXED_INCOME | 月度固定收入 | 5000 | 元/月 |
|
||||
| FIXED_COUNTERPARTY_TRANSFER | 固定对手转入 | 15000 | 元/季 |
|
||||
| SUSPICIOUS_TIME_TRANSACTION | 非工作时间交易 | 20 | 次/月 |
|
||||
|
||||
**验证点:**
|
||||
- ✅ 返回3个参数
|
||||
- ✅ 所有参数字段完整
|
||||
|
||||
**状态:** ✅ 通过
|
||||
|
||||
---
|
||||
|
||||
### 4. 保存参数配置接口
|
||||
|
||||
#### 4.1 正常保存测试
|
||||
|
||||
**接口:** `POST /ccdi/modelParam/save`
|
||||
|
||||
**请求:**
|
||||
```json
|
||||
{
|
||||
"projectId": 0,
|
||||
"modelCode": "LARGE_TRANSACTION",
|
||||
"modelName": "大额交易模型",
|
||||
"params": [
|
||||
{
|
||||
"paramCode": "SINGLE_TRANSACTION_AMOUNT",
|
||||
"paramName": "单笔交易额",
|
||||
"paramDesc": "单笔超过该金额视为大额交易",
|
||||
"paramValue": "60000",
|
||||
"paramUnit": "元",
|
||||
"sortOrder": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**响应:**
|
||||
```json
|
||||
{
|
||||
"msg": "保存成功",
|
||||
"code": 200
|
||||
}
|
||||
```
|
||||
|
||||
**状态:** ✅ 通过
|
||||
|
||||
---
|
||||
|
||||
### 5. 数据库验证
|
||||
|
||||
#### 5.1 第一次更新验证
|
||||
|
||||
**SQL:**
|
||||
```sql
|
||||
SELECT param_value, update_by, update_time
|
||||
FROM ccdi_model_param
|
||||
WHERE model_code = 'LARGE_TRANSACTION'
|
||||
AND param_code = 'SINGLE_TRANSACTION_AMOUNT';
|
||||
```
|
||||
|
||||
**结果:**
|
||||
| param_value | update_by | update_time |
|
||||
|-------------|-----------|-------------|
|
||||
| 60000 | admin | 2026-02-25 18:03:10 |
|
||||
|
||||
**验证点:**
|
||||
- ✅ param_value 已更新为 60000
|
||||
- ✅ update_by 有值 (admin)
|
||||
- ✅ update_time 有值
|
||||
|
||||
**状态:** ✅ 通过
|
||||
|
||||
---
|
||||
|
||||
### 6. 安全性验证
|
||||
|
||||
#### 6.1 尝试修改其他字段
|
||||
|
||||
**请求:**
|
||||
```json
|
||||
{
|
||||
"projectId": 0,
|
||||
"modelCode": "LARGE_TRANSACTION",
|
||||
"modelName": "大额交易模型(恶意修改)",
|
||||
"params": [
|
||||
{
|
||||
"paramCode": "SINGLE_TRANSACTION_AMOUNT",
|
||||
"paramName": "单笔交易额(恶意修改)",
|
||||
"paramDesc": "恶意修改描述",
|
||||
"paramValue": "70000",
|
||||
"paramUnit": "美元",
|
||||
"sortOrder": 99
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**数据库验证结果:**
|
||||
| 字段 | 预期值 | 实际值 | 结果 |
|
||||
|------|--------|--------|------|
|
||||
| param_name | 单笔交易额 | 单笔交易额 | ✅ 未被修改 |
|
||||
| param_desc | 单笔超过该金额视为大额交易 | 单笔超过该金额视为大额交易 | ✅ 未被修改 |
|
||||
| param_value | 70000 | 70000 | ✅ 正确更新 |
|
||||
| param_unit | 元 | 元 | ✅ 未被修改 |
|
||||
| sort_order | 1 | 1 | ✅ 未被修改 |
|
||||
| update_by | admin | admin | ✅ 有值 |
|
||||
| update_time | 有值 | 2026-02-25 18:03:33 | ✅ 有值 |
|
||||
|
||||
**结论:**
|
||||
- ✅ 只有 param_value 字段被更新
|
||||
- ✅ 其他字段(param_name、param_desc、param_unit、sort_order)保持不变
|
||||
- ✅ 安全性验证通过
|
||||
|
||||
**状态:** ✅ 通过
|
||||
|
||||
---
|
||||
|
||||
## 接口汇总
|
||||
|
||||
| 序号 | 接口 | 方法 | 路径 | 状态 |
|
||||
|------|------|------|------|------|
|
||||
| 1 | 登录获取Token | POST | /login/test | ✅ 通过 |
|
||||
| 2 | 查询模型列表 | GET | /ccdi/modelParam/modelList | ✅ 通过 |
|
||||
| 3 | 查询模型参数(大额交易) | GET | /ccdi/modelParam/list?modelCode=LARGE_TRANSACTION | ✅ 通过 |
|
||||
| 4 | 查询模型参数(外汇交易) | GET | /ccdi/modelParam/list?modelCode=SUSPICIOUS_FOREIGN_EXCHANGE | ✅ 通过 |
|
||||
| 5 | 查询模型参数(兼职) | GET | /ccdi/modelParam/list?modelCode=SUSPICIOUS_PART_TIME | ✅ 通过 |
|
||||
| 6 | 保存参数配置 | POST | /ccdi/modelParam/save | ✅ 通过 |
|
||||
|
||||
---
|
||||
|
||||
## 测试统计
|
||||
|
||||
- **通过:** 9 个测试
|
||||
- **失败:** 0 个测试
|
||||
- **总计:** 9 个测试
|
||||
|
||||
---
|
||||
|
||||
## 结论
|
||||
|
||||
✅ **所有测试通过**
|
||||
|
||||
后端功能完全符合预期:
|
||||
1. 所有接口响应正常
|
||||
2. 数据库更新正确
|
||||
3. 审计字段自动填充
|
||||
4. 安全性验证通过(只更新 param_value 字段)
|
||||
5. 三种模型的参数查询均返回正确数据
|
||||
|
||||
---
|
||||
|
||||
## 附录: 测试命令汇总
|
||||
|
||||
```bash
|
||||
# 1. 获取Token
|
||||
curl -X POST "http://localhost:8080/login/test" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username":"admin","password":"admin123"}'
|
||||
|
||||
# 2. 查询模型列表
|
||||
curl -X GET "http://localhost:8080/ccdi/modelParam/modelList" \
|
||||
-H "Authorization: Bearer {token}"
|
||||
|
||||
# 3. 查询模型参数
|
||||
curl -X GET "http://localhost:8080/ccdi/modelParam/list?modelCode=LARGE_TRANSACTION" \
|
||||
-H "Authorization: Bearer {token}"
|
||||
|
||||
# 4. 保存参数配置
|
||||
curl -X POST "http://localhost:8080/ccdi/modelParam/save" \
|
||||
-H "Authorization: Bearer {token}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"projectId": 0,
|
||||
"modelCode": "LARGE_TRANSACTION",
|
||||
"modelName": "大额交易模型",
|
||||
"params": [
|
||||
{
|
||||
"paramCode": "SINGLE_TRANSACTION_AMOUNT",
|
||||
"paramName": "单笔交易额",
|
||||
"paramDesc": "单笔超过该金额视为大额交易",
|
||||
"paramValue": "60000",
|
||||
"paramUnit": "元",
|
||||
"sortOrder": 1
|
||||
}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**测试人员:** Claude Code Agent
|
||||
**报告生成时间:** 2026-02-26 02:05:00 (UTC)
|
||||
40
ruoyi-ui/src/api/ccdi/modelParam.js
Normal file
40
ruoyi-ui/src/api/ccdi/modelParam.js
Normal file
@@ -0,0 +1,40 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
/**
|
||||
* 查询模型列表
|
||||
* @param {Object} query - 查询参数
|
||||
* @param {Number} query.projectId - 项目ID
|
||||
*/
|
||||
export function listModels(query) {
|
||||
return request({
|
||||
url: '/ccdi/modelParam/modelList',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询模型参数列表
|
||||
* @param {Object} query - 查询参数
|
||||
* @param {Number} query.projectId - 项目ID
|
||||
* @param {String} query.modelCode - 模型编码
|
||||
*/
|
||||
export function listParams(query) {
|
||||
return request({
|
||||
url: '/ccdi/modelParam/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存模型参数
|
||||
* @param {Object} data - 保存数据
|
||||
*/
|
||||
export function saveParams(data) {
|
||||
return request({
|
||||
url: '/ccdi/modelParam/save',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
219
ruoyi-ui/src/views/ccdi/modelParam/index.vue
Normal file
219
ruoyi-ui/src/views/ccdi/modelParam/index.vue
Normal file
@@ -0,0 +1,219 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!-- 顶部标题 -->
|
||||
<div class="header">
|
||||
<span class="title">模型参数管理</span>
|
||||
<router-link :to="{ path: '/project/manage' }" class="link">
|
||||
<i class="el-icon-arrow-left"></i> 返回项目管理
|
||||
</router-link>
|
||||
</div>
|
||||
|
||||
<!-- 查询筛选区 -->
|
||||
<div class="filter-container">
|
||||
<el-form :inline="true" :model="queryParams" ref="queryForm">
|
||||
<el-form-item label="模型名称" prop="modelCode">
|
||||
<el-select v-model="queryParams.modelCode" placeholder="请选择模型">
|
||||
<el-option
|
||||
v-for="model in modelList"
|
||||
:key="model.modelCode"
|
||||
:label="model.modelName"
|
||||
:value="model.modelCode"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" @click="handleQuery">
|
||||
查询
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<!-- 参数配置表格 -->
|
||||
<div class="table-container">
|
||||
<h3 class="table-title">阈值参数配置</h3>
|
||||
<el-table :data="paramList" border style="width: 100%">
|
||||
<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-container">
|
||||
<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: "ModelParam",
|
||||
data() {
|
||||
return {
|
||||
// 模型列表
|
||||
modelList: [],
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
modelCode: undefined,
|
||||
projectId: 0, // 默认查询系统级参数
|
||||
},
|
||||
// 参数列表
|
||||
paramList: [],
|
||||
// 保存中状态
|
||||
saving: false,
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getModelList();
|
||||
},
|
||||
methods: {
|
||||
/** 查询模型列表 */
|
||||
getModelList() {
|
||||
listModels({ projectId: this.queryParams.projectId }).then((response) => {
|
||||
this.modelList = response.data;
|
||||
if (this.modelList.length > 0) {
|
||||
this.queryParams.modelCode = this.modelList[0].modelCode;
|
||||
this.handleQuery();
|
||||
}
|
||||
});
|
||||
},
|
||||
/** 查询参数列表 */
|
||||
handleQuery() {
|
||||
if (!this.queryParams.modelCode) {
|
||||
this.$message.warning("请选择模型");
|
||||
return;
|
||||
}
|
||||
listParams(this.queryParams).then((response) => {
|
||||
this.paramList = response.data;
|
||||
});
|
||||
},
|
||||
/** 标记参数为已修改 */
|
||||
markAsModified(row) {
|
||||
row.modified = true;
|
||||
},
|
||||
/** 保存配置 */
|
||||
handleSave() {
|
||||
if (!this.queryParams.modelCode) {
|
||||
this.$message.warning("请选择模型");
|
||||
return;
|
||||
}
|
||||
|
||||
// 只保存修改过的参数值
|
||||
const modifiedParams = this.paramList.filter((item) => item.modified);
|
||||
if (modifiedParams.length === 0) {
|
||||
this.$message.info("没有需要保存的修改");
|
||||
return;
|
||||
}
|
||||
|
||||
const saveDTO = {
|
||||
projectId: this.queryParams.projectId,
|
||||
modelCode: this.queryParams.modelCode,
|
||||
modelName: this.modelList.find(
|
||||
(m) => m.modelCode === this.queryParams.modelCode
|
||||
)?.modelName,
|
||||
params: modifiedParams.map((item) => ({
|
||||
paramCode: item.paramCode,
|
||||
paramName: item.paramName,
|
||||
paramDesc: item.paramDesc,
|
||||
paramValue: item.paramValue,
|
||||
paramUnit: item.paramUnit,
|
||||
sortOrder: item.sortOrder,
|
||||
})),
|
||||
};
|
||||
|
||||
this.saving = true;
|
||||
saveParams(saveDTO)
|
||||
.then((response) => {
|
||||
this.$modal.msgSuccess("保存成功");
|
||||
// 清除修改标记
|
||||
this.paramList.forEach((item) => {
|
||||
item.modified = false;
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
this.saving = false;
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.app-container {
|
||||
padding: 20px;
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
padding: 15px;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
|
||||
.title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.link {
|
||||
color: #1890ff;
|
||||
text-decoration: none;
|
||||
font-size: 14px;
|
||||
|
||||
i {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filter-container {
|
||||
padding: 15px;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
padding: 20px;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.table-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin: 0 0 15px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.button-container {
|
||||
padding: 15px;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user