Browse Source

1 查询展示所有excel表中信息集合

2 查询 按产品编号展示
3查询 根据月份分组查询
4查询根据商家和产品编号客户名分组
main
zhaobo 1 week ago
parent
commit
f6bdce2b09

+ 108
- 0
zs-manager/src/main/java/com/ruoyi/tjfx/controller/TjfxAnalysisDataController.java View File

@@ -0,0 +1,108 @@
package com.ruoyi.tjfx.controller;

import cn.dev33.satoken.annotation.SaIgnore;
import com.ruoyi.tjfx.entity.TjfxAnalysisDataDTO;
import com.ruoyi.tjfx.service.TjfxAnalysisDataService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Map;

@SaIgnore
@Slf4j
@RestController
@RequestMapping("/tjfx/analysis-data")
public class TjfxAnalysisDataController {
@Autowired
private TjfxAnalysisDataService tjfxAnalysisDataService;


//查看展示所有数据结合
@SaIgnore
@GetMapping("/product-analysis")
public List<Map<String, Object>> getProductAnalysisData(
@RequestParam String startDate,
@RequestParam String endDate,
@RequestParam(required = false) String category

) throws Exception {
TjfxAnalysisDataDTO tjfxAnalysisDataDTO = new TjfxAnalysisDataDTO();
tjfxAnalysisDataDTO.setStartDate(startDate);
tjfxAnalysisDataDTO.setEndDate(endDate);
tjfxAnalysisDataDTO.setCategory(category);

return tjfxAnalysisDataService.selectByProductCode(tjfxAnalysisDataDTO);
}


//查询数据集合,按产品编号展示
@SaIgnore
@GetMapping("/filter-options")
public Map<String, List<String>> getFilterOptions(
@RequestParam String startDate,
@RequestParam String endDate,
@RequestParam(required = false) String category
) throws Exception {
TjfxAnalysisDataDTO tjfxAnalysisDataDTO = new TjfxAnalysisDataDTO();
tjfxAnalysisDataDTO.setStartDate(startDate);
tjfxAnalysisDataDTO.setEndDate(endDate);
tjfxAnalysisDataDTO.setCategory(category);

return tjfxAnalysisDataService.select(tjfxAnalysisDataDTO);
}

//查看根据月份分组进行查询,可以支持多选值的情况进行查询
@SaIgnore
@GetMapping("/monthly-analysis")
public List<Map<String, Object>> getMonthlyAnalysisData(
@RequestParam String startDate,
@RequestParam String endDate,
@RequestParam(required = false) String categorySpecs,
@RequestParam(required = false) String category,
@RequestParam(required = false) String productCode,
@RequestParam(required = false) String customerName,
@RequestParam(required = false) String brand,
@RequestParam(required = false) String shopName
) throws Exception {
TjfxAnalysisDataDTO tjfxAnalysisDataDTO = new TjfxAnalysisDataDTO();
tjfxAnalysisDataDTO.setStartDate(startDate);
tjfxAnalysisDataDTO.setEndDate(endDate);
tjfxAnalysisDataDTO.setCategorySpecs(categorySpecs);
tjfxAnalysisDataDTO.setCategory(category);
tjfxAnalysisDataDTO.setProductCode(productCode);
tjfxAnalysisDataDTO.setCustomerName(customerName);
tjfxAnalysisDataDTO.setBrand(brand);
tjfxAnalysisDataDTO.setShopName(shopName);
return tjfxAnalysisDataService.selectMonth(tjfxAnalysisDataDTO);
}


//查看,按品牌,客户名称,产品编号分组查询对应的所有分类规格
@SaIgnore
@GetMapping("/comprehensive-analysis")
public Map<String, Object> getComprehensiveAnalysisData(
@RequestParam String startDate,
@RequestParam String endDate,
@RequestParam(required = false) String categorySpecs,
@RequestParam(required = false) String category,
@RequestParam(required = false) String productCode,
@RequestParam(required = false) String customerName,
@RequestParam(required = false) String brand,
@RequestParam(required = false) String shopName
) throws Exception {
TjfxAnalysisDataDTO tjfxAnalysisDataDTO = new TjfxAnalysisDataDTO();
tjfxAnalysisDataDTO.setStartDate(startDate);
tjfxAnalysisDataDTO.setEndDate(endDate);
tjfxAnalysisDataDTO.setCategorySpecs(categorySpecs);
tjfxAnalysisDataDTO.setCategory(category);
tjfxAnalysisDataDTO.setProductCode(productCode);
tjfxAnalysisDataDTO.setCustomerName(customerName);
tjfxAnalysisDataDTO.setBrand(brand);
tjfxAnalysisDataDTO.setShopName(shopName);
return tjfxAnalysisDataService.selectByProductAndShopAndSpecification(tjfxAnalysisDataDTO);
}


}

+ 45
- 0
zs-manager/src/main/java/com/ruoyi/tjfx/entity/TjfxAnalysisDataDTO.java View File

@@ -0,0 +1,45 @@
package com.ruoyi.tjfx.entity;

import lombok.Getter;
import lombok.Setter;

import javax.validation.constraints.NotNull;


/**
* 统计分析数据传输对象(Data Transfer Object)
* 用于封装统计分析相关的查询条件
*/
@Getter
@Setter
public class TjfxAnalysisDataDTO {

@NotNull
// 统计开始时间
private String startDate;

@NotNull
// 统计结束时间
private String endDate;

// 分类规格条件
private String categorySpecs;

// 分类条件
private String category;


// 产品代码
private String productCode;

// 客户名称
private String customerName;

// 品牌
private String brand;

// 店铺名称
private String shopName;


}

+ 54
- 0
zs-manager/src/main/java/com/ruoyi/tjfx/mapper/TjfxAnalysisDataMapper.java View File

@@ -0,0 +1,54 @@
package com.ruoyi.tjfx.mapper;
import com.ruoyi.tjfx.entity.TjfxAnalysisDataDTO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;


import java.util.List;
import java.util.Map;

@Mapper
public interface TjfxAnalysisDataMapper {

List<Map<String, Object>> select(TjfxAnalysisDataDTO tjfxAnalysisDataDTO);



List<Map<String, Object>> selectByShop(TjfxAnalysisDataDTO tjfxAnalysisDataDTO);




List<Map<String, Object>> selectMonth(@Param("sql") String sql, @Param("productCodeList") List<String> productCodeList, @Param("brandList") List<String> brandList, @Param("shopNameList") List<String> shopNameList, @Param("customerNameList") List<String> customerNameList, @Param("startDate") String startDate, @Param("endDate") String endDate, @Param("category") String category);


List<Map<String, Object>> selectByCustomer(@Param("productCodeList") List<String> productCodeList,
@Param("brandList") List<String> brandList,
@Param("shopNameList") List<String> shopNameList,
@Param("customerNameList") List<String> customerNameList,
@Param("startDate") String startDate,
@Param("endDate") String endDate,
@Param("category") String category,
@Param("sql") String sql);

List<Map<String, Object>> selectByProduct(@Param("productCodeList") List<String> productCodeList, @Param("brandList") List<String> brandList, @Param("shopNameList") List<String> shopNameList, @Param("customerNameList") List<String> customerNameList, @Param("startDate") String startDate, @Param("endDate") String endDate, @Param("category") String category, @Param("sql") String sql);

List<Map<String, Object>> selectBySpecification(@Param("productCodeList") List<String> productCodeList, @Param("brandList") List<String> brandList, @Param("shopNameList") List<String> shopNameList, @Param("customerNameList") List<String> customerNameList, @Param("startDate") String startDate, @Param("endDate") String endDate, @Param("category") String category, @Param("sql") String sql);

List<Map<String, Object>> selectByBrand(@Param("productCodeList") List<String> productCodeList, @Param("brandList") List<String> brandList, @Param("shopNameList") List<String> shopNameList, @Param("customerNameList") List<String> customerNameList, @Param("startDate") String startDate, @Param("endDate") String endDate, @Param("category") String category, @Param("sql") String sql);

List<Map<String, Object>> selectByShopName(@Param("productCodeList") List<String> productCodeList, @Param("brandList") List<String> brandList, @Param("shopNameList") List<String> shopNameList, @Param("customerNameList") List<String> customerNameList, @Param("startDate") String startDate, @Param("endDate") String endDate, @Param("category") String category, @Param("sql") String sql);













}

+ 623
- 0
zs-manager/src/main/java/com/ruoyi/tjfx/service/impl/TjfxAnalysisDataServiceImpl.java View File

@@ -0,0 +1,623 @@
package com.ruoyi.tjfx.service.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.tjfx.entity.TjfxAnalysisDataDTO;
import com.ruoyi.tjfx.mapper.TjfxAnalysisDataMapper;
import com.ruoyi.tjfx.service.TjfxAnalysisDataService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;


@Service
public class TjfxAnalysisDataServiceImpl implements TjfxAnalysisDataService {
private static final Logger log = LoggerFactory.getLogger(TjfxAnalysisDataServiceImpl.class);


@Autowired
private TjfxAnalysisDataMapper tjfxAnalysisDataMapper;

/**
* 根据产品代码查询数据并进行聚合处理
*
* @param tjfxAnalysisDataDTO 查询条件数据传输对象,包含查询所需的参数
* @return 返回聚合处理后的结果列表,每个元素是一个Map,键为String类型,值为Object类型
* @throws Exception 可能抛出的异常,如数据库查询异常等
*/
public List<Map<String, Object>> selectByProductCode(TjfxAnalysisDataDTO tjfxAnalysisDataDTO) throws Exception {
// 调用数据访问层方法获取原始数据
List<Map<String, Object>> rows = tjfxAnalysisDataMapper.select(tjfxAnalysisDataDTO);
// 对获取的数据进行聚合处理后返回
return aggregate(rows);
}

/**
* 根据条件查询分析数据并返回聚合后的结果
*
* @param tjfxAnalysisDataDTO 查询条件的数据传输对象
* @return 返回一个Map,键为String类型,值为List<String>类型,表示查询结果
* @throws Exception 如果查询过程中发生异常,则抛出Exception
*/
@Override
public Map<String, List<String>> select(TjfxAnalysisDataDTO tjfxAnalysisDataDTO) throws Exception {
// 调用数据访问层的select方法查询数据,返回结果为List<Map<String, Object>>类型
List<Map<String, Object>> rows = tjfxAnalysisDataMapper.select(tjfxAnalysisDataDTO);
// 将查询结果聚合为Map<String, List<String>>类型并返回
return aggregateToMap(rows);
}

/**
* 查询月度数据
*
* @param tjfxAnalysisDataDTO 查询条件参数对象
* @return 返回月度数据列表
*/
@Override
public List<Map<String, Object>> selectMonth(TjfxAnalysisDataDTO tjfxAnalysisDataDTO) throws Exception {
String productCode = tjfxAnalysisDataDTO.getProductCode();
List<String> productCodeList;
if (productCode != null && !productCode.isEmpty()) {
productCodeList = Arrays.stream(productCode.split(","))
.filter(code -> !code.isEmpty()) // 过滤空字符串
.collect(Collectors.toList()); // 转换为可修改的ArrayList
} else {
productCodeList = new ArrayList<>(); // 空列表
}

String brand = tjfxAnalysisDataDTO.getBrand();
List<String> brandList;
if (brand != null && !brand.isEmpty()) {
brandList = Arrays.stream(brand.split(","))
.filter(code -> !code.isEmpty()) // 过滤空字符串
.collect(Collectors.toList()); // 转换为可修改的ArrayList
} else {
brandList = new ArrayList<>(); // 空列表
}

String shopName = tjfxAnalysisDataDTO.getShopName();
List<String> shopNameList;
if (shopName != null && !shopName.isEmpty()) {
shopNameList = Arrays.stream(shopName.split(","))
.filter(code -> !code.isEmpty()) // 过滤空字符串
.collect(Collectors.toList()); // 转换为可修改的ArrayList
} else {
shopNameList = new ArrayList<>(); // 空列表
}

String customerName = tjfxAnalysisDataDTO.getCustomerName();
List<String> customerNameList;
if (customerName != null && !customerName.isEmpty()) {
customerNameList = Arrays.stream(customerName.split(","))
.filter(code -> !code.isEmpty()) // 过滤空字符串
.collect(Collectors.toList()); // 转换为可修改的ArrayList
} else {
customerNameList = new ArrayList<>(); // 空列表
}

String categorySpecs = tjfxAnalysisDataDTO.getCategorySpecs();
String sql = "1=1";
if (categorySpecs != null && !categorySpecs.trim().isEmpty()) {
ObjectMapper objectMapper = new ObjectMapper();
Map<String, List<String>> res = objectMapper.readValue(categorySpecs, new com.fasterxml.jackson.core.type.TypeReference<Map<String, List<String>>>() {
});

sql = res.entrySet().stream()
.map(entry -> {
String key = entry.getKey();
List<String> values = entry.getValue();

// 明确指定JSON路径格式。

String jsonPath = String.format("$.\"%s\"", key);

// 生成条件时确保整体格式正确
String orConditions = values.stream()
.map(value -> {
String escapedValue = value.replace("'", "''");
// 注意这里JSON路径用单引号包裹,内部键名用双引号
return String.format("JSON_UNQUOTE(JSON_EXTRACT(category_specs, '%s')) = '%s'", jsonPath, escapedValue);
})
.collect(Collectors.joining(" OR ")).trim();

return "(" + orConditions + ")";
})
.collect(Collectors.joining(" AND ")).trim();
}
String startDate = tjfxAnalysisDataDTO.getStartDate();
String endDate = tjfxAnalysisDataDTO.getEndDate();
String category = tjfxAnalysisDataDTO.getCategory();

// 执行查询
List<Map<String, Object>> result = tjfxAnalysisDataMapper.selectMonth(sql, productCodeList, brandList, shopNameList, customerNameList, startDate, endDate, category);
return result;
}

/**
* 根据商品、店铺和规格条件查询分析数据
* 返回包含客户数据、商品数据和规格数据的综合结果
*
* @param tjfxAnalysisDataDTO 查询条件参数,包含商品、店铺和规格相关信息
* @return 返回包含客户数据、商品数据和规格数据的列表
*/
@Override
public Map<String, Object> selectByProductAndShopAndSpecification(TjfxAnalysisDataDTO tjfxAnalysisDataDTO) throws Exception {
// 获取商品编码并处理为列表形式
String productCode = tjfxAnalysisDataDTO.getProductCode();
List<String> productCodeList;
if (productCode != null && !productCode.isEmpty()) {
// 将逗号分隔的商品编码字符串转换为列表,并过滤空字符串
productCodeList = Arrays.stream(productCode.split(","))
.filter(code -> !code.isEmpty()) // 过滤空字符串
.collect(Collectors.toList()); // 转换为可修改的ArrayList
} else {
productCodeList = new ArrayList<>(); // 空列表
}

// 获取品牌信息并处理为列表形式
String brand = tjfxAnalysisDataDTO.getBrand();
List<String> brandList;
if (brand != null && !brand.isEmpty()) {
// 将逗号分隔的品牌字符串转换为列表,并过滤空字符串
brandList = Arrays.stream(brand.split(","))
.filter(code -> !code.isEmpty()) // 过滤空字符串
.collect(Collectors.toList()); // 转换为可修改的ArrayList
} else {
brandList = new ArrayList<>(); // 空列表
}

// 获取店铺名称并处理为列表形式
String shopName = tjfxAnalysisDataDTO.getShopName();
List<String> shopNameList;
if (shopName != null && !shopName.isEmpty()) {
// 将逗号分隔的店铺名称字符串转换为列表,并过滤空字符串
shopNameList = Arrays.stream(shopName.split(","))
.filter(code -> !code.isEmpty()) // 过滤空字符串
.collect(Collectors.toList()); // 转换为可修改的ArrayList
} else {
shopNameList = new ArrayList<>(); // 空列表
}

// 获取客户名称并处理为列表形式
String customerName = tjfxAnalysisDataDTO.getCustomerName();
List<String> customerNameList;
if (customerName != null && !customerName.isEmpty()) {
// 将逗号分隔的客户名称字符串转换为列表,并过滤空字符串
customerNameList = Arrays.stream(customerName.split(","))
.filter(code -> !code.isEmpty()) // 过滤空字符串
.collect(Collectors.toList()); // 转换为可修改的ArrayList
} else {
customerNameList = new ArrayList<>(); // 空列表
}

// 处理规格条件,构建SQL查询条件
String categorySpecs = tjfxAnalysisDataDTO.getCategorySpecs();
String sql = "1=1"; // 默认条件,表示无限制
if (categorySpecs != null && !categorySpecs.trim().isEmpty()) {
// 使用Jackson的ObjectMapper解析JSON格式的规格条件
ObjectMapper objectMapper = new ObjectMapper();
Map<String, List<String>> res = objectMapper.readValue(categorySpecs, new com.fasterxml.jackson.core.type.TypeReference<Map<String, List<String>>>() {
});
// 将规格条件转换为SQL的JSON_EXTRACT查询条件
sql = res.entrySet().stream()
.map(entry -> {
String key = entry.getKey();
List<String> values = entry.getValue();

// 明确指定JSON路径格式,使用方括号语法可能兼容性更好
String jsonPath = String.format("$.\"%s\"", key);

// 生成条件时确保整体格式正确
String orConditions = values.stream()
.map(value -> {
String escapedValue = value.replace("'", "''");
// 注意这里JSON路径用单引号包裹,内部键名用双引号
return String.format("JSON_UNQUOTE(JSON_EXTRACT(category_specs, '%s')) = '%s'", jsonPath, escapedValue);
})
.collect(Collectors.joining(" OR ")).trim();

return "(" + orConditions + ")";
})
.collect(Collectors.joining(" AND ")).trim();
}
// 获取查询的时间范围和分类信息
String startDate = tjfxAnalysisDataDTO.getStartDate();
String endDate = tjfxAnalysisDataDTO.getEndDate();
String category = tjfxAnalysisDataDTO.getCategory();
// 创建结果列表
Map<String, Object> result = new HashMap<>();
// 查询按客户名称分组的数据
List<Map<String, Object>> customerData = tjfxAnalysisDataMapper.selectByCustomer(productCodeList, brandList, shopNameList, customerNameList, startDate, endDate, category, sql);
// 查询按商品分组的数据
List<Map<String, Object>> productData = tjfxAnalysisDataMapper.selectByProduct(productCodeList, brandList, shopNameList, customerNameList, startDate, endDate, category, sql);
// 查询按规格分组的数据
List<Map<String, Object>> rows = tjfxAnalysisDataMapper.selectBySpecification(productCodeList, brandList, shopNameList, customerNameList, startDate, endDate, category, sql);
//查询店铺
List<Map<String, Object>> shop = tjfxAnalysisDataMapper.selectByShopName(productCodeList, brandList, shopNameList, customerNameList, startDate, endDate, category, sql);
//查询按品牌分组的数据
List<Map<String, Object>> brands = tjfxAnalysisDataMapper.selectByBrand(productCodeList, brandList, shopNameList, customerNameList, startDate, endDate, category, sql);
// 使用新的规格数据聚合方法处理数据
Map<String, Map<String, Map<String, Object>>> specificationData;
try {
specificationData = aggregateSpecificationData(rows);
} catch (Exception e) {
log.error("处理规格数据时发生错误", e);
specificationData = new LinkedHashMap<>();
}


result.put("customer_name", customerData);
result.put("product_code", productData);
result.put("brand", brands);
result.put("shop_name", shop);
result.put("category_specs", specificationData);
return result;
}


/**
* 将输入的Map列表聚合为包含所有唯一值的Map,每个键对应一个排序后的列表
* 主要用于获取所有可能的选项值,用于前端下拉框等场景
*
* @param input 包含多个Map的列表,每个Map代表一条记录
* @return 包含所有唯一值的Map,键为字段名,值为排序后的唯一值列表
* @throws Exception 可能抛出异常
*/
public static Map<String, List<String>> aggregateToMap(List<Map<String, Object>> input) throws Exception {
// 结果Map,包含所有需要的字段,使用LinkedHashMap保持插入顺序
Map<String, List<String>> result = new LinkedHashMap<>();

// 初始化基础字段为空列表,确保这些字段始终存在于结果中
result.put("product_code", new ArrayList<>());
result.put("brand", new ArrayList<>());
result.put("customer_name", new ArrayList<>());
result.put("shop_name", new ArrayList<>());

// 处理空输入情况,直接返回初始化的结果Map
if (input == null || input.isEmpty()) {
return result;
}

// 按product_code分组(保持插入顺序)
Map<String, List<Map<String, Object>>> grouped = input.stream()
.collect(Collectors.groupingBy(m -> nz((String) m.get("product_code")), LinkedHashMap::new, Collectors.toList()));

// 用于去重的集合
LinkedHashSet<String> productCodes = new LinkedHashSet<>();
LinkedHashSet<String> brands = new LinkedHashSet<>();
LinkedHashSet<String> customerNames = new LinkedHashSet<>();
LinkedHashSet<String> shopNames = new LinkedHashSet<>();

// 分类规格键 -> 有序去重值集合(动态添加键)
Map<String, LinkedHashSet<String>> categoryValues = new LinkedHashMap<>();

// 遍历所有分组数据
for (List<Map<String, Object>> records : grouped.values()) {
for (Map<String, Object> record : records) {
// 收集基础字段
productCodes.add(nz((String) record.get("product_code")));
brands.add(nz((String) record.get("brand")));
customerNames.add(nz((String) record.get("customer_name")));
shopNames.add(nz((String) record.get("shop_name")));

// 处理category_specs字段(动态处理所有键)
String specs = (String) record.get("category_specs");
if (specs != null && !specs.trim().isEmpty()) {
try {
// 使用与aggregate方法相同的方式解析JSON
ObjectMapper mapper = new ObjectMapper();
Map<String, String> specsMap = mapper.readValue(specs, new com.fasterxml.jackson.core.type.TypeReference<Map<String, String>>() {
});
for (Map.Entry<String, String> entry : specsMap.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
if (value != null && !value.trim().isEmpty()) {
// 动态获取或创建键对应的集合
categoryValues.computeIfAbsent(key, k -> new LinkedHashSet<>())
.add(value.trim());
}
}
} catch (Exception ignore) {
// 解析失败时忽略
}
}
}
}

// 排序并将去重后的集合转换为列表
List<String> sortedProductCodes = new ArrayList<>(productCodes);
Collections.sort(sortedProductCodes);
result.put("product_code", sortedProductCodes);

List<String> sortedBrands = new ArrayList<>(brands);
Collections.sort(sortedBrands);
result.put("brand", sortedBrands);

List<String> sortedCustomerNames = new ArrayList<>(customerNames);
Collections.sort(sortedCustomerNames);
result.put("customer_name", sortedCustomerNames);

List<String> sortedShopNames = new ArrayList<>(shopNames);
Collections.sort(sortedShopNames);
result.put("shop_name", sortedShopNames);

// 处理所有动态识别的分类规格字段
for (Map.Entry<String, LinkedHashSet<String>> entry : categoryValues.entrySet()) {
List<String> sortedValues = new ArrayList<>(entry.getValue());
Collections.sort(sortedValues);
result.put(entry.getKey(), sortedValues);
}

return result;
}


/**
* 处理null字符串,将null转为空字符串并trim
*
* @param s 待处理的字符串
* @return 处理后的字符串,null转为空字符串
*/
public static String nz(String s) {
return s == null ? "" : s.trim();
}

/**
* 解析categorySpecs字符串,支持单个JSON对象或多个JSON对象拼接的情况
*
* @param categorySpecs 规格字符串,可能是单个JSON或多个JSON拼接
* @param objectMapper Jackson ObjectMapper实例
* @return 合并后的规格Map
* @throws Exception 解析异常
*/
private static Map<String, List<String>> parseCategorySpecs(String categorySpecs, ObjectMapper objectMapper) throws Exception {
Map<String, List<String>> result = new LinkedHashMap<>();

if (categorySpecs == null || categorySpecs.trim().isEmpty()) {
return result;
}

String trimmed = categorySpecs.trim();

// 尝试直接解析为单个JSON对象
try {
Map<String, List<String>> singleResult = objectMapper.readValue(trimmed,
new com.fasterxml.jackson.core.type.TypeReference<Map<String, List<String>>>() {
});
return singleResult;
} catch (Exception e) {
// 如果单个JSON解析失败,尝试解析多个JSON对象拼接的情况
log.debug("单个JSON解析失败,尝试解析多个JSON对象拼接: {}", e.getMessage());
}

// 处理多个JSON对象拼接的情况
// 使用正则表达式分割JSON对象
// 匹配 { ... } 模式,但需要处理嵌套的大括号
List<String> jsonObjects = splitJsonObjects(trimmed);

for (String jsonStr : jsonObjects) {
if (jsonStr.trim().isEmpty()) {
continue;
}

try {
Map<String, List<String>> jsonMap = objectMapper.readValue(jsonStr.trim(),
new com.fasterxml.jackson.core.type.TypeReference<Map<String, List<String>>>() {
});

// 合并到结果中
for (Map.Entry<String, List<String>> entry : jsonMap.entrySet()) {
String key = entry.getKey();
List<String> values = entry.getValue();

if (result.containsKey(key)) {
// 如果键已存在,合并值列表并去重
Set<String> uniqueValues = new LinkedHashSet<>(result.get(key));
uniqueValues.addAll(values);
result.put(key, new ArrayList<>(uniqueValues));
} else {
result.put(key, new ArrayList<>(values));
}
}
} catch (Exception e) {
log.warn("解析JSON对象失败: {}", jsonStr, e);
}
}

return result;
}

/**
* 分割多个JSON对象拼接的字符串
*
* @param jsonString 包含多个JSON对象的字符串
* @return JSON对象列表
*/
private static List<String> splitJsonObjects(String jsonString) {
List<String> result = new ArrayList<>();
if (jsonString == null || jsonString.trim().isEmpty()) {
return result;
}

int braceCount = 0;
int start = -1;

for (int i = 0; i < jsonString.length(); i++) {
char c = jsonString.charAt(i);

if (c == '{') {
if (braceCount == 0) {
start = i; // 记录JSON对象开始位置
}
braceCount++;
} else if (c == '}') {
braceCount--;
if (braceCount == 0 && start != -1) {
// 找到一个完整的JSON对象
String jsonObj = jsonString.substring(start, i + 1);
result.add(jsonObj);
start = -1;
}
}
}

return result;
}

/**
* 按规格分组处理数据,返回按category_specs字段中所有key分组的Map结构
* 将相同规格的数据进行聚合,累加数量和金额,并按所有key进行分组展示
*
* @param input 包含规格数据的列表,每个Map包含category_specs、totalQuantity、totalAmount
* @return 返回按category_specs中所有key分组的Map,外层键为key名,内层键为key值,值为包含数量和金额的Map
* @throws Exception 可能抛出异常
*/
public static Map<String, Map<String, Map<String, Object>>> aggregateSpecificationData(List<Map<String, Object>> input) throws Exception {
// 外层Map:key名称 -> 内层Map:key值 -> {数量, 金额}
Map<String, Map<String, Map<String, Object>>> result = new LinkedHashMap<>();

// 处理空输入情况
if (input == null || input.isEmpty()) {
return result;
}

for (Map<String, Object> record : input) {
String specs = (String) record.get("category_specs");
Object totalQuantity = record.get("totalQuantity");
Object totalAmount = record.get("totalAmount");

if (specs != null && !specs.trim().isEmpty()) {
try {
ObjectMapper mapper = new ObjectMapper();
Map<String, String> specsMap = mapper.readValue(specs, new com.fasterxml.jackson.core.type.TypeReference<Map<String, String>>() {
});

// 获取数量和金额值
Long quantity = totalQuantity != null ? ((Number) totalQuantity).longValue() : 0L;
Double amount = totalAmount != null ? ((Number) totalAmount).doubleValue() : 0.0;

// 遍历规格Map中的所有key进行分组
for (Map.Entry<String, String> entry : specsMap.entrySet()) {
String keyName = entry.getKey();
String keyValue = entry.getValue() != null ? entry.getValue().trim() : "";

if (!keyValue.isEmpty()) {
// 确保外层Map包含该keyName
if (!result.containsKey(keyName)) {
result.put(keyName, new LinkedHashMap<>());
}
Map<String, Map<String, Object>> keyGroups = result.get(keyName);

// 确保内层Map包含该keyValue
if (!keyGroups.containsKey(keyValue)) {
Map<String, Object> dataMap = new LinkedHashMap<>();
dataMap.put("totalQuantity", 0L);
dataMap.put("totalAmount", 0.0);
keyGroups.put(keyValue, dataMap);
}

// 更新数量和金额
Map<String, Object> dataMap = keyGroups.get(keyValue);
Long currentQuantity = (Long) dataMap.get("totalQuantity");
Double currentAmount = (Double) dataMap.get("totalAmount");
dataMap.put("totalQuantity", currentQuantity + quantity);
dataMap.put("totalAmount", currentAmount + amount);
}
}
} catch (Exception e) {
log.warn("解析规格数据失败: " + specs, e);
}
}
}

// 对每个key下的value进行排序
for (Map.Entry<String, Map<String, Map<String, Object>>> entry : result.entrySet()) {
Map<String, Map<String, Object>> sortedKeyValues = new LinkedHashMap<>();
List<String> sortedKeys = new ArrayList<>(entry.getValue().keySet());
Collections.sort(sortedKeys);

for (String key : sortedKeys) {
sortedKeyValues.put(key, entry.getValue().get(key));
}

result.put(entry.getKey(), sortedKeyValues);
}

return result;
}


/**
* 对输入的List<Map<String, Object>>类型数据进行聚合处理
* 按产品代码分组,将相同产品的数据进行聚合,收集品牌、店铺、客户和规格信息
*
* @param input 包含Map<String, Object>的列表,作为聚合处理的输入数据
* @return 返回聚合处理后的List<Map < String, Object>>类型数据
* @throws Exception 可能在处理过程中抛出异常
*/
public static List<Map<String, Object>> aggregate(List<Map<String, Object>> input) throws Exception {
// 按product_code 分组(保持插入顺序)
Map<String, List<Map<String, Object>>> grouped = input.stream()
.collect(Collectors.groupingBy(m -> (String) m.get("product_code"), LinkedHashMap::new, Collectors.toList()));

List<Map<String, Object>> result = new ArrayList<>();

for (Map.Entry<String, List<Map<String, Object>>> entry : grouped.entrySet()) {
String product_code = entry.getKey();
List<Map<String, Object>> records = entry.getValue();

Map<String, Object> aggregated = new LinkedHashMap<>();
aggregated.put("product_code", product_code);

// 品牌名称集合
LinkedHashSet<String> brandNames = new LinkedHashSet<>();
// 店铺名称集合
LinkedHashSet<String> shopNames = new LinkedHashSet<>();
// 客户名称集合
LinkedHashSet<String> customerNames = new LinkedHashSet<>();
// 规格信息集合
Map<String, Set<String>> categorySet = new LinkedHashMap<>();

for (Map<String, Object> record : records) {
// 收集基础信息
brandNames.add((String) record.get("brand"));
shopNames.add((String) record.get("shop_name"));
customerNames.add((String) record.get("customer_name"));

// 处理规格信息
String specs = (String) record.get("category_specs");
ObjectMapper mapper = new ObjectMapper();
Map<String, String> specsMap = mapper.readValue(specs, new com.fasterxml.jackson.core.type.TypeReference<Map<String, String>>() {
});
for (Map.Entry<String, String> courseEntry : specsMap.entrySet()) {
categorySet.computeIfAbsent(courseEntry.getKey(), k -> new HashSet<>())
.add(courseEntry.getValue());
}
}

// 排序并转回 List<String>
Map<String, List<String>> categorySets = new LinkedHashMap<>();
for (Map.Entry<String, Set<String>> courseEntry : categorySet.entrySet()) {
List<String> sortedScores = new ArrayList<>(courseEntry.getValue());
Collections.sort(sortedScores); // 升序
List<String> scoresStr = sortedScores.stream()
.map(String::valueOf)
.collect(Collectors.toList());
categorySets.put(courseEntry.getKey(), scoresStr);
}

// 组装聚合结果
aggregated.put("brand", new ArrayList<>(brandNames));
aggregated.put("shop_name", new ArrayList<>(shopNames));
aggregated.put("customer_name", new ArrayList<>(customerNames));
aggregated.putAll(categorySets);

result.add(aggregated);
}

return result;
}
}

+ 281
- 0
zs-manager/src/main/resources/mapper/TjfxAnalysisDataMapper.xml View File

@@ -0,0 +1,281 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.ruoyi.tjfx.mapper.TjfxAnalysisDataMapper">
<select id="select" resultType="java.util.Map">
select shop_name,
customer_name,
product_code,
brand,
category_specs
from zs_tjfx_analysis_data
<where>
category = #{category} and date between #{startDate} and #{endDate}
</where>
order by category_specs;
</select>

<select id="selectMonth" resultType="java.util.Map">
select DATE_FORMAT(date, '%Y-%m') AS month, SUM(quantity) AS totalQuantity,SUM(total_amount) AS totalAmount
FROM zs_tjfx_analysis_data
WHERE date BETWEEN #{startDate} AND #{endDate}
AND status = 1
<if test="category != null and category != ''">
AND category = #{category}
</if>
<if test="productCodeList !=null and productCodeList.size() > 0">
AND product_code in
<foreach collection="productCodeList" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>

<if test="customerNameList !=null and customerNameList.size()>0">
AND customer_name in
<foreach collection="customerNameList" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="shopNameList !=null and shopNameList.size()>0">
AND shop_name in
<foreach collection="shopNameList" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="brandList !=null and brandList.size()>0">
AND brand in
<foreach collection="brandList" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>

<if test="sql != null and sql != ''">
AND ${sql}
</if>
GROUP BY month
</select>

<select id="selectByShop" resultType="java.util.Map">
select shop_name, SUM(quantity) AS totalQuantity,SUM(total_amount) AS totalAmount
FROM zs_tjfx_analysis_data
WHERE date BETWEEN #{startDate} AND #{endDate}
AND status = 1
<if test="category != null and category != ''">
AND category = #{category}
</if>
<if test="categorySpecs != null and categorySpecs != ''">
AND category_specs LIKE CONCAT('%', #{categorySpecs}, '%')
</if>
<if test="customerName != null and customerName != ''">
AND customer_name = #{customerName}
</if>
<if test="shopName != null and shopName != ''">
AND shop_name = #{shopName}
</if>
<if test="brand != null and brand != ''">
AND brand = #{brand}
</if>
<if test="productCode != null and productCode != ''">
AND product_code LIKE CONCAT('%', #{productCode}, '%')
</if>
GROUP BY shop_name
</select>
<select id="selectByProduct" resultType="java.util.Map">
select product_code, SUM(quantity) AS totalQuantity,SUM(total_amount) AS totalAmount
FROM zs_tjfx_analysis_data
WHERE date BETWEEN #{startDate} AND #{endDate}
AND status = 1
<if test="category != null and category != ''">
AND category = #{category}
</if>
<if test="productCodeList !=null and productCodeList.size() > 0">
AND product_code in
<foreach collection="productCodeList" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>

<if test="customerNameList !=null and customerNameList.size()>0">
AND customer_name in
<foreach collection="customerNameList" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="shopNameList !=null and shopNameList.size()>0">
AND shop_name in
<foreach collection="shopNameList" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="brandList !=null and brandList.size()>0">
AND brand in
<foreach collection="brandList" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>

<if test="sql != null and sql != ''">
AND ${sql}
</if>
GROUP BY product_code
</select>

<select id="selectByCustomer" resultType="java.util.Map">
select customer_name, SUM(quantity) AS totalQuantity,SUM(total_amount) AS totalAmount
FROM zs_tjfx_analysis_data
WHERE date BETWEEN #{startDate} AND #{endDate}
AND status = 1
<if test="category != null and category != ''">
AND category = #{category}
</if>
<if test="productCodeList !=null and productCodeList.size() > 0">
AND product_code in
<foreach collection="productCodeList" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>

<if test="customerNameList !=null and customerNameList.size()>0">
AND customer_name in
<foreach collection="customerNameList" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="shopNameList !=null and shopNameList.size()>0">
AND shop_name in
<foreach collection="shopNameList" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="brandList !=null and brandList.size()>0">
AND brand in
<foreach collection="brandList" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>

<if test="sql != null and sql != ''">
AND ${sql}
</if>
GROUP BY customer_name
</select>

<select id="selectBySpecification" resultType="java.util.Map">
select category_specs,SUM(quantity) AS totalQuantity,SUM(total_amount) AS totalAmount
FROM zs_tjfx_analysis_data
WHERE date BETWEEN #{startDate} AND #{endDate}
AND status = 1
<if test="category != null and category != ''">
AND category = #{category}
</if>
<if test="productCodeList !=null and productCodeList.size() > 0">
AND product_code in
<foreach collection="productCodeList" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>

<if test="customerNameList !=null and customerNameList.size()>0">
AND customer_name in
<foreach collection="customerNameList" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="shopNameList !=null and shopNameList.size()>0">
AND shop_name in
<foreach collection="shopNameList" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="brandList !=null and brandList.size()>0">
AND brand in
<foreach collection="brandList" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>

<if test="sql != null and sql != ''">
AND ${sql}
</if>
GROUP BY category_specs
</select>

<select id="selectByBrand" resultType="java.util.Map">
select brand,SUM(quantity) AS totalQuantity,SUM(total_amount) AS totalAmount
FROM zs_tjfx_analysis_data
WHERE date BETWEEN #{startDate} AND #{endDate}
AND status = 1
<if test="category != null and category != ''">
AND category = #{category}
</if>
<if test="productCodeList !=null and productCodeList.size() > 0">
AND product_code in
<foreach collection="productCodeList" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>

<if test="customerNameList !=null and customerNameList.size()>0">
AND customer_name in
<foreach collection="customerNameList" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="shopNameList !=null and shopNameList.size()>0">
AND shop_name in
<foreach collection="shopNameList" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="brandList !=null and brandList.size()>0">
AND brand in
<foreach collection="brandList" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>

<if test="sql != null and sql != ''">
AND ${sql}
</if>
GROUP BY brand
</select>

<select id="selectByShopName" resultType="java.util.Map">
select shop_name,SUM(quantity) AS totalQuantity,SUM(total_amount) AS totalAmount
FROM zs_tjfx_analysis_data
WHERE date BETWEEN #{startDate} AND #{endDate}
AND status = 1
<if test="category != null and category != ''">
AND category = #{category}
</if>
<if test="productCodeList !=null and productCodeList.size() > 0">
AND product_code in
<foreach collection="productCodeList" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>

<if test="customerNameList !=null and customerNameList.size()>0">
AND customer_name in
<foreach collection="customerNameList" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="shopNameList !=null and shopNameList.size()>0">
AND shop_name in
<foreach collection="shopNameList" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="brandList !=null and brandList.size()>0">
AND brand in
<foreach collection="brandList" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>

<if test="sql != null and sql != ''">
AND ${sql}
</if>
GROUP BY shop_name
</select>
</mapper>

Loading…
Cancel
Save