1. 指标子集映射

This commit is contained in:
李玉东 2025-08-11 15:07:39 +08:00
parent d2d59f8ea2
commit 8913652362
25 changed files with 548 additions and 157 deletions

View File

@ -0,0 +1,19 @@
package com.hshh.indicator.bean;
import com.hshh.indicator.entity.IndicatorBottomCsvMapper;
import com.hshh.indicator.entity.IndicatorBottomFormMapper;
import java.util.List;
import lombok.Data;
/**
* 映射保存bean.
*
* @author LiDongYU
* @since 2025/7/22
*/
@Data
public class IndicatorMapperBean {
private Integer indicatorTopId;
private List<IndicatorBottomCsvMapper> csvList;
private List<IndicatorBottomFormMapper> formList;
}

View File

@ -1,18 +0,0 @@
package com.hshh.indicator.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.stereotype.Controller;
/**
* <p>
* 保存底部指标和csv列的映射关系 前端控制器
* </p>
*
* @author liDongYu
* @since 2025-08-10
*/
@Controller
@RequestMapping("/indicator/indicatorBottomCsvMapper")
public class IndicatorBottomCsvMapperController {
}

View File

@ -1,9 +1,11 @@
package com.hshh.indicator.controller;
import com.hshh.indicator.bean.IndicatorMapperBean;
import com.hshh.indicator.entity.Indicator;
import com.hshh.indicator.entity.IndicatorBottomCsvMapper;
import com.hshh.indicator.entity.IndicatorBottomFormMapper;
import com.hshh.indicator.entity.IndicatorCsv;
import com.hshh.indicator.entity.IndicatorCsvColumn;
import com.hshh.indicator.entity.IndicatorFormMapper;
import com.hshh.indicator.service.IndicatorBottomCsvMapperService;
import com.hshh.indicator.service.IndicatorBottomFormMapperService;
@ -273,67 +275,76 @@ public class IndicatorController extends BaseController {
}
/**
* 导航到mapper页面. 1. 顶级指标放入session . 2. 底层指标放入session容器. 3. form表单字段放入session容器 .
* 4.form表单列表放入session容器 5. csv对应session容器 6. csv字段session容器 7. 底部指标对应form字段session容器 8.
* 底部指标对应csv列session容器.
* 导航到mapper页面.
*
* @param model session容器
* @param formId 表单ID
* @param csvId csvID
* @param indicatorTopId 指标顶级ID
* @return /indicator/mapper.html
* @return /indicator/indicator_mapper.html
*/
@GetMapping("/mapper")
public String mapper(Model model, final Integer indicatorTopId, final Integer formId,
final Integer csvId) {
public String mapper(Model model, final Integer indicatorTopId) {
setNavigateTitle(model, "/indicator/mapper"); //设置导航
//查询所有根指标
List<Indicator> rootList = indicatorService.queryRootList();
List<Indicator> rootList = indicatorService.queryRootList(); //查询所有根指标
setChecked(rootList, indicatorTopId); //设置根指标的选中状态
model.addAttribute("rootList", rootList); // 1. 顶级指标放入session容器
model.addAttribute("childrenIndicator",
indicatorService.selectNoChildByTopId(indicatorTopId)); // 2. 底层指标放入session容器
//处理form表单
modelForm(model, indicatorTopId, formId); // 3. form表单字段放入session容器.4.form表单列表放入session容器
//处理csv file
csvFile(model, indicatorTopId); // 5. csv对应session容器 6. csv字段session容器
//设置根指标的选中状态
setChecked(rootList, indicatorTopId, true);
// 顶级指标放入session容器
model.addAttribute("rootList", rootList);
// 底层指标放入session容器
model.addAttribute("childrenIndicator", indicatorService.selectNoChildByTopId(
indicatorTopId == null ? (rootList.isEmpty() ? 0 : rootList.get(0).getId())
: indicatorTopId));
// form表单和顶级指标对应列表放入session容器
modelForm(model, (indicatorTopId == null ? (rootList.isEmpty() ? 0 : rootList.get(0).getId())
: indicatorTopId));
// csv字段和顶级指标session容器
csvFile(model, (indicatorTopId == null ? (rootList.isEmpty() ? 0 : rootList.get(0).getId())
: indicatorTopId));
//放入底部指标和form表单字段对应关系到容器
List<IndicatorBottomFormMapper> bottomList = bottomFormMapperService.queryListByIndicatorId(
indicatorTopId);
Map<Integer, IndicatorBottomFormMapper> bottomFormMap = bottomList.stream()
.collect(Collectors.toMap(IndicatorBottomFormMapper::getFormFieldId, a -> a));
model.addAttribute("bottomFormMap", bottomFormMap); // 7. 底部指标对应form字段session容器
indicatorTopId == null ? (rootList.isEmpty() ? 0 : rootList.get(0).getId())
: indicatorTopId);
Map<Integer, Integer> bottomFormMap = bottomList.stream()
.filter(a -> a.getFormFieldId() != null)
.collect(
Collectors.toMap(IndicatorBottomFormMapper::getIndicatorId,
IndicatorBottomFormMapper::getFormFieldId));
model.addAttribute("bottomFormMap", bottomFormMap);
//放入底部指标和csv列对应关系到容器
List<IndicatorBottomCsvMapper> bottomCsvColumnList = bottomCsvMapperService.queryListByIndicatorTopId(
indicatorTopId);
Map<Integer, IndicatorBottomCsvMapper> bottomCsvColumnMap = bottomCsvColumnList.stream()
.collect(Collectors.toMap(IndicatorBottomCsvMapper::getCsvColumnId, a -> a));
model.addAttribute("bottomCsvColumnMap", bottomCsvColumnMap); // 8.底部指标对应csv列session容器
(indicatorTopId == null ? (rootList.isEmpty() ? 0 : rootList.get(0).getId())
: indicatorTopId));
Map<Integer, Integer> bottomCsvColumnMap = bottomCsvColumnList.stream()
.filter(a -> a.getCsvColumnId() != null)
.collect(
Collectors.toMap(IndicatorBottomCsvMapper::getIndicatorId,
IndicatorBottomCsvMapper::getCsvColumnId));
model.addAttribute("bottomCsvColumnMap", bottomCsvColumnMap);
return "/indicator/mapper";
return "indicator/indicator_mapper";
}
private void modelForm(Model model, final Integer indicatorTopId, Integer formId) {
private void modelForm(Model model, final Integer indicatorTopId) {
//表单相关
List<IndicatorFormMapper> mapperList = indicatorTopMapperService.selectModelAndCsvNameByIndicator(
List<IndicatorFormMapper> mapperList = indicatorTopMapperService.selectModelIdByIndicator(
indicatorTopId); //查看form和顶指标映射关系
List<ModelDefine> modelList = modelDefineService.list(); //查询所有form表单列表
//查询form表单字段
if (formId != null) {
model.addAttribute("formFieldList",
formFieldConfigService.getFormFieldConfigByModelId(formId));//3.form表单字段放入session容器
} else if (!mapperList.isEmpty()) {
if (!mapperList.isEmpty()) {
List<FormFieldConfig> formFieldList = formFieldConfigService.getFormFieldConfigByModelId(
mapperList.get(0).getIndicatorModelId());
setChecked(modelList, mapperList.get(0).getIndicatorModelId(), false); //设置form表单列表的选中状态
model.addAttribute("formFieldList", formFieldList); //3.form表单字段放入session容器
}
List<ModelDefine> modelList = modelDefineService.list(); //查询所有form表单列表
setChecked(modelList, mapperList.isEmpty() ? null
: (formId != null ? formId : mapperList.get(0).getIndicatorModelId())); //设置form表单列表的选中状态
model.addAttribute("modelList", modelList); //4.所有form表单放入session容器
}
@ -375,32 +386,48 @@ public class IndicatorController extends BaseController {
*
* @param file 文件
* @param indicatorTopId 指标
* @param formId modelID
* @return 操作结果
*/
@PostMapping("/uploadCsvAndSetTopMapper")
public OperateResult<Void> uploadCsvAndSetTopMapper(@RequestParam("file") MultipartFile file,
Integer indicatorTopId, Integer formId) {
indicatorService.saveIndicatorTopCsvMapper(indicatorTopId, formId, file);
return OperateResult.success();
@PostMapping("/uploadCsv")
@ResponseBody
public OperateResult<List<IndicatorCsvColumn>> uploadCsv(@RequestParam("file") MultipartFile file,
Integer indicatorTopId) throws IOException {
List<IndicatorCsvColumn> list = indicatorService.saveIndicatorTopCsvMapper(indicatorTopId,
file);
if (list == null) {
return OperateResult.error(null, "无法解析CSC文件或者表头为空",
ErrorCode.BUSINESS_ERROR.getCode());
}
return OperateResult.success(list, ErrorMessage.SUCCESS.getMessage());
}
/**
* 保存指标和form表单ID的映射关系.
*
* @param indicatorTopId 指标顶级ID
* @param formId 表单ID
* @param formMapper formMapper对象
* @return 操作结果
*/
@PostMapping("/indicatorFormMapper")
public OperateResult<Void> saveIndicatorFormMapper(Integer indicatorTopId, Integer formId) {
indicatorTopMapperService.saveFormMapper(indicatorTopId, formId);
return OperateResult.success();
@ResponseBody
public OperateResult<List<FormFieldConfig>> saveIndicatorFormMapper(
@RequestBody IndicatorFormMapper formMapper) {
indicatorTopMapperService.saveFormMapper(formMapper);
return OperateResult.success(
formFieldConfigService.getFormFieldConfigByModelId(formMapper.getIndicatorModelId()),
ErrorMessage.SUCCESS.getMessage());
}
private <T extends CheckedBean> void setChecked(List<T> list, Integer id) {
/**
* 设置元素默认选中状态.
*
* @param list 元素列表
* @param id 匹配的ID
* @param flag 如果匹配ID为空当为true时设置第一个为选中;
* @param <T> 泛型集成checkedBean
*/
private <T extends CheckedBean> void setChecked(List<T> list, Integer id, boolean flag) {
if (list != null && !list.isEmpty()) {
if (id == null || id.equals(0)) {
if ((id == null || id.equals(0)) && flag) {
list.get(0).setChecked(true);
return;
}
@ -411,4 +438,37 @@ public class IndicatorController extends BaseController {
}
}
}
/**
* 保存映射关系.
*
* @param mapperBean 要保存的数据
* @return 操作结果
*/
@PostMapping("/saveMapper")
@ResponseBody
public OperateResult<Void> saveMapper(@RequestBody IndicatorMapperBean mapperBean) {
//检查是否form对应是否重复
Map<Integer, Long> dupFormFieldCount = mapperBean.getFormList().stream()
.filter(x -> x.getFormFieldId() != null).collect(Collectors.collectingAndThen(
Collectors.groupingBy(IndicatorBottomFormMapper::getFormFieldId, Collectors.counting()),
m -> m.entrySet().stream().filter(e -> e.getValue() > 1)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))));
if (!dupFormFieldCount.isEmpty()) {
return OperateResult.error(null, "表单映射有重复的对应关系",
ErrorCode.BUSINESS_ERROR.getCode());
}
//检查csv对应是否重复
Map<Integer, Long> dupCsvFieldCount = mapperBean.getCsvList().stream()
.filter(x -> x.getCsvColumnId() != null).collect(Collectors.collectingAndThen(
Collectors.groupingBy(IndicatorBottomCsvMapper::getCsvColumnId, Collectors.counting()),
m -> m.entrySet().stream().filter(e -> e.getValue() > 1)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))));
if (!dupCsvFieldCount.isEmpty()) {
return OperateResult.error(null, "CSV映射有重复的对应关系",
ErrorCode.BUSINESS_ERROR.getCode());
}
indicatorService.saveBottomMapper(mapperBean);
return OperateResult.success();
}
}

View File

@ -1,18 +0,0 @@
package com.hshh.indicator.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.stereotype.Controller;
/**
* <p>
* 记录顶级指标和csv的对应关系 前端控制器
* </p>
*
* @author liDongYu
* @since 2025-08-10
*/
@Controller
@RequestMapping("/indicator/indicatorCsv")
public class IndicatorCsvController {
}

View File

@ -18,5 +18,5 @@ public interface IndicatorFormMapperMapper extends BaseMapper<IndicatorFormMappe
* @param id 指标ID
* @return 对应关系列表
*/
List<IndicatorFormMapper> selectModelAndCsvNameByIndicator(Integer id);
List<IndicatorFormMapper> selectModelIdByIndicator(Integer id);
}

View File

@ -11,5 +11,20 @@ import java.util.List;
* @since 2025-08-10
*/
public interface IndicatorBottomCsvMapperService extends IService<IndicatorBottomCsvMapper> {
List<IndicatorBottomCsvMapper> queryListByIndicatorTopId(Integer indicatorTopId);
/**
* 保存指标和csv的对应关系.
*
* @param list 对应关系
*/
void saveBottomCsvMapper(List<IndicatorBottomCsvMapper> list);
/**
* 根据顶部指标删除对应关系.
*
* @param indicatorTopId 顶部指标ID
*/
void deleteByIndicatorTopId(Integer indicatorTopId);
}

View File

@ -19,4 +19,19 @@ public interface IndicatorBottomFormMapperService extends IService<IndicatorBott
* @return 对应关系list
*/
List<IndicatorBottomFormMapper> queryListByIndicatorId(Integer topId);
/**
* 删除顶部指标对应的映射.
*
* @param id 顶部指标ID
*/
void deleteByIndicatorId(Integer id);
/**
* 保存映射关系.
*
* @param indicatorBottomFormMapperList 映射关系列表
*/
void saveMapper(List<IndicatorBottomFormMapper> indicatorBottomFormMapperList);
}

View File

@ -19,4 +19,21 @@ public interface IndicatorCsvColumnService extends IService<IndicatorCsvColumn>
* @return csv文件列
*/
List<IndicatorCsvColumn> listByIdOrderByColumn(Integer id);
/**
* 根据顶级指标ID删除列.
*
* @param id 顶级指标ID
*/
void deleteByTopId(Integer id);
/**
* 保存csv列信息
*
* @param headerList csv头
* @param indicatorId 指标ID
* @param csvId csvID
* @return 保存的列表对象
*/
List<IndicatorCsvColumn> save(List<String> headerList, Integer indicatorId, Integer csvId);
}

View File

@ -18,4 +18,21 @@ public interface IndicatorCsvService extends IService<IndicatorCsv> {
* @return 对应关系(除了csv实际数据)
*/
IndicatorCsv selectByIndicatorId(Integer topId);
/**
* 保存csv文件信息.
*
* @param indicatorTopId 指标ID
* @param data 原始数据
* @param csvName 文件名称
* @return 主键
*/
Integer saveCsv(Integer indicatorTopId, byte[] data, String csvName);
/**
* 删除指定顶级topID对应的记录.
*
* @param indicatorTopId 指标topId
*/
void removeByIndicatorId(Integer indicatorTopId);
}

View File

@ -20,15 +20,15 @@ public interface IndicatorFromMapperService extends IService<IndicatorFormMapper
* @param id 指标ID
* @return 对应关系列表
*/
List<IndicatorFormMapper> selectModelAndCsvNameByIndicator(Integer id);
List<IndicatorFormMapper> selectModelIdByIndicator(Integer id);
/**
* 保存form和指标的映射关系.
*
* @param topId 顶级指标ID
* @param formId form表单ID
* @param mapper 数据
*/
void saveFormMapper(Integer topId, Integer formId);
void saveFormMapper(IndicatorFormMapper mapper);
/**
* 根据指标顶级ID删除对应关系.

View File

@ -1,7 +1,10 @@
package com.hshh.indicator.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.hshh.indicator.bean.IndicatorMapperBean;
import com.hshh.indicator.entity.Indicator;
import com.hshh.indicator.entity.IndicatorCsvColumn;
import java.io.IOException;
import java.util.List;
import org.springframework.web.multipart.MultipartFile;
@ -59,8 +62,16 @@ public interface IndicatorService extends IService<Indicator> {
* 保存顶级映射关系记录对应的表单ID和csv信息.
*
* @param topId 顶级指标ID
* @param modelId 表单ID
* @param file csv文件
* @return 返回csv文件头
*/
void saveIndicatorTopCsvMapper(Integer topId, Integer modelId, MultipartFile file);
List<IndicatorCsvColumn> saveIndicatorTopCsvMapper(Integer topId, MultipartFile file)
throws IOException;
/**
* 保存映射关系.
*
* @param mapperBean 映射数据
*/
void saveBottomMapper(IndicatorMapperBean mapperBean);
}

View File

@ -7,11 +7,10 @@ import com.hshh.indicator.mapper.IndicatorBottomCsvMapperMapper;
import com.hshh.indicator.service.IndicatorBottomCsvMapperService;
import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* 保存底部指标和csv列的映射关系 服务实现类.
*
* @author liDongYu
* @since 2025-08-10
@ -27,4 +26,17 @@ public class IndicatorBottomCsvMapperServiceImpl extends
queryWrapper.eq("indicator_top_id", indicatorTopId);
return this.list(queryWrapper);
}
@Transactional(rollbackFor = Exception.class)
@Override
public void saveBottomCsvMapper(List<IndicatorBottomCsvMapper> list) {
list.forEach(this::save);
}
@Override
public void deleteByIndicatorTopId(Integer indicatorTopId) {
QueryWrapper<IndicatorBottomCsvMapper> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("indicator_top_id", indicatorTopId);
remove(queryWrapper);
}
}

View File

@ -7,6 +7,7 @@ import com.hshh.indicator.mapper.IndicatorBottomMapperMapper;
import com.hshh.indicator.service.IndicatorBottomFormMapperService;
import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* <p>
@ -27,4 +28,17 @@ public class IndicatorBottomMapperServiceImpl extends
queryWrapper.eq("indicator_top_id", topId);
return this.list(queryWrapper);
}
@Override
public void deleteByIndicatorId(Integer id) {
QueryWrapper<IndicatorBottomFormMapper> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("indicator_top_id", id);
remove(queryWrapper);
}
@Transactional(rollbackFor = Exception.class)
@Override
public void saveMapper(List<IndicatorBottomFormMapper> indicatorBottomFormMapperList) {
indicatorBottomFormMapperList.forEach(this::save);
}
}

View File

@ -5,8 +5,10 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hshh.indicator.entity.IndicatorCsvColumn;
import com.hshh.indicator.mapper.IndicatorCsvColumnMapper;
import com.hshh.indicator.service.IndicatorCsvColumnService;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* 指标对应的csv模板中的列只保留最近一次的记录 服务实现类.
@ -26,4 +28,29 @@ public class IndicatorCsvColumnServiceImpl extends
queryWrapper.orderByAsc("csv_column_num");
return this.list(queryWrapper);
}
@Override
public void deleteByTopId(Integer id) {
QueryWrapper<IndicatorCsvColumn> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("indicator_top_id", id);
remove(queryWrapper);
}
@Transactional(rollbackFor = Exception.class)
@Override
public List<IndicatorCsvColumn> save(List<String> headerList, Integer indicatorId,
Integer csvId) {
List<IndicatorCsvColumn> list = new ArrayList<>();
for (int i = 0; i < headerList.size(); i++) {
IndicatorCsvColumn indicatorCsvColumn = new IndicatorCsvColumn();
indicatorCsvColumn.setCsvColumnName(headerList.get(i));
indicatorCsvColumn.setCsvColumnNum(i + 1);
indicatorCsvColumn.setIndicatorCsvId(csvId);
indicatorCsvColumn.setIndicatorTopId(indicatorId);
list.add(indicatorCsvColumn);
}
list.forEach(this::save);
return list;
}
}

View File

@ -9,9 +9,7 @@ import java.util.List;
import org.springframework.stereotype.Service;
/**
* <p>
* 记录顶级指标和csv的对应关系 服务实现类
* </p>
* 记录顶级指标和csv的对应关系 服务实现类.
*
* @author liDongYu
* @since 2025-08-10
@ -31,4 +29,21 @@ public class IndicatorCsvServiceImpl extends
}
return null;
}
@Override
public Integer saveCsv(Integer indicatorTopId, byte[] data, String csvName) {
IndicatorCsv indicatorCsv = new IndicatorCsv();
indicatorCsv.setIndicatorTopId(indicatorTopId);
indicatorCsv.setCsvData(data);
indicatorCsv.setCsvName(csvName);
this.save(indicatorCsv);
return indicatorCsv.getId();
}
@Override
public void removeByIndicatorId(Integer indicatorTopId) {
QueryWrapper<IndicatorCsv> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("indicator_top_id", indicatorTopId);
remove(queryWrapper);
}
}

View File

@ -21,18 +21,16 @@ public class IndicatorFormMapperServiceImpl extends
IndicatorFromMapperService {
@Override
public List<IndicatorFormMapper> selectModelAndCsvNameByIndicator(Integer id) {
return this.baseMapper.selectModelAndCsvNameByIndicator(id);
public List<IndicatorFormMapper> selectModelIdByIndicator(Integer id) {
return this.baseMapper.selectModelIdByIndicator(id);
}
@Transactional(rollbackFor = Exception.class)
@Override
public void saveFormMapper(Integer topId, Integer formId) {
deleteFormMapperByIndicatorId(formId);
IndicatorFormMapper formMapper = new IndicatorFormMapper();
formMapper.setIndicatorTopId(topId);
formMapper.setIndicatorModelId(formId);
save(formMapper);
public void saveFormMapper(IndicatorFormMapper mapper) {
deleteFormMapperByIndicatorId(mapper.getIndicatorTopId());
save(mapper);
}

View File

@ -2,13 +2,27 @@ 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.IndicatorMapperBean;
import com.hshh.indicator.entity.Indicator;
import com.hshh.indicator.entity.IndicatorCsvColumn;
import com.hshh.indicator.mapper.IndicatorMapper;
import com.hshh.indicator.service.IndicatorBottomCsvMapperService;
import com.hshh.indicator.service.IndicatorBottomFormMapperService;
import com.hshh.indicator.service.IndicatorCsvColumnService;
import com.hshh.indicator.service.IndicatorCsvService;
import com.hshh.indicator.service.IndicatorService;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
@ -25,6 +39,20 @@ import org.springframework.web.multipart.MultipartFile;
public class IndicatorServiceImpl extends ServiceImpl<IndicatorMapper, Indicator> implements
IndicatorService {
@Resource
private IndicatorCsvService csvService;
@Resource
private IndicatorCsvColumnService csvColumnService;
/**
* csv-指标对应关系服务类.
*/
@Resource
private IndicatorBottomCsvMapperService indicatorBottomCsvMapperService;
/**
* form表单-指标 映射关系.
*/
@Resource
private IndicatorBottomFormMapperService indicatorBottomFormMapperService;
@Override
public List<Indicator> queryList(@NotNull Integer topId, @NotNull String name,
@ -87,16 +115,44 @@ public class IndicatorServiceImpl extends ServiceImpl<IndicatorMapper, Indicator
@Transactional(rollbackFor = Exception.class)
@Override
public void saveIndicatorTopCsvMapper(Integer topId, Integer modelId, MultipartFile file) {
public List<IndicatorCsvColumn> saveIndicatorTopCsvMapper(Integer topId, MultipartFile file)
throws IOException {
Reader reader = new InputStreamReader(file.getInputStream(), StandardCharsets.UTF_8);
CSVParser parser = CSVFormat.DEFAULT.builder().setHeader() // 表示第一行是表头
.setSkipHeaderRecord(true) // 解析时跳过表头行
.build().parse(reader);
ArrayList<String> headerList = new ArrayList<>(parser.getHeaderMap().keySet());
if (headerList.isEmpty()) {
return null;
}
//删除表中m_data_indicator_csv原来topID对应记录
csvService.removeByIndicatorId(topId);
//添加m_data_indicator_csv记录
Integer csvId = csvService.saveCsv(topId, file.getBytes(), file.getOriginalFilename());
//删除m_data_indicator_csv_column 原topID对应的记录
csvColumnService.deleteByTopId(topId);
//添加对应记录
return csvColumnService.save(headerList, topId, csvId);
}
/**
* 保存底部指标映射关系.
*
* @param mapperBean 映射数据
*/
@Transactional(rollbackFor = Exception.class)
@Override
public void saveBottomMapper(IndicatorMapperBean mapperBean) {
//保存csv映射关系
if (!mapperBean.getCsvList().isEmpty()) {
indicatorBottomCsvMapperService.deleteByIndicatorTopId(mapperBean.getIndicatorTopId());
indicatorBottomCsvMapperService.saveBottomCsvMapper(mapperBean.getCsvList());
}
//保存form表单和指标映射关系
if (!mapperBean.getFormList().isEmpty()) {
indicatorBottomFormMapperService.deleteByIndicatorId(mapperBean.getIndicatorTopId());
indicatorBottomFormMapperService.saveMapper(mapperBean.getFormList());
}
}
}

View File

@ -33,16 +33,16 @@ public class FormFieldConfig implements Serializable {
private Integer dataModelId;
@NotBlank(message = "字段名称不能为空")
@Size(max = 10, message = "字段名称不能超过10字符")
@Pattern(regexp = "^[A-Za-z]+$", message = "字段ID只能包含英文字符")
@Size(max = 50, message = "字段名称不能超过50字符")
@Pattern(regexp = "^[A-Za-z_]+$", message = "字段ID只能包含英文字符")
private String fieldName;
@NotBlank(message = "字段ID不能为空")
@Size(max = 10, message = "字段ID不能超过10字符")
@Pattern(regexp = "^[A-Za-z]+$", message = "字段ID只能包含英文字符")
@Size(max = 50, message = "字段ID不能超过50字符")
@Pattern(regexp = "^[A-Za-z_]+$", message = "字段ID只能包含英文字符")
private String fieldId;
@NotBlank(message = "字段标签不能为空")
@Size(max = 20, message = "字段标签不能超过20字符")
@Size(max = 50, message = "字段标签不能超过50字符")
private String fieldLabel;
private String fieldDefaultValue;

View File

@ -1,8 +1,8 @@
<?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.IndicatorFormMapperMapper">
<select id="selectModelAndCsvNameByIndicator" resultType="com.hshh.indicator.entity.IndicatorFormMapper">
select t.indicator_model_id, t.csv_name
from m_data_indicator_top_mapper t where t.indicator_top_id=#{topId}
<select id="selectModelIdByIndicator" resultType="com.hshh.indicator.entity.IndicatorFormMapper">
select t.indicator_model_id
from m_data_indicator_form t where t.indicator_top_id=#{topId}
</select>
</mapper>

View File

@ -1,6 +1,6 @@
class HttpClient {
// GET 请求
get(url, callback,formId) {
get(url, callback, formId) {
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onreadystatechange = function () {
@ -12,18 +12,16 @@ class HttpClient {
if (contentType && contentType.indexOf('application/json') !== -1) {
try {
response = JSON.parse(response);
if(response.code === 0) {
if (response.code === 0) {
callback(null, response, xhr);
}else{
errorsHandler(response,formId);
} else {
errorsHandler(response, formId);
}
} catch (e) {
// JSON 解析失败,仍返回原始文本
}
}else{
} else {
callback(null, response, xhr);
}
@ -43,7 +41,7 @@ class HttpClient {
* @param callback 回调函数
* @param formId formID
*/
post(url, data, callback, formId,dialogId) {
post(url, data, callback, formId, dialogId) {
const xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
@ -63,9 +61,15 @@ class HttpClient {
if (response.code === 0) {
callback(null, response, xhr);
} else {
if (formId) {
removeValidCss(formId);
}
//字段验证提示错误
errorsHandler(response,formId,dialogId);
if (formId && dialogId) {
errorsHandler(response, formId, dialogId);
}
}
} else {
@ -75,9 +79,45 @@ class HttpClient {
};
xhr.send(JSON.stringify(data));
}
/**
* 提交 new formData数据
* @param url
* @param data
* @param callback
* @param formId
* @param dialogId
*/
postFormData(url, data, callback, formId, dialogId) {
$.ajax({
url: url, // 你的 Spring Boot 接口
type: 'POST',
data: data,
processData: false, // 必须
contentType: false, // 必须
xhr: function () { // 进度(可选)
const xhr = $.ajaxSettings.xhr();
xhr.upload.onprogress = e => {
if (e.lengthComputable) {
}
};
return xhr;
},
success: function (res) {
if (res.code === 10001) {
errorsHandler(res, formId, dialogId);
}
if(res.code===0){
callback(null, res, null);
}
},
error: err => console.error('失败', err.responseText || err)
});
}
}
function errorsHandler(response,formId,dialogId) {
function errorsHandler(response, formId, dialogId) {
if (response.code === -1) {//输入字段提示
let errors = response.errors;
@ -95,10 +135,12 @@ function errorsHandler(response,formId,dialogId) {
}
//业务错误
if (response.code === 10001) {
if (dialogId) {
closeDialog(dialogId);
}
showAlert("danger",response.message)
showAlert("danger", response.message)
}
}

View File

@ -193,6 +193,14 @@
hideContextMenu && hideContextMenu();
});
}
if(evt.detail.target.querySelector('[data-page="indicator_mapper"]')){
if(typeof csvListen === 'function'){
csvListen();
}
}
}
});
</script>

View File

@ -9,7 +9,7 @@
white-space: nowrap;
}
</style>
<div class="page-body" data-page="metric">
<div class="page-body" data-page="evaluation_list">
<div class="container-xl">
<div th:replace="fragments/dialog::navigateDialog(${chainMenuList})"></div>
<div class="row row-cards">

View File

@ -1,5 +1,5 @@
<!-- ====== 页面主体(纯静态,无 JS/Thymeleaf ====== -->
<div class="page-body" data-page="mapper">
<div class="page-body" data-page="indicator_mapper">
<div class="container-xl">
<!-- 面包屑(静态示例) -->
@ -10,7 +10,7 @@
<div class="card-header">
<div class="card-title">指标数据映射集设置</div>
<div class="card-actions">
<a href="#" class="btn btn-primary">
<a href="javascript:void(0)" class="btn btn-primary" onclick="mapperSave()">
<!-- 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"
@ -60,8 +60,9 @@
<div class="col-3">
<select id="modelId" class="form-select" onchange="formModelChange(this)">
<option value="0">-选择form表单-</option>
<option th:each="item:${modelList}" th:value="${item.id}"
th:text="${item.getModelName()}" th:selected="${item.checked}"></option>
<option th:each="modelItem:${modelList}" th:value="${modelItem.id}"
th:text="${modelItem.getModelName()}"
th:selected="${modelItem.checked}"></option>
</select>
</div>
</div>
@ -75,13 +76,18 @@
<div class="card-title">3. 上传 CSV 文件</div>
</div>
<div class="card-body">
<input type="file" class="form-control mb-3" accept=".csv">
<input type="file" id="csvFile" class="form-control mb-3" accept=".csv">
<!-- 新增:上次上传文件 -->
<div class="mb-2" th:if="${csvMapper!=null}">
<span class="fw-bold">上次上传文件:</span>
<a th:href="@{/indicator/download/{id}(id=${csvMapper.getId()})" target="_blank"
th:text="${csvMapper.getCsvName()}"></a>
</div>
<div class="fw-bold mb-2" id="csvHeader-total-num"></div>
<div class="list-group list-group-flush csv-heads" id="csvHeaders">
</div>
</div>
</div>
@ -103,29 +109,29 @@
</tr>
</thead>
<!-- 自动编号 tbody不用写数字CSS 计数器会生成 1..20 -->
<tbody class="autonum">
<!-- 02~20同上复制即可编号与名称尾号自动增长 -->
<tr th:each="item:${childrenIndicator}">
<input type="hidden" name="indicatorId" th:value="${item.getId()}">
<td class="sticky-body-col-1 col-index"></td>
<td class="sticky-body-col-2 col-name" th:text="${item.getName()}"></td>
<td><select class="form-select form-select-sm">
<option>— 选择表单字段 —</option>
<td><select class="form-select form-select-sm" name="formField">
<option th:if="${formFieldList.size()>0}"
th:each="field:${formFieldList}" th:value="${field.id}"
th:text="${field.getFieldName()}"></option>
th:text="${field.fieldLabel}" th:selected="${field.id == (bottomFormMap != null ? bottomFormMap[item.id] : '')}"></option>
>
</option>
</select></td>
<td><select class="form-select form-select-sm">
<option>— 选择 CSV 表头 —</option>
<td><select class="form-select form-select-sm" name="csvField">
<option th:if="${csvColumns.size()>0}" th:each="column:${csvColumns}"
th:value="${column.id}"
th:text="${column.getCsvColumnName()}"></option>
th:text="${column.getCsvColumnName()}" th:selected="${column.id == (bottomCsvColumnMap != null ? bottomCsvColumnMap[item.id] : '')}"></option>
>
</option>
@ -239,8 +245,93 @@
</style>
<script>
function formModelChange(object) {
document.getElementById("_indicator_mapper").setAttribute("hx-vals",
JSON.stringify({indicatorTopId: $('#indicationId').val(), formId: object.value}))
document.getElementById("_indicator_mapper").click();
let postUrl = document.getElementById('_rootPath').value + "indicator/indicatorFormMapper";
let obj = {};
obj.indicatorTopId = $('#indicationId').val();
obj.indicatorModelId = object.value;
let http = new HttpClient();
http.post(postUrl, obj, function (error, response, xhr) {
let data = response.result;//是一个form字段列表
let $sel = $('select[name="formField"]');
$sel.empty();
$.each(data, function (_, opt) {
$sel.append('<option value="' + opt.id + '">' + opt.fieldLabel + '</option>');
});
}, null, null)
}
function csvListen() {
$('#csvFile').on('change', function () {
const f = this.files[0];
if (!f) {
return;
}
let indicatorTopId = $('#indicationId').val();
const fd = new FormData();
fd.append('file', f);
fd.append('indicatorTopId', indicatorTopId);
let postUrl = document.getElementById('_rootPath').value + "indicator/uploadCsv";
let http = new HttpClient();
http.postFormData(postUrl, fd, function (error, response, xhr) {
let data = response.result;
let len = data.length;
document.getElementById("csvHeader-total-num").innerHTML = "已解析CSV文件" + len + "列";
document.getElementById("csvHeaders").innerHTML = "";
let html = "";
if (len > 0) {
data.forEach(item => {
html = html + " <div class=\"list-group-item\">" + item.csvColumnName + "</div>";
})
document.getElementById("csvHeaders").innerHTML = html;
//处理指标映射那里的selected选项
let $sel = $('select[name="csvField"]');
$sel.empty();
$.each(data, function (_, opt) {
$sel.append('<option value="' + opt.id + '">' + opt.csvColumnName + '</option>');
});
}
}, null, null)
});
}
function mapperSave() {
let obj = {};
obj.indicatorTopId = $('#indicationId').val();
//指标ID集合
let indicatorElements = document.getElementsByName('indicatorId');
let formMapperElements = document.getElementsByName('formField');
let csvElements = document.getElementsByName('csvField');
let formList = [];
let csvList = [];
for (let i = 0; i < indicatorElements.length; i++) {
//form映射
let formOneMapper = {};
formOneMapper.indicatorId = parseInt(indicatorElements[i].value);
formOneMapper.formFieldId = parseInt(formMapperElements[i].value);
formOneMapper.indicatorTopId = obj.indicatorTopId;
formList.push(formOneMapper);
//csv映射
let csvOneMapper = {};
csvOneMapper.indicatorTopId = obj.indicatorTopId;
csvOneMapper.indicatorId = parseInt(indicatorElements[i].value);
csvOneMapper.csvColumnId = parseInt(csvElements[i].value);
csvList.push(csvOneMapper);
}
obj.csvList = csvList;
obj.formList = formList;
//开始提交
let http = new HttpClient();
let url = document.getElementById('_rootPath').value + "indicator/saveMapper";
http.post(url,obj, function (error, response, xhr) {
showAlert("success","保存成功")
},null,null);
}
</script>

View File

@ -67,6 +67,11 @@
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-csv</artifactId>
<version>1.11.0</version>
</dependency>
</dependencies>
</project>

View File

@ -111,6 +111,11 @@
<artifactId>spring-boot-starter-validation</artifactId>
<version>2.6.13</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-csv</artifactId>
<version>1.11.0</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>