Merge remote-tracking branch 'origin/main'

This commit is contained in:
ll
2026-03-03 18:07:21 +08:00
34 changed files with 1013 additions and 407 deletions

View File

@@ -84,6 +84,9 @@ public class DdSaleController extends BaseController {
@Log(title = "销售主单", businessType = BusinessType.INSERT) @Log(title = "销售主单", businessType = BusinessType.INSERT)
@PostMapping @PostMapping
public AjaxResult add(@RequestBody DdSale ddSale) { public AjaxResult add(@RequestBody DdSale ddSale) {
// 适配数据分离
ddSale.setDeptId(getDeptId());
ddSale.setUserId(getUserId());
return toAjax(ddSaleService.insertDdSale(ddSale)); return toAjax(ddSaleService.insertDdSale(ddSale));
} }

View File

@@ -4,6 +4,10 @@ import java.math.BigDecimal;
import java.util.List; import java.util.List;
import java.util.Date; import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
import com.zhyc.common.annotation.Excel; import com.zhyc.common.annotation.Excel;
@@ -15,6 +19,8 @@ import com.zhyc.common.core.domain.BaseEntity;
* @author HashMap * @author HashMap
* @date 2025-12-01 * @date 2025-12-01
*/ */
@EqualsAndHashCode(callSuper = true)
@Data
public class DdSale extends BaseEntity public class DdSale extends BaseEntity
{ {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@@ -59,119 +65,11 @@ public class DdSale extends BaseEntity
@Excel(name = "技术员") @Excel(name = "技术员")
private String tech; private String tech;
private Long userId;
private Long deptId;
/** 销售明细信息 */ /** 销售明细信息 */
private List<DdSaleItem> ddSaleItemList; private List<DdSaleItem> ddSaleItemList;
public void setId(Long id)
{
this.id = id;
}
public Long getId()
{
return id;
}
public void setSaleDate(Date saleDate)
{
this.saleDate = saleDate;
}
public Date getSaleDate()
{
return saleDate;
}
public void setCustName(String custName)
{
this.custName = custName;
}
public String getCustName()
{
return custName;
}
public void setCustPhone(String custPhone)
{
this.custPhone = custPhone;
}
public String getCustPhone()
{
return custPhone;
}
public void setCustAddr(String custAddr)
{
this.custAddr = custAddr;
}
public String getCustAddr()
{
return custAddr;
}
public void setSalesper(String salesper)
{
this.salesper = salesper;
}
public String getSalesper()
{
return salesper;
}
public void setQuaranNo(String quaranNo)
{
this.quaranNo = quaranNo;
}
public String getQuaranNo()
{
return quaranNo;
}
public void setApprNo(String apprNo)
{
this.apprNo = apprNo;
}
public String getApprNo()
{
return apprNo;
}
public void setPrice(BigDecimal price)
{
this.price = price;
}
public BigDecimal getPrice()
{
return price;
}
public void setTech(String tech)
{
this.tech = tech;
}
public String getTech()
{
return tech;
}
public List<DdSaleItem> getDdSaleItemList()
{
return ddSaleItemList;
}
public void setDdSaleItemList(List<DdSaleItem> ddSaleItemList)
{
this.ddSaleItemList = ddSaleItemList;
}
@Override @Override
public String toString() { public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)

View File

@@ -3,6 +3,7 @@ package com.zhyc.module.frozen.service.impl;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import com.zhyc.common.annotation.DataScope;
import com.zhyc.common.utils.DateUtils; import com.zhyc.common.utils.DateUtils;
import com.zhyc.module.frozen.domain.DdFe; import com.zhyc.module.frozen.domain.DdFe;
import com.zhyc.module.frozen.domain.DdFs; import com.zhyc.module.frozen.domain.DdFs;
@@ -58,6 +59,7 @@ public class DdSaleServiceImpl implements IDdSaleService {
* @return 销售主单 * @return 销售主单
*/ */
@Override @Override
@DataScope(deptAlias = "dd_sl_alias" , userAlias = "dd_sl_alias")
public List<DdSale> selectDdSaleList(DdSale ddSale) { public List<DdSale> selectDdSaleList(DdSale ddSale) {
return ddSaleMapper.selectDdSaleList(ddSale); return ddSaleMapper.selectDdSaleList(ddSale);
} }

View File

@@ -10,6 +10,7 @@ import com.zhyc.module.produce.breed.service.IScBreedPlanGenerateService;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import com.zhyc.common.annotation.Log; import com.zhyc.common.annotation.Log;
import com.zhyc.common.core.controller.BaseController; import com.zhyc.common.core.controller.BaseController;
import com.zhyc.common.core.domain.AjaxResult; import com.zhyc.common.core.domain.AjaxResult;
@@ -65,7 +66,89 @@ public class ScBreedPlanGenerateController extends BaseController
} }
/** /**
* 自动生成配种计划 * 导出已选母羊公羊配对表(供用户下载后手动填写/确认配对)
*/
@PreAuthorize("@ss.hasPermi('mating_plan:generate:export')")
@Log(title = "导出配对模板", businessType = BusinessType.EXPORT)
@PostMapping("/exportPairs")
public void exportSelectedPairs(HttpServletResponse response, @RequestBody Map<String, Object> params)
{
try {
List<?> eweIdsRaw = (List<?>) params.get("eweIds");
List<?> ramIdsRaw = (List<?>) params.get("ramIds");
if (eweIdsRaw == null || ramIdsRaw == null) {
response.setContentType("application/json;charset=utf-8");
response.getWriter().write("{\"code\":500,\"msg\":\"参数不能为空\"}");
return;
}
List<Long> eweIds = eweIdsRaw.stream()
.map(obj -> obj instanceof Integer ? ((Integer) obj).longValue()
: obj instanceof Long ? (Long) obj : Long.valueOf(obj.toString()))
.collect(Collectors.toList());
List<Long> ramIds = ramIdsRaw.stream()
.map(obj -> obj instanceof Integer ? ((Integer) obj).longValue()
: obj instanceof Long ? (Long) obj : Long.valueOf(obj.toString()))
.collect(Collectors.toList());
scBreedPlanGenerateService.exportSelectedPairs(response, eweIds, ramIds);
} catch (Exception e) {
logger.error("导出配对表失败", e);
try {
response.setContentType("application/json;charset=utf-8");
response.getWriter().write("{\"code\":500,\"msg\":\"导出失败:" + e.getMessage() + "\"}");
} catch (Exception ex) {
logger.error("返回错误信息失败", ex);
}
}
}
/**
* 解析导入的配对Excel返回配对预览数据不生成计划
*/
@PreAuthorize("@ss.hasPermi('mating_plan:generate:add')")
@PostMapping("/parsePairs")
public AjaxResult parsePairsFromExcel(@RequestParam("file") MultipartFile file)
{
try {
List<Map<String, Object>> pairs = scBreedPlanGenerateService.parsePairsFromExcel(file);
return success(pairs);
} catch (Exception e) {
logger.error("解析配对Excel失败", e);
return error("解析失败:" + e.getMessage());
}
}
/**
* 根据导入的配对数据生成配种计划
*/
@PreAuthorize("@ss.hasPermi('mating_plan:generate:auto')")
@PostMapping("/generateFromPairs")
@Log(title = "导入生成配种计划", businessType = BusinessType.INSERT)
public AjaxResult generateBreedPlanFromPairs(@RequestBody Map<String, Object> params)
{
try {
Integer planType = params.get("planType") != null ? (Integer) params.get("planType") : 1;
@SuppressWarnings("unchecked")
List<Map<String, Object>> pairs = (List<Map<String, Object>>) params.get("pairs");
if (pairs == null || pairs.isEmpty()) {
return error("配对数据不能为空");
}
ScBreedPlanGenerate planGenerate = scBreedPlanGenerateService.autoGenerateBreedPlanFromPairs(planType, pairs);
return success(planGenerate);
} catch (Exception e) {
logger.error("导入生成配种计划失败", e);
return error("生成失败:" + e.getMessage());
}
}
/**
* 自动生成配种计划(原有逻辑保留,按比例自动分配)
*/ */
@PreAuthorize("@ss.hasPermi('mating_plan:generate:auto')") @PreAuthorize("@ss.hasPermi('mating_plan:generate:auto')")
@PostMapping("/auto") @PostMapping("/auto")
@@ -73,13 +156,9 @@ public class ScBreedPlanGenerateController extends BaseController
public AjaxResult autoGenerateBreedPlan(@RequestBody Map<String, Object> params) public AjaxResult autoGenerateBreedPlan(@RequestBody Map<String, Object> params)
{ {
try { try {
// 获取计划类型
Integer planType = params.get("planType") != null ? (Integer) params.get("planType") : 1; Integer planType = params.get("planType") != null ? (Integer) params.get("planType") : 1;
// 计划名称由系统自动生成,不再从前端传入
String planName = null; String planName = null;
// 安全的类型转换
List<?> eweIdsRaw = (List<?>) params.get("eweIds"); List<?> eweIdsRaw = (List<?>) params.get("eweIds");
List<?> ramIdsRaw = (List<?>) params.get("ramIds"); List<?> ramIdsRaw = (List<?>) params.get("ramIds");
@@ -88,27 +167,13 @@ public class ScBreedPlanGenerateController extends BaseController
} }
List<Long> eweIds = eweIdsRaw.stream() List<Long> eweIds = eweIdsRaw.stream()
.map(obj -> { .map(obj -> obj instanceof Integer ? ((Integer) obj).longValue()
if (obj instanceof Integer) { : obj instanceof Long ? (Long) obj : Long.valueOf(obj.toString()))
return ((Integer) obj).longValue();
} else if (obj instanceof Long) {
return (Long) obj;
} else {
return Long.valueOf(obj.toString());
}
})
.collect(Collectors.toList()); .collect(Collectors.toList());
List<Long> ramIds = ramIdsRaw.stream() List<Long> ramIds = ramIdsRaw.stream()
.map(obj -> { .map(obj -> obj instanceof Integer ? ((Integer) obj).longValue()
if (obj instanceof Integer) { : obj instanceof Long ? (Long) obj : Long.valueOf(obj.toString()))
return ((Integer) obj).longValue();
} else if (obj instanceof Long) {
return (Long) obj;
} else {
return Long.valueOf(obj.toString());
}
})
.collect(Collectors.toList()); .collect(Collectors.toList());
ScBreedPlanGenerate planGenerate = scBreedPlanGenerateService.autoGenerateBreedPlan(planType, planName, eweIds, ramIds); ScBreedPlanGenerate planGenerate = scBreedPlanGenerateService.autoGenerateBreedPlan(planType, planName, eweIds, ramIds);
@@ -222,7 +287,6 @@ public class ScBreedPlanGenerateController extends BaseController
scBreedPlanGenerateService.exportBreedPlanDetails(response, id); scBreedPlanGenerateService.exportBreedPlanDetails(response, id);
} catch (Exception e) { } catch (Exception e) {
logger.error("导出配种计划详情失败", e); logger.error("导出配种计划详情失败", e);
// 在出错时返回错误信息给前端
try { try {
response.setContentType("application/json;charset=utf-8"); response.setContentType("application/json;charset=utf-8");
response.getWriter().write("{\"code\":500,\"msg\":\"导出失败:" + e.getMessage() + "\"}"); response.getWriter().write("{\"code\":500,\"msg\":\"导出失败:" + e.getMessage() + "\"}");
@@ -246,7 +310,7 @@ public class ScBreedPlanGenerateController extends BaseController
/** /**
* 模糊查询母羊耳号列表 * 模糊查询母羊耳号列表
*/ */
@PreAuthorize("@ss.hasPermi('mating_plan:generate:query')") // 根据实际权限修改 @PreAuthorize("@ss.hasPermi('mating_plan:generate:query')")
@GetMapping("/search_ear_numbers") @GetMapping("/search_ear_numbers")
public AjaxResult searchEarNumbers(@RequestParam("query") String query) { public AjaxResult searchEarNumbers(@RequestParam("query") String query) {
try { try {

View File

@@ -79,14 +79,20 @@ public class ScDryMilkController extends BaseController
/** /**
* 远程搜索耳号列表 * 远程搜索耳号列表
*/ */
/**
* 模糊查询母羊耳号列表
*/
@PreAuthorize("@ss.hasPermi('drymilk:drymilk:query')") // 根据实际权限修改
@GetMapping("/searchEarNumbers") @GetMapping("/searchEarNumbers")
public AjaxResult searchEarNumbers(@RequestParam(value = "query", required = false) String query) public AjaxResult searchEarNumbers(@RequestParam("query") String query) {
{ try {
if (query == null) query = ""; List<String> earNumbers = scDryMilkService.searchEarNumbers(query);
List<String> list = scDryMilkService.selectSheepEarNumberList(query); return success(earNumbers);
return AjaxResult.success(list); } catch (Exception e) {
logger.error("搜索耳号异常", e);
return error("搜索耳号失败:" + e.getMessage());
}
} }
/** /**
* 远程搜索技术员列表 * 远程搜索技术员列表
*/ */

View File

@@ -48,6 +48,7 @@ public class ScEmbryoFlushController extends BaseController
return getDataTable(list); return getDataTable(list);
} }
/** /**
* 导出冲胚记录列表 * 导出冲胚记录列表
*/ */
@@ -126,4 +127,35 @@ public class ScEmbryoFlushController extends BaseController
List<Map<String, Object>> list = scEmbryoFlushService.selectDonorFemaleList(); List<Map<String, Object>> list = scEmbryoFlushService.selectDonorFemaleList();
return success(list); return success(list);
} }
/**
* 获取供体公羊下拉列表
*/
@PreAuthorize("@ss.hasPermi('embryo:flush:query')")
@GetMapping("/donorMaleList")
public AjaxResult getDonorMaleList()
{
List<Map<String, Object>> list = scEmbryoFlushService.selectDonorMaleList();
return success(list);
}
/**
* 根据耳号获取羊只基础信息
*/
@PreAuthorize("@ss.hasPermi('embryo:flush:query')")
@GetMapping("/getSheepInfo")
public AjaxResult getSheepInfo(@RequestParam("manageTag") String manageTag) {
return success(scEmbryoFlushService.getSheepInfoByTag(manageTag));
}
/**
* 根据父母品种实时计算胚胎品种
*/
@PreAuthorize("@ss.hasPermi('embryo:flush:query')")
@GetMapping("/calculateVariety")
public AjaxResult calculateVariety(@RequestParam("maleVariety") String maleVariety,
@RequestParam("femaleVariety") String femaleVariety) {
String result = scEmbryoFlushService.calculateEmbryoVariety(maleVariety, femaleVariety);
return success(result);
}
} }

View File

@@ -1,8 +1,14 @@
package com.zhyc.module.produce.breed.controller; package com.zhyc.module.produce.breed.controller;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@@ -13,7 +19,6 @@ import com.zhyc.common.enums.BusinessType;
import com.zhyc.module.produce.breed.domain.ScLambingRecord; import com.zhyc.module.produce.breed.domain.ScLambingRecord;
import com.zhyc.module.produce.breed.domain.ScLambDetail; import com.zhyc.module.produce.breed.domain.ScLambDetail;
import com.zhyc.module.produce.breed.service.IScLambingRecordService; import com.zhyc.module.produce.breed.service.IScLambingRecordService;
import com.zhyc.common.utils.poi.ExcelUtil;
import com.zhyc.common.core.page.TableDataInfo; import com.zhyc.common.core.page.TableDataInfo;
/** /**
@@ -55,6 +60,7 @@ public class ScLambingRecordController extends BaseController {
return error("查询配种信息失败:" + e.getMessage()); return error("查询配种信息失败:" + e.getMessage());
} }
} }
@PreAuthorize("@ss.hasPermi('breed:lambing_records:query')") @PreAuthorize("@ss.hasPermi('breed:lambing_records:query')")
@GetMapping("/search_ear_numbers") @GetMapping("/search_ear_numbers")
public AjaxResult searchEarNumbers(@RequestParam("query") String query) { public AjaxResult searchEarNumbers(@RequestParam("query") String query) {
@@ -66,25 +72,217 @@ public class ScLambingRecordController extends BaseController {
return error("搜索耳号失败:" + e.getMessage()); return error("搜索耳号失败:" + e.getMessage());
} }
} }
/** /**
* 导出产羔记录列表 * 导出产羔记录列表多SheetSheet1=产羔记录Sheet2=羔羊详情)
*/ */
@PreAuthorize("@ss.hasPermi('breed:lambing_records:export')") @PreAuthorize("@ss.hasPermi('breed:lambing_records:export')")
@Log(title = "产羔记录", businessType = BusinessType.EXPORT) @Log(title = "产羔记录", businessType = BusinessType.EXPORT)
@PostMapping("/export") @PostMapping("/export")
public void export(HttpServletResponse response, ScLambingRecord scLambingRecord) { public void export(HttpServletResponse response, ScLambingRecord scLambingRecord) throws IOException {
// 不分页,查全量
List<ScLambingRecord> list = scLambingRecordService.selectScLambingRecordList(scLambingRecord); List<ScLambingRecord> list = scLambingRecordService.selectScLambingRecordList(scLambingRecord);
ExcelUtil<ScLambingRecord> util = new ExcelUtil<ScLambingRecord>(ScLambingRecord.class);
util.exportExcel(response, list, "产羔记录数据"); // 为每条记录补充羔羊详情
for (ScLambingRecord record : list) {
List<ScLambDetail> details = scLambingRecordService.selectLambDetailByLambingRecordId(record.getId());
record.setLambDetails(details);
}
// 构建多Sheet工作簿
XSSFWorkbook workbook = buildExportWorkbook(list);
// 写出响应
String filename = java.net.URLEncoder.encode("产羔记录_" + new java.text.SimpleDateFormat("yyyyMMddHHmmss").format(new java.util.Date()), "UTF-8");
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
response.setHeader("Content-Disposition", "attachment;filename=" + filename + ".xlsx");
workbook.write(response.getOutputStream());
workbook.close();
} }
/** /**
* 获取产羔记录详细信息(修改:改为获取包含关联信息的详细数据 * 构建导出工作簿两个Sheet
*/
private XSSFWorkbook buildExportWorkbook(List<ScLambingRecord> list) {
XSSFWorkbook workbook = new XSSFWorkbook();
SimpleDateFormat dateFmt = new SimpleDateFormat("yyyy-MM-dd");
// ==================== Sheet1产羔记录 ====================
Sheet sheet1 = workbook.createSheet("产羔记录");
// 标题样式
CellStyle headerStyle = createHeaderStyle(workbook);
// 普通单元格样式
CellStyle dataStyle = createDataStyle(workbook);
// 产羔记录表头(与界面完全一致)
String[] headers1 = {
"母羊耳号", "母羊品种", "配种日期", "胎次", "公羊耳号", "公羊品种",
"产羔数量", "活羔数量", "折损数", "技术员", "月龄", "产羔评分",
"公羔数量", "母羔数量", "留养公羔数量", "留养母羔数量",
"未留养公羔数量", "未留养母羔数量", "产羔时怀孕天数",
"当前羊舍", "创建人", "创建日期", "所在牧场", "备注"
};
// 列宽(字符数 * 256
int[] colWidths1 = {
14, 12, 13, 8, 14, 12,
10, 10, 8, 10, 8, 10,
10, 10, 14, 14,
16, 16, 16,
12, 10, 12, 12, 20
};
Row headerRow1 = sheet1.createRow(0);
headerRow1.setHeightInPoints(22);
for (int i = 0; i < headers1.length; i++) {
sheet1.setColumnWidth(i, colWidths1[i] * 256);
Cell cell = headerRow1.createCell(i);
cell.setCellValue(headers1[i]);
cell.setCellStyle(headerStyle);
}
// 产羔记录数据行
int rowNum1 = 1;
for (ScLambingRecord r : list) {
Row row = sheet1.createRow(rowNum1++);
row.setHeightInPoints(18);
int col = 0;
setCellValue(row, col++, r.getFemaleEarNumber(), dataStyle);
setCellValue(row, col++, r.getFemaleBreed(), dataStyle);
setCellValue(row, col++, r.getBreedingDate() != null ? dateFmt.format(r.getBreedingDate()) : "", dataStyle);
setCellValue(row, col++, r.getParity(), dataStyle);
setCellValue(row, col++, r.getMaleEarNumber(), dataStyle);
setCellValue(row, col++, r.getMaleBreed(), dataStyle);
setCellValue(row, col++, r.getLambsBorn(), dataStyle);
setCellValue(row, col++, r.getSurvival(), dataStyle);
// 折损数 = 产羔数 - 活羔数
int loss = (int) ((r.getLambsBorn() != null ? r.getLambsBorn() : 0)
- (r.getSurvival() != null ? r.getSurvival() : 0));
setCellValue(row, col++, loss, dataStyle);
setCellValue(row, col++, r.getTechnician(), dataStyle);
setCellValue(row, col++, r.getMonthAge(), dataStyle);
setCellValue(row, col++, r.getScore(), dataStyle);
setCellValue(row, col++, r.getMaleCount(), dataStyle);
setCellValue(row, col++, r.getFemaleCount(), dataStyle);
setCellValue(row, col++, r.getRetainedMaleCount(), dataStyle);
setCellValue(row, col++, r.getRetainedFemaleCount(), dataStyle);
setCellValue(row, col++, r.getUnretainedMaleCount(), dataStyle);
setCellValue(row, col++, r.getUnretainedFemaleCount(), dataStyle);
setCellValue(row, col++, r.getPregnancyDays(), dataStyle);
setCellValue(row, col++, r.getCurrentShed(), dataStyle);
setCellValue(row, col++, r.getCreateBy(), dataStyle);
setCellValue(row, col++, r.getCreateTime() != null ? dateFmt.format(r.getCreateTime()) : "", dataStyle);
setCellValue(row, col++, r.getFarm(), dataStyle);
setCellValue(row, col++, r.getComment(), dataStyle);
}
// ==================== Sheet2羔羊详情 ====================
Sheet sheet2 = workbook.createSheet("羔羊详情");
String[] headers2 = {
"母羊耳号", "胎次", "羔羊耳号", "性别", "出生体重(kg)",
"是否留养", "家系", "出生日期"
};
int[] colWidths2 = { 14, 8, 14, 8, 14, 10, 16, 13 };
Row headerRow2 = sheet2.createRow(0);
headerRow2.setHeightInPoints(22);
for (int i = 0; i < headers2.length; i++) {
sheet2.setColumnWidth(i, colWidths2[i] * 256);
Cell cell = headerRow2.createCell(i);
cell.setCellValue(headers2[i]);
cell.setCellStyle(headerStyle);
}
int rowNum2 = 1;
for (ScLambingRecord r : list) {
if (r.getLambDetails() == null || r.getLambDetails().isEmpty()) {
continue;
}
for (ScLambDetail d : r.getLambDetails()) {
Row row = sheet2.createRow(rowNum2++);
row.setHeightInPoints(18);
int col = 0;
setCellValue(row, col++, r.getFemaleEarNumber(), dataStyle);
setCellValue(row, col++, r.getParity(), dataStyle);
setCellValue(row, col++, d.getLambEarNumber(), dataStyle);
// 性别1=公0=母2=阉羊3=兼性(参照后端校验逻辑)
String genderLabel = "";
if (d.getGender() != null) {
switch (d.getGender()) {
case 0: genderLabel = ""; break;
case 1: genderLabel = ""; break;
case 2: genderLabel = "阉羊"; break;
case 3: genderLabel = "兼性"; break;
default: genderLabel = String.valueOf(d.getGender());
}
}
setCellValue(row, col++, genderLabel, dataStyle);
setCellValue(row, col++, d.getBirthWeight() != null ? d.getBirthWeight().toPlainString() : "", dataStyle);
setCellValue(row, col++, Boolean.TRUE.equals(d.getIsRetained()) ? "" : "", dataStyle);
setCellValue(row, col++, d.getLineage(), dataStyle);
setCellValue(row, col++, d.getBirthday() != null ? dateFmt.format(d.getBirthday()) : "", dataStyle);
}
}
return workbook;
}
/** 创建表头样式 */
private CellStyle createHeaderStyle(XSSFWorkbook workbook) {
CellStyle style = workbook.createCellStyle();
Font font = workbook.createFont();
font.setBold(true);
font.setFontHeightInPoints((short) 11);
style.setFont(font);
style.setFillForegroundColor(IndexedColors.LIGHT_CORNFLOWER_BLUE.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderTop(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
return style;
}
/** 创建数据行样式 */
private CellStyle createDataStyle(XSSFWorkbook workbook) {
CellStyle style = workbook.createCellStyle();
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderTop(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
return style;
}
/** 统一设置单元格值 */
private void setCellValue(Row row, int col, Object value, CellStyle style) {
Cell cell = row.createCell(col);
if (value == null) {
cell.setCellValue("");
} else if (value instanceof Integer) {
cell.setCellValue((Integer) value);
} else if (value instanceof Long) {
cell.setCellValue((Long) value);
} else if (value instanceof Double) {
cell.setCellValue((Double) value);
} else {
cell.setCellValue(value.toString());
}
cell.setCellStyle(style);
}
/**
* 获取产羔记录详细信息
*/ */
@PreAuthorize("@ss.hasPermi('breed:lambing_records:query')") @PreAuthorize("@ss.hasPermi('breed:lambing_records:query')")
@GetMapping(value = "/{id}") @GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id) { public AjaxResult getInfo(@PathVariable("id") Long id) {
// 修改:改为调用详细查询方法,获取包含母羊耳号、公羊耳号等关联信息
return success(scLambingRecordService.selectScLambingRecordDetailById(id)); return success(scLambingRecordService.selectScLambingRecordDetailById(id));
} }
@@ -96,16 +294,11 @@ public class ScLambingRecordController extends BaseController {
@PostMapping @PostMapping
public AjaxResult add(@RequestBody ScLambingRecord scLambingRecord) { public AjaxResult add(@RequestBody ScLambingRecord scLambingRecord) {
try { try {
// 设置创建人
scLambingRecord.setCreateBy(getUsername()); scLambingRecord.setCreateBy(getUsername());
// 如果没有设置创建时间,使用当前时间
if (scLambingRecord.getCreateTime() == null) { if (scLambingRecord.getCreateTime() == null) {
scLambingRecord.setCreateTime(new java.util.Date()); scLambingRecord.setCreateTime(new java.util.Date());
} }
int result = scLambingRecordService.insertScLambingRecord(scLambingRecord); int result = scLambingRecordService.insertScLambingRecord(scLambingRecord);
if (result > 0) { if (result > 0) {
String message = "新增产羔记录成功"; String message = "新增产羔记录成功";
if (scLambingRecord.getLambDetails() != null && !scLambingRecord.getLambDetails().isEmpty()) { if (scLambingRecord.getLambDetails() != null && !scLambingRecord.getLambDetails().isEmpty()) {

View File

@@ -41,6 +41,20 @@ public class ScSheepDeathController extends BaseController
@Autowired @Autowired
private ISwDiseaseService swDiseaseService; private ISwDiseaseService swDiseaseService;
/**
* 模糊查询母羊耳号列表
*/
@PreAuthorize("@ss.hasPermi('/sheep_death/death:query')") // 根据实际权限修改
@GetMapping("/search_ear_numbers")
public AjaxResult searchEarNumbers(@RequestParam("query") String query) {
try {
List<String> earNumbers = scSheepDeathService.searchEarNumbers(query);
return success(earNumbers);
} catch (Exception e) {
logger.error("搜索耳号异常", e);
return error("搜索耳号失败:" + e.getMessage());
}
}
/** /**
* 查询羊只死淘记录列表 * 查询羊只死淘记录列表
*/ */

View File

@@ -36,15 +36,6 @@ public class ScWeanRecordController extends BaseController {
return getDataTable(list); return getDataTable(list);
} }
/**
* 【新增】模糊查询耳号列表 (用于前端下拉框远程搜索)
*/
@PreAuthorize("@ss.hasPermi('Weaning:weaning_record:list')")
@GetMapping("/search_ear_numbers")
public AjaxResult searchEarNumbers(@RequestParam("query") String query) {
List<String> list = scWeanRecordService.searchEarNumbers(query);
return success(list);
}
/** /**
* 导出断奶记录列表 * 导出断奶记录列表
@@ -129,5 +120,19 @@ public class ScWeanRecordController extends BaseController {
return toAjax(scWeanRecordService.deleteScWeanRecordByIds(ids)); return toAjax(scWeanRecordService.deleteScWeanRecordByIds(ids));
} }
/**
* 模糊查询母羊耳号列表
*/
@PreAuthorize("@ss.hasPermi('breed:lambing_records:query')") // 根据实际权限修改
@GetMapping("/search_ear_numbers")
public AjaxResult searchEarNumbers(@RequestParam("query") String query) {
try {
List<String> earNumbers = scWeanRecordService.searchEarNumbers(query);
return success(earNumbers);
} catch (Exception e) {
logger.error("搜索耳号异常", e);
return error("搜索耳号失败:" + e.getMessage());
}
}
} }

View File

@@ -18,6 +18,16 @@ public class ScDryMilk extends BaseEntity
{ {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** 全部羊耳号列表(用于多耳号查询) */
private List<String> allEarNumbers;
public List<String> getAllEarNumbers() {
return allEarNumbers;
}
public void setAllEarNumbers(List<String> allEarNumbers) {
this.allEarNumbers = allEarNumbers;
}
/** 主键id */ /** 主键id */
private Long id; private Long id;

View File

@@ -18,6 +18,7 @@ public class ScSheepDeath extends BaseEntity
{ {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** 主键ID */ /** 主键ID */
private Long id; private Long id;

View File

@@ -22,6 +22,17 @@ public class ScWeanRecord extends BaseEntity {
/** 主键ID */ /** 主键ID */
private Long id; private Long id;
/** 全部羊耳号列表(用于多耳号查询) */
private List<String> allEarNumbers;
public List<String> getAllEarNumbers() {
return allEarNumbers;
}
public void setAllEarNumbers(List<String> allEarNumbers) {
this.allEarNumbers = allEarNumbers;
}
/** 羊只ID */ /** 羊只ID */
@Excel(name = "羊只ID") @Excel(name = "羊只ID")
private Long sheepId; private Long sheepId;
@@ -51,10 +62,6 @@ public class ScWeanRecord extends BaseEntity {
@Excel(name = "电子耳号") @Excel(name = "电子耳号")
private String electronicTags; private String electronicTags;
// --- 新增查询字段 ---
/** 多耳号查询列表 */
private List<String> allEarNumbers;
/** 是否在群 (1是 0否) */ /** 是否在群 (1是 0否) */
private String isInHerd; private String isInHerd;

View File

@@ -136,4 +136,26 @@ public interface ScBreedPlanGenerateMapper
* @return 耳号列表 * @return 耳号列表
*/ */
List<String> searchEarNumbers(@Param("query") String query); List<String> searchEarNumbers(@Param("query") String query);
/**
* 根据ID列表查询羊只耳号
*
* @param ids 羊只ID列表
* @param gender 性别1=母羊2=公羊
* @return 包含 id、manage_tags 的 Map 列表
*/
List<Map<String, Object>> selectEarNumbersByIds(
@Param("ids") List<Long> ids,
@Param("gender") int gender);
/**
* 根据管理耳号查询羊只ID
*
* @param manageTags 管理耳号
* @param gender 性别1=母羊2=公羊
* @return 羊只ID
*/
Long selectSheepIdByManageTags(
@Param("manageTags") String manageTags,
@Param("gender") int gender);
} }

View File

@@ -69,4 +69,11 @@ public interface ScDryMilkMapper
* 远程搜索技术员列表 * 远程搜索技术员列表
*/ */
public List<String> searchTechnicianList(@Param("query") String query); public List<String> searchTechnicianList(@Param("query") String query);
/**
* 模糊查询母羊耳号列表
*
* @param query 查询关键字
* @return 耳号列表
*/
List<String> searchEarNumbers(@Param("query") String query);
} }

View File

@@ -83,4 +83,11 @@ public interface ScEmbryoFlushMapper
* @return 母羊列表 * @return 母羊列表
*/ */
public List<Map<String, Object>> selectDonorFemaleList(); public List<Map<String, Object>> selectDonorFemaleList();
/**
* 查询所有公羊列表(用于下拉选择)
*
* @return 公羊列表
*/
public List<Map<String, Object>> selectDonorMaleList();
} }

View File

@@ -85,4 +85,12 @@ public interface ScSheepDeathMapper
* 远程搜索:处理人 * 远程搜索:处理人
*/ */
public List<String> selectDistinctHandler(@Param("query") String query); public List<String> selectDistinctHandler(@Param("query") String query);
/**
* 模糊查询母羊耳号列表
*
* @param query 查询关键字
* @return 耳号列表
*/
List<String> searchEarNumbers(@Param("query") String query);
} }

View File

@@ -68,9 +68,10 @@ public interface ScWeanRecordMapper
public int updateBasSheepWeaningInfo(ScWeanRecord scWeanRecord); public int updateBasSheepWeaningInfo(ScWeanRecord scWeanRecord);
/** /**
* 【新增】模糊查询耳号列表 (用于前端远程搜索) * 模糊查询母羊耳号列表
*
* @param query 查询关键字 * @param query 查询关键字
* @return 耳号列表 * @return 耳号列表
*/ */
public List<String> searchEarNumbers(@Param("query") String query); List<String> searchEarNumbers(@Param("query") String query);
} }

View File

@@ -4,7 +4,9 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import com.zhyc.module.produce.breed.domain.ScBreedPlanGenerate; import com.zhyc.module.produce.breed.domain.ScBreedPlanGenerate;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
/** /**
* 配种计划生成Service接口 * 配种计划生成Service接口
@@ -44,6 +46,13 @@ public interface IScBreedPlanGenerateService
*/ */
public List<Map<String, Object>> selectEligibleRam(@RequestParam(value = "manageTags", required = false) String manageTags); public List<Map<String, Object>> selectEligibleRam(@RequestParam(value = "manageTags", required = false) String manageTags);
void exportSelectedPairs(HttpServletResponse response, List<Long> eweIds, List<Long> ramIds);
List<Map<String, Object>> parsePairsFromExcel(MultipartFile file);
@Transactional
ScBreedPlanGenerate autoGenerateBreedPlanFromPairs(Integer planType, List<Map<String, Object>> pairs);
/** /**
* 自动生成配种计划 * 自动生成配种计划
* *

View File

@@ -66,4 +66,12 @@ public interface IScDryMilkService
* 远程搜索技术员列表 * 远程搜索技术员列表
*/ */
public List<String> selectTechnicianList(String query); public List<String> selectTechnicianList(String query);
/**
* 模糊查询母羊耳号列表
*
* @param query 查询关键字
* @return 耳号列表
*/
public List<String> searchEarNumbers(String query);
} }

View File

@@ -83,4 +83,13 @@ public interface IScEmbryoFlushService
* @return 母羊列表 * @return 母羊列表
*/ */
public List<Map<String, Object>> selectDonorFemaleList(); public List<Map<String, Object>> selectDonorFemaleList();
/**
* 查询所有供体公羊列表(用于下拉选择)
*
* @return 公羊列表
*/
public List<Map<String, Object>> selectDonorMaleList();
public Map<String, Object> getSheepInfoByTag(String manageTag);
} }

View File

@@ -82,4 +82,12 @@ public interface IScSheepDeathService
* 远程搜索:处理人 * 远程搜索:处理人
*/ */
public List<String> selectDistinctHandler(String query); public List<String> selectDistinctHandler(String query);
/**
* 模糊查询母羊耳号列表
*
* @param query 查询关键字
* @return 耳号列表
*/
public List<String> searchEarNumbers(String query);
} }

View File

@@ -61,7 +61,8 @@ public interface IScWeanRecordService
public Long selectSheepIdByEarNumber(String earNumber); public Long selectSheepIdByEarNumber(String earNumber);
/** /**
* 【新增】模糊查询耳号列表 * 模糊查询母羊耳号列表
*
* @param query 查询关键字 * @param query 查询关键字
* @return 耳号列表 * @return 耳号列表
*/ */

View File

@@ -3,9 +3,11 @@ package com.zhyc.module.produce.breed.service.impl;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.HashMap; import java.util.HashMap;
import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import com.zhyc.module.produce.breed.domain.ScBreedPlan; import com.zhyc.module.produce.breed.domain.ScBreedPlan;
@@ -16,6 +18,7 @@ import com.zhyc.module.produce.breed.service.IScBreedPlanGenerateService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import com.zhyc.common.utils.SecurityUtils; import com.zhyc.common.utils.SecurityUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook;
@@ -40,9 +43,6 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
/** /**
* 查询配种计划生成 * 查询配种计划生成
*
* @param id 配种计划生成主键
* @return 配种计划生成
*/ */
@Override @Override
public ScBreedPlanGenerate selectScBreedPlanGenerateById(Long id) public ScBreedPlanGenerate selectScBreedPlanGenerateById(Long id)
@@ -52,9 +52,6 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
/** /**
* 查询配种计划生成列表 * 查询配种计划生成列表
*
* @param scBreedPlanGenerate 配种计划生成
* @return 配种计划生成
*/ */
@Override @Override
public List<ScBreedPlanGenerate> selectScBreedPlanGenerateList(ScBreedPlanGenerate scBreedPlanGenerate) public List<ScBreedPlanGenerate> selectScBreedPlanGenerateList(ScBreedPlanGenerate scBreedPlanGenerate)
@@ -64,8 +61,6 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
/** /**
* 筛选符合条件的母羊 * 筛选符合条件的母羊
*
* @return 符合条件的母羊列表
*/ */
@Override @Override
public List<Map<String, Object>> selectEligibleEwe(@RequestParam(value = "manageTags", required = false) String manageTags) public List<Map<String, Object>> selectEligibleEwe(@RequestParam(value = "manageTags", required = false) String manageTags)
@@ -75,8 +70,6 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
/** /**
* 筛选符合条件的公羊 * 筛选符合条件的公羊
*
* @return 符合条件的公羊列表
*/ */
@Override @Override
public List<Map<String, Object>> selectEligibleRam(@RequestParam(value = "manageTags", required = false) String manageTags) public List<Map<String, Object>> selectEligibleRam(@RequestParam(value = "manageTags", required = false) String manageTags)
@@ -85,26 +78,302 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
} }
/** /**
* 自动生成配种计划 * 导出已选母羊公羊配对表
* 按轮询方式将母羊分配给公羊导出Excel两列母羊耳号、公羊耳号
*
* @param response HTTP响应
* @param eweIds 已选母羊ID列表
* @param ramIds 已选公羊ID列表
*/
@Override
public void exportSelectedPairs(HttpServletResponse response, List<Long> eweIds, List<Long> ramIds)
{
try {
// 查询母羊耳号gender=1
List<Map<String, Object>> eweList = scBreedPlanGenerateMapper.selectEarNumbersByIds(eweIds, 1);
// 查询公羊耳号gender=2
List<Map<String, Object>> ramList = scBreedPlanGenerateMapper.selectEarNumbersByIds(ramIds, 2);
// 建立ID->耳号 的快速查找
Map<Long, String> eweEarMap = new HashMap<>();
for (Map<String, Object> e : eweList) {
eweEarMap.put(Long.valueOf(e.get("id").toString()), e.get("manage_tags").toString());
}
Map<Long, String> ramEarMap = new HashMap<>();
for (Map<String, Object> r : ramList) {
ramEarMap.put(Long.valueOf(r.get("id").toString()), r.get("manage_tags").toString());
}
// 母羊列、公羊列各自独立写入,行数取两者最大值,不做轮询配对
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("配种配对表");
// 母羊表头样式(粉色)
CellStyle eweHeaderStyle = workbook.createCellStyle();
Font eweFont = workbook.createFont();
eweFont.setBold(true);
eweFont.setFontHeightInPoints((short) 12);
eweHeaderStyle.setFont(eweFont);
eweHeaderStyle.setAlignment(HorizontalAlignment.CENTER);
eweHeaderStyle.setFillForegroundColor(IndexedColors.ROSE.getIndex());
eweHeaderStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
setBorder(eweHeaderStyle);
// 公羊表头样式(浅蓝)
CellStyle ramHeaderStyle = workbook.createCellStyle();
Font ramFont = workbook.createFont();
ramFont.setBold(true);
ramFont.setFontHeightInPoints((short) 12);
ramHeaderStyle.setFont(ramFont);
ramHeaderStyle.setAlignment(HorizontalAlignment.CENTER);
ramHeaderStyle.setFillForegroundColor(IndexedColors.LIGHT_BLUE.getIndex());
ramHeaderStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
setBorder(ramHeaderStyle);
CellStyle dataStyle = workbook.createCellStyle();
dataStyle.setAlignment(HorizontalAlignment.CENTER);
setBorder(dataStyle);
// 写入表头
Row headerRow = sheet.createRow(0);
Cell c1 = headerRow.createCell(0);
c1.setCellValue("母羊耳号");
c1.setCellStyle(eweHeaderStyle);
Cell c2 = headerRow.createCell(1);
c2.setCellValue("公羊耳号");
c2.setCellStyle(ramHeaderStyle);
// 母羊列和公羊列各自独立写入,行数取两者最大值
int maxRows = Math.max(eweIds.size(), ramIds.size());
for (int i = 0; i < maxRows; i++) {
Row row = sheet.createRow(i + 1);
Cell dc1 = row.createCell(0);
if (i < eweIds.size()) {
dc1.setCellValue(eweEarMap.getOrDefault(eweIds.get(i), ""));
}
dc1.setCellStyle(dataStyle);
Cell dc2 = row.createCell(1);
if (i < ramIds.size()) {
dc2.setCellValue(ramEarMap.getOrDefault(ramIds.get(i), ""));
}
dc2.setCellStyle(dataStyle);
}
sheet.setColumnWidth(0, 5000);
sheet.setColumnWidth(1, 5000);
// 响应头
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
String fileName = java.net.URLEncoder.encode("配种配对表", "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
workbook.write(response.getOutputStream());
workbook.close();
} catch (IOException e) {
throw new RuntimeException("导出配对表失败", e);
}
}
/** 给样式设置边框 */
private void setBorder(CellStyle style) {
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
}
/**
* 解析导入的配对Excel返回配对预览列表
* Excel格式第一列=母羊耳号,第二列=公羊耳号,第一行为表头(跳过)
*
* @param file 上传的Excel文件
* @return 配对列表,每项包含 eweEarNo、ramEarNo、eweId可能null、ramId可能null
*/
@Override
public List<Map<String, Object>> parsePairsFromExcel(MultipartFile file)
{
List<Map<String, Object>> result = new ArrayList<>();
try (InputStream is = file.getInputStream();
Workbook workbook = WorkbookFactory.create(is)) {
Sheet sheet = workbook.getSheetAt(0);
int lastRow = sheet.getLastRowNum();
// 从第2行index=1开始跳过表头
for (int i = 1; i <= lastRow; i++) {
Row row = sheet.getRow(i);
if (row == null) continue;
String eweEarNo = getCellStringValue(row.getCell(0));
String ramEarNo = getCellStringValue(row.getCell(1));
if (eweEarNo.isEmpty() && ramEarNo.isEmpty()) continue;
Map<String, Object> pair = new HashMap<>();
pair.put("eweEarNo", eweEarNo);
pair.put("ramEarNo", ramEarNo);
pair.put("rowIndex", i);
// 尝试查找对应的ID用于校验
Long eweId = null;
Long ramId = null;
if (!eweEarNo.isEmpty()) {
eweId = scBreedPlanGenerateMapper.selectSheepIdByManageTags(eweEarNo, 1); // 母羊 gender=1
}
if (!ramEarNo.isEmpty()) {
ramId = scBreedPlanGenerateMapper.selectSheepIdByManageTags(ramEarNo, 2); // 公羊 gender=2
}
pair.put("eweId", eweId);
pair.put("ramId", ramId);
// 校验状态
if (eweEarNo.isEmpty()) {
pair.put("valid", false);
pair.put("error", "母羊耳号不能为空");
} else if (ramEarNo.isEmpty()) {
pair.put("valid", false);
pair.put("error", "公羊耳号不能为空");
} else if (eweId == null) {
pair.put("valid", false);
pair.put("error", "母羊耳号[" + eweEarNo + "]未找到");
} else if (ramId == null) {
pair.put("valid", false);
pair.put("error", "公羊耳号[" + ramEarNo + "]未找到");
} else {
pair.put("valid", true);
pair.put("error", "");
}
result.add(pair);
}
} catch (Exception e) {
throw new RuntimeException("解析Excel失败" + e.getMessage(), e);
}
return result;
}
/** 安全读取单元格字符串值 */
private String getCellStringValue(Cell cell) {
if (cell == null) return "";
switch (cell.getCellType()) {
case STRING: return cell.getStringCellValue().trim();
case NUMERIC: return String.valueOf((long) cell.getNumericCellValue());
default: return "";
}
}
/**
* 根据导入的配对数据(耳号列表)生成配种计划
* pairs 中每项包含 eweEarNo 和 ramEarNo或 eweId/ramId
* *
* @param planType 计划类型 * @param planType 计划类型
* @param planName 计划名称 * @param pairs 配对数据列表
* @param eweIds 母羊ID列表
* @param ramIds 公羊ID列表
* @return 生成的配种计划 * @return 生成的配种计划
*/ */
@Override @Override
@Transactional @Transactional
public ScBreedPlanGenerate autoGenerateBreedPlanFromPairs(Integer planType, List<Map<String, Object>> pairs)
{
// 收集所有有效的 eweId / ramId去重统计用
List<Long> eweIds = new ArrayList<>();
List<Long> ramIds = new ArrayList<>();
for (Map<String, Object> pair : pairs) {
Long eweId = null;
Long ramId = null;
// 优先用已解析的 ID其次用耳号现查
if (pair.get("eweId") != null) {
eweId = Long.valueOf(pair.get("eweId").toString());
} else if (pair.get("eweEarNo") != null) {
eweId = scBreedPlanGenerateMapper.selectSheepIdByManageTags(pair.get("eweEarNo").toString(), 1); // 母羊 gender=1
}
if (pair.get("ramId") != null) {
ramId = Long.valueOf(pair.get("ramId").toString());
} else if (pair.get("ramEarNo") != null) {
ramId = scBreedPlanGenerateMapper.selectSheepIdByManageTags(pair.get("ramEarNo").toString(), 2); // 公羊 gender=2
}
if (eweId == null || ramId == null) continue; // 跳过无效行
eweIds.add(eweId);
if (!ramIds.contains(ramId)) ramIds.add(ramId);
}
if (eweIds.isEmpty()) {
throw new RuntimeException("没有有效的配对数据,请检查耳号是否存在");
}
// 创建计划汇总记录
ScBreedPlanGenerate planGenerate = new ScBreedPlanGenerate();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String dateStr = sdf.format(new Date());
String planTypeName;
switch (planType) {
case 1: planTypeName = "供体母羊配种计划"; break;
case 2: planTypeName = "同期发情人工授精计划"; break;
case 3: planTypeName = "本交配种计划"; break;
case 4: planTypeName = "自然发情人工授精计划"; break;
default: planTypeName = "配种计划"; break;
}
planGenerate.setPlanName(dateStr + planTypeName);
planGenerate.setPlanType(planType);
planGenerate.setPlanDate(new Date());
planGenerate.setTotalEweCount(eweIds.size());
planGenerate.setTotalRamCount(ramIds.size());
double ratio = ramIds.isEmpty() ? 0 : (double) eweIds.size() / ramIds.size();
planGenerate.setBreedRatio(String.format("%.1f:1", ratio));
planGenerate.setStatus(0); // 待审批
planGenerate.setCreateBy(SecurityUtils.getUsername());
planGenerate.setCreateTime(new Date());
scBreedPlanGenerateMapper.insertScBreedPlanGenerate(planGenerate);
// 按导入的配对逐条插入临时计划(保留用户指定的配对关系)
for (Map<String, Object> pair : pairs) {
Long eweId = null;
Long ramId = null;
if (pair.get("eweId") != null) {
eweId = Long.valueOf(pair.get("eweId").toString());
} else if (pair.get("eweEarNo") != null) {
eweId = scBreedPlanGenerateMapper.selectSheepIdByManageTags(pair.get("eweEarNo").toString(), 1); // 母羊 gender=1
}
if (pair.get("ramId") != null) {
ramId = Long.valueOf(pair.get("ramId").toString());
} else if (pair.get("ramEarNo") != null) {
ramId = scBreedPlanGenerateMapper.selectSheepIdByManageTags(pair.get("ramEarNo").toString(), 2); // 公羊 gender=2
}
if (eweId == null || ramId == null) continue;
ScBreedPlan breedPlan = new ScBreedPlan();
breedPlan.setRamId(ramId.toString());
breedPlan.setEweId(eweId.toString());
breedPlan.setBreedType(Long.valueOf(planType));
scBreedPlanGenerateMapper.insertTempBreedPlan(planGenerate.getId(), breedPlan);
}
return planGenerate;
}
/**
* 自动生成配种计划(按比例自动分配,原有逻辑不变)
*/
@Override
@Transactional
public ScBreedPlanGenerate autoGenerateBreedPlan(Integer planType, String planName, List<Long> eweIds, List<Long> ramIds) public ScBreedPlanGenerate autoGenerateBreedPlan(Integer planType, String planName, List<Long> eweIds, List<Long> ramIds)
{ {
// 创建配种计划生成记录
ScBreedPlanGenerate planGenerate = new ScBreedPlanGenerate(); ScBreedPlanGenerate planGenerate = new ScBreedPlanGenerate();
// 自动生成计划名称:日期+计划类型
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String dateStr = sdf.format(new Date()); String dateStr = sdf.format(new Date());
String planTypeName = ""; String planTypeName;
switch (planType) { switch (planType) {
case 1: planTypeName = "供体母羊配种计划"; break; case 1: planTypeName = "供体母羊配种计划"; break;
case 2: planTypeName = "同期发情人工授精计划"; break; case 2: planTypeName = "同期发情人工授精计划"; break;
@@ -112,7 +381,6 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
case 4: planTypeName = "自然发情人工授精计划"; break; case 4: planTypeName = "自然发情人工授精计划"; break;
default: planTypeName = "未知配种计划"; break; default: planTypeName = "未知配种计划"; break;
} }
planName = dateStr + planTypeName; planName = dateStr + planTypeName;
planGenerate.setPlanName(planName); planGenerate.setPlanName(planName);
@@ -120,26 +388,18 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
planGenerate.setPlanDate(new Date()); planGenerate.setPlanDate(new Date());
planGenerate.setTotalEweCount(eweIds.size()); planGenerate.setTotalEweCount(eweIds.size());
planGenerate.setTotalRamCount(ramIds.size()); planGenerate.setTotalRamCount(ramIds.size());
// 计算配种比例
double ratio = (double) eweIds.size() / ramIds.size(); double ratio = (double) eweIds.size() / ramIds.size();
planGenerate.setBreedRatio(String.format("%.1f:1", ratio)); planGenerate.setBreedRatio(String.format("%.1f:1", ratio));
planGenerate.setStatus(0); // 待审批 planGenerate.setStatus(0);
planGenerate.setCreateBy(SecurityUtils.getUsername()); planGenerate.setCreateBy(SecurityUtils.getUsername());
planGenerate.setCreateTime(new Date()); planGenerate.setCreateTime(new Date());
// 保存配种计划生成记录
scBreedPlanGenerateMapper.insertScBreedPlanGenerate(planGenerate); scBreedPlanGenerateMapper.insertScBreedPlanGenerate(planGenerate);
// 生成具体的配种计划
generateBreedPlanDetails(planGenerate.getId(), eweIds, ramIds, planType); generateBreedPlanDetails(planGenerate.getId(), eweIds, ramIds, planType);
return planGenerate; return planGenerate;
} }
/**
* 生成具体的配种计划详情
*/
private void generateBreedPlanDetails(Long planGenerateId, List<Long> eweIds, List<Long> ramIds, Integer planType) private void generateBreedPlanDetails(Long planGenerateId, List<Long> eweIds, List<Long> ramIds, Integer planType)
{ {
int ramIndex = 0; int ramIndex = 0;
@@ -149,9 +409,6 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
ScBreedPlan breedPlan = new ScBreedPlan(); ScBreedPlan breedPlan = new ScBreedPlan();
breedPlan.setRamId(ramIds.get(ramIndex).toString()); breedPlan.setRamId(ramIds.get(ramIndex).toString());
breedPlan.setEweId(eweIds.get(i).toString()); breedPlan.setEweId(eweIds.get(i).toString());
// 2. 修改:直接使用 planType 作为 breedType (假设一一对应)
// 1=供体母羊配种, 2=同期发情人工授精, 3=本交, 4=自然发情人工授精
breedPlan.setBreedType(Long.valueOf(planType)); breedPlan.setBreedType(Long.valueOf(planType));
scBreedPlanGenerateMapper.insertTempBreedPlan(planGenerateId, breedPlan); scBreedPlanGenerateMapper.insertTempBreedPlan(planGenerateId, breedPlan);
@@ -164,9 +421,6 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
/** /**
* 新增配种计划生成 * 新增配种计划生成
*
* @param scBreedPlanGenerate 配种计划生成
* @return 结果
*/ */
@Override @Override
public int insertScBreedPlanGenerate(ScBreedPlanGenerate scBreedPlanGenerate) public int insertScBreedPlanGenerate(ScBreedPlanGenerate scBreedPlanGenerate)
@@ -177,9 +431,6 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
/** /**
* 修改配种计划生成 * 修改配种计划生成
*
* @param scBreedPlanGenerate 配种计划生成
* @return 结果
*/ */
@Override @Override
public int updateScBreedPlanGenerate(ScBreedPlanGenerate scBreedPlanGenerate) public int updateScBreedPlanGenerate(ScBreedPlanGenerate scBreedPlanGenerate)
@@ -190,44 +441,27 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
/** /**
* 获取审批配种计划详情 * 获取审批配种计划详情
*
* @param id 配种计划ID
* @return 审批配种计划详情
*/ */
@Override @Override
public Map<String, Object> getApproveBreedPlanDetails(Long id) public Map<String, Object> getApproveBreedPlanDetails(Long id)
{ {
Map<String, Object> result = new HashMap<>(); Map<String, Object> result = new HashMap<>();
// 获取配种计划基本信息
ScBreedPlanGenerate planGenerate = scBreedPlanGenerateMapper.selectScBreedPlanGenerateById(id); ScBreedPlanGenerate planGenerate = scBreedPlanGenerateMapper.selectScBreedPlanGenerateById(id);
result.put("planInfo", planGenerate); result.put("planInfo", planGenerate);
// 获取详细的配种计划信息
List<Map<String, Object>> planDetails = scBreedPlanGenerateMapper.selectApproveBreedPlanDetails(id); List<Map<String, Object>> planDetails = scBreedPlanGenerateMapper.selectApproveBreedPlanDetails(id);
result.put("planDetails", planDetails); result.put("planDetails", planDetails);
// 获取可选择的公羊列表
List<Map<String, Object>> availableRams = scBreedPlanGenerateMapper.selectEligibleRam(); List<Map<String, Object>> availableRams = scBreedPlanGenerateMapper.selectEligibleRam();
result.put("availableRams", availableRams); result.put("availableRams", availableRams);
return result; return result;
} }
/** /**
* 确认审批配种计划 * 确认审批配种计划
*
* @param planId 配种计划ID
* @param planDetails 配种计划详情
* @param status 审批状态
* @param approveRemark 审批意见
* @return 结果
*/ */
@Override @Override
@Transactional @Transactional
public int confirmApproveBreedPlan(Long planId, List<Map<String, Object>> planDetails, Integer status, String approveRemark) public int confirmApproveBreedPlan(Long planId, List<Map<String, Object>> planDetails, Integer status, String approveRemark)
{ {
// 更新审批状态
ScBreedPlanGenerate planGenerate = new ScBreedPlanGenerate(); ScBreedPlanGenerate planGenerate = new ScBreedPlanGenerate();
planGenerate.setId(planId); planGenerate.setId(planId);
planGenerate.setStatus(status); planGenerate.setStatus(status);
@@ -238,16 +472,13 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
int result = scBreedPlanGenerateMapper.updateScBreedPlanGenerate(planGenerate); int result = scBreedPlanGenerateMapper.updateScBreedPlanGenerate(planGenerate);
// 如果审批通过,更新临时配种计划并转为正式计划
if (result > 0 && status == 1) { if (result > 0 && status == 1) {
// 更新临时配种计划中的公羊分配
if (planDetails != null && !planDetails.isEmpty()) { if (planDetails != null && !planDetails.isEmpty()) {
for (Map<String, Object> detail : planDetails) { for (Map<String, Object> detail : planDetails) {
Long tempId = Long.valueOf(detail.get("id").toString()); Long tempId = Long.valueOf(detail.get("id").toString());
Long ramId = Long.valueOf(detail.get("ram_id").toString()); Long ramId = Long.valueOf(detail.get("ram_id").toString());
Long breedType = Long.valueOf(detail.get("breed_type").toString()); Long breedType = Long.valueOf(detail.get("breed_type").toString());
// 更新临时配种计划
Map<String, Object> updateParams = new HashMap<>(); Map<String, Object> updateParams = new HashMap<>();
updateParams.put("id", tempId); updateParams.put("id", tempId);
updateParams.put("ramId", ramId); updateParams.put("ramId", ramId);
@@ -255,65 +486,41 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
scBreedPlanGenerateMapper.updateTempBreedPlan(updateParams); scBreedPlanGenerateMapper.updateTempBreedPlan(updateParams);
} }
} }
// 将临时配种计划转为正式配种计划
scBreedPlanGenerateMapper.transferTempToFormal(planId); scBreedPlanGenerateMapper.transferTempToFormal(planId);
} }
return result; return result;
} }
/** /**
* 获取配种计划详情 * 获取配种计划详情
*
* @param id 配种计划ID
* @return 配种计划详情
*/ */
@Override @Override
public Map<String, Object> getBreedPlanDetails(Long id) public Map<String, Object> getBreedPlanDetails(Long id)
{ {
Map<String, Object> result = new HashMap<>(); Map<String, Object> result = new HashMap<>();
// 获取配种计划基本信息
ScBreedPlanGenerate planGenerate = scBreedPlanGenerateMapper.selectScBreedPlanGenerateById(id); ScBreedPlanGenerate planGenerate = scBreedPlanGenerateMapper.selectScBreedPlanGenerateById(id);
result.put("planInfo", planGenerate); result.put("planInfo", planGenerate);
// 获取配种计划详情列表
List<Map<String, Object>> planDetails = scBreedPlanGenerateMapper.selectBreedPlanDetails(id); List<Map<String, Object>> planDetails = scBreedPlanGenerateMapper.selectBreedPlanDetails(id);
result.put("planDetails", planDetails); result.put("planDetails", planDetails);
return result; return result;
} }
/** /**
* 导出配种计划详情 * 导出配种计划详情(已审批)
*
* @param response HTTP响应
* @param id 配种计划ID
*/ */
@Override @Override
public void exportBreedPlanDetails(HttpServletResponse response, Long id) public void exportBreedPlanDetails(HttpServletResponse response, Long id)
{ {
try { try {
// 获取配种计划基本信息
ScBreedPlanGenerate planGenerate = scBreedPlanGenerateMapper.selectScBreedPlanGenerateById(id); ScBreedPlanGenerate planGenerate = scBreedPlanGenerateMapper.selectScBreedPlanGenerateById(id);
if (planGenerate == null) { if (planGenerate == null) throw new RuntimeException("配种计划不存在");
throw new RuntimeException("配种计划不存在"); if (planGenerate.getStatus() != 1) throw new RuntimeException("只有已审批的配种计划才能导出");
}
// 检查是否已审批
if (planGenerate.getStatus() != 1) {
throw new RuntimeException("只有已审批的配种计划才能导出");
}
// 获取配种计划详情
List<Map<String, Object>> planDetails = scBreedPlanGenerateMapper.selectBreedPlanDetails(id); List<Map<String, Object>> planDetails = scBreedPlanGenerateMapper.selectBreedPlanDetails(id);
// 创建工作簿
Workbook workbook = new XSSFWorkbook(); Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("配种计划详情"); Sheet sheet = workbook.createSheet("配种计划详情");
// 创建样式
CellStyle titleStyle = workbook.createCellStyle(); CellStyle titleStyle = workbook.createCellStyle();
Font titleFont = workbook.createFont(); Font titleFont = workbook.createFont();
titleFont.setBold(true); titleFont.setBold(true);
@@ -350,28 +557,14 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
int rowNum = 0; int rowNum = 0;
// 标题
Row titleRow = sheet.createRow(rowNum++); Row titleRow = sheet.createRow(rowNum++);
Cell titleCell = titleRow.createCell(0); Cell titleCell = titleRow.createCell(0);
titleCell.setCellValue(planGenerate.getPlanName() + " - 配种计划详情"); titleCell.setCellValue(planGenerate.getPlanName() + " - 配种计划详情");
titleCell.setCellStyle(titleStyle); titleCell.setCellStyle(titleStyle);
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 19)); sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 19));
// 空行
rowNum++; rowNum++;
String typeName = "";
switch (planGenerate.getPlanType()) {
case 1: typeName = "供体母羊配种计划"; break;
case 2: typeName = "同期发情人工授精计划"; break;
case 3: typeName = "本交配种计划"; break;
case 4: typeName = "自然发情人工授精计划"; break;
default: typeName = "未知"; break;
}
// 基本信息
Row infoRow1 = sheet.createRow(rowNum++);
infoRow1.createCell(0).setCellValue("计划类型:");
String planTypeNameStr; String planTypeNameStr;
Integer type = planGenerate.getPlanType(); Integer type = planGenerate.getPlanType();
if (type != null) { if (type != null) {
@@ -385,6 +578,9 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
} else { } else {
planTypeNameStr = ""; planTypeNameStr = "";
} }
Row infoRow1 = sheet.createRow(rowNum++);
infoRow1.createCell(0).setCellValue("计划类型:");
infoRow1.createCell(1).setCellValue(planTypeNameStr); infoRow1.createCell(1).setCellValue(planTypeNameStr);
infoRow1.createCell(3).setCellValue("计划日期:"); infoRow1.createCell(3).setCellValue("计划日期:");
infoRow1.createCell(4).setCellValue(new SimpleDateFormat("yyyy-MM-dd").format(planGenerate.getPlanDate())); infoRow1.createCell(4).setCellValue(new SimpleDateFormat("yyyy-MM-dd").format(planGenerate.getPlanDate()));
@@ -401,55 +597,39 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
infoRow3.createCell(3).setCellValue("创建人:"); infoRow3.createCell(3).setCellValue("创建人:");
infoRow3.createCell(4).setCellValue(planGenerate.getCreateBy()); infoRow3.createCell(4).setCellValue(planGenerate.getCreateBy());
// 空行
rowNum++; rowNum++;
// 分组表头
Row groupHeaderRow = sheet.createRow(rowNum++); Row groupHeaderRow = sheet.createRow(rowNum++);
Cell groupHeaderCell1 = groupHeaderRow.createCell(1); Cell gc1 = groupHeaderRow.createCell(1);
groupHeaderCell1.setCellValue("母羊信息"); gc1.setCellValue("母羊信息");
groupHeaderCell1.setCellStyle(eweHeaderStyle); gc1.setCellStyle(eweHeaderStyle);
sheet.addMergedRegion(new CellRangeAddress(rowNum - 1, rowNum - 1, 1, 12)); sheet.addMergedRegion(new CellRangeAddress(rowNum - 1, rowNum - 1, 1, 12));
Cell gc2 = groupHeaderRow.createCell(13);
Cell groupHeaderCell2 = groupHeaderRow.createCell(13); gc2.setCellValue("公羊信息");
groupHeaderCell2.setCellValue("公羊信息"); gc2.setCellStyle(ramHeaderStyle);
groupHeaderCell2.setCellStyle(ramHeaderStyle);
sheet.addMergedRegion(new CellRangeAddress(rowNum - 1, rowNum - 1, 13, 19)); sheet.addMergedRegion(new CellRangeAddress(rowNum - 1, rowNum - 1, 13, 19));
// 详细表头
Row headerRow = sheet.createRow(rowNum++); Row headerRow = sheet.createRow(rowNum++);
String[] headers = { String[] headers = {
"序号", "序号",
// 母羊信息
"母羊耳号", "母羊品种", "母羊家系", "母羊类别", "繁育状态", "胎次", "月龄", "体重", "核心羊群", "是否种用", "羊舍", "备注", "母羊耳号", "母羊品种", "母羊家系", "母羊类别", "繁育状态", "胎次", "月龄", "体重", "核心羊群", "是否种用", "羊舍", "备注",
// 公羊信息
"公羊耳号", "公羊品种", "公羊家系", "公羊类别", "生日", "月龄", "体重", "公羊耳号", "公羊品种", "公羊家系", "公羊类别", "生日", "月龄", "体重",
// 配种信息
"配种类型" "配种类型"
}; };
for (int i = 0; i < headers.length; i++) { for (int i = 0; i < headers.length; i++) {
Cell cell = headerRow.createCell(i); Cell cell = headerRow.createCell(i);
cell.setCellValue(headers[i]); cell.setCellValue(headers[i]);
if (i == 0 || i == headers.length - 1) { if (i == 0 || i == headers.length - 1) cell.setCellStyle(headerStyle);
cell.setCellStyle(headerStyle); else if (i <= 12) cell.setCellStyle(eweHeaderStyle);
} else if (i <= 12) { else cell.setCellStyle(ramHeaderStyle);
cell.setCellStyle(eweHeaderStyle);
} else {
cell.setCellStyle(ramHeaderStyle);
}
} }
// 数据行
for (int i = 0; i < planDetails.size(); i++) { for (int i = 0; i < planDetails.size(); i++) {
Map<String, Object> detail = planDetails.get(i); Map<String, Object> detail = planDetails.get(i);
Row dataRow = sheet.createRow(rowNum++); Row dataRow = sheet.createRow(rowNum++);
int colNum = 0; int colNum = 0;
// 序号
dataRow.createCell(colNum++).setCellValue(i + 1); dataRow.createCell(colNum++).setCellValue(i + 1);
// 母羊信息
dataRow.createCell(colNum++).setCellValue(getStringValue(detail, "ewe_manage_tags")); dataRow.createCell(colNum++).setCellValue(getStringValue(detail, "ewe_manage_tags"));
dataRow.createCell(colNum++).setCellValue(getStringValue(detail, "ewe_variety")); dataRow.createCell(colNum++).setCellValue(getStringValue(detail, "ewe_variety"));
dataRow.createCell(colNum++).setCellValue(getStringValue(detail, "ewe_family")); dataRow.createCell(colNum++).setCellValue(getStringValue(detail, "ewe_family"));
@@ -462,8 +642,6 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
dataRow.createCell(colNum++).setCellValue(getBooleanValue(detail, "ewe_is_breeding") ? "" : ""); dataRow.createCell(colNum++).setCellValue(getBooleanValue(detail, "ewe_is_breeding") ? "" : "");
dataRow.createCell(colNum++).setCellValue(getStringValue(detail, "ewe_sheepfold_name")); dataRow.createCell(colNum++).setCellValue(getStringValue(detail, "ewe_sheepfold_name"));
dataRow.createCell(colNum++).setCellValue(getStringValue(detail, "ewe_comment")); dataRow.createCell(colNum++).setCellValue(getStringValue(detail, "ewe_comment"));
// 公羊信息
dataRow.createCell(colNum++).setCellValue(getStringValue(detail, "ram_manage_tags")); dataRow.createCell(colNum++).setCellValue(getStringValue(detail, "ram_manage_tags"));
dataRow.createCell(colNum++).setCellValue(getStringValue(detail, "ram_variety")); dataRow.createCell(colNum++).setCellValue(getStringValue(detail, "ram_variety"));
dataRow.createCell(colNum++).setCellValue(getStringValue(detail, "ram_family")); dataRow.createCell(colNum++).setCellValue(getStringValue(detail, "ram_family"));
@@ -472,48 +650,36 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
dataRow.createCell(colNum++).setCellValue(getStringValue(detail, "ram_month_age")); dataRow.createCell(colNum++).setCellValue(getStringValue(detail, "ram_month_age"));
dataRow.createCell(colNum++).setCellValue(getStringValue(detail, "ram_current_weight")); dataRow.createCell(colNum++).setCellValue(getStringValue(detail, "ram_current_weight"));
// 配种类型
Object breedTypeObj = detail.get("breed_type"); Object breedTypeObj = detail.get("breed_type");
String breedTypeName = "未知类型"; String breedTypeName = "未知类型";
if (breedTypeObj != null) { if (breedTypeObj != null) {
try { try {
int typeValue = Integer.parseInt(breedTypeObj.toString()); int tv = Integer.parseInt(breedTypeObj.toString());
switch (typeValue) { switch (tv) {
case 1: breedTypeName = "供体母羊配种"; break; case 1: breedTypeName = "供体母羊配种"; break;
case 2: breedTypeName = "同期发情人工授精"; break; case 2: breedTypeName = "同期发情人工授精"; break;
case 3: breedTypeName = "本交"; break; case 3: breedTypeName = "本交"; break;
case 4: breedTypeName = "自然发情人工授精"; break; case 4: breedTypeName = "自然发情人工授精"; break;
default: breedTypeName = "未知类型";
} }
} catch (NumberFormatException e) { } catch (NumberFormatException ignored) {}
// ignore
} }
} dataRow.createCell(colNum).setCellValue(breedTypeName);
dataRow.createCell(colNum++).setCellValue(breedTypeName);
// 应用数据样式
for (int j = 0; j < headers.length; j++) { for (int j = 0; j < headers.length; j++) {
if (dataRow.getCell(j) != null) { if (dataRow.getCell(j) != null) dataRow.getCell(j).setCellStyle(dataStyle);
dataRow.getCell(j).setCellStyle(dataStyle);
}
} }
} }
// 自动调整列宽
for (int i = 0; i < headers.length; i++) { for (int i = 0; i < headers.length; i++) {
sheet.autoSizeColumn(i); sheet.autoSizeColumn(i);
// 设置最小宽度 if (sheet.getColumnWidth(i) < 2000) sheet.setColumnWidth(i, 2000);
if (sheet.getColumnWidth(i) < 2000) {
sheet.setColumnWidth(i, 2000);
}
} }
// 设置响应头
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8"); response.setCharacterEncoding("utf-8");
String fileName = java.net.URLEncoder.encode(planGenerate.getPlanName() + "_配种计划详情", "UTF-8").replaceAll("\\+", "%20"); String fileName = java.net.URLEncoder.encode(planGenerate.getPlanName() + "_配种计划详情", "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx"); response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
// 输出到响应流
workbook.write(response.getOutputStream()); workbook.write(response.getOutputStream());
workbook.close(); workbook.close();
@@ -522,17 +688,11 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
} }
} }
/**
* 安全获取字符串值
*/
private String getStringValue(Map<String, Object> map, String key) { private String getStringValue(Map<String, Object> map, String key) {
Object value = map.get(key); Object value = map.get(key);
return value != null ? value.toString() : ""; return value != null ? value.toString() : "";
} }
/**
* 安全获取布尔值
*/
private boolean getBooleanValue(Map<String, Object> map, String key) { private boolean getBooleanValue(Map<String, Object> map, String key) {
Object value = map.get(key); Object value = map.get(key);
if (value == null) return false; if (value == null) return false;
@@ -543,15 +703,11 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
/** /**
* 批量删除配种计划生成 * 批量删除配种计划生成
*
* @param ids 需要删除的配种计划生成主键
* @return 结果
*/ */
@Override @Override
@Transactional @Transactional
public int deleteScBreedPlanGenerateByIds(Long[] ids) public int deleteScBreedPlanGenerateByIds(Long[] ids)
{ {
// 删除相关的临时配种计划
for (Long id : ids) { for (Long id : ids) {
scBreedPlanGenerateMapper.deleteTempBreedPlanByPlanId(id); scBreedPlanGenerateMapper.deleteTempBreedPlanByPlanId(id);
} }
@@ -560,18 +716,15 @@ public class ScBreedPlanGenerateServiceImpl implements IScBreedPlanGenerateServi
/** /**
* 删除配种计划生成信息 * 删除配种计划生成信息
*
* @param id 配种计划生成主键
* @return 结果
*/ */
@Override @Override
@Transactional @Transactional
public int deleteScBreedPlanGenerateById(Long id) public int deleteScBreedPlanGenerateById(Long id)
{ {
// 先删除相关的临时配种计划
scBreedPlanGenerateMapper.deleteTempBreedPlanByPlanId(id); scBreedPlanGenerateMapper.deleteTempBreedPlanByPlanId(id);
return scBreedPlanGenerateMapper.deleteScBreedPlanGenerateById(id); return scBreedPlanGenerateMapper.deleteScBreedPlanGenerateById(id);
} }
@Override @Override
public List<String> searchEarNumbers(String query) { public List<String> searchEarNumbers(String query) {
return scBreedPlanGenerateMapper.searchEarNumbers(query); return scBreedPlanGenerateMapper.searchEarNumbers(query);

View File

@@ -104,4 +104,9 @@ public class ScDryMilkServiceImpl implements IScDryMilkService
public List<String> selectTechnicianList(String query) { public List<String> selectTechnicianList(String query) {
return scDryMilkMapper.searchTechnicianList(query); return scDryMilkMapper.searchTechnicianList(query);
} }
@Override
public List<String> searchEarNumbers(String query) {
return scDryMilkMapper.searchEarNumbers(query);
}
} }

View File

@@ -105,7 +105,7 @@ public class ScEmbryoFlushServiceImpl implements IScEmbryoFlushService
int unfertilized = flush.getUnfertilized() != null ? flush.getUnfertilized() : 0; int unfertilized = flush.getUnfertilized() != null ? flush.getUnfertilized() : 0;
int degenerated = flush.getDegenerated() != null ? flush.getDegenerated() : 0; int degenerated = flush.getDegenerated() != null ? flush.getDegenerated() : 0;
// 有效胚 = A+ + A + B + C + D // 有效胚 = A+ + A + B + C + D(16细胞D级) + 16细胞期
flush.setValidEmbryo(gradeAPlus + gradeA + gradeB + gradeC + gradeD); flush.setValidEmbryo(gradeAPlus + gradeA + gradeB + gradeC + gradeD);
// 冲胚数 = 所有数量求和 // 冲胚数 = 所有数量求和
@@ -220,6 +220,14 @@ public class ScEmbryoFlushServiceImpl implements IScEmbryoFlushService
return calculateEmbryoVarietyById(maleId, femaleId); return calculateEmbryoVarietyById(maleId, femaleId);
} }
@Override
public Map<String, Object> getSheepInfoByTag(String manageTag) {
if (manageTag == null || manageTag.trim().isEmpty()) {
return new HashMap<>();
}
return scEmbryoFlushMapper.selectSheepInfoByManageTag(manageTag);
}
/** /**
* 根据父母品种ID计算胚胎品种核心计算方法 * 根据父母品种ID计算胚胎品种核心计算方法
* *
@@ -324,4 +332,10 @@ public class ScEmbryoFlushServiceImpl implements IScEmbryoFlushService
{ {
return scEmbryoFlushMapper.selectDonorFemaleList(); return scEmbryoFlushMapper.selectDonorFemaleList();
} }
@Override
public List<Map<String, Object>> selectDonorMaleList()
{
return scEmbryoFlushMapper.selectDonorMaleList();
}
} }

View File

@@ -49,6 +49,10 @@ public class ScSheepDeathServiceImpl implements IScSheepDeathService
{ {
return scSheepDeathMapper.selectSheepFileByManageTags(manageTags); return scSheepDeathMapper.selectSheepFileByManageTags(manageTags);
} }
@Override
public List<String> searchEarNumbers(String query) {
return scSheepDeathMapper.searchEarNumbers(query);
}
@Override @Override
@Transactional @Transactional

View File

@@ -114,11 +114,6 @@ public class ScWeanRecordServiceImpl implements IScWeanRecordService
return scWeanRecordMapper.selectSheepIdByEarNumber(earNumber); return scWeanRecordMapper.selectSheepIdByEarNumber(earNumber);
} }
/**
* 【新增】模糊查询耳号列表 (用于前端远程搜索)
* @param query 查询关键字
* @return 耳号列表
*/
@Override @Override
public List<String> searchEarNumbers(String query) { public List<String> searchEarNumbers(String query) {
return scWeanRecordMapper.searchEarNumbers(query); return scWeanRecordMapper.searchEarNumbers(query);

View File

@@ -38,7 +38,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</resultMap> </resultMap>
<sql id="selectDdSaleVo"> <sql id="selectDdSaleVo">
select id, sale_date, cust_name, cust_phone, cust_addr, salesper, quaran_no, appr_no, price, tech, remark, create_by, create_time from dd_sl select id, sale_date, cust_name, cust_phone, cust_addr, salesper, quaran_no, appr_no, price, tech, remark, create_by, create_time from dd_sl dd_sl_alias
</sql> </sql>
<select id="selectDdSaleList" parameterType="DdSale" resultMap="DdSaleResult"> <select id="selectDdSaleList" parameterType="DdSale" resultMap="DdSaleResult">
@@ -53,6 +53,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="apprNo != null and apprNo != ''"> and appr_no = #{apprNo}</if> <if test="apprNo != null and apprNo != ''"> and appr_no = #{apprNo}</if>
<if test="price != null "> and price = #{price}</if> <if test="price != null "> and price = #{price}</if>
<if test="tech != null and tech != ''"> and tech = #{tech}</if> <if test="tech != null and tech != ''"> and tech = #{tech}</if>
${params.dataScope}
</where> </where>
</select> </select>
@@ -83,6 +84,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="remark != null">remark,</if> <if test="remark != null">remark,</if>
<if test="createBy != null">create_by,</if> <if test="createBy != null">create_by,</if>
<if test="createTime != null">create_time,</if> <if test="createTime != null">create_time,</if>
<if test="userId != null">user_id,</if>
<if test="deptId != null">dept_id,</if>
</trim> </trim>
<trim prefix="values (" suffix=")" suffixOverrides=","> <trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="saleDate != null">#{saleDate},</if> <if test="saleDate != null">#{saleDate},</if>
@@ -97,6 +100,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="remark != null">#{remark},</if> <if test="remark != null">#{remark},</if>
<if test="createBy != null">#{createBy},</if> <if test="createBy != null">#{createBy},</if>
<if test="createTime != null">#{createTime},</if> <if test="createTime != null">#{createTime},</if>
<if test="userId != null">#{userId},</if>
<if test="deptId != null">#{deptId},</if>
</trim> </trim>
</insert> </insert>

View File

@@ -325,4 +325,35 @@
ORDER BY sf.bs_manage_tags ORDER BY sf.bs_manage_tags
LIMIT 50 LIMIT 50
</select> </select>
<!-- ========== 导出配对表 / 导入配对表 相关查询 ========== -->
<!--
根据ID列表查询羊只耳号
gender: 1=母羊 2=公羊
-->
<select id="selectEarNumbersByIds" resultType="java.util.Map">
SELECT id, manage_tags
FROM bas_sheep
WHERE gender = #{gender}
AND is_delete = 0
AND id IN
<foreach item="id" collection="ids" open="(" separator="," close=")">
#{id}
</foreach>
</select>
<!--
根据管理耳号查询羊只ID
gender: 1=母羊 2=公羊
-->
<select id="selectSheepIdByManageTags" resultType="java.lang.Long">
SELECT id
FROM bas_sheep
WHERE manage_tags = #{manageTags}
AND gender = #{gender}
AND is_delete = 0
LIMIT 1
</select>
</mapper> </mapper>

View File

@@ -33,6 +33,16 @@
<select id="selectScDryMilkList" parameterType="ScDryMilk" resultMap="ScDryMilkResult"> <select id="selectScDryMilkList" parameterType="ScDryMilk" resultMap="ScDryMilkResult">
<include refid="selectScDryMilkVo"/> <include refid="selectScDryMilkVo"/>
<where> <where>
<!-- 全部羊多耳号查询 -->
<if test="allEarNumbers != null and allEarNumbers.size() > 0">
AND (
<!-- 注意s 代表 sheep_file 表的别名,根据实际 SQL 别名修改 -->
s.bs_manage_tags IN
<foreach collection="allEarNumbers" item="earNumber" open="(" separator="," close=")">
#{earNumber}
</foreach>
)
</if>
<if test="manageTagsList != null and manageTagsList.size() > 0"> <if test="manageTagsList != null and manageTagsList.size() > 0">
AND s.bs_manage_tags IN AND s.bs_manage_tags IN
<foreach collection="manageTagsList" item="tag" open="(" separator="," close=")"> <foreach collection="manageTagsList" item="tag" open="(" separator="," close=")">

View File

@@ -222,4 +222,18 @@
AND (sf.is_delete = 0 OR sf.is_delete IS NULL) AND (sf.is_delete = 0 OR sf.is_delete IS NULL)
ORDER BY sf.bs_manage_tags ORDER BY sf.bs_manage_tags
</select> </select>
<!-- 查询所有公羊列表(用于下拉选择) -->
<select id="selectDonorMaleList" resultType="java.util.Map">
SELECT DISTINCT
sf.bs_manage_tags AS manageTag,
sf.variety,
sf.variety_id AS varietyId,
sf.ranch_id AS ranchId,
sf.dr_ranch AS ranchName
FROM sheep_file sf
WHERE sf.gender = 2
AND (sf.is_delete = 0 OR sf.is_delete IS NULL)
ORDER BY sf.bs_manage_tags
</select>
</mapper> </mapper>

View File

@@ -159,6 +159,7 @@
br.ram_id as ram_id, br.ram_id as ram_id,
ram.bs_manage_tags as male_ear_number, ram.bs_manage_tags as male_ear_number,
ram.variety as male_breed, ram.variety as male_breed,
ram.family as male_lineage,
br.create_time as breeding_date, br.create_time as breeding_date,
DATEDIFF(CURDATE(), br.create_time) as pregnancy_days, DATEDIFF(CURDATE(), br.create_time) as pregnancy_days,
br.technician as technician br.technician as technician
@@ -212,8 +213,6 @@
<if test="technician != null">technician = #{technician},</if> <if test="technician != null">technician = #{technician},</if>
<if test="score != null">score = #{score},</if> <if test="score != null">score = #{score},</if>
<if test="comment != null">comment = #{comment},</if> <if test="comment != null">comment = #{comment},</if>
<if test="updateBy != null">update_by = #{updateBy},</if>
<if test="updateTime != null">update_time = #{updateTime},</if>
</trim> </trim>
where id = #{id} where id = #{id}
</update> </update>

View File

@@ -39,15 +39,52 @@
select d.id, d.sheep_id, d.manage_tags, d.event_type, d.death_date, d.disease_type_id, d.disease_subtype_id, select d.id, d.sheep_id, d.manage_tags, d.event_type, d.death_date, d.disease_type_id, d.disease_subtype_id,
d.disposal_direction, d.technician, d.handler, d.work_group, d.create_by, d.create_time, d.comment, d.disposal_direction, d.technician, d.handler, d.work_group, d.create_by, d.create_time, d.comment,
d.update_by, d.update_time, d.is_delete, d.update_by, d.update_time, d.is_delete,
s.variety, s.name as sheep_type_name, s.gender, s.day_age, s.parity, s.sheepfold_name,
s.breed as breed_status, s.post_lambing_day, s.lactation_day, s.gestation_day /* --- 关联数据获取 --- */
/* 1. 品种名称 (假设品种表是 bas_variety) */
(SELECT variety FROM bas_sheep_variety WHERE id = s.variety_id) as variety,
/* 2. 羊只类型名称 (从字典表获取) */
(SELECT dict_label FROM sys_dict_data WHERE dict_type = 'sys_sheep_type' AND dict_value = s.type_id) as sheep_type_name,
/* 3. 性别直接获取 */
s.gender,
/* 4. 计算日龄 (当前日期 - 出生日期) */
DATEDIFF(NOW(), s.birthday) as day_age,
/* 5. 胎次 */
s.parity,
/* 6. 羊舍名称 (假设羊舍表是 bas_sheepfold) */
(SELECT sheepfold_name FROM da_sheepfold WHERE id = s.sheepfold_id) as sheepfold_name,
/* 7. 繁育状态 (从字典表获取) */
(SELECT breed FROM bas_breed_status WHERE id = s.status_id) as breed,
/* 8. 其他数值字段 */
s.post_lambing_day,
s.lactation_day,
s.gestation_day
from sc_sheep_death d from sc_sheep_death d
left join sheep_file s on d.manage_tags = s.bs_manage_tags /* 关键修改:直接关联 bas_sheep 表,而不是视图 */
/* 同时只通过 manage_tags 关联,忽略 status_id 的限制 */
left join bas_sheep s on d.manage_tags = s.manage_tags AND s.is_delete = 0
</sql> </sql>
<select id="selectScSheepDeathList" parameterType="ScSheepDeath" resultMap="ScSheepDeathResult"> <select id="selectScSheepDeathList" parameterType="ScSheepDeath" resultMap="ScSheepDeathResult">
<include refid="selectScSheepDeathVo"/> <include refid="selectScSheepDeathVo"/>
<where> <where>
<if test="allEarNumbers != null and allEarNumbers.size() > 0">
AND (
s.manage_tags IN
<foreach collection="allEarNumbers" item="earNumber" open="(" separator="," close=")">
#{earNumber}
</foreach>
)
</if>
<if test="sheepId != null "> and d.sheep_id = #{sheepId}</if> <if test="sheepId != null "> and d.sheep_id = #{sheepId}</if>
<if test="manageTagsList != null and manageTagsList.size() > 0"> <if test="manageTagsList != null and manageTagsList.size() > 0">
@@ -99,16 +136,28 @@
and d.work_group = #{workGroup} and d.work_group = #{workGroup}
</if> </if>
<if test="variety != null and variety != ''"> and s.variety like concat('%', #{variety}, '%')</if> <if test="variety != null and variety != ''">
AND s.variety_id IN (
SELECT id FROM bas_sheep_variety WHERE variety LIKE concat('%', #{variety}, '%')
)
</if>
<if test="sheepTypeList != null and sheepTypeList.size() > 0"> <if test="sheepTypeList != null and sheepTypeList.size() > 0">
AND s.name IN AND s.type_id IN (
SELECT dict_value FROM sys_dict_data
WHERE dict_type = 'sys_sheep_type'
AND dict_label IN
<foreach collection="sheepTypeList" item="sType" open="(" separator="," close=")"> <foreach collection="sheepTypeList" item="sType" open="(" separator="," close=")">
#{sType} #{sType}
</foreach> </foreach>
)
</if> </if>
<if test="(sheepTypeList == null or sheepTypeList.size() == 0) and sheepType != null and sheepType != ''"> <if test="(sheepTypeList == null or sheepTypeList.size() == 0) and sheepType != null and sheepType != ''">
and s.name like concat('%', #{sheepType}, '%') AND s.type_id IN (
SELECT dict_value FROM sys_dict_data
WHERE dict_type = 'sys_sheep_type'
AND dict_label LIKE concat('%', #{sheepType}, '%')
)
</if> </if>
</where> </where>
order by d.create_time desc order by d.create_time desc

View File

@@ -4,7 +4,7 @@
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zhyc.module.produce.breed.mapper.ScWeanRecordMapper"> <mapper namespace="com.zhyc.module.produce.breed.mapper.ScWeanRecordMapper">
<resultMap type="com.zhyc.module.produce.breed.domain.ScWeanRecord" id="ScWeanRecordResult"> <resultMap type="ScWeanRecord" id="ScWeanRecordResult">
<result property="id" column="id" /> <result property="id" column="id" />
<result property="sheepId" column="sheep_id" /> <result property="sheepId" column="sheep_id" />
<result property="datetime" column="datetime" /> <result property="datetime" column="datetime" />
@@ -44,8 +44,11 @@
<where> <where>
<!-- 全部羊多耳号查询 --> <!-- 全部羊多耳号查询 -->
<!-- 正确sf.bs_manage_tagssf是sheep_file的别名 --> <!-- 正确sf.bs_manage_tagssf是sheep_file的别名 -->
<!-- 全部羊多耳号查询 -->
<if test="allEarNumbers != null and allEarNumbers.size() > 0"> <if test="allEarNumbers != null and allEarNumbers.size() > 0">
AND (sf.bs_manage_tags IN AND (
<!-- 注意s 代表 sheep_file 表的别名,根据实际 SQL 别名修改 -->
sf.bs_manage_tags IN
<foreach collection="allEarNumbers" item="earNumber" open="(" separator="," close=")"> <foreach collection="allEarNumbers" item="earNumber" open="(" separator="," close=")">
#{earNumber} #{earNumber}
</foreach> </foreach>
@@ -94,6 +97,15 @@
order by wr.create_time desc order by wr.create_time desc
</select> </select>
<!-- 模糊查询耳号列表 -->
<select id="searchEarNumbers" resultType="java.lang.String">
SELECT DISTINCT sf.bs_manage_tags
FROM sheep_file sf
WHERE sf.bs_manage_tags LIKE CONCAT(#{query}, '%')
AND sf.is_delete = 0
ORDER BY sf.bs_manage_tags
LIMIT 50
</select>
<select id="selectScWeanRecordById" parameterType="Long" resultMap="ScWeanRecordResult"> <select id="selectScWeanRecordById" parameterType="Long" resultMap="ScWeanRecordResult">
<include refid="selectScWeanRecordVo"/> <include refid="selectScWeanRecordVo"/>