refactor(sip): 重构项目信息导出功能

- 使用 EasyExcel 替代原 ExcelUtil工具类
- 优化项目信息导出逻辑,支持自定义表头和数据格式
-增加产品和服务信息的导出
- 修复模板中字段名称错误和字段缺失问题
- 优化代码结构,提高可读性和可维护性
master
chenhao 2025-06-16 17:06:07 +08:00
parent 80ab707e8f
commit 8051d95ad4
26 changed files with 791 additions and 128 deletions

View File

@ -37,7 +37,7 @@
<th:block th:include="include :: footer" /> <th:block th:include="include :: footer" />
<script> <script th:inline="javascript">
var prefix=ctx+'sip/list' var prefix=ctx+'sip/list'
var removeFlag = [[${@permission.hasPermi('manage:delivery:import:remove')}]] var removeFlag = [[${@permission.hasPermi('manage:delivery:import:remove')}]]
$(function() { $(function() {

View File

@ -102,7 +102,7 @@
<div class="section-title">项目信息</div> <div class="section-title">项目信息</div>
<table class="table"> <table class="table">
<tr> <tr>
<td class="shortTd">项目编<span class="is-required">*</span></td> <td class="shortTd">项目编<span class="is-required">*</span></td>
<td colspan="2" ><input type="text" name="projectCode" class="form-control" placeholder="保存后自动生成" <td colspan="2" ><input type="text" name="projectCode" class="form-control" placeholder="保存后自动生成"
readonly> readonly>
</td> </td>
@ -157,9 +157,9 @@
</select> </select>
</td> </td>
<td>汇智责任人</td> <td>汇智责任人</td>
<td><input name="hzSupportUserName" class="form-control" type="text" <td><input name="hzSupportUserName" th:value="${user.userName}" class="form-control" type="text"
onclick="selectPeople()"> onclick="selectPeople()">
<input name="hzSupportUser" class="form-control" type="hidden"></td> <input name="hzSupportUser" th:value="${user.userId}" class="form-control" type="hidden"></td>
</tr> </tr>
<tr> <tr>
<td class="shortTd">最终客户<span class="is-required">*</span></td> <td class="shortTd">最终客户<span class="is-required">*</span></td>
@ -184,14 +184,14 @@
th:value="${dict.dictValue}"></option> th:value="${dict.dictValue}"></option>
</select> </select>
</td> </td>
<td>H3C联系人</td> <td>新华三联系人</td>
<td > <td >
<input name="h3cPerson" class="form-control" type="text" <input name="h3cPerson" class="form-control" type="text"
> >
</td> </td>
<td>H3C TEL</td> <td>新华三TEL</td>
<td > <td >
<input name="h3cPhone " class="form-control" type="text" <input name="h3cPhone" class="form-control" type="text"
> >
</td> </td>
@ -223,7 +223,7 @@
></td> ></td>
<td>POC测试</td> <td>POC测试</td>
<td> <td>
<select name="countryProduct" class="form-control" value="0"> <select name="poc" class="form-control" value="0">
<option value="">请选择</option> <option value="">请选择</option>
<option value="1"></option> <option value="1"></option>
<option value="0"></option> <option value="0"></option>
@ -259,19 +259,19 @@
<tr> <tr>
<td>关键技术问题</td> <td>关键技术问题</td>
<td colspan="5"><textarea rows="2" type="text" name="keyProblem" <td colspan="5"><textarea rows="2" type="text" name="keyProblem"
class="form-control" maxlength="200" placeholder="限制200个字符"></textarea></td> class="form-control" maxlength="500" placeholder="限制500个字符"></textarea></td>
</tr> </tr>
<tr> <tr>
<td>项目简述<span class="is-required">*</span></td> <td>项目简述<span class="is-required">*</span></td>
<td colspan="5"><textarea name="projectDesc" rows="2" <td colspan="5"><textarea name="projectDesc" rows="2"
class="form-control" required maxlength="200" class="form-control" required maxlength="500"
placeholder="限制200个字符"></textarea> placeholder="限制500个字符"></textarea>
</td> </td>
</tr> </tr>
<tr> <tr>
<td>服务器配置</td> <td>服务器配置</td>
<td colspan="5"><textarea rows="2" name="serverConfiguration" class="form-control" maxlength="200" <td colspan="5"><textarea rows="2" name="serverConfiguration" class="form-control" maxlength="500"
placeholder="限制200个字符"></textarea></td> placeholder="限制500个字符"></textarea></td>
</tr> </tr>
</table> </table>

View File

@ -116,7 +116,7 @@
<table> <table>
<tr> <tr>
<td class="shortTd">项目编<span class="is-required">*</span></td> <td class="shortTd">项目编<span class="is-required">*</span></td>
<td colspan="2"><input type="text" th:field="*{projectCode}" name="projectCode" class="form-control" <td colspan="2"><input type="text" th:field="*{projectCode}" name="projectCode" class="form-control"
placeholder="保存后自动生成" readonly> placeholder="保存后自动生成" readonly>
</td> </td>
@ -200,14 +200,14 @@
th:value="${dict.dictValue}"></option> th:value="${dict.dictValue}"></option>
</select> </select>
</td> </td>
<td>H3C联系人</td> <td>新华三联系人</td>
<td > <td >
<input name="h3cPerson" class="form-control" type="text" th:field="*{h3cPerson}" <input name="h3cPerson" class="form-control" type="text" th:field="*{h3cPerson}"
> >
</td> </td>
<td>H3C TEL</td> <td>新华三TEL</td>
<td > <td >
<input name="h3cPhone " class="form-control" type="text" th:field="*{h3cPhone}" <input name="h3cPhone" class="form-control" type="text" th:field="*{h3cPhone}"
> >
</td> </td>
@ -242,7 +242,7 @@
th:value="${#dates.format(projectInfo.estimatedOrderTime, 'yyyy-MM-dd')}"></td> th:value="${#dates.format(projectInfo.estimatedOrderTime, 'yyyy-MM-dd')}"></td>
<td>POC测试</td> <td>POC测试</td>
<td> <td>
<select name="countryProduct" class="form-control" th:field="*{countryProduct}" readonly=""> <select name="poc" class="form-control" th:field="*{poc}" disabled>
<option value="">根据POC记录变化</option> <option value="">根据POC记录变化</option>
<option value="1"></option> <option value="1"></option>
<option value="0"></option> <option value="0"></option>
@ -276,20 +276,20 @@
<tr> <tr>
<td>关键技术问题</td> <td>关键技术问题</td>
<td colspan="5"><textarea rows="2" type="text" name="keyProblem" <td colspan="5"><textarea rows="2" type="text" name="keyProblem"
class="form-control" maxlength="200" th:field="*{keyProblem}" class="form-control" maxlength="500" th:field="*{keyProblem}"
placeholder="限制200个字符"></textarea></td> placeholder="限制500个字符"></textarea></td>
</tr> </tr>
<tr> <tr>
<td>项目简述<span class="is-required">*</span></td> <td>项目简述<span class="is-required">*</span></td>
<td colspan="5"><textarea name="projectDesc" rows="2" <td colspan="5"><textarea name="projectDesc" rows="2"
class="form-control" required maxlength="200" class="form-control" required maxlength="500"
placeholder="限制200个字符" th:field="*{projectDesc}"></textarea> placeholder="限制500个字符" th:field="*{projectDesc}"></textarea>
</td> </td>
</tr> </tr>
<tr> <tr>
<td>服务器配置</td> <td>服务器配置</td>
<td colspan="5"><textarea name="serverConfiguration" class="form-control" maxlength="200" rows="2" <td colspan="5"><textarea name="serverConfiguration" class="form-control" maxlength="500" rows="2"
placeholder="限制200个字符" th:field="*{serverConfiguration}"></textarea></td> placeholder="限制500个字符" th:field="*{serverConfiguration}"></textarea></td>
</tr> </tr>
</table> </table>

View File

@ -20,7 +20,7 @@
<div class="select-list"> <div class="select-list">
<ul> <ul>
<li> <li>
<label>产品名称</label> <label>产品编码</label>
<input type="text" name="productCode"/> <input type="text" name="productCode"/>
<input type="hidden" name="type"/> <input type="hidden" name="type"/>
@ -65,7 +65,7 @@
}, },
{ {
field: 'productCode', field: 'productCode',
title: '产品名称' title: '产品编码'
}, },
{ {
field: 'model', field: 'model',

View File

@ -20,7 +20,7 @@
<div class="select-list"> <div class="select-list">
<ul> <ul>
<li> <li>
<label>产品名称</label> <label>产品编码</label>
<input type="text" name="productCode"/> <input type="text" name="productCode"/>
<input type="hidden" name="type"/> <input type="hidden" name="type"/>
@ -65,7 +65,7 @@
}, },
{ {
field: 'productCode', field: 'productCode',
title: '产品名称' title: '产品编码'
}, },
{ {
field: 'model', field: 'model',

View File

@ -52,7 +52,7 @@
<div class="form-group"> <div class="form-group">
<label class="col-sm-4 control-label is-required">所属行业:</label> <label class="col-sm-4 control-label is-required">所属行业:</label>
<div class="col-sm-8"> <div class="col-sm-8">
<select name="industryType" required th:field="*{industryType}" class="form-control" th:with="type=${@dict.getType('industry_code')}" required> <select name="industryType" th:field="*{industryType}" class="form-control" th:with="type=${@dict.getType('industry_code')}" required>
<option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option> <option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option>
</select> </select>
</div> </div>

View File

@ -99,6 +99,12 @@
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
</dependency> </dependency>
<!-- 引入easyExcel 增强excel导出-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.6</version>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -85,7 +85,7 @@ import com.ruoyi.common.utils.file.FileTypeUtils;
import com.ruoyi.common.utils.file.FileUtils; import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.common.utils.file.ImageUtils; import com.ruoyi.common.utils.file.ImageUtils;
import com.ruoyi.common.utils.reflect.ReflectUtils; import com.ruoyi.common.utils.reflect.ReflectUtils;
import com.alibaba.excel.EasyExcel;
/** /**
* Excel * Excel
* *
@ -1900,4 +1900,28 @@ public class ExcelUtil<T>
} }
return method; return method;
} }
/**
* excellistEasyExcel
*
* @param is
* @return
*/
public List<T> importEasyExcel(InputStream is) throws Exception
{
return EasyExcel.read(is).head(clazz).sheet().doReadSync();
}
/**
* listexcelEasyExcel
*
* @param list
* @param sheetName
* @return
*/
public AjaxResult exportEasyExcel(List<T> list, String sheetName)
{
String filename = encodingFilename(sheetName);
EasyExcel.write(getAbsoluteFile(filename), clazz).sheet(sheetName).doWrite(list);
return AjaxResult.success(filename);
}
} }

View File

@ -1,6 +1,8 @@
package com.ruoyi.sip.controller; package com.ruoyi.sip.controller;
import java.util.List; import java.util.List;
import com.ruoyi.common.utils.ShiroUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions; import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
@ -90,9 +92,9 @@ public class ProjectInfoController extends BaseController
public AjaxResult export(ProjectInfo projectInfo) public AjaxResult export(ProjectInfo projectInfo)
{ {
//导出查询是否需要单独 //导出查询是否需要单独
List<ProjectInfo> list = projectInfoService.selectProjectInfoExportList(projectInfo); // ExcelUtil<ProjectInfo> util = new ExcelUtil<ProjectInfo>(ProjectInfo.class);
ExcelUtil<ProjectInfo> util = new ExcelUtil<ProjectInfo>(ProjectInfo.class); // return util.exportExcel(list, "项目管理数据");
return util.exportExcel(list, "项目管理数据"); return AjaxResult.success(projectInfoService.exportList(projectInfo));
} }
/** /**
@ -100,8 +102,9 @@ public class ProjectInfoController extends BaseController
*/ */
@RequiresPermissions("sip:project:add") @RequiresPermissions("sip:project:add")
@GetMapping("/add") @GetMapping("/add")
public String add() public String add(ModelMap mmap)
{ {
mmap.put("user", ShiroUtils.getSysUser());
return prefix + "/add"; return prefix + "/add";
} }

View File

@ -85,9 +85,8 @@ public class ProjectOrderInfoController extends BaseController
@ResponseBody @ResponseBody
public AjaxResult export(ProjectOrderInfo projectOrderInfo) public AjaxResult export(ProjectOrderInfo projectOrderInfo)
{ {
List<ProjectOrderInfo> list = projectOrderInfoService.selectProjectOrderInfoList(projectOrderInfo);
ExcelUtil<ProjectOrderInfo> util = new ExcelUtil<ProjectOrderInfo>(ProjectOrderInfo.class); return AjaxResult.success(projectOrderInfoService.exportList(projectOrderInfo));
return util.exportExcel(list, "订单管理数据");
} }
/** /**

View File

@ -4,7 +4,9 @@ import java.math.BigDecimal;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import com.alibaba.excel.annotation.ExcelProperty;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excels;
import lombok.Data; import lombok.Data;
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;
@ -33,16 +35,6 @@ public class ProjectInfo extends BaseEntity
@Excel(name = "项目名称") @Excel(name = "项目名称")
private String projectName; private String projectName;
/** 客户code */
// @Excel(name = "客户code")
private String customerCode;
/** 客户名称 */
@Excel(name = "最终客户")
private String customerName;
private String customerUserName;
private String customerPhone;
/** /**
* BG * BG
*/ */
@ -52,69 +44,112 @@ public class ProjectInfo extends BaseEntity
@Excel(name = "行业") @Excel(name = "行业")
private String industryType; private String industryType;
/** 属地 */
// @Excel(name = "属地")
// private String province;
/** 代表处 */ /** 代表处 */
private String agentCode; private String agentCode;
@Excel(name = "代表处") @Excel(name = "代表处")
private String agentName; private String agentName;
private String contactEmail;
private String contactPerson;
private String contactPhone;
/** 项目把握度 */
@Excel(name = "项目把握度")
private String projectGraspDegree;
/** /**
* *
*/ */
@Excel(name = "项目阶段", dictType = "project_stage") @Excel(name = "项目阶段", dictType = "project_stage")
private String projectStage; private String projectStage;
/** 项目把握度 */
@Excel(name = "项目把握度")
private String projectGraspDegree;
/** 汇智支撑人员id */ /** 汇智支撑人员id */
private String hzSupportUser; private String hzSupportUser;
@Excel(name = "汇智负责人") @Excel(name = "汇智负责人")
private String hzSupportUserName; private String hzSupportUserName;
@Excel(name = "poc") /** 客户code */
private String poc; // @Excel(name = "客户code")
private String customerCode;
/** 客户名称 */
@Excel(name = "最终客户")
private String customerName;
@Excel(name = "客户联系人")
private String customerUserName;
@Excel(name = "客户 TEL")
private String customerPhone;
/** /**
* *
*/ */
// @Excel(name = "运作机构") @Excel(name = "运作方",dictType = "operate_institution")
private String operateInstitution; private String operateInstitution;
@Excel(name = "新华三联系人")
private String h3cPerson;
@Excel(name = "新华三TEL")
private String h3cPhone;
/** 代理商code */ /** 代理商code */
private String partnerCode; private String partnerCode;
// @Excel(name = "代理商") @Excel(name = "代理商")
private String partnerName; private String partnerName;
@Excel(name = "代理商联系人")
private String partnerUserName; private String partnerUserName;
private String partnerEmail; private String partnerEmail;
/** 联系方式 */ /** 联系方式 */
// @Excel(name = "联系方式") @Excel(name = "代理商TEL")
private String contactWay; private String contactWay;
/** 预计金额 */ /** 预计金额 */
@Excel(name = "预计金额(元)") @Excel(name = "预计金额(元)")
private BigDecimal estimatedAmount; private BigDecimal estimatedAmount;
/** 属地 */
// @Excel(name = "属地")
// private String province;
/** 币种 */
// @Excel(name = "币种")
private String currencyType;
private String h3cPerson;
private String h3cPhone;
/** 预计下单时间 */ /** 预计下单时间 */
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@Excel(name = "预计下单时间", width = 30, dateFormat = "yyyy-MM-dd") @Excel(name = "预计下单时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date estimatedOrderTime; private Date estimatedOrderTime;
private Date estimatedOrderTimeStart; private Date estimatedOrderTimeStart;
private Date estimatedOrderTimeEnd; private Date estimatedOrderTimeEnd;
@Excel(name = "POC测试")
private String poc;
private String contactEmail;
private String contactPerson;
private String contactPhone;
/** 竞争对手 */
@Excel(name = "竞争对手")
private String competitor;
private List<String> competitorList;
/**
*
*/
@Excel(name = "是否国产",readConverterExp = "0=否,1=是")
private String countryProduct;
/** 关键技术问题 */
@Excel(name = "关键技术问题")
private String keyProblem;
/** 项目描述 */
@Excel(name = "项目描述")
private String projectDesc;
/** 服务器配置 */
@Excel(name = "服务器配置")
private String serverConfiguration;
/** 币种 */
// @Excel(name = "币种")
private String currencyType;
/** 预计发货时间 */ /** 预计发货时间 */
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
// @Excel(name = "预计发货时间", width = 30, dateFormat = "yyyy-MM-dd") // @Excel(name = "预计发货时间", width = 30, dateFormat = "yyyy-MM-dd")
@ -126,39 +161,29 @@ public class ProjectInfo extends BaseEntity
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@Excel(name = "更新时间", width = 30, dateFormat = "yyyy-MM-dd") @Excel(name = "更新时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date updateTime; private Date updateTime;
/** 竞争对手 */
// @Excel(name = "竞争对手")
private String competitor;
private List<String> competitorList;
/**
*
*/
// @Excel(name = "是否国产",readConverterExp = "0=否,1=是")
private String countryProduct;
/** 服务器配置 */
// @Excel(name = "服务器配置")
private String serverConfiguration;
/** 关键技术问题 */
// @Excel(name = "关键技术问题")
private String keyProblem;
/** 项目描述 */
// @Excel(name = "项目描述")
private String projectDesc;
private Boolean highlight; private Boolean highlight;
private Boolean canGenerate; private Boolean canGenerate;
/** 项目产品信息 */ /** 项目产品信息 */
@Excel(name = "软件")
private List<ProjectProductInfo> softwareProjectProductInfoList; private List<ProjectProductInfo> softwareProjectProductInfoList;
@Excel(name = "终端")
private List<ProjectProductInfo> hardwareProjectProductInfoList; private List<ProjectProductInfo> hardwareProjectProductInfoList;
@Excel(name = "服务")
private List<ProjectProductInfo> maintenanceProjectProductInfoList; private List<ProjectProductInfo> maintenanceProjectProductInfoList;
/** 项目操作日志信息 */ /** 项目操作日志信息 */
@ExcelProperty
private List<ProjectOperateLog> projectOperateLogList; private List<ProjectOperateLog> projectOperateLogList;
/** 项目工作进度信息 */ /** 项目工作进度信息 */
@Excel(name = "工作进度")
private List<ProjectWorkProgress> projectWorkProgressList; private List<ProjectWorkProgress> projectWorkProgressList;
private ProjectPocInfo projectPocInfo; private ProjectPocInfo projectPocInfo;

View File

@ -4,6 +4,7 @@ import java.math.BigDecimal;
import java.util.List; import java.util.List;
import lombok.Data; import lombok.Data;
import lombok.ToString;
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.ruoyi.common.annotation.Excel; import com.ruoyi.common.annotation.Excel;
@ -16,6 +17,7 @@ import com.ruoyi.common.core.domain.BaseEntity;
* @date 2025-06-04 * @date 2025-06-04
*/ */
@Data @Data
@ToString
public class ProjectProductInfo extends BaseEntity public class ProjectProductInfo extends BaseEntity
{ {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@ -24,7 +26,7 @@ public class ProjectProductInfo extends BaseEntity
private Long id; private Long id;
/** 项目id */ /** 项目id */
@Excel(name = "项目id") // @Excel(name = "项目id")
private Long projectId; private Long projectId;
/** 产品编码 */ /** 产品编码 */
@ -36,11 +38,11 @@ public class ProjectProductInfo extends BaseEntity
private String model; private String model;
/** 产品代码 */ /** 产品代码 */
@Excel(name = "产品代码") // @Excel(name = "产品代码")
private String productCode; private String productCode;
/** 产品描述 */ /** 产品描述 */
@Excel(name = "产品描述") // @Excel(name = "产品描述")
private String productDesc; private String productDesc;
/** 产品数量 */ /** 产品数量 */
@ -48,15 +50,15 @@ public class ProjectProductInfo extends BaseEntity
private Long quantity; private Long quantity;
/** 目录单价 */ /** 目录单价 */
@Excel(name = "目录单价") // @Excel(name = "目录单价")
private BigDecimal cataloguePrice; private BigDecimal cataloguePrice;
/** 目录总价 */ /** 目录总价 */
@Excel(name = "目录总价") // @Excel(name = "目录总价")
private BigDecimal catalogueAllPrice; private BigDecimal catalogueAllPrice;
/** 单价 */ /** 单价 */
@Excel(name = "单价") // @Excel(name = "单价")
private BigDecimal price; private BigDecimal price;
/** 总价 */ /** 总价 */
@ -64,11 +66,11 @@ public class ProjectProductInfo extends BaseEntity
private BigDecimal allPrice; private BigDecimal allPrice;
/** 指导折扣 */ /** 指导折扣 */
@Excel(name = "指导折扣") // @Excel(name = "指导折扣")
private BigDecimal guidanceDiscount; private BigDecimal guidanceDiscount;
/** 折扣 */ /** 折扣 */
@Excel(name = "折扣") // @Excel(name = "折扣")
private BigDecimal discount; private BigDecimal discount;
private String type; private String type;

View File

@ -36,7 +36,7 @@ public class ProjectWorkProgress extends BaseEntity
private Date workTime; private Date workTime;
/** 项目id */ /** 项目id */
@Excel(name = "项目id") // @Excel(name = "项目id")
private Long projectId; private Long projectId;
private String userName; private String userName;

View File

@ -59,7 +59,7 @@ public interface ProjectProductInfoMapper
*/ */
public int deleteProjectProductInfoByIds(String[] ids); public int deleteProjectProductInfoByIds(String[] ids);
List<ProjectProductInfo> selectProjectProductInfoListByProjectId(Long projectId); List<ProjectProductInfo> selectProjectProductInfoListByProjectId(List<Long> projectId);
void saveBatch(List<ProjectProductInfo> addList); void saveBatch(List<ProjectProductInfo> addList);

View File

@ -59,7 +59,7 @@ public interface ProjectWorkProgressMapper
*/ */
public int deleteProjectWorkProgressByIds(String[] ids); public int deleteProjectWorkProgressByIds(String[] ids);
List<ProjectWorkProgress> selectProjectWorkProgressListByProjectId(Long projectId); List<ProjectWorkProgress> selectProjectWorkProgressListByProjectId(List<Long> projectId);
void insertIgnoreBatch(List<ProjectWorkProgress> projectWorkProgressList); void insertIgnoreBatch(List<ProjectWorkProgress> projectWorkProgressList);

View File

@ -59,5 +59,5 @@ public interface IProjectInfoService
*/ */
public int deleteProjectInfoById(Long id); public int deleteProjectInfoById(Long id);
List<ProjectInfo> selectProjectInfoExportList(ProjectInfo projectInfo); String exportList(ProjectInfo projectInfo);
} }

View File

@ -68,4 +68,6 @@ public interface IProjectOrderInfoService
*/ */
List< ProjectOrderInfo> selectProjectOrderInfoByProjectId(List<Long> projectId); List< ProjectOrderInfo> selectProjectOrderInfoByProjectId(List<Long> projectId);
String exportList(ProjectOrderInfo projectOrderInfo);
} }

View File

@ -59,7 +59,7 @@ public interface IProjectProductInfoService
*/ */
public int deleteProjectProductInfoById(Long id); public int deleteProjectProductInfoById(Long id);
List<ProjectProductInfo> selectProjectProductInfoListByProjectId(Long projectId); List<ProjectProductInfo> selectProjectProductInfoListByProjectId(List<Long> projectId);
void saveBatch(List<ProjectProductInfo> addList); void saveBatch(List<ProjectProductInfo> addList);
} }

View File

@ -59,7 +59,7 @@ public interface IProjectWorkProgressService
*/ */
public int deleteProjectWorkProgressById(Long id); public int deleteProjectWorkProgressById(Long id);
List<ProjectWorkProgress> selectProjectWorkProgressListByProjectId(Long projectId); List<ProjectWorkProgress> selectProjectWorkProgressListByProjectId(List<Long> projectId);
void insertIgnoreBatch(List<ProjectWorkProgress> projectWorkProgressList); void insertIgnoreBatch(List<ProjectWorkProgress> projectWorkProgressList);
} }

View File

@ -1,7 +1,14 @@
package com.ruoyi.sip.service.impl; package com.ruoyi.sip.service.impl;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.StopWatch; import cn.hutool.core.date.DateUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
import com.ruoyi.common.core.domain.entity.SysUser; import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.text.Convert; import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.exception.ServiceException;
@ -9,10 +16,14 @@ import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.DictUtils; import com.ruoyi.common.utils.DictUtils;
import com.ruoyi.common.utils.ShiroUtils; import com.ruoyi.common.utils.ShiroUtils;
import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.sip.domain.*; import com.ruoyi.sip.domain.*;
import com.ruoyi.sip.mapper.ProjectInfoMapper; import com.ruoyi.sip.mapper.ProjectInfoMapper;
import com.ruoyi.sip.service.*; import com.ruoyi.sip.service.*;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Sheet;
import org.hibernate.validator.internal.constraintvalidators.bv.time.future.FutureValidatorForOffsetTime;
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;
@ -22,7 +33,6 @@ import java.time.LocalDate;
import java.time.Period; import java.time.Period;
import java.time.ZoneId; import java.time.ZoneId;
import java.util.*; import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -61,6 +71,7 @@ public class ProjectInfoServiceImpl implements IProjectInfoService {
public static final String BG_TYPE_DICT_TYPE = "bg_type"; public static final String BG_TYPE_DICT_TYPE = "bg_type";
public static final String PROJECT_STAGE_DICT_TYPE = "project_stage"; public static final String PROJECT_STAGE_DICT_TYPE = "project_stage";
public static final String OPERATE_INSTITUTION_DICT_TYPE = "operate_institution"; public static final String OPERATE_INSTITUTION_DICT_TYPE = "operate_institution";
private FutureValidatorForOffsetTime futureValidatorForOffsetTime;
/** /**
* *
@ -75,7 +86,7 @@ public class ProjectInfoServiceImpl implements IProjectInfoService {
projectInfo.setCompetitorList(Stream.of(projectInfo.getCompetitor().split(",")).collect(Collectors.toList())); projectInfo.setCompetitorList(Stream.of(projectInfo.getCompetitor().split(",")).collect(Collectors.toList()));
} }
//查询产品信息 //查询产品信息
List<ProjectProductInfo> projectProductInfos = productInfoService.selectProjectProductInfoListByProjectId(projectInfo.getId()); List<ProjectProductInfo> projectProductInfos = productInfoService.selectProjectProductInfoListByProjectId(Collections.singletonList(projectInfo.getId()));
Map<String, List<ProjectProductInfo>> productListMap = projectProductInfos.stream().collect(Collectors.groupingBy(ProjectProductInfo::getType)); Map<String, List<ProjectProductInfo>> productListMap = projectProductInfos.stream().collect(Collectors.groupingBy(ProjectProductInfo::getType));
projectInfo.setSoftwareProjectProductInfoList(productListMap.get(ProductInfo.ProductTypeEnum.SOFTWARE.getType())); projectInfo.setSoftwareProjectProductInfoList(productListMap.get(ProductInfo.ProductTypeEnum.SOFTWARE.getType()));
projectInfo.setHardwareProjectProductInfoList(productListMap.get(ProductInfo.ProductTypeEnum.HARDWARE.getType())); projectInfo.setHardwareProjectProductInfoList(productListMap.get(ProductInfo.ProductTypeEnum.HARDWARE.getType()));
@ -83,7 +94,7 @@ public class ProjectInfoServiceImpl implements IProjectInfoService {
maintenanceProjectProductInfoList.addAll(productListMap.getOrDefault(ProductInfo.ProductTypeEnum.SOFTWARE_MAINTENANCE.getType(), new ArrayList<>())); maintenanceProjectProductInfoList.addAll(productListMap.getOrDefault(ProductInfo.ProductTypeEnum.SOFTWARE_MAINTENANCE.getType(), new ArrayList<>()));
projectInfo.setMaintenanceProjectProductInfoList(maintenanceProjectProductInfoList); projectInfo.setMaintenanceProjectProductInfoList(maintenanceProjectProductInfoList);
//查询变更记录信息 //查询变更记录信息
List<ProjectWorkProgress> projectWorkProgresses = workProgressService.selectProjectWorkProgressListByProjectId((projectInfo.getId())); List<ProjectWorkProgress> projectWorkProgresses = workProgressService.selectProjectWorkProgressListByProjectId(Collections.singletonList((projectInfo.getId())));
projectInfo.setProjectWorkProgressList(projectWorkProgresses); projectInfo.setProjectWorkProgressList(projectWorkProgresses);
//查询操作日志信息 //查询操作日志信息
List<ProjectOperateLog> projectOperateLogs = operateLogService.selectProjectOperateLogListByProjectId(projectInfo.getId()); List<ProjectOperateLog> projectOperateLogs = operateLogService.selectProjectOperateLogListByProjectId(projectInfo.getId());
@ -153,7 +164,7 @@ public class ProjectInfoServiceImpl implements IProjectInfoService {
private void setProjectCode(ProjectInfo projectInfo) { private void setProjectCode(ProjectInfo projectInfo) {
String s = projectInfoMapper.selectMaxProjectCode(projectInfo); String s = projectInfoMapper.selectMaxProjectCode(projectInfo);
if (s == null) { if (s == null) {
s = "0"; s = "V0";
} }
projectInfo.setProjectCode(PROJECT_CODE_PREFIX + String.format("%0" + PROJECT_CODE_LENGTH + "d", projectInfo.setProjectCode(PROJECT_CODE_PREFIX + String.format("%0" + PROJECT_CODE_LENGTH + "d",
Integer.parseInt(s.substring(PROJECT_CODE_PREFIX.length())) + 1)); Integer.parseInt(s.substring(PROJECT_CODE_PREFIX.length())) + 1));
@ -470,17 +481,306 @@ public class ProjectInfoServiceImpl implements IProjectInfoService {
} }
@Override @Override
public List<ProjectInfo> selectProjectInfoExportList(ProjectInfo projectInfo) { public String exportList(ProjectInfo projectInfo) {
List<ProjectInfo> projectInfos = projectInfoMapper.selectProjectInfoList(projectInfo); try {
for (ProjectInfo info : projectInfos) { // 获取项目信息列表
// info.setIndustryType(DictUtils.getDictLabel(INDUSTRY_TYPE_DICT_TYPE, info.getIndustryType())); List<ProjectInfo> projectInfos = fetchProjectInfos(projectInfo);
// info.setProjectStage(DictUtils.getDictLabel(PROJECT_STAGE_DICT_TYPE, info.getIndustryType()));
if ("YYS".equals(info.getBgProperty())){ // 补充项目详细数据
info.setIndustryType(DictUtils.getDictLabel(INDUSTRY_TYPE_YYS_DICT_TYPE, info.getIndustryType())); enrichProjectData(projectInfos);
}else{
info.setIndustryType(DictUtils.getDictLabel(INDUSTRY_TYPE_DICT_TYPE, info.getIndustryType())); // 计算各类产品的最大数量
List<Integer> maxCounts= calculateMaxProductCounts(projectInfos);
int maxSoftware = maxCounts.get(0);
int maxHardware = maxCounts.get(1);
int maxMaintenance = maxCounts.get(2);
int maxWorkIndex = maxCounts.get(3);
// 构建 Excel 表头和数据
List<List<String>> header = buildExcelHeader(maxSoftware, maxHardware, maxMaintenance, maxWorkIndex);
List<List<String>> data = buildExcelData(projectInfos, maxSoftware, maxHardware, maxMaintenance, maxWorkIndex);
// 导出 Excel 文件
return writeExcelToFile(header, data);
} catch (Exception e) {
log.error("导出项目信息失败", e);
throw new ServiceException("导出项目信息失败,请稍后重试");
} }
} }
private List<ProjectInfo> fetchProjectInfos(ProjectInfo projectInfo) {
List<ProjectInfo> projectInfos = projectInfoMapper.selectProjectInfoList(projectInfo);
if (CollUtil.isEmpty(projectInfos)) {
throw new ServiceException("未找到符合条件的项目信息");
}
return projectInfos; return projectInfos;
} }
private void enrichProjectData(List<ProjectInfo> projectInfos) {
List<Long> idList = projectInfos.stream().map(ProjectInfo::getId).distinct().collect(Collectors.toList());
// 查询产品信息
Map<Long, List<ProjectProductInfo>> productMap = productInfoService.selectProjectProductInfoListByProjectId(idList)
.stream()
.collect(Collectors.groupingBy(ProjectProductInfo::getProjectId));
// 查询变更记录信息
Map<Long, List<ProjectWorkProgress>> workProgressMap = workProgressService.selectProjectWorkProgressListByProjectId(idList)
.stream()
.collect(Collectors.groupingBy(ProjectWorkProgress::getProjectId));
for (ProjectInfo info : projectInfos) {
Optional.ofNullable(productMap.get(info.getId()))
.ifPresent(products -> {
Map<String, List<ProjectProductInfo>> productListMap = products.stream()
.collect(Collectors.groupingBy(ProjectProductInfo::getType));
info.setSoftwareProjectProductInfoList(productListMap.getOrDefault(ProductInfo.ProductTypeEnum.SOFTWARE.getType(), Collections.emptyList()));
info.setHardwareProjectProductInfoList(productListMap.getOrDefault(ProductInfo.ProductTypeEnum.HARDWARE.getType(), Collections.emptyList()));
List<ProjectProductInfo> maintenanceProjectProductInfoList = productListMap.getOrDefault(ProductInfo.ProductTypeEnum.SOFTWARE_MAINTENANCE.getType(), new ArrayList<>());
maintenanceProjectProductInfoList.addAll(productListMap.getOrDefault(ProductInfo.ProductTypeEnum.HARDWARE_MAINTENANCE.getType(), Collections.emptyList()));
info.setMaintenanceProjectProductInfoList(maintenanceProjectProductInfoList);
});
info.setProjectWorkProgressList(Optional.ofNullable(workProgressMap.get(info.getId())).orElse(Collections.emptyList()));
if ("YYS".equals(info.getBgProperty())) {
info.setIndustryType(DictUtils.getDictLabel(INDUSTRY_TYPE_YYS_DICT_TYPE, info.getIndustryType()));
} else {
info.setIndustryType(DictUtils.getDictLabel(INDUSTRY_TYPE_DICT_TYPE, info.getIndustryType()));
}
}
}
private List<Integer> calculateMaxProductCounts(List<ProjectInfo> projectInfos) {
int maxSoftware = 0, maxHardware = 0, maxMaintenance = 0, maxWorkIndex = 0;
for (ProjectInfo info : projectInfos) {
if (info.getSoftwareProjectProductInfoList() != null) {
maxSoftware = Math.max(maxSoftware, info.getSoftwareProjectProductInfoList().size());
}
if (info.getHardwareProjectProductInfoList() != null) {
maxHardware = Math.max(maxHardware, info.getHardwareProjectProductInfoList().size());
}
if (info.getMaintenanceProjectProductInfoList() != null) {
maxMaintenance = Math.max(maxMaintenance, info.getMaintenanceProjectProductInfoList().size());
}
if (info.getProjectWorkProgressList() != null) {
maxWorkIndex = Math.max(maxWorkIndex, info.getProjectWorkProgressList().size());
}
}
return Arrays.asList(maxSoftware, maxHardware, maxMaintenance, maxWorkIndex);
}
private String writeExcelToFile(List<List<String>> header, List<List<String>> data) {
ExcelUtil<ProjectInfo> util = new ExcelUtil<>(ProjectInfo.class);
String fileName = util.encodingFilename("项目信息");
String filePath = util.getAbsoluteFile(fileName);
WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
contentWriteCellStyle.setWrapped(true);
// 创建工作簿并写入数据
EasyExcel.write(filePath).head(header)
.sheet("项目信息")
// 注册自定义列宽策略
.registerWriteHandler(new CustomColumnWidthStyleStrategy())
// 注册自动换行
.registerWriteHandler(new HorizontalCellStyleStrategy(null, contentWriteCellStyle))
.doWrite(data);
return fileName;
}
// 自定义列宽策略类
private static class CustomColumnWidthStyleStrategy extends AbstractColumnWidthStyleStrategy {
@Override
protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<CellData> list, Cell cell, Head head, Integer integer, Boolean aBoolean) {
int columnIndex = cell.getColumnIndex();
Sheet sheet = writeSheetHolder.getSheet();
// 基础列范围(前 25 列)
if (columnIndex >= 0 && columnIndex <= 24) {
// 自适应列宽逻辑
sheet.setColumnWidth(columnIndex, 256 * 30); // 设置固定宽度为 20 个字符
}
// 产品和服务列范围(从第 25 列开始)
else {
// 固定列宽逻辑
sheet.setColumnWidth(columnIndex, 256 * 10); // 设置固定宽度为 20 个字符
}
}
}
public List<List<String>> buildExcelHeader( int maxSoftware, int maxHardware, int maxMaintenance, int maxWorkIndex) {
List<List<String>> headerList = new ArrayList<>();
headerList.add(Collections.singletonList("项目编号"));
headerList.add(Collections.singletonList("项目名称"));
headerList.add(Collections.singletonList("BG"));
headerList.add(Collections.singletonList("行业"));
headerList.add(Collections.singletonList("代表处"));
headerList.add(Collections.singletonList("项目阶段"));
headerList.add(Collections.singletonList("项目把握度"));
headerList.add(Collections.singletonList("汇智负责人"));
headerList.add(Collections.singletonList("最终客户"));
headerList.add(Collections.singletonList("客户联系人"));
headerList.add(Collections.singletonList("客户 TEL"));
headerList.add(Collections.singletonList("运作方"));
headerList.add(Collections.singletonList("新华三联系人"));
headerList.add(Collections.singletonList("新华三TEL"));
headerList.add(Collections.singletonList("代理商"));
headerList.add(Collections.singletonList("代理商联系人"));
headerList.add(Collections.singletonList("代理商TEL"));
headerList.add(Collections.singletonList("预计金额(元)"));
headerList.add(Collections.singletonList("预计下单时间"));
headerList.add(Collections.singletonList("POC测试"));
headerList.add(Collections.singletonList("竞争对手"));
headerList.add(Collections.singletonList("是否国产"));
headerList.add(Collections.singletonList("关键技术问题"));
headerList.add(Collections.singletonList("项目描述"));
headerList.add(Collections.singletonList("服务器配置"));
headerList.add(Collections.singletonList("更新时间"));
// 添加软件产品列
for (int i = 1; i <= maxSoftware; i++) {
headerList.add(Collections.singletonList("软件编码" + i));
headerList.add(Collections.singletonList("型号" + i));
headerList.add(Collections.singletonList("数量" + i));
headerList.add(Collections.singletonList("总价" + i));
}
// 添加终端产品列
for (int i = 1; i <= maxHardware; i++) {
headerList.add(Collections.singletonList("终端编码" + i));
headerList.add(Collections.singletonList("型号" + i));
headerList.add(Collections.singletonList("数量" + i));
headerList.add(Collections.singletonList("总价" + i));
}
// 添加服务产品列
for (int i = 1; i <= maxMaintenance; i++) {
headerList.add(Collections.singletonList("服务编码" + i));
headerList.add(Collections.singletonList("型号" + i));
headerList.add(Collections.singletonList("数量" + i));
headerList.add(Collections.singletonList("总价" + i));
}
headerList.add(Collections.singletonList("产品总价"));
for (int i = 1; i <=maxWorkIndex; i++) {
headerList.add(Collections.singletonList("变更内容" + i));
headerList.add(Collections.singletonList("变更人" + i));
headerList.add(Collections.singletonList("变更时间" + i));
}
return headerList;
}
public List<List<String>> buildExcelData(List<ProjectInfo> projectInfos, int maxSoftware, int maxHardware, int maxMaintenance, int maxWorkIndex) {
List<List<String>> dataList = new ArrayList<>();
for (ProjectInfo info : projectInfos) {
List<String> row = new ArrayList<>();
// 添加基础字段
row.add(info.getProjectCode());
row.add(info.getProjectName());
row.add(DictUtils.getDictLabel("bg_type", info.getBgProperty()));
row.add(info.getIndustryType());
row.add(info.getAgentName());
row.add(DictUtils.getDictLabel("project_stage", info.getProjectStage()));
row.add(info.getProjectGraspDegree());
row.add(info.getHzSupportUserName());
row.add(info.getCustomerName());
row.add(info.getCustomerUserName());
row.add(info.getCustomerPhone());
row.add(DictUtils.getDictLabel("operate_institution",info.getOperateInstitution()));
row.add(info.getH3cPerson());
row.add(info.getH3cPhone());
row.add(info.getPartnerName());
row.add(info.getPartnerUserName());
row.add(info.getContactWay());
row.add(info.getEstimatedAmount()!=null?info.getEstimatedAmount().toString():"");
row.add(DateUtil.format(info.getEstimatedOrderTime(), "yyyy-MM-dd"));
row.add(formatterStr(info.getPoc()));
row.add(info.getCompetitor());
row.add(formatterStr(info.getCountryProduct()));
row.add(info.getKeyProblem());
row.add(info.getProjectDesc());
row.add(info.getServerConfiguration());
row.add(DateUtil.format(info.getUpdateTime(), "yyyy-MM-dd"));
BigDecimal totalPrice = BigDecimal.ZERO;
// 添加软件产品列
for (int i = 0; i < maxSoftware; i++) {
if (CollUtil.isNotEmpty(info.getSoftwareProjectProductInfoList()) && i < info.getSoftwareProjectProductInfoList().size()) {
totalPrice= addProductRow(info.getSoftwareProjectProductInfoList().get(i), row, totalPrice);
} else {
addProductRow(null, row, totalPrice);
}
}
// 添加终端产品列
for (int i = 0; i < maxHardware; i++) {
if (CollUtil.isNotEmpty(info.getHardwareProjectProductInfoList()) && i < info.getHardwareProjectProductInfoList().size()) {
totalPrice=addProductRow(info.getHardwareProjectProductInfoList().get(i), row, totalPrice);
} else {
addProductRow(null, row, totalPrice);
}
}
// 添加服务产品列
for (int i = 0; i < maxMaintenance; i++) {
if (CollUtil.isNotEmpty(info.getMaintenanceProjectProductInfoList()) && i < info.getMaintenanceProjectProductInfoList().size()) {
totalPrice=addProductRow(info.getMaintenanceProjectProductInfoList().get(i), row, totalPrice);
} else {
addProductRow(null, row, totalPrice);
}
}
row.add(totalPrice.toString());
for (int i = 0; i < maxWorkIndex; i++) {
if (CollUtil.isNotEmpty(info.getProjectWorkProgressList()) && i < info.getProjectWorkProgressList().size()) {
ProjectWorkProgress workProgress = info.getProjectWorkProgressList().get(i);
row.add(workProgress.getWorkContent());
row.add(workProgress.getUserName());
row.add(DateUtil.format(workProgress.getWorkTime(), "yyyy-MM-dd"));
} else {
row.add("");
row.add("");
row.add("");
}
}
dataList.add(row);
}
return dataList;
}
private String formatterStr(String value){
if (StringUtils.isEmpty(value)){
return "";
}
return "0".equals(value)?"否":"是";
}
private static BigDecimal addProductRow(ProjectProductInfo productInfo, List<String> row, BigDecimal totalPrice) {
if (productInfo == null) {
row.add("");
row.add("");
row.add("");
row.add("");
return totalPrice;
}
row.add(productInfo.getProductBomCode());
row.add(productInfo.getModel());
row.add(productInfo.getQuantity() == null ? "" : productInfo.getQuantity().toString());
row.add(productInfo.getAllPrice() == null ? "" : productInfo.getAllPrice().toString());
if (productInfo.getAllPrice() != null) {
totalPrice = totalPrice.add(productInfo.getAllPrice());
}
return totalPrice;
}
} }

View File

@ -1,23 +1,37 @@
package com.ruoyi.sip.service.impl; package com.ruoyi.sip.service.impl;
import java.util.ArrayList; import java.math.BigDecimal;
import java.util.Collections; import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils; import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.DictUtils;
import com.ruoyi.common.utils.ShiroUtils; import com.ruoyi.common.utils.ShiroUtils;
import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.sip.domain.*; import com.ruoyi.sip.domain.*;
import com.ruoyi.sip.service.*; import com.ruoyi.sip.service.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Sheet;
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 com.ruoyi.sip.mapper.ProjectOrderInfoMapper; import com.ruoyi.sip.mapper.ProjectOrderInfoMapper;
import com.ruoyi.common.core.text.Convert; import com.ruoyi.common.core.text.Convert;
import static com.ruoyi.sip.service.impl.ProjectInfoServiceImpl.INDUSTRY_TYPE_DICT_TYPE;
import static com.ruoyi.sip.service.impl.ProjectInfoServiceImpl.INDUSTRY_TYPE_YYS_DICT_TYPE;
/** /**
* Service * Service
* *
@ -25,6 +39,7 @@ import com.ruoyi.common.core.text.Convert;
* @date 2025-05-30 * @date 2025-05-30
*/ */
@Service @Service
@Slf4j
public class ProjectOrderInfoServiceImpl implements IProjectOrderInfoService { public class ProjectOrderInfoServiceImpl implements IProjectOrderInfoService {
@Autowired @Autowired
private ProjectOrderInfoMapper projectOrderInfoMapper; private ProjectOrderInfoMapper projectOrderInfoMapper;
@ -46,7 +61,7 @@ public class ProjectOrderInfoServiceImpl implements IProjectOrderInfoService {
public ProjectOrderInfo selectProjectOrderInfoById(Long id) { public ProjectOrderInfo selectProjectOrderInfoById(Long id) {
ProjectOrderInfo projectOrderInfo = projectOrderInfoMapper.selectProjectOrderInfoById(id); ProjectOrderInfo projectOrderInfo = projectOrderInfoMapper.selectProjectOrderInfoById(id);
List<ProjectProductInfo> projectProductInfos = productInfoService.selectProjectProductInfoListByProjectId(projectOrderInfo.getProjectId()); List<ProjectProductInfo> projectProductInfos = productInfoService.selectProjectProductInfoListByProjectId(Collections.singletonList(projectOrderInfo.getProjectId()));
Map<String, List<ProjectProductInfo>> productListMap = projectProductInfos.stream().collect(Collectors.groupingBy(ProjectProductInfo::getType)); Map<String, List<ProjectProductInfo>> productListMap = projectProductInfos.stream().collect(Collectors.groupingBy(ProjectProductInfo::getType));
projectOrderInfo.setSoftwareProjectProductInfoList(productListMap.get(ProductInfo.ProductTypeEnum.SOFTWARE.getType())); projectOrderInfo.setSoftwareProjectProductInfoList(productListMap.get(ProductInfo.ProductTypeEnum.SOFTWARE.getType()));
projectOrderInfo.setHardwareProjectProductInfoList(productListMap.get(ProductInfo.ProductTypeEnum.HARDWARE.getType())); projectOrderInfo.setHardwareProjectProductInfoList(productListMap.get(ProductInfo.ProductTypeEnum.HARDWARE.getType()));
@ -203,4 +218,282 @@ public class ProjectOrderInfoServiceImpl implements IProjectOrderInfoService {
} }
return projectOrderInfoMapper.selectProjectOrderInfoByProjectId(projectId); return projectOrderInfoMapper.selectProjectOrderInfoByProjectId(projectId);
} }
@Override
public String exportList(ProjectOrderInfo projectOrderInfo) {
try {
// 获取项目信息列表
List<ProjectOrderInfo> projectInfos = fetchProjectInfos(projectOrderInfo);
// 补充项目详细数据
enrichProjectData(projectInfos);
// 计算各类产品的最大数量
List<Integer> maxCounts = calculateMaxProductCounts(projectInfos);
int maxSoftware = maxCounts.get(0);
int maxHardware = maxCounts.get(1);
int maxMaintenance = maxCounts.get(2);
// 构建 Excel 表头和数据
List<List<String>> header = buildExcelHeader(maxSoftware, maxHardware, maxMaintenance);
List<List<String>> data = buildExcelData(projectInfos, maxSoftware, maxHardware, maxMaintenance);
// 导出 Excel 文件
return writeExcelToFile(header, data);
} catch (Exception e) {
log.error("导出项目信息失败", e);
throw new ServiceException("导出项目信息失败,请稍后重试");
}
}
private List<ProjectOrderInfo> fetchProjectInfos(ProjectOrderInfo projectOrderInfo) {
List<ProjectOrderInfo> projectOrderInfos = projectOrderInfoMapper.selectProjectOrderInfoList(projectOrderInfo);
if (CollUtil.isEmpty(projectOrderInfos)) {
throw new ServiceException("未找到符合条件的项目信息");
}
return projectOrderInfos;
}
private void enrichProjectData(List<ProjectOrderInfo> projectInfos) {
List<Long> idList = projectInfos.stream().map(ProjectOrderInfo::getProjectId).distinct().collect(Collectors.toList());
// 查询产品信息
Map<Long, List<ProjectProductInfo>> productMap = productInfoService.selectProjectProductInfoListByProjectId(idList)
.stream()
.collect(Collectors.groupingBy(ProjectProductInfo::getProjectId));
for (ProjectOrderInfo info : projectInfos) {
Optional.ofNullable(productMap.get(info.getProjectId()))
.ifPresent(products -> {
Map<String, List<ProjectProductInfo>> productListMap = products.stream()
.collect(Collectors.groupingBy(ProjectProductInfo::getType));
info.setSoftwareProjectProductInfoList(productListMap.getOrDefault(ProductInfo.ProductTypeEnum.SOFTWARE.getType(), Collections.emptyList()));
info.setHardwareProjectProductInfoList(productListMap.getOrDefault(ProductInfo.ProductTypeEnum.HARDWARE.getType(), Collections.emptyList()));
List<ProjectProductInfo> maintenanceProjectProductInfoList = productListMap.getOrDefault(ProductInfo.ProductTypeEnum.SOFTWARE_MAINTENANCE.getType(), new ArrayList<>());
maintenanceProjectProductInfoList.addAll(productListMap.getOrDefault(ProductInfo.ProductTypeEnum.HARDWARE_MAINTENANCE.getType(), Collections.emptyList()));
info.setMaintenanceProjectProductInfoList(maintenanceProjectProductInfoList);
});
if ("YYS".equals(info.getBgProperty())) {
info.setIndustryType(DictUtils.getDictLabel(INDUSTRY_TYPE_YYS_DICT_TYPE, info.getIndustryType()));
} else {
info.setIndustryType(DictUtils.getDictLabel(INDUSTRY_TYPE_DICT_TYPE, info.getIndustryType()));
}
}
}
private List<Integer> calculateMaxProductCounts(List<ProjectOrderInfo> projectInfos) {
int maxSoftware = 0, maxHardware = 0, maxMaintenance = 0;
for (ProjectOrderInfo info : projectInfos) {
if (info.getSoftwareProjectProductInfoList() != null) {
maxSoftware = Math.max(maxSoftware, info.getSoftwareProjectProductInfoList().size());
}
if (info.getHardwareProjectProductInfoList() != null) {
maxHardware = Math.max(maxHardware, info.getHardwareProjectProductInfoList().size());
}
if (info.getMaintenanceProjectProductInfoList() != null) {
maxMaintenance = Math.max(maxMaintenance, info.getMaintenanceProjectProductInfoList().size());
}
}
return Arrays.asList(maxSoftware, maxHardware, maxMaintenance);
}
private String writeExcelToFile(List<List<String>> header, List<List<String>> data) {
ExcelUtil<ProjectInfo> util = new ExcelUtil<>(ProjectInfo.class);
String fileName = util.encodingFilename("订单信息");
String filePath = util.getAbsoluteFile(fileName);
WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
contentWriteCellStyle.setWrapped(true);
// 创建工作簿并写入数据
EasyExcel.write(filePath).head(header)
.sheet("订单信息")
// 注册自定义列宽策略
.registerWriteHandler(new CustomColumnWidthStyleStrategy())
// 注册自动换行
.registerWriteHandler(new HorizontalCellStyleStrategy(null, contentWriteCellStyle))
.doWrite(data);
return fileName;
}
// 自定义列宽策略类
private static class CustomColumnWidthStyleStrategy extends AbstractColumnWidthStyleStrategy {
@Override
protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<CellData> list, Cell cell, Head head, Integer integer, Boolean aBoolean) {
int columnIndex = cell.getColumnIndex();
Sheet sheet = writeSheetHolder.getSheet();
// 基础列范围(前 25 列)
if (columnIndex >= 0 && columnIndex <= 25) {
// 自适应列宽逻辑
sheet.setColumnWidth(columnIndex, 256 * 30); // 设置固定宽度为 20 个字符
}
// 产品和服务列范围(从第 25 列开始)
else {
// 固定列宽逻辑
sheet.setColumnWidth(columnIndex, 256 * 10); // 设置固定宽度为 20 个字符
}
}
}
public List<List<String>> buildExcelHeader(int maxSoftware, int maxHardware, int maxMaintenance) {
List<List<String>> headerList = new ArrayList<>();
headerList.add(Collections.singletonList("项目编号"));
headerList.add(Collections.singletonList("项目名称"));
headerList.add(Collections.singletonList("最终客户"));
headerList.add(Collections.singletonList("BG"));
headerList.add(Collections.singletonList("行业"));
headerList.add(Collections.singletonList("代表处"));
headerList.add(Collections.singletonList("进货商接口人"));
headerList.add(Collections.singletonList("Email"));
headerList.add(Collections.singletonList("联系方式"));
headerList.add(Collections.singletonList("合同编号"));
headerList.add(Collections.singletonList("币种"));
headerList.add(Collections.singletonList("订单金额"));
headerList.add(Collections.singletonList("执行单有效截止时间"));
headerList.add(Collections.singletonList("要求到货时间"));
headerList.add(Collections.singletonList("公司直发"));
headerList.add(Collections.singletonList("下单通路"));
headerList.add(Collections.singletonList("供货商"));
headerList.add(Collections.singletonList("汇智责任人"));
headerList.add(Collections.singletonList("Email"));
headerList.add(Collections.singletonList("进货商"));
headerList.add(Collections.singletonList("进货商代码"));
headerList.add(Collections.singletonList("进货商类型"));
headerList.add(Collections.singletonList("进货商联系人"));
headerList.add(Collections.singletonList("Email"));
headerList.add(Collections.singletonList("联系方式"));
headerList.add(Collections.singletonList("其他特别说明"));
headerList.add(Collections.singletonList("订单状态"));
// 添加软件产品列
for (int i = 1; i <= maxSoftware; i++) {
headerList.add(Collections.singletonList("软件编码" + i));
headerList.add(Collections.singletonList("型号" + i));
headerList.add(Collections.singletonList("数量" + i));
headerList.add(Collections.singletonList("总价" + i));
}
// 添加终端产品列
for (int i = 1; i <= maxHardware; i++) {
headerList.add(Collections.singletonList("终端编码" + i));
headerList.add(Collections.singletonList("型号" + i));
headerList.add(Collections.singletonList("数量" + i));
headerList.add(Collections.singletonList("总价" + i));
}
// 添加服务产品列
for (int i = 1; i <= maxMaintenance; i++) {
headerList.add(Collections.singletonList("服务编码" + i));
headerList.add(Collections.singletonList("型号" + i));
headerList.add(Collections.singletonList("数量" + i));
headerList.add(Collections.singletonList("总价" + i));
}
headerList.add(Collections.singletonList("产品总价"));
return headerList;
}
public List<List<String>> buildExcelData(List<ProjectOrderInfo> projectInfos, int maxSoftware, int maxHardware, int maxMaintenance) {
List<List<String>> dataList = new ArrayList<>();
for (ProjectOrderInfo info : projectInfos) {
List<String> row = new ArrayList<>();
// 添加基础字段
row.add(info.getProjectCode());
row.add(info.getProjectName());
row.add(info.getCustomerName());
row.add(DictUtils.getDictLabel("bg_type", info.getBgProperty()));
row.add(info.getIndustryType());
row.add(info.getAgentName());
row.add(info.getBusinessPerson());
row.add(info.getBusinessEmail());
row.add(info.getBusinessPhone());
row.add(info.getOrderCode());
row.add(DictUtils.getDictLabel("currency_type", info.getCurrencyType()));
row.add(info.getShipmentAmount() != null ? info.getShipmentAmount().toString() : "");
row.add(DateUtil.format(info.getOrderEndTime(), "yyyy-MM-dd"));
row.add(DateUtil.format(info.getDeliveryTime(), "yyyy-MM-dd"));
row.add(DictUtils.getDictLabel("company_delivery", info.getCompanyDelivery()));
row.add(StringUtils.isEmpty(info.getOrderChannel()) ? "" :
"1".equals(info.getOrderChannel()) ? "总代" : "直签");
row.add(info.getSupplier());
row.add(info.getDutyName());
row.add(info.getDutyEmail());
row.add(info.getDutyPhone());
row.add(info.getPartnerName());
row.add(info.getPartnerCode());
row.add(DictUtils.getDictLabel("identify_level", info.getLevel()));
row.add(info.getPartnerUserName());
row.add(info.getPartnerEmail());
row.add(info.getPartnerPhone());
row.add(info.getRemark());
row.add(DictUtils.getDictLabel("order_status", info.getOrderStatus()));
BigDecimal totalPrice = BigDecimal.ZERO;
// 添加软件产品列
for (int i = 0; i < maxSoftware; i++) {
if (CollUtil.isNotEmpty(info.getSoftwareProjectProductInfoList()) && i < info.getSoftwareProjectProductInfoList().size()) {
totalPrice = addProductRow(info.getSoftwareProjectProductInfoList().get(i), row, totalPrice);
} else {
addProductRow(null, row, totalPrice);
}
}
// 添加终端产品列
for (int i = 0; i < maxHardware; i++) {
if (CollUtil.isNotEmpty(info.getHardwareProjectProductInfoList()) && i < info.getHardwareProjectProductInfoList().size()) {
totalPrice = addProductRow(info.getHardwareProjectProductInfoList().get(i), row, totalPrice);
} else {
addProductRow(null, row, totalPrice);
}
}
// 添加服务产品列
for (int i = 0; i < maxMaintenance; i++) {
if (CollUtil.isNotEmpty(info.getMaintenanceProjectProductInfoList()) && i < info.getMaintenanceProjectProductInfoList().size()) {
totalPrice = addProductRow(info.getMaintenanceProjectProductInfoList().get(i), row, totalPrice);
} else {
addProductRow(null, row, totalPrice);
}
}
row.add(totalPrice.toString());
dataList.add(row);
}
return dataList;
}
private String formatterStr(String value) {
if (StringUtils.isEmpty(value)) {
return "";
}
return "0".equals(value) ? "否" : "是";
}
private static BigDecimal addProductRow(ProjectProductInfo productInfo, List<String> row, BigDecimal totalPrice) {
if (productInfo == null) {
row.add("");
row.add("");
row.add("");
row.add("");
return totalPrice;
}
row.add(productInfo.getProductBomCode());
row.add(productInfo.getModel());
row.add(productInfo.getQuantity() == null ? "" : productInfo.getQuantity().toString());
row.add(productInfo.getAllPrice() == null ? "" : productInfo.getAllPrice().toString());
if (productInfo.getAllPrice() != null) {
totalPrice = totalPrice.add(productInfo.getAllPrice());
}
return totalPrice;
}
} }

View File

@ -83,12 +83,13 @@ public class ProjectPocInfoServiceImpl implements IProjectPocInfoService
public int saveProjectPocInfo(ProjectPocInfo projectPocInfo) public int saveProjectPocInfo(ProjectPocInfo projectPocInfo)
{ {
// projectPocInfoMapper.deleteProjectPocInfoDetailByPocId(projectPocInfo.getId()); // projectPocInfoMapper.deleteProjectPocInfoDetailByPocId(projectPocInfo.getId());
insertProjectPocInfoDetail(projectPocInfo);
if (projectPocInfo.getId() == null) { if (projectPocInfo.getId() == null) {
int i = projectPocInfoMapper.insertProjectPocInfo(projectPocInfo); int i = projectPocInfoMapper.insertProjectPocInfo(projectPocInfo);
insertProjectPocInfoDetail(projectPocInfo); insertProjectPocInfoDetail(projectPocInfo);
return i; return i;
} }
insertProjectPocInfoDetail(projectPocInfo);
return projectPocInfoMapper.updateProjectPocInfo(projectPocInfo); return projectPocInfoMapper.updateProjectPocInfo(projectPocInfo);
} }

View File

@ -98,7 +98,7 @@ public class ProjectProductInfoServiceImpl implements IProjectProductInfoService
} }
@Override @Override
public List<ProjectProductInfo> selectProjectProductInfoListByProjectId(Long projectId) { public List<ProjectProductInfo> selectProjectProductInfoListByProjectId(List<Long> projectId) {
return projectProductInfoMapper.selectProjectProductInfoListByProjectId(projectId); return projectProductInfoMapper.selectProjectProductInfoListByProjectId(projectId);
} }
@ -119,7 +119,7 @@ public class ProjectProductInfoServiceImpl implements IProjectProductInfoService
} }
List<ProjectProductInfo> projectProductInfos = projectProductInfoMapper.selectProjectProductInfoListByProjectId(list.get(0).getProjectId()); List<ProjectProductInfo> projectProductInfos = projectProductInfoMapper.selectProjectProductInfoListByProjectId(Collections.singletonList(list.get(0).getProjectId()));
Set<Long> idSet = list.stream().map(ProjectProductInfo::getId).collect(Collectors.toSet()); Set<Long> idSet = list.stream().map(ProjectProductInfo::getId).collect(Collectors.toSet());
String[] deleteIds = projectProductInfos.stream().filter(item -> !idSet.contains(item.getId())).map(item -> item.getId().toString()).toArray(String[]::new); String[] deleteIds = projectProductInfos.stream().filter(item -> !idSet.contains(item.getId())).map(item -> item.getId().toString()).toArray(String[]::new);
//删除数据 //删除数据

View File

@ -96,7 +96,7 @@ public class ProjectWorkProgressServiceImpl implements IProjectWorkProgressServi
} }
@Override @Override
public List<ProjectWorkProgress> selectProjectWorkProgressListByProjectId(Long projectId) { public List<ProjectWorkProgress> selectProjectWorkProgressListByProjectId(List<Long> projectId) {
return projectWorkProgressMapper.selectProjectWorkProgressListByProjectId(projectId); return projectWorkProgressMapper.selectProjectWorkProgressListByProjectId(projectId);
} }

View File

@ -57,7 +57,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</select> </select>
<select id="selectProjectProductInfoListByProjectId" resultType="com.ruoyi.sip.domain.ProjectProductInfo"> <select id="selectProjectProductInfoListByProjectId" resultType="com.ruoyi.sip.domain.ProjectProductInfo">
<include refid="selectProjectProductRelationInfoVo"/> <include refid="selectProjectProductRelationInfoVo"/>
where project_id = #{projectId} where project_id in
<foreach collection="list" item="item" close=")" open="(" separator=",">
#{item}
</foreach>
</select> </select>
<insert id="insertProjectProductInfo" parameterType="ProjectProductInfo" useGeneratedKeys="true" keyProperty="id"> <insert id="insertProjectProductInfo" parameterType="ProjectProductInfo" useGeneratedKeys="true" keyProperty="id">

View File

@ -36,7 +36,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
t2.user_name t2.user_name
from project_work_progress t1 from project_work_progress t1
left join sys_user t2 on t1.work_user = t2.user_id left join sys_user t2 on t1.work_user = t2.user_id
where t1.project_id = #{projectId} where t1.project_id in (
<foreach item="item" index="index" collection="list" separator=",">
#{item}
</foreach>
)
order by t1.work_time order by t1.work_time
</select> </select>