zhoulisky 8 months ago
parent
commit
fe0c3aa947
30 changed files with 542 additions and 75 deletions
  1. 3 0
      kd-service-api/kd-scientific-api/src/main/java/org/sky/scientific/pojo/vo/AttendanceVO.java
  2. 8 0
      kd-service-api/kd-scientific-api/src/main/java/org/sky/scientific/pojo/vo/DailyAttendance.java
  3. 34 2
      kd-service-api/kd-scientific-api/src/main/java/org/sky/scientific/pojo/vo/XmSummary.java
  4. 3 0
      kd-service-api/kd-scientific-api/src/main/java/org/sky/scientific/pojo/vo/XmTechnicianSummary.java
  5. 3 0
      kd-service-api/kd-scientific-api/src/main/java/org/sky/scientific/pojo/vo/XmTechnicianVO.java
  6. 6 6
      kd-service-api/kd-system-api/src/main/java/org/sky/system/pojo/entity/Tenant.java
  7. 8 0
      kd-service/kd-scientific/src/main/java/org/sky/scientific/controller/AttendanceController.java
  8. 8 1
      kd-service/kd-scientific/src/main/java/org/sky/scientific/controller/XmController.java
  9. 21 0
      kd-service/kd-scientific/src/main/java/org/sky/scientific/e/XmStatusEnum.java
  10. 26 0
      kd-service/kd-scientific/src/main/java/org/sky/scientific/excel/handle/MergeCellRangeWriteHandler.java
  11. 45 0
      kd-service/kd-scientific/src/main/java/org/sky/scientific/excel/handle/XmcyryhzmExcel.java
  12. 1 1
      kd-service/kd-scientific/src/main/java/org/sky/scientific/mapper/CgQtMapper.xml
  13. 12 9
      kd-service/kd-scientific/src/main/java/org/sky/scientific/mapper/XmTechnicianMapper.xml
  14. 9 0
      kd-service/kd-scientific/src/main/java/org/sky/scientific/service/IAttendanceService.java
  15. 8 0
      kd-service/kd-scientific/src/main/java/org/sky/scientific/service/IXmService.java
  16. 155 17
      kd-service/kd-scientific/src/main/java/org/sky/scientific/service/impl/AttendanceServiceImpl.java
  17. 1 2
      kd-service/kd-scientific/src/main/java/org/sky/scientific/service/impl/SalaryServiceImpl.java
  18. 121 28
      kd-service/kd-scientific/src/main/java/org/sky/scientific/service/impl/TechnicianServiceImpl.java
  19. 69 7
      kd-service/kd-scientific/src/main/java/org/sky/scientific/service/impl/XmServiceImpl.java
  20. BIN
      kd-service/kd-scientific/src/main/resources/export-template/参研人员花名册.xls
  21. BIN
      kd-service/kd-scientific/src/main/resources/export-template/年度高新科技人员汇总表.xls
  22. BIN
      kd-service/kd-scientific/src/main/resources/export-template/每月研发工时记录表.xls
  23. BIN
      kd-service/kd-scientific/src/main/resources/export-template/每月考勤信息.xls
  24. BIN
      kd-service/kd-scientific/src/main/resources/export-template/研发项目汇总表.xls
  25. BIN
      kd-service/kd-scientific/src/main/resources/export-template/研发项目汇总表1.xls
  26. BIN
      kd-service/kd-scientific/src/main/resources/export-template/项目参研人员汇总表.xls
  27. BIN
      kd-service/kd-scientific/src/main/resources/export-template/项目变更.doc
  28. BIN
      kd-service/kd-scientific/src/main/resources/export-template/项目变更清单.xls
  29. BIN
      kd-service/kd-scientific/src/main/resources/export-template/项目实施-阶段性报告.doc
  30. 1 2
      kd-service/kd-system/src/main/java/org/sky/system/service/impl/TenantServiceImpl.java

+ 3 - 0
kd-service-api/kd-scientific-api/src/main/java/org/sky/scientific/pojo/vo/AttendanceVO.java

@@ -8,6 +8,9 @@ import java.util.List;
 @Data
 public class AttendanceVO {
 
+	@Schema(description = "序号", hidden = true)
+	private Integer xh;
+
 	@Schema(description = "研发人员唯一标识")
 	private String unicode;
 

+ 8 - 0
kd-service-api/kd-scientific-api/src/main/java/org/sky/scientific/pojo/vo/DailyAttendance.java

@@ -8,6 +8,14 @@ public class DailyAttendance {
 	private String workDate;        // 日期(如"20250506")
 	private Double workHours;
 
+	public DailyAttendance() {
+
+	}
+
+	public DailyAttendance(String workDate, Double workHours) {
+		this.workDate = workDate;
+		this.workHours = workHours;
+	}
 	@Override
 	public String toString() {
 		return "DailyAttendance{" +

+ 34 - 2
kd-service-api/kd-scientific-api/src/main/java/org/sky/scientific/pojo/vo/XmSummary.java

@@ -8,11 +8,15 @@ import lombok.Data;
 import org.sky.core.tool.utils.DateUtil;
 import org.springframework.format.annotation.DateTimeFormat;
 
+import java.math.BigDecimal;
 import java.util.Date;
 
 @Data
 public class XmSummary {
 
+	@Schema(description = "序号")
+	private Integer xh;
+
 	@Schema(description = "项目id,主键")
 	private Long id;
 
@@ -25,13 +29,17 @@ public class XmSummary {
 	@Schema(description = "研发开始时间")
 	@DateTimeFormat(pattern = DateUtil.PATTERN_DATE)
 	@JsonFormat(pattern = DateUtil.PATTERN_DATE)
-	private Date xmkssj;
+ 	private Date xmkssj;
+
+	private String xmkssjStr;
 
 	@Schema(description = "研发结束时间")
 	@DateTimeFormat(pattern = DateUtil.PATTERN_DATE)
 	@JsonFormat(pattern = DateUtil.PATTERN_DATE)
 	private Date xmjssj;
 
+	private String xmjssjStr;
+
 	@Schema(description = "项目负责人id")
 	private Long xmfzr;
 
@@ -47,16 +55,31 @@ public class XmSummary {
 	@Schema(description = "项目状态")
 	private Integer status;
 
+	@Schema(description = "项目状态")
+	private String statusName;
+
 	@Schema(description = "预算明细", hidden = true)
 	@JsonIgnore
 	private String xmyszemx;
 
 	@Schema(description = "预算总额")
-	private Double xmysze;
+	private BigDecimal xmysze;
+
+	@Schema(description = "全周期已归集(万元)")
+	private BigDecimal qzqygj;
+
+	@Schema(description = "总预算完成率(%)")
+	private BigDecimal zyswcl;
 
 	@Schema(description = "本年度预算")
 	private Double xmyszeCurrentYear;
 
+	@Schema(description = "年度已归集研发费用(万元)")
+	private BigDecimal ndygjyffy;
+
+	@Schema(description = "年度已归集金额/年度预算(%)")
+	private BigDecimal ndygjjel;
+
 	@Schema(description = "立项报告", hidden = true)
 	@JsonIgnore
 	private String lxbg;
@@ -64,12 +87,21 @@ public class XmSummary {
 	@Schema(description = "立项报告数量")
 	private int lxbgCount;
 
+	@Schema(description = "立项报告数量")
+	private String lxbgStatus;
+
 	@Schema(description = "阶段性报告数量")
 	private int jdxbgCount;
 
+	@Schema(description = "阶段性报告数量")
+	private String jdxbgStatus;
+
 	@Schema(description = "验收报告数量")
 	private int ysbgCount;
 
+	@Schema(description = "验收报告数量")
+	private String ysbgStatus;
+
 	@Schema(description = "发明专利")
 	private int fmzlCount;
 

+ 3 - 0
kd-service-api/kd-scientific-api/src/main/java/org/sky/scientific/pojo/vo/XmTechnicianSummary.java

@@ -10,6 +10,9 @@ import java.util.List;
 @Data
 public class XmTechnicianSummary {
 
+	@Schema(description = "研发项目编号")
+	private String xmbh;
+
 	@Schema(description = "研发项目名称")
 	private String xmmc;
 

+ 3 - 0
kd-service-api/kd-scientific-api/src/main/java/org/sky/scientific/pojo/vo/XmTechnicianVO.java

@@ -63,4 +63,7 @@ public class XmTechnicianVO implements Serializable {
 
 	@Schema(description = "研发时长")
 	private List<DailyAttendance> hoursList;
+//
+//	@Schema(description = "研发时长")
+//	private List<BigDecimal> hours1;
 }

+ 6 - 6
kd-service-api/kd-system-api/src/main/java/org/sky/system/pojo/entity/Tenant.java

@@ -6,6 +6,7 @@
  import com.fasterxml.jackson.databind.annotation.JsonSerialize;
  import com.fasterxml.jackson.databind.ser.std.NullSerializer;
  import io.swagger.v3.oas.annotations.media.Schema;
+ import jakarta.validation.constraints.Size;
  import lombok.Data;
  import lombok.EqualsAndHashCode;
  import org.sky.core.mp.base.BaseEntity;
@@ -28,17 +29,16 @@ public class Tenant extends BaseEntity {
 	@Serial
 	private static final long serialVersionUID = 1L;
 
-	/**
-	 * 租户ID
-	 */
 	@Schema(description = "租户ID")
 	private String tenantId;
-	/**
-	 * 租户名称
-	 */
+
 	@Schema(description = "租户名称")
 	private String tenantName;
 
+	@Schema(description = "租户简称")
+	@Size(max = 6)
+	private String shortName;
+
 	@Schema(description = "统一社会信用代码")
 	private String uscc;
 

+ 8 - 0
kd-service/kd-scientific/src/main/java/org/sky/scientific/controller/AttendanceController.java

@@ -5,6 +5,7 @@ import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.annotation.Resource;
+import jakarta.servlet.http.HttpServletResponse;
 import jakarta.validation.Valid;
 import lombok.AllArgsConstructor;
 import org.sky.core.boot.ctrl.KdController;
@@ -50,6 +51,13 @@ public class AttendanceController extends KdController {
 		return R.success("操作成功");
 	}
 
+	@PostMapping("export")
+	@ApiOperationSupport(order = 3)
+	@Operation(summary = "每月考勤信息-导出")
+	public void exportAttendance(@RequestBody AttendanceDto dto, HttpServletResponse response) {
+		service.exportAttendance(response, dto);
+	}
+
 	@PostMapping("/remove")
 	@ApiOperationSupport(order = 7)
 	@Operation(summary = "每月考勤信息-清除考勤数据", description  = "多个 unicode、yearAndMonth")

+ 8 - 1
kd-service/kd-scientific/src/main/java/org/sky/scientific/controller/XmController.java

@@ -83,12 +83,19 @@ public class XmController extends KdController {
 
 	@GetMapping("/summary")
 	@ApiOperationSupport(order = 3)
-	@Operation(summary = "研项目-汇总表", description  = "传入xm")
+	@Operation(summary = "研项目汇总表", description  = "传入xm")
 	public R<IPage<XmSummary>> summary(XmDTO xm, Query query) {
 		IPage<XmSummary> pages = service.summaryPage(Condition.getPage(query),xm);
 		return R.data(pages);
 	}
 
+	@PostMapping("summary-export")
+	@ApiOperationSupport(order = 3)
+	@Operation(summary = "研发项目汇总表-导出")
+	public void exportSummary(@RequestBody XmDTO dto, HttpServletResponse response) {
+		service.exportSummary(response, dto);
+	}
+
 	@PostMapping("/save")
 	@ApiOperationSupport(order = 4)
 	@Operation(summary = "项目立项", description  = "传入xm")

+ 21 - 0
kd-service/kd-scientific/src/main/java/org/sky/scientific/e/XmStatusEnum.java

@@ -0,0 +1,21 @@
+package org.sky.scientific.e;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+public enum XmStatusEnum {
+
+//	1:开展中,2:已结题,3:项目暂停,4:项目终止
+
+
+	开展中("开展中", 1),
+	已结题("已结题", 2),
+	项目暂停("项目暂停", 3),
+	项目终止("项目终止", 4);
+
+	final String name;
+	final int code;
+
+}

+ 26 - 0
kd-service/kd-scientific/src/main/java/org/sky/scientific/excel/handle/MergeCellRangeWriteHandler.java

@@ -0,0 +1,26 @@
+package org.sky.scientific.excel.handle;
+
+import com.alibaba.excel.write.handler.SheetWriteHandler;
+import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
+import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.util.CellRangeAddress;
+
+import java.util.Collections;
+import java.util.List;
+
+public class MergeCellRangeWriteHandler implements SheetWriteHandler {
+
+	private final List<CellRangeAddress> rangeCellList;
+
+	public MergeCellRangeWriteHandler(List<CellRangeAddress> rangeCellList) {
+		this.rangeCellList = (rangeCellList == null) ? Collections.emptyList() : rangeCellList;
+	}
+
+	public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
+		Sheet sheet = writeSheetHolder.getSheet();
+		for (CellRangeAddress cellRangeAddress : this.rangeCellList) {
+			sheet.addMergedRegionUnsafe(cellRangeAddress);
+		}
+	}
+}

+ 45 - 0
kd-service/kd-scientific/src/main/java/org/sky/scientific/excel/handle/XmcyryhzmExcel.java

@@ -0,0 +1,45 @@
+
+package org.sky.scientific.excel.handle;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+public class XmcyryhzmExcel {
+
+ 	private String xh;
+
+	@Schema(description = "研发项目编号")
+	private String xmbh;
+
+	@Schema(description = "研发项目名称")
+	private String xmmc;
+
+	@Schema(description = "人员编号")
+ 	private String number;
+
+	@Schema(description = "身份证号")
+ 	private String idCard;
+
+	@Schema(description = "姓名")
+ 	private String name;
+
+	@Schema(description = "所属二级单位部门/科室/项目部")
+ 	private String department;
+
+	@Schema(description = "岗位职务")
+ 	private String post;
+
+	@Schema(description = "岗位所在地")
+ 	private String postLocation;
+
+	@Schema(description = "用工性质")
+ 	private String nature;
+
+	@Schema(description = "参研情况")
+ 	private String situation;
+
+	@Schema(description = "人员类型")
+ 	private String personnelType;
+
+}

+ 1 - 1
kd-service/kd-scientific/src/main/java/org/sky/scientific/mapper/CgQtMapper.xml

@@ -41,7 +41,7 @@
     <select id="xmCgSummary" resultType="org.sky.scientific.pojo.vo.XmCGSummary">
         SELECT ZBMC as cgmc,ZBH as cgbh,SSRQ as date1,SSRQ as date2, '技术标准' as cglx FROM kd_cg_jsbz WHERE XM_ID = #{xmId}
         UNION
-        SELECT QKMC as cgmc,KJH as cgbh,FBRQ as date1,FBRQ as date2, '科研论文' as cglx FROM kd_cg_kylw  WHERE XM_ID = #{xmId}
+        SELECT LWMC as cgmc,KJH as cgbh,FBRQ as date1,FBRQ as date2, '科研论文' as cglx FROM kd_cg_kylw  WHERE XM_ID = #{xmId}
         UNION
         SELECT CGMC as cgmc,CGBH as cgbh,SHENQING_RQ as date1,SHOUQUAN_RQ as date2, '其他成果' as cglx FROM kd_cg_qt WHERE XM_ID = #{xmId}
         UNION

+ 12 - 9
kd-service/kd-scientific/src/main/java/org/sky/scientific/mapper/XmTechnicianMapper.xml

@@ -59,7 +59,7 @@
             resultMap="ResultMap"
             parameterType="org.sky.scientific.pojo.dto.XmTechnicianDto">
         SELECT
-        xt.id,
+        xt.id,xt.year_and_month,
         xm.*,
         t.name, t.unicode, t.number,t.id_card,t.department,t.nature,t.situation,t.personnel_type
         FROM kd_xm_technician xt
@@ -80,6 +80,7 @@
     </select>
 
     <resultMap id="ResultXmTechnicianSummary" type="org.sky.scientific.pojo.vo.XmTechnicianSummary">
+        <result column="xmbh" property="xmbh"/>
         <result column="XMMC" property="xmmc"/>
         <collection property="technicianList" ofType="org.sky.scientific.pojo.entity.XmTechnicianEntity">
             <result column="year_and_month" property="yearAndMonth"/>
@@ -101,8 +102,8 @@
             resultMap="ResultXmTechnicianSummary"
             parameterType="org.sky.scientific.pojo.entity.XmTechnicianEntity">
         SELECT
-        xm.XMMC,
-        t.unicode,t.name, t.number,t.id_card,t.department_id,t.department,t.post,t.post_location,t.nature,t.situation,t.personnel_type,t.year_and_month
+            xm.xmbh,xm.XMMC,
+            t.unicode,t.name, t.number,t.id_card,t.department_id,t.department,t.post,t.post_location,t.nature,t.situation,t.personnel_type,t.year_and_month
         FROM kd_xm_technician m
         LEFT JOIN kd_technician t on t.unicode = m.unicode and t.year_and_month = #{ew.yearAndMonth}
         LEFT JOIN (
@@ -121,6 +122,7 @@
         <if test="ew.situation!=null and ew.situation!=''">and t.situation = #{ew.situation}</if>
         <if test="ew.personnelType!=null and ew.personnelType!=''">and t.personnel_type = #{ew.personnelType}</if>
         GROUP BY xm.XMMC,m.id
+        order by xm.XMMC asc
     </select>
 
 
@@ -128,15 +130,15 @@
             resultMap="ResultXmTechnicianSummary"
             parameterType="org.sky.scientific.pojo.entity.XmTechnicianEntity">
         SELECT
-        xm.XMMC,
-        t.unicode,t.number,t.id_card, t.name,t.department_id,t.department,t.post,t.post_location,t.nature,t.situation,t.personnel_type
+            xm.xmbh,xm.XMMC,
+            t.unicode,t.number,t.id_card, t.name,t.department_id,t.department,t.post,t.post_location,t.nature,t.situation,t.personnel_type
         FROM kd_xm_technician m
         LEFT JOIN kd_technician t on t.unicode = m.unicode and left(t.year_and_month,4) = #{ew.yearAndMonth}
         LEFT JOIN (
-        SELECT temp.id as xm_id, XMBH,
-        COALESCE( (SELECT bgrz.BGHDZ FROM kd_xm_bgrz bgrz WHERE bgrz.XM_ID = temp.id AND bgrz.ZDMC = 'xmmc' AND
-        DATE_FORMAT(bgrz.BGSXSJ, '%Y%m') >= #{ew.yearAndMonth} ORDER BY bgrz.BGSXSJ DESC LIMIT 1), temp.XMMC ) AS XMMC
-        FROM kd_xm temp
+            SELECT temp.id as xm_id, XMBH,
+                COALESCE( (SELECT bgrz.BGHDZ FROM kd_xm_bgrz bgrz WHERE bgrz.XM_ID = temp.id AND bgrz.ZDMC = 'xmmc' AND
+                DATE_FORMAT(bgrz.BGSXSJ, '%Y%m') >= #{ew.yearAndMonth} ORDER BY bgrz.BGSXSJ DESC LIMIT 1), temp.XMMC ) AS XMMC
+            FROM kd_xm temp
         ) xm ON m.xm_id = xm.xm_id
         WHERE LEFT(m.year_and_month,4) = #{ew.yearAndMonth}
         <if test="ew.xmmc!=null and ew.xmmc!=''">and xm.XMMC like concat('%',#{ew.xmmc},'%')</if>
@@ -149,6 +151,7 @@
         <if test="ew.situation!=null and ew.situation!=''">and t.situation = #{ew.situation}</if>
         <if test="ew.personnelType!=null and ew.personnelType!=''">and t.personnel_type = #{ew.personnelType}</if>
         GROUP BY m.xm_id,m.unicode
+        order by xm.XMMC asc
     </select>
 
     <select id="canyanrenyuanhuamingce" resultMap="ResultVoMap">

+ 9 - 0
kd-service/kd-scientific/src/main/java/org/sky/scientific/service/IAttendanceService.java

@@ -2,6 +2,7 @@
 package org.sky.scientific.service;
 
 import com.baomidou.mybatisplus.core.metadata.IPage;
+import jakarta.servlet.http.HttpServletResponse;
 import org.sky.core.mp.base.BaseService;
 import org.sky.scientific.pojo.dto.AttendanceDto;
 import org.sky.scientific.pojo.entity.AttendanceEntity;
@@ -41,6 +42,13 @@ public interface IAttendanceService extends BaseService<AttendanceEntity> {
 	void importAttendance(MultipartFile file, String yearAndMonth);
 
 	/**
+	 * 研发人员考勤表-导出
+	 * @param response
+	 * @param dto
+	 */
+	void exportAttendance(HttpServletResponse response, AttendanceDto dto);
+
+	/**
 	 * 研发人员考勤表-批量新增或修改
 	 * @param attendance
 	 * @return
@@ -53,4 +61,5 @@ public interface IAttendanceService extends BaseService<AttendanceEntity> {
 	 * @return
 	 */
 	boolean clearAttendance(List<AttendanceDto> attendanceList);
+
 }

+ 8 - 0
kd-service/kd-scientific/src/main/java/org/sky/scientific/service/IXmService.java

@@ -4,6 +4,7 @@ package org.sky.scientific.service;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
+import jakarta.servlet.http.HttpServletResponse;
 import org.sky.core.mp.base.BaseService;
 import org.sky.scientific.excel.XmExcel;
 import org.sky.scientific.pojo.dto.XmDTO;
@@ -64,6 +65,13 @@ public interface IXmService extends BaseService<XmEntity> {
 	IPage<XmSummary> summaryPage(IPage<XmSummary> page, XmDTO xm);
 
 	/**
+	 * 项目汇总表-导出
+	 * @param response
+	 * @param dto
+	 */
+	void exportSummary(HttpServletResponse response, XmDTO dto);
+
+	/**
 	 * 项目预算总表
 	 * @param xmId
 	 * @return

+ 155 - 17
kd-service/kd-scientific/src/main/java/org/sky/scientific/service/impl/AttendanceServiceImpl.java

@@ -2,14 +2,22 @@
 package org.sky.scientific.service.impl;
 
 import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.write.metadata.style.WriteCellStyle;
+import com.alibaba.excel.write.metadata.style.WriteFont;
+import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import jakarta.servlet.http.HttpServletResponse;
 import lombok.AllArgsConstructor;
 import lombok.SneakyThrows;
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
 import org.sky.core.log.exception.ServiceException;
 import org.sky.core.mp.base.BaseServiceImpl;
+import org.sky.core.mp.support.Condition;
+import org.sky.core.mp.support.Query;
 import org.sky.core.secure.utils.SecureUtil;
+import org.sky.core.tool.utils.Charsets;
 import org.sky.core.tool.utils.CollectionUtil;
 import org.sky.core.tool.utils.Func;
 import org.sky.core.tool.utils.StringUtil;
@@ -29,9 +37,10 @@ import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.multipart.MultipartFile;
 
 import java.io.BufferedInputStream;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
+import java.net.URLEncoder;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
 
 /**
  * 考勤管理 服务实现类
@@ -46,17 +55,6 @@ public class AttendanceServiceImpl extends BaseServiceImpl<AttendanceMapper, Att
 	private final TechnicianMapper technicianMapper;
 	private final SettingMapper settingMapper;
 
-//	@PostConstruct
-//	public void init() {
-//		List<SettingEntity> settingList = settingMapper.selectList(null);
-//		if (settingList.isEmpty()) {
-//			SettingEntity setting = new SettingEntity();
-//			setting.setId(1L);
-//			setting.setAttendanceStartTime("08:00");
-//			setting.setAttendanceEndTime("17:30");
-//		}
-//	}
-
 	@Override
 	public IPage<AttendanceVO> selectAttendancePage(AttendanceDto attendance, IPage<AttendanceVO> page) {
 		if (StringUtil.isBlank(attendance.getYearAndMonth())) {
@@ -65,13 +63,13 @@ public class AttendanceServiceImpl extends BaseServiceImpl<AttendanceMapper, Att
 		List<AttendanceVO> list = baseMapper.selectAttendancePage(page, attendance);
 		for (AttendanceVO vo : list) {
 			List<DailyAttendance> dailyAttendanceList = baseMapper.selectDailyAttendanceByUnicodeAndMonth(vo.getUnicode(), attendance.getYearAndMonth());
-			if(CollectionUtil.isEmpty(dailyAttendanceList)){
+			if (CollectionUtil.isEmpty(dailyAttendanceList)) {
 				continue;
 			}
 			vo.setMonthlyHours(dailyAttendanceList.stream().mapToDouble(DailyAttendance::getWorkHours).sum());
 
 			SettingEntity one = settingMapper.selectOne(Wrappers.<SettingEntity>lambdaQuery().eq(SettingEntity::getYearAndMonth, attendance.getYearAndMonth().substring(0, 4)));
-			if(Func.isNull(one) || Func.isNull(one.getWorkHours())){
+			if (Func.isNull(one) || Func.isNull(one.getWorkHours())) {
 				throw new ServiceException("企业未设置考勤时长");
 			}
 
@@ -173,6 +171,146 @@ public class AttendanceServiceImpl extends BaseServiceImpl<AttendanceMapper, Att
 	}
 
 	@Override
+	public void exportAttendance(HttpServletResponse response, AttendanceDto dto) {
+
+		try {
+			response.setContentType("application/vnd.ms-excel");
+			response.setCharacterEncoding("utf-8");
+			response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("每月考勤信息.xls", Charsets.UTF_8));
+
+			Query query = new Query().setCurrent(1).setSize(Integer.MAX_VALUE);
+
+			List<AttendanceVO> list = this.selectAttendancePage(dto, Condition.getPage(query)).getRecords();
+
+			Integer year = Integer.valueOf(dto.getYearAndMonth().substring(0, 4));
+			Integer month = Integer.valueOf(dto.getYearAndMonth().substring(5));
+			// 1. 构建完整数据(包含标题、表头、数据)
+			List<List<Object>> allData = buildCompleteData(list, year, month);
+
+			// 2. 构建表头
+			List<List<String>> head = Collections.emptyList();//buildDynamicHead(year, month);
+
+			// 3. 设置样式
+			WriteCellStyle headStyle = new WriteCellStyle();
+			headStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
+			WriteFont headFont = new WriteFont();
+			headFont.setBold(true);
+			headFont.setFontHeightInPoints((short) 12);
+			headStyle.setWriteFont(headFont);
+
+			WriteCellStyle contentStyle = new WriteCellStyle();
+			contentStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
+
+			HorizontalCellStyleStrategy styleStrategy = new HorizontalCellStyleStrategy(headStyle, contentStyle);
+
+			// 4. 导出Excel
+			EasyExcel.write(response.getOutputStream())
+				.head(head)
+				.registerWriteHandler(styleStrategy)
+				.autoCloseStream(true)
+				.sheet("考勤信息")
+				.doWrite(allData);
+		} catch (Exception e) {
+			throw new ServiceException("导出失败:" + e.getLocalizedMessage());
+		}
+	}
+
+	/**
+	 * 构建完整数据(包含标题行、表头行、数据行)
+	 */
+	private List<List<Object>> buildCompleteData(List<AttendanceVO> attendanceList, Integer year, Integer month) {
+		List<List<Object>> allData = new ArrayList<>();
+
+		LocalDate startDate = LocalDate.of(year, month, 1);
+		int daysInMonth = startDate.lengthOfMonth();
+		int totalColumns = 5 + daysInMonth + 2; // 5个固定列 + 天数列 + 2个统计列
+
+		// 1. 标题行(第一行)
+		List<Object> titleRow = new ArrayList<>();
+		titleRow.add("每月考勤信息" + year + "年" + month + "月");
+		// 填充剩余单元格为空,用于合并
+		for (int i = 1; i < totalColumns; i++) {
+			titleRow.add("");
+		}
+		allData.add(titleRow);
+
+		// 2. 空行(第二行)
+		allData.add(Collections.nCopies(totalColumns, ""));
+
+		// 3. 表头行(第三行)
+		List<Object> headerRow = new ArrayList<>();
+		headerRow.add("序号");
+		headerRow.add("姓名");
+		headerRow.add("工号");
+		headerRow.add("身份证号码");
+		headerRow.add("科研人员类别");
+
+		// 日期表头
+		DateTimeFormatter dayFormatter = DateTimeFormatter.ofPattern("dd");
+		for (int day = 1; day <= daysInMonth; day++) {
+			headerRow.add(String.valueOf(day));
+		}
+
+		headerRow.add("本月出勤/小时");
+		headerRow.add("本月出勤/天");
+		allData.add(headerRow);
+
+		// 4. 周几行(第四行)
+		List<Object> weekRow = new ArrayList<>();
+		weekRow.add(""); // 序号列
+		weekRow.add(""); // 姓名列
+		weekRow.add(""); // 工号列
+		weekRow.add(""); // 身份证列
+		weekRow.add(""); // 人员类型列
+
+		// 周几数据
+		DateTimeFormatter weekFormatter = DateTimeFormatter.ofPattern("E", Locale.CHINA);
+		for (int day = 1; day <= daysInMonth; day++) {
+			LocalDate date = LocalDate.of(year, month, day);
+			weekRow.add(date.format(weekFormatter));
+		}
+
+		weekRow.add(""); // 小时统计列
+		weekRow.add(""); // 天数统计列
+		allData.add(weekRow);
+
+		// 5. 数据行(从第五行开始)
+		DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyyMMdd");
+		for (AttendanceVO attendance : attendanceList) {
+			List<Object> dataRow = new ArrayList<>();
+
+			// 基础信息
+			dataRow.add(attendance.getXh());
+			dataRow.add(attendance.getName());
+			dataRow.add(attendance.getNumber());
+			dataRow.add(attendance.getIdCard());
+			dataRow.add(attendance.getPersonnelType());
+
+			// 每日考勤数据
+			Map<String, Double> dailyMap = new HashMap<>();
+			if (attendance.getDailyAttendances() != null) {
+				for (DailyAttendance daily : attendance.getDailyAttendances()) {
+					dailyMap.put(daily.getWorkDate(), daily.getWorkHours());
+				}
+			}
+
+			for (int day = 1; day <= daysInMonth; day++) {
+				LocalDate currentDate = LocalDate.of(year, month, day);
+				String dateKey = currentDate.format(dateFormatter);
+				dataRow.add(dailyMap.getOrDefault(dateKey, 0.0));
+			}
+
+			// 统计信息
+			dataRow.add(attendance.getMonthlyHours());
+			dataRow.add(attendance.getMonthlyDays());
+
+			allData.add(dataRow);
+		}
+
+		return allData;
+	}
+
+	@Override
 	public boolean batchSubmit(AttendanceDto attendance) {
 		List<AttendanceEntity> dbList = baseMapper.selectByYearAndMonth(attendance.getYearAndMonth());
 		List<AttendanceEntity> dataByTechnicianMonthly = dbList.stream().filter(e -> e.getUnicode().equals(attendance.getUnicode())).toList();
@@ -216,7 +354,7 @@ public class AttendanceServiceImpl extends BaseServiceImpl<AttendanceMapper, Att
 
 	@Override
 	public boolean clearAttendance(List<AttendanceDto> attendanceList) {
-		if(CollectionUtil.isEmpty(attendanceList)){
+		if (CollectionUtil.isEmpty(attendanceList)) {
 			throw new ServiceException("参数不能为空");
 		}
 		int affectNum = 0;

+ 1 - 2
kd-service/kd-scientific/src/main/java/org/sky/scientific/service/impl/SalaryServiceImpl.java

@@ -94,7 +94,6 @@ public class SalaryServiceImpl extends BaseServiceImpl<SalaryMapper, SalaryEntit
 		if (templateFileName != null) {
 			templateFileName.close();
 		}
-
 	}
 
 	@Override
@@ -153,7 +152,7 @@ public class SalaryServiceImpl extends BaseServiceImpl<SalaryMapper, SalaryEntit
 
 	@Override
 	public boolean clearSalary(List<SalaryEntity> salaryList) {
-		Integer affectNum = 0;
+		int affectNum = 0;
 		//根据unicode、yearAndMonth工资数据
 		for (SalaryEntity salary : salaryList) {
 			affectNum += baseMapper.clearSalary(salary);

+ 121 - 28
kd-service/kd-scientific/src/main/java/org/sky/scientific/service/impl/TechnicianServiceImpl.java

@@ -1,6 +1,7 @@
 
 package org.sky.scientific.service.impl;
 
+import cn.hutool.core.bean.BeanUtil;
 import com.alibaba.excel.EasyExcel;
 import com.alibaba.excel.ExcelWriter;
 import com.alibaba.excel.support.ExcelTypeEnum;
@@ -13,6 +14,7 @@ import jakarta.servlet.http.HttpServletResponse;
 import lombok.AllArgsConstructor;
 import lombok.SneakyThrows;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.poi.ss.util.CellRangeAddress;
 import org.sky.core.log.exception.ServiceException;
 import org.sky.core.mp.base.BaseServiceImpl;
 import org.sky.core.mp.support.Condition;
@@ -20,6 +22,8 @@ import org.sky.core.mp.support.Query;
 import org.sky.core.secure.utils.SecureUtil;
 import org.sky.core.tool.utils.*;
 import org.sky.scientific.excel.TechnicianExcel;
+import org.sky.scientific.excel.handle.MergeCellRangeWriteHandler;
+import org.sky.scientific.excel.handle.XmcyryhzmExcel;
 import org.sky.scientific.mapper.*;
 import org.sky.scientific.pojo.dto.TechnicianDTO;
 import org.sky.scientific.pojo.dto.XmSalaryDto;
@@ -36,9 +40,11 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.io.InputStream;
+import java.math.BigDecimal;
 import java.net.URLEncoder;
 import java.util.*;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 
 /**
@@ -73,10 +79,10 @@ public class TechnicianServiceImpl extends BaseServiceImpl<TechnicianMapper, Tec
 		}
 		technician.setUnicode(unicode);
 
-		if(Func.isNull(technician.getId())){
+		if (Func.isNull(technician.getId())) {
 			//新增
 			return this.save(technician);
-		}else {
+		} else {
 			return this.updateById(technician);
 		}
 //		LambdaQueryWrapper<TechnicianEntity> wrapper = Wrappers.<TechnicianEntity>lambdaQuery()
@@ -147,8 +153,8 @@ public class TechnicianServiceImpl extends BaseServiceImpl<TechnicianMapper, Tec
 		for (TechnicianExcel excel : data) {
 			TechnicianEntity technician = Objects.requireNonNull(BeanUtil.copyProperties(excel, TechnicianDTO.class));
 
-			technician.setNumber(Func.isBlank(technician.getNumber())? null:technician.getNumber().trim());
-			technician.setIdCard(Func.isBlank(technician.getIdCard())?null:technician.getIdCard().trim());
+			technician.setNumber(Func.isBlank(technician.getNumber()) ? null : technician.getNumber().trim());
+			technician.setIdCard(Func.isBlank(technician.getIdCard()) ? null : technician.getIdCard().trim());
 			//转换 性别字典
 			technician.setGender(Integer.valueOf(DictCache.getKey(DictEnum.SEX, excel.getGenderName())));
 			technician.setYearAndMonth(yearAndMonth);
@@ -170,19 +176,19 @@ public class TechnicianServiceImpl extends BaseServiceImpl<TechnicianMapper, Tec
 				}
 				technician.setDepartmentId(dbDepartment.getId());
 			}
- 			String unicode = baseMapper.getUnicodeByNumberAndIdentityCard(technician.getNumber(), technician.getIdCard());
+			String unicode = baseMapper.getUnicodeByNumberAndIdentityCard(technician.getNumber(), technician.getIdCard());
 			if (unicode == null) {
 				unicode = UUID.randomUUID().toString();
 			}
 			technician.setUnicode(unicode);
 
 			insertList.add(technician);
-			if(insertList.size() %20==0){
+			if (insertList.size() % 20 == 0) {
 				this.saveBatch(insertList);
 				insertList.clear();
 			}
 		}
-		if(Func.isNotEmpty(insertList)){
+		if (Func.isNotEmpty(insertList)) {
 			this.saveBatch(insertList);
 		}
 	}
@@ -294,20 +300,20 @@ public class TechnicianServiceImpl extends BaseServiceImpl<TechnicianMapper, Tec
 	}
 
 	@Override
-	public IPage<XmTechnicianEntity> meiyueshijicanyuanrenyuanmingdan( IPage<XmTechnicianEntity> page,XmTechnicianDto entity) {
+	public IPage<XmTechnicianEntity> meiyueshijicanyuanrenyuanmingdan(IPage<XmTechnicianEntity> page, XmTechnicianDto entity) {
 		return xmTechnicianMapper.meiyueshijicanyuanrenyuanmingdan(page, entity);
 	}
 
 	@SneakyThrows
 	@Override
-	public void exportXmTechnicianMonthly(HttpServletResponse response,XmTechnicianDto dto) {
+	public void exportXmTechnicianMonthly(HttpServletResponse response, XmTechnicianDto dto) {
 		Query query = new Query().setCurrent(1).setSize(Integer.MAX_VALUE);
 		List<XmTechnicianEntity> list = this.meiyueshijicanyuanrenyuanmingdan(Condition.getPage(query), dto).getRecords();
 
 		AtomicInteger i = new AtomicInteger(1);
 		list.forEach(temp -> {
-			temp.setXh(i.getAndIncrement()+"");
- 		});
+			temp.setXh(i.getAndIncrement() + "");
+		});
 
 		InputStream templateFileName = this.getClass().getClassLoader().getResourceAsStream("export-template/每月实际参研人员名单.xls");
 		response.setContentType("application/vnd.ms-excel");
@@ -321,8 +327,8 @@ public class TechnicianServiceImpl extends BaseServiceImpl<TechnicianMapper, Tec
 		WriteSheet writeSheet = EasyExcel.writerSheet().build();
 		//设置list集合数据
 		excelWriter.fill(list, writeSheet);
-		Map<String,Object> map = new HashMap<>();
-		map.put("year", dto.getYearAndMonth().substring(0,4));
+		Map<String, Object> map = new HashMap<>();
+		map.put("year", dto.getYearAndMonth().substring(0, 4));
 		map.put("month", dto.getYearAndMonth().substring(4));
 		excelWriter.fill(map, writeSheet);
 		excelWriter.finish();
@@ -406,11 +412,22 @@ public class TechnicianServiceImpl extends BaseServiceImpl<TechnicianMapper, Tec
 			// 参研项目列表
 			vo.setXmList(xmTechnicianMapper.selectXmListByUnicodeAndYear(vo.getUnicode(), entity.getYearAndMonth()));
 			// 参研时长列表
-			vo.setHoursList(xmTechnicianHoursMapper.selectHoursListByUnicodeAndYear(vo.getUnicode(), entity.getYearAndMonth()));
+			List<DailyAttendance> attendances = xmTechnicianHoursMapper.selectHoursListByUnicodeAndYear(vo.getUnicode(), entity.getYearAndMonth());
+
+
+			vo.setHoursList(attendances);
 		}
 		return page.setRecords(list);
 	}
 
+//	public static void main(String[] args) {
+//		List<Integer> list = new ArrayList<>();
+//		list.add(1);
+//		list.add(2);
+//		list.add(0, 5);
+//		System.out.println(list);
+//	}
+
 	@Override
 	public IPage<TechnicianVO> niandugaokejirenyuanhuizongbiao(IPage<TechnicianVO> page, TechnicianDTO dto) {
 		//TODO 目前因测试数据不多,需求是 年度天数大于183的才显示,这里先用18代替,后期要改回来
@@ -462,11 +479,25 @@ public class TechnicianServiceImpl extends BaseServiceImpl<TechnicianMapper, Tec
 		Query query = new Query().setCurrent(1).setSize(Integer.MAX_VALUE);
 		List<XmTechnicianVO> list = this.canyanrenyuanhuamingce(Condition.getPage(query), dto).getRecords();
 
+		List<Map<String, Object>> resultList = new ArrayList<>(list.size());
+
 		AtomicInteger i = new AtomicInteger(1);
 		list.forEach(temp -> {
-			temp.setXh(i.getAndIncrement()+"");
+			temp.setXh(i.getAndIncrement() + "");
 			temp.setXmmcJoin(temp.getXmList().stream().map(XmEntity::getXmmc).collect(Collectors.joining(";")));
 			temp.setXmCount(temp.getXmList().size());
+
+			List<BigDecimal> hours1 = new ArrayList<>(temp.getHoursList().stream().map(x -> BigDecimal.valueOf(x.getWorkHours())).toList());
+			Map<String, Object> map =  BeanUtil.beanToMap(temp);
+			for (int k = 0; k<12; k++) {
+				String month = k < 10 ? "0" + (k+1) : (k+1) + "";
+				if (temp.getHoursList().stream().noneMatch(a -> Func.equals(a.getWorkDate(), dto.getYearAndMonth() + month))) {
+ 					hours1.add(k, new BigDecimal(0));
+				}
+				map.put("month" + (k+1), hours1.get(k));
+			}
+			map.put("hoursByMonth",  hours1.stream().reduce(BigDecimal.ZERO, BigDecimal::add));
+			resultList.add(map);
 		});
 
 		InputStream templateFileName = this.getClass().getClassLoader().getResourceAsStream("export-template/参研人员花名册.xls");
@@ -480,9 +511,9 @@ public class TechnicianServiceImpl extends BaseServiceImpl<TechnicianMapper, Tec
 
 		WriteSheet writeSheet = EasyExcel.writerSheet().build();
 		//设置list集合数据
-		excelWriter.fill(list, writeSheet);
-		Map<String,Object> map = new HashMap<>();
-		map.put("year", dto.getYearAndMonth().substring(0,4));
+		excelWriter.fill(resultList, writeSheet);
+		Map<String, Object> map = new HashMap<>();
+		map.put("year", dto.getYearAndMonth().substring(0, 4));
 		map.put("month", dto.getYearAndMonth().substring(4));
 		excelWriter.fill(map, writeSheet);
 		excelWriter.finish();
@@ -494,28 +525,52 @@ public class TechnicianServiceImpl extends BaseServiceImpl<TechnicianMapper, Tec
 	@SneakyThrows
 	@Override
 	public void exportXmcyryhzb(HttpServletResponse response, XmTechnicianEntity dto) {
+
+		InputStream templateFileName = this.getClass().getClassLoader().getResourceAsStream("export-template/项目参研人员汇总表.xls");
+		response.setContentType("application/vnd.ms-excel");
+		response.setCharacterEncoding("utf-8");
+		response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("项目参研人员汇总表.xls", Charsets.UTF_8));
+
 		Query query = new Query().setCurrent(1).setSize(Integer.MAX_VALUE);
 		List<XmTechnicianSummary> list = this.xiangmucanyanrenyuanhuizongbiao(Condition.getPage(query), dto).getRecords();
 
+		List<XmcyryhzmExcel> dataList = new ArrayList<>();
 		AtomicInteger i = new AtomicInteger(1);
+
 		list.forEach(temp -> {
-			temp.setXh(i.getAndIncrement()+"");
+
+			List<XmTechnicianEntity> technicianList = temp.getTechnicianList();
+			technicianList.forEach(technician -> {
+				XmcyryhzmExcel data = new XmcyryhzmExcel();
+				data.setXh(i.getAndIncrement()+"");
+				data.setXmbh(temp.getXmbh());
+				data.setXmmc(temp.getXmmc());
+				data.setName(technician.getName());
+				data.setNumber(technician.getNumber());
+				data.setIdCard(technician.getIdCard());
+				data.setDepartment(technician.getDepartment());
+				data.setPost(technician.getPost());
+				data.setPostLocation(technician.getPostLocation());
+				data.setNature(technician.getNature());
+				data.setSituation(technician.getSituation());
+				data.setPersonnelType(technician.getPersonnelType());
+				dataList.add(data);
+ 			});
 		});
 
-		InputStream templateFileName = this.getClass().getClassLoader().getResourceAsStream("export-template/项目参研人员汇总表.xls");
-		response.setContentType("application/vnd.ms-excel");
-		response.setCharacterEncoding("utf-8");
-		response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("项目参研人员汇总表.xls", Charsets.UTF_8));
+		List<CellRangeAddress> rangeCellList = createCellRange(dataList, new int[]{1,2}, 2, XmcyryhzmExcel::getXmbh);
+
 		ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream())
 			.withTemplate((templateFileName))
+			.registerWriteHandler(new MergeCellRangeWriteHandler(rangeCellList))
 			.excelType(ExcelTypeEnum.XLS)
 			.build();
 
 		WriteSheet writeSheet = EasyExcel.writerSheet().build();
 		//设置list集合数据
-		excelWriter.fill(list, writeSheet);
-		Map<String,Object> map = new HashMap<>();
-		map.put("year", dto.getYearAndMonth().substring(0,4));
+		excelWriter.fill(dataList, writeSheet);
+		Map<String, Object> map = new HashMap<>();
+		map.put("year", dto.getYearAndMonth().substring(0, 4));
 		map.put("month", dto.getYearAndMonth().substring(4));
 		excelWriter.fill(map, writeSheet);
 		excelWriter.finish();
@@ -524,6 +579,44 @@ public class TechnicianServiceImpl extends BaseServiceImpl<TechnicianMapper, Tec
 		}
 	}
 
+	/**
+	 * 生成合并区域
+	 *
+	 * @param detailList       数据列表
+	 * @param mergeColumnIndex 要合并的列索引
+	 * @param startRowIndex    起始行(含表头时,表头行数)
+	 * @param groupFunction    分组函数,如 e -> e.get某字段()
+	 * @return 合并区域集合
+	 */
+	public static <T> List<CellRangeAddress> createCellRange(List<T> detailList, int[] mergeColumnIndex, int startRowIndex, Function<T, String> groupFunction) {
+		if (detailList == null || detailList.isEmpty()) {
+			return Collections.emptyList();
+		}
+		// 计算每个key下的数量
+		Map<String, Long> groupMap = new LinkedHashMap<>();
+		for (T item : detailList) {
+			String key = groupFunction.apply(item);
+			groupMap.put(key, groupMap.getOrDefault(key, 0L) + 1);
+		}
+
+		List<CellRangeAddress> rangeCellList = new ArrayList<>();
+		// 当前行数
+		int lineCount = startRowIndex;
+
+		for (Map.Entry<String, Long> entry : groupMap.entrySet()) {
+			int count = entry.getValue().intValue();
+			if (count > 1) {
+				int endRowIndex = lineCount + count - 1;
+				for (int columnIndex : mergeColumnIndex) {
+					rangeCellList.add(new CellRangeAddress(lineCount, endRowIndex, columnIndex, columnIndex));
+				}
+			}
+			lineCount += count;
+		}
+
+		return rangeCellList;
+	}
+
 	@SneakyThrows
 	@Override
 	public void exportNdgkjryhzb(HttpServletResponse response, TechnicianDTO dto) {
@@ -547,8 +640,8 @@ public class TechnicianServiceImpl extends BaseServiceImpl<TechnicianMapper, Tec
 		WriteSheet writeSheet = EasyExcel.writerSheet().build();
 		//设置list集合数据
 		excelWriter.fill(list, writeSheet);
-		Map<String,Object> map = new HashMap<>();
-		map.put("year", dto.getYearAndMonth().substring(0,4));
+		Map<String, Object> map = new HashMap<>();
+		map.put("year", dto.getYearAndMonth().substring(0, 4));
 		excelWriter.fill(map, writeSheet);
 		excelWriter.finish();
 		if (templateFileName != null) {

+ 69 - 7
kd-service/kd-scientific/src/main/java/org/sky/scientific/service/impl/XmServiceImpl.java

@@ -1,17 +1,26 @@
 
 package org.sky.scientific.service.impl;
 
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.ExcelWriter;
+import com.alibaba.excel.support.ExcelTypeEnum;
+import com.alibaba.excel.write.metadata.WriteSheet;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import jakarta.servlet.http.HttpServletResponse;
 import lombok.AllArgsConstructor;
+import lombok.SneakyThrows;
 import org.sky.core.log.exception.ServiceException;
 import org.sky.core.mp.base.BaseServiceImpl;
+import org.sky.core.mp.support.Condition;
+import org.sky.core.mp.support.Query;
 import org.sky.core.secure.utils.SecureUtil;
 import org.sky.core.tool.utils.*;
+import org.sky.scientific.e.XmStatusEnum;
 import org.sky.scientific.excel.XmExcel;
 import org.sky.scientific.mapper.DepartmentMapper;
 import org.sky.scientific.mapper.TechnicianMapper;
@@ -25,9 +34,11 @@ import org.sky.scientific.pojo.vo.XmYszbVO;
 import org.sky.scientific.service.IXmService;
 import org.springframework.stereotype.Service;
 
-import java.util.Arrays;
-import java.util.Date;
-import java.util.List;
+import java.io.InputStream;
+import java.math.BigDecimal;
+import java.net.URLEncoder;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * 科研项目 服务实现类
@@ -117,15 +128,13 @@ public class XmServiceImpl extends BaseServiceImpl<XmMapper, XmEntity> implement
 
 		int year = Integer.parseInt(xm.getYearAndMonth().substring(0, 4));
 		for (XmSummary xmVO : xmList) {
-//			if (xmVO.getXmfzr() != null) {
-//				xmVO.setXmfzrxm(technicianMapper.getNameById(xmVO.getXmfzr()));
-//			}
+
 			//立项报告数量
 			xmVO.setLxbgCount(JSON.parseArray(xmVO.getLxbg()).size());
 
 			List<JSONObject> xmysmx = JSON.parseArray(xmVO.getXmyszemx(), JSONObject.class);
 			//预算总额
-			xmVO.setXmysze(xmysmx.stream().mapToDouble(item -> JSONObject.parseObject(item.toString()).getDoubleValue("amount")).sum());
+			xmVO.setXmysze(BigDecimal.valueOf(xmysmx.stream().mapToDouble(item -> JSONObject.parseObject(item.toString()).getDoubleValue("amount")).sum()));
 
 			//本年度预算
 			for (JSONObject o : xmysmx) {
@@ -140,6 +149,59 @@ public class XmServiceImpl extends BaseServiceImpl<XmMapper, XmEntity> implement
 		return page.setRecords(xmList);
 	}
 
+	@SneakyThrows
+	@Override
+	public void exportSummary(HttpServletResponse response, XmDTO dto) {
+//		try {
+			InputStream templateFileName = this.getClass().getClassLoader().getResourceAsStream("export-template/研发项目汇总表.xls");
+			response.setContentType("application/vnd.ms-excel");
+			response.setCharacterEncoding("utf-8");
+			response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("研发项目汇总表.xls", Charsets.UTF_8));
+
+			Query query = new Query().setCurrent(1).setSize(Integer.MAX_VALUE);
+
+			List<XmSummary> list = this.summaryPage(Condition.getPage(query), dto).getRecords();
+			AtomicInteger index = new AtomicInteger();
+			for (XmSummary summary : list) {
+				summary.setXh(index.incrementAndGet());
+				Optional<XmStatusEnum> first = Arrays.stream(XmStatusEnum.values()).filter(status -> status.getCode() == summary.getStatus()).findFirst();
+				summary.setStatusName(first.isPresent()?first.get().getName():"");
+
+				summary.setXmkssjStr(DateUtil.format(summary.getXmkssj(), DateUtil.PATTERN_DATE));
+				summary.setXmjssjStr(DateUtil.format(summary.getXmjssj(), DateUtil.PATTERN_DATE));
+
+				summary.setLxbgStatus((summary.getLxbgCount()>0)?"已提交":"未提交");
+				summary.setJdxbgStatus((summary.getJdxbgCount()>0)?"已提交":"未提交");
+				summary.setYsbgStatus((summary.getYsbgCount()>0)?"已提交":"未提交");
+			}
+
+			ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream())
+				.withTemplate((templateFileName))
+				.excelType(ExcelTypeEnum.XLS)
+				.build();
+
+			WriteSheet writeSheet = EasyExcel.writerSheet().build();
+			//设置list集合数据
+			excelWriter.fill(list, writeSheet);
+			Map<String, Object> map = new HashMap<>();
+			map.put("year", dto.getYearAndMonth().substring(0, 4));
+			map.put("month", dto.getYearAndMonth().substring(4));
+			excelWriter.fill(map, writeSheet);
+			excelWriter.finish();
+			if (templateFileName != null) {
+				templateFileName.close();
+			}
+//		}catch (Exception e) {
+//			throw new ServiceException("导出失败," + e.getLocalizedMessage());
+//			try {
+//				response.getWriter().write("{\"code\":500,\"msg\":\"操作失败\"}");
+//			} catch (IOException ex) {
+//				throw new RuntimeException(ex);
+//			}
+//		}
+
+	}
+
 	@Override
 	public XmYszbVO selectYszb(Long xmId) {
 		if (xmId == null || xmId <= 0) {

BIN
kd-service/kd-scientific/src/main/resources/export-template/参研人员花名册.xls


BIN
kd-service/kd-scientific/src/main/resources/export-template/年度高新科技人员汇总表.xls


BIN
kd-service/kd-scientific/src/main/resources/export-template/每月研发工时记录表.xls


BIN
kd-service/kd-scientific/src/main/resources/export-template/每月考勤信息.xls


BIN
kd-service/kd-scientific/src/main/resources/export-template/研发项目汇总表.xls


BIN
kd-service/kd-scientific/src/main/resources/export-template/研发项目汇总表1.xls


BIN
kd-service/kd-scientific/src/main/resources/export-template/项目参研人员汇总表.xls


BIN
kd-service/kd-scientific/src/main/resources/export-template/项目变更.doc


BIN
kd-service/kd-scientific/src/main/resources/export-template/项目变更清单.xls


BIN
kd-service/kd-scientific/src/main/resources/export-template/项目实施-阶段性报告.doc


+ 1 - 2
kd-service/kd-system/src/main/java/org/sky/system/service/impl/TenantServiceImpl.java

@@ -121,8 +121,7 @@ public class TenantServiceImpl extends BaseServiceImpl<TenantMapper, Tenant> imp
 		}
 		CacheUtil.clear(SYS_CACHE, tenant.getTenantId());
 		CacheUtil.evict(SYS_CACHE, TENANT_TENANT_ID, tenant.getTenantId(), Boolean.FALSE);
-//		CacheUtil.clear("kd::vip:years:" + tenant.getTenantId(),Boolean.FALSE);
-		redis.del("kd::vip:years:" + tenant.getTenantId());
+ 		redis.del("kd::vip:years:" + tenant.getTenantId());
 		return super.saveOrUpdate(tenant);
 	}