Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
2025-09-24 14:45:29 +08:00
41 changed files with 1382 additions and 415 deletions

View File

@ -1,10 +1,7 @@
package org.dromara.common.excel.coryUtils; package org.dromara.common.excel.coryUtils;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
@ -84,7 +81,7 @@ public class ExcelReader {
SheetData sheetData = new SheetData(); SheetData sheetData = new SheetData();
sheetData.setSheetName(sheet.getSheetName()); sheetData.setSheetName(sheet.getSheetName());
processSheetData(sheet, sheetData); processSheetData(workbook,sheet, sheetData);
sheetDataList.add(sheetData); sheetDataList.add(sheetData);
} }
} }
@ -114,7 +111,7 @@ public class ExcelReader {
sheetData.setSheetName(sheet.getSheetName()); sheetData.setSheetName(sheet.getSheetName());
// 处理单个sheet的数据 // 处理单个sheet的数据
processSheetData(sheet, sheetData); processSheetData(workbook, sheet, sheetData);
sheetDataList.add(sheetData); sheetDataList.add(sheetData);
} }
@ -129,7 +126,7 @@ public class ExcelReader {
/** /**
* 处理单个工作表的数据 * 处理单个工作表的数据
*/ */
private static void processSheetData(org.apache.poi.ss.usermodel.Sheet sheet, SheetData sheetData) { private static void processSheetData(Workbook workbook, Sheet sheet, SheetData sheetData) {
boolean foundChineseStart = false; boolean foundChineseStart = false;
List<List<String>> data = new ArrayList<>(); List<List<String>> data = new ArrayList<>();
boolean isFirstRow = true; boolean isFirstRow = true;
@ -140,29 +137,44 @@ public class ExcelReader {
isFirstRow = false; isFirstRow = false;
continue; continue;
} }
if(hasValidData(workbook,row)){
List<String> rowData = new ArrayList<>();
// 读取A到E列索引0到4
for (int cellIndex = 0; cellIndex < 6; cellIndex++) {
Cell cell = row.getCell(cellIndex, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
rowData.add(getCellValue(workbook,cell));
}
List<String> rowData = new ArrayList<>(); // 检查是否找到中文数字开头的行
// 读取A到E列索引0到4 String aColumnValue = rowData.get(0).trim();
for (int cellIndex = 0; cellIndex < 6; cellIndex++) { if (aColumnValue.matches(CHINESE_NUMBERS_REGEX)) {
Cell cell = row.getCell(cellIndex, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK); foundChineseStart = true;
rowData.add(getCellValue(cell)); }
// 只有找到中文数字开头的行之后,才开始收集数据
if (foundChineseStart) {
data.add(rowData);
}
} }
// 检查是否找到中文数字开头的行
String aColumnValue = rowData.get(0).trim();
if (aColumnValue.matches(CHINESE_NUMBERS_REGEX)) {
foundChineseStart = true;
}
// 只有找到中文数字开头的行之后,才开始收集数据
if (foundChineseStart) {
data.add(rowData);
}
} }
sheetData.setData(data); sheetData.setData(data);
} }
private static boolean hasValidData(Workbook workbook,Row row) {
// 遍历行中的所有单元格
for (int cellIndex = 0; cellIndex < row.getLastCellNum(); cellIndex++) {
Cell cell = row.getCell(cellIndex, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
String cellValue = getCellValue(workbook, cell).trim();
// 只要有一个单元格有非空值,就认为是有效行
if (!cellValue.isEmpty()) {
return true;
}
}
return false;
}
/** /**
* 根据数据构建树形结构 * 根据数据构建树形结构
*/ */
@ -263,7 +275,7 @@ public class ExcelReader {
/** /**
* 获取单元格的值,处理不同数据类型 * 获取单元格的值,处理不同数据类型
*/ */
private static String getCellValue(Cell cell) { private static String getCellValue(Workbook workbook, Cell cell) {
if (cell == null) { if (cell == null) {
return ""; return "";
} }
@ -278,6 +290,21 @@ public class ExcelReader {
return numericValue.substring(0, numericValue.length() - 2); return numericValue.substring(0, numericValue.length() - 2);
} }
return numericValue; return numericValue;
case FORMULA:
//这样对于字符串cell.getStringCellValue()方法即可取得其值如果公式生成的是数值使用cell.getStringCellValue()方法会抛出IllegalStateException异常在异常处理中使用cell.getNumericCellValue();
// 1. 获取公式文本(如 "A1+B1"
String formula = cell.getCellFormula();
System.out.println("公式文本:" + formula);
// 2. 创建公式计算器(关键步骤:用于计算公式结果)
FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator();
// 3. 计算公式返回包含结果类型和值的CellValue对象
CellValue cellValue = evaluator.evaluate(cell);
// 4. 根据结果类型提取值
return getValue( cellValue.getCellType(), cellValue);
case BOOLEAN: case BOOLEAN:
return String.valueOf(cell.getBooleanCellValue()); return String.valueOf(cell.getBooleanCellValue());
default: default:
@ -285,6 +312,19 @@ public class ExcelReader {
} }
} }
private static String getValue( CellType resultType, CellValue cellValue) {
return switch (resultType) {
case NUMERIC -> String.valueOf(cellValue.getNumberValue());
case STRING -> String.valueOf(cellValue.getStringValue());
case BOOLEAN -> String.valueOf(cellValue.getBooleanValue());
case ERROR -> String.valueOf(cellValue.getErrorValue());
case BLANK -> String.valueOf(CellType.BLANK);
default ->
// 保留原公式(不处理的类型)
cellValue.getStringValue();
};
}
/** /**
* 根据A列的值确定父节点的键 * 根据A列的值确定父节点的键
*/ */

View File

@ -141,8 +141,8 @@ public class BusMrpBaseController extends BaseController {
* 获取剩余量 * 获取剩余量
*/ */
@GetMapping("/remaining") @GetMapping("/remaining")
public R<Map<String,Object>> remaining(Long suppliespriceId,Long mrpBaseId) { public R<Map<String,Object>> remaining(Long projectId, String suppliespriceName,String specification,Long mrpBaseId) {
return R.ok(busMrpBaseService.remaining(suppliespriceId,mrpBaseId)); return R.ok(busMrpBaseService.remaining(projectId,suppliespriceName,specification,mrpBaseId));
} }
@ -182,19 +182,20 @@ public class BusMrpBaseController extends BaseController {
@GetMapping("/coryEngineeringList") @GetMapping("/coryEngineeringList")
public R<List<BusBillofquantities>> obtainTheList(CoryObtainTheListReq req) { public R<List<BusBillofquantities>> obtainTheList(CoryObtainTheListReq req) {
// public R<List<ObtainTheListRes>> obtainTheList(CoryObtainTheListReq req) { // public R<List<ObtainTheListRes>> obtainTheList(CoryObtainTheListReq req) {
BusBillofquantitiesVersions one = busBillofquantitiesVersionsService.getOne(Wrappers.<BusBillofquantitiesVersions>lambdaQuery() // BusBillofquantitiesVersions one = busBillofquantitiesVersionsService.getOne(Wrappers.<BusBillofquantitiesVersions>lambdaQuery()
.eq(BusBillofquantitiesVersions::getWorkOrderType, "3") //物资工程量清单 // .eq(BusBillofquantitiesVersions::getWorkOrderType, "3") //物资工程量清单
.eq(BusBillofquantitiesVersions::getProjectId, req.getProjectId()) // .eq(BusBillofquantitiesVersions::getProjectId, req.getProjectId())
.eq(BusBillofquantitiesVersions::getVersions, req.getVersions()) // .eq(BusBillofquantitiesVersions::getVersions, req.getVersions())
.eq(BusBillofquantitiesVersions::getStatus, BusinessStatusEnum.FINISH.getStatus()) // .eq(BusBillofquantitiesVersions::getStatus, BusinessStatusEnum.FINISH.getStatus())
.last("limit 1") // .last("limit 1")
); // );
if (one == null){ // if (one == null){
throw new ServiceException("请先完成物资工程量清单"); // throw new ServiceException("请先完成物资工程量清单");
} // }
List<BusBillofquantities> list = busBillofquantitiesService.list(Wrappers.<BusBillofquantities>lambdaQuery() // List<BusBillofquantities> list = busBillofquantitiesService.list(Wrappers.<BusBillofquantities>lambdaQuery()
.eq(BusBillofquantities::getVersions, one.getVersions()) // .eq(BusBillofquantities::getVersions, one.getVersions())
); // );
// return R.ok(list);
// List<ObtainTheListRes> obtainTheListRes = new ArrayList<>(); // List<ObtainTheListRes> obtainTheListRes = new ArrayList<>();
// list.forEach(billofquantities -> { // list.forEach(billofquantities -> {
// ObtainTheListRes res = new ObtainTheListRes(); // ObtainTheListRes res = new ObtainTheListRes();
@ -208,8 +209,19 @@ public class BusMrpBaseController extends BaseController {
// // 3. 递归组装树形结构从顶级节点pid=0开始 // // 3. 递归组装树形结构从顶级节点pid=0开始
// List<ObtainTheListRes> treeList = buildTree("0", parentMap); // List<ObtainTheListRes> treeList = buildTree("0", parentMap);
// return R.ok(treeList); // return R.ok(treeList);
return R.ok(list); List<BusBillofquantities> busBillofquantities = busBillofquantitiesService.getBaseMapper()
// return R.ok(busBillofquantitiesService.getBaseMapper().selectList(new LambdaQueryWrapper<BusBillofquantities>().eq(BusBillofquantities::getPid, req.getSid()))); .selectList(new LambdaQueryWrapper<BusBillofquantities>()
.eq(BusBillofquantities::getProjectId, req.getProjectId())
.eq(BusBillofquantities::getName, req.getSid()));
List<String> sids = new ArrayList<>();
busBillofquantities.forEach(busBillofquantities1 -> {
sids.add(busBillofquantities1.getSid());
});
return R.ok(busBillofquantitiesService
.getBaseMapper()
.selectList(new LambdaQueryWrapper<BusBillofquantities>()
.eq(BusBillofquantities::getProjectId, req.getProjectId())
.in(BusBillofquantities::getPid, sids)));
} }
private List<ObtainTheListRes> buildTree(String parentId, Map<String, List<ObtainTheListRes>> parentMap) { private List<ObtainTheListRes> buildTree(String parentId, Map<String, List<ObtainTheListRes>> parentMap) {

View File

@ -86,5 +86,5 @@ public interface IBusMrpBaseService extends IService<BusMrpBase>{
/** /**
* 获取物资已有数量 * 获取物资已有数量
*/ */
Map<String, Object> remaining(Long suppliespriceId, Long mrpBaseId); Map<String, Object> remaining(Long projectId, String suppliespriceName, String specification, Long mrpBaseId);
} }

View File

@ -75,7 +75,7 @@ public class BusMrpBaseServiceImpl extends ServiceImpl<BusMrpBaseMapper, BusMrpB
planBo.setMrpBaseId(id); planBo.setMrpBaseId(id);
List<BusMaterialbatchdemandplanVo> voList = planservice.queryList(planBo); List<BusMaterialbatchdemandplanVo> voList = planservice.queryList(planBo);
for (BusMaterialbatchdemandplanVo vo : voList) { for (BusMaterialbatchdemandplanVo vo : voList) {
Map<String, Object> map = remaining(vo.getSuppliespriceId(), id); Map<String, Object> map = remaining(vo.getProjectId(),vo.getName(), vo.getSpecification(), id);
vo.setRemaining(Convert.toBigDecimal(map.get("remainingQuantity"))); vo.setRemaining(Convert.toBigDecimal(map.get("remainingQuantity")));
} }
busMrpVo.setMrpBaseBo(busMrpBaseVo); busMrpVo.setMrpBaseBo(busMrpBaseVo);
@ -190,25 +190,34 @@ public class BusMrpBaseServiceImpl extends ServiceImpl<BusMrpBaseMapper, BusMrpB
if (CollectionUtil.isNotEmpty(dto.getPlanList())) { if (CollectionUtil.isNotEmpty(dto.getPlanList())) {
// 按 suppliespriceId 分组统计本次提交的数量 // 按 suppliespriceId 分组统计本次提交的数量
Map<Long, BigDecimal> batchSumMap = dto.getPlanList().stream() Map<String, BigDecimal> batchSumMap = dto.getPlanList().stream()
.collect(Collectors.groupingBy( .collect(Collectors.groupingBy(
BusMaterialbatchdemandplanBo::getSuppliespriceId, // 关键修改:使用 name + specification 拼接作为分组键
item -> {
String name = item.getName() != null ? item.getName() : "";
String spec = item.getSpecification() != null ? item.getSpecification() : "";
return name + "&&&" + spec;
},
Collectors.reducing( Collectors.reducing(
BigDecimal.ZERO, BigDecimal.ZERO,
BusMaterialbatchdemandplanBo::getDemandQuantity, item -> {
BigDecimal quantity = item.getDemandQuantity();
return quantity != null ? quantity : BigDecimal.ZERO;
},
BigDecimal::add BigDecimal::add
) )
)); ));
// 检查每种物料是否超出数量限制 // 检查每种物料是否超出数量限制
for (Map.Entry<Long, BigDecimal> entry : batchSumMap.entrySet()) { for (Map.Entry<String, BigDecimal> entry : batchSumMap.entrySet()) {
Long suppliespriceId = entry.getKey(); String biaoshi = entry.getKey();
BigDecimal batchSum = entry.getValue(); BigDecimal batchSum = entry.getValue();
String[] split = biaoshi.split("&&&");
// 获取数据库中已有的数量 // 获取数据库中已有的数量
List<BusMaterialbatchdemandplan> existingList = planservice.list( List<BusMaterialbatchdemandplan> existingList = planservice.list(
Wrappers.lambdaQuery(BusMaterialbatchdemandplan.class) Wrappers.lambdaQuery(BusMaterialbatchdemandplan.class)
.eq(BusMaterialbatchdemandplan::getSuppliespriceId, suppliespriceId) .eq(BusMaterialbatchdemandplan::getName, split[0])
.eq(BusMaterialbatchdemandplan::getSpecification,split[1])
.ne(BusMaterialbatchdemandplan::getMrpBaseId, convert.getId()) // 排除当前批次 .ne(BusMaterialbatchdemandplan::getMrpBaseId, convert.getId()) // 排除当前批次
); );
@ -217,13 +226,29 @@ public class BusMrpBaseServiceImpl extends ServiceImpl<BusMrpBaseMapper, BusMrpB
.reduce(BigDecimal.ZERO, BigDecimal::add); .reduce(BigDecimal.ZERO, BigDecimal::add);
// 检查总数量是否超出限制 // 检查总数量是否超出限制
BusBillofquantities billofquantities = busBillofquantitiesService.getById(suppliespriceId); List<BusBillofquantities> busBillofquantities = busBillofquantitiesService.getBaseMapper().selectList(new LambdaQueryWrapper<BusBillofquantities>()
if (existingSum.add(batchSum).compareTo(billofquantities.getQuantity()) > 0) { .eq(BusBillofquantities::getProjectId, dto.getMrpBaseBo().getProjectId())
.eq(BusBillofquantities::getName, split[0])
.eq(BusBillofquantities::getSpecification, split[1]));
BigDecimal quantity = busBillofquantities.stream()
.map(BusBillofquantities::getQuantity)
.filter(Objects::nonNull)
.reduce(BigDecimal.ZERO, BigDecimal::add);
if (existingSum.add(batchSum).compareTo(quantity) > 0) {
// 找到超出限制的物料名称用于提示 // 找到超出限制的物料名称用于提示
String itemName = dto.getPlanList().stream() String itemName = dto.getPlanList().stream()
.filter(plan -> plan.getSuppliespriceId().equals(suppliespriceId)) .filter(item -> {
String name = item.getName() != null ? item.getName() : "";
String spec = item.getSpecification() != null ? item.getSpecification() : "";
return (name + "&&&" + spec).equals(biaoshi);
})
.findFirst() .findFirst()
.map(BusMaterialbatchdemandplanBo::getName) .map(item -> {
String name = item.getName() != null ? item.getName() : "";
String spec = item.getSpecification() != null ? item.getSpecification() : "";
return "名称:“"+name + "”,规格:“" + spec+"";
})
.orElse("未知物料"); .orElse("未知物料");
throw new ServiceException(itemName + "超出数量"); throw new ServiceException(itemName + "超出数量");
} }
@ -272,13 +297,23 @@ public class BusMrpBaseServiceImpl extends ServiceImpl<BusMrpBaseMapper, BusMrpB
@Override @Override
public Map<String, Object> remaining(Long suppliespriceId, Long mrpBaseId) { public Map<String, Object> remaining(Long projectId, String suppliespriceName, String specification, Long mrpBaseId) {
Map<String, Object> map = new HashMap<>(); Map<String, Object> map = new HashMap<>();
BusBillofquantities byId = busBillofquantitiesService.getById(suppliespriceId); List<BusBillofquantities> busBillofquantities = busBillofquantitiesService.getBaseMapper()
.selectList(new LambdaQueryWrapper<BusBillofquantities>()
.eq(BusBillofquantities::getProjectId, projectId)
.eq(BusBillofquantities::getName, suppliespriceName)
.eq(BusBillofquantities::getSpecification,specification));
BigDecimal quantity = busBillofquantities.stream()
.map(BusBillofquantities::getQuantity)
.filter(Objects::nonNull)
.reduce(BigDecimal.ZERO, BigDecimal::add);
// BusBillofquantities byId = busBillofquantitiesService.getById(suppliespriceId);
// 获取数据库中已有的数量 // 获取数据库中已有的数量
List<BusMaterialbatchdemandplan> existingList = planservice.list( List<BusMaterialbatchdemandplan> existingList = planservice.list(
Wrappers.lambdaQuery(BusMaterialbatchdemandplan.class) Wrappers.lambdaQuery(BusMaterialbatchdemandplan.class)
.eq(BusMaterialbatchdemandplan::getSuppliespriceId, suppliespriceId) .eq(BusMaterialbatchdemandplan::getName, suppliespriceName)
.eq(BusMaterialbatchdemandplan::getSpecification ,specification)
.ne(mrpBaseId!=null,BusMaterialbatchdemandplan::getMrpBaseId, mrpBaseId)// 排除当前批次 .ne(mrpBaseId!=null,BusMaterialbatchdemandplan::getMrpBaseId, mrpBaseId)// 排除当前批次
); );
BigDecimal reduce = BigDecimal.ZERO; BigDecimal reduce = BigDecimal.ZERO;
@ -287,11 +322,11 @@ public class BusMrpBaseServiceImpl extends ServiceImpl<BusMrpBaseMapper, BusMrpB
.map(BusMaterialbatchdemandplan::getDemandQuantity) .map(BusMaterialbatchdemandplan::getDemandQuantity)
.reduce(BigDecimal.ZERO, BigDecimal::add); .reduce(BigDecimal.ZERO, BigDecimal::add);
} }
map.put("remainingQuantity",byId.getQuantity().subtract(reduce)); map.put("remainingQuantity",quantity.subtract(reduce));
map.put("specification",byId.getSpecification()); map.put("specification",busBillofquantities.getFirst().getSpecification());
map.put("unit",byId.getUnit()); map.put("unit",busBillofquantities.getFirst().getUnit());
map.put("remark",byId.getRemark()); map.put("remark",busBillofquantities.getFirst().getRemark());
map.put("name",byId.getName()); map.put("name",busBillofquantities.getFirst().getName());
return map; return map;
} }

View File

@ -0,0 +1,60 @@
package org.dromara.contractor.controller.app;
import cn.dev33.satoken.annotation.SaCheckPermission;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.contractor.domain.SubUserSalaryDetail;
import org.dromara.contractor.domain.dto.usersalarydetail.SubUserSalaryDetailQueryReq;
import org.dromara.contractor.domain.dto.usersalaryperiod.SubConstructionUserSalaryDto;
import org.dromara.contractor.domain.vo.usersalarydetail.SubUserSalaryDetailVo;
import org.dromara.contractor.domain.vo.usersalaryperiod.SubConstructionUserSalaryVo;
import org.dromara.contractor.service.ISubUserSalaryDetailService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* app工资
*
* @author lilemy
* @date 2025-09-04
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/app/contractor/userSalaryDetail")
public class SubUserSalaryDetailAppController extends BaseController {
private final ISubUserSalaryDetailService subUserSalaryDetailService;
/**
* 工资分页
*/
@GetMapping("/salaryPageList")
public TableDataInfo<SubConstructionUserSalaryVo> salaryPageList( SubConstructionUserSalaryDto dto, PageQuery pageQuery) {
return subUserSalaryDetailService.salaryPageList(dto, pageQuery);
}
/**
* 工资详情
*/
@GetMapping("/detailList")
public R<List<SubUserSalaryDetail>> detailList( SubConstructionUserSalaryDto dto) {
return R.ok(subUserSalaryDetailService.detailList(dto));
}
}

View File

@ -100,4 +100,29 @@ public class SubConstructionUserSalaryVo implements Serializable {
*/ */
private String createTime; private String createTime;
private Long createBy;
/**
* 创建人
*/
@Translation(type = TransConstant.USER_ID_TO_NAME, mapper = "createBy")
private String createByName;
/**
* 发放时间
*/
private Long tim;
/**
* 班组id
*/
private Long teamId;
/**
* 班组名
*/
private String teamName;
} }

View File

@ -1,9 +1,13 @@
package org.dromara.contractor.mapper; package org.dromara.contractor.mapper;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
import org.dromara.contractor.domain.SubUserSalaryDetail; import org.dromara.contractor.domain.SubUserSalaryDetail;
import org.dromara.contractor.domain.vo.usersalarydetail.SubUserSalaryDetailVo; import org.dromara.contractor.domain.vo.usersalarydetail.SubUserSalaryDetailVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import java.util.List;
/** /**
* 员工每日工资Mapper接口 * 员工每日工资Mapper接口
* *
@ -12,4 +16,15 @@ import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
*/ */
public interface SubUserSalaryDetailMapper extends BaseMapperPlus<SubUserSalaryDetail, SubUserSalaryDetailVo> { public interface SubUserSalaryDetailMapper extends BaseMapperPlus<SubUserSalaryDetail, SubUserSalaryDetailVo> {
@Insert({
"<script>",
"INSERT INTO sub_user_salary_detail (project_id, team_id, user_id, user_name, report_date, work_hour, daily_salary, total_salary, create_by, update_by, remark) VALUES ",
"<foreach collection='list' item='item' separator=','>",
"(#{item.projectId}, #{item.teamId}, #{item.userId}, #{item.userName}, #{item.reportDate}, #{item.workHour}, #{item.dailySalary}, #{item.totalSalary}, #{userId}, #{userId}, #{item.remark})",
"</foreach>",
"</script>"
})
Boolean insertAll(@Param("list")List<SubUserSalaryDetail> list, @Param("userId") Long userId);
} }

View File

@ -935,7 +935,10 @@ public class SubConstructionUserServiceImpl extends ServiceImpl<SubConstructionU
constructionUserVo.setPostId(one.getPostId()); constructionUserVo.setPostId(one.getPostId());
} }
}else {
constructionUserVo.setTeamName(null);
} }
return constructionUserVo; return constructionUserVo;
}).toList(); }).toList();
constructionUserVoPage.setRecords(constructionUserVoList); constructionUserVoPage.setRecords(constructionUserVoList);

View File

@ -18,6 +18,7 @@ import jakarta.annotation.Resource;
import jakarta.servlet.ServletException; import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.pdfbox.io.IOUtils; import org.apache.pdfbox.io.IOUtils;
import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.Workbook;
@ -30,6 +31,7 @@ import org.dromara.common.core.utils.ObjectUtils;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.utils.IdCardEncryptorUtil; import org.dromara.common.utils.IdCardEncryptorUtil;
import org.dromara.contractor.domain.SubConstructionUser; import org.dromara.contractor.domain.SubConstructionUser;
import org.dromara.contractor.domain.SubUserSalaryDetail; import org.dromara.contractor.domain.SubUserSalaryDetail;
@ -54,17 +56,16 @@ import org.dromara.project.service.IBusAttendanceService;
import org.dromara.project.service.IBusProjectService; import org.dromara.project.service.IBusProjectService;
import org.dromara.project.service.IBusProjectTeamService; import org.dromara.project.service.IBusProjectTeamService;
import org.dromara.project.service.IBusWorkWageService; import org.dromara.project.service.IBusWorkWageService;
import org.dromara.project.service.impl.BusProjectTeamServiceImpl;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import software.amazon.awssdk.utils.CollectionUtils; import software.amazon.awssdk.utils.CollectionUtils;
import java.io.ByteArrayInputStream; import java.io.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.RoundingMode; import java.math.RoundingMode;
import java.net.URLEncoder; import java.net.URLEncoder;
@ -72,6 +73,7 @@ import java.time.LocalDate;
import java.time.YearMonth; import java.time.YearMonth;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.dromara.contractor.excel.SalaryExcelReader; import org.dromara.contractor.excel.SalaryExcelReader;
@ -83,6 +85,7 @@ import org.dromara.contractor.excel.SalaryExcelReader;
*/ */
@RequiredArgsConstructor @RequiredArgsConstructor
@Service @Service
@Slf4j
public class SubUserSalaryDetailServiceImpl extends ServiceImpl<SubUserSalaryDetailMapper, SubUserSalaryDetail> public class SubUserSalaryDetailServiceImpl extends ServiceImpl<SubUserSalaryDetailMapper, SubUserSalaryDetail>
implements ISubUserSalaryDetailService { implements ISubUserSalaryDetailService {
@ -272,14 +275,13 @@ public class SubUserSalaryDetailServiceImpl extends ServiceImpl<SubUserSalaryDet
@Override @Override
public TableDataInfo<SubConstructionUserSalaryVo> salaryPageList(SubConstructionUserSalaryDto dto, PageQuery pageQuery) { public TableDataInfo<SubConstructionUserSalaryVo> salaryPageList(SubConstructionUserSalaryDto dto, PageQuery pageQuery) {
String time = dto.getTime(); String time = dto.getTime();
YearMonth parse = YearMonth.parse(time, DateTimeFormatter.ofPattern("yyyy-MM")); YearMonth parse = YearMonth.parse(time, DateTimeFormatter.ofPattern("yyyy-MM"));
LocalDate start = parse.atDay(1); LocalDate start = parse.atDay(1);
LocalDate end = parse.atEndOfMonth(); LocalDate end = parse.atEndOfMonth();
QueryWrapper<SubUserSalaryDetail> queryWrapper = new QueryWrapper<>(); QueryWrapper<SubUserSalaryDetail> queryWrapper = new QueryWrapper<>();
queryWrapper.select("user_id", "SUM(work_hour) as workHour", "SUM(total_salary) as totalSalary","max(create_time) as createTime") queryWrapper.select("user_id","max(team_id) as teamId","max(create_by) as createBy", "SUM(work_hour) as workHour", "SUM(total_salary) as totalSalary","max(create_time) as createTime")
.eq("project_id", dto.getProjectId()) .eq("project_id", dto.getProjectId())
.between("report_date", start, end) .between("report_date", start, end)
.eq(dto.getTeamId()!=null,"team_id", dto.getTeamId()) .eq(dto.getTeamId()!=null,"team_id", dto.getTeamId())
@ -290,15 +292,24 @@ public class SubUserSalaryDetailServiceImpl extends ServiceImpl<SubUserSalaryDet
Page<SubUserSalaryDetail> result = this.page(pageQuery.build(), queryWrapper); Page<SubUserSalaryDetail> result = this.page(pageQuery.build(), queryWrapper);
List<SubUserSalaryDetail> records = result.getRecords(); List<SubUserSalaryDetail> records = result.getRecords();
List<Long> userIds = records.stream().map(SubUserSalaryDetail::getUserId).toList(); List<Long> userIds = records.stream().map(SubUserSalaryDetail::getUserId).toList();
Map<Long, SubConstructionUser> collect = new HashMap<>(); Map<Long, SubConstructionUser> collect = new HashMap<>();
if(CollectionUtil.isNotEmpty(userIds)){ if(CollectionUtil.isNotEmpty(userIds)){
List<SubConstructionUser> subConstructionUsers = constructionUserService.list(Wrappers.lambdaQuery(SubConstructionUser.class) List<SubConstructionUser> subConstructionUsers = constructionUserService.list(Wrappers.lambdaQuery(SubConstructionUser.class)
.in(SubConstructionUser::getSysUserId, userIds)); .in(SubConstructionUser::getSysUserId, userIds));
collect = subConstructionUsers.stream().collect(Collectors.toMap(SubConstructionUser::getSysUserId, vo -> vo)); collect = subConstructionUsers.stream().collect(Collectors.toMap(SubConstructionUser::getSysUserId, vo -> vo));
} }
Set<Long> teamIds = records.stream().map(SubUserSalaryDetail::getTeamId).collect(Collectors.toSet());
Map<Long, BusProjectTeam> teamMap = new HashMap<>();
if(CollectionUtil.isNotEmpty(teamIds)){
List<BusProjectTeam> busProjectTeams = projectTeamService.listByIds(teamIds);
teamMap = busProjectTeams.stream().collect(Collectors.toMap(BusProjectTeam::getId, vo -> vo));
}
ArrayList<SubConstructionUserSalaryVo> vos = new ArrayList<>(); ArrayList<SubConstructionUserSalaryVo> vos = new ArrayList<>();
for (SubUserSalaryDetail detail : records) { for (SubUserSalaryDetail detail : records) {
SubConstructionUserSalaryVo vo = new SubConstructionUserSalaryVo(); SubConstructionUserSalaryVo vo = new SubConstructionUserSalaryVo();
vo.setCreateBy(detail.getCreateBy());
vo.setId(detail.getId()); vo.setId(detail.getId());
vo.setTime(dto.getTime()); vo.setTime(dto.getTime());
vo.setTotalSalary(detail.getTotalSalary()); vo.setTotalSalary(detail.getTotalSalary());
@ -306,6 +317,7 @@ public class SubUserSalaryDetailServiceImpl extends ServiceImpl<SubUserSalaryDet
vo.setWorkDay(detail.getWorkHour()); vo.setWorkDay(detail.getWorkHour());
vo.setProjectId(dto.getProjectId()); vo.setProjectId(dto.getProjectId());
vo.setUserId(detail.getUserId()); vo.setUserId(detail.getUserId());
vo.setTeamId(detail.getTeamId());
SubConstructionUser constructionUser = collect.get(detail.getUserId()); SubConstructionUser constructionUser = collect.get(detail.getUserId());
if(constructionUser != null){ if(constructionUser != null){
if(constructionUser.getSfzNumber() != null){ if(constructionUser.getSfzNumber() != null){
@ -314,6 +326,11 @@ public class SubUserSalaryDetailServiceImpl extends ServiceImpl<SubUserSalaryDet
vo.setUserName(constructionUser.getUserName()); vo.setUserName(constructionUser.getUserName());
vo.setYhkNumber(constructionUser.getYhkNumber()); vo.setYhkNumber(constructionUser.getYhkNumber());
vo.setYhkOpeningBank(constructionUser.getYhkOpeningBank()); vo.setYhkOpeningBank(constructionUser.getYhkOpeningBank());
}
BusProjectTeam projectTeam = teamMap.get(detail.getTeamId());
if(projectTeam != null){
vo.setTeamName(projectTeam.getTeamName());
} }
vos.add(vo); vos.add(vo);
} }
@ -323,157 +340,162 @@ public class SubUserSalaryDetailServiceImpl extends ServiceImpl<SubUserSalaryDet
@Override @Override
public void export(HttpServletResponse response, SubConstructionUserSalaryDto dto) throws IOException { public void export(HttpServletResponse response, SubConstructionUserSalaryDto dto) throws IOException {
// 1. 查询项目和人员数据 // 1. 查询项目信息并校验
BusProject project = projectService.getById(dto.getProjectId()); BusProject project = projectService.getById(dto.getProjectId());
if (project == null) { if (project == null) {
throw new ServiceException("项目不存在"); throw new ServiceException("项目不存在");
} }
// 2. 解析年月和查询薪资明细
// 2. 解析时间参数
YearMonth yearMonth = YearMonth.parse(dto.getTime(), DateTimeFormatter.ofPattern("yyyy-MM")); YearMonth yearMonth = YearMonth.parse(dto.getTime(), DateTimeFormatter.ofPattern("yyyy-MM"));
LocalDate start = yearMonth.atDay(1); LocalDate start = yearMonth.atDay(1);
LocalDate end = yearMonth.atEndOfMonth(); LocalDate end = yearMonth.atEndOfMonth();
// 3. 查询薪资明细数据
QueryWrapper<SubUserSalaryDetail> queryWrapper = new QueryWrapper<>(); QueryWrapper<SubUserSalaryDetail> queryWrapper = new QueryWrapper<>();
queryWrapper.select("user_id", "SUM(work_hour) as workHour", "SUM(total_salary) as totalSalary","max(create_time) as createTime") queryWrapper.select("user_id", "SUM(work_hour) as workHour", "SUM(total_salary) as totalSalary")
.eq("project_id", dto.getProjectId()) .eq("project_id", dto.getProjectId())
.between("report_date", start, end) .between("report_date", start, end)
.eq(dto.getUserId()!=null,"user_id", dto.getUserId()) .eq(dto.getUserId() != null, "user_id", dto.getUserId())
.eq(dto.getTeamId()!=null,"team_id", dto.getTeamId()) .eq(dto.getTeamId() != null, "team_id", dto.getTeamId())
.like(StringUtils.isNotBlank(dto.getUserName()),"user_name", dto.getUserName()) .like(StringUtils.isNotBlank(dto.getUserName()), "user_name", dto.getUserName())
.groupBy("user_id"); .groupBy("user_id");
List<SubUserSalaryDetail> salaryDetailsList= baseMapper.selectList(queryWrapper); List<SubUserSalaryDetail> salaryDetailsList = baseMapper.selectList(queryWrapper);
if (salaryDetailsList.isEmpty()) { if (salaryDetailsList.isEmpty()) {
throw new ServiceException("暂无数据"); throw new ServiceException("暂无数据");
} }
Map<Long, SubUserSalaryDetail> map = salaryDetailsList.stream().collect(Collectors.toMap(SubUserSalaryDetail::getUserId, vo -> vo));
List<Long> userIds = salaryDetailsList.stream().map(SubUserSalaryDetail::getUserId).toList();
// 4. 构建薪资数据映射
Map<Long, SubUserSalaryDetail> salaryMap = salaryDetailsList.stream()
.collect(Collectors.toMap(SubUserSalaryDetail::getUserId, vo -> vo));
// 5. 批量查询用户信息(只查必要字段)
Set<Long> userIds = salaryMap.keySet();
List<SubConstructionUser> userList = constructionUserService.lambdaQuery() List<SubConstructionUser> userList = constructionUserService.lambdaQuery()
.select(SubConstructionUser::getSysUserId, SubConstructionUser::getTeamId, SubConstructionUser::getUserName
, SubConstructionUser::getYhkNumber, SubConstructionUser::getYhkOpeningBank)
.in(SubConstructionUser::getSysUserId, userIds) .in(SubConstructionUser::getSysUserId, userIds)
.list(); .list();
// 3. 设置响应头(下载文件配置 // 6. 预处理班组信息解决null键问题
// 使用-1L作为临时键替代null避免groupingBy出现null键错误
Map<Long, List<SubConstructionUser>> teamUsersMap = userList.stream()
.collect(Collectors.groupingBy(user -> {
Long teamId = user.getTeamId();
return teamId != null ? teamId : -1L; // 关键修复null键映射为-1
}));
// 提取实际班组ID排除-1并计算数量
List<Long> teamIds = teamUsersMap.keySet().stream()
.filter(id -> !id.equals(-1L))
.collect(Collectors.toList());
int actualTeamCount = teamIds.size();
// 动态计算初始容量避免HashMap扩容
Map<Long, String> teamNameMap = new HashMap<>((int) Math.ceil(actualTeamCount * 1.5));
// 批量查询班组名称
if (!teamIds.isEmpty()) {
List<BusProjectTeam> teams = projectTeamService.lambdaQuery()
.select(BusProjectTeam::getId, BusProjectTeam::getTeamName)
.in(BusProjectTeam::getId, teamIds)
.list();
teams.forEach(team -> teamNameMap.put(team.getId(), team.getTeamName()));
}
// 7. 检查班组数量阈值
int maxSheetThreshold = 100; // 根据实际情况调整
if (actualTeamCount > maxSheetThreshold) {
log.warn("班组数量过多({}个可能导致Excel文件过大或导出缓慢", actualTeamCount);
// 若需严格限制,可在此处抛出异常
// throw new ServiceException("班组数量超过限制,最多支持" + maxSheetThreshold + "个");
}
// 8. 设置响应头
setResponseHeader(response, yearMonth.getYear(), yearMonth.getMonthValue()); setResponseHeader(response, yearMonth.getYear(), yearMonth.getMonthValue());
// 5. 读取模板文件到字节数组(复用模板 // 9. 读取模板文件(缓冲流优化
byte[] templateBytes; byte[] templateBytes;
try (InputStream templateStream = Thread.currentThread().getContextClassLoader() try (InputStream templateStream = Thread.currentThread().getContextClassLoader()
.getResourceAsStream("excelTemplate/salary_template.xlsx")) { .getResourceAsStream("excelTemplate/salary_template.xlsx");
BufferedInputStream bis = new BufferedInputStream(templateStream);
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
if (templateStream == null) { if (templateStream == null) {
throw new ServiceException("模板文件不存在路径excelTemplate/salary_template.xlsx"); throw new ServiceException("模板文件不存在路径excelTemplate/salary_template.xlsx");
} }
templateBytes = IOUtils.toByteArray(templateStream);
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = bis.read(buffer)) != -1) {
baos.write(buffer, 0, bytesRead);
}
templateBytes = baos.toByteArray();
} }
// 6. POI 处理多 sheet核心:克隆模板 + 命名 // 10. POI处理多Sheet核心逻辑
// 存储 <sheet名称, sheet对象> 映射,方便后续 EasyExcel 匹配
Map<String, Sheet> sheetMap = new LinkedHashMap<>();
try (Workbook workbook = WorkbookFactory.create(new ByteArrayInputStream(templateBytes)); try (Workbook workbook = WorkbookFactory.create(new ByteArrayInputStream(templateBytes));
ByteArrayOutputStream bos = new ByteArrayOutputStream()) { ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
// 校验模板是否有有效 sheet
if (workbook.getNumberOfSheets() == 0) { if (workbook.getNumberOfSheets() == 0) {
throw new ServiceException("模板文件中无任何 sheet 页,请检查模板"); throw new ServiceException("模板文件中无任何sheet页请检查模板");
} }
// 6.1 预处理班组信息获取班组ID + 班组名称映射) int originalSheetIndex = 0;
List<Long> teamIds = userList.stream()
.map(SubConstructionUser::getTeamId)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
Map<Long, String> teamNameMap = new HashMap<>();
if (!teamIds.isEmpty()) {
List<BusProjectTeam> teams = projectTeamService.lambdaQuery()
.in(BusProjectTeam::getId, teamIds)
.list();
teamNameMap = teams.stream()
.collect(Collectors.toMap(
BusProjectTeam::getId,
BusProjectTeam::getTeamName,
(oldVal, newVal) -> newVal // 避免重复ID冲突
));
}
// 6.2 克隆模板:为每个班组生成 sheet(关键修正) // 10.1 为每个班组创建sheet
for (Long teamId : teamIds) { for (Long teamId : teamIds) {
// ① 克隆原始模板 sheet返回克隆后的 Sheet 对象) Sheet newSheet = workbook.cloneSheet(originalSheetIndex);
Sheet newSheet = workbook.cloneSheet(0);
// ② 生成班组名称(避免空名称)
String teamName = teamNameMap.getOrDefault(teamId, "未知班组_" + teamId); String teamName = teamNameMap.getOrDefault(teamId, "未知班组_" + teamId);
// ③ 给新 sheet 命名(通过 sheet 对象获取索引)
int newSheetIndex = workbook.getSheetIndex(newSheet); int newSheetIndex = workbook.getSheetIndex(newSheet);
workbook.setSheetName(newSheetIndex, teamName); workbook.setSheetName(newSheetIndex, teamName);
// ④ 存入映射,方便后续填充
sheetMap.put(teamName, newSheet);
} }
// 6.3 克隆模板:处理无班组数据(如果需要 // 10.2 处理无班组数据(使用-1对应的用户列表
boolean needNoTeamSheet = dto.getTeamId() == null; boolean needNoTeamSheet = dto.getTeamId() == null && teamUsersMap.containsKey(-1L);
if (needNoTeamSheet) { if (needNoTeamSheet) {
Sheet noTeamSheet = workbook.cloneSheet(0); Sheet noTeamSheet = workbook.cloneSheet(originalSheetIndex);
String noTeamName = "无班组";
int noTeamSheetIndex = workbook.getSheetIndex(noTeamSheet); int noTeamSheetIndex = workbook.getSheetIndex(noTeamSheet);
workbook.setSheetName(noTeamSheetIndex, noTeamName); workbook.setSheetName(noTeamSheetIndex, "无班组");
sheetMap.put(noTeamName, noTeamSheet);
} }
// 6.4 删除原始模板 sheet(避免最终文件保留空模板) // 10.3 移除原始模板sheet
workbook.removeSheetAt(0); workbook.removeSheetAt(originalSheetIndex);
// 6.5 将 POI 处理后的 workbook 写入字节数组,供 EasyExcel 使用 // 10.4 写入并关闭workbook
workbook.write(bos); workbook.write(bos);
workbook.close();
byte[] workbookBytes = bos.toByteArray(); byte[] workbookBytes = bos.toByteArray();
// 7. EasyExcel 填充数据(按 sheet 映射顺序填充) // 11. EasyExcel填充数据
try (InputStream workbookStream = new ByteArrayInputStream(workbookBytes); try (InputStream workbookStream = new ByteArrayInputStream(workbookBytes);
ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()) ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream())
.withTemplate(workbookStream) .withTemplate(workbookStream)
.build()) { .build()) {
// 7.1 填充“班组”sheet 数据 FillConfig fillConfig = FillConfig.builder().forceNewRow(true).build();
for (Map.Entry<String, Sheet> entry : sheetMap.entrySet()) {
String sheetName = entry.getKey();
// 跳过“无班组”sheet后续单独处理
if ("无班组".equals(sheetName)) {
continue;
}
// 根据 sheet 名称反查班组ID用于过滤数据
Long teamId = teamNameMap.entrySet().stream()
.filter(e -> sheetName.equals(e.getValue()))
.map(Map.Entry::getKey)
.findFirst()
.orElse(null);
// 创建 EasyExcel 的 WriteSheet通过名称匹配 sheet // 11.1 填充各班组数据
WriteSheet writeSheet = EasyExcel.writerSheet(sheetName).build(); for (Long teamId : teamIds) {
FillConfig fillConfig = FillConfig.builder() String teamName = teamNameMap.getOrDefault(teamId, "未知班组_" + teamId);
.forceNewRow(true) // 强制新增行,不覆盖模板内容 WriteSheet writeSheet = EasyExcel.writerSheet(teamName).build();
.build(); // 使用提前分组好的数据
excelWriter.fill(getTeamData(teamUsersMap.get(teamId), salaryMap, teamId), fillConfig, writeSheet);
// 填充列表数据和汇总数据 excelWriter.fill(getTeamSummary(project, teamName, yearMonth), writeSheet);
excelWriter.fill(getTeamData(userList, map, teamId), fillConfig, writeSheet);
excelWriter.fill(getTeamSummary(project, teamNameMap.get(teamId), yearMonth), writeSheet);
} }
// 7.2 填充无班组”sheet 数据 // 11.2 填充无班组数据
if (needNoTeamSheet) { if (needNoTeamSheet) {
WriteSheet noTeamSheet = EasyExcel.writerSheet("无班组").build(); WriteSheet noTeamSheet = EasyExcel.writerSheet("无班组").build();
FillConfig fillConfig = FillConfig.builder().forceNewRow(true).build(); excelWriter.fill(getTeamData(teamUsersMap.get(-1L), salaryMap, null), fillConfig, noTeamSheet);
excelWriter.fill(getTeamData(userList, map, null), fillConfig, noTeamSheet);
excelWriter.fill(getTeamSummary(project, null, yearMonth), noTeamSheet); excelWriter.fill(getTeamSummary(project, null, yearMonth), noTeamSheet);
} }
} }
} }
} }
/** /**
* 设置响应头信息 * 设置响应头信息
*/ */
@ -492,15 +514,15 @@ public class SubUserSalaryDetailServiceImpl extends ServiceImpl<SubUserSalaryDet
private List<SubConstructionUserSalaryVo> getTeamData(List<SubConstructionUser> rows, private List<SubConstructionUserSalaryVo> getTeamData(List<SubConstructionUser> rows,
Map<Long, SubUserSalaryDetail> map,Long teamId){ Map<Long, SubUserSalaryDetail> map,Long teamId){
List<SubConstructionUser> list1 ; // List<SubConstructionUser> list1 ;
if(teamId == null){ // if(teamId == null){
list1 = rows.stream().filter(row -> row.getTeamId() == null && Arrays.asList("1","2").contains(row.getExitStatus())).toList(); // list1 = rows.stream().filter(row -> row.getTeamId() == null && Arrays.asList("1","2").contains(row.getExitStatus())).toList();
}else { // }else {
list1 = rows.stream().filter(row -> teamId.equals(row.getTeamId())).toList(); // list1 = rows.stream().filter(row -> teamId.equals(row.getTeamId())).toList();
} // }
ArrayList<SubConstructionUserSalaryVo> vos = new ArrayList<>(); ArrayList<SubConstructionUserSalaryVo> vos = new ArrayList<>();
int i =1; int i =1;
for (SubConstructionUser row : list1) { for (SubConstructionUser row : rows) {
SubConstructionUserSalaryVo vo = new SubConstructionUserSalaryVo(); SubConstructionUserSalaryVo vo = new SubConstructionUserSalaryVo();
BeanUtil.copyProperties(row,vo); BeanUtil.copyProperties(row,vo);
vo.setOrder(i); vo.setOrder(i);
@ -525,19 +547,21 @@ public class SubUserSalaryDetailServiceImpl extends ServiceImpl<SubUserSalaryDet
@Override @Override
@Transactional(rollbackFor = Exception.class)
public void importData(MultipartFile file, String month) { public void importData(MultipartFile file, String month) {
long startTime = System.currentTimeMillis();
// 1. 校验文件合法性 // 1. 校验文件合法性
if (file.isEmpty()) { if (file.isEmpty()) {
throw new IllegalArgumentException("上传的Excel文件不能为空"); throw new IllegalArgumentException("上传的Excel文件不能为空");
} }
// 校验文件格式仅允许xlsx/xls
String originalFilename = file.getOriginalFilename(); String originalFilename = file.getOriginalFilename();
if (originalFilename == null || !(originalFilename.endsWith(".xlsx"))) { if (originalFilename == null || !(originalFilename.endsWith(".xlsx"))) {
throw new IllegalArgumentException("仅支持上传Excel文件.xlsx 格式)!"); throw new IllegalArgumentException("仅支持上传Excel文件.xlsx 格式)!");
} }
List<DynamicSalaryData> dataList; List<DynamicSalaryData> dataList;
try { try {
// 2. 将 MultipartFile 转为 InputStream传给工具类解析
dataList = SalaryExcelReader.readAllAttendanceByStream(file.getInputStream(), month); dataList = SalaryExcelReader.readAllAttendanceByStream(file.getInputStream(), month);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException("Excel流解析失败" + e.getMessage(), e); throw new RuntimeException("Excel流解析失败" + e.getMessage(), e);
@ -546,42 +570,79 @@ public class SubUserSalaryDetailServiceImpl extends ServiceImpl<SubUserSalaryDet
throw new ServiceException("未读取到数据"); throw new ServiceException("未读取到数据");
} }
Map<String, Map<String, Double>> dataMap = dataList.stream().collect(Collectors.toMap(vo -> idCardEncryptorUtil.encrypt(vo.getIdCard()), DynamicSalaryData::getDailyAttendance)); // 2. 数据预处理 - 使用并行流提高处理速度
Set<String> cards = dataMap.keySet(); Map<String, Map<String, Double>> dataMap = dataList.parallelStream()
.collect(Collectors.toMap(
vo -> idCardEncryptorUtil.encrypt(vo.getIdCard()),
DynamicSalaryData::getDailyAttendance
));
Set<String> cards = dataMap.keySet();
month = dataList.getFirst().getMonth(); month = dataList.getFirst().getMonth();
YearMonth parse = YearMonth.parse(month, DateTimeFormatter.ofPattern("yyyy-MM")); YearMonth parse = YearMonth.parse(month, DateTimeFormatter.ofPattern("yyyy-MM"));
LocalDate start = parse.atDay(1); LocalDate start = parse.atDay(1);
LocalDate end = parse.atEndOfMonth(); LocalDate end = parse.atEndOfMonth();
//人员数据 // 3. 分批处理人员数据 - 避免一次性加载大量数据
List<SubConstructionUser> list = constructionUserService.list(Wrappers.<SubConstructionUser>lambdaQuery() List<SubConstructionUser> allUsers = new ArrayList<>();
.in(SubConstructionUser::getSfzNumber, cards)); int batchSize = 1000; // 批量查询大小
List<Long> userIds = list.stream().map(SubConstructionUser::getSysUserId).toList(); // 分批查询用户数据
//考勤数据 List<List<String>> cardBatches = new ArrayList<>();
List<BusAttendance> attendanceList = attendanceService List<String> cardList = new ArrayList<>(cards);
.list(Wrappers.<BusAttendance>lambdaQuery() for (int i = 0; i < cardList.size(); i += batchSize) {
.in(BusAttendance::getUserId, userIds) int endIndex = Math.min(i + batchSize, cardList.size());
.between(BusAttendance::getClockDate, start, end) cardBatches.add(cardList.subList(i, endIndex));
}
for (List<String> batchCards : cardBatches) {
List<SubConstructionUser> batchUsers = constructionUserService.list(
Wrappers.<SubConstructionUser>lambdaQuery()
.in(SubConstructionUser::getSfzNumber, batchCards)
); );
// 将 attendanceList 转换为 Map<String, BigDecimal> 格式 allUsers.addAll(batchUsers);
Map<String, BigDecimal> attendanceSalaryMap = new HashMap<>(); }
// 按 userId+日期 分组只保留每组的第一条记录的salary if (CollUtil.isEmpty(allUsers)) {
attendanceList.stream() throw new ServiceException("未找到匹配的人员信息");
.collect(Collectors.groupingBy( }
attendance -> attendance.getUserId() + "_" + attendance.getClockDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")),
Collectors.toList()
))
.forEach((key, dateList) -> attendanceSalaryMap.put(key, dateList.getFirst().getSalary()));
// 4. 分批处理考勤数据 - 避免内存溢出
List<Long> userIds = allUsers.stream()
.map(SubConstructionUser::getSysUserId)
.distinct()
.collect(Collectors.toList());
List<SubUserSalaryDetail> addList = new ArrayList<>(); Map<String, BigDecimal> attendanceSalaryMap = new ConcurrentHashMap<>();
for(SubConstructionUser constructionUser : list){
Map<String, Double> stringIntegerMap = dataMap.get(constructionUser.getSfzNumber()); // 分批查询考勤数据
if(stringIntegerMap != null){ for (int i = 0; i < userIds.size(); i += batchSize) {
for(Map.Entry<String, Double> entry : stringIntegerMap.entrySet()){ int endIndex = Math.min(i + batchSize, userIds.size());
List<Long> batchUserIds = userIds.subList(i, endIndex);
List<BusAttendance> batchAttendanceList = attendanceService.list(
Wrappers.<BusAttendance>lambdaQuery()
.in(BusAttendance::getUserId, batchUserIds)
.between(BusAttendance::getClockDate, start, end)
);
// 并行处理考勤数据
batchAttendanceList.parallelStream()
.collect(Collectors.groupingBy(
attendance -> attendance.getUserId() + "_" + attendance.getClockDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")),
Collectors.toList()
))
.forEach((key, dateList) -> attendanceSalaryMap.put(key, dateList.getFirst().getSalary()));
}
// 5. 批量构建数据 - 使用并行处理
List<SubUserSalaryDetail> allAddList = Collections.synchronizedList(new ArrayList<>());
// 并行处理用户数据构建
allUsers.parallelStream().forEach(constructionUser -> {
Map<String, Double> userAttendanceMap = dataMap.get(constructionUser.getSfzNumber());
if(userAttendanceMap != null){
userAttendanceMap.entrySet().parallelStream().forEach(entry -> {
String key = entry.getKey(); String key = entry.getKey();
Double value = entry.getValue(); Double value = entry.getValue();
@ -600,16 +661,35 @@ public class SubUserSalaryDetailServiceImpl extends ServiceImpl<SubUserSalaryDet
} }
subUserSalaryDetail.setDailySalary(bigDecimal); subUserSalaryDetail.setDailySalary(bigDecimal);
subUserSalaryDetail.setTotalSalary(bigDecimal.multiply(new BigDecimal(value.toString()))); subUserSalaryDetail.setTotalSalary(bigDecimal.multiply(new BigDecimal(value.toString())));
addList.add(subUserSalaryDetail);
} allAddList.add(subUserSalaryDetail);
});
}
});
// 6. 批量删除和插入 - 分批处理避免数据库压力
if (CollUtil.isNotEmpty(allAddList)) {
// 批量删除现有数据
for (int i = 0; i < userIds.size(); i += batchSize) {
int endIndex = Math.min(i + batchSize, userIds.size());
List<Long> deleteUserIds = userIds.subList(i, endIndex);
baseMapper.delete(Wrappers.<SubUserSalaryDetail>lambdaQuery()
.in(SubUserSalaryDetail::getUserId, deleteUserIds)
.between(SubUserSalaryDetail::getReportDate, start, end)
);
} }
// 批量插入新数据 - 使用自定义批量插入方法
for (int i = 0; i < allAddList.size(); i += batchSize) {
int endIndex = Math.min(i + batchSize, allAddList.size());
List<SubUserSalaryDetail> insertBatch = allAddList.subList(i, endIndex);
baseMapper.insertAll(insertBatch,LoginHelper.getUserId());
}
} }
baseMapper.delete(Wrappers.<SubUserSalaryDetail>lambdaQuery()
.in(SubUserSalaryDetail::getUserId, userIds) long endTime = System.currentTimeMillis();
.between(SubUserSalaryDetail::getReportDate, start, end) log.info("工资数据导入完成,耗时: {} ms处理数据: {} 条", (endTime - startTime), allAddList.size());
);
baseMapper.insertBatch(addList);
} }
@ -626,6 +706,7 @@ public class SubUserSalaryDetailServiceImpl extends ServiceImpl<SubUserSalaryDet
wrapper.eq( dto.getTeamId()!=null,SubUserSalaryDetail::getTeamId, dto.getTeamId()); wrapper.eq( dto.getTeamId()!=null,SubUserSalaryDetail::getTeamId, dto.getTeamId());
wrapper.eq(dto.getUserId()!=null,SubUserSalaryDetail::getUserId, dto.getUserId()); wrapper.eq(dto.getUserId()!=null,SubUserSalaryDetail::getUserId, dto.getUserId());
wrapper.between(SubUserSalaryDetail::getReportDate,start, end); wrapper.between(SubUserSalaryDetail::getReportDate,start, end);
wrapper.orderByAsc(SubUserSalaryDetail::getReportDate);
return this.list(wrapper); return this.list(wrapper);
} }

View File

@ -0,0 +1,70 @@
package org.dromara.cory.controller.app;
import cn.dev33.satoken.annotation.SaCheckPermission;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.cory.domain.bo.BusContactnoticeBo;
import org.dromara.cory.domain.dto.BusContactnoticeAppDto;
import org.dromara.cory.domain.vo.BusContactnoticeVo;
import org.dromara.cory.service.IBusContactnoticeService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* APP联系单
*
* @author Lion Li
* @date 2025-07-03
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/app/cory/contactnotice")
public class BusContactnoticeAppController extends BaseController {
private final IBusContactnoticeService busContactnoticeService;
/**
* 查询联系单列表
*/
@GetMapping("/list")
public TableDataInfo<BusContactnoticeVo> list(BusContactnoticeAppDto dto, PageQuery pageQuery) {
return busContactnoticeService.queryAppPageList(dto, pageQuery);
}
/**
* 获取联系单详细信息
*
* @param id 主键
*/
@GetMapping("/{id}")
public R<BusContactnoticeVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(busContactnoticeService.queryById(id));
}
/**
* 新增联系单
*/
@Log(title = "联系单", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<BusContactnoticeVo> add(@Validated(AddGroup.class) @RequestBody BusContactnoticeBo bo) {
return R.ok(busContactnoticeService.insertByBo(bo));
}
}

View File

@ -0,0 +1,39 @@
package org.dromara.cory.domain.dto;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.core.validate.QueryGroup;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.cory.domain.BusContactnotice;
import java.util.List;
/**
* 联系单业务对象 bus_contactnotice
*
* @author Lion Li
* @date 2025-07-03
*/
@Data
public class BusContactnoticeAppDto extends BaseEntity {
/**
* 项目ID
*/
private Long projectId;
/**
* 模板类型
*/
private Long type;
/**
* 查询参数
*/
private String queryParam;
}

View File

@ -3,6 +3,7 @@ package org.dromara.cory.service;
import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.cory.domain.bo.BusContactnoticeBo; import org.dromara.cory.domain.bo.BusContactnoticeBo;
import org.dromara.cory.domain.dto.BusContactnoticeAppDto;
import org.dromara.cory.domain.vo.BusContactnoticeVo; import org.dromara.cory.domain.vo.BusContactnoticeVo;
import java.util.Collection; import java.util.Collection;
@ -65,4 +66,9 @@ public interface IBusContactnoticeService {
* @return 是否删除成功 * @return 是否删除成功
*/ */
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid); Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
/**
* app列表
*/
TableDataInfo<BusContactnoticeVo> queryAppPageList(BusContactnoticeAppDto dto, PageQuery pageQuery);
} }

View File

@ -1,7 +1,9 @@
package org.dromara.cory.service.impl; package org.dromara.cory.service.impl;
import cn.hutool.core.convert.Convert; import cn.hutool.core.convert.Convert;
import cn.hutool.core.stream.StreamUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@ -19,12 +21,14 @@ import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.cory.domain.BusContactnotice; import org.dromara.cory.domain.BusContactnotice;
import org.dromara.cory.domain.bo.BusContactnoticeBo; import org.dromara.cory.domain.bo.BusContactnoticeBo;
import org.dromara.cory.domain.dto.BusContactnoticeAppDto;
import org.dromara.cory.domain.vo.BusContactnoticeVo; import org.dromara.cory.domain.vo.BusContactnoticeVo;
import org.dromara.cory.mapper.BusContactnoticeMapper; import org.dromara.cory.mapper.BusContactnoticeMapper;
import org.dromara.cory.service.IBusContactnoticeService; import org.dromara.cory.service.IBusContactnoticeService;
import org.springframework.context.event.EventListener; import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@ -158,6 +162,28 @@ public class BusContactnoticeServiceImpl implements IBusContactnoticeService {
return baseMapper.deleteByIds(ids) > 0; return baseMapper.deleteByIds(ids) > 0;
} }
@Override
public TableDataInfo<BusContactnoticeVo> queryAppPageList(BusContactnoticeAppDto dto, PageQuery pageQuery) {
LambdaQueryWrapper<BusContactnotice> lqw = Wrappers.lambdaQuery();
lqw.eq(BusContactnotice::getProjectId, dto.getProjectId());
lqw.eq(BusContactnotice::getType, dto.getType());
// 处理查询参数(避免空值)
String queryParam = dto.getQueryParam();
if (StrUtil.isNotBlank(queryParam)) {
// 使用JSON函数+参数绑定避免SQL注入
lqw.and(wrapper -> wrapper
// 对volumeNumber进行模糊查询
.apply("detail->>'$.volumeNumber' LIKE CONCAT('%', {0}, '%')", queryParam)
// 或对volumeName进行模糊查询
.or()
.apply("detail->>'$.volumeName' LIKE CONCAT('%', {0}, '%')", queryParam)
);
}
Page<BusContactnoticeVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/** /**
* 总体流程监听(例如: 草稿,撤销,退回,作废,终止,已完成,单任务完成等) * 总体流程监听(例如: 草稿,撤销,退回,作废,终止,已完成,单任务完成等)
* 正常使用只需#processEvent.flowCode=='leave1' * 正常使用只需#processEvent.flowCode=='leave1'

View File

@ -0,0 +1,143 @@
package org.dromara.design.controller.app;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.enums.BusinessStatusEnum;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.design.domain.DesVolumeFile;
import org.dromara.design.domain.dto.designchange.DesDesignChangeCreateReq;
import org.dromara.design.domain.dto.designchange.DesDesignChangeQueryReq;
import org.dromara.design.domain.dto.designchange.DesDesignChangeUpdateReq;
import org.dromara.design.domain.vo.designchange.DesDesignChangeVo;
import org.dromara.design.domain.vo.volumecatalog.DesVolumeCatalogVo;
import org.dromara.design.service.IDesDesignChangeService;
import org.dromara.design.service.IDesVolumeCatalogService;
import org.dromara.design.service.IDesVolumeFileService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 设计变更管理
*
* @author lilemy
* @date 2025-07-03
*/
@Validated
@RestController
@RequestMapping("/app/design/designChange")
public class DesDesignChangeAppController extends BaseController {
@Resource
private IDesDesignChangeService desDesignChangeService;
@Resource
private IDesVolumeCatalogService desVolumeCatalogService;
@Resource
private IDesVolumeFileService desVolumeFileService;
/**
* 查询设计变更管理列表
*/
@SaCheckPermission("design:designChange:list")
@GetMapping("/list")
public TableDataInfo<DesDesignChangeVo> list(DesDesignChangeQueryReq req, PageQuery pageQuery) {
return desDesignChangeService.queryPageList(req, pageQuery);
}
/**
* 根据主键导出设计变更单
*/
@SaCheckPermission("design:designChange:exportWord")
@Log(title = "设计变更管理", businessType = BusinessType.EXPORT)
@PostMapping("/export/word")
public void exportWordById(@NotNull(message = "主键不能为空") Long id,
HttpServletResponse response) {
desDesignChangeService.exportWordById(id, response);
}
/**
* 获取设计变更管理详细信息
*
* @param id 主键
*/
@SaCheckPermission("design:designChange:query")
@GetMapping("/{id}")
public R<DesDesignChangeVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(desDesignChangeService.queryById(id));
}
/**
* 新增设计变更管理
*/
@SaCheckPermission("design:designChange:add")
@Log(title = "设计变更管理", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<DesDesignChangeVo> add(@Validated(AddGroup.class) @RequestBody DesDesignChangeCreateReq req) {
return R.ok(desDesignChangeService.insertByBo(req));
}
/**
* 修改设计变更管理
*/
@SaCheckPermission("design:designChange:edit")
@Log(title = "设计变更管理", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<DesDesignChangeVo> edit(@Validated(EditGroup.class) @RequestBody DesDesignChangeUpdateReq req) {
return R.ok(desDesignChangeService.updateByBo(req));
}
/**
* 删除设计变更管理
*
* @param ids 主键串
*/
@SaCheckPermission("design:designChange:remove")
@Log(title = "设计变更管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(desDesignChangeService.deleteByIds(List.of(ids)));
}
/**
* 查询选择卷册目录
*/
@SaCheckPermission("design:designChange:catalogList")
@GetMapping("/catalogList/{projectId}")
public R<List<DesVolumeCatalogVo>> catalogList(@PathVariable("projectId") Long projectId) {
return R.ok(desVolumeCatalogService.catalogList(projectId));
}
/**
* 查询选择目录下的蓝图
*/
@SaCheckPermission("design:designChange:blueprint")
@GetMapping("/blueprint/{volumeCatalogId}")
public R<List<DesVolumeFile>> blueprint(@PathVariable("volumeCatalogId") Long volumeCatalogId) {
List<DesVolumeFile> list = desVolumeFileService.list(Wrappers.lambdaQuery(DesVolumeFile.class)
.eq(DesVolumeFile::getVolumeCatalogId, volumeCatalogId)
.eq(DesVolumeFile::getType, DesVolumeFile.BLUEPRINT)
.eq(DesVolumeFile::getAuditStatus, BusinessStatusEnum.FINISH.getStatus()));
return R.ok(list);
}
}

View File

@ -0,0 +1,51 @@
package org.dromara.design.controller.app;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaIgnore;
import jakarta.annotation.Resource;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import org.dromara.common.core.domain.R;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.design.domain.bo.DesVolumeFileBo;
import org.dromara.design.domain.dto.volumefile.DesVolumeFileAppPageDto;
import org.dromara.design.domain.dto.volumefile.DesVolumeFileCreateReq;
import org.dromara.design.domain.vo.volumefile.DesVolumeFileAppVo;
import org.dromara.design.domain.vo.volumefile.DesVolumeFileCodeVo;
import org.dromara.design.domain.vo.volumefile.DesVolumeFileJoinVo;
import org.dromara.design.domain.vo.volumefile.DesVolumeFileVo;
import org.dromara.design.service.IDesVolumeFileService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* App图纸
*
* @author lilemy
* @date 2025-07-30
*/
@Validated
@RestController
@RequestMapping("/app/design/volumeFile")
public class DesVolumeFileAppController extends BaseController {
@Resource
private IDesVolumeFileService desVolumeFileService;
/**
* app图纸管理分页查询
*/
@GetMapping("/list")
public TableDataInfo<DesVolumeFileAppVo> list(DesVolumeFileAppPageDto dto, PageQuery pageQuery) {
return desVolumeFileService.queryAppPageList(dto, pageQuery);
}
}

View File

@ -42,4 +42,6 @@ public class CoryObtainTheListReq implements Serializable {
private String sid; private String sid;
private String name;
} }

View File

@ -0,0 +1,36 @@
package org.dromara.design.domain.dto.volumefile;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.design.domain.DesVolumeFile;
/**
* 卷册文件业务对象 des_volume_file
*
* @author lilemy
* @date 2025-08-19
*/
@Data
public class DesVolumeFileAppPageDto {
/**
* 项目ID
*/
private Long projectId;
/**
* 图纸类型(1-过程3-蓝图4-作废)
*/
private String type;
/**
* 查询参数
*/
private String queryParam;
}

View File

@ -20,6 +20,7 @@ public class DetailsMaterialAndEquipmentApprovalRes implements Serializable {
* 版本号 * 版本号
*/ */
private String versions; private String versions;
private String versionsName;
/** /**

View File

@ -0,0 +1,108 @@
package org.dromara.design.domain.vo.volumefile;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.common.translation.annotation.Translation;
import org.dromara.common.translation.constant.TransConstant;
import org.dromara.design.domain.DesVolumeFile;
import java.io.Serial;
import java.io.Serializable;
/**
* 卷册文件视图对象 des_volume_file
*
* @author lilemy
* @date 2025-07-30
*/
@Data
public class DesVolumeFileAppVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
private Long id;
/**
* 卷册目录ID
*/
private Long volumeCatalogId;
/**
* 卷册号
*/
private String volumeNumber;
/**
* 设计子项
*/
private String designSubitem;
/**
* 文件ID
*/
private Long fileId;
/**
* 文件名
*/
private String fileName;
/**
* 状态1正常 2变更
*/
private String status;
/**
* 专业
*/
private String specialty;
/**
* 专业
*/
@Translation(type = TransConstant.DICT_TYPE_TO_LABEL, mapper = "specialty",other = "des_user_major")
private String specialtyName;
/**
* 图纸类型(1-过程3-蓝图4-作废)
*/
private String type;
/**
* 版本号
*/
private String version;
/**
* 审核状态
*/
private String auditStatus;
/**
* 负责人
*/
private String principal;
/**
* 负责人
*/
@Translation(type = TransConstant.USER_ID_TO_NICKNAME, mapper = "principal")
private String principalName;
/**
* 路径
*/
private String url;
/**
* 资料名称
*/
private String documentName;
}

View File

@ -62,7 +62,7 @@ public class DesVolumeFileVo implements Serializable {
private String remark; private String remark;
/** /**
* 图纸类型(1-过程,2-变更,3-蓝图4-作废) * 图纸类型(1-过程3-蓝图4-作废)
*/ */
private String type; private String type;

View File

@ -5,6 +5,8 @@ import org.apache.ibatis.annotations.Param;
import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.design.domain.DesVolumeFile; import org.dromara.design.domain.DesVolumeFile;
import org.dromara.design.domain.bo.DesVolumeFileBo; import org.dromara.design.domain.bo.DesVolumeFileBo;
import org.dromara.design.domain.dto.volumefile.DesVolumeFileAppPageDto;
import org.dromara.design.domain.vo.volumefile.DesVolumeFileAppVo;
import org.dromara.design.domain.vo.volumefile.DesVolumeFileJoinVo; import org.dromara.design.domain.vo.volumefile.DesVolumeFileJoinVo;
import org.dromara.design.domain.vo.volumefile.DesVolumeFileVo; import org.dromara.design.domain.vo.volumefile.DesVolumeFileVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
@ -20,4 +22,7 @@ public interface DesVolumeFileMapper extends BaseMapperPlus<DesVolumeFile, DesVo
Page<DesVolumeFileJoinVo> queryJoinPageList(@Param("page")Page<DesVolumeFileJoinVo> page, @Param("bo")DesVolumeFileBo bo); Page<DesVolumeFileJoinVo> queryJoinPageList(@Param("page")Page<DesVolumeFileJoinVo> page, @Param("bo")DesVolumeFileBo bo);
Page<DesVolumeFileAppVo> queryAppPageList(@Param("page")Page<DesVolumeFileJoinVo> page, @Param("dto") DesVolumeFileAppPageDto dto);
} }

View File

@ -6,7 +6,9 @@ import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.design.domain.DesVolumeFile; import org.dromara.design.domain.DesVolumeFile;
import org.dromara.design.domain.bo.DesVolumeFileBo; import org.dromara.design.domain.bo.DesVolumeFileBo;
import org.dromara.design.domain.dto.volumefile.DesVolumeFileAppPageDto;
import org.dromara.design.domain.dto.volumefile.DesVolumeFileCreateReq; import org.dromara.design.domain.dto.volumefile.DesVolumeFileCreateReq;
import org.dromara.design.domain.vo.volumefile.DesVolumeFileAppVo;
import org.dromara.design.domain.vo.volumefile.DesVolumeFileCodeVo; import org.dromara.design.domain.vo.volumefile.DesVolumeFileCodeVo;
import org.dromara.design.domain.vo.volumefile.DesVolumeFileJoinVo; import org.dromara.design.domain.vo.volumefile.DesVolumeFileJoinVo;
import org.dromara.design.domain.vo.volumefile.DesVolumeFileVo; import org.dromara.design.domain.vo.volumefile.DesVolumeFileVo;
@ -93,4 +95,9 @@ public interface IDesVolumeFileService extends IService<DesVolumeFile> {
* 二维码获取卷册文件详细信息 * 二维码获取卷册文件详细信息
*/ */
DesVolumeFileCodeVo getCodeInfo(Long id); DesVolumeFileCodeVo getCodeInfo(Long id);
/**
* app分页查询
*/
TableDataInfo<DesVolumeFileAppVo> queryAppPageList(DesVolumeFileAppPageDto dto, PageQuery pageQuery);
} }

View File

@ -208,9 +208,20 @@ public class BusBillofquantitiesVersionsServiceImpl extends ServiceImpl<BusBillo
//1、获取到解析数据 //1、获取到解析数据
ExcelReader.ExcelData excelData = ExcelReader.readExcelFromMultipartFile(file); ExcelReader.ExcelData excelData = ExcelReader.readExcelFromMultipartFile(file);
//走正常的工程清单 //走正常的工程清单
if (!Objects.equals(bo.getWorkOrderType(), "3")) { // if (!Objects.equals(bo.getWorkOrderType(), "3")) {
// 2. 解析所有工作表转换为带父子关系的ExcelMaterial列表 解析所有Sheet数据按规则生成sid和pid // 2. 解析所有工作表转换为带父子关系的ExcelMaterial列表 解析所有Sheet数据按规则生成sid和pid
List<BusBillofquantities> allMaterials = new ArrayList<>(); List<BusBillofquantities> allMaterials = new ArrayList<>();
if (Objects.equals(bo.getWorkOrderType(), "3")){
ExcelReader.SheetData sheetData = excelData.getSheetDataList().getFirst();
String sheetName = sheetData.getSheetName();
List<List<String>> rowDataList = sheetData.getData();
// 构建当前Sheet的树形结构复用ExcelReader的buildTree方法
ExcelReader.TreeNode rootNode = ExcelReader.buildTree(rowDataList);
// 存储节点映射TreeNode → ExcelMaterial用于子节点关联父节点
Map<ExcelReader.TreeNode, BusBillofquantities> nodeMap = new HashMap<>();
// 递归遍历树形结构生成sid和pid
traverseTree(rootNode, nodeMap, allMaterials, sheetName, banBen);
}else {
for (ExcelReader.SheetData sheetData : excelData.getSheetDataList()) { for (ExcelReader.SheetData sheetData : excelData.getSheetDataList()) {
String sheetName = sheetData.getSheetName(); String sheetName = sheetData.getSheetName();
List<List<String>> rowDataList = sheetData.getData(); List<List<String>> rowDataList = sheetData.getData();
@ -221,35 +232,36 @@ public class BusBillofquantitiesVersionsServiceImpl extends ServiceImpl<BusBillo
// 递归遍历树形结构生成sid和pid // 递归遍历树形结构生成sid和pid
traverseTree(rootNode, nodeMap, allMaterials, sheetName, banBen); traverseTree(rootNode, nodeMap, allMaterials, sheetName, banBen);
} }
// 3. 批量插入数据库
for (BusBillofquantities allMaterial : allMaterials) {
allMaterial.setProjectId(bo.getProjectId());
}
boolean b = busBillofquantitiesService.saveBatch(allMaterials);
if (!b) {
throw new RuntimeException("导入失败");
}
return true;
} else {
// 跳过1行表头读取0到6列共7列映射到ExcelData实体类
List<MaterialsAndEquipmentExcelDto> dataList = ExcelDynamicReader.readExcel(
file, // 上传的文件
1, // 跳过1行表头
0, // 从第0列开始
5, // 到第5列结束
MaterialsAndEquipmentExcelDto.class // 目标实体类
);
List<BusBillofquantities> busBillofquantities = BeanUtil.copyToList(dataList, BusBillofquantities.class);
for (BusBillofquantities busBillofquantity : busBillofquantities) {
busBillofquantity.setProjectId(bo.getProjectId());
busBillofquantity.setVersions(banBen);
}
boolean b = busBillofquantitiesService.saveBatch(busBillofquantities);
if (!b) {
throw new RuntimeException("导入失败");
}
return true;
} }
// 3. 批量插入数据库
for (BusBillofquantities allMaterial : allMaterials) {
allMaterial.setProjectId(bo.getProjectId());
}
boolean b = busBillofquantitiesService.saveBatch(allMaterials);
if (!b) {
throw new RuntimeException("导入失败");
}
return true;
// } else {
// // 跳过1行表头读取0到6列共7列映射到ExcelData实体类
// List<MaterialsAndEquipmentExcelDto> dataList = ExcelDynamicReader.readExcel(
// file, // 上传的文件
// 1, // 跳过1行表头
// 0, // 从第0列开始
// 5, // 到第5列结束
// MaterialsAndEquipmentExcelDto.class // 目标实体类
// );
// List<BusBillofquantities> busBillofquantities = BeanUtil.copyToList(dataList, BusBillofquantities.class);
// for (BusBillofquantities busBillofquantity : busBillofquantities) {
// busBillofquantity.setProjectId(bo.getProjectId());
// busBillofquantity.setVersions(banBen);
// }
// boolean b = busBillofquantitiesService.saveBatch(busBillofquantities);
// if (!b) {
// throw new RuntimeException("导入失败");
// }
// return true;
// }
} }
@ -261,9 +273,9 @@ public class BusBillofquantitiesVersionsServiceImpl extends ServiceImpl<BusBillo
return Collections.emptyList(); return Collections.emptyList();
} }
// sheet为空表示不走下面的代码 // sheet为空表示不走下面的代码
if (StringUtils.isBlank(bo.getSheet())) { // if (StringUtils.isBlank(bo.getSheet())) {
return flatList; // return flatList;
} // }
// 2. 构建父子映射key=父节点pidvalue=该父节点的所有子节点 // 2. 构建父子映射key=父节点pidvalue=该父节点的所有子节点
Map<String, List<ObtainTheListRes>> parentMap = flatList.stream() Map<String, List<ObtainTheListRes>> parentMap = flatList.stream()
.collect(Collectors.groupingBy(ObtainTheListRes::getPid)); .collect(Collectors.groupingBy(ObtainTheListRes::getPid));
@ -288,6 +300,7 @@ public class BusBillofquantitiesVersionsServiceImpl extends ServiceImpl<BusBillo
throw new ServiceException("版本不存在"); throw new ServiceException("版本不存在");
} }
detailsMaterialAndEquipmentApprovalRes.setVersions(busBillofquantitiesVersions.getVersions()); detailsMaterialAndEquipmentApprovalRes.setVersions(busBillofquantitiesVersions.getVersions());
detailsMaterialAndEquipmentApprovalRes.setVersionsName(StringUtils.isNotBlank(busBillofquantitiesVersions.getVersionsName())?busBillofquantitiesVersions.getVersionsName(): null);
detailsMaterialAndEquipmentApprovalRes.setStatus(busBillofquantitiesVersions.getStatus()); detailsMaterialAndEquipmentApprovalRes.setStatus(busBillofquantitiesVersions.getStatus());
detailsMaterialAndEquipmentApprovalRes.setAuditData(busBillofquantities); detailsMaterialAndEquipmentApprovalRes.setAuditData(busBillofquantities);
return detailsMaterialAndEquipmentApprovalRes; return detailsMaterialAndEquipmentApprovalRes;
@ -337,7 +350,8 @@ public class BusBillofquantitiesVersionsServiceImpl extends ServiceImpl<BusBillo
public List<BusBillofquantitiesVo> obtainAllClassification(ObtainAllVersionNumbersReq bo) { public List<BusBillofquantitiesVo> obtainAllClassification(ObtainAllVersionNumbersReq bo) {
LambdaQueryWrapper<BusBillofquantitiesVersions> lqw = Wrappers.lambdaQuery(); LambdaQueryWrapper<BusBillofquantitiesVersions> lqw = Wrappers.lambdaQuery();
lqw.eq(bo.getProjectId() != null, BusBillofquantitiesVersions::getProjectId, bo.getProjectId()); lqw.eq(bo.getProjectId() != null, BusBillofquantitiesVersions::getProjectId, bo.getProjectId());
lqw.eq(StringUtils.isNotBlank(bo.getWorkOrderType()), BusBillofquantitiesVersions::getWorkOrderType, bo.getWorkOrderType()); lqw.eq(BusBillofquantitiesVersions::getWorkOrderType, "3");
lqw.eq(BusBillofquantitiesVersions::getStatus, BusinessStatusEnum.FINISH.getStatus());
List<BusBillofquantitiesVersionsVo> result = baseMapper.selectVoList(lqw); List<BusBillofquantitiesVersionsVo> result = baseMapper.selectVoList(lqw);
List<String> versionss = new ArrayList<>(); List<String> versionss = new ArrayList<>();
result.forEach(v -> versionss.add(v.getVersions())); result.forEach(v -> versionss.add(v.getVersions()));

View File

@ -15,10 +15,12 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.dromara.design.domain.dto.desUser.DesUserBatchDto; import org.dromara.design.domain.dto.desUser.DesUserBatchDto;
import org.dromara.system.domain.SysUserRole;
import org.dromara.system.domain.bo.SysUserBo; import org.dromara.system.domain.bo.SysUserBo;
import org.dromara.system.domain.vo.SysDictDataVo; import org.dromara.system.domain.vo.SysDictDataVo;
import org.dromara.system.domain.vo.SysUserVo; import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.service.ISysDictDataService; import org.dromara.system.service.ISysDictDataService;
import org.dromara.system.service.ISysRoleService;
import org.dromara.system.service.ISysUserService; import org.dromara.system.service.ISysUserService;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.dromara.design.domain.bo.DesUserBo; import org.dromara.design.domain.bo.DesUserBo;
@ -26,6 +28,7 @@ import org.dromara.design.domain.vo.DesUserVo;
import org.dromara.design.domain.DesUser; import org.dromara.design.domain.DesUser;
import org.dromara.design.mapper.DesUserMapper; import org.dromara.design.mapper.DesUserMapper;
import org.dromara.design.service.IDesUserService; import org.dromara.design.service.IDesUserService;
import org.springframework.transaction.annotation.Transactional;
import java.io.IOException; import java.io.IOException;
import java.net.URLEncoder; import java.net.URLEncoder;
@ -49,6 +52,8 @@ public class DesUserServiceImpl extends ServiceImpl<DesUserMapper, DesUser> impl
private final ISysUserService sysUserService; private final ISysUserService sysUserService;
private final ISysRoleService roleService;
/** /**
* 查询设计人员 * 查询设计人员
* *
@ -151,9 +156,44 @@ public class DesUserServiceImpl extends ServiceImpl<DesUserMapper, DesUser> impl
} }
@Override @Override
@Transactional(rollbackFor = Exception.class)
public Boolean batchAddOrUpdate(DesUserBatchDto dto) { public Boolean batchAddOrUpdate(DesUserBatchDto dto) {
baseMapper.delete(Wrappers.<DesUser>lambdaQuery().eq(DesUser::getProjectId, dto.getProjectId())); baseMapper.delete(Wrappers.<DesUser>lambdaQuery().eq(DesUser::getProjectId, dto.getProjectId()));
//1961314792461586433 校审员 3
//1961315028017893378 审核员 5
//1961315147199041537 审定员 4
List<DesUser> desUsers = MapstructUtils.convert(dto.getList(), DesUser.class); List<DesUser> desUsers = MapstructUtils.convert(dto.getList(), DesUser.class);
roleService.deleteDesignUser(dto.getProjectId());
ArrayList<String> userIds = new ArrayList<>();
List<SysUserRole> sysUserRoles = new ArrayList<>();
for (DesUserBo bo : dto.getList()) {
if(bo.getUserId()==null){
continue;
}
SysUserRole sysUserRole = new SysUserRole();
if("3".equals(bo.getUserType())){
sysUserRole.setRoleId(1961314792461586433L);
}else if("5".equals(bo.getUserType())){
sysUserRole.setRoleId(1961315028017893378L);
}else if("4".equals(bo.getUserType())){
sysUserRole.setRoleId(1961315147199041537L);
}else {
continue;
}
sysUserRole.setUserId(bo.getUserId());
sysUserRole.setProjectId(dto.getProjectId());
if(userIds.contains(sysUserRole.getUserId()+"-"+sysUserRole.getRoleId())){
continue;
}
sysUserRoles.add(sysUserRole);
userIds.add(sysUserRole.getUserId()+"-"+sysUserRole.getRoleId());
}
roleService.insertUserRoleBatch(sysUserRoles);
//todo:发消息 //todo:发消息
return saveBatch(desUsers); return saveBatch(desUsers);
} }
@ -428,5 +468,9 @@ public class DesUserServiceImpl extends ServiceImpl<DesUserMapper, DesUser> impl
DataValidationConstraint constraint = helper.createFormulaListConstraint(personRange); DataValidationConstraint constraint = helper.createFormulaListConstraint(personRange);
// 作用范围第2行到100行第四列 // 作用范围第2行到100行第四列
CellRangeAddressList addressList = new CellRangeAddressList(1, 100, rowCount, rowCount); CellRangeAddressList addressList = new CellRangeAddressList(1, 100, rowCount, rowCount);
DataValidation validation = helper.createValidation(constraint, addressList);
validation.setShowErrorBox(true);
mainSheet.addValidationData(validation);
} }
} }

View File

@ -27,8 +27,10 @@ import org.dromara.common.sse.utils.SseMessageUtils;
import org.dromara.design.domain.DesVolumeCatalog; import org.dromara.design.domain.DesVolumeCatalog;
import org.dromara.design.domain.DesVolumeFile; import org.dromara.design.domain.DesVolumeFile;
import org.dromara.design.domain.bo.DesVolumeFileBo; import org.dromara.design.domain.bo.DesVolumeFileBo;
import org.dromara.design.domain.dto.volumefile.DesVolumeFileAppPageDto;
import org.dromara.design.domain.dto.volumefile.DesVolumeFileCreateReq; import org.dromara.design.domain.dto.volumefile.DesVolumeFileCreateReq;
import org.dromara.design.domain.vo.BusDrawingreviewReceiptsVo; import org.dromara.design.domain.vo.BusDrawingreviewReceiptsVo;
import org.dromara.design.domain.vo.volumefile.DesVolumeFileAppVo;
import org.dromara.design.domain.vo.volumefile.DesVolumeFileCodeVo; import org.dromara.design.domain.vo.volumefile.DesVolumeFileCodeVo;
import org.dromara.design.domain.vo.volumefile.DesVolumeFileJoinVo; import org.dromara.design.domain.vo.volumefile.DesVolumeFileJoinVo;
import org.dromara.design.domain.vo.volumefile.DesVolumeFileVo; import org.dromara.design.domain.vo.volumefile.DesVolumeFileVo;
@ -469,6 +471,12 @@ public class DesVolumeFileServiceImpl extends ServiceImpl<DesVolumeFileMapper, D
} }
@Override
public TableDataInfo<DesVolumeFileAppVo> queryAppPageList(DesVolumeFileAppPageDto dto, PageQuery pageQuery) {
Page<DesVolumeFileAppVo> result = baseMapper.queryAppPageList(pageQuery.build(), dto);
return TableDataInfo.build(result);
}
/** /**
* 总体流程监听(例如: 草稿,撤销,退回,作废,终止,已完成,单任务完成等) * 总体流程监听(例如: 草稿,撤销,退回,作废,终止,已完成,单任务完成等)
* 正常使用只需#processEvent.flowCode=='leave1' * 正常使用只需#processEvent.flowCode=='leave1'

View File

@ -50,7 +50,7 @@ public class BusAttendanceController extends BaseController {
/** /**
* 查询施工人员月份考勤列表 * 查询施工人员月份考勤列表
*/ */
@SaCheckPermission("project:attendance:list") // @SaCheckPermission("project:attendance:list")
@GetMapping("/list/month/byUserId") @GetMapping("/list/month/byUserId")
public R<List<BusAttendanceMonthByUserIdVo>> listAttendanceMonthListByUserId(BusAttendanceMonthByUserIdReq req) { public R<List<BusAttendanceMonthByUserIdVo>> listAttendanceMonthListByUserId(BusAttendanceMonthByUserIdReq req) {
return R.ok(busAttendanceService.listAttendanceMonthListByUserId(req)); return R.ok(busAttendanceService.listAttendanceMonthListByUserId(req));

View File

@ -60,7 +60,7 @@ public class BusProjectAppController {
@GetMapping("/login/list") @GetMapping("/login/list")
public R<List<BusLoginUserProjectRelevancyVo>> listByLoginUser() { public R<List<BusLoginUserProjectRelevancyVo>> listByLoginUser() {
Long userId = LoginHelper.getUserId(); Long userId = LoginHelper.getUserId();
return R.ok(userProjectRelevancyService.queryListByUserId(userId)); return R.ok(userProjectRelevancyService.appQueryListByUserId(userId));
} }
/** /**

View File

@ -35,6 +35,11 @@ public class BusProjectTeamMemberCreateReq implements Serializable {
*/ */
private String postId; private String postId;
/**
* 工种
*/
private String typeOfWork;
/** /**
* 备注 * 备注
*/ */

View File

@ -44,4 +44,10 @@ public class BusLoginUserProjectRelevancyVo implements Serializable {
* 用户类型(0系统管理员 1普通人员 2项目管理员) * 用户类型(0系统管理员 1普通人员 2项目管理员)
*/ */
private String userType; private String userType;
/**
* 班组
*/
private Long teamId;
} }

View File

@ -115,6 +115,8 @@ public interface IBusUserProjectRelevancyService extends IService<BusUserProject
*/ */
List<BusLoginUserProjectRelevancyVo> queryListByUserId(Long userId); List<BusLoginUserProjectRelevancyVo> queryListByUserId(Long userId);
List<BusLoginUserProjectRelevancyVo> appQueryListByUserId(Long userId);
/** /**
* 获取当前登录用户项目分页 * 获取当前登录用户项目分页
* *

View File

@ -17,6 +17,7 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.dromara.common.core.constant.DateConstant; import org.dromara.common.core.constant.DateConstant;
import org.dromara.common.core.constant.HttpStatus; import org.dromara.common.core.constant.HttpStatus;
import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.exception.ServiceException;
@ -1041,7 +1042,7 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
AttendanceUserDataDetailVo detailVo = new AttendanceUserDataDetailVo(); AttendanceUserDataDetailVo detailVo = new AttendanceUserDataDetailVo();
detailVo.setClockDate(key); detailVo.setClockDate(key);
detailVo.setWeek(key.getDayOfWeek().getValue()); detailVo.setWeek(key.getDayOfWeek().getValue());
detailVo.setWorkDay(value.size()*0.5); detailVo.setWorkDay(value.size() * 0.5);
workList.add(detailVo); workList.add(detailVo);
} }
vo.setWork(workList); vo.setWork(workList);
@ -1178,164 +1179,170 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
@Override @Override
public void getExportList(AttendanceExportDto dto, HttpServletResponse response) { public void getExportList(AttendanceExportDto dto, HttpServletResponse response) {
try (OutputStream outputStream = response.getOutputStream()) {
BusProject project = projectService.getById(dto.getProjectId());
BusProject project = projectService.getById(dto.getProjectId()); if (project == null) {
throw new ServiceException("项目不存在");
if (project == null) {
throw new ServiceException("项目不存在");
}
List<SubConstructionUser> list = constructionUserService.list(Wrappers.lambdaQuery(SubConstructionUser.class)
.eq(SubConstructionUser::getProjectId, dto.getProjectId())
.eq(dto.getTeamId() != null, SubConstructionUser::getTeamId, dto.getTeamId())
.eq(StrUtil.isNotBlank(dto.getTypeOfWork()), SubConstructionUser::getTypeOfWork, dto.getTypeOfWork())
.like(StringUtils.isNotBlank(dto.getUserName()), SubConstructionUser::getUserName, dto.getUserName())
);
if (list.isEmpty()) {
throw new ServiceException("未查询到相关人员");
}
List<Long> userIds = list.stream().map(SubConstructionUser::getSysUserId).toList();
String clockDate = dto.getClockDate();
YearMonth yearMonth = YearMonth.parse(clockDate, DateTimeFormatter.ofPattern("yyyy-MM"));
LocalDate start = yearMonth.atDay(1);
LocalDate end = yearMonth.atEndOfMonth();
int daysInMonth = yearMonth.lengthOfMonth(); // 动态获取天数
List<BusAttendance> attendanceList = list(Wrappers.lambdaQuery(BusAttendance.class)
.in(BusAttendance::getUserId, userIds)
.between(BusAttendance::getClockDate, start, end)
);
Map<Long, List<SubConstructionUser>> teamUserMap = list.stream()
.collect(Collectors.groupingBy(user ->
user.getTeamId() != null ? user.getTeamId() : 0L));
Workbook workbook = new HSSFWorkbook();
for (Map.Entry<Long, List<SubConstructionUser>> entry : teamUserMap.entrySet()) {
Long teamId = entry.getKey();
System.out.println("teamId:" + teamId);
List<SubConstructionUser> users = entry.getValue();
SubConstructionUser constructionUser = users.getFirst();
String teamName = constructionUser.getTeamName();
if (teamId == 0) {
teamName = "无班组";
} }
if (StringUtils.isBlank(teamName)) { String clockDate = dto.getClockDate();
teamName = teamId.toString(); YearMonth yearMonth = YearMonth.parse(clockDate, DateTimeFormatter.ofPattern("yyyy-MM"));
} LocalDate start = yearMonth.atDay(1);
System.out.println("name:" + teamName); LocalDate end = yearMonth.atEndOfMonth();
Sheet sheet = workbook.createSheet(teamName);
// ==================== 设置列宽 ==================== LocalDateTime startTime = LocalDateTime.of(yearMonth.atDay(1), LocalTime.MIN); // 00:00:00
sheet.setColumnWidth(0, 5 * 256); // 序号 LocalDateTime endTime = LocalDateTime.of(yearMonth.atEndOfMonth(), LocalTime.MAX);
sheet.setColumnWidth(1, 10 * 256); // 姓名/日期
sheet.setColumnWidth(2, 30 * 256); // 身份证号
// 日期列(每天一列) List<SubConstructionUser> list = constructionUserService.list(Wrappers.lambdaQuery(SubConstructionUser.class)
for (int i = 3; i < 3 + daysInMonth; i++) { .eq(SubConstructionUser::getProjectId, dto.getProjectId())
sheet.setColumnWidth(i, 5 * 256); // 每天列宽 15 字符 .isNotNull(SubConstructionUser::getEntryDate)
.and(wrapper -> wrapper.between(SubConstructionUser::getLeaveDate, startTime, endTime).or()
.isNull(SubConstructionUser::getLeaveDate))
.eq(dto.getTeamId() != null, SubConstructionUser::getTeamId, dto.getTeamId())
.eq(StrUtil.isNotBlank(dto.getTypeOfWork()), SubConstructionUser::getTypeOfWork, dto.getTypeOfWork())
.like(StringUtils.isNotBlank(dto.getUserName()), SubConstructionUser::getUserName, dto.getUserName())
);
if (list.isEmpty()) {
throw new ServiceException("未查询到相关人员");
} }
sheet.setColumnWidth(3 + daysInMonth, 5 * 256); // 合计 List<Long> userIds = list.stream().map(SubConstructionUser::getSysUserId).toList();
sheet.setColumnWidth(3 + daysInMonth + 1, 10 * 256); // 是否离场
sheet.setColumnWidth(3 + daysInMonth + 2, 15 * 256); // 签字
// ==================== 创建样式 ==================== int daysInMonth = yearMonth.lengthOfMonth(); // 动态获取天数
CellStyle borderStyle = createBorderStyle(workbook);
List<BusAttendance> attendanceList = list(Wrappers.lambdaQuery(BusAttendance.class)
.in(BusAttendance::getUserId, userIds)
.between(BusAttendance::getClockDate, start, end)
);
Map<Long, List<SubConstructionUser>> teamUserMap = list.stream()
.collect(Collectors.groupingBy(user ->
user.getTeamId() != null ? user.getTeamId() : 0L));
Workbook workbook = new XSSFWorkbook();
for (Map.Entry<Long, List<SubConstructionUser>> entry : teamUserMap.entrySet()) {
Long teamId = entry.getKey();
System.out.println("teamId:" + teamId);
List<SubConstructionUser> users = entry.getValue();
SubConstructionUser constructionUser = users.getFirst();
String teamName = constructionUser.getTeamName();
if (teamId == 0) {
teamName = "无班组";
}
if (StringUtils.isBlank(teamName)) {
teamName = teamId.toString();
}
System.out.println("name:" + teamName);
Sheet sheet = workbook.createSheet(teamName);
// ==================== 设置列宽 ====================
sheet.setColumnWidth(0, 5 * 256); // 序号
sheet.setColumnWidth(1, 10 * 256); // 姓名/日期
sheet.setColumnWidth(2, 30 * 256); // 身份证号
// 日期列(每天一列)
for (int i = 3; i < 3 + daysInMonth; i++) {
sheet.setColumnWidth(i, 5 * 256); // 每天列宽 15 字符
}
sheet.setColumnWidth(3 + daysInMonth, 5 * 256); // 合计
sheet.setColumnWidth(3 + daysInMonth + 1, 10 * 256); // 是否离场
sheet.setColumnWidth(3 + daysInMonth + 2, 15 * 256); // 签字
// ==================== 创建样式 ====================
CellStyle borderStyle = createBorderStyle(workbook);
// CellStyle numberStyle = createNumberStyle(workbook); // CellStyle numberStyle = createNumberStyle(workbook);
// ==================== 表头部分 ==================== // ==================== 表头部分 ====================
Row titleRow = sheet.createRow(0); Row titleRow = sheet.createRow(0);
Cell titleCell = titleRow.createCell(0); Cell titleCell = titleRow.createCell(0);
titleCell.setCellValue("建筑施工企业现场人员考勤表(" + clockDate + ""); titleCell.setCellValue("建筑施工企业现场人员考勤表(" + clockDate + "");
titleCell.setCellStyle(createTitleCellStyle(workbook)); titleCell.setCellStyle(createTitleCellStyle(workbook));
// 合并标题行,横跨所有列 // 合并标题行,横跨所有列
int totalColumns = 3 + daysInMonth + 3; // 总列数 int totalColumns = 3 + daysInMonth + 3; // 总列数
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, totalColumns - 1)); sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, totalColumns - 1));
// ==================== 第二行:项目名称和班组类别 ==================== // ==================== 第二行:项目名称和班组类别 ====================
Row projectRow = sheet.createRow(1); Row projectRow = sheet.createRow(1);
// 设置项目名称(左对齐,占据前半部分) // 设置项目名称(左对齐,占据前半部分)
Cell projectNameCell = projectRow.createCell(0); Cell projectNameCell = projectRow.createCell(0);
projectNameCell.setCellValue("项目名称:" + project.getProjectName()); projectNameCell.setCellValue("项目名称:" + project.getProjectName());
projectNameCell.setCellStyle(createProjectCellStyle(workbook)); // 可选:设置样式 projectNameCell.setCellStyle(createProjectCellStyle(workbook)); // 可选:设置样式
int midColumn = (totalColumns / 2) - 1; int midColumn = (totalColumns / 2) - 1;
// 设置班组类别(右对齐,占据后半部分) // 设置班组类别(右对齐,占据后半部分)
int teamCol = midColumn + 1; // int teamCol = midColumn + 1; //
Cell teamNameCell = projectRow.createCell(teamCol); Cell teamNameCell = projectRow.createCell(teamCol);
teamNameCell.setCellValue("班组类别:" + teamName); teamNameCell.setCellValue("班组类别:" + teamName);
teamNameCell.setCellStyle(createProjectCellStyle(workbook)); teamNameCell.setCellStyle(createProjectCellStyle(workbook));
// 合并项目名称区域(从第 0 列到中间) // 合并项目名称区域(从第 0 列到中间)
if (midColumn >= 0) { if (midColumn >= 0) {
sheet.addMergedRegion(new CellRangeAddress(1, 1, 0, midColumn)); sheet.addMergedRegion(new CellRangeAddress(1, 1, 0, midColumn));
}
// 合并班组类别区域(从 teamCol 开始,到最后一列)
if (teamCol < totalColumns) {
sheet.addMergedRegion(new CellRangeAddress(1, 1, teamCol, totalColumns - 1));
}
// ==================== 数据表头 ====================
Row headerRow = sheet.createRow(2);
writeHeaderRow(headerRow, daysInMonth);
// 设置表头边框
for (int i = 0; i < totalColumns; i++) {
Cell cell = headerRow.getCell(i);
if (cell != null) {
cell.setCellStyle(borderStyle);
} }
}
// ==================== 数据行 ==================== // 合并班组类别区域(从 teamCol 开始,到最后一列)
CellStyle numberBorderStyle = createNumberBorderStyle(workbook); if (teamCol < totalColumns) {
sheet.addMergedRegion(new CellRangeAddress(1, 1, teamCol, totalColumns - 1));
}
int rowIndex = 3; // ==================== 数据表头 ====================
for (SubConstructionUser user : users) { Row headerRow = sheet.createRow(2);
Row row = sheet.createRow(rowIndex++); writeHeaderRow(headerRow, daysInMonth);
writeDataRow(row, user, attendanceList, start, end, daysInMonth,borderStyle,numberBorderStyle); // 设置表头边框
// 设置数据行边框
for (int i = 0; i < totalColumns; i++) { for (int i = 0; i < totalColumns; i++) {
Cell cell = row.getCell(i); Cell cell = headerRow.getCell(i);
if (cell != null) { if (cell != null) {
cell.setCellStyle(borderStyle); cell.setCellStyle(borderStyle);
} }
} }
}
// ==================== 设置月份天数列为数字格式 + 边框 ====================
// ==================== 数据行 ====================
CellStyle numberBorderStyle = createNumberBorderStyle(workbook);
// 表头 int rowIndex = 3;
for (int i = 3; i < 3 + daysInMonth; i++) { for (SubConstructionUser user : users) {
Cell headerCell = headerRow.getCell(i); Row row = sheet.createRow(rowIndex++);
if (headerCell != null) { writeDataRow(row, user, attendanceList, start, end, daysInMonth, borderStyle, numberBorderStyle);
headerCell.setCellStyle(numberBorderStyle);
}
}
// 数据行 // 设置数据行边框
for (int i = 3; i < 3 + daysInMonth; i++) { for (int i = 0; i < totalColumns; i++) {
for (int j = 3; j < rowIndex; j++) { Cell cell = row.getCell(i);
Row dataRow = sheet.getRow(j); if (cell != null) {
if (dataRow != null) { cell.setCellStyle(borderStyle);
Cell dataCell = dataRow.getCell(i);
if (dataCell != null) {
dataCell.setCellStyle(numberBorderStyle);
} }
} }
} }
} // ==================== 设置月份天数列为数字格式 + 边框 ====================
// ==================== 设置合计列公式 ====================
// 表头
for (int i = 3; i < 3 + daysInMonth; i++) {
Cell headerCell = headerRow.getCell(i);
if (headerCell != null) {
headerCell.setCellStyle(numberBorderStyle);
}
}
// 数据行
for (int i = 3; i < 3 + daysInMonth; i++) {
for (int j = 3; j < rowIndex; j++) {
Row dataRow = sheet.getRow(j);
if (dataRow != null) {
Cell dataCell = dataRow.getCell(i);
if (dataCell != null) {
dataCell.setCellStyle(numberBorderStyle);
}
}
}
}
// ==================== 设置合计列公式 ====================
// for (int i = 4; i < rowIndex; i++) { // for (int i = 4; i < rowIndex; i++) {
// Row dataRow = sheet.getRow(i); // Row dataRow = sheet.getRow(i);
// if (dataRow != null) { // if (dataRow != null) {
@ -1358,47 +1365,47 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
// } // }
// } // }
// ==================== 表尾部分 ==================== // ==================== 表尾部分 ====================
sheet.createRow(rowIndex++); sheet.createRow(rowIndex++);
Row footerRow1 = sheet.createRow(rowIndex++); Row footerRow1 = sheet.createRow(rowIndex++);
int allTotalColumns = 3 + daysInMonth + 3; int allTotalColumns = 3 + daysInMonth + 3;
int quarter = allTotalColumns / 4; int quarter = allTotalColumns / 4;
int[] colSpans = { int[] colSpans = {
quarter - 3, // 第一列减少3列 quarter - 3, // 第一列减少3列
quarter + 2, // 第二列增加2列 quarter + 2, // 第二列增加2列
quarter + 2, // 第三列增加2列 quarter + 2, // 第三列增加2列
allTotalColumns - (quarter - 3 + quarter + 2 + quarter + 2) // 自动计算最后一列 allTotalColumns - (quarter - 3 + quarter + 2 + quarter + 2) // 自动计算最后一列
}; };
int startCol = 0; int startCol = 0;
String[] footerLabels = {"制表人:", "班组负责人:", "项目负责人:", "制表日期:"}; String[] footerLabels = {"制表人:", "班组负责人:", "项目负责人:", "制表日期:"};
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
int endCol = startCol + colSpans[i] - 1; int endCol = startCol + colSpans[i] - 1;
// 合并单元格 // 合并单元格
sheet.addMergedRegion(new CellRangeAddress(rowIndex - 1, rowIndex - 1, startCol, endCol)); sheet.addMergedRegion(new CellRangeAddress(rowIndex - 1, rowIndex - 1, startCol, endCol));
// 创建单元格并设置内容 // 创建单元格并设置内容
Cell cell = footerRow1.createCell(startCol); Cell cell = footerRow1.createCell(startCol);
cell.setCellValue(footerLabels[i]); cell.setCellValue(footerLabels[i]);
cell.setCellStyle(createLeftAlignedCellStyle(workbook)); // 左对齐 cell.setCellStyle(createLeftAlignedCellStyle(workbook)); // 左对齐
startCol = endCol + 1;
}
Row footerRow2 = sheet.createRow(rowIndex++);
footerRow2.createCell(0).setCellValue("注:");
footerRow2.createCell(1).setCellValue("1、本考勤表必须按照每日实际工时进行填写");
Row footerRow3 = sheet.createRow(rowIndex++);
footerRow3.createCell(0).setCellValue("");
footerRow3.createCell(1).setCellValue("2、本考勤表作为工资计发的重要依据。");
startCol = endCol + 1;
} }
Row footerRow2 = sheet.createRow(rowIndex++);
footerRow2.createCell(0).setCellValue("注:");
footerRow2.createCell(1).setCellValue("1、本考勤表必须按照每日实际工时进行填写");
Row footerRow3 = sheet.createRow(rowIndex++);
footerRow3.createCell(0).setCellValue("");
footerRow3.createCell(1).setCellValue("2、本考勤表作为工资计发的重要依据。");
}
try (OutputStream outputStream = response.getOutputStream()) {
workbook.write(outputStream); workbook.write(outputStream);
workbook.close(); workbook.close();
} catch (IOException e) { } catch (IOException e) {
@ -1417,6 +1424,7 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
} }
return columnName.toString(); return columnName.toString();
} }
private CellStyle createBorderStyle(Workbook workbook) { private CellStyle createBorderStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle(); CellStyle style = workbook.createCellStyle();
style.setBorderTop(BorderStyle.THIN); style.setBorderTop(BorderStyle.THIN);
@ -1425,10 +1433,11 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
style.setBorderRight(BorderStyle.THIN); style.setBorderRight(BorderStyle.THIN);
return style; return style;
} }
private CellStyle createNumberBorderStyle(Workbook workbook) { private CellStyle createNumberBorderStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle(); CellStyle style = workbook.createCellStyle();
DataFormat format = workbook.createDataFormat(); DataFormat format = workbook.createDataFormat();
style.setDataFormat(format.getFormat("0")); // 强制显示 0 style.setDataFormat(format.getFormat("0.0")); // 显示一位小数
style.setBorderTop(BorderStyle.THIN); style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN); style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN); style.setBorderLeft(BorderStyle.THIN);
@ -1478,8 +1487,8 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
row.createCell(2 + daysInMonth + 3).setCellValue("签字"); row.createCell(2 + daysInMonth + 3).setCellValue("签字");
} }
private void writeDataRow(Row row, SubConstructionUser user, List<BusAttendance> attendanceList, LocalDate start, LocalDate end, int daysInMonth,CellStyle borderStyle,CellStyle numberBorderStyle) { private void writeDataRow(Row row, SubConstructionUser user, List<BusAttendance> attendanceList, LocalDate start, LocalDate end, int daysInMonth, CellStyle borderStyle, CellStyle numberBorderStyle) {
int index = row.getRowNum()-2; int index = row.getRowNum() - 2;
row.createCell(0).setCellValue(index); row.createCell(0).setCellValue(index);
row.createCell(1).setCellValue(user.getUserName()); row.createCell(1).setCellValue(user.getUserName());
row.createCell(2).setCellValue(idCardEncryptorUtil.decrypt(user.getSfzNumber())); row.createCell(2).setCellValue(idCardEncryptorUtil.decrypt(user.getSfzNumber()));
@ -1502,7 +1511,7 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
sumCell.setCellFormula(formula); sumCell.setCellFormula(formula);
sumCell.setCellStyle(borderStyle); sumCell.setCellStyle(borderStyle);
String leaveStatus = "0".equals(user.getExitStatus()) ? "离场" : "离场"; String leaveStatus = user.getLeaveDate() != null ? "离场" : "离场";
row.createCell(2 + daysInMonth + 2).setCellValue(leaveStatus); row.createCell(2 + daysInMonth + 2).setCellValue(leaveStatus);
row.createCell(2 + daysInMonth + 3).setCellValue(""); row.createCell(2 + daysInMonth + 3).setCellValue("");
@ -1541,7 +1550,7 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
while (!current.isAfter(end)) { while (!current.isAfter(end)) {
Double value = getAttendanceValue(userId, current, attendanceList); Double value = getAttendanceValue(userId, current, attendanceList);
total+=value; total += value;
current = current.plusDays(1); current = current.plusDays(1);
} }

View File

@ -16,6 +16,7 @@ import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.project.constant.BusProjectConstant; import org.dromara.project.constant.BusProjectConstant;
import org.dromara.project.domain.BusProject; import org.dromara.project.domain.BusProject;
import org.dromara.project.domain.BusProjectTeamMember;
import org.dromara.project.domain.BusUserProjectRelevancy; import org.dromara.project.domain.BusUserProjectRelevancy;
import org.dromara.project.domain.dto.project.BusProjectBatchByProjectListReq; import org.dromara.project.domain.dto.project.BusProjectBatchByProjectListReq;
import org.dromara.project.domain.dto.userprojectrelevancy.BusUserProjectRelevancyCreateReq; import org.dromara.project.domain.dto.userprojectrelevancy.BusUserProjectRelevancyCreateReq;
@ -25,6 +26,7 @@ import org.dromara.project.domain.vo.userprojectrelevancy.BusLoginUserProjectRel
import org.dromara.project.domain.vo.userprojectrelevancy.BusUserProjectRelevancyVo; import org.dromara.project.domain.vo.userprojectrelevancy.BusUserProjectRelevancyVo;
import org.dromara.project.mapper.BusUserProjectRelevancyMapper; import org.dromara.project.mapper.BusUserProjectRelevancyMapper;
import org.dromara.project.service.IBusProjectService; import org.dromara.project.service.IBusProjectService;
import org.dromara.project.service.IBusProjectTeamMemberService;
import org.dromara.project.service.IBusUserProjectRelevancyService; import org.dromara.project.service.IBusUserProjectRelevancyService;
import org.dromara.system.service.ISysUserService; import org.dromara.system.service.ISysUserService;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
@ -52,6 +54,10 @@ public class BusUserProjectRelevancyServiceImpl extends ServiceImpl<BusUserProje
@Resource @Resource
private ISysUserService userService; private ISysUserService userService;
@Lazy
@Resource
private IBusProjectTeamMemberService projectMemberService;
/** /**
* 查询系统用户与项目关联 * 查询系统用户与项目关联
* *
@ -329,6 +335,54 @@ public class BusUserProjectRelevancyServiceImpl extends ServiceImpl<BusUserProje
.toList(); .toList();
} }
@Override
public List<BusLoginUserProjectRelevancyVo> appQueryListByUserId(Long userId) {
// 添加查询条件,根据当前用户,获取数据
LambdaQueryWrapper<BusUserProjectRelevancy> queryWrapper = Wrappers.lambdaQuery();
queryWrapper.eq(BusUserProjectRelevancy::getUserId, userId);
// 查询数据库,获取数据
List<BusUserProjectRelevancy> list = this.list(queryWrapper);
List<Long> projectIdList = list.stream().map(BusUserProjectRelevancy::getProjectId).toList();
if (CollUtil.isEmpty(projectIdList)) {
return new ArrayList<>();
}
Map<Long, List<BusProject>> projectMap = projectService.lambdaQuery()
.select(BusProject::getId, BusProject::getPId, BusProject::getProjectName, BusProject::getShortName)
.in(BusProject::getId, projectIdList)
.eq(BusProject::getPId, BusProjectConstant.PARENT_ID)
.list()
.stream().collect(Collectors.groupingBy(BusProject::getId));
Map<Long, Long> teamMap = projectMemberService.lambdaQuery().eq(BusProjectTeamMember::getMemberId, userId).list()
.stream().collect(Collectors.toMap(BusProjectTeamMember::getProjectId, BusProjectTeamMember::getTeamId));
// 获取封装
return list.stream()
.map(userProjectRelevancy -> {
Long projectId = userProjectRelevancy.getProjectId();
BusProject project = null;
if (projectMap.containsKey(projectId)) {
project = projectMap.get(projectId).getFirst();
}
if (project != null && project.getPId().equals(BusProjectConstant.PARENT_ID)) {
BusLoginUserProjectRelevancyVo loginUserProjectRelevancy = new BusLoginUserProjectRelevancyVo();
loginUserProjectRelevancy.setId(userProjectRelevancy.getId());
loginUserProjectRelevancy.setUserId(userProjectRelevancy.getUserId());
loginUserProjectRelevancy.setProjectId(projectId);
loginUserProjectRelevancy.setProjectName(project.getProjectName());
loginUserProjectRelevancy.setShortName(project.getShortName());
loginUserProjectRelevancy.setTeamId(teamMap.get(projectId));
return loginUserProjectRelevancy;
}
return null;
})
.filter(Objects::nonNull)
.toList();
}
/** /**
* 获取当前登录用户项目分页 * 获取当前登录用户项目分页
* *

View File

@ -7,11 +7,13 @@ import lombok.Data;
import org.dromara.common.translation.annotation.Translation; import org.dromara.common.translation.annotation.Translation;
import org.dromara.common.translation.constant.TransConstant; import org.dromara.common.translation.constant.TransConstant;
import org.dromara.quality.domain.QltQualityConstructionLog; import org.dromara.quality.domain.QltQualityConstructionLog;
import org.dromara.system.domain.vo.SysOssVo;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.Date; import java.util.Date;
import java.util.List;
/** /**
@ -76,8 +78,7 @@ public class QltQualityConstructionLogVo implements Serializable {
/** /**
* 附件列表 * 附件列表
*/ */
@Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "file") private List<SysOssVo> fileList;
private String fileList;
/** /**
* 创建者 * 创建者

View File

@ -196,6 +196,13 @@ public class QltQualityConstructionLogServiceImpl extends ServiceImpl<QltQuality
if (qualityConstructionLog == null) { if (qualityConstructionLog == null) {
return qualityConstructionLogVo; return qualityConstructionLogVo;
} }
// 关联附件信息
String file = qualityConstructionLog.getFile();
if (StringUtils.isNotBlank(file)) {
List<Long> ossIdList = Arrays.stream(file.split(",")).map(Long::parseLong).toList();
List<SysOssVo> ossVoList = ossService.listByIds(ossIdList);
qualityConstructionLogVo.setFileList(ossVoList);
}
BeanUtils.copyProperties(qualityConstructionLog, qualityConstructionLogVo); BeanUtils.copyProperties(qualityConstructionLog, qualityConstructionLogVo);
return qualityConstructionLogVo; return qualityConstructionLogVo;
} }

View File

@ -3,6 +3,7 @@ package org.dromara.system.service;
import org.dromara.common.core.domain.vo.SysProjectRolePermissionVo; import org.dromara.common.core.domain.vo.SysProjectRolePermissionVo;
import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.design.domain.DesUser;
import org.dromara.system.domain.SysUserRole; import org.dromara.system.domain.SysUserRole;
import org.dromara.system.domain.bo.SysRoleBo; import org.dromara.system.domain.bo.SysRoleBo;
import org.dromara.system.domain.dto.role.SysRoleProjectDto; import org.dromara.system.domain.dto.role.SysRoleProjectDto;
@ -208,4 +209,10 @@ public interface ISysRoleService {
void cleanOnlineUser(List<Long> userIds); void cleanOnlineUser(List<Long> userIds);
void insertUserRoleBatch(List<SysUserRole> list);
/**
* 批量删除审核审定校审
*/
void deleteDesignUser(Long projectId);
} }

View File

@ -96,7 +96,7 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService {
.orderByAsc("r.role_sort").orderByAsc("r.create_time"); .orderByAsc("r.role_sort").orderByAsc("r.create_time");
// 添加部门ID或角色ID在2,3,4中的条件 // 添加部门ID或角色ID在2,3,4中的条件
if (ObjectUtil.isNotNull(bo.getDeptId())) { if (ObjectUtil.isNotNull(bo.getDeptId())) {
wrapper.and(w -> w.eq("r.dept_id", bo.getDeptId()).or().in("r.role_id", Arrays.asList(2L, 3L, 4L))); wrapper.and(w -> w.eq("r.dept_id", bo.getDeptId()).or().eq("r.dept_id", 99L));
} }
return wrapper; return wrapper;
} }
@ -627,4 +627,17 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService {
}); });
} }
@Override
public void insertUserRoleBatch(List<SysUserRole> list) {
userRoleMapper.insertBatch(list);
}
@Override
public void deleteDesignUser(Long projectId) {
List<Long> list1 = Arrays.asList(1961314792461586433L, 1961315028017893378L, 1961315147199041537L);
userRoleMapper.delete(Wrappers.lambdaQuery(SysUserRole.class)
.eq(SysUserRole::getProjectId, projectId)
.in(SysUserRole::getRoleId, list1)
);
}
} }

View File

@ -87,16 +87,8 @@ public class TenderSupplierInputController extends BaseController {
@PostMapping("/import") @PostMapping("/import")
public R<Void> importData(Long projectId,@RequestParam("file")MultipartFile file) throws IOException { public R<Void> importData(Long projectId,@RequestParam("file")MultipartFile file) throws IOException {
List<TenderSupplierInputVo> tenderSupplierInputVos = ExcelUtil.importExcel(file.getInputStream(), TenderSupplierInputVo.class); List<TenderSupplierInputVo> tenderSupplierInputVos = ExcelUtil.importExcel(file.getInputStream(), TenderSupplierInputVo.class);
List<TenderSupplierInput> tenderSupplierInputs = new ArrayList<>();
for (TenderSupplierInputVo tenderSupplierInputVo : tenderSupplierInputVos) { return toAjax(tenderSupplierInputService.importData(tenderSupplierInputVos,projectId));
TenderSupplierInput newTenderSupplierInput = new TenderSupplierInput();
BeanUtils.copyProperties(tenderSupplierInputVo, newTenderSupplierInput);
newTenderSupplierInput.setProjectId(projectId);
newTenderSupplierInput.setState("draft");
tenderSupplierInputs.add(newTenderSupplierInput);
}
return toAjax(tenderSupplierInputService.saveOrUpdateBatch(tenderSupplierInputs));
} }
/** /**

View File

@ -72,4 +72,6 @@ ITenderSupplierInputService extends IService<TenderSupplierInput>{
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid); Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
List<TenderSupplierInputVo> getList(TenderSupplierInputBo bo); List<TenderSupplierInputVo> getList(TenderSupplierInputBo bo);
boolean importData(List<TenderSupplierInputVo> tenderSupplierInputVos, Long projectId);
} }

View File

@ -203,9 +203,9 @@ public class BusBillofquantitiesLimitListServiceImpl extends ServiceImpl<BusBill
.forEach(item -> { .forEach(item -> {
item.setPrice(item.getUnitPrice().multiply(item.getQuantity()).setScale(4, RoundingMode.HALF_UP)); item.setPrice(item.getUnitPrice().multiply(item.getQuantity()).setScale(4, RoundingMode.HALF_UP));
}); });
if (bo.getType().equals(LimitListTypeEnum.SPECIAL.getCode())) { // if (bo.getType().equals(LimitListTypeEnum.SPECIAL.getCode())) {
return listVoList; // return listVoList;
} // }
//构建父子映射 //构建父子映射
Map<String, List<BusBillofquantitiesLimitListVo>> parentMap = listVoList.stream() Map<String, List<BusBillofquantitiesLimitListVo>> parentMap = listVoList.stream()

View File

@ -3,11 +3,9 @@ package org.dromara.tender.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.cailiaoshebei.controller.constant; import org.dromara.cailiaoshebei.controller.constant;
import org.dromara.cailiaoshebei.domain.BusCailiaoshebeiPici;
import org.dromara.common.core.domain.event.ProcessEvent; import org.dromara.common.core.domain.event.ProcessEvent;
import org.dromara.common.core.enums.BusinessStatusEnum; import org.dromara.common.core.enums.BusinessStatusEnum;
import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.service.OssService;
import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.TableDataInfo;
@ -16,11 +14,9 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.dromara.formalities.enums.FormalitiesStatusEnum;
import org.dromara.system.domain.vo.SysOssUploadVo;
import org.dromara.system.domain.vo.SysOssVo; import org.dromara.system.domain.vo.SysOssVo;
import org.dromara.system.service.ISysOssService; import org.dromara.system.service.ISysOssService;
import org.dromara.tender.domain.BusBiddingPlan; import org.springframework.beans.BeanUtils;
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.dromara.tender.domain.bo.TenderSupplierInputBo; import org.dromara.tender.domain.bo.TenderSupplierInputBo;
@ -35,7 +31,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Collection; import java.util.Collection;
import static org.dromara.common.constant.MinioPathConstant.ContactNoticeTemplate;
import static org.dromara.common.constant.MinioPathConstant.SupplierInput; import static org.dromara.common.constant.MinioPathConstant.SupplierInput;
/** /**
@ -222,6 +217,24 @@ public class TenderSupplierInputServiceImpl extends ServiceImpl<TenderSupplierIn
return list; return list;
} }
@Override
public boolean importData(List<TenderSupplierInputVo> tenderSupplierInputVos, Long projectId) {
List<TenderSupplierInput> tenderSupplierInputs = new ArrayList<>();
for (TenderSupplierInputVo tenderSupplierInputVo : tenderSupplierInputVos) {
Long count = baseMapper.selectCount(new LambdaQueryWrapper<TenderSupplierInput>().eq(TenderSupplierInput::getSupplierCode, tenderSupplierInputVo.getSupplierCode()));
if (count > 0) {
continue;
}
TenderSupplierInput newTenderSupplierInput = new TenderSupplierInput();
BeanUtils.copyProperties(tenderSupplierInputVo, newTenderSupplierInput);
newTenderSupplierInput.setProjectId(projectId);
newTenderSupplierInput.setState("draft");
tenderSupplierInputs.add(newTenderSupplierInput);
}
return baseMapper.insertBatch(tenderSupplierInputs);
}
/** /**
* 总体流程监听(例如: 草稿,撤销,退回,作废,终止,已完成,单任务完成等) * 总体流程监听(例如: 草稿,撤销,退回,作废,终止,已完成,单任务完成等)

View File

@ -17,4 +17,29 @@
</if> </if>
</select> </select>
<select id="queryAppPageList" resultType="org.dromara.design.domain.vo.volumefile.DesVolumeFileAppVo">
select f.id,
f.volume_catalog_id,
f.type,
f.status,
f.audit_status,
f.version,
f.file_id,
c.design_subitem,
c.specialty,
c.principal,
c.document_name,
c.volume_number,
o.url
from des_volume_file f
left join des_volume_catalog c on f.volume_catalog_id = c.design
left join sys_oss o on f.file_id = o.oss_id
where c.project_id = #{dto.projectId}
and f.type = #{dto.type}
<if test="dto.queryParam != null and dto.queryParam != '' ">
and (c.document_name like concat('%',#{dto.queryParam},'%') or c.volume_number like concat('%',#{dto.queryParam},'%'))
</if>
</select>
</mapper> </mapper>