Compare commits

...

4 Commits

Author SHA1 Message Date
李玉东
92f28962ef 1. 指标子集映射 2025-09-05 15:16:19 +08:00
李玉东
e0bc36ae0e 1. 指标子集映射 2025-08-21 17:25:25 +08:00
李玉东
e7974f15cb 1. 指标子集映射 2025-08-21 16:01:26 +08:00
李玉东
ba1a3bcae4 1. 指标子集映射 2025-08-21 16:00:41 +08:00
186 changed files with 9176 additions and 1673 deletions

View File

@ -1,14 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<component name="Encoding" defaultCharsetForPropertiesFiles="UTF-8">
<file url="file://$PROJECT_DIR$/manager-admin/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/manager-admin/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/manager-common/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/manager-common/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/manager-system/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/manager-system/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/manager-z-generation/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/manager-z-generation/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
</component>

View File

@ -0,0 +1,10 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="DuplicatedCode" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<Languages>
<language minSize="65" name="Java" />
</Languages>
</inspection_tool>
</profile>
</component>

View File

@ -25,6 +25,11 @@
<artifactId>system</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
@ -34,6 +39,15 @@
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>

View File

@ -1,206 +1,140 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>指标设置 Demo</title>
<!DOCTYPE html>
<meta charset="utf-8" />
<title>替换雷达为横向条形图 · 最小示例</title>
<style>
body { font-family: system-ui, -apple-system, "Segoe UI", Roboto, Arial; background:#f8fafc; }
.chart-card { width: 900px; margin: 40px auto; background: #f3f6fb; border-radius: 12px; padding: 24px; }
.chart-title { margin: 0 0 8px; text-align: center; color:#0f172a; }
.chart-container {
position: relative;
height: 300px; /* 你提供的高度 */
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
</style>
<!-- Tabler 样式(也可换成你项目内置的 tabler.min.css -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/core@1.0.0-beta20/dist/css/tabler.min.css"/>
<style>
:root{
/* 你的主题主色,可按项目实际调整 */
--accent: #206bc4;
}
/* 强调卡片 */
.card-priority{
border: 2px solid var(--accent);
box-shadow: 0 10px 30px rgba(32,107,196,.16);
background: linear-gradient(180deg, rgba(32,107,196,.04), rgba(32,107,196,.02));
position: sticky; /* 吸顶 */
top: 12px;
z-index: 3;
transition: box-shadow .18s ease, transform .18s ease;
}
.card-priority .card-header{
background: linear-gradient(90deg, rgba(32,107,196,.10), rgba(32,107,196,0));
border-bottom: 1px solid rgba(32,107,196,.2);
}
.priority-dot{
width: 10px; height: 10px; border-radius: 999px;
background: var(--accent);
box-shadow: 0 0 0 4px rgba(32,107,196,.15);
margin-right: .5rem;
}
.card-priority:hover,
.card-priority:focus-within{
box-shadow: 0 14px 40px rgba(32,107,196,.24);
transform: translateY(-1px);
}
/* 切换时的短暂高亮 */
@keyframes flashHighlight{
0% { box-shadow: 0 0 0 0 rgba(32,107,196,0); }
20% { box-shadow: 0 0 0 6px rgba(32,107,196,.25); }
100% { box-shadow: 0 10px 30px rgba(32,107,196,.16); }
}
.card-priority.flash{ animation: flashHighlight .9s ease-out; }
/* 次级卡片更弱一些,拉开层级 */
.card-secondary{
border: 1px solid rgba(0,0,0,.06);
box-shadow: 0 4px 14px rgba(0,0,0,.06);
}
</style>
</head>
<body class="theme-light">
<div class="page">
<header class="navbar navbar-expand-md navbar-light d-print-none" style="border-bottom:1px solid rgba(0,0,0,.06);">
<div class="container-xl">
<h2 class="navbar-brand">海上评估系统</h2>
<div class="ms-auto text-muted">管理员</div>
</div>
</header>
<div class="page-body">
<div class="container-xl">
<!-- 重点卡片:选择指标 -->
<div class="card card-priority mb-3" id="cardPriority">
<div class="card-header d-flex align-items-center">
<span class="priority-dot"></span>
<h3 class="card-title mb-0">选择指标
<span class="badge bg-primary ms-2">必填</span>
</h3>
<div class="ms-auto">
<button class="btn btn-primary" id="btnNext">下一步</button>
</div>
</div>
<div class="card-body">
<div class="row g-3 align-items-center">
<div class="col-auto">
<label for="metricSelect" class="col-form-label">指标列表</label>
</div>
<div class="col-12 col-sm-6 col-lg-4">
<select id="metricSelect" class="form-select">
<option value="" selected disabled>请选择一个指标</option>
<option value="aaa">AAA</option>
<option value="bbb">BBB</option>
<option value="ccc">CCC</option>
</select>
</div>
<div class="col-12 col-lg-auto">
<span class="form-hint">选择后将影响下方所有设置。</span>
</div>
</div>
</div>
</div>
<!-- 次级卡片:评价集设置 -->
<div class="card card-secondary">
<div class="card-header">
<h3 class="card-title mb-0">评价集设置</h3>
<div class="ms-auto">
<button class="btn btn-outline-primary" id="btnAdd">增加评价</button>
</div>
</div>
<div class="card-body">
<div class="row g-3">
<!-- 左侧:子集列表 -->
<div class="col-12 col-md-3">
<div class="list-group">
<label class="list-group-item">
<input class="form-check-input me-2" type="radio" name="subset" value="x1"> x1
</label>
<label class="list-group-item">
<input class="form-check-input me-2" type="radio" name="subset" value="c2" checked> c2
</label>
</div>
</div>
<!-- 右侧:表格占位 -->
<div class="col-12 col-md-9">
<div class="table-responsive">
<table class="table table-vcenter">
<thead>
<tr>
<th>序号</th>
<th>名称</th>
<th>符号</th>
<th>下限值</th>
<th>符号</th>
<th>上限值</th>
</tr>
</thead>
<tbody id="rulesBody">
<tr class="text-muted">
<td colspan="6">尚未添加评价规则,点击右上角“增加评价”。</td>
</tr>
</tbody>
</table>
</div>
</div>
</div> <!-- row -->
</div>
</div>
<!-- 占位内容,方便滚动测试吸顶 -->
<div style="height: 40vh;"></div>
</div>
<div class="chart-card">
<h3 class="chart-title">一级维度得分对比</h3>
<div class="chart-container">
<svg id="radarChart"></svg>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/d3@7"></script>
<script>
// 切换指标时,给重点卡片一个轻微高亮动画
const card = document.getElementById('cardPriority');
const select = document.getElementById('metricSelect');
select.addEventListener('change', () => {
card.classList.add('flash');
setTimeout(() => card.classList.remove('flash'), 900);
});
/**
* 横向条形图(替换 #radarChart
* @param {string|SVGElement} svgSel - 目标 SVG 选择器/元素
* @param {{name:string,score:number}[]} data - 数据score 建议 0~100
* @param {{domainMax?:number, sortAsc?:boolean, target?:number, margin?:object}} opts
*/
function renderBarChart(svgSel, data, opts = {}) {
const cfg = Object.assign({
sortAsc: true,
domainMax: 100, // 轴与条统一最大值,确保与刻度对齐
target: 80,
margin: { top: 24, right: 120, bottom: 28, left: 200 }
}, opts);
// 下一步按钮:如果未选择则聚焦并闪烁
document.getElementById('btnNext').addEventListener('click', () => {
if (!select.value) {
select.focus();
card.classList.add('flash');
setTimeout(() => card.classList.remove('flash'), 900);
} else {
// 这里写你的跳转或展开逻辑
window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' });
}
});
const src = (data||[]).map(d=>({...d}));
if (cfg.sortAsc) src.sort((a,b)=>d3.ascending(a.score,b.score));
// 增加评价:简单追加一行示例
document.getElementById('btnAdd').addEventListener('click', () => {
const body = document.getElementById('rulesBody');
if (body.firstElementChild && body.firstElementChild.classList.contains('text-muted')) {
body.innerHTML = '';
const svg = d3.select(svgSel);
const parent = svg.node().parentNode;
const W = parent.clientWidth || 800;
const H = parent.clientHeight || Math.max(240, src.length*40 + cfg.margin.top + cfg.margin.bottom);
svg.attr("width", W).attr("height", H)
.attr("viewBox", `0 0 ${W} ${H}`)
.attr("preserveAspectRatio","xMidYMid meet")
.selectAll("*").remove();
const {top,right,bottom,left} = cfg.margin;
const width = Math.max(50, W - left - right);
const innerH = Math.max(20, H - top - bottom);
const gap = 10;
const barH = Math.max(16, Math.min(32, (innerH - (src.length-1)*gap) / Math.max(1,src.length)));
const g = svg.append("g").attr("transform", `translate(${left},${top})`);
// ★ 唯一的 x 比例尺(条/轴/目标线共用)
const x = d3.scaleLinear()
.domain([0, cfg.domainMax])
.nice()
.range([0, width]);
const y = d3.scaleBand()
.domain(src.map(d=>d.name))
.range([0, src.length*(barH+gap)-gap])
.paddingInner(gap/(barH+gap));
const color = d3.scaleLinear()
.domain([0.5*cfg.domainMax, 0.75*cfg.domainMax, 0.9*cfg.domainMax])
.range (["#ef4444", "#f59e0b", "#10b981"])
.clamp(true);
// X 轴
g.append("g")
.attr("transform", `translate(0,${y.range()[1]})`)
.call(d3.axisBottom(x).ticks(6).tickSizeOuter(0))
.selectAll("path,line").attr("stroke", "#e5e7eb");
// 行
const row = g.selectAll(".row").data(src).enter().append("g")
.attr("class","row")
.attr("transform", d=>`translate(0,${y(d.name)})`);
// 条形
row.append("rect")
.attr("height", barH)
.attr("width", d=>x(d.score)) // 与轴同一比例尺
.attr("fill", d=>color(d.score))
.attr("rx", 8).attr("ry", 8);
// 左侧分类名
row.append("text")
.attr("x", -10).attr("y", barH/2).attr("dy",".35em")
.attr("text-anchor","end").attr("fill","#0f172a").attr("font-weight",700)
.text(d=>d.name);
// 右侧分数
row.append("text")
.attr("x", d=>x(d.score)+8).attr("y", barH/2).attr("dy",".35em")
.attr("fill","#0f172a").attr("font-weight",800).attr("font-size",18)
.text(d=>d.score.toFixed(1));
// 最低分标注
const minScore = d3.min(src, d=>d.score);
row.filter(d=>d.score===minScore).append("text")
.attr("x", d=>x(d.score)+8).attr("y", barH/2+16)
.attr("fill","#ef4444").attr("font-weight",800).attr("font-size",12)
.text("LOWEST");
// 目标线(可选)
if (typeof cfg.target === "number") {
g.append("line")
.attr("x1", x(cfg.target)).attr("x2", x(cfg.target))
.attr("y1", 0).attr("y2", y.range()[1])
.attr("stroke", "#94a3b8").attr("stroke-dasharray", "4,4");
g.append("text")
.attr("x", x(cfg.target)+6).attr("y", -6)
.attr("fill","#64748b").attr("font-size",12)
.text(`目标 ${cfg.target}`);
}
const idx = body.children.length + 1;
const tr = document.createElement('tr');
tr.innerHTML = `
<td>${idx}</td>
<td><input class="form-control form-control-sm" placeholder="名称"/></td>
<td>
<select class="form-select form-select-sm">
<option>&gt;=</option><option>&gt;</option><option>=</option>
<option>&lt;=</option><option>&lt;</option>
</select>
</td>
<td><input class="form-control form-control-sm" placeholder="下限"/></td>
<td>
<select class="form-select form-select-sm">
<option>&lt;=</option><option>&lt;</option><option>=</option>
<option>&gt;=</option><option>&gt;</option>
</select>
</td>
<td><input class="form-control form-control-sm" placeholder="上限"/></td>
`;
body.appendChild(tr);
});
}
// ====== 最小示例数据(可替换为你的实时数据)======
const level1 = [
{ name:"港口安全管理", score: 90 },
{ name:"港口环境与资源利用", score: 90 },
{ name:"港口运行效率", score:90 }
];
// 渲染 & 自适应
renderBarChart('#radarChart', level1, { domainMax: 100, target: 80 });
window.addEventListener('resize', () =>
renderBarChart('#radarChart', level1, { domainMax: 100, target: 80 })
);
</script>
</body>
</html>

View File

@ -0,0 +1,52 @@
package com.hshh;
import com.hshh.system.base.service.LogsService;
import com.hshh.system.common.threadpool.ThreadPools;
import com.hshh.system.logs.LogRecorderThread;
import com.hshh.thread.HandleEvaluationCmdThread;
import java.util.concurrent.ExecutorService;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* 系统初始化类.
*
* @author LiDongYU
* @since 2025/7/22
*/
@Component
@Slf4j
public class AdminInit {
@Resource
private LogsService logsService;
private static final ExecutorService cpu = ThreadPools.newCpuBoundPool("cpu-pool");
private static final int evaluationThreadNum = 5;
/**
* 初始化系统相关函数.
*/
@PostConstruct
public void init() {
//评估线程
startEvaluationThread();
//日志记录线程
startRecordLogs();
}
private void startEvaluationThread() {
try {
for (int i = 0; i < evaluationThreadNum; i++) {
cpu.execute(new Thread(new HandleEvaluationCmdThread()));
}
} catch (Exception e) {
log.error("error::", e);
}
}
private void startRecordLogs() {
new Thread(new LogRecorderThread(logsService)).start();
}
}

View File

@ -24,4 +24,5 @@ public class Application {
SpringApplication.run(Application.class, args);
}
}

View File

@ -7,6 +7,7 @@ import com.hshh.model.entity.ModelDefine;
import com.hshh.model.service.FormFieldConfigService;
import com.hshh.model.service.FormValueService;
import com.hshh.model.service.ModelDefineService;
import com.hshh.system.annotation.LogOperation;
import com.hshh.system.common.bean.BaseController;
import com.hshh.system.common.bean.ErrorField;
import com.hshh.system.common.bean.OperateResult;
@ -70,6 +71,7 @@ public class DataController extends BaseController {
* @param model session容器
* @return /data/list.html
*/
@LogOperation("进入数据管理")
@GetMapping("/list")
@Operation(summary = "导航到数据管理页面", description = "导航到数据管理页面")
public String index(PaginationBean request, Model model) {
@ -81,12 +83,12 @@ public class DataController extends BaseController {
model.addAttribute("modelDefineList", modelDefineList); //设置到session中
if (!modelDefineList.isEmpty()) {
if (!modelDefineList.isEmpty() && request.getBusinessKey() != null) {
List<FormFieldConfig> formConfigList = formFieldConfigService.getFormFieldConfigByModelId(
modelDefineList.get(0).getId()); //查询模型表头
request.getBusinessKey()); //查询模型表头
sortFieldList(formConfigList); //排序
Map<String, String> headerMap = new LinkedHashMap<>(); //定义表头兑现
Map<String, String> headerMap = new LinkedHashMap<>(); //定义表头
formConfigList.forEach(formFieldConfig -> {
headerMap.put(formFieldConfig.getFieldName(), formFieldConfig.getFieldLabel());
});
@ -114,10 +116,10 @@ public class DataController extends BaseController {
*/
private void setCurrentActiveModelDefine(Model model, PaginationBean request,
List<ModelDefine> modelDefineList) {
AtomicReference<ModelDefine> activeModelDefine = new AtomicReference<>(); //当前激活的数据模型tab页
AtomicReference<ModelDefine> activeModelDefine = new AtomicReference<>();
modelDefineList.forEach(a -> {
if (request.getId() != null && request.getId().equals(a.getId())) {
if (request.getBusinessKey() != null && request.getBusinessKey().equals(a.getId())) {
a.setChecked(true);
activeModelDefine.set(a);
@ -129,12 +131,6 @@ public class DataController extends BaseController {
});
if (activeModelDefine.get() != null) {
model.addAttribute("currentModelDefine", activeModelDefine.get());
} else {
//设置默认第一个选中
modelDefineList.get(0).setChecked(true);
//设置查询参数为第一个
request.setId(modelDefineList.get(0).getId());
model.addAttribute("currentModelDefine", modelDefineList.get(0));
}
}
@ -154,6 +150,7 @@ public class DataController extends BaseController {
* @param id 模型id
* @return form表单内容 div...
*/
@LogOperation("获取form输入窗体")
@GetMapping("/getForm/{id}")
@Operation(summary = "获取form输入窗体", description = "根据模型ID获取form窗体")
@ResponseBody
@ -168,6 +165,7 @@ public class DataController extends BaseController {
* @param formValue 表单值
* @return 操作结果
*/
@LogOperation("保存对应模型ID对应的form表单记录")
@PostMapping("/form/save")
@ResponseBody
@Operation(summary = "保存记录", description = "保存对应模型ID对应的form表单记录")
@ -191,6 +189,7 @@ public class DataController extends BaseController {
* @param id 记录ID
* @return 操作结果
*/
@LogOperation("根据ID删除记录")
@ResponseBody
@GetMapping("/remove/{id}")
@Operation(summary = "删除记录", description = "根据ID删除记录")

View File

@ -1,14 +0,0 @@
package com.hshh.data.controller;
/**
* [类的简要说明]
* <p>
* [详细描述可选]
* <p>
*
* @author LiDongYU
* @since 2025/7/22
*/
public class TestController {
}

View File

@ -0,0 +1,19 @@
package com.hshh.evaluation.bean;
import lombok.Data;
/**
* [类的简要说明]
* <p>
* [详细描述可选]
* <p>
*
* @author LiDongYU
* @since 2025/7/22
*/
@Data
public class BarData {
private String name;
private double score;
}

View File

@ -0,0 +1,19 @@
package com.hshh.evaluation.bean;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.web.multipart.MultipartFile;
/**
* csv上传文件描述类.
*
* @author LiDongYU
* @since 2025/7/22
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class CsvUploadBean extends PageEvaluationRequest {
private MultipartFile file;
}

View File

@ -1,39 +0,0 @@
package com.hshh.evaluation.bean;
import java.util.List;
import java.util.Map;
import lombok.Data;
/**
* 模板权重暂存数据.
*
* @author LiDongYU
* @since 2025/7/22
*/
@Data
public class DraftWeightData {
/**
* 页面定义的临时key.
*/
private String key;
/**
* 父指标ID.
*/
private Integer parentIndicationId;
/**
* 模板ID.
*/
private Integer templateId;
/**
* 表头map linkedMap. key是指标id,value是 {id,name}
*/
private Map<Integer, MetricTableHeaderBean> headerMap;
/**
* 页面中指标表的权重信息设置.key 为fromIndicatorId+"_"+toIndicatorId.
*/
private List<List<MetricMapperWeightBean>> weight;
}

View File

@ -0,0 +1,28 @@
package com.hshh.evaluation.bean;
import com.hshh.indicator.entity.IndicatorEvalItem;
import java.util.List;
import lombok.Data;
/**
* 描述评估字段对应的信息包含权重和评价集.
*
* @author LiDongYU
* @since 2025/7/22
*/
@Data
public class EvalItemToFieldInfo {
/**
* 字段名称.
*/
private String name;
/**
* 权重.
*/
private double weight;
/**
* 评价集列表.
*/
private List<IndicatorEvalItem> evalItemList;
}

View File

@ -0,0 +1,20 @@
package com.hshh.evaluation.bean;
import java.io.Serializable;
import lombok.Data;
/**
* 描述请求数据到类型类.
*
* @author LiDongYU
* @since 2025/7/22
*/
@Data
public class PageDatasourceRequest implements Serializable {
private static final long serialVersionUID = 1L;
//数据源类型 数据库||Csv文件
private String datasourceType;
//工程ID
private Integer projectId;
}

View File

@ -0,0 +1,51 @@
package com.hshh.evaluation.bean;
import lombok.Data;
/**
* 描述评估请求对象.
*
* @author LiDongYU
* @since 2025/7/22
*/
@Data
public class PageEvaluationRequest {
/**
* database | csv.
*/
private String datasourceType;
/**
* 基础设施ID.
*/
private Integer modelId;
/**
* 工程ID.
*/
private Integer projectId;
/**
* 查询关键子.
*/
private String search;
/**
* 模板ID.
*/
private Integer templateId;
/**
* 随机字符.
*/
private String randomKey;
/**
* 计算方式.
*/
private String method;
private Integer indicatorTopId;
/**
* 用户ID.
*/
private Integer userId;
private String membership;
private int[] ids;
//csv数据标识字段
private String identifier;
}

View File

@ -0,0 +1,22 @@
package com.hshh.evaluation.bean;
import lombok.Data;
/**
* 页面指标权重.
*
* @author LiDongYU
* @since 2025/7/22
*/
@Data
public class PageIndicatorWeight {
/**
* 指标ID.
*/
private int indicatorId;
/**
* 权重.
*/
private String weight;
}

View File

@ -10,9 +10,9 @@ import lombok.Data;
* @since 2025/7/22
*/
@Data
public class MetricComputeRequest {
public class PageMetricComputeRequest {
private List<String> metric;
private List<List<MetricMapperWeightBean>> weightList;
private List<List<PageMetricMapperWeightBean>> weightList;
}

View File

@ -9,7 +9,7 @@ import lombok.Data;
* @since 2025/7/22
*/
@Data
public class MetricComputerResponse {
public class PageMetricComputerResponse {
private String id;
private String weight;

View File

@ -9,7 +9,7 @@ import lombok.Data;
* @since 2025/7/22
*/
@Data
public class MetricMapperWeightBean {
public class PageMetricMapperWeightBean {
/**
* 从那个指标开始.
@ -31,4 +31,5 @@ public class MetricMapperWeightBean {
* 行号.
*/
private int rowNum;
}

View File

@ -9,7 +9,7 @@ import lombok.Data;
* @since 2025/7/22
*/
@Data
public class MetricTableHeaderBean {
public class PageMetricTableHeaderBean {
private int id;
private String name;

View File

@ -0,0 +1,22 @@
package com.hshh.evaluation.bean;
import java.io.Serializable;
import lombok.Data;
/**
* [类的简要说明]
* <p>
* [详细描述可选]
* <p>
*
* @author LiDongYU
* @since 2025/7/22
*/
@Data
public class PieData implements Serializable {
private static final long serialVersionUID = 1L;
private String label;
private double value;
private String color;
}

View File

@ -0,0 +1,28 @@
package com.hshh.evaluation.bean;
import com.hshh.evaluation.entity.EvaluationRootResult;
import com.hshh.indicator.entity.IndicatorTopLevel;
import java.io.Serializable;
import java.util.List;
import lombok.Data;
/**
* 报告模板类.
*
* @author LiDongYU
* @since 2025/7/22
*/
@Data
public class ReportBean implements Serializable {
private EvaluationRootResult result;
private List<IndicatorTopLevel> levelList;
private String highRate;
private Integer bottomNum;
private List<PieData> pieDataList;
private List<ReportIndicatorNodeData> dataNodeList;
private List<BarData> barDataList;
}

View File

@ -0,0 +1,28 @@
package com.hshh.evaluation.bean;
import java.io.Serializable;
import java.util.Map;
import lombok.Data;
/**
* [类的简要说明]
* <p>
* [详细描述可选]
* <p>
*
* @author LiDongYU
* @since 2025/7/22
*/
@Data
public class ReportIndicatorNodeData implements Serializable {
private static final long serialVersionUID = 1L;
private String id;
private String name;
private Integer level;
private double score;
private Map<String, Double> membershipDist;
private double weight;
private String parent;
private String evaluation;
}

View File

@ -0,0 +1,19 @@
package com.hshh.evaluation.bean;
import lombok.Data;
/**
* 评估数据描述.原始数据id,实际数据datatype=csv or database.
*
* @author LiDongYU
* @since 2025/7/22
*/
@Data
public class SingleEvaluationData {
private int id;
private String data;
private String type;
}

View File

@ -11,14 +11,14 @@ import lombok.Data;
* @since 2025/7/22
*/
@Data
public class DynamicTable {
public class pageDynamicTable {
/**
* 表头map linkedMap.
*/
private Map<Integer, MetricTableHeaderBean> headerMap;
private Map<Integer, PageMetricTableHeaderBean> headerMap;
private List<List<MetricMapperWeightBean>> weight;
private List<List<PageMetricMapperWeightBean>> weight;
}

View File

@ -0,0 +1,145 @@
package com.hshh.evaluation.controller;
import com.hshh.evaluation.bean.BarData;
import com.hshh.evaluation.bean.PieData;
import com.hshh.evaluation.bean.ReportIndicatorNodeData;
import com.hshh.evaluation.entity.EvaluationIndicatorResult;
import com.hshh.evaluation.entity.EvaluationRootResult;
import com.hshh.indicator.entity.IndicatorTopLevel;
import com.hshh.system.common.bean.BaseController;
import com.hshh.system.common.enums.LevelEnum;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
/**
* [类的简要说明]
* <p>
* [详细描述可选]
* <p>
*
* @author LiDongYU
* @since 2025/7/22
*/
public class AssistantEvaluationProjectController extends BaseController {
protected Map<String, Double> membershipScoreMap(String input) {
Map<String, Double> map = new LinkedHashMap<>(); // 保证顺序和输入一致
for (String pair : input.split(";")) {
if (pair.trim().isEmpty()) {
continue;
}
String[] kv = pair.split(":");
if (kv.length == 2) {
String key = kv[0].trim();
double value = Double.parseDouble(kv[1].trim());
map.put(key, value);
}
}
return map;
}
protected String getClosestLevel(List<IndicatorTopLevel> levelList, int targetScore) {
IndicatorTopLevel closest = null;
int minDiff = Integer.MAX_VALUE;
for (IndicatorTopLevel item : levelList) {
int diff = Math.abs(targetScore - (int) Double.parseDouble(item.getGrade()));
if (diff < minDiff) {
minDiff = diff;
closest = item;
}
}
return closest != null ? closest.getLevelName() : null;
}
//获取优良率
protected String highRate(Map<String, Double> membershipMap) {
AtomicReference<Double> highRate = new AtomicReference<>((double) 0);
membershipMap.forEach((k, v) -> {
if (k.equals(LevelEnum.Excellent.getLevel())) {
highRate.set(highRate.get() + v);
}
if (k.equals(LevelEnum.Good.getLevel())) {
highRate.set(highRate.get() + v);
}
});
return String.format("%.2f", highRate.get() * 100);
}
protected void setLevelName(List<IndicatorTopLevel> levelList, EvaluationRootResult rootResult) {
if (levelList.isEmpty()) {
return;
}
rootResult.setLevelName(levelList.get(levelList.size() - 1).getLevelName());
for (IndicatorTopLevel indicatorTopLevel : levelList) {
if (Double.parseDouble(rootResult.getFinalScore()) >= Double.parseDouble(
indicatorTopLevel.getGrade())) {
rootResult.setLevelName(indicatorTopLevel.getLevelName());
break;
}
}
}
protected List<PieData> pieDataList(Map<String, Double> membershipMap) {
List<PieData> list = new ArrayList<>();
membershipMap.forEach((k, v) -> {
PieData pieData = new PieData();
pieData.setLabel(k);
pieData.setValue(v * 100);
pieData.setColor(LevelEnum.getColor(k));
list.add(pieData);
});
return list;
}
protected List<ReportIndicatorNodeData> covertIndicatorResultToReportIndicatorNodeData(
List<EvaluationIndicatorResult> list, List<IndicatorTopLevel> levelList) {
List<ReportIndicatorNodeData> resultList = new ArrayList<>();
list.forEach(indicatorResult -> {
ReportIndicatorNodeData reportIndicatorNodeData = new ReportIndicatorNodeData();
resultList.add(reportIndicatorNodeData);
reportIndicatorNodeData.setId("node_" + indicatorResult.getIndicatorId());
reportIndicatorNodeData.setName(indicatorResult.getIndicatorName());
reportIndicatorNodeData.setLevel(indicatorResult.getLevel());
reportIndicatorNodeData.setScore(
new BigDecimal(indicatorResult.getFinalScore()).setScale(2, RoundingMode.HALF_UP)
.doubleValue());
Map<String, Double> membershipScoreMap = new HashMap<>();
Map<String, Double> originalMap = membershipScoreMap(indicatorResult.getMemberShipScore());
originalMap.forEach((k, v) -> {
membershipScoreMap.put(LevelEnum.getCode(k),
BigDecimal.valueOf(v * 100).setScale(2, RoundingMode.HALF_UP).doubleValue());
});
reportIndicatorNodeData.setMembershipDist(membershipScoreMap);
reportIndicatorNodeData.setEvaluation(
getClosestLevel(levelList, (int) reportIndicatorNodeData.getScore()));
reportIndicatorNodeData.setWeight(indicatorResult.getWeight());
if (indicatorResult.getIndicatorParentId() != null) {
reportIndicatorNodeData.setParent("node_" + indicatorResult.getIndicatorParentId());
}
});
return resultList;
}
protected List<BarData> covertIndicatorResultToBarData(List<EvaluationIndicatorResult> list) {
List<BarData> resultList = new ArrayList<>();
list.forEach(indicatorResult -> {
BarData barData = new BarData();
barData.setName(indicatorResult.getIndicatorName());
barData.setScore(
new BigDecimal(indicatorResult.getFinalScore()).setScale(2, RoundingMode.HALF_UP)
.doubleValue());
resultList.add(barData);
});
return resultList;
}
}

View File

@ -1,16 +1,19 @@
package com.hshh.evaluation.controller;
import com.hshh.evaluation.bean.DraftWeightData;
import com.hshh.evaluation.bean.MetricMapperWeightBean;
import com.hshh.evaluation.entity.EvaluationTemplate;
import com.hshh.evaluation.bean.PageMetricMapperWeightBean;
import com.hshh.evaluation.bean.PageMetricTableHeaderBean;
import com.hshh.evaluation.bean.pageDynamicTable;
import com.hshh.evaluation.entity.EvaluationTemplateWeight;
import com.hshh.indicator.entity.Indicator;
import com.hshh.indicator.service.IndicatorService;
import com.hshh.indicator.service.impl.IndicatorServiceImpl;
import com.hshh.system.common.bean.BaseController;
import com.hshh.system.common.util.DraftStore;
import com.hshh.system.common.bean.SpringContextHolder;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 评估模板类辅助方法.
@ -27,7 +30,7 @@ public class AssistantTemplateController extends BaseController {
* @return list string数组
*/
protected List<String[]> convertMetricMapperWeightBeanListToStrArray(
List<List<MetricMapperWeightBean>> weightList) {
List<List<PageMetricMapperWeightBean>> weightList) {
List<String[]> data = new ArrayList<>();
weightList.forEach(weight -> {
@ -41,84 +44,108 @@ public class AssistantTemplateController extends BaseController {
}
/**
* 组装历史权重数据.
* 把数据库中的明细数据转化为页面约定的数据类型.
*
* @param evaluationTemplate 当前模板数据
* @return 评估数据列表
* @param detailList 数据库数据
* @return 页面约定的权重列表
*/
@SuppressWarnings("unchecked")
protected Map<Integer, DraftWeightData> unitWeightMap(
EvaluationTemplate evaluationTemplate) {
protected List<List<PageMetricMapperWeightBean>> convertEvaluationTemplateWeightList(
List<EvaluationTemplateWeight> detailList) {
List<List<PageMetricMapperWeightBean>> finalList = new ArrayList<>();
Map<Integer, List<EvaluationTemplateWeight>> groupByFromIndicatorMap = detailList.stream()
.collect(Collectors.groupingBy(EvaluationTemplateWeight::getFromIndicatorId));
// 获取缓存中的数据
Map<Integer, DraftWeightData> cacheData = DraftStore.get(evaluationTemplate.getDraftKey(),
Map.class);
if (cacheData == null) {
if (evaluationTemplate.getCurrentPagePartData() != null) {
groupByFromIndicatorMap.forEach((key, value) -> {
List<PageMetricMapperWeightBean> list = new ArrayList<>();
finalList.add(list);
value.forEach(detail -> {
PageMetricMapperWeightBean weightBean = new PageMetricMapperWeightBean();
weightBean.setRowNum(detail.getRowNum());
weightBean.setValue(detail.getWeight());
weightBean.setRowId(detail.getFromIndicatorId());
weightBean.setColId(detail.getToIndicatorId());
weightBean.setParentId(detail.getIndicatorParentId());
list.add(weightBean);
});
});
Map<Integer, DraftWeightData> defaultMap = new LinkedHashMap<>();
DraftWeightData weightData = createDefaultDraftWeightData(evaluationTemplate);
defaultMap.put(weightData.getParentIndicationId(), weightData);
return defaultMap;
}
}
//cacheData数据结构 父ID-->当前id下子指标的权重信息
//查看当前页面上传的最后一次指标权重信息;用当前页面的数据更新缓存或者增加缓存
if (evaluationTemplate.getCurrentPagePartData() != null) {
int parentId = evaluationTemplate.getCurrentPagePartData().get(0).get(0).getParentId();
cacheData.remove(parentId);
//构建一个简易的只包含实际当前表格中的数据的暂存对象类供后面合并
DraftWeightData draftWeightData = createDefaultDraftWeightData(evaluationTemplate);
cacheData.put(parentId, draftWeightData);
}
return cacheData;
return finalList;
}
private DraftWeightData createDefaultDraftWeightData(EvaluationTemplate evaluationTemplate) {
int pid = evaluationTemplate.getCurrentPagePartData().get(0).get(0).getParentId();
DraftWeightData draftWeightData = new DraftWeightData();
draftWeightData.setTemplateId(evaluationTemplate.getId());
draftWeightData.setWeight(evaluationTemplate.getCurrentPagePartData());
draftWeightData.setParentIndicationId(pid);
draftWeightData.setKey(evaluationTemplate.getDraftKey());
return draftWeightData;
}
/**
* 当一个指标下面只有一个孩子时填充默认权重1.
* 创建表格头.
*
* @param lonelyChild 只有自己没有其他节点和他同级
* @param unitWeightDataMap 当前已经设置的权重信息.
* @param children 子指标
* @return 表格头信息
*/
protected void paddingDefaultValueWhenParentHasOnlyLonelyChild(List<Indicator> lonelyChild,
Map<Integer, DraftWeightData> unitWeightDataMap) {
for (Indicator indicator : lonelyChild) {
if (!unitWeightDataMap.containsKey(indicator.getParentId())) {
List<List<MetricMapperWeightBean>> singleRowList = new ArrayList<>();
List<MetricMapperWeightBean> list = new ArrayList<>();
MetricMapperWeightBean weightBean = new MetricMapperWeightBean();
list.add(weightBean);
singleRowList.add(list);
weightBean.setParentId(indicator.getParentId());
weightBean.setValue("1");
weightBean.setRowId(indicator.getId());
weightBean.setColId(indicator.getId());
weightBean.setRowNum(1);
DraftWeightData draftWeightData = new DraftWeightData();
draftWeightData.setParentIndicationId(indicator.getParentId());
draftWeightData.setWeight(singleRowList);
;
unitWeightDataMap.put(indicator.getParentId(), draftWeightData);
}
}
protected Map<Integer, PageMetricTableHeaderBean> createTableHeaderMap(List<Indicator> children) {
List<PageMetricTableHeaderBean> headerBeans = new ArrayList<>();
children.forEach(a -> {
PageMetricTableHeaderBean bean = new PageMetricTableHeaderBean();
bean.setId(a.getId());
bean.setName(a.getName());
headerBeans.add(bean);
});
Map<Integer, PageMetricTableHeaderBean> headerMap = new LinkedHashMap<>();
headerBeans.forEach(header -> {
headerMap.put(header.getId(), header);
});
return headerMap;
}
/**
* 从数据库创建一个空的默认映射列表.
*
* @param parentIndicatorId 父指标
* @param children 子指标
* @param i 行号
* @return 默认映射列表
*/
protected List<PageMetricMapperWeightBean> createEmptyMapperList(Integer parentIndicatorId,
List<Indicator> children, int i) {
List<PageMetricMapperWeightBean> innerList = new ArrayList<>();
for (Indicator child : children) {
PageMetricMapperWeightBean weightBean = new PageMetricMapperWeightBean();
//行ID
weightBean.setRowId(children.get(i).getId());
//列Id
weightBean.setColId(child.getId());
//初始默认为1
weightBean.setValue("1")
;
weightBean.setRowNum(i + 1);
weightBean.setParentId(parentIndicatorId);
innerList.add(weightBean);
}
return innerList;
}
/**
* 从数据库创建动态表.
*
* @param indicatePid 父节点指标
* @return 动态表
*/
protected pageDynamicTable createNewDynamicFromDatabase(Integer indicatePid) {
pageDynamicTable dynamicTable = new pageDynamicTable();
//查询当前指标的子指标
IndicatorService indicatorService = SpringContextHolder.getBean(IndicatorServiceImpl.class);
List<Indicator> children = indicatorService.queryChildren(indicatePid);
//设置头信息
dynamicTable.setHeaderMap(createTableHeaderMap(children));
//设置权重信息
List<List<PageMetricMapperWeightBean>> weight = new ArrayList<>();
for (int i = 0; i < children.size(); i++) {
List<PageMetricMapperWeightBean> innerList = createEmptyMapperList(indicatePid, children, i
);
weight.add(innerList);
}
dynamicTable.setWeight(weight);
return dynamicTable;
}
}

View File

@ -1,30 +1,58 @@
package com.hshh.evaluation.controller;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.hshh.evaluation.bean.CsvUploadBean;
import com.hshh.evaluation.bean.PageEvaluationRequest;
import com.hshh.evaluation.bean.ReportBean;
import com.hshh.evaluation.entity.EvaluationCsvData;
import com.hshh.evaluation.entity.EvaluationHistory;
import com.hshh.evaluation.entity.EvaluationIndicatorResult;
import com.hshh.evaluation.entity.EvaluationProject;
import com.hshh.evaluation.entity.EvaluationRootResult;
import com.hshh.evaluation.entity.EvaluationTemplate;
import com.hshh.evaluation.service.EvaluationCsvDataService;
import com.hshh.evaluation.service.EvaluationHistoryService;
import com.hshh.evaluation.service.EvaluationIndicatorResultService;
import com.hshh.evaluation.service.EvaluationProjectService;
import com.hshh.evaluation.service.EvaluationRootResultService;
import com.hshh.evaluation.service.EvaluationTemplateService;
import com.hshh.indicator.entity.Indicator;
import com.hshh.indicator.entity.IndicatorTopLevel;
import com.hshh.indicator.service.IndicatorService;
import com.hshh.indicator.service.IndicatorTopLevelService;
import com.hshh.model.entity.FormValue;
import com.hshh.model.service.FormFieldConfigService;
import com.hshh.model.service.FormValueService;
import com.hshh.system.annotation.LogOperation;
import com.hshh.system.base.entity.TableRelations;
import com.hshh.system.base.service.TableRelationsService;
import com.hshh.system.common.bean.BaseController;
import com.hshh.system.common.bean.OperateResult;
import com.hshh.system.common.bean.PaginationBean;
import com.hshh.system.common.enums.ErrorCode;
import com.hshh.system.common.enums.ErrorMessage;
import io.swagger.v3.oas.annotations.Operation;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
/**
* 评估工程.
@ -34,21 +62,62 @@ import org.springframework.web.bind.annotation.ResponseBody;
*/
@Controller
@RequestMapping("/evaluation/project")
public class EvaluationProjectController extends BaseController {
public class EvaluationProjectController extends AssistantEvaluationProjectController {
@Resource
private EvaluationProjectService evaluationProjectService;
/**
* 模板服务类.
*/
@Resource
private EvaluationTemplateService evaluationTemplateService;
/**
* 数据库引用关系记录服务类.
*/
@Resource
private TableRelationsService tableRelationsService;
/**
* 基础设施服务类.
*/
@Resource
private FormFieldConfigService formFieldConfigService;
/**
* 数据查询服务类.
*/
@Resource
private FormValueService formValueService;
/**
* 指标服务类.
*/
@Resource
private IndicatorService indicatorService;
/**
* 上传的csv文件数据记录服务类.
*/
@Resource
private EvaluationCsvDataService evaluationCsvDataService;
/**
* 评估历史服务类.
*/
@Resource
private EvaluationHistoryService evaluationHistoryService;
/**
* 根指标评估历史记录服务类.
*/
@Resource
private EvaluationRootResultService rootResultService;
@Resource
private IndicatorTopLevelService topLevelService;
@Resource
private EvaluationIndicatorResultService evaluationIndicatorResultService;
/**
* 默认页.
*
@ -87,6 +156,7 @@ public class EvaluationProjectController extends BaseController {
*/
@ResponseBody
@PostMapping("/save")
@LogOperation("保存评估工程")
public OperateResult<Object> save(@Valid @RequestBody EvaluationProject project,
BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
@ -119,9 +189,9 @@ public class EvaluationProjectController extends BaseController {
*/
@ResponseBody
@GetMapping("/remove/{id}")
@LogOperation("删除工程")
public OperateResult<Void> remove(@PathVariable("id") Integer id) {
List<TableRelations> reList = tableRelationsService.queryRel(id,
"m_data_evaluation_project");
List<TableRelations> reList = tableRelationsService.queryRel(id, "m_data_evaluation_project");
if (!reList.isEmpty()) {
return OperateResult.error(null, ErrorMessage.OBJ_ALREADY_TAKEN.getMessage(),
ErrorCode.BUSINESS_ERROR.getCode());
@ -130,4 +200,307 @@ public class EvaluationProjectController extends BaseController {
return OperateResult.success();
}
/**
* 选择数据源类型.
*
* @param params 请求参数
* @param model session容器
* @return project/datasource_select.html
*/
@GetMapping("/selectDatasourceType")
public String selectDatasourceType(@RequestParam Map<String, String> params, Model model) {
setAttribute(model, params);
return "project/datasource_select";
}
/**
* 导航到数据库记录展现界面.
*
* @param request 查询请求
* @param model session容器
* @return project/datasource_db.html
*/
@GetMapping("/database")
public String databasePage(PaginationBean request, Model model,
@RequestParam Map<String, String> params) {
//获取基础设施ID
Long modelId = evaluationProjectService.selectModelIdByProjectId(
params.get("projectId") == null ? 0 : Integer.parseInt(params.get("projectId")));
if (modelId == null) {
throw new RuntimeException("没有设置基础设施");
}
params.put("modelId", modelId.toString());
//设置数据源页面中的隐藏属性
addDatasourceModelAttribute(request, model, params);
//获取表头
Map<String, String> headerMap = formFieldConfigService.getHeaderMap(modelId.intValue());
model.addAttribute("headerMap", headerMap);
//查询数据
//替换为modelId查询
request.setBusinessKey(modelId.intValue());
List<FormValue> formDataList = formValueService.list(request);
List<Map<String, Object>> listMap = new ArrayList<>();
formDataList.forEach(formData -> {
listMap.add(formData.toMap()); //转为map对象放入listMap中
});
Long total = formValueService.count(request);
setPaginationInfo(request, listMap, total, model);
return "project/datasource_db";
}
private void addDatasourceModelAttribute(PaginationBean request, Model model,
@RequestParam Map<String, String> params) {
EvaluationProject project = evaluationProjectService.getById(
params.get("projectId") == null ? 0 : Integer.parseInt(params.get("projectId")));
if (project != null) {
model.addAttribute("templateId", project.getTemplateId());
params.put("templateId", project.getTemplateId() + "");
}
setAttribute(model, params);
}
private void setAttribute(Model model, @RequestParam Map<String, String> params) {
if (params.get("projectId") != null) {
model.addAttribute("projectId", params.get("projectId"));
}
if (params.get("templateId") != null) {
model.addAttribute("templateId", params.get("templateId"));
}
if (params.get("method") != null) {
model.addAttribute("method", params.get("method"));
}
if (params.get("search") != null) {
model.addAttribute("search", params.get("search"));
}
if (params.get("randomKey") != null) {
model.addAttribute("randomKey", params.get("randomKey"));
}
if (params.get("modelId") != null) {
model.addAttribute("modelId", params.get("modelId"));
}
if (params.get("membership") != null) {
model.addAttribute("membership", params.get("membership"));
}
}
/**
* 导航到csv记录呈现界面.
*
* @param request 查询请求
* @param model session容器
* @return project/datasource_csv.html
*/
@GetMapping("/csv")
public String csvPage(PaginationBean request, Model model,
@RequestParam Map<String, String> params) {
if (params.get("projectId") != null && params.get("templateId") == null) {
EvaluationProject project = evaluationProjectService.getById(
Integer.parseInt(params.get("projectId")));
if (project != null) {
model.addAttribute("templateId", project.getTemplateId());
}
}
setAttribute(model, params);
//分页查询randomKey
List<EvaluationCsvData> list = evaluationCsvDataService.list(request);
if (!list.isEmpty()) {
//取出csvHeader
JSONArray first = JSON.parseArray(list.get(0).getCsvHeader());
Map<String, String> headerMap = new LinkedHashMap<>();
first.forEach(item -> {
headerMap.put(item.toString(), item.toString());
});
model.addAttribute("headerMap", headerMap);
List<Map<String, Object>> listMap = new ArrayList<>();
list.forEach(formData -> {
listMap.add(formData.toMap()); //转为map对象放入listMap中
});
Long total = evaluationCsvDataService.count(request);
setPaginationInfo(request, listMap, total, model);
}
return "project/datasource_csv";
}
/**
* 上传csv文件.
*
* @param uploadInfo 上传的信息
* @return project/datasource_csv.html
*/
@PostMapping("/uploadCsv")
public String uploadCsv(@ModelAttribute CsvUploadBean uploadInfo,
RedirectAttributes ra, @RequestParam Map<String, String> params) throws IOException {
//上传文件信息放入数据库
evaluationProjectService.uploadCsv(uploadInfo);
//设置工程ID
ra.addAttribute("id", uploadInfo.getProjectId());
ra.addAttribute("search", uploadInfo.getSearch());
ra.addAttribute("randomKey", uploadInfo.getRandomKey());
ra.addAttribute("method", uploadInfo.getMethod());
for (Map.Entry<String, String> entry : params.entrySet()) {
ra.addAttribute(entry.getKey(), entry.getValue());
}
return "redirect:/evaluation/project/csv";
}
/**
* 导航的数据评估执行页面.
*
* @param request 评估请求
* @param model session容器
* @return 评估处理页面
*/
@LogOperation("评估执行页面")
@PostMapping("/processEvaluation")
public String processEvaluationPage(@RequestBody PageEvaluationRequest request, Model model) {
addProcessEvaluationModelAttribute(request, model);
return "project/project_evaluation_process";
}
private void addProcessEvaluationModelAttribute(PageEvaluationRequest request, Model model) {
//获取工程名称
EvaluationProject project = evaluationProjectService.getById(request.getProjectId());
if (project != null) {
model.addAttribute("projectName", project.getProjectName());
}
Indicator indicator = indicatorService.selectByTemplateId(request.getTemplateId());
//获取指标名称
if (indicator != null) {
model.addAttribute("indicatorName", indicator.getName());
}
//设置数据源类型
model.addAttribute("datasourceName",
(request.getDatasourceType().equals("database") ? "数据库" : "CSV文件"));
}
/**
* 查看根指标评估历史.
*
* @param projectId 工程ID
* @return project/evaluation_root_history.html
*/
@GetMapping("/root_history")
public String rootHistory(Integer projectId) {
EvaluationHistory recentHistory = evaluationHistoryService.queryRecentByProjectId(projectId);
if (recentHistory == null) {
throw new RuntimeException("请先开始评估");
}
String randomKey = recentHistory.getRandomKey();
// 重定向到另一个接口带上参数
return "redirect:root_history_by_randomKey?randomKey=" + randomKey + "&projectId=" + projectId;
}
@GetMapping("/root_history_by_randomKey")
public String rootHistoryByRandomKey(String randomKey, Integer projectId, Model model) {
List<EvaluationRootResult> rootList = rootResultService.queryListByRandomKey(
randomKey);
model.addAttribute("projectId", projectId);
model.addAttribute("rootList", rootList);
return "project/evaluation_root_history";
}
/**
* 查看报告.
*
* @param id 根历史记录ID
*/
@GetMapping("/reportData")
@ResponseBody
@LogOperation("查看报告")
public OperateResult<ReportBean> report(Integer id) {
final ReportBean reportBean = new ReportBean();
//设置摘要信息
EvaluationRootResult rootResult = rootResultService.getById(id);
reportBean.setResult(rootResult);
//规范化时间
rootResult.setCreateTimeStr(
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm").format(rootResult.getCreateTime()));
//获取底层指标数量
int bottomNum = indicatorService.selectNoChildByTopId(rootResult.getIndicatorTopId()).size();
reportBean.setBottomNum(bottomNum);
//设置所在评价集区间名称
List<IndicatorTopLevel> levelList = topLevelService.getTopLevel(rootResult.getIndicatorTopId());
levelList.sort((a, b) -> Double.compare(Double.parseDouble(b.getGrade()),
Double.parseDouble(a.getGrade())));
setLevelName(levelList, rootResult);
reportBean.setLevelList(levelList);
//各维度得分对比
String membership = rootResult.getMembershipScore();
Map<String, Double> membershipMap = membershipScoreMap(membership);
StringBuilder replaceMemberShipBuilder = new StringBuilder();
levelList.forEach(level -> {
if (membershipMap.get(level.getLevelName()) != null) {
replaceMemberShipBuilder.append(level.getLevelName()).append(":")
.append(membershipMap.get(level.getLevelName())).append(";");
level.setActualValue(
Double.parseDouble(level.getGrade()) * membershipMap.get(level.getLevelName()));
}
});
//按照优先级重新覆盖隶属函数对应的值
rootResult.setMembershipScore(replaceMemberShipBuilder.toString());
//设置指标表格
List<EvaluationIndicatorResult> indicatorResultList = evaluationIndicatorResultService.selectByRootId(
id);
reportBean.setDataNodeList(
covertIndicatorResultToReportIndicatorNodeData(indicatorResultList, levelList));
//设置优和良综合
reportBean.setHighRate(highRate(membershipMap));
//设置隶属度pie数据
reportBean.setPieDataList(pieDataList(membershipMap));
//查询一级指标的名称和得分
List<EvaluationIndicatorResult> firstLevelList = evaluationIndicatorResultService.queryListByParentIdAndRandomKey(
rootResult.getIndicatorTopId(),
rootResult.getRandomKey(), rootResult.getId());
//设置柱状图数据
reportBean.setBarDataList(covertIndicatorResultToBarData(firstLevelList));
return OperateResult.success(reportBean);
}
/**
* 导航到报告页面.
*/
@GetMapping("/report")
public String reportPage() {
return "project/evaluation_report";
}
/**
* 导航到历史页面.
*/
@GetMapping("/history")
public String history(Integer projectId, Model model) {
List<EvaluationHistory> list = evaluationHistoryService.queryListByProjectId(
projectId);
model.addAttribute("list", list);
return "project/evaluation_history";
}
}

View File

@ -1,36 +1,31 @@
package com.hshh.evaluation.controller;
import com.hshh.evaluation.bean.DraftWeightData;
import com.hshh.evaluation.bean.DynamicTable;
import com.hshh.evaluation.bean.MetricComputeRequest;
import com.hshh.evaluation.bean.MetricComputerResponse;
import com.hshh.evaluation.bean.MetricMapperWeightBean;
import com.hshh.evaluation.bean.MetricTableHeaderBean;
import com.hshh.evaluation.bean.PageMetricComputeRequest;
import com.hshh.evaluation.bean.PageMetricComputerResponse;
import com.hshh.evaluation.bean.PageMetricMapperWeightBean;
import com.hshh.evaluation.entity.EvaluationTemplate;
import com.hshh.evaluation.entity.EvaluationTemplateWeight;
import com.hshh.evaluation.service.EvaluationTemplateIndicatorWeightService;
import com.hshh.evaluation.service.EvaluationTemplateService;
import com.hshh.evaluation.service.EvaluationTemplateWeightService;
import com.hshh.indicator.entity.Indicator;
import com.hshh.indicator.service.IndicatorService;
import com.hshh.system.annotation.LogOperation;
import com.hshh.system.base.entity.TableRelations;
import com.hshh.system.base.service.TableRelationsService;
import com.hshh.system.common.Strings.StringOperationUtil;
import com.hshh.system.common.algorithm.AhpNode;
import com.hshh.system.common.algorithm.AhpTreeCompute;
import com.hshh.system.common.Strings.StringUtil;
import com.hshh.system.algorithm.ahp.AhpNode;
import com.hshh.system.algorithm.ahp.AhpTreeCompute;
import com.hshh.system.common.bean.JsTree;
import com.hshh.system.common.bean.OperateResult;
import com.hshh.system.common.bean.PaginationBean;
import com.hshh.system.common.enums.ErrorCode;
import com.hshh.system.common.enums.ErrorMessage;
import com.hshh.system.common.util.DraftStore;
import io.swagger.v3.oas.annotations.Operation;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import javax.validation.Valid;
import lombok.extern.slf4j.Slf4j;
@ -74,10 +69,16 @@ public class EvaluationTemplateController extends AssistantTemplateController {
private TableRelationsService tableRelationsService;
/**
* 权重服务.
* 权重服务详情.
*/
@Resource
private EvaluationTemplateWeightService evaluationTemplateWeightService;
private EvaluationTemplateWeightService evaluationTemplateDetailWeightService;
/**
* 指标权重类.
*/
@Resource
private EvaluationTemplateIndicatorWeightService evaluationTemplateIndicatorWeightService;
/**
* 默认页.
@ -96,6 +97,7 @@ public class EvaluationTemplateController extends AssistantTemplateController {
return "project_template/list";
}
/**
* 导航到增加页面.
*
@ -108,19 +110,6 @@ public class EvaluationTemplateController extends AssistantTemplateController {
return "project_template/add";
}
/**
* 获取指标树.
*
* @param id 项目ID
* @return 指标树
*/
@GetMapping("/metricTree/{id}")
@ResponseBody
public OperateResult<List<JsTree>> metricTree(@PathVariable("id") Integer id, Model model) {
return OperateResult.success(indicatorService.metricTree(id));
}
/**
* 保存模板. 必须设置指标设置完成权重才成成功提交
@ -131,6 +120,7 @@ public class EvaluationTemplateController extends AssistantTemplateController {
*/
@ResponseBody
@PostMapping("/save")
@LogOperation("保存模板")
public OperateResult<Object> save(@Valid @RequestBody EvaluationTemplate evaluationTemplate,
BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
@ -150,35 +140,19 @@ public class EvaluationTemplateController extends AssistantTemplateController {
evaluationTemplate.setCreateTime(LocalDateTime.now());
}
//1.合并权重数据
Map<Integer, DraftWeightData> unitWeightDataMap = unitWeightMap(evaluationTemplate);
//2.填充数据;如果一个指标下面只有一个孩子则默认填充为1
List<Indicator> lonelyChild = indicatorService.queryLonelyChild(
evaluationTemplate.getIndicatorTopId());
paddingDefaultValueWhenParentHasOnlyLonelyChild(lonelyChild, unitWeightDataMap);
//3.开始验证完整性/一致性
String validateWholeMessage = validate(evaluationTemplate.getIndicatorTopId(),
unitWeightDataMap);
if (!validateWholeMessage.isEmpty()) {
return OperateResult.error(null, validateWholeMessage, ErrorCode.BUSINESS_ERROR.getCode());
}
//4. 提交
evaluationTemplateService.saveWhole(evaluationTemplate, unitWeightDataMap);
evaluationTemplateService.saveWhole(evaluationTemplate);
return OperateResult.success();
}
/**
* 根据id删除指标.
* 根据id删除模板.
*
* @param id 要删除的ID
* @return 操作结果
*/
@GetMapping("/remove/{id}")
@ResponseBody
@LogOperation("删除模板")
public OperateResult<Object> remove(@PathVariable("id") Integer id) {
List<TableRelations> reledList = tableRelationsService.queryRel(id,
"m_data_evaluation_template");
@ -208,157 +182,68 @@ public class EvaluationTemplateController extends AssistantTemplateController {
}
/**
* 暂存权重数据.
* 获取指标树.
*
* @param data 权重数据 页面传入的权重map数据key为rowId_colId(from指标id_to指标ID
* value为{rowId:a,colId:b,value:0.1}类似
* @return 操作结果
* @param id 指标ID
* @return 指标树
*/
@PostMapping("/receiveDraft")
@GetMapping("/metricTree/{id}/{templateId}")
@ResponseBody
@SuppressWarnings("unchecked")
public synchronized OperateResult<Void> receiveDraft(@RequestBody DraftWeightData data) {
log.info("receive draft data: pId= {}", data.getParentIndicationId());
//根据key获取已经缓存的信息
Map<Integer, DraftWeightData> dataMap = DraftStore.get(data.getKey(), Map.class);
//如果缓存不存在
if (dataMap == null) {
dataMap = new HashMap<>();
public OperateResult<List<JsTree>> metricTree(@PathVariable("id") Integer id,
@PathVariable("templateId") Integer templateId) {
if (id == null) {
return OperateResult.success(new ArrayList<>());
}
//key为上级指标
dataMap.put(data.getParentIndicationId(), data);
//更新缓存
DraftStore.put(data.getKey(), dataMap);
return OperateResult.success();
//查询指标权重树
Map<Integer, String> weightMap = evaluationTemplateIndicatorWeightService.getEvaluationTemplateIndicatorWeightMap(
id, templateId);
//获取互比信息
Map<Integer, List<EvaluationTemplateWeight>> weightDetailMap = evaluationTemplateDetailWeightService.groupByParentIndicatorId(
id, templateId);
List<JsTree> jsTreeList = indicatorService.metricTree(id);
//开始给指标设置权重指标名称后挂在权重信息;并在data属性中增加子节点-子节点的权重信息
setWeightToTree(jsTreeList, weightMap, weightDetailMap);
return OperateResult.success(jsTreeList);
}
/**
* 获取暂存数据.
*
* @param requestData 请求数据.
* @return 表格和数据
*/
@SuppressWarnings("unchecked")
@PostMapping("/getDraftData")
public synchronized String getDraftData(@RequestBody DraftWeightData requestData, Model model) {
//查看key 是否有缓存记录
Map<Integer, Object> cacheData = DraftStore.get(requestData.getKey(), Map.class);
//如果没有记录
if (cacheData == null) { //从数据库中获取
model.addAttribute("data", createNewDynamicFromDatabase(requestData));
return "project_template/table";
private void setWeightToTree(List<JsTree> jsTreeList, Map<Integer, String> weightMap,
Map<Integer, List<EvaluationTemplateWeight>> weightDetailMap) {
if (jsTreeList == null) {
return;
}
//如果有记录查看对应的指标是否有记录
Object cacheWeightObj = cacheData.get(requestData.getParentIndicationId());
//如果对应指标没有记录
if (cacheWeightObj == null) { //从数据库读取
model.addAttribute("data", createNewDynamicFromDatabase(requestData));
return "project_template/table";
}
DraftWeightData data = (DraftWeightData) cacheWeightObj;
jsTreeList.forEach(tree -> {
if (weightMap.containsKey((int) tree.getOriginalId())) {
model.addAttribute("data", createDynamicFromCache(data));
tree.getData().put("weight", weightMap.get((int) tree.getOriginalId()));
tree.setText(tree.getText() + "(" + weightMap.get((int) tree.getOriginalId()) + ")");
} else {
tree.setText(tree.getText() + "(1)");
tree.getData().put("weight", 1);
}
if (weightDetailMap.containsKey((int) tree.getOriginalId())) {
tree.getData().put("children",
convertEvaluationTemplateWeightList(weightDetailMap.get((int) tree.getOriginalId())));
}
setWeightToTree(tree.getChildren(), weightMap, weightDetailMap);
});
}
/**
* 获取指标编辑/新增时的table表格.
*
* @param indicateId 指标ID
* @param model session容器
* @return table.html
*/
@GetMapping("/tablePage")
public String tablePage(Integer indicateId, Model model) {
model.addAttribute("data", createNewDynamicFromDatabase(indicateId));
model.addAttribute("indicateId", indicateId);
return "project_template/table";
}
/**
* 从数据库创建动态表.
*
* @param requestData 用户请求数据
* @return 动态表
*/
private DynamicTable createNewDynamicFromDatabase(DraftWeightData requestData) {
DynamicTable dynamicTable = new DynamicTable();
//查询当前指标的子指标
List<Indicator> children = indicatorService.queryChildren(requestData.getParentIndicationId());
//获取指标权重信息
List<EvaluationTemplateWeight> weightInDbList = evaluationTemplateWeightService.queryListByIndicatorParentIdAndTemplateId(
requestData.getParentIndicationId(), requestData.getTemplateId());
//转化为key=fromId+"_"+toId,value=self
Map<String, EvaluationTemplateWeight> weightInMap = weightInDbList.stream()
.collect(
Collectors.toMap(a -> a.getFromIndicatorId() + "_" + a.getToIndicatorId(), a -> a));
//设置头信息
dynamicTable.setHeaderMap(createTableHeaderMap(children));
//设置权重信息
List<List<MetricMapperWeightBean>> weight = new ArrayList<>();
for (int i = 0; i < children.size(); i++) {
List<MetricMapperWeightBean> innerList = createEmptyMapperList(requestData, children, i,
weightInMap);
weight.add(innerList);
}
dynamicTable.setWeight(weight);
return dynamicTable;
}
/**
* 创建表格头.
*
* @param children 子指标
* @return 表格头信息
*/
private Map<Integer, MetricTableHeaderBean> createTableHeaderMap(List<Indicator> children) {
List<MetricTableHeaderBean> headerBeans = new ArrayList<>();
children.forEach(a -> {
MetricTableHeaderBean bean = new MetricTableHeaderBean();
bean.setId(a.getId());
bean.setName(a.getName());
headerBeans.add(bean);
});
Map<Integer, MetricTableHeaderBean> headerMap = new LinkedHashMap<>();
headerBeans.forEach(header -> {
headerMap.put(header.getId(), header);
});
return headerMap;
}
/**
* 从数据库创建一个空的默认映射列表.
*
* @param requestData 请求数据
* @param children 子指标
* @param i 行号
* @return 默认映射列表
*/
private static List<MetricMapperWeightBean> createEmptyMapperList(DraftWeightData requestData,
List<Indicator> children, int i, Map<String, EvaluationTemplateWeight> weightInMap) {
List<MetricMapperWeightBean> innerList = new ArrayList<>();
for (Indicator child : children) {
MetricMapperWeightBean weightBean = new MetricMapperWeightBean();
//行ID
weightBean.setRowId(children.get(i).getId());
//列Id
weightBean.setColId(child.getId());
EvaluationTemplateWeight evaluationTemplateWeight = weightInMap.get(
weightBean.getRowId() + "_" + weightBean.getColId());
//初始默认为1
weightBean.setValue(
evaluationTemplateWeight == null ? "1" : evaluationTemplateWeight.getWeight() + "");
weightBean.setRowNum(evaluationTemplateWeight == null ? (i + 1)
: evaluationTemplateWeight.getRowNum());
weightBean.setParentId(requestData.getParentIndicationId());
innerList.add(weightBean);
}
return innerList;
}
/**
* 从缓存中创建动态表.
*
* @param data 缓存数据
* @return 动态表
*/
protected DynamicTable createDynamicFromCache(DraftWeightData data) {
DynamicTable dynamicTable = new DynamicTable();
dynamicTable.setHeaderMap(data.getHeaderMap());
dynamicTable.setWeight(data.getWeight());
return dynamicTable;
}
/**
* 计算指标当设置变化时触发.
*
@ -367,13 +252,13 @@ public class EvaluationTemplateController extends AssistantTemplateController {
*/
@PostMapping("/computer")
@ResponseBody
public synchronized OperateResult<List<MetricComputerResponse>> computerWeight(
@RequestBody MetricComputeRequest requests) {
public synchronized OperateResult<List<PageMetricComputerResponse>> computerWeight(
@RequestBody PageMetricComputeRequest requests) {
//如果没有值直接静默返回
if (requests.getWeightList() == null || requests.getWeightList().isEmpty()) {
return OperateResult.success();
}
final List<MetricComputerResponse> responseList = new ArrayList<>();
final List<PageMetricComputerResponse> responseList = new ArrayList<>();
List<String> metricList = requests.getMetric();
final AhpNode H = new AhpNode("H");
@ -381,101 +266,21 @@ public class EvaluationTemplateController extends AssistantTemplateController {
AhpNode m = new AhpNode(metric);
H.add(m);
});
List<List<MetricMapperWeightBean>> weightList = requests.getWeightList();
List<List<PageMetricMapperWeightBean>> weightList = requests.getWeightList();
double[][] data = StringOperationUtil.toDoubleArray(
double[][] data = StringUtil.toDoubleArray(
convertMetricMapperWeightBeanListToStrArray(weightList));
H.setLocalMatrix(data);
H.globalWeight = 1; // 根节点全局权重=1
AhpTreeCompute.computeTree(H);
H.children.forEach(child -> {
MetricComputerResponse response = new MetricComputerResponse();
PageMetricComputerResponse response = new PageMetricComputerResponse();
response.setId(child.name);
response.setWeight(String.format("%.2f", child.globalWeight));
response.setWeight(String.format("%.4f", child.globalWeight));
responseList.add(response);
});
return OperateResult.success(responseList);
}
/**
* 验证指标权重完整性/一致性.
*
* @param indicatorTopId 指标顶级ID
* @param weightByParentIdMap 已经设置的权重Map
* @return 验证提示结果
*/
private String validate(Integer indicatorTopId,
Map<Integer, DraftWeightData> weightByParentIdMap) {
//所有topId=indicatorTopId列表
List<Indicator> list = indicatorService.queryByTopId(indicatorTopId);
// key=指标ID value=指标数据
Map<Integer, Indicator> indicatorMap = list.stream()
.collect(Collectors.toMap(Indicator::getId, a -> a));
Map<Integer, List<Indicator>> parentMap = list.stream().filter(a -> a.getParentId() != null)
.collect(Collectors.groupingBy(Indicator::getParentId));
StringBuffer sb = new StringBuffer();
parentMap.forEach((indicatorId, children) -> {
//完整性
if (!weightByParentIdMap.containsKey(indicatorId)) {
String indicatorName =
indicatorMap.get(indicatorId) == null ? "" : indicatorMap.get(indicatorId).getName();
sb.append(indicatorName).append("没有设置权重<br>\n");
}
if (weightByParentIdMap.get(indicatorId) != null) {
//一致性
List<List<MetricMapperWeightBean>> childWeightList = weightByParentIdMap.get(indicatorId)
.getWeight();
consistencyValidate(sb, childWeightList, indicatorMap);
}
});
return sb.toString();
}
/**
* 验证一致性. 假设有三个指标 AB,C A--AA AB,AC |B--BB BA BC| C--CC CA CB.
*
* @param sb 提示消息
* @param childWeightList 要验证的数据
* @param indicatorMap 指标map key=id value=指标
*/
private void consistencyValidate(StringBuffer sb,
List<List<MetricMapperWeightBean>> childWeightList, Map<Integer, Indicator> indicatorMap) {
if (childWeightList != null && childWeightList.size() > 1) {
//获取第一行记录作为标准
List<MetricMapperWeightBean> firstRow = childWeightList.get(0);
//获取A指标ID
Integer firstFormIndicatorId = firstRow.get(0).getRowId();
//把第一行的数据用rowId+"_+colId作为键value为值
Map<String, Double> firstRowMap = new HashMap<>();
firstRow.forEach(child -> {
firstRowMap.put(child.getRowId() + "_" + child.getColId(),
Double.parseDouble(child.getValue()));
});
for (int i = 1; i < childWeightList.size(); i++) {
List<MetricMapperWeightBean> weightBeanList = childWeightList.get(i);
for (int j = 1; j < weightBeanList.size(); j++) {
//获取第一行她的from指标值 AB
Double abValue = firstRowMap.get(
firstFormIndicatorId + "_" + weightBeanList.get(j).getRowId());
//获取AC
Double acValue = firstRowMap.get(
firstFormIndicatorId + "_" + weightBeanList.get(j).getColId());
//获取BC
double bcValue = Double.parseDouble(weightBeanList.get(j).getValue());
if (bcValue > 1 && acValue.compareTo(abValue) > 0) {
sb.append(indicatorMap.get(weightBeanList.get(j).getRowId())).append("必须小于1")
.append("<br>\n");
}
if (bcValue < 1 && acValue.compareTo(abValue) < 0) {
sb.append(indicatorMap.get(weightBeanList.get(j).getRowId())).append("必须大于1")
.append("<br>\n");
}
}
}
}
}
}

View File

@ -0,0 +1,54 @@
package com.hshh.evaluation.entity;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.hshh.system.common.bean.BaseBean;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import lombok.Data;
/**
* csv数据.
*
* @author liDongYu
* @since 2025-08-21
*/
@TableName("m_data_evaluation_csv_data")
@Data
public class EvaluationCsvData extends BaseBean {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
private Integer projectId;
private String randomKey;
private String rawData;
private LocalDateTime createTime;
private Integer userId;
public Map<String, Object> toMap() {
Map<String, Object> map = new HashMap<>();
map.put("id", id);
map.put("seq", getSeq());
JSONObject jsonObj = JSON.parseObject(rawData, JSONObject.class);
map.putAll(jsonObj);
return map;
}
private String csvHeader;
}

View File

@ -0,0 +1,39 @@
package com.hshh.evaluation.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.time.LocalDateTime;
import lombok.Data;
/**
* 评估记录表.
*
* @author liDongYu
* @since 2025-08-21
*/
@TableName("m_data_evaluation_history")
@Data
public class EvaluationHistory implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
private LocalDateTime createTime;
private Integer projectId;
private Integer userId;
private String randomKey;
private String datasourceType;
private String membershipFunc;
private String fuzzyOperator;
}

View File

@ -4,7 +4,8 @@ import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.hshh.evaluation.bean.MetricMapperWeightBean;
import com.hshh.evaluation.bean.PageIndicatorWeight;
import com.hshh.evaluation.bean.PageMetricMapperWeightBean;
import com.hshh.system.common.bean.BaseBean;
import java.time.LocalDateTime;
import java.util.List;
@ -47,12 +48,16 @@ public class EvaluationTemplate extends BaseBean {
private String indicatorTopName;
@TableField(exist = false)
private String templateStatusName;
//提交时当前页面对应的指标id,其余指标从缓存取
@TableField(exist = false)
private List<List<MetricMapperWeightBean>> currentPagePartData;
//暂存指标权重的key
@TableField(exist = false)
private String draftKey;
/**
* 前端传递的各个指标的权重.
*/
@TableField(exist = false)
private List<PageIndicatorWeight> indicatorWeightList;
/**
* 前端传递的各个指标之间对比权重信息.
*/
@TableField(exist = false)
private List<PageMetricMapperWeightBean> templateDetailWeightList;
}

View File

@ -0,0 +1,33 @@
package com.hshh.evaluation.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import lombok.Data;
/**
* 模板指标权重表.
*
* @author liDongYu
* @since 2025-08-21
*/
@TableName("m_data_evaluation_template_indicator_weight")
@Data
public class EvaluationTemplateIndicatorWeight implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
private Integer templateId;
private Integer indicatorTopId;
private Integer indicatorId;
private String weight;
}

View File

@ -8,13 +8,13 @@ import lombok.Data;
/**
* <p>
* 模板指标权重
* 模板指标权重详情.
* </p>
*
* @author liDongYu
* @since 2025-08-14
*/
@TableName("m_data_evaluation_template_weight")
@TableName("m_data_evaluation_template_weight_detail")
@Data
public class EvaluationTemplateWeight implements Serializable {
@ -42,7 +42,7 @@ public class EvaluationTemplateWeight implements Serializable {
/**
* 权重.
*/
private Double weight;
private String weight;
/**
* 父指标ID.

View File

@ -0,0 +1,40 @@
package com.hshh.evaluation.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.hshh.evaluation.entity.EvaluationCsvData;
import com.hshh.system.common.bean.PaginationBean;
import java.util.List;
import org.apache.ibatis.annotations.Param;
/**
* 上传的评估数据csv原始数据记录.
*
* @author liDongYu
* @since 2025-08-21
*/
public interface EvaluationCsvDataMapper extends BaseMapper<EvaluationCsvData> {
/**
* 列表信息.
*
* @param paginationBean 请求参数
* @return 结果列表
*/
List<EvaluationCsvData> list(PaginationBean paginationBean);
/**
* 总条数.
*
* @param paginationBean 请求参数
* @return 总条目
*/
Long count(PaginationBean paginationBean);
/**
* 根据id集合查找记录.
*
* @param ids id集合
* @return 结果列表
*/
List<EvaluationCsvData> selectByIds(@Param("ids") List<Integer> ids);
}

View File

@ -0,0 +1,24 @@
package com.hshh.evaluation.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.hshh.evaluation.entity.EvaluationHistory;
import java.util.List;
/**
* <p>
* 评估记录表 Mapper 接口
* </p>
*
* @author liDongYu
* @since 2025-08-21
*/
public interface EvaluationHistoryMapper extends BaseMapper<EvaluationHistory> {
/**
* 根据项目查询最新的历史记录.
*
* @param projectId 工程ID
* @return 最新的历史记录
*/
List<EvaluationHistory> queryRecentByProjectId(Integer projectId);
}

View File

@ -28,4 +28,12 @@ public interface EvaluationProjectMapper extends BaseMapper<EvaluationProject> {
* @return 总条数
*/
Long count(PaginationBean paginationBean);
/**
* 根据工程ID获取到对应的基础设施ID.
*
* @param projectId 工程ID
* @return 基础设施ID
*/
Long selectModelIdByProjectId(Integer projectId);
}

View File

@ -0,0 +1,16 @@
package com.hshh.evaluation.mapper;
import com.hshh.evaluation.entity.EvaluationTemplateIndicatorWeight;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* Mapper 接口
* </p>
*
* @author liDongYu
* @since 2025-08-21
*/
public interface EvaluationTemplateIndicatorWeightMapper extends BaseMapper<EvaluationTemplateIndicatorWeight> {
}

View File

@ -28,4 +28,6 @@ public interface EvaluationTemplateMapper extends BaseMapper<EvaluationTemplate>
* @return 总数
*/
Long count(PaginationBean search);
}

View File

@ -0,0 +1,21 @@
package com.hshh.evaluation.service;
import com.hshh.evaluation.bean.PageEvaluationRequest;
/**
* 核心评估服务.
*
* @author LiDongYU
* @since 2025/7/22
*/
public interface CoreEvaluationService {
/**
* 核心评估算法.
*
* @param request 评估请求
* @param userId 用户 ID
*/
void evaluation(PageEvaluationRequest request, Integer userId);
}

View File

@ -0,0 +1,50 @@
package com.hshh.evaluation.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.hshh.evaluation.bean.CsvUploadBean;
import com.hshh.evaluation.entity.EvaluationCsvData;
import com.hshh.system.common.bean.PaginationBean;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Param;
/**
* 保存每次评估时上传的csv数据.
*
* @author liDongYu
* @since 2025-08-21
*/
public interface EvaluationCsvDataService extends IService<EvaluationCsvData> {
/**
* 保存用户上传的csv文件记录.
*
* @param dataList csv解析后的数据
* @param uploadInfo 附加表单信息
*/
void saveWhole(List<Map<String, String>> dataList, CsvUploadBean uploadInfo);
/**
* 列表信息.
*
* @param paginationBean 请求参数
* @return 结果列表
*/
List<EvaluationCsvData> list(PaginationBean paginationBean);
/**
* 总条数.
*
* @param paginationBean 请求参数
* @return 总条目
*/
Long count(PaginationBean paginationBean);
/**
* 根据id集合查找记录.
*
* @param ids id集合
* @return 结果列表
*/
List<EvaluationCsvData> selectByIds(@Param("ids") List<Integer> ids);
}

View File

@ -0,0 +1,38 @@
package com.hshh.evaluation.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.hshh.evaluation.bean.PageEvaluationRequest;
import com.hshh.evaluation.entity.EvaluationHistory;
import java.util.List;
/**
* 评估记录表 服务类.
*
* @author liDongYu
* @since 2025-08-21
*/
public interface EvaluationHistoryService extends IService<EvaluationHistory> {
/**
* 保存评估历史.
*
* @param request 评估请求
*/
void saveWhole(PageEvaluationRequest request);
/**
* 根据项目查询最新的历史记录.
*
* @param projectId 工程ID
* @return 最新的历史记录
*/
EvaluationHistory queryRecentByProjectId(Integer projectId);
/**
* 查询工程评估的历史记录.
*
* @param projectId 工程ID
* @return 历史记录
*/
List<EvaluationHistory> queryListByProjectId(Integer projectId);
}

View File

@ -1,8 +1,11 @@
package com.hshh.evaluation.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.hshh.evaluation.bean.CsvUploadBean;
import com.hshh.evaluation.bean.PageEvaluationRequest;
import com.hshh.evaluation.entity.EvaluationProject;
import com.hshh.system.common.bean.PaginationBean;
import java.io.IOException;
import java.util.List;
/**
@ -43,4 +46,27 @@ public interface EvaluationProjectService extends IService<EvaluationProject> {
* @param evaluationProject 工程数据
*/
void saveWhole(EvaluationProject evaluationProject);
/**
* 根据工程ID获取到对应的基础设施ID.
*
* @param projectId 工程ID
* @return 基础设施ID
*/
Long selectModelIdByProjectId(Integer projectId);
/**
* 评估动作.
*
* @param request 评估请求
* @param userId 用户ID
*/
void evaluate(PageEvaluationRequest request, Integer userId);
/**
* 上传csv文件.
*
* @param uploadInfo csv文件信息
*/
void uploadCsv(CsvUploadBean uploadInfo) throws IOException;
}

View File

@ -0,0 +1,44 @@
package com.hshh.evaluation.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.hshh.evaluation.entity.EvaluationTemplateIndicatorWeight;
import java.util.List;
import java.util.Map;
/**
* 指标权重服务类.
*
* @author liDongYu
* @since 2025-08-21
*/
public interface EvaluationTemplateIndicatorWeightService extends
IService<EvaluationTemplateIndicatorWeight> {
/**
* 获取指标对应的权重.
*
* @param indicatorTopId 指标ID
* @param templateId 模板 ID
* @return 权重信息
*/
Map<Integer, String> getEvaluationTemplateIndicatorWeightMap(Integer indicatorTopId,
Integer templateId);
/**
* 保存指标权重信息.
*
* @param templateId 模板ID
* @param indicatorTopId 顶级指标ID
* @param list 指标权重集合
*/
void saveEvaluationTemplateIndicatorWeight(Integer templateId, Integer indicatorTopId,
List<EvaluationTemplateIndicatorWeight> list);
/**
* 删除指标权重信息.
*
* @param templateId 模板 ID
* @param indicatorTopId 顶级指标ID
*/
void deleteByTemplateIdAndIndicatorTopId(Integer templateId, Integer indicatorTopId);
}

View File

@ -1,8 +1,6 @@
package com.hshh.evaluation.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.hshh.evaluation.bean.DraftWeightData;
import com.hshh.evaluation.bean.MetricMapperWeightBean;
import com.hshh.evaluation.entity.EvaluationTemplate;
import com.hshh.system.common.bean.PaginationBean;
import java.util.List;
@ -36,10 +34,10 @@ public interface EvaluationTemplateService extends IService<EvaluationTemplate>
* 保存评估模板数据.
*
* @param evaluationTemplate 评估模板
* @param weightData 权重
*/
void saveWhole(EvaluationTemplate evaluationTemplate,
Map<Integer, DraftWeightData> weightData);
void saveWhole(EvaluationTemplate evaluationTemplate
);
/**
* 根据名称查询列表.
@ -48,5 +46,13 @@ public interface EvaluationTemplateService extends IService<EvaluationTemplate>
* @return 模板列表
*/
List<EvaluationTemplate> queryByName(String name);
/**
* 删除模板(事务删除).
*
* @param evaluationTemplate 模板数据
*/
void deleteTemplate(EvaluationTemplate evaluationTemplate);
}

View File

@ -3,6 +3,7 @@ package com.hshh.evaluation.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.hshh.evaluation.entity.EvaluationTemplateWeight;
import java.util.List;
import java.util.Map;
/**
* 模板指标权重表 服务类.
@ -13,24 +14,6 @@ import java.util.List;
public interface EvaluationTemplateWeightService extends IService<EvaluationTemplateWeight> {
/**
* 根据模板id+指标id删除.
*
* @param templateId 模板ID
* @param indicatorTopId 顶级指标ID
* @param parentId 上级指标ID
*/
void deleteEvaluationTemplateWeightWithTemplateIdAndIndicatorId(Integer templateId,
Integer indicatorTopId,
Integer parentId);
/**
* 根据模板ID删除权重信息.
*
* @param templateId 模板ID
*/
void deleteEvaluationTemplateWeightWithTemplateId(Integer templateId);
/**
* 根据指标父ID和模板id获取权重列表.
*
@ -40,4 +23,43 @@ public interface EvaluationTemplateWeightService extends IService<EvaluationTemp
*/
List<EvaluationTemplateWeight> queryListByIndicatorParentIdAndTemplateId(
Integer indicatorParentId, Integer templateId);
/**
* 根据顶级ID和模板id获取权重列表.
*
* @param indicatorTopId 指标顶级ID
* @param templateId 模板ID
* @return 权重列表
*/
List<EvaluationTemplateWeight> queryListByIndicatorTopIdAndTemplateId(
Integer indicatorTopId, Integer templateId);
/**
* 指标对应的权重详情按照父指标ID分类.
*
* @param indicatorParentId 父指标ID
* @param templateId 模板ID
* @return 权重map信息
*/
Map<Integer, List<EvaluationTemplateWeight>> groupByParentIndicatorId(Integer indicatorParentId,
Integer templateId);
/**
* 保存指标和指标之间的矩阵信息.
*
* @param templateId 模板ID
* @param indicatorTopId 指标ID
* @param weightList 互比矩阵列表
*/
void saveIndicatorMatrix(Integer templateId, Integer indicatorTopId,
List<EvaluationTemplateWeight> weightList);
/**
* 删除互比信息.
*
* @param templateId 模板ID
* @param indicatorTopId 顶级指标ID
*/
void deleteByTemplateIdAndIndicatorTopId(Integer templateId, Integer indicatorTopId);
}

View File

@ -0,0 +1,514 @@
package com.hshh.evaluation.service.impl;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.hshh.evaluation.bean.PageEvaluationRequest;
import com.hshh.evaluation.bean.SingleEvaluationData;
import com.hshh.evaluation.entity.EvaluationCsvData;
import com.hshh.evaluation.entity.EvaluationTemplate;
import com.hshh.evaluation.service.CoreEvaluationService;
import com.hshh.evaluation.service.EvaluationCsvDataService;
import com.hshh.evaluation.service.EvaluationHistoryService;
import com.hshh.evaluation.service.EvaluationRootResultService;
import com.hshh.evaluation.service.EvaluationTemplateIndicatorWeightService;
import com.hshh.evaluation.service.EvaluationTemplateService;
import com.hshh.indicator.entity.Indicator;
import com.hshh.indicator.entity.IndicatorBottomCsvMapper;
import com.hshh.indicator.entity.IndicatorBottomFormMapper;
import com.hshh.indicator.entity.IndicatorEvalItem;
import com.hshh.indicator.entity.IndicatorTopLevel;
import com.hshh.indicator.entity.IndicatorTopSet;
import com.hshh.indicator.service.IndicatorBottomCsvMapperService;
import com.hshh.indicator.service.IndicatorBottomFormMapperService;
import com.hshh.indicator.service.IndicatorEvalItemService;
import com.hshh.indicator.service.IndicatorService;
import com.hshh.indicator.service.IndicatorTopLevelService;
import com.hshh.indicator.service.IndicatorTopSetService;
import com.hshh.model.entity.FormValue;
import com.hshh.model.service.FormValueService;
import com.hshh.system.algorithm.fuzzy.EvaluationLevel;
import com.hshh.system.algorithm.fuzzy.FuzzyEvaluationService;
import com.hshh.system.algorithm.fuzzy.FuzzyOperator;
import com.hshh.system.algorithm.fuzzy.GlobalEvaluationConfig;
import com.hshh.system.algorithm.fuzzy.GlobalMembershipFunction;
import com.hshh.system.algorithm.fuzzy.GradeRange;
import com.hshh.system.algorithm.fuzzy.IndicatorNode;
import com.hshh.system.algorithm.fuzzy.LeafFullTriangularMembershipFunction;
import com.hshh.system.algorithm.fuzzy.LeafHybridMembershipFunction;
import com.hshh.system.common.bean.PaginationBean;
import com.hshh.system.ws.WsEvaluationServer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* 模糊综合评估服务类 (重构后).
*
* @author LiDongYU
* @since 2025/7/22
*/
@Service
public class CoreEvaluationServiceImpl implements CoreEvaluationService {
@Resource
private EvaluationTemplateService evaluationTemplateService;
@Resource
private IndicatorTopSetService indicatorTopSetService;
@Resource
private IndicatorService indicatorService;
@Resource
private IndicatorTopLevelService indicatorTopLevelService;
@Resource
private IndicatorBottomFormMapperService indicatorBottomFormMapperService;
@Resource
private IndicatorBottomCsvMapperService indicatorBottomCsvMapperService;
@Resource
private FormValueService formValueService;
@Resource
private EvaluationCsvDataService evaluationCsvDataService;
@Resource
private EvaluationTemplateIndicatorWeightService evaluationTemplateIndicatorWeightService;
@Resource
private IndicatorEvalItemService indicatorEvalItemService; //底部指标评价集区间
@Resource
private EvaluationHistoryService evaluationHistoryService; //评估历史批次记录
@Resource
private EvaluationRootResultService evaluationRootResultService; //评估详情及概况记录
// 定义分页大小常量便于管理
private static final int DEFAULT_PAGE_SIZE = 100;
/**
* 评估主入口方法 通过策略模式分发到不同的数据源处理器.
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void evaluation(PageEvaluationRequest request, Integer userId) {
long now = System.currentTimeMillis();
try {
// 1. 获取评估所需的通用前置数据模板隶属度评价集指标树
EvaluationTemplate template = getTemplateOrThrow(request.getTemplateId());
Integer indicatorTopId = template.getIndicatorTopId();
request.setIndicatorTopId(indicatorTopId);
IndicatorTopSet membershipFuncSet = getMembershipFuncSetOrThrow(indicatorTopId); //隶属函数设置
List<IndicatorTopLevel> topLevelList = getTopLevelListOrThrow(indicatorTopId); //评价集区间
Indicator indicator = indicatorService.getIndicator(indicatorTopId); //获取指标
Map<Integer, String> indicatorWeightMap = evaluationTemplateIndicatorWeightService.getEvaluationTemplateIndicatorWeightMap(
indicatorTopId, template.getId()); //指标权重map
Map<Integer, List<IndicatorEvalItem>> indicatorEvalItemMap = indicatorEvalItemService.getIndicatorEvalItemMapByIndicatorTopId(
indicatorTopId); //底部指标评价范围
// 2. 根据数据源类型选择并执行相应的策略
String dataSourceType = request.getDatasourceType();
if ("database".equals(dataSourceType)) {
// 获取数据库数据源的特定映射关系
List<IndicatorBottomFormMapper> mapperList = indicatorBottomFormMapperService.selectIndicatorToFieldNameListByIndicatorTopId(
indicatorTopId);
if (mapperList == null || mapperList.isEmpty()) {
throw new EvaluationException("设施字段和指标没有设置映射关系");
}
Map<Integer, String> mappingMap = mapperList.stream().collect(
Collectors.toMap(IndicatorBottomFormMapper::getIndicatorId,
IndicatorBottomFormMapper::getFiledName));
// 创建数据库策略并执行
DataSourceStrategy<FormValue> dbStrategy = new DatabaseSourceStrategy(formValueService,
FormValue::getModelData);
processEvaluation(request, userId, membershipFuncSet, topLevelList, indicator, mappingMap,
dbStrategy, indicatorWeightMap, indicatorEvalItemMap);
} else if ("csv".equals(dataSourceType)) {
// 获取CSV数据源的特定映射关系
List<IndicatorBottomCsvMapper> mapperList = indicatorBottomCsvMapperService.selectIndicatorToFieldNameListByIndicatorTopId(
indicatorTopId);
if (mapperList == null || mapperList.isEmpty()) {
throw new EvaluationException("CSV列和指标没有设置映射关系");
}
Map<Integer, String> mappingMap = mapperList.stream().collect(
Collectors.toMap(IndicatorBottomCsvMapper::getIndicatorId,
IndicatorBottomCsvMapper::getCsvColumnName));
// 创建CSV策略并执行
DataSourceStrategy<EvaluationCsvData> csvStrategy = new CsvSourceStrategy(
evaluationCsvDataService, EvaluationCsvData::getRawData);
processEvaluation(request, userId, membershipFuncSet, topLevelList, indicator, mappingMap,
csvStrategy, indicatorWeightMap, indicatorEvalItemMap);
} else {
throw new EvaluationException("不支持的数据源类型: " + dataSourceType);
}
} catch (EvaluationException e) {
WsEvaluationServer.sendTips(e.getMessage(), request.getRandomKey());
}
long end = System.currentTimeMillis();
long diff = (end - now) / 1000;
WsEvaluationServer.sendTips("整体评估完成,总耗时" + diff + "", request.getRandomKey());
}
/**
* 统一的评估处理流程与具体数据源解耦.
*
* @param strategy 数据源策略封装了如何获取数据的具体实现
*/
private <T> void processEvaluation(PageEvaluationRequest request, Integer userId,
IndicatorTopSet membershipFuncSet, List<IndicatorTopLevel> topLevelList,
Indicator indicator, Map<Integer, String> mappingMap,
DataSourceStrategy<T> strategy, Map<Integer, String> indicatorWeightMap,
Map<Integer, List<IndicatorEvalItem>> indicatorEvalItemMap) {
//首先记录本次评估
request.setMembership(membershipFuncSet.getMembershipFunc());
evaluationHistoryService.saveWhole(request);
boolean hasSpecificIds = request.getIds() != null && request.getIds().length > 0;
if (hasSpecificIds) {
// --- 按指定ID列表进行评估 ---
List<Integer> idList = Arrays.stream(request.getIds()).boxed().collect(Collectors.toList());
List<T> dataList = strategy.fetchByIds(idList);
if (dataList != null && !dataList.isEmpty()) {
List<SingleEvaluationData> evaluationData = strategy.extractData(dataList);
startEvaluation(request, userId, membershipFuncSet, topLevelList, indicator, evaluationData,
mappingMap, indicatorWeightMap, indicatorEvalItemMap);
}
} else {
// --- 分页处理所有数据进行评估 ---
PaginationBean paginationBean = new PaginationBean();
paginationBean.setSearch(request.getSearch());
paginationBean.setPageSize(DEFAULT_PAGE_SIZE);
paginationBean.setRandomKey(request.getRandomKey());
paginationBean.setBusinessKey(request.getModelId());
Long totalRecords = strategy.count(paginationBean);
if (totalRecords == null || totalRecords == 0) {
return; // 没有数据直接返回
}
int totalPages = (int) ((totalRecords + DEFAULT_PAGE_SIZE - 1) / DEFAULT_PAGE_SIZE);
for (int currentPage = 1; currentPage <= totalPages; currentPage++) {
paginationBean.setCurrentPage(currentPage);
List<T> currentPageData = strategy.fetchByPage(paginationBean);
if (currentPageData != null && !currentPageData.isEmpty()) {
List<SingleEvaluationData> evaluationData = strategy.extractData(currentPageData);
startEvaluation(request, userId, membershipFuncSet, topLevelList, indicator,
evaluationData, mappingMap, indicatorWeightMap, indicatorEvalItemMap);
}
}
}
}
/**
* 数据源策略接口 定义了数据访问的统一契约将数据获取的算法从主流程中分离出来.
*
* @param <T> 数据源对应的实体类型 ( FormValue, EvaluationCsvData)
*/
private interface DataSourceStrategy<T> {
List<T> fetchByIds(List<Integer> ids);
List<T> fetchByPage(PaginationBean paginationBean);
Long count(PaginationBean paginationBean);
List<SingleEvaluationData> extractData(List<T> dataList);
}
/**
* 数据库数据源的具体实现策略.
*/
private static class DatabaseSourceStrategy implements DataSourceStrategy<FormValue> {
private final FormValueService service;
private final Function<FormValue, String> dataExtractor;
public DatabaseSourceStrategy(FormValueService service,
Function<FormValue, String> dataExtractor) {
this.service = service;
this.dataExtractor = dataExtractor;
}
@Override
public List<FormValue> fetchByIds(List<Integer> ids) {
return service.selectByIds(ids);
}
@Override
public List<FormValue> fetchByPage(PaginationBean paginationBean) {
return service.list(paginationBean);
}
@Override
public Long count(PaginationBean paginationBean) {
return service.count(paginationBean);
}
@Override
public List<SingleEvaluationData> extractData(List<FormValue> dataList) {
List<SingleEvaluationData> list = new ArrayList<>();
dataList.forEach(value -> {
SingleEvaluationData singleEvaluationData = new SingleEvaluationData();
singleEvaluationData.setData(value.getModelData());
singleEvaluationData.setId(value.getId());
singleEvaluationData.setType("database");
list.add(singleEvaluationData);
});
return list;
}
}
/**
* CSV文件数据源的具体实现策略.
*/
private static class CsvSourceStrategy implements DataSourceStrategy<EvaluationCsvData> {
private final EvaluationCsvDataService service;
private final Function<EvaluationCsvData, String> dataExtractor;
public CsvSourceStrategy(EvaluationCsvDataService service,
Function<EvaluationCsvData, String> dataExtractor) {
this.service = service;
this.dataExtractor = dataExtractor;
}
@Override
public List<EvaluationCsvData> fetchByIds(List<Integer> ids) {
return service.selectByIds(ids);
}
@Override
public List<EvaluationCsvData> fetchByPage(PaginationBean paginationBean) {
return service.list(paginationBean);
}
@Override
public Long count(PaginationBean paginationBean) {
return service.count(paginationBean);
}
@Override
public List<SingleEvaluationData> extractData(List<EvaluationCsvData> dataList) {
List<SingleEvaluationData> list = new ArrayList<>();
dataList.forEach(value -> {
SingleEvaluationData singleEvaluationData = new SingleEvaluationData();
singleEvaluationData.setId(value.getId());
singleEvaluationData.setType("csv");
singleEvaluationData.setData(value.getRawData());
list.add(singleEvaluationData);
});
return list;
}
}
private EvaluationTemplate getTemplateOrThrow(Integer templateId) {
EvaluationTemplate template = evaluationTemplateService.getById(templateId);
if (template == null) {
throw new EvaluationException("没有找到对应的指标信息");
}
return template;
}
private IndicatorTopSet getMembershipFuncSetOrThrow(Integer indicatorTopId) {
IndicatorTopSet set = indicatorTopSetService.getIndicatorTopSet(indicatorTopId);
if (set == null) {
throw new EvaluationException("没有找到对应的隶属度函数");
}
return set;
}
private List<IndicatorTopLevel> getTopLevelListOrThrow(Integer indicatorTopId) {
List<IndicatorTopLevel> list = indicatorTopLevelService.getTopLevel(indicatorTopId);
if (list == null || list.isEmpty()) {
throw new EvaluationException("没有找到全局指标集区间和分值");
}
return list;
}
private static class EvaluationException extends RuntimeException {
public EvaluationException(String message) {
super(message);
}
}
private GlobalEvaluationConfig globalEvaluationConfig(List<IndicatorTopLevel> topLevelList) {
topLevelList.sort((a, b) -> Integer.compare(b.getLevelOrder(), a.getLevelOrder()));
List<EvaluationLevel> evaluationLevels = new ArrayList<>();
topLevelList.forEach(topLevel -> {
EvaluationLevel evaluationLevel = new EvaluationLevel(topLevel.getLevelName(),
Double.parseDouble(topLevel.getGrade()));
evaluationLevels.add(evaluationLevel);
});
return new GlobalEvaluationConfig(evaluationLevels);
}
/**
* 核心评估触发点此方法保持不变.
*/
private void startEvaluation(PageEvaluationRequest request, Integer userId,
IndicatorTopSet membershipFuncSet, List<IndicatorTopLevel> topLevelList,
Indicator indicator, List<SingleEvaluationData> originalList, Map<Integer, String> mappingMap,
Map<Integer, String> indicatorWeightMap,
Map<Integer, List<IndicatorEvalItem>> indicatorEvalItemMap) {
long now = System.currentTimeMillis();
WsEvaluationServer.sendTips("分批次评估" + originalList.size() + "条记录",
request.getRandomKey());
List<IndicatorNode> resultList = new ArrayList<>();
//全局level等级及代表分
GlobalEvaluationConfig globalEvaluationConfig = globalEvaluationConfig(topLevelList);
//遍历数据
for (SingleEvaluationData singleEvaluationData : originalList) {
JSONObject jsonObject = JSON.parseObject(singleEvaluationData.getData());
Map<String, Object> valueMap = new HashMap<>(jsonObject);
IndicatorNode root = new IndicatorNode(indicator.getId() + "", indicator.getName());
//工程ID
root.setProjectId(request.getProjectId());
//记录原始数据ID
root.setOriginalId(indicator.getId());
//记录数据来源
root.setType(request.getDatasourceType());
//本批次唯一标识
root.setRandomKey(request.getRandomKey());
//用户id
root.setUserId(userId);
//设置标识
root.setLogicName(jsonObject.getString(request.getIdentifier()) == null ? ""
: jsonObject.getString(request.getIdentifier()));
root.setLevel(0);
//设置隶属函数
root.setMembershipFunction(new GlobalMembershipFunction(membershipFuncSet.getMembershipFunc(),
Double.parseDouble(membershipFuncSet.getTriangleOverlapRatio()),
Double.parseDouble(membershipFuncSet.getTrianglePeakRatio()),
Double.parseDouble(membershipFuncSet.getSoftEdgeS()),
globalEvaluationConfig));
//设置权重
root.setWeight(indicatorWeightMap.get(indicator.getId()) == null ? 1
: Double.parseDouble(indicatorWeightMap.get(indicator.getId())));
recursionBuild(valueMap, indicatorWeightMap, indicator, root, mappingMap,
globalEvaluationConfig, membershipFuncSet, indicatorEvalItemMap);
FuzzyEvaluationService service = new FuzzyEvaluationService(globalEvaluationConfig);
service.performFuzzyEvaluation(root, FuzzyOperator.valueOf(request.getMethod()));
resultList.add(root);
}
saveResult(resultList);
long end = System.currentTimeMillis();
long diff = (end - now) / 1000;
WsEvaluationServer.sendTips(
"分批次结束评估" + originalList.size() + "条记录,耗时:" + diff + "",
request.getRandomKey());
}
private void saveResult(List<IndicatorNode> resultList) {
resultList.forEach(indicatorNode -> {
evaluationRootResultService.saveWhole(indicatorNode);
});
}
/**
* 递归一个根指标设置实际值隶属度权重.
*
* @param valueMap 实际值map key=字段名称
* @param indicatorWeightMap 权重map key=指标ID
* @param indicator 当前指标
* @param node 当前节点
* @param mappingMap 指标id对应字段名称
* @param globalEvaluationConfig 全局评价集集合
* @param membershipFuncSet 全局隶属函数设置告知采用那个函数
* @param indicatorEvalItemMap 底部指标评价集区间
*/
private void recursionBuild(Map<String, Object> valueMap,
Map<Integer, String> indicatorWeightMap,
Indicator indicator, IndicatorNode node, Map<Integer, String> mappingMap,
GlobalEvaluationConfig globalEvaluationConfig, IndicatorTopSet membershipFuncSet,
Map<Integer, List<IndicatorEvalItem>> indicatorEvalItemMap) {
if (indicator.getChildren() != null && !indicator.getChildren().isEmpty()) {
node.setChildren(new ArrayList<>());
indicator.getChildren().forEach(child -> {
IndicatorNode childNode = new IndicatorNode(child.getId() + "", child.getName());
childNode.setLevel(node.getLevel() + 1);
//设置隶属函数
childNode.setMembershipFunction(
new GlobalMembershipFunction(membershipFuncSet.getMembershipFunc(),
Double.parseDouble(membershipFuncSet.getTriangleOverlapRatio()),
Double.parseDouble(membershipFuncSet.getTrianglePeakRatio()),
Double.parseDouble(membershipFuncSet.getSoftEdgeS()),
globalEvaluationConfig));
//设置权重
childNode.setWeight(indicatorWeightMap.get(child.getId()) == null ? 1
: Double.parseDouble(indicatorWeightMap.get(child.getId())));
childNode.setOriginalId(child.getId());
childNode.setRandomKey(node.getRandomKey());
node.getChildren().add(childNode);
recursionBuild(valueMap, indicatorWeightMap, child, childNode, mappingMap,
globalEvaluationConfig, membershipFuncSet, indicatorEvalItemMap);
});
} else { //是叶子节点设置值设置
//根据mappingMap获取这个指标对应的输入字段
String fieldName = mappingMap.get(indicator.getId());
if (fieldName != null) {
Object currentValue = valueMap.get(fieldName);
if (currentValue != null) {
node.setActualValue(Double.parseDouble(currentValue.toString()));
}
}
if (indicatorEvalItemMap.get(indicator.getId()) != null) {
List<IndicatorEvalItem> indicatorEvalItemList = indicatorEvalItemMap.get(indicator.getId());
List<GradeRange> gradeRanges = getGradeRange(indicatorEvalItemList);
if (membershipFuncSet.getMembershipFunc().equals("TRAP_TRI")) {
node.setMembershipFunction(new LeafHybridMembershipFunction(gradeRanges,
Double.parseDouble(membershipFuncSet.getTriangleOverlapRatio()),
Double.parseDouble(membershipFuncSet.getTrianglePeakRatio()),
Double.parseDouble(membershipFuncSet.getSoftEdgeS()),
globalEvaluationConfig));
} else {
node.setMembershipFunction(new LeafFullTriangularMembershipFunction(gradeRanges,
Double.parseDouble(membershipFuncSet.getTriangleOverlapRatio()),
Double.parseDouble(membershipFuncSet.getTrianglePeakRatio()),
Double.parseDouble(membershipFuncSet.getSoftEdgeS()),
globalEvaluationConfig));
}
}
}
}
private List<GradeRange> getGradeRange(List<IndicatorEvalItem> indicatorEvalItemList) {
List<GradeRange> list = new ArrayList<>();
indicatorEvalItemList.sort(
Comparator.comparingDouble(a -> Double.parseDouble(a.getBottomValue())));
indicatorEvalItemList.forEach(indicatorEvalItem -> {
GradeRange gradeRange = new GradeRange(indicatorEvalItem.getEvaluationName(),
Double.parseDouble(indicatorEvalItem.getBottomValue()),
Double.parseDouble(indicatorEvalItem.getTopValue()));
list.add(gradeRange);
});
return list;
}
}

View File

@ -0,0 +1,61 @@
package com.hshh.evaluation.service.impl;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hshh.evaluation.bean.CsvUploadBean;
import com.hshh.evaluation.entity.EvaluationCsvData;
import com.hshh.evaluation.mapper.EvaluationCsvDataMapper;
import com.hshh.evaluation.service.EvaluationCsvDataService;
import com.hshh.system.common.bean.PaginationBean;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* 服务实现类.
*
* @author liDongYu
* @since 2025-08-21
*/
@Service
public class EvaluationCsvDataServiceImpl extends
ServiceImpl<EvaluationCsvDataMapper, EvaluationCsvData> implements
EvaluationCsvDataService {
@Transactional(rollbackFor = Exception.class)
@Override
public void saveWhole(List<Map<String, String>> dataList, CsvUploadBean uploadInfo) {
dataList.forEach(map -> {
StringBuilder header = new StringBuilder();
EvaluationCsvData evaluationCsvData = new EvaluationCsvData();
//设置原始数据
evaluationCsvData.setRawData(JSON.toJSONString(map));
//设置随机key
evaluationCsvData.setRandomKey(uploadInfo.getRandomKey());
//时间
evaluationCsvData.setCreateTime(LocalDateTime.now());
//工程
evaluationCsvData.setProjectId(uploadInfo.getProjectId());
//设置表头
evaluationCsvData.setCsvHeader(JSON.toJSONString(map.keySet()));
save(evaluationCsvData);
});
}
@Override
public List<EvaluationCsvData> list(PaginationBean paginationBean) {
return this.baseMapper.list(paginationBean);
}
@Override
public Long count(PaginationBean paginationBean) {
return this.baseMapper.count(paginationBean);
}
@Override
public List<EvaluationCsvData> selectByIds(List<Integer> ids) {
return this.baseMapper.selectByIds(ids);
}
}

View File

@ -0,0 +1,57 @@
package com.hshh.evaluation.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hshh.evaluation.bean.PageEvaluationRequest;
import com.hshh.evaluation.entity.EvaluationHistory;
import com.hshh.evaluation.mapper.EvaluationHistoryMapper;
import com.hshh.evaluation.service.EvaluationHistoryService;
import java.time.LocalDateTime;
import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* <p>
* 评估记录表 服务实现类
* </p>
*
* @author liDongYu
* @since 2025-08-21
*/
@Service
public class EvaluationHistoryServiceImpl extends
ServiceImpl<EvaluationHistoryMapper, EvaluationHistory> implements
EvaluationHistoryService {
@Transactional
@Override
public void saveWhole(PageEvaluationRequest request) {
EvaluationHistory history = new EvaluationHistory();
history.setCreateTime(LocalDateTime.now());
history.setUserId(request.getUserId());
history.setRandomKey(request.getRandomKey());
history.setProjectId(request.getProjectId());
history.setDatasourceType(request.getDatasourceType());
history.setMembershipFunc(request.getMembership());
history.setFuzzyOperator(request.getMethod());
save(history);
}
@Override
public EvaluationHistory queryRecentByProjectId(Integer projectId) {
List<EvaluationHistory> list = this.baseMapper.queryRecentByProjectId(projectId);
if (!list.isEmpty()) {
return list.get(0);
}
return null;
}
@Override
public List<EvaluationHistory> queryListByProjectId(Integer projectId) {
QueryWrapper<EvaluationHistory> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("project_id", projectId);
queryWrapper.orderByDesc("id");
return this.baseMapper.selectList(queryWrapper);
}
}

View File

@ -2,14 +2,30 @@ package com.hshh.evaluation.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hshh.evaluation.bean.CsvUploadBean;
import com.hshh.evaluation.bean.PageEvaluationRequest;
import com.hshh.evaluation.entity.EvaluationProject;
import com.hshh.evaluation.mapper.EvaluationProjectMapper;
import com.hshh.evaluation.service.CoreEvaluationService;
import com.hshh.evaluation.service.EvaluationCsvDataService;
import com.hshh.evaluation.service.EvaluationProjectService;
import com.hshh.system.base.service.TableRelationsService;
import com.hshh.system.common.bean.PaginationBean;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -20,9 +36,10 @@ import org.springframework.transaction.annotation.Transactional;
* @since 2025-08-11
*/
@Service
@Slf4j
public class EvaluationProjectServiceImpl extends
ServiceImpl<EvaluationProjectMapper, EvaluationProject> implements
EvaluationProjectService {
ServiceImpl<EvaluationProjectMapper, EvaluationProject> implements EvaluationProjectService {
/**
* 数据库引用关系记录服务类.
@ -30,6 +47,19 @@ public class EvaluationProjectServiceImpl extends
@Resource
private TableRelationsService tableRelationsService;
/**
* 上传的csv文件数据记录服务类.
*/
@Resource
private EvaluationCsvDataService evaluationCsvDataService;
/**
* 数据库记录评估服务.
*/
@Resource
private CoreEvaluationService coreEvaluationService;
@Override
public List<EvaluationProject> list(PaginationBean paginationBean) {
return this.baseMapper.list(paginationBean);
@ -58,4 +88,61 @@ public class EvaluationProjectServiceImpl extends
tableRelationsService.addRel(evaluationProject.getId(), "m_data_evaluation_project", relList,
"m_data_evaluation_template");
}
@Override
public Long selectModelIdByProjectId(Integer projectId) {
return this.baseMapper.selectModelIdByProjectId(projectId);
}
/**
* 评估操作.
*
* @param request 评估请求
* @param userId 用户ID
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void evaluate(PageEvaluationRequest request, Integer userId) {
//评估接口单列
coreEvaluationService.evaluation(request, userId);
}
@Override
public void uploadCsv(CsvUploadBean uploadInfo) throws IOException {
CSVFormat format = CSVFormat.DEFAULT
.builder()
.setHeader() // 表示第一行就是表头
.setSkipHeaderRecord(true) // 跳过第一行数据不把它算到记录里
.setIgnoreEmptyLines(true)
.setTrim(true)
.build();
//获取表头信息
List<Map<String, String>> rows = new ArrayList<>();
try (Reader reader = new InputStreamReader(uploadInfo.getFile().getInputStream(),
StandardCharsets.UTF_8);
CSVParser parser = new CSVParser(reader, format)) {
// 取有序表头Apache 返回 Map<列名, 索引>按索引排序得到原始顺序
List<String> headers = parser.getHeaderMap()
.entrySet()
.stream()
.sorted(Comparator.comparingInt(Map.Entry::getValue))
.map(Map.Entry::getKey)
.collect(Collectors.toList());
for (CSVRecord rec : parser) {
Map<String, String> obj = new LinkedHashMap<>();
for (String h : headers) {
// get(h)按列名取值可能为 null
obj.put(h, rec.isMapped(h) ? rec.get(h) : null);
}
rows.add(obj);
}
evaluationCsvDataService.saveWhole(rows, uploadInfo);
}
}
}

View File

@ -0,0 +1,63 @@
package com.hshh.evaluation.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hshh.evaluation.entity.EvaluationTemplateIndicatorWeight;
import com.hshh.evaluation.mapper.EvaluationTemplateIndicatorWeightMapper;
import com.hshh.evaluation.service.EvaluationTemplateIndicatorWeightService;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* <p>
* 服务实现类
* </p>
*
* @author liDongYu
* @since 2025-08-21
*/
@Service
public class EvaluationTemplateIndicatorWeightServiceImpl extends
ServiceImpl<EvaluationTemplateIndicatorWeightMapper, EvaluationTemplateIndicatorWeight> implements
EvaluationTemplateIndicatorWeightService {
@Override
public Map<Integer, String> getEvaluationTemplateIndicatorWeightMap(Integer indicatorTopId,
Integer templateId) {
QueryWrapper<EvaluationTemplateIndicatorWeight> wrapper = new QueryWrapper<>();
wrapper.eq("indicator_top_id", indicatorTopId);
wrapper.eq("template_id", templateId);
List<EvaluationTemplateIndicatorWeight> list = this.list(wrapper);
if (list != null && !list.isEmpty()) {
return list.stream().collect(
Collectors.toMap(EvaluationTemplateIndicatorWeight::getIndicatorId,
EvaluationTemplateIndicatorWeight::getWeight));
}
return Map.of();
}
@Transactional(rollbackFor = Exception.class)
@Override
public void saveEvaluationTemplateIndicatorWeight(Integer templateId, Integer indicatorTopId,
List<EvaluationTemplateIndicatorWeight> list) {
//先删除旧信息
QueryWrapper<EvaluationTemplateIndicatorWeight> wrapper = new QueryWrapper<>();
wrapper.eq("template_id", templateId);
wrapper.eq("indicator_top_id", indicatorTopId);
this.remove(wrapper);
if (list != null && !list.isEmpty()) {
list.forEach(this::save);
}
}
@Override
public void deleteByTemplateIdAndIndicatorTopId(Integer templateId, Integer indicatorTopId) {
QueryWrapper<EvaluationTemplateIndicatorWeight> wrapper = new QueryWrapper<>();
wrapper.eq("template_id", templateId);
wrapper.eq("indicator_top_id", indicatorTopId);
this.remove(wrapper);
}
}

View File

@ -2,15 +2,16 @@ package com.hshh.evaluation.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hshh.evaluation.bean.DraftWeightData;
import com.hshh.evaluation.entity.EvaluationTemplate;
import com.hshh.evaluation.entity.EvaluationTemplateIndicatorWeight;
import com.hshh.evaluation.entity.EvaluationTemplateWeight;
import com.hshh.evaluation.mapper.EvaluationTemplateMapper;
import com.hshh.evaluation.service.EvaluationTemplateIndicatorWeightService;
import com.hshh.evaluation.service.EvaluationTemplateService;
import com.hshh.evaluation.service.EvaluationTemplateWeightService;
import com.hshh.system.common.bean.PaginationBean;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -25,8 +26,16 @@ import org.springframework.transaction.annotation.Transactional;
public class EvaluationTemplateServiceImpl extends
ServiceImpl<EvaluationTemplateMapper, EvaluationTemplate> implements EvaluationTemplateService {
/**
* 指标互比详情服务类.
*/
@Resource
private EvaluationTemplateWeightService evaluationTemplateWeightService;
private EvaluationTemplateWeightService evaluationTemplateDetailWeightService;
/**
* 指标权重服务类.
*/
@Resource
private EvaluationTemplateIndicatorWeightService evaluationTemplateIndicatorWeightService;
@Override
public List<EvaluationTemplate> list(PaginationBean search) {
@ -40,42 +49,52 @@ public class EvaluationTemplateServiceImpl extends
@Transactional(rollbackFor = Exception.class)
@Override
public void saveWhole(EvaluationTemplate evaluationTemplate,
Map<Integer, DraftWeightData> weightDataMap) {
public void saveWhole(EvaluationTemplate evaluationTemplate
) {
//保存或者更新模板信息
saveOrUpdate(evaluationTemplate);
if (weightDataMap != null && !weightDataMap.isEmpty()) {
weightDataMap.forEach((k, v) -> {
//先按模板ID+上级指标ID
evaluationTemplateWeightService.deleteEvaluationTemplateWeightWithTemplateIdAndIndicatorId(
evaluationTemplate.getId(), evaluationTemplate.getIndicatorTopId(), k);
v.getWeight().forEach(weightBeanList -> {
weightBeanList.forEach(weightBean -> {
EvaluationTemplateWeight evaluationTemplateWeight = new EvaluationTemplateWeight();
//设置模板ID
evaluationTemplateWeight.setTemplateId(evaluationTemplate.getId());
//设置权重
evaluationTemplateWeight.setWeight(Double.valueOf(weightBean.getValue()));
//设置顶级指标ID
evaluationTemplateWeight.setIndicatorTopId(evaluationTemplate.getIndicatorTopId());
//fromIndicatorId
evaluationTemplateWeight.setFromIndicatorId(weightBean.getRowId());
//toIndicatorId
evaluationTemplateWeight.setToIndicatorId(weightBean.getColId());
//设置上级指标ID
evaluationTemplateWeight.setIndicatorParentId(weightBean.getParentId());
//设置行号
evaluationTemplateWeight.setRowNum(weightBean.getRowNum());
//保存
evaluationTemplateWeightService.save(evaluationTemplateWeight);
});
});
//保存指标权重信息
List<EvaluationTemplateIndicatorWeight> indicatorWeightList = new ArrayList<>();
if (evaluationTemplate.getIndicatorWeightList() != null
&& !evaluationTemplate.getIndicatorWeightList()
.isEmpty()) {
evaluationTemplate.getIndicatorWeightList().forEach(a -> {
EvaluationTemplateIndicatorWeight indicatorWeight = new EvaluationTemplateIndicatorWeight();
indicatorWeight.setWeight(a.getWeight());
//设置模板ID
indicatorWeight.setTemplateId(evaluationTemplate.getId());
//设置顶级指标ID
indicatorWeight.setIndicatorTopId(evaluationTemplate.getIndicatorTopId());
//设置指标ID
indicatorWeight.setIndicatorId(a.getIndicatorId());
indicatorWeightList.add(indicatorWeight);
});
evaluationTemplateIndicatorWeightService.saveEvaluationTemplateIndicatorWeight(
evaluationTemplate.getId(), evaluationTemplate.getIndicatorTopId(), indicatorWeightList);
}
//保存各个指标之间互相比较的权重信息
if (evaluationTemplate.getTemplateDetailWeightList() != null
&& !evaluationTemplate.getTemplateDetailWeightList()
.isEmpty()) {
List<EvaluationTemplateWeight> detailList = new ArrayList<>();
evaluationTemplate.getTemplateDetailWeightList().forEach(a -> {
EvaluationTemplateWeight detailWeight = new EvaluationTemplateWeight();
//设置模板ID
detailWeight.setTemplateId(evaluationTemplate.getId());
//设置顶级指标ID
detailWeight.setIndicatorTopId(evaluationTemplate.getIndicatorTopId());
detailWeight.setFromIndicatorId(a.getRowId());
detailWeight.setToIndicatorId(a.getColId());
detailWeight.setWeight(a.getValue());
detailWeight.setIndicatorParentId(a.getParentId());
detailWeight.setRowNum(a.getRowNum());
detailList.add(detailWeight);
});
evaluationTemplateDetailWeightService.saveIndicatorMatrix(evaluationTemplate.getId(),
evaluationTemplate.getIndicatorTopId(), detailList);
}
}
@Override
@ -88,8 +107,16 @@ public class EvaluationTemplateServiceImpl extends
@Transactional(rollbackFor = Exception.class)
@Override
public void deleteTemplate(EvaluationTemplate evaluationTemplate) {
evaluationTemplateWeightService.deleteEvaluationTemplateWeightWithTemplateId(
evaluationTemplate.getId());
//删除互比信息
evaluationTemplateDetailWeightService.deleteByTemplateIdAndIndicatorTopId(
evaluationTemplate.getId(), evaluationTemplate.getIndicatorTopId());
//删除指标权重信息
evaluationTemplateIndicatorWeightService.deleteByTemplateIdAndIndicatorTopId(
evaluationTemplate.getId(), evaluationTemplate.getIndicatorTopId());
//删除模板
this.removeById(evaluationTemplate.getId());
}
}

View File

@ -6,8 +6,9 @@ import com.hshh.evaluation.entity.EvaluationTemplateWeight;
import com.hshh.evaluation.mapper.EvaluationTemplateWeightMapper;
import com.hshh.evaluation.service.EvaluationTemplateWeightService;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* <p>
@ -23,25 +24,6 @@ public class EvaluationTemplateWeightServiceImpl extends
EvaluationTemplateWeightService {
@Transactional(rollbackFor = Exception.class)
@Override
public void deleteEvaluationTemplateWeightWithTemplateIdAndIndicatorId(Integer templateId,
Integer indicatorTopId, Integer parentId) {
QueryWrapper<EvaluationTemplateWeight> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("template_id", templateId);
queryWrapper.eq("indicator_top_id", indicatorTopId);
queryWrapper.eq("indicator_parent_id", parentId);
this.remove(queryWrapper);
}
@Override
public void deleteEvaluationTemplateWeightWithTemplateId(Integer templateId) {
QueryWrapper<EvaluationTemplateWeight> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("template_id", templateId);
this.remove(queryWrapper);
}
@Override
public List<EvaluationTemplateWeight> queryListByIndicatorParentIdAndTemplateId(
Integer indicatorParentId, Integer templateId) {
@ -51,4 +33,45 @@ public class EvaluationTemplateWeightServiceImpl extends
queryWrapper.eq("indicator_parent_id", indicatorParentId);
return this.list(queryWrapper);
}
@Override
public Map<Integer, List<EvaluationTemplateWeight>> groupByParentIndicatorId(
Integer indicatorParentId, Integer templateId) {
List<EvaluationTemplateWeight> list = this.queryListByIndicatorTopIdAndTemplateId(
indicatorParentId, templateId);
if (!list.isEmpty()) {
return list.stream()
.collect(Collectors.groupingBy(EvaluationTemplateWeight::getIndicatorParentId));
}
return Map.of();
}
@Override
public void saveIndicatorMatrix(Integer templateId, Integer indicatorTopId,
List<EvaluationTemplateWeight> weightList) {
QueryWrapper<EvaluationTemplateWeight> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("template_id", templateId);
queryWrapper.eq("indicator_top_id", indicatorTopId);
this.remove(queryWrapper);
if (weightList != null && !weightList.isEmpty()) {
weightList.forEach(this::save);
}
}
@Override
public void deleteByTemplateIdAndIndicatorTopId(Integer templateId, Integer indicatorTopId) {
QueryWrapper<EvaluationTemplateWeight> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("template_id", templateId);
queryWrapper.eq("indicator_top_id", indicatorTopId);
this.remove(queryWrapper);
}
@Override
public List<EvaluationTemplateWeight> queryListByIndicatorTopIdAndTemplateId(
Integer indicatorTopId, Integer templateId) {
QueryWrapper<EvaluationTemplateWeight> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("template_id", templateId);
queryWrapper.eq("indicator_top_id", indicatorTopId);
return this.list(queryWrapper);
}
}

View File

@ -0,0 +1,26 @@
package com.hshh.indicator.bean;
import com.hshh.indicator.entity.IndicatorEvalItem;
import com.hshh.system.common.bean.JsTree;
import java.util.List;
import lombok.Data;
/**
* [类的简要说明]
* <p>
* [详细描述可选]
* <p>
*
* @author LiDongYU
* @since 2025/7/22
*/
@Data
public class IndicatorEvalBean {
private List<JsTree> jsTreeList;
//评价集范围
private List<IndicatorEvalItem> evalItemList;
//顶级指标ID
private Integer indicatorTopId;
}

View File

@ -0,0 +1,19 @@
package com.hshh.indicator.bean;
import com.hshh.indicator.entity.IndicatorTopLevel;
import com.hshh.indicator.entity.IndicatorTopSet;
import java.util.List;
import lombok.Data;
/**
* 接收全局指标level隶属函数bean.
*
* @author LiDongYU
* @since 2025/7/22
*/
@Data
public class IndicatorSetBean {
private List<IndicatorTopLevel> levels;
private IndicatorTopSet topSet;
}

View File

@ -1,20 +1,23 @@
package com.hshh.indicator.controller;
import com.hshh.indicator.bean.IndicatorEvalBean;
import com.hshh.indicator.entity.Indicator;
import com.hshh.indicator.entity.IndicatorEvalItem;
import com.hshh.indicator.service.IndicatorEvalItemService;
import com.hshh.indicator.service.IndicatorService;
import com.hshh.indicator.service.IndicatorTopLevelService;
import com.hshh.indicator.service.IndicatorTopSetService;
import com.hshh.system.annotation.LogOperation;
import com.hshh.system.common.bean.BaseController;
import com.hshh.system.common.bean.JsTree;
import com.hshh.system.common.bean.OperateResult;
import com.hshh.system.common.enums.ErrorCode;
import com.hshh.system.common.enums.ErrorMessage;
import io.swagger.v3.oas.annotations.Operation;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import javax.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
@ -32,13 +35,14 @@ import org.springframework.web.bind.annotation.ResponseBody;
@RequestMapping("/evaluation")
public class EvaluationController extends BaseController {
/**
* 指标服务类. 查询所有指标列表
* 指标服务类.
*/
@Resource
private IndicatorService indicatorService;
/**
* 评价服务类.
* 指标评价集范围服务类.
*/
@Resource
private IndicatorEvalItemService indicatorEvalItemService;
@ -49,109 +53,87 @@ public class EvaluationController extends BaseController {
* @return indicator/evaluation_list.html
*/
@GetMapping("/evaluationList")
public String evaluationList(Integer topIndicatorId, Integer indicatorId, Model model) {
public String evaluationList(Model model) {
setNavigateTitle(model, "/evaluation/evaluationList");
List<Indicator> rootList = indicatorService.queryRootList();
if (rootList != null && !rootList.isEmpty()) {
if (topIndicatorId != null) {
for (Indicator indicator : rootList) {
if (indicator.getId().equals(topIndicatorId)) {
indicator.setChecked(true);
}
}
} else {
topIndicatorId = rootList.get(0).getId();
rootList.get(0).setChecked(true);
}
}
//查询所有没有孩子的子节点
List<Indicator> indicatorListWithoutChildren = indicatorService.selectNoChildByTopId(
topIndicatorId);
if (!indicatorListWithoutChildren.isEmpty()) {
if (indicatorId != null) {
for (Indicator indicator : indicatorListWithoutChildren) {
if (indicator.getId().equals(indicatorId)) {
indicator.setChecked(true);
}
}
} else {
indicatorListWithoutChildren.get(0).setChecked(true);
}
}
//查询子指标的评价集
List<IndicatorEvalItem> evaluationList = indicatorEvalItemService.queryListByIndicatorId(
indicatorId == null ? (indicatorListWithoutChildren.isEmpty() ? 0
: indicatorListWithoutChildren.get(0).getId()) : indicatorId);
model.addAttribute("evaluationList", evaluationList);
model.addAttribute("rootList", rootList);
model.addAttribute("indicatorListWithoutChildren", indicatorListWithoutChildren);
List<Indicator> bottomList = indicatorService.queryRootList();
model.addAttribute("rootList", bottomList);
return "indicator/evaluation_list";
}
/**
* 导航到评价集增加页面.
* 获取当前指标的评价信息.
*
* @return indicator/add_evaluation.html
* @param id 指标ID
* @return 评价详细信息
*/
@RequestMapping("/evaluationAdd")
@Operation(summary = "导航到评价集增加页面", description = "导航到评价集增加页面")
public String evaluationAdd() {
return "indicator/add_evaluation";
@GetMapping("/{id}")
@ResponseBody
public OperateResult<IndicatorEvalBean> metricIdChange(@PathVariable("id") Integer id) {
IndicatorEvalBean indicatorEvalBean = new IndicatorEvalBean();
//查询底部评价集
indicatorEvalBean.setJsTreeList(evalItemTree(id));
return OperateResult.success(indicatorEvalBean);
}
/**
* 保存评价集.
* 建立底部指标树.
*
* @param item 评价集数据
* @param bindingResult 字段错误信息
* @param id 根指标
* @return 包含底部指标的树
*/
private List<JsTree> evalItemTree(Integer id) {
List<JsTree> jsTreeList = new ArrayList<>();
JsTree jsTree = new JsTree();
jsTree.setText("根指标");
jsTree.setId("0");
jsTreeList.add(jsTree);
jsTree.setChildren(new ArrayList<>());
//查询根部指标所有底部指标
List<Indicator> bottomList = indicatorService.selectNoChildByTopId(
id);
//查询底部指标的评价集范围
Map<Integer, List<IndicatorEvalItem>> evalMap = indicatorEvalItemService.getIndicatorEvalItemMapByIndicatorTopId(
id);
bottomList.forEach(a -> {
JsTree child = new JsTree();
jsTree.getChildren().add(child);
child.setId("tree_" + a.getId());
child.setOriginalId(a.getId());
child.setText(a.getName());
if (evalMap.containsKey(a.getId())) {
List<IndicatorEvalItem> evalItemList = evalMap.get(a.getId());
evalItemList = evalItemList.stream().peek(aa -> {
if (aa.getEqualValue() == null) {
aa.setEqualValue("");
}
}).collect(Collectors.toList());
StringBuilder tipBuilder = new StringBuilder();
evalItemList.forEach(evalItem -> {
tipBuilder.append(evalItem.getEvaluationName()).append(":")
.append(evalItem.getBottomValue())
.append(",").append(evalItem.getTopValue());
child.setText(a.getName() + "(" + tipBuilder + ")");
});
child.getData().put("range", evalItemList);
}
});
return jsTreeList;
}
/**
* 保存评价集隶属函数设置评级集枚举和分值.
*
* @param data 前端数据
* @return 操作结果
*/
@PostMapping("/save")
@ResponseBody
public OperateResult<Object> save(@Valid @RequestBody IndicatorEvalItem item,
BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return errorsInputHandle(bindingResult);
}
indicatorEvalItemService.saveOrUpdate(item);
@LogOperation("保存评价集")
public OperateResult<Void> save(@RequestBody IndicatorEvalBean data) {
indicatorEvalItemService.saveWhole(data);
return OperateResult.success();
}
/**
* 根据id删除评价.
*
* @param id 要删除的id
* @return 操作结果
*/
@GetMapping("/remove/{id}")
@ResponseBody
public OperateResult<Void> remove(@PathVariable("id") Integer id) {
IndicatorEvalItem evaluation = indicatorEvalItemService.getById(id);
if (evaluation == null) {
return OperateResult.error(null, ErrorMessage.ID_NOT_EXIT.getMessage(),
ErrorCode.BUSINESS_ERROR.getCode());
}
indicatorEvalItemService.removeById(id);
return OperateResult.success();
}
/**
* 根据ID查询评价信息.
*
* @param id 评价ID
* @return id对应的评价
*/
@GetMapping("/{id}")
@ResponseBody
public OperateResult<IndicatorEvalItem> view(@PathVariable("id") Integer id) {
IndicatorEvalItem evaluation = indicatorEvalItemService.getById(id);
if (evaluation == null) {
return OperateResult.error(null, ErrorMessage.ID_NOT_EXIT.getMessage(),
ErrorCode.BUSINESS_ERROR.getCode());
}
return OperateResult.success(evaluation, ErrorMessage.SUCCESS.getMessage());
}
}

View File

@ -17,6 +17,7 @@ import com.hshh.model.entity.FormFieldConfig;
import com.hshh.model.entity.ModelDefine;
import com.hshh.model.service.FormFieldConfigService;
import com.hshh.model.service.ModelDefineService;
import com.hshh.system.annotation.LogOperation;
import com.hshh.system.common.bean.BaseController;
import com.hshh.system.common.bean.CheckedBean;
import com.hshh.system.common.bean.OperateResult;
@ -121,7 +122,7 @@ public class IndicatorController extends BaseController {
}
}
model.addAttribute("rootList", rootList);
return "/indicator/list";
return "indicator/list";
}
@ -138,7 +139,7 @@ public class IndicatorController extends BaseController {
List<ModelDefine> modelDefineList = modelDefineService.list();
model.addAttribute("modelDefineList", modelDefineList);
}
return "/indicator/add_indicator";
return "indicator/add_indicator";
}
/**
@ -165,6 +166,7 @@ public class IndicatorController extends BaseController {
* @param bindingResult 字段验证信息
* @return 保存结果
*/
@LogOperation("保存指标")
@PostMapping("/save")
@ResponseBody
@Operation(summary = "保存指标", description = "验证后保存指标")
@ -202,6 +204,7 @@ public class IndicatorController extends BaseController {
*/
@GetMapping("/remove/{id}")
@ResponseBody
@LogOperation("删除指标")
@Operation(summary = "删除指标", description = "根据ID删除指定指标")
public OperateResult<Void> remove(@PathVariable("id") Integer id) {
if (id == null) {
@ -236,14 +239,15 @@ public class IndicatorController extends BaseController {
List<Indicator> rootList = indicatorService.queryRootList();
//设置根指标的选中状态
setChecked(rootList, indicatorTopId, true);
setChecked(rootList, indicatorTopId, false);
// 顶级指标放入session容器
model.addAttribute("rootList", rootList);
// 底层指标放入session容器
model.addAttribute("childrenIndicator", indicatorService.selectNoChildByTopId(
indicatorTopId == null ? (rootList.isEmpty() ? 0 : rootList.get(0).getId())
: indicatorTopId));
if (indicatorTopId != null) {
model.addAttribute("childrenIndicator", indicatorService.selectNoChildByTopId(
indicatorTopId));
}
// form表单和顶级指标对应列表放入session容器
modelForm(model, (indicatorTopId == null ? (rootList.isEmpty() ? 0 : rootList.get(0).getId())
@ -336,6 +340,7 @@ public class IndicatorController extends BaseController {
*/
@PostMapping("/uploadCsv")
@ResponseBody
@LogOperation("上传csv文件")
public OperateResult<List<IndicatorCsvColumn>> uploadCsv(@RequestParam("file") MultipartFile file,
Integer indicatorTopId) throws IOException {
List<IndicatorCsvColumn> list = indicatorService.saveIndicatorTopCsvMapper(indicatorTopId,
@ -355,6 +360,7 @@ public class IndicatorController extends BaseController {
*/
@PostMapping("/indicatorFormMapper")
@ResponseBody
@LogOperation("保存指标和form表单ID的映射关系")
public OperateResult<List<FormFieldConfig>> saveIndicatorFormMapper(
@RequestBody IndicatorFormMapper formMapper) {
indicatorTopMapperService.saveFormMapper(formMapper);

View File

@ -0,0 +1,42 @@
package com.hshh.indicator.controller;
import com.hshh.indicator.bean.IndicatorSetBean;
import com.hshh.system.common.bean.BaseController;
import com.hshh.system.common.bean.OperateResult;
import io.swagger.v3.oas.annotations.tags.Tag;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 指标全局函数设置.
*
* @author LiDongYU
* @since 2025/7/22
*/
@Controller
@RequestMapping("/indicatorSet")
@Tag(name = "指标全局函数设置", description = "指标全局函数设置")
public class IndicatorSetController extends BaseController {
/**
* 指标全局函数默认设置页面.
*
* @param model 数据容器
* @param request 页面请求
* @return indicator_set.html
*/
@GetMapping("/list")
public String list(Model model, HttpServletRequest request) {
setNavigateTitle(model, "/indicatorSet/list");
return "system/indicator/indicator_set";
}
@PostMapping("/save")
public OperateResult<Void> save(@RequestBody IndicatorSetBean indicatorSetBean) {
}
}

View File

@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.hshh.system.common.bean.CheckedBean;
import java.util.List;
import java.util.Map;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import lombok.Data;
@ -45,5 +46,17 @@ public class Indicator extends CheckedBean {
return this.name;
}
@TableField(exist = false)
private double weight;
//当前值
@TableField(exist = false)
private String value;
//隶属度描述
@TableField(exist = false)
private Map<String, String> membership;
//得分
@TableField(exist = false)
private String score;
}

View File

@ -1,6 +1,7 @@
package com.hshh.indicator.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
@ -17,58 +18,69 @@ import java.io.Serializable;
public class IndicatorBottomCsvMapper implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
private Integer indicatorTopId;
private Integer indicatorTopId;
private Integer indicatorId;
private Integer indicatorId;
private Integer csvColumnId;
private Integer csvColumnId;
public Integer getId() {
return id;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getIndicatorTopId() {
return indicatorTopId;
}
public Integer getIndicatorTopId() {
return indicatorTopId;
}
public void setIndicatorTopId(Integer indicatorTopId) {
this.indicatorTopId = indicatorTopId;
}
public void setIndicatorTopId(Integer indicatorTopId) {
this.indicatorTopId = indicatorTopId;
}
public Integer getIndicatorId() {
return indicatorId;
}
public Integer getIndicatorId() {
return indicatorId;
}
public void setIndicatorId(Integer indicatorId) {
this.indicatorId = indicatorId;
}
public void setIndicatorId(Integer indicatorId) {
this.indicatorId = indicatorId;
}
public Integer getCsvColumnId() {
return csvColumnId;
}
public Integer getCsvColumnId() {
return csvColumnId;
}
public void setCsvColumnId(Integer csvColumnId) {
this.csvColumnId = csvColumnId;
}
public void setCsvColumnId(Integer csvColumnId) {
this.csvColumnId = csvColumnId;
}
@Override
public String toString() {
return "IndicatorBottomCsvMapper{" +
@Override
public String toString() {
return "IndicatorBottomCsvMapper{" +
"id = " + id +
", indicatorTopId = " + indicatorTopId +
", indicatorId = " + indicatorId +
", csvColumnId = " + csvColumnId +
"}";
}
}
@TableField(exist = false)
private String csvColumnName;
public String getCsvColumnName() {
return csvColumnName;
}
public void setCsvColumnName(String csvColumnName) {
this.csvColumnName = csvColumnName;
}
}

View File

@ -1,6 +1,7 @@
package com.hshh.indicator.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
@ -28,10 +29,7 @@ public class IndicatorBottomFormMapper implements Serializable {
private Integer formFieldId;
private Integer indicatorTopId;
@TableField(exist = false)
private String filedName;
}

View File

@ -1,6 +1,7 @@
package com.hshh.indicator.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
@ -32,26 +33,13 @@ public class IndicatorEvalItem implements Serializable {
private Integer indicatorId;
private Integer sortOrder;
@NotNull(message = "名称不能为空")
@Size(max = 50, message = "名称不能超过50字符")
private String evaluationName;
private String topValue;
@NotNull(message = "符号不能为空")
@Size(max = 5, message = "名称不能超过5字符")
private String minSymbol;
@NotNull(message = "值不能为空")
@Size(max = 10, message = "值不能超过10字符")
private String minValue;
@NotNull(message = "符号不能为空")
@Size(max = 5, message = "名称不能超过5字符")
private String maxSymbol;
@NotNull(message = "值不能为空")
@Size(max = 10, message = "值不能超过10字符")
private String maxValue;
private String bottomValue;
//除去范围外等于某个字符串值
private String equalValue;
}

View File

@ -1,6 +1,7 @@
package com.hshh.indicator.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
@ -27,4 +28,5 @@ public class IndicatorFormMapper implements Serializable {
}

View File

@ -1,7 +1,8 @@
package com.hshh.indicator.mapper;
import com.hshh.indicator.entity.IndicatorBottomCsvMapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.hshh.indicator.entity.IndicatorBottomCsvMapper;
import java.util.List;
/**
* <p>
@ -13,4 +14,11 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
*/
public interface IndicatorBottomCsvMapperMapper extends BaseMapper<IndicatorBottomCsvMapper> {
/**
* 根据根指标查找对应关系.
*
* @param indicatorTopId 根指标
* @return 对应关系列表
*/
List<IndicatorBottomCsvMapper> selectByIndicatorTopId(Integer indicatorTopId);
}

View File

@ -1,11 +1,12 @@
package com.hshh.indicator.mapper;
import com.hshh.indicator.entity.IndicatorBottomFormMapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.hshh.indicator.entity.IndicatorBottomFormMapper;
import java.util.List;
/**
* <p>
* Mapper 接口
* Mapper 接口
* </p>
*
* @author liDongYu
@ -13,5 +14,12 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
*/
public interface IndicatorBottomMapperMapper extends BaseMapper<IndicatorBottomFormMapper> {
/**
* 查找指标ID和字段名称的对应关系.
*
* @param topId 根指标ID
* @return 对应关系列表
*/
List<IndicatorBottomFormMapper> selectByIndicatorTopId(Integer topId);
}

View File

@ -19,5 +19,13 @@ public interface IndicatorMapper extends BaseMapper<Indicator> {
* @param topId 祖先指标ID
* @return 指标集
*/
public List<Indicator> selectNoChildByTopId(@Param("topId") Integer topId);
List<Indicator> selectNoChildByTopId(@Param("topId") Integer topId);
/**
* 根据评估模板获取指标.
*
* @param templateId 模板 ID
* @return 指标列表
*/
List<Indicator> selectByTemplateId(@Param("templateId") Integer templateId);
}

View File

@ -27,4 +27,12 @@ public interface IndicatorBottomCsvMapperService extends IService<IndicatorBotto
* @param indicatorTopId 顶部指标ID
*/
void deleteByIndicatorTopId(Integer indicatorTopId);
/**
* 根据指标ID获取对应关系.
*
* @param topId 根指标ID
* @return 对应关系列表
*/
List<IndicatorBottomCsvMapper> selectIndicatorToFieldNameListByIndicatorTopId(Integer topId);
}

View File

@ -34,4 +34,11 @@ public interface IndicatorBottomFormMapperService extends IService<IndicatorBott
*/
void saveMapper(List<IndicatorBottomFormMapper> indicatorBottomFormMapperList);
/**
* 查找指标ID和字段名称的对应关系.
*
* @param topId 根指标ID
* @return 对应关系列表
*/
List<IndicatorBottomFormMapper> selectIndicatorToFieldNameListByIndicatorTopId(Integer topId);
}

View File

@ -1,8 +1,10 @@
package com.hshh.indicator.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.hshh.indicator.bean.IndicatorEvalBean;
import com.hshh.indicator.entity.IndicatorEvalItem;
import java.util.List;
import java.util.Map;
/**
* 指标规则表 服务类.
@ -13,19 +15,18 @@ import java.util.List;
public interface IndicatorEvalItemService extends IService<IndicatorEvalItem> {
/**
* 根据指标ID和名称查询记录.
* 根据顶级指标查询返回以子指标为key,value为子指标评价范围集合.
*
* @param indicatorId 指标ID
* @param name 名称
* @return 记录
* @param indicatorTopId 顶级指标
* @return 以子指标分类的评价集范围
*/
List<IndicatorEvalItem> queryListByNameAndIndicatorId(Integer indicatorId, String name);
Map<Integer, List<IndicatorEvalItem>> getIndicatorEvalItemMapByIndicatorTopId(
Integer indicatorTopId);
/**
* 根据子指标查询他的评价集.
* 保存评价集
*
* @param indicatorId 子指标
* @return 评价集
* @param data 前端数据
*/
List<IndicatorEvalItem> queryListByIndicatorId(Integer indicatorId);
void saveWhole(IndicatorEvalBean data);
}

View File

@ -7,6 +7,7 @@ import com.hshh.indicator.entity.IndicatorCsvColumn;
import com.hshh.system.common.bean.JsTree;
import java.io.IOException;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import org.springframework.web.multipart.MultipartFile;
/**
@ -99,4 +100,14 @@ public interface IndicatorService extends IService<Indicator> {
* @return 指标列表topId一致
*/
List<Indicator> queryByTopId(Integer topId);
/**
* 根据评估模板获取指标.
*
* @param templateId 模板 ID
* @return 指标列表
*/
Indicator selectByTemplateId(@Param("templateId") Integer templateId);
}

View File

@ -39,4 +39,10 @@ public class IndicatorBottomCsvMapperServiceImpl extends
queryWrapper.eq("indicator_top_id", indicatorTopId);
remove(queryWrapper);
}
@Override
public List<IndicatorBottomCsvMapper> selectIndicatorToFieldNameListByIndicatorTopId(
Integer topId) {
return this.baseMapper.selectByIndicatorTopId(topId);
}
}

View File

@ -41,4 +41,9 @@ public class IndicatorBottomMapperServiceImpl extends
public void saveMapper(List<IndicatorBottomFormMapper> indicatorBottomFormMapperList) {
indicatorBottomFormMapperList.forEach(this::save);
}
@Override
public List<IndicatorBottomFormMapper> selectIndicatorToFieldNameListByIndicatorTopId(Integer topId) {
return this.baseMapper.selectByIndicatorTopId(topId);
}
}

View File

@ -2,11 +2,18 @@ package com.hshh.indicator.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hshh.indicator.bean.IndicatorEvalBean;
import com.hshh.indicator.entity.IndicatorEvalItem;
import com.hshh.indicator.mapper.IndicatorEvalItemMapper;
import com.hshh.indicator.service.IndicatorEvalItemService;
import com.hshh.indicator.service.IndicatorTopLevelService;
import com.hshh.indicator.service.IndicatorTopSetService;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* 指标规则表 服务实现类.
@ -18,21 +25,31 @@ import org.springframework.stereotype.Service;
public class IndicatorEvalItemServiceImpl extends
ServiceImpl<IndicatorEvalItemMapper, IndicatorEvalItem> implements IndicatorEvalItemService {
@Override
public List<IndicatorEvalItem> queryListByNameAndIndicatorId(Integer indicatorId, String name) {
QueryWrapper<IndicatorEvalItem> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("indicator_id", indicatorId);
queryWrapper.eq("evaluation_name", name);
return this.list(queryWrapper);
@Override
public Map<Integer, List<IndicatorEvalItem>> getIndicatorEvalItemMapByIndicatorTopId(
Integer indicatorTopId) {
QueryWrapper<IndicatorEvalItem> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("indicator_top_id", indicatorTopId);
queryWrapper.orderByAsc("id");
List<IndicatorEvalItem> indicatorEvalItemList = this.list(queryWrapper);
return indicatorEvalItemList.stream()
.collect(Collectors.groupingBy(IndicatorEvalItem::getIndicatorId));
}
@Transactional(rollbackFor = Exception.class)
@Override
public List<IndicatorEvalItem> queryListByIndicatorId(Integer indicatorId) {
public void saveWhole(IndicatorEvalBean data) {
//保存评价集
QueryWrapper<IndicatorEvalItem> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("indicator_id", indicatorId);
queryWrapper.orderByAsc("sort_order", "id");
queryWrapper.eq("indicator_top_id", data.getIndicatorTopId());
remove(queryWrapper);
data.getEvalItemList().forEach(item -> {
item.setIndicatorTopId(data.getIndicatorTopId());
save(item);
});
return this.list(queryWrapper);
}
}

View File

@ -187,4 +187,15 @@ public class IndicatorServiceImpl extends ServiceImpl<IndicatorMapper, Indicator
return this.list(queryWrapper);
}
@Override
public Indicator selectByTemplateId(Integer templateId) {
List<Indicator> list = this.baseMapper.selectByTemplateId(templateId);
if (list.isEmpty()) {
return null;
}
return list.get(0);
}
}

View File

@ -99,10 +99,13 @@ public class ModelDefineController extends BaseController {
modelDefine.getId()));
} else {
fieldList.addAll(formFieldConfigService.getFormFieldConfigByModelId(
modelList.get(0).getId()));
if (!modelList.isEmpty()) {
fieldList.addAll(formFieldConfigService.getFormFieldConfigByModelId(
modelList.get(0).getId()));
modelList.get(0).setChecked(true);
}
modelList.get(0).setChecked(true);
}
}

View File

@ -34,12 +34,10 @@ public class FormFieldConfig implements Serializable {
@NotBlank(message = "字段名称不能为空")
@Size(max = 50, message = "字段名称不能超过50字符")
@Pattern(regexp = "^[A-Za-z_]+$", message = "字段ID只能包含英文字符")
@Pattern(regexp = "^[A-Za-z_0-9]+$", message = "字段ID只能包含英文和数字字符")
private String fieldName;
@NotBlank(message = "字段ID不能为空")
@Size(max = 50, message = "字段ID不能超过50字符")
@Pattern(regexp = "^[A-Za-z_]+$", message = "字段ID只能包含英文字符")
private String fieldId;
@NotBlank(message = "字段标签不能为空")
@Size(max = 50, message = "字段标签不能超过50字符")

View File

@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.hshh.model.entity.FormValue;
import com.hshh.system.common.bean.PaginationBean;
import java.util.List;
import org.apache.ibatis.annotations.Param;
/**
* 动态表单实际对象记录表 Mapper 接口.
@ -28,4 +29,12 @@ public interface FormValueMapper extends BaseMapper<FormValue> {
* @return 查询结果
*/
Long count(PaginationBean request);
/**
* 查询指定id的记录.
*
* @param ids id集合
* @return 结果列表
*/
List<FormValue> selectByIds(@Param("ids") List<Integer> ids);
}

View File

@ -3,6 +3,7 @@ package com.hshh.model.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.hshh.model.entity.FormFieldConfig;
import java.util.List;
import java.util.Map;
import javax.validation.constraints.NotNull;
/**
@ -19,7 +20,7 @@ public interface FormFieldConfigService extends IService<FormFieldConfig> {
* @param modelId 模型ID
* @return 字段列表
*/
public List<FormFieldConfig> getFormFieldConfigByModelId(Integer modelId);
List<FormFieldConfig> getFormFieldConfigByModelId(Integer modelId);
/**
* 查询符合条件的字段列表.
@ -30,6 +31,14 @@ public interface FormFieldConfigService extends IService<FormFieldConfig> {
* @param id id
* @return 结果列表
*/
public List<FormFieldConfig> getFormFieldConfigByLabelOrNameOrId(@NotNull Integer modelId,
List<FormFieldConfig> getFormFieldConfigByLabelOrNameOrId(@NotNull Integer modelId,
@NotNull String label, @NotNull String name, @NotNull String id);
/**
* 获取基础设施的字段信息按照map返回linkedHashMap key=fieldName,value=fieldLabel.
*
* @param modelId 基础设施ID
* @return 字段map
*/
Map<String, String> getHeaderMap(Integer modelId);
}

View File

@ -12,6 +12,7 @@ import java.util.List;
* @since 2025-08-01
*/
public interface FormValueService extends IService<FormValue> {
/**
* 查询列表.
*
@ -27,4 +28,12 @@ public interface FormValueService extends IService<FormValue> {
* @return 查询结果
*/
Long count(PaginationBean request);
/**
* 查询指定id的记录.
*
* @param ids id集合
* @return 结果列表
*/
List<FormValue> selectByIds(List<Integer> ids);
}

View File

@ -5,7 +5,9 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hshh.model.entity.FormFieldConfig;
import com.hshh.model.mapper.FormFieldConfigMapper;
import com.hshh.model.service.FormFieldConfigService;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.stereotype.Service;
/**
@ -36,4 +38,14 @@ public class FormFieldConfigServiceImpl extends
.eq("field_id", id));
return this.list(queryWrapper);
}
@Override
public Map<String, String> getHeaderMap(Integer modelId) {
Map<String, String> headerMap = new HashMap<>();
List<FormFieldConfig> list = getFormFieldConfigByModelId(modelId);
list.forEach(formFieldConfig -> {
headerMap.put(formFieldConfig.getFieldName(), formFieldConfig.getFieldLabel());
});
return headerMap;
}
}

View File

@ -27,4 +27,9 @@ public class FormValueServiceImpl extends ServiceImpl<FormValueMapper, FormValue
public Long count(PaginationBean request) {
return this.baseMapper.count(request);
}
@Override
public List<FormValue> selectByIds(List<Integer> ids) {
return this.baseMapper.selectByIds(ids);
}
}

View File

@ -0,0 +1,44 @@
package com.hshh.thread;
import com.alibaba.fastjson2.JSON;
import com.hshh.evaluation.bean.PageEvaluationRequest;
import com.hshh.evaluation.service.impl.EvaluationProjectServiceImpl;
import com.hshh.system.Global;
import com.hshh.system.common.bean.SpringContextHolder;
import com.hshh.system.common.cmd.CmdEnum;
import com.hshh.system.common.cmd.CmdInfo;
import java.util.Objects;
import lombok.extern.slf4j.Slf4j;
/**
* 处理系统中的任务线程.
*
* @author LiDongYU
* @since 2025/7/22
*/
@Slf4j
public class HandleEvaluationCmdThread implements Runnable {
@Override
public void run() {
while (!Thread.interrupted()) {
try {
CmdInfo cmd = Global.cmdQueue.take();
if (Objects.requireNonNull(cmd.getType()) == CmdEnum.EVALUATION) {
evaluation(cmd);
}
} catch (Exception e) {
log.error("error:", e);
}
}
}
//评估
private void evaluation(CmdInfo cmd) {
SpringContextHolder.getBean(EvaluationProjectServiceImpl.class)
.evaluate(JSON.parseObject(cmd.getCmd(),
PageEvaluationRequest.class), cmd.getUserId());
}
}

View File

@ -22,15 +22,17 @@ spring:
max-request-size: 20MB
mybatis-plus:
mapper-locations: classpath*:/mapper/**/*.xml
configuration:
database-id: mysql
# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
database-id: mysql
# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
id-type: auto

View File

@ -0,0 +1,72 @@
<?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.hshh.evaluation.mapper.EvaluationCsvDataMapper">
<select id="list" resultType="com.hshh.evaluation.entity.EvaluationCsvData"
parameterType="com.hshh.system.common.bean.PaginationBean" databaseId="mysql">
SELECT
@rownum := @rownum + 1 AS seq,
t.*
FROM (
SELECT * FROM m_data_evaluation_csv_data
<where>
<if test="randomKey!=null">
and random_key = #{randomKey}
</if>
<if test="search != null and search !='' ">
and raw_data LIKE CONCAT('%',#{search},'%')
</if>
</where>
order by id asc ) t, ( SELECT @rownum := #{start} ) r limit
#{start},#{pageSize}
</select>
<select id="list" resultType="com.hshh.evaluation.entity.EvaluationCsvData"
parameterType="com.hshh.system.common.bean.PaginationBean" databaseId="dm">SELECT
t.seq,
t.*
FROM (
SELECT
ROW_NUMBER() OVER (ORDER BY id ASC) AS seq,
a.*
FROM m_data_evaluation_csv_data a
<where>
<if test="randomKey!=null">
and random_key = #{randomKey}
</if>
<if test="search != null and search !='' ">
and raw_data LIKE '%'||#{search}||'%'
</if>
</where>
) t
WHERE t.seq > #{start} AND t.seq &lt;= (#{start} + #{pageSize})
</select>
<select id="count" resultType="java.lang.Long" databaseId="dm">
select count(id) from m_data_evaluation_csv_data
<where>
<if test="randomKey!=null">
and random_key = #{randomKey}
</if>
<if test="search != null and search !=''">
and (raw_data LIKE '%'||#{search}||'%')
</if>
</where>
</select>
<select id="count" resultType="java.lang.Long" databaseId="mysql">
select count(id) from m_data_evaluation_csv_data
<where>
<if test="randomKey!=null">
and random_key = #{randomKey}
</if>
<if test="search != null and search !=''">
and (raw_data LIKE CONCAT('%',#{search},'%'))
</if>
</where>
</select>
<select id="selectByIds" resultType="com.hshh.evaluation.entity.EvaluationCsvData">
SELECT * FROM m_data_evaluation_csv_data
WHERE id IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
</mapper>

View File

@ -0,0 +1,12 @@
<?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.hshh.evaluation.mapper.EvaluationHistoryMapper">
<select id="queryRecentByProjectId" resultType="com.hshh.evaluation.entity.EvaluationHistory">
SELECT *
FROM m_data_evaluation_history
where project_id = #{projectId}
ORDER BY id DESC
LIMIT 1;
</select>
</mapper>

View File

@ -50,4 +50,12 @@
</if>
</where>
</select>
<select id="selectModelIdByProjectId" resultType="java.lang.Long">
SELECT t3.indicator_model_id
FROM m_data_evaluation_project t1
LEFT JOIN m_data_evaluation_template t2 ON t1.template_id = t2.id
LEFT JOIN m_data_indicator_form t3 ON t2.indicator_top_id = t3.indicator_top_id
where t1.id = #{projectId}
and t3.indicator_model_id is not null
</select>
</mapper>

View File

@ -0,0 +1,5 @@
<?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.hshh.evaluation.mapper.EvaluationTemplateIndicatorWeightMapper">
</mapper>

View File

@ -50,4 +50,5 @@
</if>
</where>
</select>
</mapper>

View File

@ -9,8 +9,8 @@
FROM (
SELECT * FROM m_data_model_field_value
<where>
<if test="id!=null">
and model_define_id = #{id}
<if test="businessKey!=null">
and model_define_id = #{businessKey}
</if>
<if test="search != null and search !='' ">
and model_data LIKE CONCAT('%',#{search},'%')
@ -29,8 +29,8 @@
a.*
FROM m_data_model_field_value a
<where>
<if test="id!=null">
and model_define_id = #{id}
<if test="businessKey!=null">
and model_define_id = #{businessKey}
</if>
<if test="search != null and search !='' ">
and model_data LIKE '%'||#{search}||'%'
@ -42,8 +42,8 @@
<select id="count" resultType="java.lang.Long" databaseId="dm">
select count(id) from m_data_model_field_value
<where>
<if test="id!=null">
and model_define_id = #{id}
<if test="businessKey!=null">
and model_define_id = #{businessKey}
</if>
<if test="search != null and search !=''">
and (model_data LIKE '%'||#{search}||'%')
@ -53,12 +53,19 @@
<select id="count" resultType="java.lang.Long" databaseId="mysql">
select count(id) from m_data_model_field_value
<where>
<if test="id!=null">
and model_define_id = #{id}
<if test="businessKey!=null">
and model_define_id = #{businessKey}
</if>
<if test="search != null and search !=''">
and (model_data LIKE CONCAT('%',#{search},'%'))
</if>
</where>
</select>
<select id="selectByIds" resultType="com.hshh.model.entity.FormValue">
SELECT * FROM m_data_model_field_value
WHERE id IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
</mapper>

View File

@ -1,5 +1,14 @@
<?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.hshh.indicator.mapper.IndicatorBottomCsvMapperMapper">
<select id="selectByIndicatorTopId" resultType="com.hshh.indicator.entity.IndicatorBottomCsvMapper">
SELECT
t1.indicator_id,
t2.csv_column_name
FROM
m_data_indicator_bottom_csv_mapper t1
LEFT JOIN m_data_indicator_csv_column t2 on t1.csv_column_id=t2.id
where t1.indicator_top_id=#{id}
</select>
</mapper>

View File

@ -2,5 +2,13 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hshh.indicator.mapper.IndicatorBottomMapperMapper">
<select id="selectByIndicatorTopId" resultType="com.hshh.indicator.entity.IndicatorBottomFormMapper">
SELECT
t1.indicator_id,
t2.field_id as filedName
FROM
m_data_indicator_bottom_form_mapper t1
LEFT JOIN m_data_model_field_config t2 on t1.form_field_id=t2.id
where t1.indicator_top_id=#{indicatorTopId}
</select>
</mapper>

View File

@ -5,4 +5,5 @@
select t.indicator_model_id
from m_data_indicator_form t where t.indicator_top_id=#{topId}
</select>
</mapper>

View File

@ -10,4 +10,12 @@
WHERE t2.top_id = #{topId} and t2.parent_id IS NOT NULL)
order by sort_order
</select>
<select id="selectByTemplateId" resultType="com.hshh.indicator.entity.Indicator">
SELECT
t1.*
FROM
m_data_indicator t1
inner join m_data_evaluation_template t2 ON t1.id = t2.indicator_top_id
where t2.id=#{templateId}
</select>
</mapper>

View File

@ -4,7 +4,6 @@
*/
function removeValidCss(formId) {
let form = document.getElementById(formId);
let elements = form.elements;
for (const element of elements) {
@ -63,17 +62,19 @@ function openDialog(id) {
return;
}
}
function disabledForm(formId) {
let form = document.getElementById(formId);
// 只读 input 和 textarea
form.querySelectorAll('input, textarea').forEach(function(el) {
form.querySelectorAll('input, textarea').forEach(function (el) {
el.readOnly = true;
});
// 禁用 select 和 button
form.querySelectorAll('select').forEach(function(el) {
form.querySelectorAll('select').forEach(function (el) {
el.disabled = true;
});
}
/**
* 自动填充表单根据 JSON 数据中的 key 和嵌套对象自动拼接 name 属性进行匹配
* @param {string} formId - 表单的 ID例如 "myForm"
@ -88,8 +89,6 @@ function fillForm(formId, data) {
return;
}
/**
* 递归处理 JSON 对象, 将嵌套对象的 key 拼接为点连接的形式
* @param {Object} obj - 当前处理的 JSON 对象
@ -102,7 +101,6 @@ function fillForm(formId, data) {
var fullKey = prefix ? prefix + "." + key : key;
var value = obj[key];
// 如果值为对象,但不为数组,则递归处理
if (value && typeof value === "object" && !Array.isArray(value)) {
@ -130,7 +128,7 @@ function fillForm(formId, data) {
field.value = value;
}
}else{
} else {
}
}
@ -220,7 +218,7 @@ function common_batchRemove() {
}
//分页相关
//普通静态数据分页相关
function _next() {
let currentPageNum = document.getElementById("_currentPage").value;
_setPage(parseInt(currentPageNum) + 1);
@ -246,6 +244,23 @@ function _resetPageNoSearch() {
_search();
}
//动态数据,动态表头,动态表格 header=map record=list(map)
function _dynamic_next(form) {
let pageNum = document.forms[form]["currentPage"].value;
document.forms[form]["currentPage"].value = parseInt(pageNum)+1;
dynamic_search();
}
function _dynamic_pre(form) {
let pageNum = document.forms[form]["currentPage"].value;
document.forms[form]["currentPage"].value = parseInt(pageNum)-1;
dynamic_search();
}
function _dynamic_resetPageNoSearch(form){
document.forms[form]["currentPage"].value = 1;
dynamic_search();
}
document.addEventListener('DOMContentLoaded', function () {
if (document.getElementById('uploadIcon') && document.getElementById(
'fileInput')) {
@ -352,13 +367,15 @@ function hideContextMenu() {
}
}
function generateDraftKey() {
// 优先用 crypto.randomUUID 生成
if (window.crypto && window.crypto.randomUUID) {
return 'tpl:new:' + window.crypto.randomUUID();
}
// 兼容老浏览器
return 'tpl:new:' + Date.now().toString(36) + Math.random().toString(36).substring(2, 10);
return 'tpl:new:' + Date.now().toString(36) + Math.random().toString(
36).substring(2, 10);
}
function mapToObject(map) {
@ -368,10 +385,54 @@ function mapToObject(map) {
}
return obj;
}
function showFieldErrorTip(formId,fieldId,message){
function showFieldErrorTip(formId, fieldId, message) {
let formElement = document.getElementById(formId);
formElement.elements[fieldId].classList.add(
"is-invalid");
document.getElementById(
fieldId + "_error_tip").innerHTML =message;
fieldId + "_error_tip").innerHTML = message;
}
function connectWs(url, {
onOpen = () => {},
onClose = () => {},
onMessage = (msg) => {},
onError = (err) => {}
} = {}) {
url = wsUrl(url);
const ws = new WebSocket(url);
ws.onopen = (ev) => { onOpen(ev, ws); };
ws.onclose = (ev) => { onClose(ev, ws); };
ws.onmessage = (ev) => { onMessage(ev.data, ws); };
ws.onerror = (err) => { onError(err, ws); };
return ws;
}
function wsUrl(path) {
const loc = window.location; // 当前页面的地址
const scheme = loc.protocol === "https:" ? "wss:" : "ws:";
return scheme + "//" + loc.host + path;
}
function formToQueryString(formId) {
const form = document.getElementById(formId);
const formData = new FormData(form);
const params = new URLSearchParams();
for (let [key, value] of formData.entries()) {
// 排除文件输入
const field = form.elements[key];
if (field && field.type === "file") continue;
params.append(key, value);
}
return params.toString(); // "key1=val1&key2=val2"
}
function encryptPassword(password, aesKeyFromBackend,aesIVFromBackend) {
var key = CryptoJS.enc.Hex.parse(aesKeyFromBackend); // 32字节
var iv = CryptoJS.enc.Hex.parse(aesIVFromBackend); // 16字节
return CryptoJS.AES.encrypt(password, key, { iv: iv, mode: CryptoJS.mode.CBC }).toString();
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -3,83 +3,82 @@
<div class="container-xl">
<!-- 面包屑导航 -->
<div th:replace="fragments/dialog::navigateDialog(${chainMenuList})"></div>
<div class="row">
<div class="card">
<div class="card-header">
<ul class="nav nav-tabs card-header-tabs" data-bs-toggle="tabs" role="tablist">
<li class="nav-item" role="presentation" th:each="item ,stat: ${modelDefineList}">
<a
href="javascript:void(0)"
th:class="'nav-link '+${item.checked?'active':''}"
data-bs-toggle="tab"
th:text="${item.getModelName()}"
th:onclick="|_tabChange('${item.id}')|"></a>
</li>
</ul>
</div>
<div class="card-body">
<div class="tab-content" id="tab-Content">
<div class="tab-pane active show" role="tabpanel">
<div class="row">
<div class="ms-auto text-end">
<a href="javascript:void(0)" class="btn btn-primary"
th:onclick="|addModeDataRecord('${currentModelDefine?.id}')|">
<!-- Download SVG icon from http://tabler-icons.io/i/plus -->
<svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24"
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none"
stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M12 5l0 14"></path>
<path d="M5 12l14 0"></path>
</svg>
新增
</a>
</div>
</div>
<div>
<div th:replace="fragments/dialog::searchConditionDialog(${condition})"></div>
<div class="table-responsive">
<table class="table card-table table-vcenter text-nowrap datatable">
<thead>
<tr>
<th class="w-1">No.
<!-- Download SVG icon from http://tabler-icons.io/i/chevron-up -->
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-sm icon-thick"
width="24" height="24" viewBox="0 0 24 24" stroke-width="2"
stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M6 15l6 -6l6 6"></path>
</svg>
</th>
<th th:each="item:${headerMap}" th:text="${item.value}"></th>
<th></th>
</tr>
</thead>
<tbody>
<tr th:each="item : ${result.getList()}">
<td th:text="${item['seq']}"></td>
<td th:each="entry : ${headerMap}" th:text="${item[entry.key]}"></td>
<td>
<a href="javascript:void(0)"
th:onclick="|editModelFieldRecord('${item['id']}')|">编辑</a>
<a href="javascript:void(0)"
th:onclick="|removeModelFieldRecord('${item['id']}')|">删除</a>
</td>
</tr>
</tbody>
</table>
</div>
<div th:replace="fragments/dialog::paginationDialog(${result})"></div>
</div>
</div>
<div class="row g-4 mb-4">
<div class="col-8">
<div class="mb-3 row">
<label class="col-2 col-form-label">基础设施</label>
<div class="col-6">
<label>
<select class="form-select" id="modelId" onchange="_tabChange(this)">
<option>--请选择基础设施--</option>
<option th:each="item ,stat: ${modelDefineList}" th:text="${item.getModelName()}"
th:value="${item.id}" th:selected="${item.checked}"></option>
</select>
</label>
</div>
</div>
</div>
<div class="col-12">
<div class="card">
<div class="card-header">
<div class="card-actions">
<a href="javascript:void(0)" class="btn btn-primary" onclick="addModeDataRecord()">
<!-- Download SVG icon from http://tabler-icons.io/i/plus -->
<svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24"
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none"
stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M12 5l0 14"></path>
<path d="M5 12l14 0"></path>
</svg>
新增
</a>
</div>
</div>
<div th:replace="fragments/dialog::searchConditionDialog(${condition})"></div>
<div class="table-responsive">
<table class="table card-table table-vcenter text-nowrap datatable">
<thead>
<tr>
<th class="w-1">No.
<!-- Download SVG icon from http://tabler-icons.io/i/chevron-up -->
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-sm icon-thick"
width="24" height="24" viewBox="0 0 24 24" stroke-width="2"
stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M6 15l6 -6l6 6"></path>
</svg>
</th>
<th th:each="item:${headerMap}" th:text="${item.value}"></th>
<th></th>
</tr>
</thead>
<tbody>
<tr th:each="item : ${result?.getList()}">
<td th:text="${item['seq']}"></td>
<td th:each="entry : ${headerMap}" th:text="${item[entry.key]}"></td>
<td>
<a href="javascript:void(0)"
th:onclick="|editModelFieldRecord('${item['id']}')|">编辑</a>
<a href="javascript:void(0)"
th:onclick="|removeModelFieldRecord('${item['id']}')|">删除</a>
</td>
</tr>
</tbody>
</table>
</div>
<div th:if="${result!=null}" th:replace="fragments/dialog::paginationDialog(${result})"></div>
</div>
</div>
</div>
</div>
@ -91,12 +90,12 @@
</form>
<form id="dataExtendForm">
<input type="hidden" name="id" id="id">
<input type="hidden" name="modelDefineId" id="modelDefineId" th:value="${currentModelDefine.id}">
<input type="hidden" name="modelDefineId" id="modelDefineId" th:value="${currentModelDefine?.id}">
</form>
<script>
//增加模型数据的实际表单记录
function addModeDataRecord(modelId, data) {
function addModeDataRecord( data) {
let modelId = document.getElementById( "modelId" ).value;
let url = document.getElementById("_rootPath").value + "data/getForm/" + modelId;
let http = new HttpClient();
http.get(url, function (error, res, xhr) {
@ -130,14 +129,15 @@
http.post(url, postObj, function (error, res, xhr) {
closeDialog("modal-full-width");
document.getElementById("_data_list").setAttribute("hx-vals",
JSON.stringify({id: postObj.modelDefineId}))
JSON.stringify({businessKey: postObj.modelDefineId}))
document.getElementById("_data_list").click();
}, "dataRecordForm", "modal-full-width")
}
function _tabChange(id) {
function _tabChange(obj) {
document.getElementById("_data_list").setAttribute("hx-vals",
JSON.stringify({id: id}));
JSON.stringify({businessKey: obj.value}));
document.getElementById("_data_list").click();
}
@ -174,7 +174,7 @@
if (!error) {
document.getElementById("dataExtendForm")['id'].value = res.result.id;
addModeDataRecord(res.result.modelDefineId, res.result);
addModeDataRecord( res.result);
}
}, "dataRecordForm")
}

View File

@ -0,0 +1,12 @@
<div class="page page-center">
<div class="container-tight py-4">
<div class="empty">
<div class="empty-header">404</div>
<p class="empty-title">页面不存在</p>
</div>
</div>
</div>

View File

@ -0,0 +1,13 @@
<div class="page page-center">
<div class="container-tight py-4">
<div class="empty">
<div class="empty-header">500</div>
<p class="empty-title" th:text="${errorMessage}"></p>
</div>
</div>
</div>

View File

@ -92,17 +92,40 @@
</div>
</div>
</div>
<div th:fragment="addFullScreenFormDialogNoBtn" id="addFormDialog-no-btn">
<div class="modal modal-blur fade" id="modal-full-width-no-btn" tabindex="-1" role="dialog"
aria-hidden="true" >
<div class="modal-dialog modal-full-width modal-dialog-centered modal-dialog-scrollable"
role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="addFormDialog-model-title-no-btn"></h5>
<button type="button" class="btn-close"
onclick="closeDialog('modal-full-width-no-btn')"></button>
</div>
<div class="modal-body" id="add-full-screen-form-modal-body-no-btn" >
</div>
</div>
</div>
</div>
</div>
<!-- form add对话框 简单form操作居中对话框-->
<div th:fragment="addSimpleFormDialog" id="addSimpleFormDialog">
<div class="modal modal-blur fade" id="simple-form-model" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal modal-blur fade" id="simple-form-model" tabindex="-1" role="dialog" aria-hidden="true" >
<div class="modal-dialog modal-dialog-centered" role="document" style="min-width: 50em;">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="SimpleFormDialog_title"></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" onclick="closeDialog('simple-form-model')"></button>
</div>
<div class="modal-body" id="simpleFormBody">
<div class="modal-body" id="simpleFormBody" >
</div>
<div class="modal-footer">
@ -113,12 +136,12 @@
</div>
</div>
<div th:fragment="paginationDialog(data)" id="paginationDialog">
<div class="card-footer d-flex align-items-center" th:if="${data.total>0}">
<div class="card-footer d-flex align-items-center" th:if="${data?.total>0}">
<p class="m-0 text-muted"><span
th:text="${data.total}"></span>
th:text="${data?.total}"></span>
记录</p>
<ul class="pagination m-0 ms-auto">
<li th:class="${data.isHasPrevious()}?'page-item':'page-item disabled'">
<li th:class="${data?.isHasPrevious()}?'page-item':'page-item disabled'">
<a class="page-link" href="#" tabindex="-1" aria-disabled="true"
onclick="_pre()">
<!-- Download SVG icon from http://tabler-icons.io/i/chevron-left -->
@ -158,7 +181,7 @@
<div class="card-body border-bottom py-3">
<form id="searchForm">
<input type="hidden" id="_currentPage" name="currentPage"
th:value="${condition.currentPage}"/>
th:value="${condition?.currentPage}"/>
<div class="card-body border-bottom py-3">
<div class="d-flex">
@ -166,16 +189,16 @@
显示
<div class="mx-2 d-inline-block">
<input type="text" class="form-control form-control-sm"
th:value="${condition.pageSize}" size="3"
th:value="${condition?.pageSize}" size="3"
name="pageSize">
</div>
对象
记录/每页
</div>
<div class="ms-auto text-muted">
<div class="input-group mb-2">
<input type="text" class="form-control" placeholder="名称/编码" id="search"
name="search" th:value="${condition.search}">
name="search" th:value="${condition?.search}">
<button class="btn" type="button" onclick="_resetPageNoSearch()">查询</button>
</div>
</div>
@ -196,4 +219,84 @@
</ol>
</nav>
</div>
<div th:fragment="datasetForListMap(formId,headerMap,result,condition)" id="dataset_list_map_table_page" style="min-height: 50em;max-height: 50em;overflow-y: scroll;">
<input type="hidden" name="currentPage"
th:value="${condition?.currentPage}"/>
<div class="card-body border-bottom py-3">
<div class="d-flex">
<div class="text-muted">
显示
<div class="mx-2 d-inline-block">
<input type="text" class="form-control form-control-sm" name="pageSize" th:value="${condition?.pageSize}" size="3" aria-label="Invoices count">
</div>
记录/每页
</div>
<div class="ms-auto text-muted">
<div class="input-group mb-2">
<label>
<input type="text" class="form-control" placeholder="名称/编码"
name="search" th:value="${condition?.search}">
</label>
<button class="btn" type="button" th:attr="data-form-id=${#strings.escapeJavaScript(formId)}" onclick="_dynamic_resetPageNoSearch(this.dataset.formId)">查询</button>
</div>
</div>
</div>
</div>
<div class="table-responsive" style="min-height: 20em;">
<table class="table card-table table-vcenter text-nowrap datatable">
<thead>
<tr>
<th></th>
<th th:each="item:${headerMap}" th:text="${item.value}"></th>
</tr>
</thead>
<tbody>
<tr th:each="item : ${result?.getList()}">
<td><label>
<input type="checkbox" th:value="${item['id']}" name="list_map_table_checkbox">
</label></td>
<td th:each="entry : ${headerMap}" th:text="${item[entry.key]}"></td>
</tr>
</tbody>
</table>
</div>
<div class="card-footer d-flex align-items-center">
<ul class="pagination m-0 ms-auto">
<li th:class="${result?.isHasPrevious()}?'page-item':'page-item disabled'">
<a class="page-link" href="#" tabindex="-1" aria-disabled="true" th:attr="data-form-id=${#strings.escapeJavaScript(formId)}"
onclick="_dynamic_pre(this.dataset.formId)">
<!-- Download SVG icon from http://tabler-icons.io/i/chevron-left -->
<svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24"
height="24"
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"
fill="none"
stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M15 6l-6 6l6 6"/>
</svg>
</a>
</li>
<li th:class="${result?.isHasNext()}?'page-item':'page-item disabled'" >
<a class="page-link" href="#" onclick="_dynamic_next(this.dataset.formId)" th:attr="data-form-id=${#strings.escapeJavaScript(formId)}">
<svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24"
height="24"
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"
fill="none"
stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M9 6l6 6l-6 6"/>
</svg>
</a>
</li>
</ul>
</div>
</div>

Some files were not shown because too many files have changed in this diff Show More