还差回退机制

This commit is contained in:
shenquanyi
2025-12-05 18:15:08 +08:00
parent 1ba3fb0c85
commit 5c8e828c60
6 changed files with 790 additions and 773 deletions

View File

@@ -392,7 +392,7 @@ public class CattleDataController {
@ResponseStatus(HttpStatus.CREATED) @ResponseStatus(HttpStatus.CREATED)
public Result<CattleData> createCattleData( public Result<CattleData> createCattleData(
@ApiParam(value = "牛只数据信息", required = true) @ApiParam(value = "牛只数据信息", required = true)
@Valid @RequestBody CattleDataDTO dto) { @Valid @ModelAttribute CattleDataDTO dto) {
CattleData data = cattleDataService.createCattleData(dto); CattleData data = cattleDataService.createCattleData(dto);
return Result.success("创建成功", data); return Result.success("创建成功", data);
} }

View File

@@ -1,94 +1,94 @@
package com.example.cattletends.entity; package com.example.cattletends.entity;
import javax.persistence.*; import javax.persistence.*;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
/** /**
* 牛只数据实体类 * 牛只数据实体类
*/ */
@Entity @Entity
@Table(name = "cattleData", indexes = { @Table(name = "cattleData", indexes = {
@Index(name = "idx_price", columnList = "price"), @Index(name = "idx_price", columnList = "price"),
@Index(name = "idx_type", columnList = "type"), @Index(name = "idx_type", columnList = "type"),
@Index(name = "idx_province", columnList = "province"), @Index(name = "idx_province", columnList = "province"),
@Index(name = "idx_type_price", columnList = "type,price"), @Index(name = "idx_type_price", columnList = "type,price"),
@Index(name = "idx_province_price", columnList = "province,price"), @Index(name = "idx_province_price", columnList = "province,price"),
@Index(name = "idx_type_province_location_price", columnList = "type,province,location,price") @Index(name = "idx_type_province_location_price", columnList = "type,province,location,price")
}) })
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class CattleData { public class CattleData {
/** /**
* 主键ID * 主键ID
*/ */
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false, updatable = false) @Column(name = "id", nullable = false, updatable = false)
private Integer id; private Integer id;
/** /**
* 创建时间 * 创建时间
*/ */
@Column(name = "create_time") @Column(name = "create_time")
private LocalDateTime createTime; private LocalDateTime createTime;
/** /**
* 品种 * 品种
*/ */
@Column(name = "type", length = 255) @Column(name = "type", length = 255)
private String type; private String type;
/** /**
* 省份 * 省份
*/ */
@Column(name = "province", length = 255, nullable = false) @Column(name = "province", length = 255, nullable = false)
private String province; private String province;
/** /**
* 所在产地 * 所在产地
*/ */
@Column(name = "location", length = 255) @Column(name = "location", length = 255)
private String location; private String location;
/** /**
* 价格(元/斤) * 价格(元/斤)
*/ */
@Column(name = "price", precision = 10, scale = 2) @Column(name = "price", precision = 10, scale = 2)
private BigDecimal price; private BigDecimal price;
/** /**
* 更新时间 * 更新时间
*/ */
@Column(name = "up_time", nullable = false) @Column(name = "up_time", nullable = false)
private LocalDateTime upTime; private LocalDateTime upTime;
/** /**
* 保存前自动设置创建时间和更新时间 * 保存前自动设置创建时间和更新时间
*/ */
@PrePersist @PrePersist
protected void onCreate() { protected void onCreate() {
LocalDateTime now = LocalDateTime.now(); LocalDateTime now = LocalDateTime.now();
if (createTime == null) { if (createTime == null) {
createTime = now; createTime = now;
} }
if (upTime == null) { if (upTime == null) {
upTime = now; upTime = now;
} }
} }
/** /**
* 更新前自动设置更新时间 * 更新前自动设置更新时间
*/ */
@PreUpdate @PreUpdate
protected void onUpdate() { protected void onUpdate() {
upTime = LocalDateTime.now(); upTime = LocalDateTime.now();
} }
} }

View File

@@ -4,6 +4,7 @@ import com.example.cattletends.common.Result;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.FieldError; import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ExceptionHandler;
@@ -37,6 +38,22 @@ public class GlobalExceptionHandler {
return Result.error(400, "参数校验失败", errors); return Result.error(400, "参数校验失败", errors);
} }
/**
* 处理请求体缺失或格式错误异常
*/
@ExceptionHandler(HttpMessageNotReadableException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Result<Void> handleHttpMessageNotReadableException(HttpMessageNotReadableException ex) {
String message = ex.getMessage();
if (message != null && message.contains("Required request body is missing")) {
logger.warn("请求体缺失: {}", ex.getMessage());
return Result.error(400, "请求体不能为空请提供JSON格式的请求数据");
} else {
logger.warn("请求体格式错误: {}", ex.getMessage());
return Result.error(400, "请求体格式错误请检查JSON格式是否正确");
}
}
/** /**
* 处理业务异常 * 处理业务异常
*/ */

View File

@@ -1,123 +1,123 @@
package com.example.cattletends.repository; package com.example.cattletends.repository;
import com.example.cattletends.entity.CattleData; import com.example.cattletends.entity.CattleData;
import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
/** /**
* 牛只数据Repository接口 * 牛只数据Repository接口
*/ */
@Repository @Repository
public interface CattleDataRepository extends JpaRepository<CattleData, Integer> { public interface CattleDataRepository extends JpaRepository<CattleData, Integer> {
/** /**
* 根据省份查询牛只数据,按价格升序排序 * 根据省份查询牛只数据,按价格升序排序
* *
* @param province 省份 * @param province 省份
* @param sort 排序规则 * @param sort 排序规则
* @return 牛只数据列表 * @return 牛只数据列表
*/ */
List<CattleData> findByProvince(String province, Sort sort); List<CattleData> findByProvince(String province, Sort sort);
/** /**
* 根据品种查询牛只数据,按价格升序排序 * 根据品种查询牛只数据,按价格升序排序
* *
* @param type 品种 * @param type 品种
* @param sort 排序规则 * @param sort 排序规则
* @return 牛只数据列表 * @return 牛只数据列表
*/ */
List<CattleData> findByType(String type, Sort sort); List<CattleData> findByType(String type, Sort sort);
/** /**
* 根据品种、省份、产地、价格查询牛只数据(用于判断重复) * 根据品种、省份、产地、价格查询牛只数据(用于判断重复)
* 注意:使用 findFirstBy避免因为存在多条相同记录导致 NonUniqueResultException * 注意:使用 findFirstBy避免因为存在多条相同记录导致 NonUniqueResultException
* *
* @param type 品种 * @param type 品种
* @param province 省份 * @param province 省份
* @param location 产地 * @param location 产地
* @param price 价格 * @param price 价格
* @return 第一条匹配的牛只数据(如果存在) * @return 第一条匹配的牛只数据(如果存在)
*/ */
Optional<CattleData> findFirstByTypeAndProvinceAndLocationAndPrice( Optional<CattleData> findFirstByTypeAndProvinceAndLocationAndPrice(
String type, String province, String location, BigDecimal price); String type, String province, String location, BigDecimal price);
/** /**
* 根据品种和产地查询牛只数据(用于判断重复) * 根据品种和产地查询牛只数据(用于判断重复)
* 注意:使用 findFirstBy避免因为存在多条相同记录导致 NonUniqueResultException * 注意:使用 findFirstBy避免因为存在多条相同记录导致 NonUniqueResultException
* *
* @param type 品种 * @param type 品种
* @param location 产地 * @param location 产地
* @return 第一条匹配的牛只数据(如果存在) * @return 第一条匹配的牛只数据(如果存在)
*/ */
Optional<CattleData> findFirstByTypeAndLocation(String type, String location); Optional<CattleData> findFirstByTypeAndLocation(String type, String location);
/** /**
* 根据品种和产地查询所有匹配的牛只数据(用于删除重复记录) * 根据品种和产地查询所有匹配的牛只数据(用于删除重复记录)
* 使用 TRIM 函数来匹配,忽略前后空格 * 使用 TRIM 函数来匹配,忽略前后空格
* *
* @param type 品种 * @param type 品种
* @param location 产地 * @param location 产地
* @return 所有匹配的牛只数据列表 * @return 所有匹配的牛只数据列表
*/ */
@org.springframework.data.jpa.repository.Query("SELECT c FROM CattleData c WHERE TRIM(c.type) = TRIM(?1) AND TRIM(c.location) = TRIM(?2)") @org.springframework.data.jpa.repository.Query("SELECT c FROM CattleData c WHERE TRIM(c.type) = TRIM(?1) AND TRIM(c.location) = TRIM(?2)")
List<CattleData> findByTypeAndLocation(String type, String location); List<CattleData> findByTypeAndLocation(String type, String location);
/** /**
* 获取所有不重复的省份列表 * 获取所有不重复的省份列表
* *
* @return 省份列表 * @return 省份列表
*/ */
@org.springframework.data.jpa.repository.Query("SELECT DISTINCT c.province FROM CattleData c WHERE c.province IS NOT NULL AND c.province != ''") @org.springframework.data.jpa.repository.Query("SELECT DISTINCT c.province FROM CattleData c WHERE c.province IS NOT NULL AND c.province != ''")
List<String> findAllDistinctProvinces(); List<String> findAllDistinctProvinces();
/** /**
* 根据日期范围查询牛只数据(用于删除当天数据) * 根据日期范围查询牛只数据(用于删除当天数据)
* *
* @param startTime 开始时间当天00:00:00 * @param startTime 开始时间当天00:00:00
* @param endTime 结束时间当天23:59:59 * @param endTime 结束时间当天23:59:59
* @return 该日期范围内的所有数据 * @return 该日期范围内的所有数据
*/ */
@org.springframework.data.jpa.repository.Query("SELECT c FROM CattleData c WHERE c.createTime >= ?1 AND c.createTime <= ?2") @org.springframework.data.jpa.repository.Query("SELECT c FROM CattleData c WHERE c.createTime >= ?1 AND c.createTime <= ?2")
List<CattleData> findByCreateTimeBetween(LocalDateTime startTime, LocalDateTime endTime); List<CattleData> findByCreateTimeBetween(LocalDateTime startTime, LocalDateTime endTime);
/** /**
* 根据日期查询当天的牛只数据(用于查询接口) * 根据日期查询当天的牛只数据(用于查询接口)
* 使用DATE()函数比较日期部分,忽略时间部分 * 使用DATE()函数比较日期部分,忽略时间部分
* *
* @param date 日期(只比较日期部分,忽略时间) * @param date 日期(只比较日期部分,忽略时间)
* @param sort 排序规则 * @param sort 排序规则
* @return 当天的数据列表 * @return 当天的数据列表
*/ */
@org.springframework.data.jpa.repository.Query("SELECT c FROM CattleData c WHERE DATE(c.createTime) = DATE(?1)") @org.springframework.data.jpa.repository.Query("SELECT c FROM CattleData c WHERE DATE(c.createTime) = DATE(?1)")
List<CattleData> findByCreateTimeDate(LocalDate date, Sort sort); List<CattleData> findByCreateTimeDate(LocalDate date, Sort sort);
/** /**
* 根据省份和日期查询当天的牛只数据 * 根据省份和日期查询当天的牛只数据
* *
* @param province 省份 * @param province 省份
* @param date 日期 * @param date 日期
* @param sort 排序规则 * @param sort 排序规则
* @return 该省份当天的数据列表 * @return 该省份当天的数据列表
*/ */
@org.springframework.data.jpa.repository.Query("SELECT c FROM CattleData c WHERE c.province = ?1 AND DATE(c.createTime) = DATE(?2)") @org.springframework.data.jpa.repository.Query("SELECT c FROM CattleData c WHERE c.province = ?1 AND DATE(c.createTime) = DATE(?2)")
List<CattleData> findByProvinceAndCreateTimeDate(String province, LocalDate date, Sort sort); List<CattleData> findByProvinceAndCreateTimeDate(String province, LocalDate date, Sort sort);
/** /**
* 根据品种和日期查询当天的牛只数据 * 根据品种和日期查询当天的牛只数据
* *
* @param type 品种 * @param type 品种
* @param date 日期 * @param date 日期
* @param sort 排序规则 * @param sort 排序规则
* @return 该品种当天的数据列表 * @return 该品种当天的数据列表
*/ */
@org.springframework.data.jpa.repository.Query("SELECT c FROM CattleData c WHERE c.type = ?1 AND DATE(c.createTime) = DATE(?2)") @org.springframework.data.jpa.repository.Query("SELECT c FROM CattleData c WHERE c.type = ?1 AND DATE(c.createTime) = DATE(?2)")
List<CattleData> findByTypeAndCreateTimeDate(String type, LocalDate date, Sort sort); List<CattleData> findByTypeAndCreateTimeDate(String type, LocalDate date, Sort sort);
} }

View File

@@ -1,82 +1,82 @@
package com.example.cattletends.service; package com.example.cattletends.service;
import com.example.cattletends.dto.CattleDataDTO; import com.example.cattletends.dto.CattleDataDTO;
import com.example.cattletends.dto.ImportResult; import com.example.cattletends.dto.ImportResult;
import com.example.cattletends.entity.CattleData; import com.example.cattletends.entity.CattleData;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
* 牛只数据服务接口 * 牛只数据服务接口
*/ */
public interface CattleDataService { public interface CattleDataService {
/** /**
* 获取所有牛只数据 * 获取所有牛只数据
* *
* @param date 日期可选如果为null则查询今天的数据 * @param date 日期可选如果为null则查询今天的数据
* @return 牛只数据列表 * @return 牛只数据列表
*/ */
List<CattleData> getAllCattleData(LocalDate date); List<CattleData> getAllCattleData(LocalDate date);
/** /**
* 根据省份获取牛只数据 * 根据省份获取牛只数据
* *
* @param province 省份 * @param province 省份
* @param date 日期可选如果为null则查询今天的数据 * @param date 日期可选如果为null则查询今天的数据
* @return 牛只数据列表 * @return 牛只数据列表
*/ */
List<CattleData> getCattleDataByProvince(String province, LocalDate date); List<CattleData> getCattleDataByProvince(String province, LocalDate date);
/** /**
* 根据品种获取牛只数据 * 根据品种获取牛只数据
* *
* @param type 品种 * @param type 品种
* @param date 日期可选如果为null则查询今天的数据 * @param date 日期可选如果为null则查询今天的数据
* @return 牛只数据列表 * @return 牛只数据列表
*/ */
List<CattleData> getCattleDataByType(String type, LocalDate date); List<CattleData> getCattleDataByType(String type, LocalDate date);
/** /**
* 根据ID获取牛只数据 * 根据ID获取牛只数据
* *
* @param id 主键ID * @param id 主键ID
* @return 牛只数据 * @return 牛只数据
*/ */
CattleData getCattleDataById(Integer id); CattleData getCattleDataById(Integer id);
/** /**
* 创建牛只数据 * 创建牛只数据
* *
* @param dto 牛只数据传输对象 * @param dto 牛只数据传输对象
* @return 创建的牛只数据 * @return 创建的牛只数据
*/ */
CattleData createCattleData(CattleDataDTO dto); CattleData createCattleData(CattleDataDTO dto);
/** /**
* 更新牛只数据 * 更新牛只数据
* *
* @param id 主键ID * @param id 主键ID
* @param dto 牛只数据传输对象 * @param dto 牛只数据传输对象
* @return 更新后的牛只数据 * @return 更新后的牛只数据
*/ */
CattleData updateCattleData(Integer id, CattleDataDTO dto); CattleData updateCattleData(Integer id, CattleDataDTO dto);
/** /**
* 删除牛只数据 * 删除牛只数据
* *
* @param id 主键ID * @param id 主键ID
*/ */
void deleteCattleData(Integer id); void deleteCattleData(Integer id);
/** /**
* 批量导入牛只数据 * 批量导入牛只数据
* *
* @param dataList 牛只数据列表 * @param dataList 牛只数据列表
* @return 导入结果(包含数据列表、新增数量、更新数量) * @return 导入结果(包含数据列表、新增数量、更新数量)
*/ */
ImportResult batchImportCattleData(List<CattleDataDTO> dataList); ImportResult batchImportCattleData(List<CattleDataDTO> dataList);
} }

View File

@@ -1,473 +1,473 @@
package com.example.cattletends.service.impl; package com.example.cattletends.service.impl;
import com.example.cattletends.dto.CattleDataDTO; import com.example.cattletends.dto.CattleDataDTO;
import com.example.cattletends.dto.ImportResult; import com.example.cattletends.dto.ImportResult;
import com.example.cattletends.entity.CattleData; import com.example.cattletends.entity.CattleData;
import com.example.cattletends.repository.CattleDataRepository; import com.example.cattletends.repository.CattleDataRepository;
import com.example.cattletends.service.CattleDataService; import com.example.cattletends.service.CattleDataService;
import com.example.cattletends.service.ProvinceDailyPriceService; import com.example.cattletends.service.ProvinceDailyPriceService;
import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
* 牛只数据服务实现类 * 牛只数据服务实现类
*/ */
@Service @Service
public class CattleDataServiceImpl implements CattleDataService { public class CattleDataServiceImpl implements CattleDataService {
private final CattleDataRepository cattleDataRepository; private final CattleDataRepository cattleDataRepository;
private final ProvinceDailyPriceService provinceDailyPriceService; private final ProvinceDailyPriceService provinceDailyPriceService;
private final com.example.cattletends.service.CattleProvinceService cattleProvinceService; private final com.example.cattletends.service.CattleProvinceService cattleProvinceService;
private final com.example.cattletends.repository.CattleProvinceRepository cattleProvinceRepository; private final com.example.cattletends.repository.CattleProvinceRepository cattleProvinceRepository;
/** /**
* 构造器注入 * 构造器注入
*/ */
public CattleDataServiceImpl(CattleDataRepository cattleDataRepository, public CattleDataServiceImpl(CattleDataRepository cattleDataRepository,
ProvinceDailyPriceService provinceDailyPriceService, ProvinceDailyPriceService provinceDailyPriceService,
com.example.cattletends.service.CattleProvinceService cattleProvinceService, com.example.cattletends.service.CattleProvinceService cattleProvinceService,
com.example.cattletends.repository.CattleProvinceRepository cattleProvinceRepository) { com.example.cattletends.repository.CattleProvinceRepository cattleProvinceRepository) {
this.cattleDataRepository = cattleDataRepository; this.cattleDataRepository = cattleDataRepository;
this.provinceDailyPriceService = provinceDailyPriceService; this.provinceDailyPriceService = provinceDailyPriceService;
this.cattleProvinceService = cattleProvinceService; this.cattleProvinceService = cattleProvinceService;
this.cattleProvinceRepository = cattleProvinceRepository; this.cattleProvinceRepository = cattleProvinceRepository;
} }
@Override @Override
@Transactional @Transactional
public List<CattleData> getAllCattleData(LocalDate date) { public List<CattleData> getAllCattleData(LocalDate date) {
// 如果date为null使用今天 // 如果date为null使用今天
LocalDate queryDate = date != null ? date : LocalDate.now(); LocalDate queryDate = date != null ? date : LocalDate.now();
Sort sort = Sort.by(Sort.Direction.ASC, "price"); Sort sort = Sort.by(Sort.Direction.ASC, "price");
// 查询指定日期的数据 // 查询指定日期的数据
List<CattleData> dataList = cattleDataRepository.findByCreateTimeDate(queryDate, sort); List<CattleData> dataList = cattleDataRepository.findByCreateTimeDate(queryDate, sort);
return dataList; return dataList;
} }
@Override @Override
@Transactional @Transactional
public List<CattleData> getCattleDataByProvince(String province, LocalDate date) { public List<CattleData> getCattleDataByProvince(String province, LocalDate date) {
// 如果date为null使用今天 // 如果date为null使用今天
LocalDate queryDate = date != null ? date : LocalDate.now(); LocalDate queryDate = date != null ? date : LocalDate.now();
Sort sort = Sort.by(Sort.Direction.ASC, "price"); Sort sort = Sort.by(Sort.Direction.ASC, "price");
// 查询指定日期和省份的数据 // 查询指定日期和省份的数据
List<CattleData> dataList = cattleDataRepository.findByProvinceAndCreateTimeDate(province.trim(), queryDate, sort); List<CattleData> dataList = cattleDataRepository.findByProvinceAndCreateTimeDate(province.trim(), queryDate, sort);
return dataList; return dataList;
} }
@Override @Override
@Transactional @Transactional
public List<CattleData> getCattleDataByType(String type, LocalDate date) { public List<CattleData> getCattleDataByType(String type, LocalDate date) {
// 如果date为null使用今天 // 如果date为null使用今天
LocalDate queryDate = date != null ? date : LocalDate.now(); LocalDate queryDate = date != null ? date : LocalDate.now();
Sort sort = Sort.by(Sort.Direction.ASC, "price"); Sort sort = Sort.by(Sort.Direction.ASC, "price");
// 查询指定日期和品种的数据 // 查询指定日期和品种的数据
List<CattleData> dataList = cattleDataRepository.findByTypeAndCreateTimeDate(type.trim(), queryDate, sort); List<CattleData> dataList = cattleDataRepository.findByTypeAndCreateTimeDate(type.trim(), queryDate, sort);
return dataList; return dataList;
} }
/** /**
* 按 location 字段去重处理 * 按 location 字段去重处理
* 如果同一个 location 有多条记录保留最新的一条up_time最大更新其他记录 * 如果同一个 location 有多条记录保留最新的一条up_time最大更新其他记录
* @param dataList 原始数据列表 * @param dataList 原始数据列表
* @param province 省份筛选条件(可选,用于重新查询时保持筛选) * @param province 省份筛选条件(可选,用于重新查询时保持筛选)
* @param type 品种筛选条件(可选,用于重新查询时保持筛选) * @param type 品种筛选条件(可选,用于重新查询时保持筛选)
*/ */
private List<CattleData> deduplicateByLocation(List<CattleData> dataList, String province, String type) { private List<CattleData> deduplicateByLocation(List<CattleData> dataList, String province, String type) {
if (dataList == null || dataList.isEmpty()) { if (dataList == null || dataList.isEmpty()) {
return dataList; return dataList;
} }
System.out.println("[查询去重] 开始按 type + location 去重处理"); System.out.println("[查询去重] 开始按 type + location 去重处理");
System.out.println("[查询去重] 去重前记录数: " + dataList.size()); System.out.println("[查询去重] 去重前记录数: " + dataList.size());
// 按 type + location 分组,找出每个 type + location 组合的最新记录 // 按 type + location 分组,找出每个 type + location 组合的最新记录
java.util.Map<String, CattleData> typeLocationMap = new java.util.HashMap<>(); java.util.Map<String, CattleData> typeLocationMap = new java.util.HashMap<>();
java.util.List<CattleData> duplicatesToUpdate = new java.util.ArrayList<>(); java.util.List<CattleData> duplicatesToUpdate = new java.util.ArrayList<>();
for (CattleData item : dataList) { for (CattleData item : dataList) {
String itemType = item.getType(); String itemType = item.getType();
String location = item.getLocation(); String location = item.getLocation();
if ((itemType == null || itemType.trim().isEmpty()) || if ((itemType == null || itemType.trim().isEmpty()) ||
(location == null || location.trim().isEmpty())) { (location == null || location.trim().isEmpty())) {
continue; continue;
} }
String trimmedType = itemType.trim(); String trimmedType = itemType.trim();
String trimmedLocation = location.trim(); String trimmedLocation = location.trim();
String key = trimmedType + "|" + trimmedLocation; // 使用 | 作为分隔符 String key = trimmedType + "|" + trimmedLocation; // 使用 | 作为分隔符
if (!typeLocationMap.containsKey(key)) { if (!typeLocationMap.containsKey(key)) {
// 第一次遇到该 type + location 组合,直接添加 // 第一次遇到该 type + location 组合,直接添加
typeLocationMap.put(key, item); typeLocationMap.put(key, item);
} else { } else {
// 已存在该 type + location 组合,比较更新时间,保留最新的 // 已存在该 type + location 组合,比较更新时间,保留最新的
CattleData existing = typeLocationMap.get(key); CattleData existing = typeLocationMap.get(key);
if (item.getUpTime() != null && existing.getUpTime() != null) { if (item.getUpTime() != null && existing.getUpTime() != null) {
if (item.getUpTime().isAfter(existing.getUpTime())) { if (item.getUpTime().isAfter(existing.getUpTime())) {
// 当前记录更新,标记旧记录需要更新 // 当前记录更新,标记旧记录需要更新
duplicatesToUpdate.add(existing); duplicatesToUpdate.add(existing);
typeLocationMap.put(key, item); typeLocationMap.put(key, item);
} else { } else {
// 现有记录更新,标记当前记录需要更新 // 现有记录更新,标记当前记录需要更新
duplicatesToUpdate.add(item); duplicatesToUpdate.add(item);
} }
} else if (item.getUpTime() != null) { } else if (item.getUpTime() != null) {
// 当前记录有更新时间,保留它 // 当前记录有更新时间,保留它
duplicatesToUpdate.add(existing); duplicatesToUpdate.add(existing);
typeLocationMap.put(key, item); typeLocationMap.put(key, item);
} else if (existing.getUpTime() != null) { } else if (existing.getUpTime() != null) {
// 现有记录有更新时间,保留现有记录 // 现有记录有更新时间,保留现有记录
duplicatesToUpdate.add(item); duplicatesToUpdate.add(item);
} else { } else {
// 都没有更新时间保留ID较小的通常是最早创建的 // 都没有更新时间保留ID较小的通常是最早创建的
if (item.getId() < existing.getId()) { if (item.getId() < existing.getId()) {
duplicatesToUpdate.add(existing); duplicatesToUpdate.add(existing);
typeLocationMap.put(key, item); typeLocationMap.put(key, item);
} else { } else {
duplicatesToUpdate.add(item); duplicatesToUpdate.add(item);
} }
} }
} }
} }
// 删除重复记录:只保留最新的记录,删除其他所有重复记录 // 删除重复记录:只保留最新的记录,删除其他所有重复记录
int deletedCount = 0; int deletedCount = 0;
for (CattleData duplicate : duplicatesToUpdate) { for (CattleData duplicate : duplicatesToUpdate) {
String duplicateType = duplicate.getType() != null ? duplicate.getType().trim() : ""; String duplicateType = duplicate.getType() != null ? duplicate.getType().trim() : "";
String duplicateLocation = duplicate.getLocation() != null ? duplicate.getLocation().trim() : ""; String duplicateLocation = duplicate.getLocation() != null ? duplicate.getLocation().trim() : "";
String key = duplicateType + "|" + duplicateLocation; String key = duplicateType + "|" + duplicateLocation;
CattleData latest = typeLocationMap.get(key); CattleData latest = typeLocationMap.get(key);
if (latest != null && !latest.getId().equals(duplicate.getId())) { if (latest != null && !latest.getId().equals(duplicate.getId())) {
// 删除重复记录,只保留最新的 // 删除重复记录,只保留最新的
System.out.println("[查询去重] 删除重复记录: ID=" + duplicate.getId() + ", type=[" + duplicateType + "], location=[" + duplicateLocation + "]"); System.out.println("[查询去重] 删除重复记录: ID=" + duplicate.getId() + ", type=[" + duplicateType + "], location=[" + duplicateLocation + "]");
cattleDataRepository.delete(duplicate); cattleDataRepository.delete(duplicate);
deletedCount++; deletedCount++;
} }
} }
// 如果删除了重复记录,重新查询(保持原有的筛选条件) // 如果删除了重复记录,重新查询(保持原有的筛选条件)
if (deletedCount > 0) { if (deletedCount > 0) {
System.out.println("[查询去重] 删除了 " + deletedCount + " 条重复记录"); System.out.println("[查询去重] 删除了 " + deletedCount + " 条重复记录");
// 重新查询数据(保持原有排序和筛选条件) // 重新查询数据(保持原有排序和筛选条件)
Sort sort = Sort.by(Sort.Direction.ASC, "price"); Sort sort = Sort.by(Sort.Direction.ASC, "price");
if (type != null && !type.trim().isEmpty()) { if (type != null && !type.trim().isEmpty()) {
dataList = cattleDataRepository.findByType(type.trim(), sort); dataList = cattleDataRepository.findByType(type.trim(), sort);
} else if (province != null && !province.trim().isEmpty()) { } else if (province != null && !province.trim().isEmpty()) {
dataList = cattleDataRepository.findByProvince(province.trim(), sort); dataList = cattleDataRepository.findByProvince(province.trim(), sort);
} else { } else {
dataList = cattleDataRepository.findAll(sort); dataList = cattleDataRepository.findAll(sort);
} }
} }
// 最终去重:按 type + location 去重,每个 type + location 组合只保留一条(保留最新的) // 最终去重:按 type + location 去重,每个 type + location 组合只保留一条(保留最新的)
java.util.Map<String, CattleData> finalMap = new java.util.LinkedHashMap<>(); java.util.Map<String, CattleData> finalMap = new java.util.LinkedHashMap<>();
for (CattleData item : dataList) { for (CattleData item : dataList) {
String itemType = item.getType(); String itemType = item.getType();
String location = item.getLocation(); String location = item.getLocation();
if ((itemType == null || itemType.trim().isEmpty()) || if ((itemType == null || itemType.trim().isEmpty()) ||
(location == null || location.trim().isEmpty())) { (location == null || location.trim().isEmpty())) {
continue; continue;
} }
String trimmedType = itemType.trim(); String trimmedType = itemType.trim();
String trimmedLocation = location.trim(); String trimmedLocation = location.trim();
String key = trimmedType + "|" + trimmedLocation; String key = trimmedType + "|" + trimmedLocation;
if (!finalMap.containsKey(key)) { if (!finalMap.containsKey(key)) {
finalMap.put(key, item); finalMap.put(key, item);
} else { } else {
CattleData existing = finalMap.get(key); CattleData existing = finalMap.get(key);
if (item.getUpTime() != null && existing.getUpTime() != null) { if (item.getUpTime() != null && existing.getUpTime() != null) {
if (item.getUpTime().isAfter(existing.getUpTime())) { if (item.getUpTime().isAfter(existing.getUpTime())) {
finalMap.put(key, item); finalMap.put(key, item);
} }
} else if (item.getUpTime() != null) { } else if (item.getUpTime() != null) {
finalMap.put(key, item); finalMap.put(key, item);
} else if (existing.getUpTime() != null) { } else if (existing.getUpTime() != null) {
// 保持现有记录 // 保持现有记录
} else { } else {
// 都没有更新时间保留ID较小的 // 都没有更新时间保留ID较小的
if (item.getId() < existing.getId()) { if (item.getId() < existing.getId()) {
finalMap.put(key, item); finalMap.put(key, item);
} }
} }
} }
} }
dataList = new java.util.ArrayList<>(finalMap.values()); dataList = new java.util.ArrayList<>(finalMap.values());
System.out.println("[查询去重] 去重后记录数: " + dataList.size()); System.out.println("[查询去重] 去重后记录数: " + dataList.size());
System.out.println("[查询去重] 去重处理完成"); System.out.println("[查询去重] 去重处理完成");
return dataList; return dataList;
} }
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public CattleData getCattleDataById(Integer id) { public CattleData getCattleDataById(Integer id) {
return cattleDataRepository.findById(id) return cattleDataRepository.findById(id)
.orElseThrow(() -> new RuntimeException("牛只数据不存在ID: " + id)); .orElseThrow(() -> new RuntimeException("牛只数据不存在ID: " + id));
} }
@Override @Override
@Transactional @Transactional
public CattleData createCattleData(CattleDataDTO dto) { public CattleData createCattleData(CattleDataDTO dto) {
CattleData cattleData = new CattleData(); CattleData cattleData = new CattleData();
cattleData.setType(dto.getType()); cattleData.setType(dto.getType());
cattleData.setProvince(dto.getProvince()); cattleData.setProvince(dto.getProvince());
cattleData.setLocation(dto.getLocation()); cattleData.setLocation(dto.getLocation());
cattleData.setPrice(dto.getPrice()); cattleData.setPrice(dto.getPrice());
return cattleDataRepository.save(cattleData); return cattleDataRepository.save(cattleData);
} }
@Override @Override
@Transactional @Transactional
public CattleData updateCattleData(Integer id, CattleDataDTO dto) { public CattleData updateCattleData(Integer id, CattleDataDTO dto) {
CattleData cattleData = cattleDataRepository.findById(id) CattleData cattleData = cattleDataRepository.findById(id)
.orElseThrow(() -> new RuntimeException("牛只数据不存在ID: " + id)); .orElseThrow(() -> new RuntimeException("牛只数据不存在ID: " + id));
cattleData.setType(dto.getType()); cattleData.setType(dto.getType());
cattleData.setProvince(dto.getProvince()); cattleData.setProvince(dto.getProvince());
cattleData.setLocation(dto.getLocation()); cattleData.setLocation(dto.getLocation());
cattleData.setPrice(dto.getPrice()); cattleData.setPrice(dto.getPrice());
return cattleDataRepository.save(cattleData); return cattleDataRepository.save(cattleData);
} }
@Override @Override
@Transactional @Transactional
public void deleteCattleData(Integer id) { public void deleteCattleData(Integer id) {
if (!cattleDataRepository.existsById(id)) { if (!cattleDataRepository.existsById(id)) {
throw new RuntimeException("牛只数据不存在ID: " + id); throw new RuntimeException("牛只数据不存在ID: " + id);
} }
cattleDataRepository.deleteById(id); cattleDataRepository.deleteById(id);
} }
@Override @Override
@Transactional @Transactional
public ImportResult batchImportCattleData(List<CattleDataDTO> dataList) { public ImportResult batchImportCattleData(List<CattleDataDTO> dataList) {
List<CattleData> savedList = new ArrayList<>(); List<CattleData> savedList = new ArrayList<>();
int newCount = 0; int newCount = 0;
int updateCount = 0; // 保留字段以兼容返回类型但始终为0 int updateCount = 0; // 保留字段以兼容返回类型但始终为0
// 确定导入日期:统一使用导入时的当前日期 // 确定导入日期:统一使用导入时的当前日期
LocalDate importDate = LocalDate.now(); LocalDate importDate = LocalDate.now();
LocalDateTime startOfDay = importDate.atStartOfDay(); // 当天 00:00:00 LocalDateTime startOfDay = importDate.atStartOfDay(); // 当天 00:00:00
LocalDateTime endOfDay = importDate.atTime(23, 59, 59, 999999999); // 当天 23:59:59.999999999 LocalDateTime endOfDay = importDate.atTime(23, 59, 59, 999999999); // 当天 23:59:59.999999999
System.out.println("========== 开始导入数据,导入日期: " + importDate + " =========="); System.out.println("========== 开始导入数据,导入日期: " + importDate + " ==========");
// 删除当天所有已存在的数据 // 删除当天所有已存在的数据
List<CattleData> todayData = cattleDataRepository.findByCreateTimeBetween(startOfDay, endOfDay); List<CattleData> todayData = cattleDataRepository.findByCreateTimeBetween(startOfDay, endOfDay);
if (todayData != null && !todayData.isEmpty()) { if (todayData != null && !todayData.isEmpty()) {
System.out.println("========== 删除当天已存在的数据: " + todayData.size() + " 条 =========="); System.out.println("========== 删除当天已存在的数据: " + todayData.size() + " 条 ==========");
cattleDataRepository.deleteAll(todayData); cattleDataRepository.deleteAll(todayData);
} }
// 只新增,不做去重和更新 // 只新增,不做去重和更新
LocalDateTime importDateTime = LocalDateTime.now(); // 统一使用导入时的当前时间 LocalDateTime importDateTime = LocalDateTime.now(); // 统一使用导入时的当前时间
for (CattleDataDTO dto : dataList) { for (CattleDataDTO dto : dataList) {
// 验证必填字段 // 验证必填字段
String type = dto.getType() != null ? dto.getType().trim() : null; String type = dto.getType() != null ? dto.getType().trim() : null;
String location = dto.getLocation() != null ? dto.getLocation().trim() : null; String location = dto.getLocation() != null ? dto.getLocation().trim() : null;
if (type == null || type.isEmpty() || location == null || location.isEmpty()) { if (type == null || type.isEmpty() || location == null || location.isEmpty()) {
continue; // 跳过无效数据 continue; // 跳过无效数据
} }
// 创建新记录 // 创建新记录
CattleData cattleData = new CattleData(); CattleData cattleData = new CattleData();
cattleData.setType(type); cattleData.setType(type);
cattleData.setProvince(dto.getProvince() != null ? dto.getProvince().trim() : null); cattleData.setProvince(dto.getProvince() != null ? dto.getProvince().trim() : null);
cattleData.setLocation(location); cattleData.setLocation(location);
cattleData.setPrice(dto.getPrice()); cattleData.setPrice(dto.getPrice());
// 统一设置create_time为导入时的当前时间 // 统一设置create_time为导入时的当前时间
cattleData.setCreateTime(importDateTime); cattleData.setCreateTime(importDateTime);
CattleData saved = cattleDataRepository.save(cattleData); CattleData saved = cattleDataRepository.save(cattleData);
savedList.add(saved); savedList.add(saved);
newCount++; newCount++;
System.out.println("[新增] 创建新记录: ID=" + saved.getId() + ", type=[" + type + "], location=[" + location + "], price=" + dto.getPrice()); System.out.println("[新增] 创建新记录: ID=" + saved.getId() + ", type=[" + type + "], location=[" + location + "], price=" + dto.getPrice());
} }
System.out.println("========== 导入完成: 新增 " + newCount + " 条数据 =========="); System.out.println("========== 导入完成: 新增 " + newCount + " 条数据 ==========");
// 导入完成后,计算当天省份的平均价格并更新省份每日均价 // 导入完成后,计算当天省份的平均价格并更新省份每日均价
System.out.println("========== 开始计算当天省份平均价格并更新每日均价 =========="); System.out.println("========== 开始计算当天省份平均价格并更新每日均价 ==========");
calculateAndUpdateProvinceDailyPrices(importDate); calculateAndUpdateProvinceDailyPrices(importDate);
System.out.println("========== 省份平均价格计算完成 =========="); System.out.println("========== 省份平均价格计算完成 ==========");
return new ImportResult(savedList, newCount, updateCount); return new ImportResult(savedList, newCount, updateCount);
} }
/** /**
* 计算当天省份的平均价格并更新省份每日均价 * 计算当天省份的平均价格并更新省份每日均价
* 只计算当天导入的数据 * 只计算当天导入的数据
* *
* @param importDate 导入日期 * @param importDate 导入日期
*/ */
private void calculateAndUpdateProvinceDailyPrices(LocalDate importDate) { private void calculateAndUpdateProvinceDailyPrices(LocalDate importDate) {
try { try {
System.out.println("导入日期: " + importDate); System.out.println("导入日期: " + importDate);
// 获取所有15个省份从 cattleprovince 表) // 获取所有15个省份从 cattleprovince 表)
List<com.example.cattletends.entity.CattleProvince> allProvinces = cattleProvinceService.getAllProvinceData(null); List<com.example.cattletends.entity.CattleProvince> allProvinces = cattleProvinceService.getAllProvinceData(null);
System.out.println("获取到 " + (allProvinces != null ? allProvinces.size() : 0) + " 个省份"); System.out.println("获取到 " + (allProvinces != null ? allProvinces.size() : 0) + " 个省份");
if (allProvinces == null || allProvinces.isEmpty()) { if (allProvinces == null || allProvinces.isEmpty()) {
System.out.println("没有找到省份数据,跳过省份平均价格计算"); System.out.println("没有找到省份数据,跳过省份平均价格计算");
return; return;
} }
// 只查询当天导入的数据 // 只查询当天导入的数据
Sort sort = Sort.by(Sort.Direction.ASC, "price"); Sort sort = Sort.by(Sort.Direction.ASC, "price");
List<CattleData> todayDataList = cattleDataRepository.findByCreateTimeDate(importDate, sort); List<CattleData> todayDataList = cattleDataRepository.findByCreateTimeDate(importDate, sort);
if (todayDataList == null || todayDataList.isEmpty()) { if (todayDataList == null || todayDataList.isEmpty()) {
System.out.println("当天没有导入数据,跳过省份平均价格计算"); System.out.println("当天没有导入数据,跳过省份平均价格计算");
return; return;
} }
System.out.println("当天导入的数据条数: " + todayDataList.size()); System.out.println("当天导入的数据条数: " + todayDataList.size());
int updatedCount = 0; int updatedCount = 0;
int createdCount = 0; int createdCount = 0;
// 按省份分组,计算每个省份的平均价格 // 按省份分组,计算每个省份的平均价格
java.util.Map<String, java.util.Map<String, Object>> provinceStats = new java.util.HashMap<>(); java.util.Map<String, java.util.Map<String, Object>> provinceStats = new java.util.HashMap<>();
for (CattleData data : todayDataList) { for (CattleData data : todayDataList) {
if (data.getProvince() == null || data.getProvince().trim().isEmpty() || data.getPrice() == null) { if (data.getProvince() == null || data.getProvince().trim().isEmpty() || data.getPrice() == null) {
continue; continue;
} }
String originalProvince = data.getProvince().trim(); String originalProvince = data.getProvince().trim();
String canonicalProvince = resolveProvinceNameForMatch(originalProvince); String canonicalProvince = resolveProvinceNameForMatch(originalProvince);
if (provinceStats.containsKey(canonicalProvince)) { if (provinceStats.containsKey(canonicalProvince)) {
java.util.Map<String, Object> existing = provinceStats.get(canonicalProvince); java.util.Map<String, Object> existing = provinceStats.get(canonicalProvince);
java.math.BigDecimal existingSum = (java.math.BigDecimal) existing.get("sum"); java.math.BigDecimal existingSum = (java.math.BigDecimal) existing.get("sum");
int existingCount = (Integer) existing.get("count"); int existingCount = (Integer) existing.get("count");
existingSum = existingSum.add(data.getPrice()); existingSum = existingSum.add(data.getPrice());
existingCount++; existingCount++;
existing.put("sum", existingSum); existing.put("sum", existingSum);
existing.put("count", existingCount); existing.put("count", existingCount);
} else { } else {
java.util.Map<String, Object> stats = new java.util.HashMap<>(); java.util.Map<String, Object> stats = new java.util.HashMap<>();
stats.put("sum", data.getPrice()); stats.put("sum", data.getPrice());
stats.put("count", 1); stats.put("count", 1);
provinceStats.put(canonicalProvince, stats); provinceStats.put(canonicalProvince, stats);
} }
} }
// 计算每个省份的平均价格并更新 // 计算每个省份的平均价格并更新
for (java.util.Map.Entry<String, java.util.Map<String, Object>> entry : provinceStats.entrySet()) { for (java.util.Map.Entry<String, java.util.Map<String, Object>> entry : provinceStats.entrySet()) {
String province = entry.getKey(); String province = entry.getKey();
java.util.Map<String, Object> stats = entry.getValue(); java.util.Map<String, Object> stats = entry.getValue();
java.math.BigDecimal totalSum = (java.math.BigDecimal) stats.get("sum"); java.math.BigDecimal totalSum = (java.math.BigDecimal) stats.get("sum");
int totalCount = (Integer) stats.get("count"); int totalCount = (Integer) stats.get("count");
// 计算平均值 // 计算平均值
java.math.BigDecimal averagePrice = totalSum.divide( java.math.BigDecimal averagePrice = totalSum.divide(
java.math.BigDecimal.valueOf(totalCount), java.math.BigDecimal.valueOf(totalCount),
2, 2,
java.math.RoundingMode.HALF_UP java.math.RoundingMode.HALF_UP
); );
System.out.println("省份: " + province + ", 当天数据条数: " + totalCount + ", 平均价格: " + averagePrice); System.out.println("省份: " + province + ", 当天数据条数: " + totalCount + ", 平均价格: " + averagePrice);
// 更新 cattleprovince 表的 province_price 字段 // 更新 cattleprovince 表的 province_price 字段
java.util.Optional<com.example.cattletends.entity.CattleProvince> cattleProvinceOpt = java.util.Optional<com.example.cattletends.entity.CattleProvince> cattleProvinceOpt =
cattleProvinceRepository.findFirstByProvince(province); cattleProvinceRepository.findFirstByProvince(province);
if (cattleProvinceOpt.isPresent()) { if (cattleProvinceOpt.isPresent()) {
com.example.cattletends.entity.CattleProvince cattleProvince = cattleProvinceOpt.get(); com.example.cattletends.entity.CattleProvince cattleProvince = cattleProvinceOpt.get();
cattleProvince.setProvincePrice(averagePrice); cattleProvince.setProvincePrice(averagePrice);
cattleProvinceRepository.save(cattleProvince); cattleProvinceRepository.save(cattleProvince);
System.out.println("✓ 更新 cattleprovince 表的 province_price: " + province + ", 价格: " + averagePrice); System.out.println("✓ 更新 cattleprovince 表的 province_price: " + province + ", 价格: " + averagePrice);
} }
// 更新或创建省份每日均价记录 // 更新或创建省份每日均价记录
provinceDailyPriceService.saveOrUpdate(province, averagePrice, importDate); provinceDailyPriceService.saveOrUpdate(province, averagePrice, importDate);
updatedCount++; updatedCount++;
System.out.println("✓ 更新省份每日均价: " + province + ", 日期: " + importDate + ", 价格: " + averagePrice); System.out.println("✓ 更新省份每日均价: " + province + ", 日期: " + importDate + ", 价格: " + averagePrice);
} }
// 对于没有数据的省份,使用省份表中的省份均价(如果有) // 对于没有数据的省份,使用省份表中的省份均价(如果有)
for (com.example.cattletends.entity.CattleProvince province : allProvinces) { for (com.example.cattletends.entity.CattleProvince province : allProvinces) {
String provinceName = province.getProvince(); String provinceName = province.getProvince();
if (provinceName == null || provinceName.trim().isEmpty()) { if (provinceName == null || provinceName.trim().isEmpty()) {
continue; continue;
} }
String trimmedProvince = provinceName.trim(); String trimmedProvince = provinceName.trim();
if (!provinceStats.containsKey(trimmedProvince)) { if (!provinceStats.containsKey(trimmedProvince)) {
// 没有当天数据,使用省份表中的省份均价 // 没有当天数据,使用省份表中的省份均价
if (province.getProvincePrice() != null) { if (province.getProvincePrice() != null) {
provinceDailyPriceService.saveOrUpdate(trimmedProvince, province.getProvincePrice(), importDate); provinceDailyPriceService.saveOrUpdate(trimmedProvince, province.getProvincePrice(), importDate);
createdCount++; createdCount++;
System.out.println("✓ 创建省份每日均价(使用省份均价): " + trimmedProvince + ", 日期: " + importDate + ", 价格: " + province.getProvincePrice()); System.out.println("✓ 创建省份每日均价(使用省份均价): " + trimmedProvince + ", 日期: " + importDate + ", 价格: " + province.getProvincePrice());
} }
} }
} }
System.out.println("省份每日均价处理完成: 更新 " + updatedCount + " 条,创建 " + createdCount + ""); System.out.println("省份每日均价处理完成: 更新 " + updatedCount + " 条,创建 " + createdCount + "");
} catch (Exception e) { } catch (Exception e) {
System.err.println("计算省份平均价格失败: " + e.getMessage()); System.err.println("计算省份平均价格失败: " + e.getMessage());
e.printStackTrace(); e.printStackTrace();
} }
} }
/** /**
* 将牛只数据中的省份名称,转换为与 cattleprovince / province_daily_price 表中一致的标准省份名称 * 将牛只数据中的省份名称,转换为与 cattleprovince / province_daily_price 表中一致的标准省份名称
* 例如:把“安徽省”“河北省”“新疆维吾尔自治区”等映射为“安徽”“河北”“新疆”等 * 例如:把“安徽省”“河北省”“新疆维吾尔自治区”等映射为“安徽”“河北”“新疆”等
*/ */
private String resolveProvinceNameForMatch(String province) { private String resolveProvinceNameForMatch(String province) {
if (province == null) { if (province == null) {
return null; return null;
} }
String trimmed = province.trim(); String trimmed = province.trim();
// 1. 先按原样查找 // 1. 先按原样查找
java.util.Optional<com.example.cattletends.entity.CattleProvince> exact = java.util.Optional<com.example.cattletends.entity.CattleProvince> exact =
cattleProvinceRepository.findFirstByProvince(trimmed); cattleProvinceRepository.findFirstByProvince(trimmed);
if (exact.isPresent()) { if (exact.isPresent()) {
return exact.get().getProvince(); return exact.get().getProvince();
} }
// 2. 尝试去掉常见后缀(省、市、自治区、回族、壮族、维吾尔) // 2. 尝试去掉常见后缀(省、市、自治区、回族、壮族、维吾尔)
String shortName = trimmed String shortName = trimmed
.replace("维吾尔", "") .replace("维吾尔", "")
.replace("回族", "") .replace("回族", "")
.replace("壮族", "") .replace("壮族", "")
.replace("自治区", "") .replace("自治区", "")
.replace("", "") .replace("", "")
.replace("", "") .replace("", "")
.trim(); .trim();
if (!shortName.equals(trimmed)) { if (!shortName.equals(trimmed)) {
java.util.Optional<com.example.cattletends.entity.CattleProvince> shortMatch = java.util.Optional<com.example.cattletends.entity.CattleProvince> shortMatch =
cattleProvinceRepository.findFirstByProvince(shortName); cattleProvinceRepository.findFirstByProvince(shortName);
if (shortMatch.isPresent()) { if (shortMatch.isPresent()) {
return shortMatch.get().getProvince(); return shortMatch.get().getProvince();
} }
} }
// 3. 找不到匹配时,退回原始名称(保证不会中断流程) // 3. 找不到匹配时,退回原始名称(保证不会中断流程)
return trimmed; return trimmed;
} }
} }