From c50e48fbbdd535cd3a77bb8c630a8bd96273303d Mon Sep 17 00:00:00 2001 From: wang Date: Tue, 3 Mar 2026 22:39:29 +0800 Subject: [PATCH] =?UTF-8?q?fix:=E6=8A=A5=E8=A1=A8=E7=BB=9F=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CategoryController.java | 6 + .../category/domain/vo/CategoryTreeVO.java | 4 + .../category/service/CategoryService.java | 53 ++ .../ServiceApplicationsController.java | 58 +- .../service/dao/ServiceApplicationsDao.java | 64 +- .../form/LawyerStatisticsQueryForm.java | 10 +- .../form/LawyerStatisticsQueryFormList.java | 1 - .../form/LawyerStatisticsQueryFormPage.java | 40 + .../form/ServiceApplicationsQueryForm.java | 3 - .../domain/form/ServiceLawyerQueryForm.java | 1 - .../module/service/domain/vo/ActivityVO.java | 20 + .../domain/vo/FirmActivityCountVO.java | 56 ++ .../domain/vo/LawyerActivityCountVO.java | 62 ++ .../domain/vo/ServiceApplicationsVO.java | 2 - .../ServiceApplicationsDataListener.java | 2 - .../service/ServiceApplicationsService.java | 693 +++++++++++++++++- .../admin/module/word/WordToImageService.java | 148 ++++ .../service/ServiceApplicationsMapper.xml | 308 ++++++++ 18 files changed, 1498 insertions(+), 33 deletions(-) create mode 100644 yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/form/LawyerStatisticsQueryFormPage.java create mode 100644 yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/vo/ActivityVO.java create mode 100644 yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/vo/FirmActivityCountVO.java create mode 100644 yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/vo/LawyerActivityCountVO.java create mode 100644 yun-admin/src/main/java/net/lab1024/sa/admin/module/word/WordToImageService.java diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/business/category/controller/CategoryController.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/business/category/controller/CategoryController.java index e5bd9e5..82e750d 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/business/category/controller/CategoryController.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/business/category/controller/CategoryController.java @@ -54,6 +54,12 @@ public class CategoryController { return categoryService.queryTree(queryForm); } + @Operation(summary = "查询类目层级树 @author 胡克") + @GetMapping("/category/tree/child") + //@SaCheckPermission("category:tree") + public ResponseDTO> queryTreeAndChild() { + return categoryService.queryTreeAndChild(); + } @Operation(summary = "删除类目 @author 胡克") @GetMapping("/category/delete/{categoryId}") @SaCheckPermission("category:delete") diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/business/category/domain/vo/CategoryTreeVO.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/business/category/domain/vo/CategoryTreeVO.java index f847b40..e5861c8 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/business/category/domain/vo/CategoryTreeVO.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/business/category/domain/vo/CategoryTreeVO.java @@ -2,6 +2,7 @@ package net.lab1024.sa.admin.module.business.category.domain.vo; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import net.lab1024.sa.admin.module.business.goods.domain.entity.GoodsEntity; import java.io.Serializable; import java.util.List; @@ -40,4 +41,7 @@ public class CategoryTreeVO implements Serializable { @Schema(description = "子类") private List children; + + @Schema(description = "活动名称子类") + private List childrenGood; } diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/business/category/service/CategoryService.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/business/category/service/CategoryService.java index 7f8ef46..7231438 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/business/category/service/CategoryService.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/business/category/service/CategoryService.java @@ -2,6 +2,7 @@ package net.lab1024.sa.admin.module.business.category.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.google.common.collect.Lists; +import net.lab1024.sa.admin.module.business.category.constant.CategoryTypeEnum; import net.lab1024.sa.admin.module.business.category.dao.CategoryDao; import net.lab1024.sa.admin.module.business.category.domain.dto.CategorySimpleDTO; import net.lab1024.sa.admin.module.business.category.domain.entity.CategoryEntity; @@ -11,6 +12,8 @@ import net.lab1024.sa.admin.module.business.category.domain.form.CategoryUpdateF import net.lab1024.sa.admin.module.business.category.domain.vo.CategoryTreeVO; import net.lab1024.sa.admin.module.business.category.domain.vo.CategoryVO; import net.lab1024.sa.admin.module.business.category.manager.CategoryCacheManager; +import net.lab1024.sa.admin.module.business.goods.dao.GoodsDao; +import net.lab1024.sa.admin.module.business.goods.domain.entity.GoodsEntity; import net.lab1024.sa.base.common.code.UserErrorCode; import net.lab1024.sa.base.common.domain.ResponseDTO; import net.lab1024.sa.base.common.util.SmartBeanUtil; @@ -45,6 +48,9 @@ public class CategoryService { @Resource private CategoryCacheManager categoryCacheManager; + @Resource + private GoodsDao goodsDao; + /** * 添加类目 */ @@ -213,4 +219,51 @@ public class CategoryService { public List getAllCategory() { return categoryDao.selectList(new LambdaQueryWrapper().eq(CategoryEntity::getDeletedFlag,0)); } + + public ResponseDTO> queryTreeAndChild() { + CategoryTreeQueryForm queryForm = new CategoryTreeQueryForm(); + queryForm.setCategoryType(CategoryTypeEnum.GOODS.getValue()); + queryForm.setParentId(0L); + List treeList = categoryCacheManager.queryCategoryTree(queryForm.getParentId(), queryForm.getCategoryType()); + + // 循环查询每个分类下的商品列表 + if (CollectionUtils.isNotEmpty(treeList)) { + for (CategoryTreeVO category : treeList) { + // 查询当前分类的商品 + List goodsList = goodsDao.selectList( + new LambdaQueryWrapper() + .eq(GoodsEntity::getCategoryId, category.getCategoryId()) + .eq(GoodsEntity::getDeletedFlag, false) + ); + category.setChildrenGood(goodsList); + + // 递归查询子分类的商品 + // queryGoodsForChildren(category); + } + } + + return ResponseDTO.ok(treeList); + } + + /** + * 递归查询子分类的商品列表 + */ + private void queryGoodsForChildren(CategoryTreeVO parentCategory) { + if (CollectionUtils.isEmpty(parentCategory.getChildren())) { + return; + } + + for (CategoryTreeVO childCategory : parentCategory.getChildren()) { + // 查询当前子分类的商品 + List goodsList = goodsDao.selectList( + new LambdaQueryWrapper() + .eq(GoodsEntity::getCategoryId, childCategory.getCategoryId()) + .eq(GoodsEntity::getDeletedFlag, false) + ); + childCategory.setChildrenGood(goodsList); + + // 递归查询下一级子分类 + queryGoodsForChildren(childCategory); + } + } } diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/controller/ServiceApplicationsController.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/controller/ServiceApplicationsController.java index 95baf96..3208c92 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/controller/ServiceApplicationsController.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/controller/ServiceApplicationsController.java @@ -1,13 +1,14 @@ package net.lab1024.sa.admin.module.service.controller; import net.lab1024.sa.admin.module.service.domain.form.*; +import net.lab1024.sa.admin.module.service.domain.vo.FirmActivityCountVO; +import net.lab1024.sa.admin.module.service.domain.vo.LawyerActivityCountVO; import net.lab1024.sa.admin.module.service.domain.vo.LawyerStatisticsVO; import net.lab1024.sa.admin.module.service.domain.vo.ServiceApplicationsVO; import net.lab1024.sa.admin.module.service.domain.vo.ServiceReportStatisticsVO; import net.lab1024.sa.admin.module.service.service.ServiceApplicationsService; import net.lab1024.sa.base.common.domain.ValidateList; import org.springframework.web.bind.annotation.*; -import cn.dev33.satoken.annotation.SaCheckPermission; import net.lab1024.sa.base.common.domain.ResponseDTO; import net.lab1024.sa.base.common.domain.PageResult; import io.swagger.v3.oas.annotations.tags.Tag; @@ -47,6 +48,14 @@ public class ServiceApplicationsController { public ResponseDTO> queryPage(@RequestBody @Valid ServiceApplicationsQueryForm queryForm) { return ResponseDTO.ok(serviceApplicationsService.queryPage(queryForm)); } + + @Operation(summary = "查询是否有未审核的数据 @author wzh") + @GetMapping("/serviceApplications/queryNoReview") + //@SaCheckPermission("serviceApplications:query") + public ResponseDTO queryNoReview() { + return ResponseDTO.ok(serviceApplicationsService.queryNoReview()); + } + @Operation(summary = "服务申报分页查询 @author wzh") @PostMapping("/serviceApplications/queryPageReport") //@SaCheckPermission("serviceApplications:query") @@ -182,4 +191,49 @@ public class ServiceApplicationsController { public ResponseDTO batchReviewByDepartmentId(@RequestBody @Valid ServiceApplicationsUpdateForm updateForm) { return serviceApplicationsService.batchReviewByDepartmentId(updateForm); } -} + + /** + * 按律师统计活动参与次数 + */ + @Operation(summary = "按律师统计活动参与次数 @author wzh") + @PostMapping("/serviceApplications/statistics/lawyerActivityCount") + public ResponseDTO> getLawyerActivityCount(@RequestBody @Valid LawyerStatisticsQueryFormPage queryForm) { + return ResponseDTO.ok(serviceApplicationsService.getLawyerActivityCount(queryForm)); + } + + /** + * 按律所统计活动参与次数 + */ + @Operation(summary = "按律所统计活动参与次数 @author wzh") + @PostMapping("/serviceApplications/statistics/firmActivityCount") + public ResponseDTO> getFirmActivityCount(@RequestBody @Valid LawyerStatisticsQueryFormPage queryForm) { + return ResponseDTO.ok(serviceApplicationsService.getFirmActivityCount(queryForm)); + } + + /** + * 导出律师活动统计(不分页) + */ + @Operation(summary = "导出律师活动统计 @author wzh") + @PostMapping("/serviceApplications/export/lawyerActivityCount") + public void exportLawyerActivityStatistics(@RequestBody @Valid LawyerStatisticsQueryForm queryForm, HttpServletResponse response) { + serviceApplicationsService.exportLawyerActivityStatistics(queryForm, response); + } + + /** + * 导出律所活动统计(不含律师分级) + */ + @Operation(summary = "导出律所活动统计 @author wzh") + @PostMapping("/serviceApplications/export/firmActivityCount") + public void exportFirmActivityStatistics(@RequestBody @Valid LawyerStatisticsQueryForm queryForm, HttpServletResponse response) { + serviceApplicationsService.exportFirmActivityStatistics(queryForm, response); + } + + /** + * 导出活动明细,根据权限去导出,个人就看自己的,律所看本所,ceo看所有 + */ + @Operation(summary = "导出活动明细 @author wzh") + @PostMapping("/serviceApplications/export/activityDetail") + public void exportActivityDetail(HttpServletResponse response) { + serviceApplicationsService.exportActivityDetail( response); + } +} \ No newline at end of file diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/dao/ServiceApplicationsDao.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/dao/ServiceApplicationsDao.java index d023a33..0463618 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/dao/ServiceApplicationsDao.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/dao/ServiceApplicationsDao.java @@ -4,18 +4,12 @@ import java.math.BigDecimal; import java.util.List; import net.lab1024.sa.admin.module.service.domain.entity.ServiceApplicationsEntity; import net.lab1024.sa.admin.module.service.domain.form.*; -import net.lab1024.sa.admin.module.service.domain.vo.LawyerStatisticsVO; -import net.lab1024.sa.admin.module.service.domain.vo.ServiceApplicationsVO; +import net.lab1024.sa.admin.module.service.domain.vo.*; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import net.lab1024.sa.admin.module.service.domain.vo.ServiceReportStatisticsVO; -import net.lab1024.sa.base.common.domain.ResponseDTO; -import net.lab1024.sa.base.common.domain.ValidateList; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; -import javax.validation.Valid; - /** * 服务申报表 Dao * @@ -150,4 +144,60 @@ public interface ServiceApplicationsDao extends BaseMapper getLawyerAmountStatistics(@Param("queryForm") LawyerStatisticsQueryForm queryForm); List selectByRecordNoNotMy(@Param("userId") Long userId, @Param("recordNo") String recordNo); + + /** + * 查询上个月是否有未审核的数据 + * @return 未审核数据的数量 + */ + Long queryNoReview(@Param("departmentId") Long departmentId); + + /** + * 查询律师列表(分页) + */ + List getLawyerActivityCount(@Param("page") Page page, @Param("queryForm") LawyerStatisticsQueryFormPage queryForm); + + /** + * 查询指定律师的活动统计 + */ + List getLawyerActivityDetail(@Param("userId") Long userId, @Param("queryForm") LawyerStatisticsQueryFormPage queryForm); + + /** + * 查询律所列表(分页) + */ + List getFirmActivityCount(@Param("page") Page page, @Param("queryForm") LawyerStatisticsQueryFormPage queryForm); + + /** + * 查询指定律所下的所有律师 + */ + List getLawyersByFirmId(@Param("firmId") Long firmId, @Param("queryForm") LawyerStatisticsQueryFormPage queryForm); + + /** + * 查询所有律师(不分页,用于导出) + */ + List getAllLawyers(@Param("queryForm") LawyerStatisticsQueryForm queryForm); + + /** + * 查询所有律所(不分页,用于导出) + */ + List getAllFirms(@Param("queryForm") LawyerStatisticsQueryForm queryForm); + + /** + * 查询指定律所的活动统计(用于导出) + */ + List getFirmActivityDetail(@Param("firmId") Long firmId, @Param("queryForm") LawyerStatisticsQueryForm queryForm); + + /** + * 导出律师活动统计(有什么导出什么) + */ + List exportLawyerActivity(@Param("queryForm") LawyerStatisticsQueryForm queryForm); + + /** + * 导出律所活动统计(有什么导出什么) + */ + List exportFirmActivity(@Param("queryForm") LawyerStatisticsQueryForm queryForm); + + /** + * 导出活动明细 + */ + List exportActivityDetail(@Param("queryForm") ServiceApplicationsQueryForm queryForm); } \ No newline at end of file diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/form/LawyerStatisticsQueryForm.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/form/LawyerStatisticsQueryForm.java index 30a110a..262882d 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/form/LawyerStatisticsQueryForm.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/form/LawyerStatisticsQueryForm.java @@ -2,19 +2,15 @@ package net.lab1024.sa.admin.module.service.domain.form; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; -import net.lab1024.sa.base.common.domain.PageParam; - -import javax.validation.constraints.Pattern; -import java.time.LocalDateTime; /** - * 律师统计查询表单 + * 律师统计查询表单(不分页) * * @author wzh */ @Data -@Schema(description = "律师统计查询表单") -public class LawyerStatisticsQueryForm{ +@Schema(description = "律师统计查询表单(不分页)") +public class LawyerStatisticsQueryForm { @Schema(description = "季度,1,2,3,4") private Integer quarter; diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/form/LawyerStatisticsQueryFormList.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/form/LawyerStatisticsQueryFormList.java index dcbdb32..eb4e512 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/form/LawyerStatisticsQueryFormList.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/form/LawyerStatisticsQueryFormList.java @@ -2,7 +2,6 @@ package net.lab1024.sa.admin.module.service.domain.form; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; -import net.lab1024.sa.base.common.domain.PageParam; /** * 律师统计查询表单 diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/form/LawyerStatisticsQueryFormPage.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/form/LawyerStatisticsQueryFormPage.java new file mode 100644 index 0000000..065e1ef --- /dev/null +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/form/LawyerStatisticsQueryFormPage.java @@ -0,0 +1,40 @@ +package net.lab1024.sa.admin.module.service.domain.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import net.lab1024.sa.base.common.domain.PageParam; + +/** + * 律师统计查询表单(分页) + * + * @author wzh + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Schema(description = "律师统计查询表单(分页)") +public class LawyerStatisticsQueryFormPage extends PageParam { + + @Schema(description = "季度,1,2,3,4") + private Integer quarter; + + @Schema(description = "年度,格式:yyyy") + private Integer year; + + @Schema(description = "律师姓名") + private String lawyerName; + + @Schema(description = "律所名称") + private String firmName; + + @Schema(description = "律所id") + private Long firmId; + + @Schema(description = "开始时间") + private String startTime; + + @Schema(description = "结束时间") + private String endTime; + + private Long userId; +} \ No newline at end of file diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/form/ServiceApplicationsQueryForm.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/form/ServiceApplicationsQueryForm.java index 6d94852..e427ba7 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/form/ServiceApplicationsQueryForm.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/form/ServiceApplicationsQueryForm.java @@ -1,13 +1,10 @@ package net.lab1024.sa.admin.module.service.domain.form; -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableId; import io.swagger.v3.oas.annotations.media.Schema; import net.lab1024.sa.base.common.domain.PageParam; import lombok.Data; import lombok.EqualsAndHashCode; -import javax.validation.constraints.NotBlank; import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.List; diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/form/ServiceLawyerQueryForm.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/form/ServiceLawyerQueryForm.java index 95ace3e..ce5a60c 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/form/ServiceLawyerQueryForm.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/form/ServiceLawyerQueryForm.java @@ -2,7 +2,6 @@ package net.lab1024.sa.admin.module.service.domain.form; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; -import net.lab1024.sa.base.common.domain.PageParam; /** * 律师统计查询表单 diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/vo/ActivityVO.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/vo/ActivityVO.java new file mode 100644 index 0000000..d6e0112 --- /dev/null +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/vo/ActivityVO.java @@ -0,0 +1,20 @@ +package net.lab1024.sa.admin.module.service.domain.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * 活动信息 VO + * + * @Author wzh + */ +@Data +@Schema(description = "活动信息") +public class ActivityVO { + + @Schema(description = "活动ID") + private Long activityId; + + @Schema(description = "活动名称") + private String activityName; +} \ No newline at end of file diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/vo/FirmActivityCountVO.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/vo/FirmActivityCountVO.java new file mode 100644 index 0000000..e986083 --- /dev/null +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/vo/FirmActivityCountVO.java @@ -0,0 +1,56 @@ +package net.lab1024.sa.admin.module.service.domain.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +/** + * 律所活动参与次数统计 VO + * + * @Author wzh + */ +@Data +@Schema(description = "律所活动参与次数统计") +public class FirmActivityCountVO { + + @Schema(description = "律所ID") + private Long firmId; + + @Schema(description = "律所名称") + private String firmName; + + @Schema(description = "活动ID(临时字段,用于接收SQL查询结果)") + private Long activityId; + + @Schema(description = "活动名称(临时字段,用于接收SQL查询结果)") + private String activityName; + + @Schema(description = "参与次数(临时字段,用于接收SQL查询结果)") + private Integer count; + + @Schema(description = "活动参与次数统计(key:活动ID,value:参与次数)") + private Map activityCountMap; + + @Schema(description = "总参与次数") + private Integer totalCount; + + @Schema(description = "下属律师参与统计列表") + private List lawyerList; + + @Schema(description = "活动参与详情列表") + private List activityList; + + @Data + public static class ActivityParticipationVO { + @Schema(description = "活动ID") + private Long activityId; + + @Schema(description = "活动名称") + private String activityName; + + @Schema(description = "参与次数") + private Integer count; + } +} \ No newline at end of file diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/vo/LawyerActivityCountVO.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/vo/LawyerActivityCountVO.java new file mode 100644 index 0000000..f417270 --- /dev/null +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/vo/LawyerActivityCountVO.java @@ -0,0 +1,62 @@ +package net.lab1024.sa.admin.module.service.domain.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +/** + * 律师活动参与次数统计 VO + * + * @Author wzh + */ +@Data +@Schema(description = "律师活动参与次数统计") +public class LawyerActivityCountVO { + + @Schema(description = "律师ID") + private Long userId; + + @Schema(description = "律师姓名") + private String lawyerName; + + @Schema(description = "律所ID") + private Long firmId; + + @Schema(description = "律所名称") + private String firmName; + + @Schema(description = "执业证号") + private String certificateNumber; + + @Schema(description = "活动ID(临时字段,用于接收SQL查询结果)") + private Long activityId; + + @Schema(description = "活动名称(临时字段,用于接收SQL查询结果)") + private String activityName; + + @Schema(description = "参与次数(临时字段,用于接收SQL查询结果)") + private Integer count; + + @Schema(description = "活动参与次数统计(key:活动ID,value:参与次数)") + private Map activityCountMap; + + @Schema(description = "活动参与详情列表") + private List activityList; + + @Schema(description = "总参与次数") + private Integer totalCount; + + @Data + public static class ActivityParticipationVO { + @Schema(description = "活动ID") + private Long activityId; + + @Schema(description = "活动名称") + private String activityName; + + @Schema(description = "参与次数") + private Integer count; + } +} \ No newline at end of file diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/vo/ServiceApplicationsVO.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/vo/ServiceApplicationsVO.java index cdec1cd..4448f12 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/vo/ServiceApplicationsVO.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/vo/ServiceApplicationsVO.java @@ -8,8 +8,6 @@ import java.util.List; import lombok.Data; import net.lab1024.sa.base.module.support.file.domain.entity.FileEntity; -import javax.validation.constraints.NotBlank; - /** * 服务申报表 列表VO * diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/service/ServiceApplicationsDataListener.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/service/ServiceApplicationsDataListener.java index dbaf724..1ba5af7 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/service/ServiceApplicationsDataListener.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/service/ServiceApplicationsDataListener.java @@ -1,10 +1,8 @@ package net.lab1024.sa.admin.module.service.service; -import com.alibaba.excel.EasyExcel; import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.event.AnalysisEventListener; import net.lab1024.sa.admin.module.service.domain.form.ServiceApplicationsImportForm; -import org.apache.commons.collections4.CollectionUtils; import java.util.ArrayList; import java.util.List; diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/service/ServiceApplicationsService.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/service/ServiceApplicationsService.java index 8974364..0ae29b5 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/service/ServiceApplicationsService.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/service/ServiceApplicationsService.java @@ -2,9 +2,17 @@ package net.lab1024.sa.admin.module.service.service; import cn.hutool.core.bean.BeanUtil; import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelWriter; import com.alibaba.excel.write.handler.SheetWriteHandler; +import com.alibaba.excel.write.metadata.WriteSheet; import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; +import com.alibaba.excel.write.metadata.style.WriteCellStyle; +import com.alibaba.excel.write.style.HorizontalCellStyleStrategy; +import com.alibaba.excel.write.style.column.SimpleColumnWidthStyleStrategy; +import net.lab1024.sa.base.common.util.SmartRequestUtil; +import org.apache.poi.ss.usermodel.HorizontalAlignment; +import org.apache.poi.ss.usermodel.VerticalAlignment; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.extern.slf4j.Slf4j; import net.lab1024.sa.base.common.code.UserErrorCode; @@ -28,6 +36,8 @@ import net.lab1024.sa.admin.module.service.dao.ServiceApplicationsDao; import net.lab1024.sa.admin.module.service.domain.entity.ServiceApplicationsEntity; import net.lab1024.sa.admin.module.service.domain.form.*; import net.lab1024.sa.admin.module.service.domain.vo.*; +import net.lab1024.sa.admin.module.service.domain.form.ActivityDetailExportForm; +import java.util.stream.Collectors; import net.lab1024.sa.admin.module.system.datascope.constant.DataScopeViewTypeEnum; import net.lab1024.sa.admin.module.system.datascope.service.DataScopeViewService; import net.lab1024.sa.admin.module.system.department.domain.entity.DepartmentEntity; @@ -36,16 +46,12 @@ import net.lab1024.sa.admin.module.system.employee.domain.entity.EmployeeEntity; import net.lab1024.sa.admin.module.system.employee.service.EmployeeService; import net.lab1024.sa.admin.module.system.login.domain.RequestEmployee; import net.lab1024.sa.admin.module.system.position.service.PositionService; -import net.lab1024.sa.admin.module.system.role.domain.entity.RoleEntity; import net.lab1024.sa.admin.module.system.role.domain.vo.RoleVO; import net.lab1024.sa.admin.module.system.role.service.RoleEmployeeService; -import net.lab1024.sa.admin.module.system.role.service.RoleService; import net.lab1024.sa.admin.util.AdminRequestUtil; import net.lab1024.sa.admin.util.CellStyleStrategy; import net.lab1024.sa.admin.util.DateTimeUtil; import net.lab1024.sa.admin.util.TimeVo; -import net.sf.jsqlparser.expression.LongValue; -import org.apache.catalina.util.RequestUtil; import org.apache.commons.collections4.CollectionUtils; import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.util.CellRangeAddressList; @@ -56,14 +62,14 @@ import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; -import java.io.FileOutputStream; import java.io.IOException; import java.math.BigDecimal; import java.math.RoundingMode; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.*; import java.time.LocalDateTime; import java.util.function.Function; -import java.util.stream.Collectors; @@ -260,7 +266,7 @@ public class ServiceApplicationsService { public PageResult queryPageReport(@Valid ServiceApplicationsQueryForm queryForm) { - List longs = new ArrayList<>(); + Page page = SmartPageUtil.convert2PageQuery(queryForm); //根据用户角色的查询数据范围来查询数据 RequestUser requestUser = AdminRequestUtil.getRequestUser(); @@ -1322,6 +1328,12 @@ public class ServiceApplicationsService { return ResponseDTO.ok(); } + public Long queryNoReview() { + //查询上个月是否有未审核的数据,注意跨年情况 + Long departmentId = AdminRequestUtil.getRequestUser().getDepartmentId(); + return serviceApplicationsDao.queryNoReview(departmentId); + } + /** * Excel下拉列表处理器 @@ -1588,7 +1600,18 @@ public class ServiceApplicationsService { queryForm.setFirmId(requestUser.getDepartmentId()); } } - + + /** + * 应用用户权限控制(分页表单) + */ + private void applyUserPermissionControl(LawyerStatisticsQueryFormPage queryForm) { + RequestEmployee requestUser = AdminRequestUtil.getRequestUser(); + RoleVO roleList = roleEmployeeService.getRoleIdList(requestUser.getEmployeeId()).get(0); + if (UserTypeEnum.CTO.getDesc().equals(roleList.getRoleCode())){ + queryForm.setFirmId(requestUser.getDepartmentId()); + } + } + /** * 设置年度时间范围 */ @@ -1597,6 +1620,42 @@ public class ServiceApplicationsService { queryForm.setStartTime(yearStartAndEnd.getStartTime()); queryForm.setEndTime(yearStartAndEnd.getEndTime()); } + + /** + * 根据季度设置时间范围 + */ + private void setTimeRangeByQuarter(LawyerStatisticsQueryForm queryForm) { + if (queryForm.getQuarter() != null) { + // 有季度参数,使用季度时间范围 + LocalDateTime quarterStart = DateTimeEnum.getQuarterStart(queryForm.getYear(), queryForm.getQuarter()); + LocalDateTime quarterEnd = DateTimeEnum.getQuarterEnd(queryForm.getYear(), queryForm.getQuarter()); + queryForm.setStartTime(quarterStart.toString()); + queryForm.setEndTime(quarterEnd.toString()); + } else { + // 没有季度参数,使用年度时间范围 + TimeVo yearStartAndEnd = DateTimeUtil.getYearStartAndEnd(queryForm.getYear()); + queryForm.setStartTime(yearStartAndEnd.getStartTime()); + queryForm.setEndTime(yearStartAndEnd.getEndTime()); + } + } + + /** + * 根据季度设置时间范围(分页表单) + */ + private void setTimeRangeByQuarter(LawyerStatisticsQueryFormPage queryForm) { + if (queryForm.getQuarter() != null) { + // 有季度参数,使用季度时间范围 + LocalDateTime quarterStart = DateTimeEnum.getQuarterStart(queryForm.getYear(), queryForm.getQuarter()); + LocalDateTime quarterEnd = DateTimeEnum.getQuarterEnd(queryForm.getYear(), queryForm.getQuarter()); + queryForm.setStartTime(quarterStart.toString()); + queryForm.setEndTime(quarterEnd.toString()); + } else { + // 没有季度参数,使用年度时间范围 + TimeVo yearStartAndEnd = DateTimeUtil.getYearStartAndEnd(queryForm.getYear()); + queryForm.setStartTime(yearStartAndEnd.getStartTime()); + queryForm.setEndTime(yearStartAndEnd.getEndTime()); + } + } /** * 处理有服务时长类型数据的律师统计 @@ -2057,4 +2116,622 @@ public class ServiceApplicationsService { List list = serviceApplicationsDao.getServiceReportStatistics(page, queryForm); return SmartPageUtil.convert2PageResult(page, list); } + + /** + * 按律师统计活动参与次数 + */ + public PageResult getLawyerActivityCount(LawyerStatisticsQueryFormPage queryForm) { + // 应用权限控制 + applyUserPermissionControl(queryForm); + + // 设置时间范围(根据季度参数设置) + setTimeRangeByQuarter(queryForm); + + // 第一阶段:使用 SmartPageUtil 创建分页对象,查询律师列表 + Page page = SmartPageUtil.convert2PageQuery(queryForm); + List lawyerList = serviceApplicationsDao.getLawyerActivityCount(page, queryForm); + + // 第二阶段:查询每个律师的活动统计 + if (lawyerList != null && !lawyerList.isEmpty()) { + for (LawyerActivityCountVO lawyer : lawyerList) { + // 查询该律师的所有活动统计 + List activityStats = + serviceApplicationsDao.getLawyerActivityDetail(lawyer.getUserId(), queryForm); + + List activityList = new ArrayList<>(); + int totalCount = 0; + + if (activityStats != null && !activityStats.isEmpty()) { + for (LawyerActivityCountVO stat : activityStats) { + LawyerActivityCountVO.ActivityParticipationVO activityVO = + new LawyerActivityCountVO.ActivityParticipationVO(); + activityVO.setActivityId(stat.getActivityId()); + activityVO.setCount(stat.getCount() != null ? stat.getCount() : 0); + activityList.add(activityVO); + totalCount += activityVO.getCount(); + } + } + + lawyer.setActivityList(activityList); + lawyer.setTotalCount(totalCount); + } + } + + // 按服务总次数降序排序 + lawyerList.sort((a, b) -> { + int countA = a.getTotalCount() != null ? a.getTotalCount() : 0; + int countB = b.getTotalCount() != null ? b.getTotalCount() : 0; + return Integer.compare(countB, countA); + }); + + // 使用 SmartPageUtil 构建分页结果 + return SmartPageUtil.convert2PageResult(page, lawyerList); + } + + /** + * 按律所统计活动参与次数 + */ + public PageResult getFirmActivityCount(LawyerStatisticsQueryFormPage queryForm) { + // 应用权限控制 + applyUserPermissionControl(queryForm); + + // 设置时间范围(根据季度参数设置) + setTimeRangeByQuarter(queryForm); + + // 使用 SmartPageUtil 创建分页对象,查询律所列表 + Page page = SmartPageUtil.convert2PageQuery(queryForm); + List firmList = serviceApplicationsDao.getFirmActivityCount(page, queryForm); + + // 如果没有数据,返回空分页结果 + if (firmList == null || firmList.isEmpty()) { + return SmartPageUtil.convert2PageResult(page, new ArrayList()); + } + + // 查询所有活动(用于获取活动名称) + List allGoods = goodsService.getAllGoods(); + + // 处理每个律所的统计 + for (FirmActivityCountVO firm : firmList) { + // 查询该律所下的所有律师 + List firmLawyers = + serviceApplicationsDao.getLawyersByFirmId(firm.getFirmId(), queryForm); + + // 为每个律师查询活动统计 + if (firmLawyers != null && !firmLawyers.isEmpty()) { + for (LawyerActivityCountVO lawyer : firmLawyers) { + // 查询该律师的所有活动统计 + List activityStats = + serviceApplicationsDao.getLawyerActivityDetail(lawyer.getUserId(), queryForm); + + List activityList = new ArrayList<>(); + int lawyerTotalCount = 0; + + if (activityStats != null && !activityStats.isEmpty()) { + for (LawyerActivityCountVO stat : activityStats) { + LawyerActivityCountVO.ActivityParticipationVO activityVO = + new LawyerActivityCountVO.ActivityParticipationVO(); + activityVO.setActivityId(stat.getActivityId()); + activityVO.setCount(stat.getCount() != null ? stat.getCount() : 0); + activityList.add(activityVO); + lawyerTotalCount += activityVO.getCount(); + } + } + + lawyer.setActivityList(activityList); + lawyer.setTotalCount(lawyerTotalCount); + } + } + + firm.setLawyerList(firmLawyers); + + // 累加律师的参与次数到律所 + Map firmActivityCount = new HashMap<>(); + for (LawyerActivityCountVO lawyer : firmLawyers) { + if (lawyer.getActivityList() != null) { + for (LawyerActivityCountVO.ActivityParticipationVO activity : lawyer.getActivityList()) { + firmActivityCount.merge(activity.getActivityId(), activity.getCount(), Integer::sum); + } + } + } + + // 确保所有goods活动都有记录 + for (GoodsEntity goods : allGoods) { + firmActivityCount.putIfAbsent(goods.getGoodsId(), 0); + } + + // 计算总参与次数 + int totalCount = 0; + List activityList = new ArrayList<>(); + for (Map.Entry entry : firmActivityCount.entrySet()) { + FirmActivityCountVO.ActivityParticipationVO vo = + new FirmActivityCountVO.ActivityParticipationVO(); + vo.setActivityId(entry.getKey()); + vo.setActivityName(getGoodsNameById(allGoods, entry.getKey())); + vo.setCount(entry.getValue()); + activityList.add(vo); + totalCount += entry.getValue(); + } + firm.setActivityList(activityList); + firm.setActivityCountMap(firmActivityCount); + firm.setTotalCount(totalCount); + } + + // 按服务总次数降序排序 + firmList.sort((a, b) -> { + int countA = a.getTotalCount() != null ? a.getTotalCount() : 0; + int countB = b.getTotalCount() != null ? b.getTotalCount() : 0; + return Integer.compare(countB, countA); + }); + + return SmartPageUtil.convert2PageResult(page, firmList); + } + + /** + * 导出律师活动统计(不分页) + */ + public void exportLawyerActivityStatistics(LawyerStatisticsQueryForm queryForm, HttpServletResponse response) { + // 应用权限控制 + applyUserPermissionControl(queryForm); + + // 设置时间范围(根据季度参数设置) + setTimeRangeByQuarter(queryForm); + + // 直接查询有数据的律师活动统计(有什么导出什么) + List lawyerList = serviceApplicationsDao.exportLawyerActivity(queryForm); + + // 导出Excel + exportLawyerActivityExcel(lawyerList, response); + } + + /** + * 导出律所活动统计(不含律师分级) + */ + public void exportFirmActivityStatistics(LawyerStatisticsQueryForm queryForm, HttpServletResponse response) { + // 应用权限控制 + applyUserPermissionControl(queryForm); + + // 设置时间范围(根据季度参数设置) + setTimeRangeByQuarter(queryForm); + + // 直接查询有数据的律所活动统计(有什么导出什么) + List firmList = serviceApplicationsDao.exportFirmActivity(queryForm); + + // 导出Excel + exportFirmActivityExcel(firmList, response); + } + + /** + * 导出律师活动统计Excel + */ + private void exportLawyerActivityExcel(List rawList, HttpServletResponse response) { + // 设置响应头 + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + String fileName = URLEncoder.encode("律师活动统计", StandardCharsets.UTF_8).replaceAll("\\+", "%20"); + response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx"); + + try (ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).build()) { + // 按律师分组 + Map lawyerMap = new LinkedHashMap<>(); + // 查询所有活动及其分类信息 + List allGoods = goodsService.getAllGoods(); + Map goodsMap = allGoods.stream() + .collect(Collectors.toMap(GoodsEntity::getGoodsId, g -> g)); + + // 获取活动分类信息 + Map categoryMap = getActivityCategoryMap(allGoods); + + // 收集所有涉及的活动ID + Set activityIds = new LinkedHashSet<>(); + + if (rawList != null && !rawList.isEmpty()) { + for (LawyerActivityCountVO record : rawList) { + Long userId = record.getUserId(); + LawyerActivityCountVO lawyer = lawyerMap.get(userId); + + if (lawyer == null) { + lawyer = new LawyerActivityCountVO(); + lawyer.setUserId(userId); + lawyer.setLawyerName(record.getLawyerName()); + lawyer.setFirmId(record.getFirmId()); + lawyer.setFirmName(record.getFirmName()); + lawyer.setCertificateNumber(record.getCertificateNumber()); + lawyer.setActivityList(new ArrayList<>()); + lawyerMap.put(userId, lawyer); + } + + // 添加活动 + if (record.getActivityId() != null && record.getCount() != null) { + LawyerActivityCountVO.ActivityParticipationVO activity = + new LawyerActivityCountVO.ActivityParticipationVO(); + activity.setActivityId(record.getActivityId()); + activity.setActivityName(record.getActivityName()); + activity.setCount(record.getCount()); + lawyer.getActivityList().add(activity); + activityIds.add(record.getActivityId()); + } + } + } + + // 按分类组织所有活动(不是只包含有数据的) + Map> categoryActivities = new LinkedHashMap<>(); + for (GoodsEntity goods : allGoods) { + String categoryName = categoryMap.getOrDefault(goods.getCategoryId(), "未分类"); + categoryActivities.computeIfAbsent(categoryName, k -> new ArrayList<>()).add(goods.getGoodsId()); + } + + // 构建两级表头 - 使用 List> 格式,每个内部列表是一列 + List> head = new ArrayList<>(); + + // 第一列:律所名称(合并两行) + head.add(Arrays.asList("律所名称", "")); + // 第二列:律师名称(合并两行) + head.add(Arrays.asList("律师名称", "")); + + // 构建表头 + List activityIdList = new ArrayList<>(); + for (Map.Entry> entry : categoryActivities.entrySet()) { + String categoryName = entry.getKey(); + List activities = entry.getValue(); + + // 为每个活动添加一列 + for (Long activityId : activities) { + GoodsEntity goods = goodsMap.get(activityId); + String activityName = goods != null ? goods.getGoodsName() : "未知活动"; + // 第一行是大类,第二行是活动名称 + head.add(Arrays.asList(categoryName, activityName)); + activityIdList.add(activityId); + } + } + + // 最后一列:服务总次数(合并两行) + head.add(Arrays.asList("服务总次数", "")); + + // 构建数据 + List> data = new ArrayList<>(); + for (LawyerActivityCountVO lawyer : lawyerMap.values()) { + List row = new ArrayList<>(); + row.add(lawyer.getFirmName()); + row.add(lawyer.getLawyerName()); + + // 按活动ID顺序填充数据 + Map activityCountMap = lawyer.getActivityList().stream() + .collect(Collectors.toMap( + LawyerActivityCountVO.ActivityParticipationVO::getActivityId, + LawyerActivityCountVO.ActivityParticipationVO::getCount, + (v1, v2) -> v1 + )); + + int totalCount = 0; + for (Long activityId : activityIdList) { + Integer count = activityCountMap.getOrDefault(activityId, 0); + row.add(count); + totalCount += count; + } + row.add(totalCount); + data.add(row); + } + + // 添加合计行 + List totalRow = new ArrayList<>(); + totalRow.add("合计"); + totalRow.add(""); + int grandTotal = 0; + for (Long activityId : activityIdList) { + int activityTotal = 0; + for (LawyerActivityCountVO lawyer : lawyerMap.values()) { + Map activityCountMap = lawyer.getActivityList().stream() + .collect(Collectors.toMap( + LawyerActivityCountVO.ActivityParticipationVO::getActivityId, + LawyerActivityCountVO.ActivityParticipationVO::getCount, + (v1, v2) -> v1 + )); + activityTotal += activityCountMap.getOrDefault(activityId, 0); + } + totalRow.add(activityTotal); + grandTotal += activityTotal; + } + totalRow.add(grandTotal); + data.add(totalRow); + + // 创建样式策略 - 设置字体不换行 + HorizontalCellStyleStrategy styleStrategy = new HorizontalCellStyleStrategy( + getHeadStyle(), // 表头样式 + getContentStyle() // 内容样式 + ); + + WriteSheet writeSheet = EasyExcel.writerSheet("律师活动统计") + .head(head) + .registerWriteHandler(styleStrategy) + .registerWriteHandler(new SimpleColumnWidthStyleStrategy(15)) // 设置默认列宽 + .build(); + excelWriter.write(data, writeSheet); + } catch (IOException e) { + log.error("导出律师活动统计失败", e); + throw new RuntimeException("导出失败"); + } + } + + /** + * 获取表头样式 - 字体不换行 + */ + private WriteCellStyle getHeadStyle() { + WriteCellStyle headStyle = new WriteCellStyle(); + headStyle.setWrapped(false); // 不换行 + headStyle.setHorizontalAlignment(HorizontalAlignment.CENTER); + headStyle.setVerticalAlignment(VerticalAlignment.CENTER); + return headStyle; + } + + /** + * 获取内容样式 - 字体不换行 + */ + private WriteCellStyle getContentStyle() { + WriteCellStyle contentStyle = new WriteCellStyle(); + contentStyle.setWrapped(false); // 不换行 + contentStyle.setHorizontalAlignment(HorizontalAlignment.CENTER); + contentStyle.setVerticalAlignment(VerticalAlignment.CENTER); + return contentStyle; + } + + /** + * 获取活动分类映射 + */ + private Map getActivityCategoryMap(List goodsList) { + Map categoryMap = new HashMap<>(); + // 获取所有分类ID + Set categoryIds = goodsList.stream() + .map(GoodsEntity::getCategoryId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + + // 查询所有分类 + List categories = categoryService.getAllCategory(); + Map categoryNameMap = categories.stream() + .collect(Collectors.toMap(CategoryEntity::getCategoryId, CategoryEntity::getCategoryName, (v1, v2) -> v1)); + + // 构建分类映射 + for (Long categoryId : categoryIds) { + categoryMap.put(categoryId, categoryNameMap.getOrDefault(categoryId, "未分类")); + } + + return categoryMap; + } + + /** + * 导出律所活动统计Excel + */ + private void exportFirmActivityExcel(List rawList, HttpServletResponse response) { + // 设置响应头 + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + String fileName = URLEncoder.encode("律所活动统计", StandardCharsets.UTF_8).replaceAll("\\+", "%20"); + response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx"); + + try (ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).build()) { + // 按律所分组 + Map firmMap = new LinkedHashMap<>(); + Set activityNames = new LinkedHashSet<>(); + + if (rawList != null && !rawList.isEmpty()) { + for (FirmActivityCountVO record : rawList) { + Long firmId = record.getFirmId(); + FirmActivityCountVO firm = firmMap.get(firmId); + + if (firm == null) { + firm = new FirmActivityCountVO(); + firm.setFirmId(firmId); + firm.setFirmName(record.getFirmName()); + firm.setActivityList(new ArrayList<>()); + firmMap.put(firmId, firm); + } + + // 添加活动 + if (record.getActivityId() != null && record.getCount() != null) { + FirmActivityCountVO.ActivityParticipationVO activity = + new FirmActivityCountVO.ActivityParticipationVO(); + activity.setActivityId(record.getActivityId()); + activity.setActivityName(record.getActivityName()); + activity.setCount(record.getCount()); + firm.getActivityList().add(activity); + activityNames.add(record.getActivityName()); + } + } + } + + // 查询所有活动及其分类信息 + List allGoods = goodsService.getAllGoods(); + Map goodsMap = allGoods.stream() + .collect(Collectors.toMap(GoodsEntity::getGoodsId, g -> g)); + + // 获取活动分类信息 + Map categoryMap = getActivityCategoryMap(allGoods); + + // 按分类组织所有活动(不是只包含有数据的) + Map> categoryActivities = new LinkedHashMap<>(); + for (GoodsEntity goods : allGoods) { + String categoryName = categoryMap.getOrDefault(goods.getCategoryId(), "未分类"); + categoryActivities.computeIfAbsent(categoryName, k -> new ArrayList<>()).add(goods.getGoodsId()); + } + + // 构建两级表头 - 使用 List> 格式,每个内部列表是一列 + List> head = new ArrayList<>(); + + // 第一列:律所名称(合并两行) + head.add(Arrays.asList("律所名称", "")); + + // 构建表头 + List activityIdList = new ArrayList<>(); + for (Map.Entry> entry : categoryActivities.entrySet()) { + String categoryName = entry.getKey(); + List activities = entry.getValue(); + + // 为每个活动添加一列 + for (Long activityId : activities) { + GoodsEntity goods = goodsMap.get(activityId); + String activityName = goods != null ? goods.getGoodsName() : "未知活动"; + // 第一行是大类,第二行是活动名称 + head.add(Arrays.asList(categoryName, activityName)); + activityIdList.add(activityId); + } + } + + // 最后一列:服务总次数(合并两行) + head.add(Arrays.asList("服务总次数", "")); + + // 构建数据 + List> data = new ArrayList<>(); + for (FirmActivityCountVO firm : firmMap.values()) { + List row = new ArrayList<>(); + row.add(firm.getFirmName()); + + // 按活动ID顺序填充数据 + Map activityCountMap = firm.getActivityList().stream() + .collect(Collectors.toMap( + FirmActivityCountVO.ActivityParticipationVO::getActivityId, + FirmActivityCountVO.ActivityParticipationVO::getCount, + (v1, v2) -> v1 + )); + + int totalCount = 0; + for (Long activityId : activityIdList) { + Integer count = activityCountMap.getOrDefault(activityId, 0); + row.add(count); + totalCount += count; + } + row.add(totalCount); + data.add(row); + } + + // 添加合计行 + List totalRow = new ArrayList<>(); + totalRow.add("合计"); + int grandTotal = 0; + for (Long activityId : activityIdList) { + int activityTotal = 0; + for (FirmActivityCountVO firm : firmMap.values()) { + Map activityCountMap = firm.getActivityList().stream() + .collect(Collectors.toMap( + FirmActivityCountVO.ActivityParticipationVO::getActivityId, + FirmActivityCountVO.ActivityParticipationVO::getCount, + (v1, v2) -> v1 + )); + activityTotal += activityCountMap.getOrDefault(activityId, 0); + } + totalRow.add(activityTotal); + grandTotal += activityTotal; + } + totalRow.add(grandTotal); + data.add(totalRow); + + // 创建样式策略 - 设置字体不换行 + HorizontalCellStyleStrategy styleStrategy = new HorizontalCellStyleStrategy( + getHeadStyle(), // 表头样式 + getContentStyle() // 内容样式 + ); + + WriteSheet writeSheet = EasyExcel.writerSheet("律所活动统计") + .head(head) + .registerWriteHandler(styleStrategy) + .registerWriteHandler(new SimpleColumnWidthStyleStrategy(15)) // 设置默认列宽 + .build(); + excelWriter.write(data, writeSheet); + } catch (IOException e) { + log.error("导出律所活动统计失败", e); + throw new RuntimeException("导出失败"); + } + } + + /** + * 根据goodsID获取goods名称 + */ + private String getGoodsNameById(List goodsList, Long goodsId) { + if (goodsList == null || goodsId == null) { + return "未知活动"; + } + return goodsList.stream() + .filter(g -> g != null && goodsId.equals(g.getGoodsId())) + .map(GoodsEntity::getGoodsName) + .findFirst() + .orElse("未知活动"); + } + + /** + * 导出活动明细 + * 根据权限导出:个人只看自己的,律所看本所,CEO看所有 + */ + public void exportActivityDetail( HttpServletResponse response) { + ServiceApplicationsQueryForm queryForm = new ServiceApplicationsQueryForm(); + // 获取当前登录用户 + RequestEmployee requestUser = AdminRequestUtil.getRequestUser(); + RoleVO roleList = roleEmployeeService.getRoleIdList(requestUser.getEmployeeId()).get(0); + + // 根据用户类型应用权限控制 + if (UserTypeEnum.USER.getDesc().equals(roleList.getRoleCode())) { + // 律师只能看自己 + queryForm.setUserId(requestUser.getUserId()); + } else if (UserTypeEnum.CTO.getDesc().equals(roleList.getRoleCode())) { + // 律所管理员看本所 + queryForm.setFirmId(requestUser.getDepartmentId()); + } + // CEO可以看所有,不需要设置过滤条件 + + // 查询活动明细数据 + List dataList = serviceApplicationsDao.exportActivityDetail(queryForm); + + // 转换为导出实体 + List exportList = convertToExportForm(dataList); + + // 写入数据到文件 + exportExcel(response, "活动明细.xlsx", "活动明细", ActivityDetailExportForm.class, exportList); + } + + /** + * 转换为导出实体 + */ + private List convertToExportForm(List dataList) { + List exportList = new ArrayList<>(); + if (dataList == null || dataList.isEmpty()) { + return exportList; + } + + for (ServiceApplicationsVO vo : dataList) { + ActivityDetailExportForm form = new ActivityDetailExportForm(); + form.setRecordNo(vo.getRecordNo()); + form.setUserName(vo.getUserName()); + form.setDepartmentName(vo.getDepartmentName()); + form.setActivityName(vo.getActivityName()); + form.setServiceStart(vo.getServiceStart()); + form.setServiceEnd(vo.getServiceEnd()); + form.setServiceDuration(vo.getServiceDuration()); + form.setBeneficiaryCount(vo.getBeneficiaryCount()); + form.setOrganizerName(vo.getOrganizerName()); + form.setOrganizerContact(vo.getOrganizerContact()); + form.setOrganizerPhone(vo.getOrganizerPhone()); + exportList.add(form); + } + + return exportList; + } + + /** + * 获取审核状态文本 + */ + private String getAuditStatusText(Integer status) { + if (status == null) { + return "未知"; + } + switch (status) { + case 0: + return "待审核"; + case 1: + return "审核中"; + case 2: + return "已通过"; + case 3: + return "已拒绝"; + default: + return "未知"; + } + } } \ No newline at end of file diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/word/WordToImageService.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/word/WordToImageService.java new file mode 100644 index 0000000..0ddf3ad --- /dev/null +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/word/WordToImageService.java @@ -0,0 +1,148 @@ +package net.lab1024.sa.admin.module.word; + +import fr.opensagres.poi.xwpf.converter.pdf.PdfConverter; +import fr.opensagres.poi.xwpf.converter.pdf.PdfOptions; +import lombok.extern.slf4j.Slf4j; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.rendering.ImageType; +import org.apache.pdfbox.rendering.PDFRenderer; +import org.apache.poi.xwpf.usermodel.XWPFDocument; +import org.springframework.stereotype.Service; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * Word文档转图片服务 + * + * @Author wzh + * @Date 2026-02-03 + */ +@Service +@Slf4j +public class WordToImageService { + + /** + * 将Word文档转换为PNG图片数组 + * + * @param wordBytes Word文档字节数组 + * @param dpi 图片分辨率 (建议值: 150-300) + * @return PNG图片字节数组列表 + */ + public List convertWordToImages(byte[] wordBytes, int dpi) throws Exception { + List imageBytesList = new ArrayList<>(); + + try { + // 1. Word转PDF + byte[] pdfBytes = convertWordToPdf(wordBytes); + + // 2. PDF转图片 + imageBytesList = convertPdfToImages(pdfBytes, dpi); + + } catch (Exception e) { + log.error("Word转图片失败: {}", e.getMessage(), e); + throw new Exception("Word文档转换为图片失败: " + e.getMessage(), e); + } + + return imageBytesList; + } + + /** + * 将Word文档转换为PDF + */ + private byte[] convertWordToPdf(byte[] wordBytes) throws Exception { + try (XWPFDocument document = new XWPFDocument(new ByteArrayInputStream(wordBytes)); + ByteArrayOutputStream pdfOutputStream = new ByteArrayOutputStream()) { + + PdfOptions options = PdfOptions.create(); + PdfConverter.getInstance().convert(document, pdfOutputStream, options); + + return pdfOutputStream.toByteArray(); + + } catch (Exception e) { + log.error("Word转PDF失败: {}", e.getMessage(), e); + throw new Exception("Word文档转换为PDF失败: " + e.getMessage(), e); + } + } + + /** + * 将PDF转换为图片 + */ + private List convertPdfToImages(byte[] pdfBytes, int dpi) throws IOException { + List imageBytesList = new ArrayList<>(); + + try (PDDocument document = PDDocument.load(new ByteArrayInputStream(pdfBytes))) { + PDFRenderer pdfRenderer = new PDFRenderer(document); + + // 遍历每一页 + for (int pageIndex = 0; pageIndex < document.getNumberOfPages(); pageIndex++) { + try { + // 渲染页面为图片 + BufferedImage image = pdfRenderer.renderImageWithDPI(pageIndex, dpi, ImageType.RGB); + + // 转换为字节数组 + ByteArrayOutputStream imageOutputStream = new ByteArrayOutputStream(); + ImageIO.write(image, "PNG", imageOutputStream); + + imageBytesList.add(imageOutputStream.toByteArray()); + imageOutputStream.close(); + + } catch (IOException e) { + log.error("渲染PDF第{}页失败: {}", pageIndex + 1, e.getMessage(), e); + throw new IOException("渲染PDF第" + (pageIndex + 1) + "页失败: " + e.getMessage(), e); + } + } + } + + return imageBytesList; + } + + /** + * 将多张图片合并为一张长图 + */ + public byte[] mergeImagesToSingleImage(List imageBytesList) throws IOException { + if (imageBytesList == null || imageBytesList.isEmpty()) { + throw new IllegalArgumentException("图片列表不能为空"); + } + + if (imageBytesList.size() == 1) { + return imageBytesList.get(0); + } + + // 读取所有图片获取尺寸信息 + List images = new ArrayList<>(); + int totalHeight = 0; + int maxWidth = 0; + + for (byte[] imageBytes : imageBytesList) { + BufferedImage image = ImageIO.read(new ByteArrayInputStream(imageBytes)); + images.add(image); + totalHeight += image.getHeight(); + maxWidth = Math.max(maxWidth, image.getWidth()); + } + + // 创建合并后的图片 + BufferedImage mergedImage = new BufferedImage(maxWidth, totalHeight, BufferedImage.TYPE_INT_RGB); + java.awt.Graphics2D g2d = mergedImage.createGraphics(); + + // 绘制所有图片 + int currentY = 0; + for (BufferedImage image : images) { + g2d.drawImage(image, 0, currentY, null); + currentY += image.getHeight(); + } + + g2d.dispose(); + + // 转换为字节数组 + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ImageIO.write(mergedImage, "PNG", outputStream); + + return outputStream.toByteArray(); + } +} \ No newline at end of file diff --git a/yun-admin/src/main/resources/mapper/service/ServiceApplicationsMapper.xml b/yun-admin/src/main/resources/mapper/service/ServiceApplicationsMapper.xml index 889b663..ab1d6a3 100644 --- a/yun-admin/src/main/resources/mapper/service/ServiceApplicationsMapper.xml +++ b/yun-admin/src/main/resources/mapper/service/ServiceApplicationsMapper.xml @@ -690,4 +690,312 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file