zhoulisky 10 months ago
parent
commit
d400774e46

+ 27 - 0
kd-service-api/kd-scientific-api/src/main/java/org/sky/scientific/pojo/dto/XmTechnicianMonthlyDto.java

@@ -0,0 +1,27 @@
+
+package org.sky.scientific.pojo.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotEmpty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.sky.scientific.pojo.entity.XmEntity;
+import org.sky.scientific.pojo.entity.XmTechnicianMonthlyEntity;
+
+import java.util.List;
+
+/**
+ * 每月实际参研人员名单
+ *
+ * @author Kd
+ * @since 2025-06-29
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class XmTechnicianMonthlyDto extends XmTechnicianMonthlyEntity {
+
+	@Schema(description = "研发项目,可多选, 请填写id,xmmc两个字段")
+	@NotEmpty(message = "至少选择一个研发项目")
+	private List<XmEntity> xmList;
+
+}

+ 2 - 2
kd-service-api/kd-scientific-api/src/main/java/org/sky/scientific/pojo/entity/AttendanceEntity.java

@@ -31,7 +31,7 @@ public class AttendanceEntity extends TenantEntity {
 
 	@Schema(description = "研发人员id")
 	@NotBlank(message = "请选择研发人员")
-	private String technicianId;
+	private Long technicianId;
 
 	@Schema(description = "姓名")
 	@TableField(exist = false)
@@ -57,6 +57,6 @@ public class AttendanceEntity extends TenantEntity {
 	@NotNull(message = "请填写出勤时长(小时)")
 	@Min(value = 0, message = "请填写正确的出勤时长(小时)")
 	@Max(value = 24, message = "请填写正确的出勤时长(小时)")
-	private float workDuration;
+	private Double workDuration;
 
 }

+ 11 - 2
kd-service/kd-scientific/src/main/java/org/sky/scientific/controller/AttendanceController.java

@@ -14,6 +14,7 @@ import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
 
 @RestController
 @AllArgsConstructor
@@ -22,13 +23,21 @@ import org.springframework.web.bind.annotation.RestController;
 public class AttendanceController extends KdController {
 
 	@Resource
-	private IAttendanceService attendanceService;
+	private IAttendanceService service;
 
 	@PostMapping("/submit")
 	@ApiOperationSupport(order = 1)
 	@Operation(summary = "研发人员考勤表-新增或修改")
 	public R submit(@Valid @RequestBody AttendanceEntity attendance) {
-		return R.status(attendanceService.submit(attendance));
+		return R.status(service.submit(attendance));
+	}
+
+	@PostMapping("/import")
+	@ApiOperationSupport(order = 6)
+	@Operation(summary = "研发人员考勤表-导入", description = "传入excel")
+	public R importAttendance(MultipartFile file,String yearAndMonth) {
+		service.importAttendance(file, yearAndMonth);
+		return R.success("操作成功");
 	}
 
 }

+ 5 - 4
kd-service/kd-scientific/src/main/java/org/sky/scientific/controller/TechnicianController.java

@@ -19,6 +19,7 @@ import org.sky.core.tool.utils.Func;
 import org.sky.scientific.excel.TechnicianExcel;
 import org.sky.scientific.excel.TechnicianImporter;
 import org.sky.scientific.pojo.dto.TechnicianDTO;
+import org.sky.scientific.pojo.dto.XmTechnicianMonthlyDto;
 import org.sky.scientific.pojo.entity.TechnicianEntity;
 import org.sky.scientific.pojo.entity.XmTechnicianMonthlyEntity;
 import org.sky.scientific.pojo.vo.TechnicianVO;
@@ -108,15 +109,15 @@ public class TechnicianController extends KdController {
 	@GetMapping("xm/monthly/page")
 	@ApiOperationSupport(order = 20)
 	@Operation(summary = "每月实际参研人员名单-分页")
-	public R<IPage<XmTechnicianMonthlyEntity>> meiyueshijicanyuanrenyuanmingdan(XmTechnicianMonthlyEntity entity, Query query){
+	public R<IPage<XmTechnicianMonthlyEntity>> meiyueshijicanyuanrenyuanmingdan(XmTechnicianMonthlyDto entity, Query query){
 		return R.data(technicianService.meiyueshijicanyuanrenyuanmingdan(entity, Condition.getPage(query)));
 	}
 
-	@GetMapping("xm/monthly/yyysbmd")
+	@PostMapping("xm/monthly/yyysbmd")
 	@ApiOperationSupport(order = 21)
 	@Operation(summary = "每月实际参研人员名单-直接引用预算表名单", description = "传入xmId、yearAndMonth")
-	public R<Boolean> zhijieyinyongyusuanbiaomingdan(XmTechnicianMonthlyEntity entity){
-		technicianService.zhijieyinyongyusuanbiaomingdan(entity);
+	public R<Boolean> zhijieyinyongyusuanbiaomingdan(@Valid @RequestBody XmTechnicianMonthlyDto dto){
+		technicianService.zhijieyinyongyusuanbiaomingdan(dto);
 		return R.success();
 	}
 

+ 137 - 0
kd-service/kd-scientific/src/main/java/org/sky/scientific/excel/AttendanceMonthlyDataListener.java

@@ -0,0 +1,137 @@
+package org.sky.scientific.excel;
+
+import com.alibaba.excel.context.AnalysisContext;
+import com.alibaba.excel.metadata.data.ReadCellData;
+import com.alibaba.excel.read.listener.ReadListener;
+import lombok.Data;
+import org.sky.core.tool.utils.StringUtil;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+@Data
+public class AttendanceMonthlyDataListener implements ReadListener<Map<Integer, String>> {
+
+	private static final int HEADER_ROW_1 = 0; // 星期行(0-based)
+	private static final int HEADER_ROW_2 = 1; // 日期行
+	private static final int DATA_START_ROW = 2; // 数据开始行
+
+	private List<String> dayOfWeekHeaders = new ArrayList<>();
+	private List<String> dateHeaders = new ArrayList<>();
+	private List<AttendanceMonthlyExcel> records = new ArrayList<>();
+	private boolean headersProcessed = false;
+	private String yearAndMonth;
+
+	@Override
+	public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
+		int rowIndex = context.readRowHolder().getRowIndex();
+
+		if (rowIndex == HEADER_ROW_1) {
+			// 处理星期行
+			dayOfWeekHeaders = extractHeaders(headMap, 5);
+		} else if (rowIndex == HEADER_ROW_2) {
+			// 处理日期行
+			dateHeaders = extractHeaders(headMap, 5);
+			headersProcessed = true;
+		}
+	}
+
+	private List<String> extractHeaders(Map<Integer, ReadCellData<?>> headMap, int startCol) {
+		List<String> headers = new ArrayList<>();
+		headMap.entrySet().stream()
+			.filter(entry -> entry.getKey() >= startCol)
+			.sorted(Map.Entry.comparingByKey())
+			.forEach(entry -> {
+				String stringValue = entry.getValue().getStringValue();
+				if(StringUtil.isBlank(stringValue)){
+					BigDecimal numberValue = entry.getValue().getNumberValue();
+					if(numberValue != null){
+						headers.add(numberValue.intValue()<=9?"0"+numberValue.intValue() : numberValue.intValue()+"");
+					}
+				}else {
+					headers.add(stringValue);
+				}
+
+
+			});
+		return headers;
+	}
+
+	@Override
+	public void invoke(Map<Integer, String> data, AnalysisContext context) {
+		if (!headersProcessed || context.readRowHolder().getRowIndex() < DATA_START_ROW) {
+			return;
+		}
+
+		// 跳过空行
+		if (data == null || data.isEmpty() || data.values().stream().allMatch(v -> v == null || v.trim().isEmpty())) {
+			return;
+		}
+
+		AttendanceMonthlyExcel record = new AttendanceMonthlyExcel();
+
+		// 解析固定列
+		record.setSerialNumber(safeParseInt(data.get(0)));
+		record.setName(data.get(1));
+		record.setEmployeeId(data.get(2));
+		record.setIdCard(data.get(3));
+		record.setEmployeeType(data.get(4));
+
+		// 解析每日考勤数据
+		int maxDays = Math.min(dateHeaders.size(), dayOfWeekHeaders.size());
+		for (int i = 0; i < maxDays; i++) {
+			String hoursStr = data.get(i + 5); // 考勤数据从第5列开始
+			//没有填报的日期,跳过
+			if (hoursStr != null && !hoursStr.trim().isEmpty()) {
+				try {
+					DailyAttendance daily = new DailyAttendance();
+					daily.setDate(yearAndMonth + dateHeaders.get(i));
+					daily.setHours(safeParseDouble(hoursStr));
+					record.getDailyAttendances().add(daily);
+				} catch (NumberFormatException e) {
+					System.err.printf("解析错误: 员工[%s] 日期[%s] 值[%s]%n",
+						record.getName(), dateHeaders.get(i), hoursStr);
+				}
+			}
+		}
+
+		// 解析月度汇总数据(最后两列)
+		int lastCol = 5 + maxDays;
+		if (data.size() > lastCol) {
+			record.setMonthlyHours(safeParseDouble(data.get(lastCol)));
+			record.setMonthlyDays(safeParseDouble(data.get(lastCol + 1)));
+		}
+		System.out.println(record);
+		records.add(record);
+	}
+
+	@Override
+	public void doAfterAllAnalysed(AnalysisContext context) {
+		System.out.println("考勤数据解析完成,共解析 " + records.size() + " 条记录");
+	}
+
+
+	private Integer safeParseInt(String value) {
+		try {
+			return value == null || value.trim().isEmpty() ? null : Integer.parseInt(value.trim());
+		} catch (NumberFormatException e) {
+			return null;
+		}
+	}
+
+	private Double safeParseDouble(String value) {
+		try {
+			return value == null || value.trim().isEmpty() ? null : Double.parseDouble(value.trim());
+		} catch (NumberFormatException e) {
+			return null;
+		}
+	}
+
+//	public static void main(String[] args) {
+//		Date yyyYmmdd = DateUtil.parse("2025056", "yyyyMMdd");
+//		String str = DateUtil.format(yyyYmmdd, "yyyyMMdd");
+//		System.out.println(str);
+//	}
+}

+ 40 - 0
kd-service/kd-scientific/src/main/java/org/sky/scientific/excel/AttendanceMonthlyExcel.java

@@ -0,0 +1,40 @@
+
+package org.sky.scientific.excel;
+
+
+import com.alibaba.excel.annotation.write.style.ColumnWidth;
+import com.alibaba.excel.annotation.write.style.ContentRowHeight;
+import com.alibaba.excel.annotation.write.style.HeadRowHeight;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * 人员月度考勤
+ *
+ * @author Kd
+ * @since 2025-06-29
+ */
+@Data
+@ColumnWidth(25)
+@HeadRowHeight(20)
+@ContentRowHeight(18)
+public class AttendanceMonthlyExcel implements Serializable {
+
+	@Serial
+	private static final long serialVersionUID = 1L;
+
+	private Integer serialNumber;  // 序号
+	private String name;           // 姓名
+	private String employeeId;     // 工号
+	private String idCard;         // 身份证号码
+	private String employeeType;   // 科研人员类别
+	private List<DailyAttendance> dailyAttendances = new ArrayList<>(); // 每日考勤数据
+	private Double monthlyHours;   // 本月出勤/小时
+	private Double monthlyDays;    // 本月出勤/天
+
+}

+ 18 - 0
kd-service/kd-scientific/src/main/java/org/sky/scientific/excel/DailyAttendance.java

@@ -0,0 +1,18 @@
+package org.sky.scientific.excel;
+
+import lombok.Data;
+
+@Data
+public class DailyAttendance {
+
+	private String date;        // 日期(如"20250506")
+	private Double hours;
+
+	@Override
+	public String toString() {
+		return "DailyAttendance{" +
+			"date='" + date + '\'' +
+			", hours=" + hours +
+			'}';
+	}
+}

+ 2 - 7
kd-service/kd-scientific/src/main/java/org/sky/scientific/mapper/XmTechnicianMonthlyMapper.java

@@ -4,16 +4,11 @@ package org.sky.scientific.mapper;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import org.apache.ibatis.annotations.Param;
-import org.sky.scientific.excel.TechnicianExcel;
-import org.sky.scientific.pojo.dto.TechnicianDTO;
-import org.sky.scientific.pojo.entity.TechnicianEntity;
+import org.sky.scientific.pojo.dto.XmTechnicianMonthlyDto;
 import org.sky.scientific.pojo.entity.XmTechnicianMonthlyEntity;
-import org.sky.scientific.pojo.vo.TechnicianVO;
 import org.sky.scientific.pojo.vo.XmTechnicianSummary;
 
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 /**
  * 每月实际参研人员名单 Mapper 接口
@@ -30,7 +25,7 @@ public interface XmTechnicianMonthlyMapper extends BaseMapper<XmTechnicianMonthl
 	 * @param entity
 	 * @return
 	 */
-	IPage<XmTechnicianMonthlyEntity> meiyueshijicanyuanrenyuanmingdan(IPage<Object> page, @Param("ew") XmTechnicianMonthlyEntity entity);
+	IPage<XmTechnicianMonthlyEntity> meiyueshijicanyuanrenyuanmingdan(IPage<Object> page, @Param("ew") XmTechnicianMonthlyDto entity);
 
 	/**
 	 * 批量物理删除

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

@@ -56,7 +56,11 @@
             ) ttt ON ttt.technician_id = t1.id
         ) t ON t.technician_id = m.technician_id
         WHERE m.year_and_month = #{ew.yearAndMonth}
-        <if test="ew.xmId!=null and ew.xmId>0">and xm.xm_id = #{ew.xmId}</if>
+        <if test="ew.xmId!=null and ew.xmId>0">and xm.xm_id in
+            <foreach collection="ew.xmList" item="xm" open="(" separator="," close=")">
+                #{xm.id}
+            </foreach>
+        </if>
         <if test="ew.technicianId!=null and ew.technicianId!=''">and t.technician_id = #{ew.technicianId}</if>
     </select>
 

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

@@ -3,6 +3,7 @@ package org.sky.scientific.service;
 
 import org.sky.core.mp.base.BaseService;
 import org.sky.scientific.pojo.entity.AttendanceEntity;
+import org.springframework.web.multipart.MultipartFile;
 
 /**
  * 考勤管理 服务类
@@ -18,4 +19,11 @@ public interface IAttendanceService extends BaseService<AttendanceEntity> {
 	 * @return
 	 */
 	boolean submit(AttendanceEntity attendance);
+
+	/**
+	 * 研发人员考勤表-导入
+	 * @param file
+	 * @param yearAndMonth 年月
+	 */
+	void importAttendance(MultipartFile file, String yearAndMonth);
 }

+ 7 - 8
kd-service/kd-scientific/src/main/java/org/sky/scientific/service/ITechnicianService.java

@@ -1,14 +1,13 @@
 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 org.sky.core.mp.base.BaseService;
+import org.sky.scientific.excel.TechnicianExcel;
 import org.sky.scientific.pojo.dto.TechnicianDTO;
+import org.sky.scientific.pojo.dto.XmTechnicianMonthlyDto;
 import org.sky.scientific.pojo.entity.TechnicianEntity;
 import org.sky.scientific.pojo.entity.XmTechnicianMonthlyEntity;
 import org.sky.scientific.pojo.vo.TechnicianVO;
-import org.sky.scientific.excel.TechnicianExcel;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import org.sky.core.mp.base.BaseService;
 import org.sky.scientific.pojo.vo.XmTechnicianSummary;
 
 import java.util.HashMap;
@@ -75,10 +74,10 @@ public interface ITechnicianService extends BaseService<TechnicianEntity> {
 
 	/**
 	 * 每月实际参研人员名单-直接引用预算表名单
-	 * @param entity
+	 * @param dto
 	 * @return
 	 */
-	void zhijieyinyongyusuanbiaomingdan(XmTechnicianMonthlyEntity entity);
+	void zhijieyinyongyusuanbiaomingdan(XmTechnicianMonthlyDto dto);
 
 
 	/**
@@ -87,7 +86,7 @@ public interface ITechnicianService extends BaseService<TechnicianEntity> {
 	 * @param page
 	 * @return
 	 */
-	IPage<XmTechnicianMonthlyEntity> meiyueshijicanyuanrenyuanmingdan(XmTechnicianMonthlyEntity entity, IPage<Object> page);
+	IPage<XmTechnicianMonthlyEntity> meiyueshijicanyuanrenyuanmingdan(XmTechnicianMonthlyDto entity, IPage<Object> page);
 
 	/**
 	 * 每月实际参研人员名单-新增或修改

+ 57 - 2
kd-service/kd-scientific/src/main/java/org/sky/scientific/service/impl/AttendanceServiceImpl.java

@@ -1,13 +1,26 @@
 
 package org.sky.scientific.service.impl;
 
+import com.alibaba.excel.EasyExcel;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.google.protobuf.ServiceException;
+import lombok.SneakyThrows;
 import org.sky.core.mp.base.BaseServiceImpl;
+import org.sky.core.tool.utils.CollectionUtil;
+import org.sky.scientific.excel.AttendanceMonthlyDataListener;
+import org.sky.scientific.excel.AttendanceMonthlyExcel;
+import org.sky.scientific.excel.DailyAttendance;
 import org.sky.scientific.mapper.AttendanceMapper;
 import org.sky.scientific.pojo.entity.AttendanceEntity;
 import org.sky.scientific.service.IAttendanceService;
 import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.BufferedInputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 
 /**
  * 考勤管理 服务实现类
@@ -23,10 +36,10 @@ public class AttendanceServiceImpl extends BaseServiceImpl<AttendanceMapper, Att
 		int affectNum = 0;
 		AttendanceEntity one = baseMapper.selectOne(Wrappers.<AttendanceEntity>lambdaQuery().
 			eq(AttendanceEntity::getTechnicianId, attendance.getTechnicianId()).eq(AttendanceEntity::getWorkDate, attendance.getWorkDate()));
-		if(one == null){
+		if (one == null) {
 			//新增
 			affectNum = baseMapper.insert(attendance);
-		}else {
+		} else {
 			//修改
 			LambdaQueryWrapper<AttendanceEntity> updateWrapper = Wrappers.<AttendanceEntity>lambdaQuery()
 				.eq(AttendanceEntity::getTechnicianId, attendance.getTechnicianId())
@@ -35,4 +48,46 @@ public class AttendanceServiceImpl extends BaseServiceImpl<AttendanceMapper, Att
 		}
 		return affectNum == 1;
 	}
+
+	@SneakyThrows
+	@Override
+	public void importAttendance(MultipartFile file, String yearAndMonth) {
+		AttendanceMonthlyDataListener listener = new AttendanceMonthlyDataListener();
+		listener.setYearAndMonth(yearAndMonth);
+		EasyExcel.read(new BufferedInputStream(file.getInputStream()))
+			.registerReadListener(listener)
+			.sheet(0)
+			.headRowNumber(2) // 重要:设置表头行数(0-based)
+			.doRead();
+		List<AttendanceMonthlyExcel> records = listener.getRecords();
+		if (CollectionUtil.isEmpty(records)) {
+			throw new ServiceException("未能从模板中读取数据,请检查模板格式是否正确");
+		}
+		List<Long> technicianIds = Arrays.asList(2L, 3L, 5L);
+
+		List<AttendanceEntity> attendanceEntities = new ArrayList<>();
+		int index = 0;
+		for (AttendanceMonthlyExcel record : records) {
+			//保存
+//			technicianId 2,3,5,先写死吧
+
+			for (DailyAttendance dailyAttendance : record.getDailyAttendances()) {
+				index++;
+				AttendanceEntity entity = new AttendanceEntity();
+				entity.setTechnicianId(technicianIds.get(records.indexOf(record)));
+				entity.setWorkDate(dailyAttendance.getDate());
+				entity.setWorkDuration(dailyAttendance.getHours());
+				attendanceEntities.add(entity);
+				if(index%20 == 0){
+					boolean saveBatchResult = this.saveBatch(attendanceEntities);
+					if(saveBatchResult){
+						attendanceEntities.clear();
+					}
+				}
+			}
+			if(index%20>0){
+				this.saveBatch(attendanceEntities);
+			}
+		}
+	}
 }

+ 50 - 49
kd-service/kd-scientific/src/main/java/org/sky/scientific/service/impl/TechnicianServiceImpl.java

@@ -1,15 +1,11 @@
 
 package org.sky.scientific.service.impl;
 
-import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import jakarta.annotation.Resource;
-import jakarta.validation.constraints.Email;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.ibatis.executor.BatchResult;
-import org.glassfish.jaxb.core.v2.TODO;
 import org.sky.core.log.exception.ServiceException;
 import org.sky.core.mp.base.BaseServiceImpl;
 import org.sky.core.secure.utils.AuthUtil;
@@ -20,7 +16,9 @@ import org.sky.scientific.mapper.TechnicianMapper;
 import org.sky.scientific.mapper.XmTechnicianMonthlyMapper;
 import org.sky.scientific.mapper.YsRyrgfyMapper;
 import org.sky.scientific.pojo.dto.TechnicianDTO;
+import org.sky.scientific.pojo.dto.XmTechnicianMonthlyDto;
 import org.sky.scientific.pojo.entity.TechnicianEntity;
+import org.sky.scientific.pojo.entity.XmEntity;
 import org.sky.scientific.pojo.entity.XmTechnicianMonthlyEntity;
 import org.sky.scientific.pojo.entity.YsRyrgfyEntity;
 import org.sky.scientific.pojo.vo.TechnicianVO;
@@ -31,8 +29,9 @@ import org.sky.system.pojo.enums.DictEnum;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
-import java.util.*;
-import java.util.stream.Collectors;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Objects;
 
 /**
  * 技术人员花名册 服务实现类
@@ -70,12 +69,12 @@ public class TechnicianServiceImpl extends BaseServiceImpl<TechnicianMapper, Tec
 		//查询快照表是否存在,存在则覆盖,不存在则新增
 		// TODO 新增基础信息成功后,technician.getTechnicianId()是否为空
 		TechnicianEntity technicianSnapshot = baseMapper.selectByTechnicianIdAndYearAndMonth(technicianId, technician.getYearAndMonth());
-		if(technicianSnapshot == null){
+		if (technicianSnapshot == null) {
 			//新增
-			affectNum = baseMapper.saveTechnicianSnapshot(technicianId,technician);
-		}else {
+			affectNum = baseMapper.saveTechnicianSnapshot(technicianId, technician);
+		} else {
 			//覆盖
-			affectNum = baseMapper.updateTechnicianSnapshot(technicianId,technician);
+			affectNum = baseMapper.updateTechnicianSnapshot(technicianId, technician);
 		}
 		return affectNum == 1;
 	}
@@ -87,15 +86,15 @@ public class TechnicianServiceImpl extends BaseServiceImpl<TechnicianMapper, Tec
 
 	@Override
 	public IPage<TechnicianVO> selectTechnicianPage(IPage<TechnicianVO> page, TechnicianDTO technician) {
-		if(StringUtil.isBlank(technician.getYearAndMonth())){
+		if (StringUtil.isBlank(technician.getYearAndMonth())) {
 			throw new ServiceException("参数yearAndMonth不能为空");
 		}
 		List<TechnicianVO> voList = null;
-		if(technician.getYearAndMonth().length() == 4){
+		if (technician.getYearAndMonth().length() == 4) {
 			voList = baseMapper.selectByYear(technician);
-		}else if(technician.getYearAndMonth().length() == 6){
+		} else if (technician.getYearAndMonth().length() == 6) {
 			voList = baseMapper.selectTechnicianPage(page, technician);
-		}else {
+		} else {
 			throw new ServiceException("参数yearAndMonth只能是YYYY或YYYYmm格式");
 		}
 		return page.setRecords(voList);
@@ -129,7 +128,7 @@ public class TechnicianServiceImpl extends BaseServiceImpl<TechnicianMapper, Tec
 		//得到上个月的年月
 		String lastDate = DateUtil.format(DateUtil.minusMonths(DateUtil.parse(technician.getYearAndMonth(), DateUtil.PATTERN_YYYYMM), 1), DateUtil.PATTERN_YYYYMM);
 		List<TechnicianEntity> lastMonthList = this.baseMapper.selectByYearAndMonth(lastDate);
-		if(CollectionUtil.isEmpty(lastMonthList)){
+		if (CollectionUtil.isEmpty(lastMonthList)) {
 			throw new ServiceException("上个月人员名单为空");
 		}
 		//与本月数据对比,去重后保存至数据库
@@ -152,33 +151,35 @@ public class TechnicianServiceImpl extends BaseServiceImpl<TechnicianMapper, Tec
 
 	@Transactional
 	@Override
-	public void zhijieyinyongyusuanbiaomingdan(XmTechnicianMonthlyEntity entity) {
-		//先根据xmId查询项目预算中的人员名单
-		List<YsRyrgfyEntity> ysRyrgfyEntityList = ysRyrgfyMapper.selectList(Wrappers.<YsRyrgfyEntity>lambdaQuery().eq(YsRyrgfyEntity::getXmId, entity.getXmId()));
-		if(CollectionUtil.isEmpty(ysRyrgfyEntityList)){
-			throw new ServiceException("该项目没有填写人员人工费用预算");
-		}
-		List<Long> technicianIdList = ysRyrgfyEntityList.stream().map(YsRyrgfyEntity::getTechnicianId).toList();
-		for (Long technicianId : technicianIdList) {
-			XmTechnicianMonthlyEntity monthlyEntity = new XmTechnicianMonthlyEntity();
-			monthlyEntity.setXmId(entity.getXmId());
-			monthlyEntity.setTechnicianId(technicianId);
-			monthlyEntity.setYearAndMonth(entity.getYearAndMonth());
-			monthlyEntity.setCreateUser(SecureUtil.getUserId());
-			monthlyEntity.setUpdateUser(SecureUtil.getUserId());
-
-			LambdaQueryWrapper<XmTechnicianMonthlyEntity> wrapper = Wrappers.<XmTechnicianMonthlyEntity>lambdaQuery().
-				eq(XmTechnicianMonthlyEntity::getYearAndMonth, entity.getYearAndMonth()).
-				eq(XmTechnicianMonthlyEntity::getTechnicianId, technicianId).
-				eq(XmTechnicianMonthlyEntity::getXmId, entity.getXmId());
-			if(xmTechnicianMonthlyMapper.selectCount(wrapper) == 0) {
-				xmTechnicianMonthlyMapper.insert(monthlyEntity);
+	public void zhijieyinyongyusuanbiaomingdan(XmTechnicianMonthlyDto dto) {
+		//遍历xmIdList,根据xmId查询项目预算中的人员名单
+		for (XmEntity xm : dto.getXmList()) {
+			List<YsRyrgfyEntity> ysRyrgfyList = ysRyrgfyMapper.selectList(Wrappers.<YsRyrgfyEntity>lambdaQuery().eq(YsRyrgfyEntity::getXmId, xm.getId()));
+			if (CollectionUtil.isEmpty(ysRyrgfyList)) {
+				throw new ServiceException("项目[" + xm.getXmmc() + "]没有填写人员人工费用预算");
+			}
+			List<Long> technicianIdList = ysRyrgfyList.stream().map(YsRyrgfyEntity::getTechnicianId).toList();
+			for (Long technicianId : technicianIdList) {
+				XmTechnicianMonthlyEntity monthlyEntity = new XmTechnicianMonthlyEntity();
+				monthlyEntity.setXmId(xm.getId());
+				monthlyEntity.setTechnicianId(technicianId);
+				monthlyEntity.setYearAndMonth(dto.getYearAndMonth());
+				monthlyEntity.setCreateUser(SecureUtil.getUserId());
+				monthlyEntity.setUpdateUser(SecureUtil.getUserId());
+
+				LambdaQueryWrapper<XmTechnicianMonthlyEntity> wrapper = Wrappers.<XmTechnicianMonthlyEntity>lambdaQuery().
+					eq(XmTechnicianMonthlyEntity::getYearAndMonth, dto.getYearAndMonth()).
+					eq(XmTechnicianMonthlyEntity::getTechnicianId, technicianId).
+					eq(XmTechnicianMonthlyEntity::getXmId, xm.getId());
+				if (xmTechnicianMonthlyMapper.selectCount(wrapper) == 0) {
+					xmTechnicianMonthlyMapper.insert(monthlyEntity);
+				}
 			}
 		}
 	}
 
 	@Override
-	public IPage<XmTechnicianMonthlyEntity> meiyueshijicanyuanrenyuanmingdan(XmTechnicianMonthlyEntity entity, IPage<Object> page) {
+	public IPage<XmTechnicianMonthlyEntity> meiyueshijicanyuanrenyuanmingdan(XmTechnicianMonthlyDto entity, IPage<Object> page) {
 		return xmTechnicianMonthlyMapper.meiyueshijicanyuanrenyuanmingdan(page, entity);
 	}
 
@@ -193,17 +194,17 @@ public class TechnicianServiceImpl extends BaseServiceImpl<TechnicianMapper, Tec
 			eq(XmTechnicianMonthlyEntity::getYearAndMonth, entity.getYearAndMonth()).
 			eq(XmTechnicianMonthlyEntity::getTechnicianId, entity.getTechnicianId()).
 			eq(XmTechnicianMonthlyEntity::getXmId, entity.getXmId());
-		if(xmTechnicianMonthlyMapper.selectCount(wrapper) == 0) {
+		if (xmTechnicianMonthlyMapper.selectCount(wrapper) == 0) {
 			affectNum = xmTechnicianMonthlyMapper.insert(entity);
-		}else{
-			throw new ServiceException("科技人员已是项目在["+entity.getYearAndMonth()+"]的参研人员,请勿重复新增");
+		} else {
+			throw new ServiceException("科技人员已是项目在[" + entity.getYearAndMonth() + "]的参研人员,请勿重复新增");
 		}
 		return affectNum == 1;
 	}
 
 	@Override
 	public boolean physicalDeleteXmTechnicianMonthlyByIds(List<Long> ids) {
-		if(CollectionUtil.isEmpty(ids)){
+		if (CollectionUtil.isEmpty(ids)) {
 			throw new ServiceException("参数不能为空");
 		}
 		return xmTechnicianMonthlyMapper.physicalDeleteXmTechnicianMonthlyByIds(ids) == ids.size();
@@ -215,24 +216,24 @@ public class TechnicianServiceImpl extends BaseServiceImpl<TechnicianMapper, Tec
 	}
 
 	@Override
-	public IPage<TechnicianEntity> selectTechnicianPageByDateInterval(IPage<TechnicianEntity> page, HashMap<String,String> param) {
-		if(!param.containsKey("startDate") || !param.containsKey("endDate")){
+	public IPage<TechnicianEntity> selectTechnicianPageByDateInterval(IPage<TechnicianEntity> page, HashMap<String, String> param) {
+		if (!param.containsKey("startDate") || !param.containsKey("endDate")) {
 			throw new ServiceException("startDate或者endDate为空");
 		}
 		String startDate = param.get("startDate");
 		String endDate = param.get("endDate");
-		if(startDate.contains("-")){
+		if (startDate.contains("-")) {
 			startDate = startDate.replace("-", "");
 		}
-		if(endDate.contains("-")){
+		if (endDate.contains("-")) {
 			endDate = endDate.replace("-", "");
 		}
 
-		if(startDate.length() == 8){
-			startDate = startDate.substring(0,6);
+		if (startDate.length() == 8) {
+			startDate = startDate.substring(0, 6);
 		}
-		if(endDate.length() == 8){
-			endDate = endDate.substring(0,6);
+		if (endDate.length() == 8) {
+			endDate = endDate.substring(0, 6);
 		}
 		param.put("startDate", startDate);
 		param.put("endDate", endDate);