From 6ee2f4b0602041623190b1f4fb1bf657f3004643 Mon Sep 17 00:00:00 2001 From: wang Date: Wed, 7 Oct 2026 09:48:22 +0800 Subject: [PATCH] =?UTF-8?q?fix:=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/entity/CategoryEntity.java | 6 + .../category/domain/form/CategoryAddForm.java | 5 + .../category/domain/vo/CategoryVO.java | 5 + .../goods/domain/entity/GoodsEntity.java | 3 + .../goods/domain/form/GoodsAddForm.java | 3 + .../business/goods/domain/vo/GoodsVO.java | 3 + .../controller/FirmReportsController.java | 12 + .../cost/service/FirmReportsService.java | 50 ++ .../controller/PenaltyApplyController.java | 4 +- .../controller/PenaltyLawyerController.java | 4 +- .../module/penalty/dao/PenaltyApplyDao.java | 2 +- .../module/penalty/dao/PenaltyLawyerDao.java | 2 +- .../penalty/dao/PenaltyLawyerManager.java | 2 +- .../domain/entity/PenaltyApplyEntity.java | 2 +- .../domain/entity/PenaltyLawyerEntity.java | 2 +- .../domain/form/PenaltyApplyAddForm.java | 2 +- .../domain/form/PenaltyApplyQueryForm.java | 2 +- .../domain/form/PenaltyApplyUpdateForm.java | 2 +- .../domain/form/PenaltyLawyerAddForm.java | 2 +- .../domain/form/PenaltyLawyerQueryForm.java | 2 +- .../form/PenaltyLawyerQueryFormNoPage.java | 2 +- .../domain/form/PenaltyLawyerUpdateForm.java | 2 +- .../penalty/domain/vo/PenaltyApplyVO.java | 2 +- .../penalty/domain/vo/PenaltyLawyerVO.java | 2 +- .../penalty/service/PenaltyApplyService.java | 6 +- .../penalty/service/PenaltyLawyerService.java | 2 +- .../ServiceApplicationsController.java | 9 +- .../service/dao/ServiceApplicationsDao.java | 8 +- .../entity/ServiceApplicationsEntity.java | 32 +- .../form/ServiceApplicationsAddForm.java | 10 +- .../form/ServiceApplicationsUpdateForm.java | 5 +- .../domain/vo/ServiceApplicationsVO.java | 33 + .../service/ServiceApplicationsService.java | 791 ++++++++++++------ .../domain/entity/EmployeeEntity.java | 5 + .../employee/service/EmployeeService.java | 9 +- .../login/controller/LoginController.java | 7 + .../system/login/service/LoginService.java | 18 +- .../word/WordApplicationsController.java | 8 +- .../service/ServiceApplicationsMapper.xml | 159 ++-- .../sa/base/common/code/UserErrorCode.java | 2 +- .../message/controller/MessageController.java | 3 +- 41 files changed, 824 insertions(+), 406 deletions(-) diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/business/category/domain/entity/CategoryEntity.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/business/category/domain/entity/CategoryEntity.java index b8d0a77..8e1437c 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/business/category/domain/entity/CategoryEntity.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/business/category/domain/entity/CategoryEntity.java @@ -54,6 +54,12 @@ public class CategoryEntity implements Serializable { */ private Integer sort; + /** + * @deprecated 多人代报已迁移到具体活动 t_goods.can_batch_select;仅保留二进制兼容。 + */ + @Deprecated + private Boolean canBatchSelect; + /** * 删除状态 */ diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/business/category/domain/form/CategoryAddForm.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/business/category/domain/form/CategoryAddForm.java index c48b3f7..4b93d25 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/business/category/domain/form/CategoryAddForm.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/business/category/domain/form/CategoryAddForm.java @@ -37,6 +37,11 @@ public class CategoryAddForm { @Schema(description = "排序|可选") private Integer sort; + /** @deprecated 多人代报已改为具体活动级配置,仅保留旧接口兼容。 */ + @Deprecated + @Schema(description = "旧分类级多人代报字段(已废弃)") + private Boolean canBatchSelect; + @Schema(description = "备注|可选") @Length(max = 200, message = "备注最多200字符") private String remark; diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/business/category/domain/vo/CategoryVO.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/business/category/domain/vo/CategoryVO.java index 1436958..d6dd0eb 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/business/category/domain/vo/CategoryVO.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/business/category/domain/vo/CategoryVO.java @@ -31,6 +31,11 @@ public class CategoryVO { @Schema(description = "排序|可选") private Integer sort; + /** @deprecated 多人代报已改为具体活动级配置,仅保留旧接口兼容。 */ + @Deprecated + @Schema(description = "旧分类级多人代报字段(已废弃)") + private Boolean canBatchSelect; + @Schema(description = "备注|可选") private String remark; diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/business/goods/domain/entity/GoodsEntity.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/business/goods/domain/entity/GoodsEntity.java index 37d0a29..723c6eb 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/business/goods/domain/entity/GoodsEntity.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/business/goods/domain/entity/GoodsEntity.java @@ -44,6 +44,9 @@ public class GoodsEntity { */ private String timeType; + /** 是否允许一人代同所多人申报 */ + private Boolean canBatchSelect; + /** * 上架状态 */ diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/business/goods/domain/form/GoodsAddForm.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/business/goods/domain/form/GoodsAddForm.java index 9eb4090..e3f2354 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/business/goods/domain/form/GoodsAddForm.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/business/goods/domain/form/GoodsAddForm.java @@ -37,6 +37,9 @@ public class GoodsAddForm { @Schema(description = "计算类型") private String timeType; + @Schema(description = "是否允许一人代同所多人申报") + private Boolean canBatchSelect; + @Schema(description = "备注|可选") private String remark; } diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/business/goods/domain/vo/GoodsVO.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/business/goods/domain/vo/GoodsVO.java index 7dd291c..db6f13d 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/business/goods/domain/vo/GoodsVO.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/business/goods/domain/vo/GoodsVO.java @@ -29,6 +29,9 @@ public class GoodsVO { @Schema(description = "计算类型") private String timeType; + @Schema(description = "是否允许一人代同所多人申报") + private Boolean canBatchSelect; + @Schema(description = "上架状态") private Boolean shelvesFlag; diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/cost/controller/FirmReportsController.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/cost/controller/FirmReportsController.java index f6b3ccb..0139a85 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/cost/controller/FirmReportsController.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/cost/controller/FirmReportsController.java @@ -49,6 +49,18 @@ public class FirmReportsController { return firmReportsService.query(previousQuarter, previousYear); } + @Operation(summary = "按活动季度查询营业收入是否已提交") + @GetMapping("/firmReports/queryByQuarter") + public ResponseDTO queryByQuarter(@RequestParam Integer year, @RequestParam Integer quarter) { + return firmReportsService.isQuarterIncomeSubmitted(quarter, year); + } + + @Operation(summary = "查询当前律所是否具备成本权限") + @GetMapping("/firmReports/firmHasCostPermission") + public ResponseDTO firmHasCostPermission() { + return firmReportsService.currentFirmHasCostPermission(); + } + @Operation(summary = "分页查询 @author wzh") @PostMapping("/firmReports/queryPage") //@SaCheckPermission("firmReports:query") diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/cost/service/FirmReportsService.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/cost/service/FirmReportsService.java index 529c4e7..783371a 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/cost/service/FirmReportsService.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/cost/service/FirmReportsService.java @@ -16,9 +16,11 @@ import net.lab1024.sa.admin.module.cost.domain.form.FirmReportsUpdateForm; import net.lab1024.sa.admin.module.cost.domain.vo.FirmReportsCountVO; import net.lab1024.sa.admin.module.cost.domain.vo.FirmReportsVO; import net.lab1024.sa.admin.module.system.department.service.DepartmentService; +import net.lab1024.sa.admin.module.system.employee.dao.EmployeeDao; import net.lab1024.sa.admin.module.system.login.domain.RequestEmployee; 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.service.service.ServiceApplicationsService; import net.lab1024.sa.admin.util.AdminRequestUtil; import net.lab1024.sa.admin.util.DateTimeUtil; import net.lab1024.sa.base.common.code.UnexpectedErrorCode; @@ -33,6 +35,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import net.lab1024.sa.base.module.support.dict.service.DictService; import org.apache.commons.collections4.CollectionUtils; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; @@ -55,6 +58,10 @@ public class FirmReportsService { private DepartmentService departmentService; @Resource private RoleEmployeeService roleEmployeeService; + @Resource + private EmployeeDao employeeDao; + @Resource + private ServiceApplicationsService serviceApplicationsService; /** * 分页查询 */ @@ -100,6 +107,7 @@ public class FirmReportsService { /** * 添加 */ + @Transactional(rollbackFor = Exception.class) public ResponseDTO add(FirmReportsAddForm addForm) { if (!hasCostPermission(AdminRequestUtil.getRequestUser())) { return ResponseDTO.error(UserErrorCode.NO_PERMISSION); @@ -123,6 +131,7 @@ public class FirmReportsService { * 更新 * */ + @Transactional(rollbackFor = Exception.class) public ResponseDTO update(FirmReportsUpdateForm updateForm) { if (!hasCostPermission(AdminRequestUtil.getRequestUser())) { return ResponseDTO.error(UserErrorCode.NO_PERMISSION); @@ -193,6 +202,7 @@ public class FirmReportsService { return ResponseDTO.ok(); } + @Transactional(rollbackFor = Exception.class) public ResponseDTO commit(ValidateList idList) { if (!hasCostPermission(AdminRequestUtil.getRequestUser())) { return ResponseDTO.error(UserErrorCode.NO_PERMISSION); @@ -226,9 +236,21 @@ public class FirmReportsService { } firmReportsDao.commit(idList); + for (Integer id : idList) { + refreshActualCost(firmReportsDao.selectById(id)); + } return ResponseDTO.ok(); } + private void refreshActualCost(FirmReportsEntity report) { + if (report == null || report.getFirmId() == null || report.getDeclareYear() == null + || report.getDeclareQuarter() == null) { + return; + } + serviceApplicationsService.recalculateActualCost(report.getFirmId().longValue(), + report.getDeclareYear(), Integer.valueOf(report.getDeclareQuarter())); + } + /** * 提交前预检查,返回缺失的前置季度列表 */ @@ -335,6 +357,34 @@ public class FirmReportsService { return ResponseDTO.ok(canReport); } + /** + * 活动上报专用校验:成本权限按律所判断,只要本所有一名有效人员具备权限, + * 就要求活动所属季度已经提交收入;无成本权限的律所不受此限制。 + */ + public ResponseDTO isQuarterIncomeSubmitted(Integer quarter, Integer year) { + RequestEmployee requestUser = AdminRequestUtil.getRequestUser(); + List roles = roleEmployeeService.getRoleIdList(requestUser.getEmployeeId()); + if (!AdminRequestUtil.isFirmRole(roles)) { + return ResponseDTO.error(UserErrorCode.NO_PERMISSION); + } + boolean firmHasCostPermission = hasFirmCostPermission(requestUser.getDepartmentId()); + if (!firmHasCostPermission) { + return ResponseDTO.ok(true); + } + return ResponseDTO.ok(firmReportsDao.query(quarter, year, requestUser.getDepartmentId())); + } + + public ResponseDTO currentFirmHasCostPermission() { + RequestEmployee requestUser = AdminRequestUtil.getRequestUser(); + return ResponseDTO.ok(hasFirmCostPermission(requestUser.getDepartmentId())); + } + + private boolean hasFirmCostPermission(Long firmId) { + return firmId != null && employeeDao.selectByDepartmentId(firmId, false).stream() + .anyMatch(employee -> Boolean.TRUE.equals(employee.getCostVisibleFlag()) + && !Boolean.TRUE.equals(employee.getDisabledFlag())); + } + public FirmReportsCountVO income(Integer year) { // 获取当前用户 RequestEmployee requestUser = AdminRequestUtil.getRequestUser(); diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/controller/PenaltyApplyController.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/controller/PenaltyApplyController.java index fa31175..0560429 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/controller/PenaltyApplyController.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/controller/PenaltyApplyController.java @@ -21,7 +21,7 @@ import javax.annotation.Resource; import javax.validation.Valid; /** - * 无处罚证明申请主表 Controller + * 无处分证明申请主表 Controller * * @Author wzh * @Date 2026-01-07 21:36:44 @@ -29,7 +29,7 @@ import javax.validation.Valid; */ @RestController -@Tag(name = "无处罚证明申请主表") +@Tag(name = "无处分证明申请主表") public class PenaltyApplyController { @Resource diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/controller/PenaltyLawyerController.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/controller/PenaltyLawyerController.java index fbb9f71..78f5e7e 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/controller/PenaltyLawyerController.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/controller/PenaltyLawyerController.java @@ -22,7 +22,7 @@ import javax.annotation.Resource; import javax.validation.Valid; /** - * 处罚人 Controller + * 处分人 Controller * * @Author wzh * @Date 2026-01-09 10:53:07 @@ -30,7 +30,7 @@ import javax.validation.Valid; */ @RestController -@Tag(name = "处罚人") +@Tag(name = "处分人") public class PenaltyLawyerController { @Resource diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/dao/PenaltyApplyDao.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/dao/PenaltyApplyDao.java index 733799b..45eb27b 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/dao/PenaltyApplyDao.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/dao/PenaltyApplyDao.java @@ -10,7 +10,7 @@ import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; /** - * 无处罚证明申请主表 Dao + * 无处分证明申请主表 Dao * * @Author wzh * @Date 2026-01-07 21:36:44 diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/dao/PenaltyLawyerDao.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/dao/PenaltyLawyerDao.java index f74370e..0a157f0 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/dao/PenaltyLawyerDao.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/dao/PenaltyLawyerDao.java @@ -14,7 +14,7 @@ import org.apache.ibatis.annotations.Param; import javax.validation.Valid; /** - * 处罚人 Dao + * 处分人 Dao * * @Author wzh * @Date 2026-01-09 10:53:07 diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/dao/PenaltyLawyerManager.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/dao/PenaltyLawyerManager.java index 59ebff5..822858f 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/dao/PenaltyLawyerManager.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/dao/PenaltyLawyerManager.java @@ -6,7 +6,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.springframework.stereotype.Service; /** - * 处罚人 Manager + * 处分人 Manager * * @Author wzh * @Date 2026-01-09 10:53:07 diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/entity/PenaltyApplyEntity.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/entity/PenaltyApplyEntity.java index d709b3e..e7d468f 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/entity/PenaltyApplyEntity.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/entity/PenaltyApplyEntity.java @@ -10,7 +10,7 @@ import java.time.LocalDateTime; import lombok.Data; /** - * 无处罚证明申请主表 实体类 + * 无处分证明申请主表 实体类 * * @Author wzh * @Date 2026-01-07 21:36:44 diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/entity/PenaltyLawyerEntity.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/entity/PenaltyLawyerEntity.java index 774a2a7..e6673b2 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/entity/PenaltyLawyerEntity.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/entity/PenaltyLawyerEntity.java @@ -7,7 +7,7 @@ import java.time.LocalDateTime; import lombok.Data; /** - * 处罚人 实体类 + * 处分人 实体类 * * @Author wzh * @Date 2026-01-09 10:53:07 diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/form/PenaltyApplyAddForm.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/form/PenaltyApplyAddForm.java index 0cc383d..01b4fd2 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/form/PenaltyApplyAddForm.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/form/PenaltyApplyAddForm.java @@ -7,7 +7,7 @@ import javax.validation.constraints.NotNull; import lombok.Data; /** - * 无处罚证明申请主表 新建表单 + * 无处分证明申请主表 新建表单 * * @Author wzh * @Date 2026-01-07 21:36:44 diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/form/PenaltyApplyQueryForm.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/form/PenaltyApplyQueryForm.java index bc8181a..5fecf2a 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/form/PenaltyApplyQueryForm.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/form/PenaltyApplyQueryForm.java @@ -8,7 +8,7 @@ import java.time.LocalDate; import java.time.LocalDateTime; /** - * 无处罚证明申请主表 分页查询表单 + * 无处分证明申请主表 分页查询表单 * * @Author wzh * @Date 2026-01-07 21:36:44 diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/form/PenaltyApplyUpdateForm.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/form/PenaltyApplyUpdateForm.java index 7859a45..1183af1 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/form/PenaltyApplyUpdateForm.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/form/PenaltyApplyUpdateForm.java @@ -5,7 +5,7 @@ import javax.validation.constraints.NotNull; import lombok.Data; /** - * 无处罚证明申请主表 更新表单 + * 无处分证明申请主表 更新表单 * * @Author wzh * @Date 2026-01-07 21:36:44 diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/form/PenaltyLawyerAddForm.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/form/PenaltyLawyerAddForm.java index af46b75..10c1f22 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/form/PenaltyLawyerAddForm.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/form/PenaltyLawyerAddForm.java @@ -6,7 +6,7 @@ import javax.validation.constraints.NotNull; import lombok.Data; /** - * 处罚人 新建表单 + * 处分人 新建表单 * * @Author wzh * @Date 2026-01-09 10:53:07 diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/form/PenaltyLawyerQueryForm.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/form/PenaltyLawyerQueryForm.java index edee94a..b750bb1 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/form/PenaltyLawyerQueryForm.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/form/PenaltyLawyerQueryForm.java @@ -5,7 +5,7 @@ import lombok.Data; import lombok.EqualsAndHashCode; /** - * 处罚人 分页查询表单 + * 处分人 分页查询表单 * * @Author wzh * @Date 2026-01-09 10:53:07 diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/form/PenaltyLawyerQueryFormNoPage.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/form/PenaltyLawyerQueryFormNoPage.java index 46677e4..f8dd061 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/form/PenaltyLawyerQueryFormNoPage.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/form/PenaltyLawyerQueryFormNoPage.java @@ -5,7 +5,7 @@ import lombok.EqualsAndHashCode; import net.lab1024.sa.base.common.domain.PageParam; /** - * 处罚人 分页查询表单 + * 处分人 分页查询表单 * * @Author wzh * @Date 2026-01-09 10:53:07 diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/form/PenaltyLawyerUpdateForm.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/form/PenaltyLawyerUpdateForm.java index cf18c1d..2c9629e 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/form/PenaltyLawyerUpdateForm.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/form/PenaltyLawyerUpdateForm.java @@ -5,7 +5,7 @@ import javax.validation.constraints.NotNull; import lombok.Data; /** - * 处罚人 更新表单 + * 处分人 更新表单 * * @Author wzh * @Date 2026-01-09 10:53:07 diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/vo/PenaltyApplyVO.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/vo/PenaltyApplyVO.java index 84ea74d..f8c8918 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/vo/PenaltyApplyVO.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/vo/PenaltyApplyVO.java @@ -6,7 +6,7 @@ import java.time.LocalDateTime; import lombok.Data; /** - * 无处罚证明申请主表 列表VO + * 无处分证明申请主表 列表VO * * @Author wzh * @Date 2026-01-07 21:36:44 diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/vo/PenaltyLawyerVO.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/vo/PenaltyLawyerVO.java index 0c74437..fa64997 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/vo/PenaltyLawyerVO.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/domain/vo/PenaltyLawyerVO.java @@ -5,7 +5,7 @@ import java.time.LocalDateTime; import lombok.Data; /** - * 处罚人 列表VO + * 处分人 列表VO * * @Author wzh * @Date 2026-01-09 10:53:07 diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/service/PenaltyApplyService.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/service/PenaltyApplyService.java index 737d668..303a51a 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/service/PenaltyApplyService.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/service/PenaltyApplyService.java @@ -35,7 +35,7 @@ import javax.validation.Valid; import lombok.extern.slf4j.Slf4j; /** - * 无处罚证明申请主表 Service + * 无处分证明申请主表 Service * * @Author wzh * @Date 2026-01-07 21:36:44 @@ -109,13 +109,13 @@ public class PenaltyApplyService { // PERSONAL = 个人证明(查申请人自己) // FIRM = 律所证明(查律所) if ("PERSONAL".equals(addForm.getApplyType())) { - // 个人证明:查询申请人自己是否有处罚记录 + // 个人证明:查询申请人自己是否有处分记录 Boolean query = penaltyLawyerService.query(entity.getActualName()); if (query) { return ResponseDTO.error(UserErrorCode.FORM_REPEAT_SUBMIT_PENALTY); } } else if ("FIRM".equals(addForm.getApplyType())) { - // 律所证明:查询律所是否有处罚记录 + // 律所证明:查询律所是否有处分记录 DepartmentVO departmentById = departmentService.getDepartmentById(entity.getDepartmentId()); Boolean query = penaltyLawyerService.queryNoName(departmentById.getDepartmentName()); if (query) { diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/service/PenaltyLawyerService.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/service/PenaltyLawyerService.java index ecfb8ed..870b187 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/service/PenaltyLawyerService.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/penalty/service/PenaltyLawyerService.java @@ -23,7 +23,7 @@ import javax.annotation.Resource; import javax.validation.Valid; /** - * 处罚人 Service + * 处分人 Service * * @Author wzh * @Date 2026-01-09 10:53:07 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 b9257d5..00920f5 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 @@ -5,6 +5,7 @@ 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.SameFirmLawyerVO; 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; @@ -42,6 +43,12 @@ public class ServiceApplicationsController { return ResponseDTO.ok(serviceApplicationsService.queryDetail(applicationId)); } + @Operation(summary = "获取同律所已签署承诺书的合格同行律师列表") + @GetMapping("/serviceApplications/querySameFirmLawyers") + public ResponseDTO> querySameFirmLawyers() { + return ResponseDTO.ok(serviceApplicationsService.querySameFirmLawyers()); + } + @Operation(summary = "服务审批分页查询 @author wzh") @PostMapping("/serviceApplications/queryPage") //@SaCheckPermission("serviceApplications:query") @@ -243,4 +250,4 @@ public class ServiceApplicationsController { public void exportActivityDetail(@RequestBody ServiceApplicationsQueryForm queryForm, HttpServletResponse response) { serviceApplicationsService.exportActivityDetail(queryForm, 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 99ebf02..8dbe6d6 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 @@ -222,13 +222,13 @@ public interface ServiceApplicationsDao extends BaseMapper participantLawyerIds; +} diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/form/ServiceApplicationsUpdateForm.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/form/ServiceApplicationsUpdateForm.java index 1a580f5..f6de198 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/form/ServiceApplicationsUpdateForm.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/service/domain/form/ServiceApplicationsUpdateForm.java @@ -95,4 +95,7 @@ public class ServiceApplicationsUpdateForm{ * 申报服务类型 */ private String serviceType; -} \ No newline at end of file + + /** 勾选同所同行律师ID列表 */ + private java.util.List participantLawyerIds; +} 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 7baf8b4..6505953 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 @@ -105,6 +105,9 @@ public class ServiceApplicationsVO { @Schema(description = "职业证号") private String certificateNumber; + @Schema(description = "草稿暂存的同行代报律师ID,逗号分隔") + private String draftParticipantIds; + private String activityCategory; private String activityName; @@ -117,6 +120,36 @@ public class ServiceApplicationsVO { private String attachmentIds; @Schema(description = "附件地址集合") private List attachmentList; + /** + * 证明材料集合 + */ + private List proofMaterials; + + /** + * 是否批量/代勾选申报(0-否, 1-是) + */ + private Integer isBatch; + + /** + * 主申报人/提交人律师ID + */ + private Long declarerId; + + /** + * 主申报人/提交人姓名 + */ + private String declarerName; + + /** + * 父级申报单ID(主记录为NULL, 子记录指向主记录ID) + */ + private Long parentApplicationId; + + /** + * 同所参与同行姓名汇总 + */ + private String participantNames; + /** * 职务id */ 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 bd8adec..c86fcda 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 @@ -107,6 +107,9 @@ public class ServiceApplicationsService { @Resource private FirmReportsDao firmReportsDao; + @Resource + private net.lab1024.sa.admin.module.letter.service.LetterService letterService; + @Resource private net.lab1024.sa.base.module.support.message.service.MessageService messageService; @@ -143,7 +146,7 @@ public class ServiceApplicationsService { boolean isCurrentUserRecord = existingRecords.stream() .anyMatch(record -> record.getUserId().equals(currentUserId)); - if (!isCurrentUserRecord || existingRecords.size() > 1) { + if (!isCurrentUserRecord || !isRecordNoOwnedByFamily(existingRecords, addForm.getApplicationId())) { // 如果不是当前用户的记录,或者有多条记录(包括当前用户),则不允许 throw new BusinessException(UserErrorCode.HAS_EXIST.getMsg() + existingRecords.get(0).getUserName() + UserErrorCode.HAS_EXIST1.getMsg()); @@ -152,6 +155,16 @@ public class ServiceApplicationsService { } } + private boolean isRecordNoOwnedByFamily(List records, Long applicationId) { + ServiceApplicationsEntity current = serviceApplicationsDao.selectById(applicationId); + if (current == null) { + return false; + } + Long mainId = current.getParentApplicationId() == null ? current.getApplicationId() : current.getParentApplicationId(); + return records.stream().allMatch(record -> Objects.equals(record.getApplicationId(), mainId) + || Objects.equals(record.getParentApplicationId(), mainId)); + } + // 查看详情 public ServiceApplicationsVO queryDetail(Long applicationId) { ServiceApplicationsVO serviceApplicationsVO = new ServiceApplicationsVO(); @@ -182,6 +195,43 @@ public class ServiceApplicationsService { return serviceApplicationsVO; } + /** + * 获取同律所且已签署有效承诺书的合格同行律师列表 + */ + public List querySameFirmLawyers() { + RequestEmployee requestUser = AdminRequestUtil.getRequestUser(); + if (requestUser == null) { + return new ArrayList<>(); + } + Long currentUserId = requestUser.getUserId(); + Long departmentId = requestUser.getDepartmentId(); + if (departmentId == null) { + EmployeeEntity currentEmp = employeeService.getById(currentUserId); + if (currentEmp != null) { + departmentId = currentEmp.getDepartmentId(); + } + } + if (departmentId == null) { + return new ArrayList<>(); + } + // 查询同部门/律所的所有员工 + List sameFirmEmployees = employeeDao.selectList( + new LambdaQueryWrapper() + .eq(EmployeeEntity::getDepartmentId, departmentId) + .eq(EmployeeEntity::getDisabledFlag, false) + .eq(EmployeeEntity::getDeletedFlag, false) + ); + if (CollectionUtils.isEmpty(sameFirmEmployees)) { + return new ArrayList<>(); + } + // 过滤:排除本人,且必须在 letterService.isLetter(employeeId) 为 true + return sameFirmEmployees.stream() + .filter(emp -> !emp.getEmployeeId().equals(currentUserId)) + .filter(emp -> Boolean.TRUE.equals(letterService.isLetter(emp.getEmployeeId()))) + .map(emp -> SmartBeanUtil.copy(emp, SameFirmLawyerVO.class)) + .collect(Collectors.toList()); + } + /** * 分页查询 */ @@ -208,27 +258,10 @@ public class ServiceApplicationsService { queryForm.setCostReportViewOnly(costReportViewOnly); } } else if (isFirmRole) { - // 律所主任或行政:能看到自己部门范围内的数据,但只显示firmAuditStatus为待审核、通过和拒绝的数据和自己创建的数据 - Integer oneByRoleId = dataScopeViewService.getOneByRoleId(roleIdList.get(0).getRoleId()); - // 获取自己部门范围内的数据 - List departmentEmployees = new ArrayList<>(); - if (DataScopeViewTypeEnum.ME.getValue().equals(oneByRoleId)) { - departmentEmployees = dataScopeViewService.getCanViewEmployeeId(DataScopeViewTypeEnum.ME, - requestUser.getUserId()); - } else if (DataScopeViewTypeEnum.DEPARTMENT.getValue().equals(oneByRoleId)) { - departmentEmployees = dataScopeViewService.getCanViewEmployeeId(DataScopeViewTypeEnum.DEPARTMENT, - requestUser.getUserId()); - } - - // 确保包含当前用户ID,以便能看到自己创建的数据 - if (departmentEmployees == null) { - departmentEmployees = new ArrayList<>(); - } - if (!departmentEmployees.contains(requestUser.getUserId())) { - departmentEmployees.add(requestUser.getUserId()); - } - - queryForm.setEmployeeIdList(departmentEmployees); + // 律所主任或行政按 firm_id 查看本所全部申报,不能再受角色员工数据范围限制, + // 否则未包含在数据范围内的本所律师会被待审核统计命中,却无法在审批列表中看到。 + queryForm.setFirmId(requestUser.getDepartmentId()); + queryForm.setEmployeeIdList(null); // 律所主任或行政可以查看部门内所有已审核数据以及自己创建的有效数据(明确排除未提交的草稿) queryForm.setIncludeFirmReviewed(true); } else { @@ -335,10 +368,13 @@ public class ServiceApplicationsService { /** * 添加 */ + @Transactional(rollbackFor = Exception.class) public ResponseDTO add(ServiceApplicationsAddForm addForm) { + validateActivityEnd(addForm.getServiceEnd()); ServiceApplicationsEntity serviceApplicationsEntity = SmartBeanUtil.copy(addForm, ServiceApplicationsEntity.class); + serviceApplicationsEntity.setDraftParticipantIds(serializeParticipantIds(addForm.getParticipantLawyerIds())); if (addForm.getApplicationId() == null) { // 新增的时候判断活动类型查询案号有没有存在 if ("TIME".equals(addForm.getServiceType())) { @@ -364,7 +400,7 @@ public class ServiceApplicationsService { boolean isCurrentUserRecord = existingRecords.stream() .anyMatch(record -> record.getUserId().equals(userId)); - if (!isCurrentUserRecord || existingRecords.size() > 1) { + if (!isCurrentUserRecord || !isRecordNoOwnedByFamily(existingRecords, addForm.getApplicationId())) { // 如果不是当前用户的记录,或者有多条记录(包括当前用户),则不允许 throw new BusinessException(UserErrorCode.HAS_EXIST.getMsg() + existingRecords.get(0).getUserName() + UserErrorCode.HAS_EXIST1.getMsg()); @@ -380,7 +416,18 @@ public class ServiceApplicationsService { * 更新 * */ + @Transactional(rollbackFor = Exception.class) public ResponseDTO update(ServiceApplicationsUpdateForm updateForm) { + validateActivityEnd(updateForm.getServiceEnd()); + ServiceApplicationsEntity currentEntity = serviceApplicationsDao.selectById(updateForm.getApplicationId()); + if (currentEntity == null) { + throw new BusinessException("申报记录不存在"); + } + Long currentUserId = AdminRequestUtil.getRequestUser().getUserId(); + Long ownerId = currentEntity.getDeclarerId() == null ? currentEntity.getUserId() : currentEntity.getDeclarerId(); + if (currentEntity.getParentApplicationId() != null || !Objects.equals(ownerId, currentUserId)) { + throw new BusinessException("代报记录仅允许主申报人修改"); + } // 判断活动类型查询案号有没有存在 if ("TIME".equals(updateForm.getServiceType())) { // 当前登录人的ID @@ -393,7 +440,7 @@ public class ServiceApplicationsService { boolean isCurrentUserRecord = existingRecords.stream() .anyMatch(record -> record.getUserId().equals(userId)); - if (!isCurrentUserRecord || existingRecords.size() > 1) { + if (!isCurrentUserRecord || !isRecordNoOwnedByFamily(existingRecords, updateForm.getApplicationId())) { // 如果不是当前用户的记录,或者有多条记录(包括当前用户),则不允许 throw new BusinessException(UserErrorCode.HAS_EXIST.getMsg() + existingRecords.get(0).getUserName() + UserErrorCode.HAS_EXIST1.getMsg()); @@ -402,6 +449,7 @@ public class ServiceApplicationsService { } ServiceApplicationsEntity serviceApplicationsEntity = SmartBeanUtil.copy(updateForm, ServiceApplicationsEntity.class); + serviceApplicationsEntity.setDraftParticipantIds(serializeParticipantIds(updateForm.getParticipantLawyerIds())); // serviceApplicationsEntity.setFirmAuditStatus(ReviewEnum.APPROVAL.getValue()); serviceApplicationsDao.updateById(serviceApplicationsEntity); @@ -553,60 +601,77 @@ public class ServiceApplicationsService { /** * 提交 */ + @Transactional(rollbackFor = Exception.class) public ResponseDTO submit(ServiceApplicationsAddForm addForm) { - // 案号重复性校验 - validateRecordNoUniquenessForSubmit(addForm); - - if (null == addForm.getApplicationId()) { - // 新增提交 - ServiceApplicationsEntity serviceApplications = new ServiceApplicationsEntity(); - SmartBeanUtil.copyProperties(addForm, serviceApplications); - serviceApplications.setFirmAuditStatus(ReviewEnum.APPROVAL.getValue()); - serviceApplications.setReportTime(LocalDateTime.now()); - serviceApplicationsDao.insert(serviceApplications); - serviceApplicationsDao.markSubmitted(serviceApplications.getApplicationId(), - ReviewEnum.APPROVAL.getValue()); - } else { - // 编辑提交(二次提交/驳回后重新提交) - ServiceApplicationsEntity serviceApplications = serviceApplicationsDao - .selectById(addForm.getApplicationId()); - SmartBeanUtil.copyProperties(addForm, serviceApplications); - serviceApplications.setFirmAuditStatus(ReviewEnum.APPROVAL.getValue()); - // 首次从草稿/已有记录提交时补提交时间;二次提交保持原有提交时间不变。 - if (serviceApplications.getReportTime() == null) { - serviceApplications.setReportTime(LocalDateTime.now()); + // 从列表直接提交草稿时,前端行数据没有 List 字段,需恢复保存草稿时暂存的关联人。 + if (addForm.getApplicationId() != null && CollectionUtils.isEmpty(addForm.getParticipantLawyerIds())) { + ServiceApplicationsEntity draft = serviceApplicationsDao.selectById(addForm.getApplicationId()); + if (draft != null) { + addForm.setParticipantLawyerIds(parseParticipantIds(draft.getDraftParticipantIds())); } - serviceApplicationsDao.updateById(serviceApplications); - serviceApplicationsDao.markSubmitted(serviceApplications.getApplicationId(), - ReviewEnum.APPROVAL.getValue()); } - return ResponseDTO.ok(); + // 统一复用完整提交逻辑:校验同行、生成子记录、同步主子状态并保证事务一致性。 + return addSubmit(addForm); } /** * 批量删除 */ + @Transactional(rollbackFor = Exception.class) public ResponseDTO batchDelete(List idList) { if (CollectionUtils.isEmpty(idList)) { return ResponseDTO.ok(); } - serviceApplicationsDao.batchUpdateDeleted(idList, true); + Set familyIds = resolveAuthorizedFamilyIds(idList); + serviceApplicationsDao.batchUpdateDeleted(new ArrayList<>(familyIds), true); return ResponseDTO.ok(); } /** * 单个删除 */ + @Transactional(rollbackFor = Exception.class) public ResponseDTO delete(Long applicationId) { if (null == applicationId) { return ResponseDTO.ok(); } - serviceApplicationsDao.updateDeleted(applicationId, true); + Set familyIds = resolveAuthorizedFamilyIds(Collections.singletonList(applicationId)); + serviceApplicationsDao.batchUpdateDeleted(new ArrayList<>(familyIds), true); return ResponseDTO.ok(); } + private Set resolveAuthorizedFamilyIds(List applicationIds) { + RequestEmployee requestUser = AdminRequestUtil.getRequestUser(); + List roles = roleEmployeeService.getRoleIdList(requestUser.getUserId()); + boolean manager = AdminRequestUtil.isFirmRole(roles) || AdminRequestUtil.isAssociationRole(roles); + Set result = new LinkedHashSet<>(); + + for (Long applicationId : applicationIds) { + ServiceApplicationsEntity record = serviceApplicationsDao.selectById(applicationId); + if (record == null) { + continue; + } + Long ownerId = record.getDeclarerId() == null ? record.getUserId() : record.getDeclarerId(); + if (!manager && !Objects.equals(ownerId, requestUser.getUserId())) { + throw new BusinessException("无权删除其他申报人的记录"); + } + if (AdminRequestUtil.isFirmRole(roles) + && !Objects.equals(record.getFirmId(), requestUser.getDepartmentId())) { + throw new BusinessException("无权删除其他律所的申报记录"); + } + + Long mainId = record.getParentApplicationId() == null + ? record.getApplicationId() : record.getParentApplicationId(); + result.add(mainId); + serviceApplicationsDao.selectList(new LambdaQueryWrapper() + .eq(ServiceApplicationsEntity::getParentApplicationId, mainId)) + .forEach(item -> result.add(item.getApplicationId())); + } + return result; + } + @Transactional public ResponseDTO importService(MultipartFile file) { // 预加载转换器缓存 @@ -789,12 +854,13 @@ public class ServiceApplicationsService { * @param idList * @return */ + @Transactional(rollbackFor = Exception.class) public ResponseDTO batchSubmit(List idList) { if (CollectionUtils.isEmpty(idList)) { return ResponseDTO.ok(); } - serviceApplicationsDao.batchSubmit(idList, ReviewEnum.APPROVAL.getValue()); + serviceApplicationsDao.batchSubmit(new ArrayList<>(expandFamilyIds(idList)), ReviewEnum.APPROVAL.getValue()); return ResponseDTO.ok(); } @@ -804,10 +870,12 @@ public class ServiceApplicationsService { * @param idList * @return */ + @Transactional(rollbackFor = Exception.class) public ResponseDTO batchSubmitAsFirm(List idList) { if (CollectionUtils.isEmpty(idList)) { return ResponseDTO.ok(); } + idList = new ArrayList<>(expandFamilyIds(idList)); // 获取当前用户信息 RequestEmployee requestUser = AdminRequestUtil.getRequestUser(); @@ -820,7 +888,7 @@ public class ServiceApplicationsService { } Long targetFirmId = null; - LocalDateTime firstReportTime = null; + Set activityPeriods = new LinkedHashSet<>(); // 查询这些记录的详细信息,进行权限和状态校验 for (Long id : idList) { @@ -829,10 +897,9 @@ public class ServiceApplicationsService { return ResponseDTO.error(UserErrorCode.DATA_NOT_EXIST, "记录ID:" + id + "不存在"); } - // 记录律所ID和上报时间(用于后续成本校验) + // 记录律所ID和活动所属季度(用于后续收入校验) if (targetFirmId == null) { targetFirmId = record.getFirmId(); - firstReportTime = record.getReportTime(); } // 校验1:只能上报本所的数据 @@ -851,20 +918,29 @@ public class ServiceApplicationsService { !ReviewEnum.REFUSE.getValue().equals(associationStatus)) { return ResponseDTO.error(UserErrorCode.PARAM_ERROR, "记录ID:" + id + "已上报或正在审核中,无法重复上报"); } - } - - // 校验4:仅对有成本查看权限的用户(参与成本核算的律所)检查前置季度成本是否已提交 - if (Boolean.TRUE.equals(requestUser.getCostVisibleFlag()) && targetFirmId != null && firstReportTime != null) { - int reportYear = firstReportTime.getYear(); - int reportMonth = firstReportTime.getMonthValue(); - int reportQuarter = (reportMonth - 1) / 3 + 1; - List unsubmittedQuarters = firmReportsDao.findUnsubmittedEarlierQuarters(targetFirmId.intValue(), - reportYear, reportQuarter); - if (unsubmittedQuarters != null && !unsubmittedQuarters.isEmpty()) { - String quarterNames = String.join("、", unsubmittedQuarters); - return ResponseDTO.userErrorParam( - "请先提交" + quarterNames + "的成本报表后,再批量上报服务数据"); + if (record.getServiceEnd() == null) { + return ResponseDTO.userErrorParam("记录ID:" + id + "缺少活动结束时间,请补充后再上报"); + } + int activityYear = record.getServiceEnd().getYear(); + int activityQuarter = (record.getServiceEnd().getMonthValue() - 1) / 3 + 1; + activityPeriods.add(activityYear + "-" + activityQuarter); + } + + // 仅对参与成本填报的律所检查每条活动所属季度的收入报表 + if (targetFirmId != null && isFirmHasCostPermission(targetFirmId)) { + List missingPeriods = new ArrayList<>(); + for (String period : activityPeriods) { + String[] parts = period.split("-"); + int year = Integer.parseInt(parts[0]); + int quarter = Integer.parseInt(parts[1]); + if (!firmReportsDao.query(quarter, year, targetFirmId)) { + missingPeriods.add(year + "年第" + quarter + "季度"); + } + } + if (!missingPeriods.isEmpty()) { + return ResponseDTO.userErrorParam("以下活动所属季度尚未提交营业收入:" + + String.join("、", missingPeriods) + ",请完成填报后再上报律协"); } } @@ -873,6 +949,24 @@ public class ServiceApplicationsService { return ResponseDTO.ok(); } + private Set expandFamilyIds(List applicationIds) { + Set result = new LinkedHashSet<>(); + for (Long applicationId : applicationIds) { + ServiceApplicationsEntity record = serviceApplicationsDao.selectById(applicationId); + if (record == null) { + continue; + } + Long mainId = record.getParentApplicationId() == null + ? record.getApplicationId() : record.getParentApplicationId(); + result.add(mainId); + serviceApplicationsDao.selectList(new LambdaQueryWrapper() + .eq(ServiceApplicationsEntity::getParentApplicationId, mainId)) + .forEach(item -> result.add(item.getApplicationId())); + } + return result; + } + + @Transactional(rollbackFor = Exception.class) public ResponseDTO addSubmit(@Valid ServiceApplicationsAddForm addForm) { // ServiceApplicationsEntity serviceApplicationsEntity = @@ -910,52 +1004,235 @@ public class ServiceApplicationsService { + serviceApplicationsEntity.get(0).getUserName() + UserErrorCode.HAS_EXIST1.getMsg()); } } + + validateActivityEnd(addForm.getServiceEnd()); + + // 1. 强校验:主申报人必须签署承诺书 + Long currentUserId = AdminRequestUtil.getRequestUser().getUserId(); + if (!Boolean.TRUE.equals(letterService.isLetter(currentUserId))) { + throw new BusinessException("您尚未上传有效申报承诺书,无法进行活动申报"); + } + + List participantLawyerIds = addForm.getParticipantLawyerIds(); + boolean isBatchMode = CollectionUtils.isNotEmpty(participantLawyerIds); + + // 收集同行姓名 + List participantNameList = new ArrayList<>(); + EmployeeEntity mainEmp = employeeService.getById(currentUserId); + if (mainEmp == null || Boolean.TRUE.equals(mainEmp.getDisabledFlag()) + || Boolean.TRUE.equals(mainEmp.getDeletedFlag())) { + throw new BusinessException("当前申报人不存在或已停用"); + } + + if (isBatchMode) { + GoodsEntity activity = goodsService.queryById(addForm.getActivityNameId()); + if (activity == null || !Boolean.TRUE.equals(activity.getCanBatchSelect())) { + throw new BusinessException("当前活动不支持同行批量申报"); + } + + // 2. 强校验:所有同行律师必须也签署承诺书 + Set uniqueParticipantIds = new LinkedHashSet<>(participantLawyerIds); + if (uniqueParticipantIds.contains(currentUserId)) { + throw new BusinessException("同行律师不能包含申报人本人"); + } + participantLawyerIds = new ArrayList<>(uniqueParticipantIds); + for (Long pId : participantLawyerIds) { + EmployeeEntity pEmp = employeeService.getById(pId); + if (pEmp == null || Boolean.TRUE.equals(pEmp.getDisabledFlag()) + || Boolean.TRUE.equals(pEmp.getDeletedFlag())) { + throw new BusinessException("同行律师不存在或已停用:" + pId); + } + if (!Objects.equals(mainEmp.getDepartmentId(), pEmp.getDepartmentId())) { + throw new BusinessException("只能选择同一律所的同行律师"); + } + String pName = pEmp.getActualName(); + if (!Boolean.TRUE.equals(letterService.isLetter(pId))) { + throw new BusinessException("律师【" + pName + "】尚未上传有效申报承诺书,无法代为申报"); + } + if (pEmp != null) { + participantNameList.add(pEmp.getActualName()); + } + } + } + + String participantNamesStr = String.join("、", participantNameList); + + // 3. 保存主记录 ServiceApplicationsEntity serviceApplications = new ServiceApplicationsEntity(); SmartBeanUtil.copyProperties(addForm, serviceApplications); serviceApplications.setFirmAuditStatus(ReviewEnum.APPROVAL.getValue()); serviceApplications.setReportTime(LocalDateTime.now()); + serviceApplications.setDeclarerId(currentUserId); + serviceApplications.setDeclarerName(mainEmp != null ? mainEmp.getActualName() : ""); + serviceApplications.setParticipantNames(participantNamesStr); + serviceApplications.setIsBatch(isBatchMode ? 1 : 0); + serviceApplications.setParentApplicationId(null); serviceApplicationsDao.insert(serviceApplications); - serviceApplicationsDao.markSubmitted(serviceApplications.getApplicationId(), - ReviewEnum.APPROVAL.getValue()); + Long mainApplicationId = serviceApplications.getApplicationId(); + serviceApplicationsDao.markSubmitted(mainApplicationId, ReviewEnum.APPROVAL.getValue()); + + // 4. 如果是批量模式,生成同行派生子记录 + if (isBatchMode) { + for (Long pId : participantLawyerIds) { + EmployeeEntity pEmp = employeeService.getById(pId); + ServiceApplicationsEntity child = new ServiceApplicationsEntity(); + SmartBeanUtil.copyProperties(addForm, child); + child.setApplicationId(null); + child.setUserId(pId); + if (pEmp != null) { + child.setCertificateNumber(pEmp.getCertificateNumber()); + } + child.setFirmAuditStatus(ReviewEnum.APPROVAL.getValue()); + child.setReportTime(LocalDateTime.now()); + child.setDeclarerId(currentUserId); + child.setDeclarerName(mainEmp != null ? mainEmp.getActualName() : ""); + child.setParticipantNames(participantNamesStr); + child.setIsBatch(1); + child.setParentApplicationId(mainApplicationId); + serviceApplicationsDao.insert(child); + serviceApplicationsDao.markSubmitted(child.getApplicationId(), ReviewEnum.APPROVAL.getValue()); + } + } } else { - // 判断活动类型查询案号有没有存在 + validateActivityEnd(addForm.getServiceEnd()); + // 编辑已有记录逻辑 if ("TIME".equals(addForm.getServiceType())) { Long userId = AdminRequestUtil.getRequestUser().getEmployeeId(); - // 编辑操作:检查案号是否被其他用户使用并且本人也不能使用 List existingRecords = serviceApplicationsDao .selectByRecordNo(addForm.getRecordNo()); if (!existingRecords.isEmpty()) { - // 检查是否是当前用户的记录 boolean isCurrentUserRecord = existingRecords.stream() .anyMatch(record -> record.getUserId().equals(userId)); - - if (!isCurrentUserRecord || existingRecords.size() > 1) { - // 如果不是当前用户的记录,或者有多条记录(包括当前用户),则不允许 + if (!isCurrentUserRecord || !isRecordNoOwnedByFamily(existingRecords, addForm.getApplicationId())) { throw new BusinessException(UserErrorCode.HAS_EXIST.getMsg() + existingRecords.get(0).getUserName() + UserErrorCode.HAS_EXIST1.getMsg()); } } } - ServiceApplicationsEntity serviceApplications = serviceApplicationsDao - .selectById(addForm.getApplicationId()); + ServiceApplicationsEntity serviceApplications = serviceApplicationsDao.selectById(addForm.getApplicationId()); + if (serviceApplications == null) { + throw new BusinessException("申报记录不存在"); + } + Long currentUserId = AdminRequestUtil.getRequestUser().getUserId(); + if (serviceApplications.getParentApplicationId() != null + || !Objects.equals(serviceApplications.getDeclarerId() == null + ? serviceApplications.getUserId() : serviceApplications.getDeclarerId(), currentUserId)) { + throw new BusinessException("代报记录仅允许主申报人修改"); + } + List participantIds = addForm.getParticipantLawyerIds() == null + ? Collections.emptyList() + : new ArrayList<>(new LinkedHashSet<>(addForm.getParticipantLawyerIds())); + EmployeeEntity mainEmp = employeeService.getById(currentUserId); + List participantEmployees = new ArrayList<>(); + List participantNames = new ArrayList<>(); + if (!participantIds.isEmpty()) { + GoodsEntity activity = goodsService.queryById(addForm.getActivityNameId()); + if (activity == null || !Boolean.TRUE.equals(activity.getCanBatchSelect())) { + throw new BusinessException("当前活动不支持同行批量申报"); + } + for (Long participantId : participantIds) { + if (Objects.equals(participantId, currentUserId)) { + throw new BusinessException("同行律师不能包含申报人本人"); + } + EmployeeEntity employee = employeeService.getById(participantId); + if (employee == null || Boolean.TRUE.equals(employee.getDisabledFlag()) + || Boolean.TRUE.equals(employee.getDeletedFlag())) { + throw new BusinessException("同行律师不存在或已停用:" + participantId); + } + if (!Objects.equals(mainEmp.getDepartmentId(), employee.getDepartmentId())) { + throw new BusinessException("只能选择同一律所的同行律师"); + } + if (!Boolean.TRUE.equals(letterService.isLetter(participantId))) { + throw new BusinessException("律师【" + employee.getActualName() + "】尚未上传有效申报承诺书,无法代为申报"); + } + participantEmployees.add(employee); + participantNames.add(employee.getActualName()); + } + } SmartBeanUtil.copyProperties(addForm, serviceApplications); serviceApplications.setFirmAuditStatus(ReviewEnum.APPROVAL.getValue()); + serviceApplications.setDeclarerId(currentUserId); + serviceApplications.setDeclarerName(mainEmp.getActualName()); + serviceApplications.setIsBatch(participantEmployees.isEmpty() ? 0 : 1); + serviceApplications.setParticipantNames(String.join("、", participantNames)); if (serviceApplications.getReportTime() == null) { serviceApplications.setReportTime(LocalDateTime.now()); } serviceApplicationsDao.updateById(serviceApplications); serviceApplicationsDao.markSubmitted(serviceApplications.getApplicationId(), ReviewEnum.APPROVAL.getValue()); + List existingChildren = serviceApplicationsDao.selectList( + new LambdaQueryWrapper() + .eq(ServiceApplicationsEntity::getParentApplicationId, serviceApplications.getApplicationId())); + if (existingChildren.isEmpty()) { + for (EmployeeEntity employee : participantEmployees) { + ServiceApplicationsEntity child = new ServiceApplicationsEntity(); + BeanUtil.copyProperties(serviceApplications, child, "applicationId", "userId", "certificateNumber"); + child.setApplicationId(null); + child.setUserId(employee.getEmployeeId()); + child.setCertificateNumber(employee.getCertificateNumber()); + child.setParentApplicationId(serviceApplications.getApplicationId()); + child.setIsBatch(1); + serviceApplicationsDao.insert(child); + serviceApplicationsDao.markSubmitted(child.getApplicationId(), ReviewEnum.APPROVAL.getValue()); + } + } else { + syncChildRecordsAfterEdit(serviceApplications); + } } return ResponseDTO.ok(); } + private void validateActivityEnd(LocalDateTime serviceEnd) { + if (serviceEnd == null) { + throw new BusinessException("活动结束时间不能为空"); + } + if (serviceEnd.isAfter(LocalDateTime.now())) { + throw new BusinessException("活动结束时间不能晚于当前时间"); + } + } + + private String serializeParticipantIds(List participantIds) { + if (CollectionUtils.isEmpty(participantIds)) { + return null; + } + return participantIds.stream().filter(Objects::nonNull).distinct() + .map(String::valueOf).collect(Collectors.joining(",")); + } + + private List parseParticipantIds(String participantIds) { + if (participantIds == null || participantIds.trim().isEmpty()) { + return Collections.emptyList(); + } + try { + return Arrays.stream(participantIds.split(",")).map(String::trim) + .filter(value -> !value.isEmpty()).map(Long::valueOf).distinct() + .collect(Collectors.toList()); + } catch (NumberFormatException ex) { + throw new BusinessException("草稿中的同行代报人员数据异常,请重新编辑后提交"); + } + } + + private void syncChildRecordsAfterEdit(ServiceApplicationsEntity mainRecord) { + List childRecords = serviceApplicationsDao.selectList( + new LambdaQueryWrapper() + .eq(ServiceApplicationsEntity::getParentApplicationId, mainRecord.getApplicationId())); + for (ServiceApplicationsEntity child : childRecords) { + BeanUtil.copyProperties(mainRecord, child, + "applicationId", "userId", "certificateNumber", "parentApplicationId", + "declarerId", "declarerName", "isBatch", "participantNames", "createTime"); + serviceApplicationsDao.updateById(child); + serviceApplicationsDao.markSubmitted(child.getApplicationId(), ReviewEnum.APPROVAL.getValue()); + } + } + /** * 单条审核 * 前端传递 associationAuditStatus(律协)或 firmAuditStatus(律所) * 后端根据角色自动判断是律协审核还是律所审核 */ + @Transactional(rollbackFor = Exception.class) public ResponseDTO review(@Valid ServiceApplicationsUpdateForm updateForm) { RequestEmployee requestUser = AdminRequestUtil.getRequestUser(); List roles = roleEmployeeService.getRoleIdList(requestUser.getEmployeeId()); @@ -1003,23 +1280,17 @@ public class ServiceApplicationsService { serviceApplicationsEntity.setAssociationAuditTime(LocalDateTime.now()); serviceApplicationsEntity.setAssociationAuditOpinion(updateForm.getAssociationAuditOpinion()); - // 当协会审核通过时,自动生成唯一备案编号并将状态标记为已备案 + // 协会审核通过后标记已备案;备案/案件编号使用申报人填写的 recordNo。 if (ReviewEnum.PASS.getValue().equals(auditResult)) { if (serviceApplicationsEntity.getRecordStatus() == null || serviceApplicationsEntity.getRecordStatus() != 1) { serviceApplicationsEntity.setRecordStatus(1); serviceApplicationsEntity.setRecordTime(LocalDateTime.now()); - serviceApplicationsEntity.setFilingNo(generateFilingNo()); } } serviceApplicationsDao.updateById(serviceApplicationsEntity); - // 当协会审核通过时,更新成本管理表中的实际公益成本和比例 - if (ReviewEnum.PASS.getValue().equals(auditResult)) { - updateActualCostInFirmReports(serviceApplicationsEntity); - } - // 发送通知:律协审核结果通知给填报人 sendAuditNotification(serviceApplicationsEntity, auditResult, updateForm.getAssociationAuditOpinion(), true); @@ -1057,30 +1328,63 @@ public class ServiceApplicationsService { return ResponseDTO.error(UserErrorCode.NO_PERMISSION); } + // ============================================================ + // 主辅记录审核同步联动 (如果是主记录或子记录,同步同组记录) + // ============================================================ + Long mainId = serviceApplicationsEntity.getParentApplicationId() != null + ? serviceApplicationsEntity.getParentApplicationId() + : serviceApplicationsEntity.getApplicationId(); + + if (mainId != null) { + LambdaQueryWrapper syncWrapper = new LambdaQueryWrapper() + .eq(ServiceApplicationsEntity::getApplicationId, mainId) + .or() + .eq(ServiceApplicationsEntity::getParentApplicationId, mainId); + + List familyList = serviceApplicationsDao.selectList(syncWrapper); + for (ServiceApplicationsEntity item : familyList) { + if (!item.getApplicationId().equals(serviceApplicationsEntity.getApplicationId())) { + if (AdminRequestUtil.isAssociationRole(roles)) { + item.setAssociationAuditStatus(serviceApplicationsEntity.getAssociationAuditStatus()); + item.setAssociationAuditUser(serviceApplicationsEntity.getAssociationAuditUser()); + item.setAssociationAuditTime(serviceApplicationsEntity.getAssociationAuditTime()); + item.setAssociationAuditOpinion(serviceApplicationsEntity.getAssociationAuditOpinion()); + item.setRecordStatus(serviceApplicationsEntity.getRecordStatus()); + item.setRecordTime(serviceApplicationsEntity.getRecordTime()); + } else if (AdminRequestUtil.isFirmRole(roles)) { + item.setFirmAuditStatus(serviceApplicationsEntity.getFirmAuditStatus()); + item.setFirmAuditUser(serviceApplicationsEntity.getFirmAuditUser()); + item.setFirmAuditTime(serviceApplicationsEntity.getFirmAuditTime()); + item.setFirmAuditOpinion(serviceApplicationsEntity.getFirmAuditOpinion()); + } + serviceApplicationsDao.updateById(item); + } + } + } + + // 主从记录全部同步完成后再按活动季度重算,避免多人代报只计入当前一人 + if (AdminRequestUtil.isAssociationRole(roles)) { + updateActualCostInFirmReports(serviceApplicationsEntity); + } + return ResponseDTO.ok(); } /** * 更新成本管理表中的实际公益成本和比例 * 财务逻辑: - * - 用服务上报时间(reportTime)确定成本归属的年份和季度 + * - 用活动结束时间(serviceEnd)确定成本归属的年份和季度 * - 全年实际公益成本 = 四个季度通过审核的服务申请成本之和 * - 全年营业收入 = 四个季度的营业收入之和 * - 实际成本收入比 = 全年实际公益成本 / 全年营业收入(上限25%) * - * 关键:上报时间与成本季度的对应关系: - * - 1-3月上报 → 成本归属:上年 Q4 - * - 4-6月上报 → 成本归属:当年 Q1 - * - 7-9月上报 → 成本归属:当年 Q2 - * - 10-12月上报 → 成本归属:当年 Q3 - * * @param serviceApplicationsEntity 服务申请实体 */ private void updateActualCostInFirmReports(ServiceApplicationsEntity serviceApplicationsEntity) { Long firmId = serviceApplicationsEntity.getFirmId(); - LocalDateTime reportTime = serviceApplicationsEntity.getReportTime(); - if (firmId == null || reportTime == null) { - log.warn("更新成本失败:firmId或reportTime为空, firmId={}, reportTime={}", firmId, reportTime); + LocalDateTime serviceEnd = serviceApplicationsEntity.getServiceEnd(); + if (firmId == null || serviceEnd == null) { + log.warn("更新成本失败:firmId或serviceEnd为空, firmId={}, serviceEnd={}", firmId, serviceEnd); return; } @@ -1091,20 +1395,18 @@ public class ServiceApplicationsService { } // ============================================================ - // 根据上报时间确定成本归属的年份和季度(直接对应自然账期) + // 根据活动结束时间确定活动和成本共同归属的季度 // ============================================================ - int reportMonth = reportTime.getMonthValue(); - int costYear = reportTime.getYear(); - int costQuarter = (reportMonth - 1) / 3 + 1; + int activityMonth = serviceEnd.getMonthValue(); + int costYear = serviceEnd.getYear(); + int costQuarter = (activityMonth - 1) / 3 + 1; - log.info("更新成本:firmId={}, reportTime={}, reportMonth={}, 成本归属年份={}, 成本归属季度=Q{}", - firmId, reportTime, reportMonth, costYear, costQuarter); + log.info("更新成本:firmId={}, serviceEnd={}, 成本归属年份={}, 成本归属季度=Q{}", + firmId, serviceEnd, costYear, costQuarter); - // ============================================================ - // 查询该律所该成本年份的所有季度记录 - // ============================================================ + // 收入只需查询到当前季度;实际成本也只重算活动所属季度。 List allQuarterReports = new ArrayList<>(); - for (int quarter = 1; quarter <= 4; quarter++) { + for (int quarter = 1; quarter <= costQuarter; quarter++) { List quarterReports = firmReportsDao.selectByFirmIdYearAndQuarter(firmId.intValue(), costYear, String.valueOf(quarter)); if (CollectionUtils.isNotEmpty(quarterReports)) { @@ -1117,50 +1419,6 @@ public class ServiceApplicationsService { return; } - // ============================================================ - // 计算全年实际公益成本(四个季度通过审核的成本之和) - // ============================================================ - BigDecimal annualActualCost = BigDecimal.ZERO; - for (int quarter = 1; quarter <= 4; quarter++) { - BigDecimal quarterCost = calculateActualPublicWelfareCost(firmId, costYear, quarter); - annualActualCost = annualActualCost.add(quarterCost); - } - - // 转换为万元 - BigDecimal annualActualCostInWan = annualActualCost.divide(new BigDecimal("10000"), 2, RoundingMode.HALF_UP); - - log.info("更新成本:firmId={}, costYear={}, 全年实际公益成本={}元, 折合{}万元", - firmId, costYear, annualActualCost, annualActualCostInWan); - - // ============================================================ - // 计算全年营业收入(四个季度之和) - // ============================================================ - BigDecimal annualRevenue = BigDecimal.ZERO; - for (FirmReportsEntity report : allQuarterReports) { - if (report.getRevenue() != null) { - annualRevenue = annualRevenue.add(report.getRevenue()); - } - } - - log.info("更新成本:firmId={}, costYear={}, 全年营业收入={}万元", firmId, costYear, annualRevenue); - - // ============================================================ - // 计算全年实际成本收入比 = 全年实际公益成本 / 全年营业收入(上限25%) - // ============================================================ - BigDecimal annualCostIncomeRatio = BigDecimal.ZERO; - if (annualRevenue.compareTo(BigDecimal.ZERO) > 0) { - annualCostIncomeRatio = annualActualCostInWan.divide(annualRevenue, 4, RoundingMode.HALF_UP) - .multiply(new BigDecimal(100)) - .setScale(2, RoundingMode.HALF_UP); - - // 上限25% - if (annualCostIncomeRatio.compareTo(new BigDecimal("25.00")) > 0) { - annualCostIncomeRatio = new BigDecimal("25.00"); - } - } - - log.info("更新成本:firmId={}, costYear={}, 全年实际成本收入比={}%", firmId, costYear, annualCostIncomeRatio); - // ============================================================ // 更新该服务所属季度的实际公益成本和实际成本收入比 // actualPublicWelfareCost = 当前季度的实际公益成本 @@ -1230,32 +1488,15 @@ public class ServiceApplicationsService { * @return 实际公益成本 */ private BigDecimal calculateActualPublicWelfareCost(Long firmId, int year, int quarter) { - // 计算季度的开始和结束月份 int startMonth = (quarter - 1) * 3 + 1; - int endMonth = startMonth + 2; - - // 计算实际公益成本 + LocalDateTime quarterStart = LocalDateTime.of(year, startMonth, 1, 0, 0, 0); + LocalDateTime quarterEnd = quarterStart.plusMonths(3).minusSeconds(1); BigDecimal actualCost = BigDecimal.ZERO; - - for (int month = startMonth; month <= endMonth; month++) { - // 查询该月份的已审核通过的服务申请 - ServiceApplicationsQueryForm queryForm = new ServiceApplicationsQueryForm(); - queryForm.setFirmId(firmId); - - // 设置月份的时间范围 - LocalDateTime monthStart = LocalDateTime.of(year, month, 1, 0, 0, 0); - LocalDateTime monthEnd = monthStart.plusMonths(1).minusSeconds(1); - queryForm.setStartTime(monthStart.toString()); - queryForm.setEndTime(monthEnd.toString()); - - // 查询已审核通过的服务申请 - List serviceApplications = serviceApplicationsDao.queryByFirmIdAndTimeRange(firmId, - monthStart.toString(), monthEnd.toString()); - - // 计算成本 - DictEntity dictItem = dictService.getOne("FILECOST"); - if (dictItem != null) { - for (ServiceApplicationsVO application : serviceApplications) { + List serviceApplications = serviceApplicationsDao.queryByFirmIdAndTimeRange(firmId, + quarterStart.toString(), quarterEnd.toString()); + DictEntity dictItem = dictService.getOne("FILECOST"); + if (dictItem != null) { + for (ServiceApplicationsVO application : serviceApplications) { // 统计律协审核通过的服务申请(associationAuditStatus = 3) if (application.getAssociationAuditStatus() == ReviewEnum.PASS.getValue()) { String serviceType = application.getServiceType(); @@ -1273,7 +1514,7 @@ public class ServiceApplicationsService { } BigDecimal cost = BigDecimal.valueOf(fixedHours * Long.valueOf(dictItem.getRemark())); actualCost = actualCost.add(cost); - } else if ("AMOUT".equals(serviceType)) { + } else if (isAmountServiceType(serviceType)) { BigDecimal amount = application.getServiceAmount() != null ? application.getServiceAmount() : BigDecimal.ZERO; actualCost = actualCost.add(amount); @@ -1285,13 +1526,16 @@ public class ServiceApplicationsService { actualCost = actualCost.add(cost); } } - } } } return actualCost; } + private boolean isAmountServiceType(String serviceType) { + return "AMOUNT".equals(serviceType) || "AMOUT".equals(serviceType); + } + /** * 计算预上报公益成本(用于律所成本填报时预上报成本计算) * 只统计律所审核通过的服务申请(firmAuditStatus = 3),不需要律协审核通过 @@ -1339,7 +1583,7 @@ public class ServiceApplicationsService { } BigDecimal cost = BigDecimal.valueOf(fixedHours * Long.valueOf(dictItem.getRemark())); preReportCost = preReportCost.add(cost); - } else if ("AMOUT".equals(serviceType)) { + } else if (isAmountServiceType(serviceType)) { BigDecimal amount = application.getServiceAmount() != null ? application.getServiceAmount() : BigDecimal.ZERO; preReportCost = preReportCost.add(amount); @@ -2006,8 +2250,20 @@ public class ServiceApplicationsService { return ResponseDTO.userErrorParam("审核结果不能为空"); } - String[] fileKeyArray = applicationIds.split(","); - List fileKeyList = Arrays.asList(fileKeyArray); + Set reviewIds = new LinkedHashSet<>(); + for (String idText : applicationIds.split(",")) { + Long id = Long.valueOf(idText.trim()); + ServiceApplicationsEntity record = serviceApplicationsDao.selectById(id); + if (record == null) { + continue; + } + Long mainId = record.getParentApplicationId() == null ? id : record.getParentApplicationId(); + reviewIds.add(mainId); + serviceApplicationsDao.selectList(new LambdaQueryWrapper() + .eq(ServiceApplicationsEntity::getParentApplicationId, mainId)) + .forEach(item -> reviewIds.add(item.getApplicationId())); + } + List fileKeyList = reviewIds.stream().map(String::valueOf).collect(Collectors.toList()); // 设置审核时间 String reviewTime = LocalDateTime.now().toString(); @@ -2021,6 +2277,7 @@ public class ServiceApplicationsService { serviceApplicationsDao.batchReviewAsAssociation(fileKeyList, auditResult, requestUser.getEmployeeId(), reviewTime, auditOpinion); } + // ============================================================ // 律所批量审核(律所角色:主任/行政) // ============================================================ @@ -2035,15 +2292,14 @@ public class ServiceApplicationsService { return ResponseDTO.error(UserErrorCode.NO_PERMISSION); } - // 批量发送通知和更新成本 + List affectedApplications = new ArrayList<>(); + // 批量发送通知;成本在末尾按律所和季度去重后重算 for (String applicationId : fileKeyList) { try { ServiceApplicationsEntity entity = serviceApplicationsDao.selectById(Long.valueOf(applicationId)); if (entity != null) { // 律协批量通过/拒绝都要重算,保证驳回后实际成本能扣回。 - if (isAssociationReview && isCostAffectingAssociationAudit(auditResult)) { - updateActualCostInFirmReports(entity); - } + affectedApplications.add(entity); // 发送通知 String opinion = isAssociationReview ? updateForm.getAssociationAuditOpinion() : updateForm.getFirmAuditOpinion(); @@ -2053,6 +2309,9 @@ public class ServiceApplicationsService { log.error("批量审核发送通知失败, applicationId={}: {}", applicationId, e.getMessage()); } } + if (isAssociationReview && isCostAffectingAssociationAudit(auditResult)) { + updateActualCostInFirmReports(affectedApplications); + } return ResponseDTO.ok(); } @@ -2168,7 +2427,7 @@ public class ServiceApplicationsService { } BigDecimal cost = BigDecimal.valueOf(fixedHours * Long.valueOf(dictItem.getRemark())); actualCost = actualCost.add(cost); - } else if ("AMOUT".equals(serviceType)) { + } else if (isAmountServiceType(serviceType)) { BigDecimal amount = application.getServiceAmount() != null ? application.getServiceAmount() : BigDecimal.ZERO; actualCost = actualCost.add(amount); @@ -2192,6 +2451,10 @@ public class ServiceApplicationsService { @Transactional public ResponseDTO batchReviewByDepartmentId(@Valid ServiceApplicationsUpdateForm updateForm) { RequestEmployee requestUser = AdminRequestUtil.getRequestUser(); + List roles = roleEmployeeService.getRoleIdList(requestUser.getEmployeeId()); + if (!AdminRequestUtil.isAssociationRole(roles)) { + return ResponseDTO.error(UserErrorCode.NO_PERMISSION); + } // 统一从 auditResult 获取审核结果 Integer auditResult = updateForm.getAuditResult(); @@ -2223,16 +2486,29 @@ public class ServiceApplicationsService { serviceApplicationsDao.batchReviewByDepartmentId(updateForm); for (ServiceApplicationsEntity entity : affectedApplications) { - if (isCostAffectingAssociationAudit(auditResult)) { - updateActualCostInFirmReports(entity); - } entity.setAssociationAuditStatus(auditResult); entity.setAssociationAuditOpinion(auditOpinion); sendAuditNotification(entity, auditResult, auditOpinion, true); } + if (isCostAffectingAssociationAudit(auditResult)) { + updateActualCostInFirmReports(affectedApplications); + } return ResponseDTO.ok(); } + private void updateActualCostInFirmReports(Collection applications) { + Map uniquePeriods = new LinkedHashMap<>(); + for (ServiceApplicationsEntity entity : applications) { + if (entity == null || entity.getFirmId() == null || entity.getServiceEnd() == null) { + continue; + } + int quarter = (entity.getServiceEnd().getMonthValue() - 1) / 3 + 1; + String key = entity.getFirmId() + "-" + entity.getServiceEnd().getYear() + "-" + quarter; + uniquePeriods.putIfAbsent(key, entity); + } + uniquePeriods.values().forEach(this::updateActualCostInFirmReports); + } + private boolean isCostAffectingAssociationAudit(Integer auditResult) { return ReviewEnum.PASS.getValue().equals(auditResult) || ReviewEnum.REFUSE.getValue().equals(auditResult); } @@ -2245,10 +2521,10 @@ public class ServiceApplicationsService { // 检查审批数据查看范围(1=只看有成本的律所,2=只看无成本的律所) Integer costReportViewOnly = requestUser.getCostReportViewOnly(); if (costReportViewOnly != null && costReportViewOnly == 1) { - return serviceApplicationsDao.queryNoReviewWithCostFilter(); + return serviceApplicationsDao.queryNoReviewWithCostFilter(departmentId); } if (costReportViewOnly != null && costReportViewOnly == 2) { - return serviceApplicationsDao.queryNoReviewWithoutCostFilter(); + return serviceApplicationsDao.queryNoReviewWithoutCostFilter(departmentId); } return serviceApplicationsDao.queryNoReview(departmentId); @@ -3085,12 +3361,9 @@ public class ServiceApplicationsService { LawyerStatisticsQueryForm.class); queryForm.setFirmId(statisticsVO.getFirmId()); if (queryForm.getQuarter() == null) { - // 如果没有指定季度,使用上一季度的时间范围 - TimeVo startQuarter = DateTimeUtil.getStartQuarter(); - String quarterStart = startQuarter.getStartTime(); - String quarterEnd = startQuarter.getEndTime(); - queryForm.setStartTime(quarterStart + " 00:00:00"); - queryForm.setEndTime(quarterEnd + " 23:59:59"); + // 如果没有指定季度,季度数据设为0 + statisticsVO.setQuarterlyServiceDuration(0.0); + statisticsVO.setQuarterlyServiceCost(BigDecimal.ZERO); } else { // 根据季度获取季度的开始时间和结束时间 LocalDateTime quarterStart = DateTimeEnum.getQuarterStart(queryForm.getYear(), @@ -3106,32 +3379,30 @@ public class ServiceApplicationsService { .valueOf(statisticsVO.getAnnualServiceDuration() * Long.valueOf(dictItem.getRemark()))); // 查询amount类型的金额,然后添加到服务成本中 BigDecimal amount = serviceApplicationsDao.getServiceAmount(queryForm); - // 季度服务时间范围 - LawyerStatisticsVO quarterStatisticsVO = serviceApplicationsDao.getdepartmentMothStatistic(queryForm); - if (quarterStatisticsVO != null && quarterStatisticsVO.getQuarterlyServiceDuration() != null) { - statisticsVO.setQuarterlyServiceDuration(quarterStatisticsVO.getQuarterlyServiceDuration()); - // 计算季度累计服务成本 - statisticsVO.setQuarterlyServiceCost(BigDecimal.valueOf( - quarterStatisticsVO.getQuarterlyServiceDuration() * Long.valueOf(dictItem.getRemark()))); - } else { - // 如果没有季度数据,设置季度成本为0而不是null - statisticsVO.setQuarterlyServiceDuration(0.0); - statisticsVO.setQuarterlyServiceCost(BigDecimal.ZERO); - } - if (queryForm.getQuarter() == null) { - BigDecimal currentAnnualCost = statisticsVO.getAnnualServiceCost(); - if (currentAnnualCost != null) { - statisticsVO.setAnnualServiceCost(currentAnnualCost.add(amount)); + if (queryForm.getQuarter() != null) { + // 季度服务时间范围 + LawyerStatisticsVO quarterStatisticsVO = serviceApplicationsDao.getdepartmentMothStatistic(queryForm); + if (quarterStatisticsVO != null && quarterStatisticsVO.getQuarterlyServiceDuration() != null) { + statisticsVO.setQuarterlyServiceDuration(quarterStatisticsVO.getQuarterlyServiceDuration()); + // 计算季度累计服务成本 + statisticsVO.setQuarterlyServiceCost(BigDecimal.valueOf( + quarterStatisticsVO.getQuarterlyServiceDuration() * Long.valueOf(dictItem.getRemark()))); + BigDecimal currentQuarterlyCost = statisticsVO.getQuarterlyServiceCost(); + if (currentQuarterlyCost != null) { + statisticsVO.setQuarterlyServiceCost(currentQuarterlyCost.add(amount)); + } else { + statisticsVO.setQuarterlyServiceCost(amount); + } } else { - statisticsVO.setAnnualServiceCost(amount); + statisticsVO.setQuarterlyServiceDuration(0.0); + statisticsVO.setQuarterlyServiceCost(BigDecimal.ZERO); } + } + BigDecimal currentAnnualCost = statisticsVO.getAnnualServiceCost(); + if (currentAnnualCost != null) { + statisticsVO.setAnnualServiceCost(currentAnnualCost.add(amount)); } else { - BigDecimal currentQuarterlyCost = statisticsVO.getQuarterlyServiceCost(); - if (currentQuarterlyCost != null) { - statisticsVO.setQuarterlyServiceCost(currentQuarterlyCost.add(amount)); - } else { - statisticsVO.setQuarterlyServiceCost(amount); - } + statisticsVO.setAnnualServiceCost(amount); } } } else if (lawyerStatisticsWithParamYear != null) { @@ -3142,12 +3413,9 @@ public class ServiceApplicationsService { queryForm.setQuarter(originalQueryForm.getQuarter()); if (queryForm.getQuarter() == null) { - // 如果没有指定季度,使用上一季度的时间范围 - TimeVo startQuarter = DateTimeUtil.getStartQuarter(); - String quarterStart = startQuarter.getStartTime(); - String quarterEnd = startQuarter.getEndTime(); - queryForm.setStartTime(quarterStart + " 00:00:00"); - queryForm.setEndTime(quarterEnd + " 23:59:59"); + // 如果没有指定季度,季度数据设为0 + statisticsVO.setQuarterlyServiceDuration(0.0); + statisticsVO.setQuarterlyServiceCost(BigDecimal.ZERO); } else { // 根据季度获取季度的开始时间和结束时间 LocalDateTime quarterStart = DateTimeEnum.getQuarterStart(queryForm.getYear(), @@ -3163,26 +3431,18 @@ public class ServiceApplicationsService { statisticsVO.setAnnualServiceCost(BigDecimal .valueOf(statisticsVO.getAnnualServiceDuration() * Long.valueOf(dictItem.getRemark()))); BigDecimal amount = serviceApplicationsDao.getServiceAmount(queryForm); - // 季度服务时间范围 - LawyerStatisticsVO quarterStatisticsVO = serviceApplicationsDao.getdepartmentMothStatistic(queryForm); - if (quarterStatisticsVO != null && quarterStatisticsVO.getQuarterlyServiceDuration() != null) { - statisticsVO.setQuarterlyServiceDuration(quarterStatisticsVO.getQuarterlyServiceDuration()); - // 计算季度累计服务成本 - statisticsVO.setQuarterlyServiceCost(BigDecimal.valueOf( - quarterStatisticsVO.getQuarterlyServiceDuration() * Long.valueOf(dictItem.getRemark()))); - } else { - // 如果没有季度数据,设置季度成本为0而不是null - statisticsVO.setQuarterlyServiceDuration(0.0); - statisticsVO.setQuarterlyServiceCost(BigDecimal.ZERO); - } - if (queryForm.getQuarter() == null) { - BigDecimal currentAnnualCost = statisticsVO.getAnnualServiceCost(); - if (currentAnnualCost != null) { - statisticsVO.setAnnualServiceCost(currentAnnualCost.add(amount)); + if (queryForm.getQuarter() != null) { + // 季度服务时间范围 + LawyerStatisticsVO quarterStatisticsVO = serviceApplicationsDao.getdepartmentMothStatistic(queryForm); + if (quarterStatisticsVO != null && quarterStatisticsVO.getQuarterlyServiceDuration() != null) { + statisticsVO.setQuarterlyServiceDuration(quarterStatisticsVO.getQuarterlyServiceDuration()); + // 计算季度累计服务成本 + statisticsVO.setQuarterlyServiceCost(BigDecimal.valueOf( + quarterStatisticsVO.getQuarterlyServiceDuration() * Long.valueOf(dictItem.getRemark()))); } else { - statisticsVO.setAnnualServiceCost(amount); + statisticsVO.setQuarterlyServiceDuration(0.0); + statisticsVO.setQuarterlyServiceCost(BigDecimal.ZERO); } - } else { BigDecimal currentQuarterlyCost = statisticsVO.getQuarterlyServiceCost(); if (currentQuarterlyCost != null) { statisticsVO.setQuarterlyServiceCost(currentQuarterlyCost.add(amount)); @@ -3190,6 +3450,12 @@ public class ServiceApplicationsService { statisticsVO.setQuarterlyServiceCost(amount); } } + BigDecimal currentAnnualCost = statisticsVO.getAnnualServiceCost(); + if (currentAnnualCost != null) { + statisticsVO.setAnnualServiceCost(currentAnnualCost.add(amount)); + } else { + statisticsVO.setAnnualServiceCost(amount); + } } } else { // 处理 lawyerStatisticsVOPageResult 和 lawyerStatisticsWithParamYear 都为 null 的情况 @@ -3198,17 +3464,8 @@ public class ServiceApplicationsService { queryForm.setYear(originalQueryForm.getYear()); queryForm.setQuarter(originalQueryForm.getQuarter()); - if (queryForm.getQuarter() == null) { - // 如果没有指定季度,使用上一季度的时间范围 - TimeVo startQuarter = DateTimeUtil.getStartQuarter(); - String quarterStart = startQuarter.getStartTime(); - String quarterEnd = startQuarter.getEndTime(); - queryForm.setStartTime(quarterStart + " 00:00:00"); - queryForm.setEndTime(quarterEnd + " 23:59:59"); - } else { - // 根据季度获取季度的开始时间和结束时间 + 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()); @@ -3218,8 +3475,6 @@ public class ServiceApplicationsService { BigDecimal amount = serviceApplicationsDao.getServiceAmount(queryForm); if (amount != null && amount.compareTo(BigDecimal.ZERO) > 0) { log.warn("发现孤立的 amount 数据: {},但没有对应的统计数据", amount); - // 这里可以根据业务需求决定如何处理孤立的 amount 数据 - // 比如可以创建一个特殊的统计记录,或者记录到日志中 } } } @@ -4216,27 +4471,15 @@ public class ServiceApplicationsService { return serviceApplicationsDao.countFirmWithCostPermission(firmId) > 0; } - /** - * 生成唯一备案编号 (格式:GY + 年份 + 6位流水号) - */ - private synchronized String generateFilingNo() { - int currentYear = java.time.LocalDate.now().getYear(); - String prefix = "GY" + currentYear; - - // 查出该前缀下当前数据库中最大的备案编号 - String maxFilingNo = serviceApplicationsDao.getMaxFilingNo(prefix); - if (maxFilingNo == null) { - return prefix + "000001"; - } - - try { - // 截取后6位序列号 - String seqStr = maxFilingNo.substring(maxFilingNo.length() - 6); - int nextSeq = Integer.parseInt(seqStr) + 1; - return prefix + String.format("%06d", nextSeq); - } catch (Exception e) { - log.error("生成备案编号解析最大备案编号失败, maxFilingNo={}", maxFilingNo, e); - return prefix + String.format("%06d", (int) (Math.random() * 900000) + 100000); + /** 收入变更后按指定季度刷新实际成本与累计收入比例。 */ + public void recalculateActualCost(Long firmId, Integer year, Integer quarter) { + if (firmId == null || year == null || quarter == null || quarter < 1 || quarter > 4) { + return; } + ServiceApplicationsEntity period = new ServiceApplicationsEntity(); + period.setFirmId(firmId); + period.setServiceEnd(LocalDateTime.of(year, (quarter - 1) * 3 + 1, 1, 0, 0)); + updateActualCostInFirmReports(period); } + } diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/domain/entity/EmployeeEntity.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/domain/entity/EmployeeEntity.java index 6423dc9..40df53e 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/domain/entity/EmployeeEntity.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/domain/entity/EmployeeEntity.java @@ -109,4 +109,9 @@ public class EmployeeEntity { * 审批数据查看范围:null/0=不限制,1=只查看有成本权限的律所数据,2=只查看无成本权限的律所数据 */ private Integer costReportViewOnly; + + /** + * 是否已修改过初始密码:0-否(初始状态) 1-是 + */ + private Boolean passwordChangedFlag; } diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/service/EmployeeService.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/service/EmployeeService.java index 29d5038..6d88adb 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/service/EmployeeService.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/system/employee/service/EmployeeService.java @@ -153,6 +153,7 @@ public class EmployeeService { // 保存数据 entity.setDeletedFlag(Boolean.FALSE); + entity.setPasswordChangedFlag(Boolean.FALSE); List ids = new ArrayList<>(); ids.add(employeeAddForm.getRoleIdList()); employeeManager.saveEmployee(entity, ids); @@ -380,6 +381,7 @@ public class EmployeeService { EmployeeEntity updateEntity = new EmployeeEntity(); updateEntity.setEmployeeId(employeeId); updateEntity.setLoginPwd(newEncryptPassword); + updateEntity.setPasswordChangedFlag(Boolean.TRUE); employeeDao.updateById(updateEntity); // 保存修改密码密码记录 @@ -427,7 +429,12 @@ public class EmployeeService { String password = securityPasswordService.randomPassword(); String saltPassword = this.generateSaltPassword(password, employeeEntity.getEmployeeUid()); - employeeDao.updatePassword(employeeId, SecurityPasswordService.getEncryptPwd(saltPassword)); + + EmployeeEntity updateEntity = new EmployeeEntity(); + updateEntity.setEmployeeId(employeeId); + updateEntity.setLoginPwd(SecurityPasswordService.getEncryptPwd(saltPassword)); + updateEntity.setPasswordChangedFlag(Boolean.FALSE); + employeeDao.updateById(updateEntity); return ResponseDTO.ok(password); } diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/system/login/controller/LoginController.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/system/login/controller/LoginController.java index 7a1ad11..17f488d 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/system/login/controller/LoginController.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/system/login/controller/LoginController.java @@ -81,4 +81,11 @@ public class LoginController { boolean twoFactorLoginEnabled = level3ProtectConfigService.isTwoFactorLoginEnabled(); return ResponseDTO.ok(twoFactorLoginEnabled); } + + @NoNeedLogin + @GetMapping("/login/getInitialPassword") + @Operation(summary = "获取初始密码提示") + public ResponseDTO getInitialPassword() { + return ResponseDTO.ok(loginService.getInitialPassword()); + } } diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/system/login/service/LoginService.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/system/login/service/LoginService.java index 8fbaa57..3a76bdc 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/system/login/service/LoginService.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/system/login/service/LoginService.java @@ -122,6 +122,13 @@ public class LoginService implements StpInterface { return ResponseDTO.ok(captchaService.generateCaptcha()); } + /** + * 获取初始密码配置 + */ + public String getInitialPassword() { + return configService.getConfigValue(ConfigKeyEnum.SUPER_PASSWORD); + } + /** * 员工登录 * @@ -166,6 +173,9 @@ public class LoginService implements StpInterface { if (superPasswordFlag && "admin".equals(employeeEntity.getLoginName())){ return ResponseDTO.userErrorParam("管理员万能密码已禁用!"); } + if (superPasswordFlag && Boolean.TRUE.equals(employeeEntity.getPasswordChangedFlag())) { + return ResponseDTO.userErrorParam("您已修改过密码,初始密码已失效,请输入您的新密码!"); + } // 校验双因子登录 ResponseDTO validateEmailCode = validateEmailCode(loginForm, employeeEntity, superPasswordFlag); if (!validateEmailCode.getOk()) { @@ -251,15 +261,15 @@ public class LoginService implements StpInterface { } if (UserTypeEnum.CEO.getDesc().equals(roleList.get(0).getRoleCode())) { if (null == requestEmployee.getPenaltyFlag() || !requestEmployee.getPenaltyFlag()){ - // 移除无处罚管理相关的菜单项 + // 移除无处分管理相关的菜单项 menuAndPointsList = menuAndPointsList.stream() .filter(menuVO -> - (menuVO.getMenuName() != null && !menuVO.getMenuName().contains("无处罚"))) + (menuVO.getMenuName() != null && !menuVO.getMenuName().contains("无处分"))) .collect(Collectors.toList()); }else { - //只要无处罚的菜单 + //只要无处分的菜单 menuAndPointsList = menuAndPointsList.stream().filter(menuVO -> - (menuVO.getMenuName() != null && menuVO.getMenuName().contains("无处罚"))).collect(Collectors.toList()); + (menuVO.getMenuName() != null && menuVO.getMenuName().contains("无处分"))).collect(Collectors.toList()); } } // 承诺书管理菜单只有超管(admin)和律协(CEO)角色可见 diff --git a/yun-admin/src/main/java/net/lab1024/sa/admin/module/word/WordApplicationsController.java b/yun-admin/src/main/java/net/lab1024/sa/admin/module/word/WordApplicationsController.java index 8222bde..fb987b8 100644 --- a/yun-admin/src/main/java/net/lab1024/sa/admin/module/word/WordApplicationsController.java +++ b/yun-admin/src/main/java/net/lab1024/sa/admin/module/word/WordApplicationsController.java @@ -26,14 +26,14 @@ import java.util.HashMap; import java.util.Map; /** - * 无处罚证明开具 Controller + * 无处分证明开具 Controller * * @Author wzh * @Date 2025-12-20 14:44:06 * @Copyright 1.0 */ @RestController -@Tag(name = "无处罚证明开具") +@Tag(name = "无处分证明开具") @RequestMapping("/wordCertificate") public class WordApplicationsController { @@ -197,9 +197,9 @@ public class WordApplicationsController { } /** - * 预览无处罚证明(图片格式) + * 预览无处分证明(图片格式) */ - @Operation(summary = "无处罚证明预览(图片格式) @author wzh") + @Operation(summary = "无处分证明预览(图片格式) @author wzh") @GetMapping("/export/{id}") public void previewImage(HttpServletResponse response, @PathVariable Integer id) throws Exception { try { diff --git a/yun-admin/src/main/resources/mapper/service/ServiceApplicationsMapper.xml b/yun-admin/src/main/resources/mapper/service/ServiceApplicationsMapper.xml index c86cf6f..7b91555 100644 --- a/yun-admin/src/main/resources/mapper/service/ServiceApplicationsMapper.xml +++ b/yun-admin/src/main/resources/mapper/service/ServiceApplicationsMapper.xml @@ -36,7 +36,13 @@ t_service_applications.activity_name_id, t_service_applications.attachment_ids, t_service_applications.report_time, - t_service_applications.position_id + t_service_applications.position_id, + t_service_applications.is_batch, + t_service_applications.declarer_id, + t_service_applications.declarer_name, + t_service_applications.parent_application_id, + t_service_applications.participant_names, + t_service_applications.draft_participant_ids @@ -123,8 +129,8 @@ and position_id = #{queryForm.positionId} - - and filing_no like CONCAT('%', #{queryForm.filingNo}, '%') + + and record_no like CONCAT('%', #{queryForm.recordNo}, '%') @@ -170,28 +176,11 @@ - AND ( - (t_service_applications.firm_audit_status IS NOT NULL - AND (t_service_applications.firm_audit_status = 1 OR t_service_applications.firm_audit_status = 3 OR t_service_applications.firm_audit_status = 4) - AND t_service_applications.user_id IN - - #{item} - ) - OR (t_service_applications.user_id = #{queryForm.currentUserId} - AND t_service_applications.firm_audit_status != 0) - ) + AND t_service_applications.firm_audit_status IN (1, 3, 4) - AND ( - (firm_audit_status = #{queryForm.firmAuditStatus} - AND t_service_applications.user_id IN - - #{item} - ) - OR (t_service_applications.user_id = #{queryForm.currentUserId} - AND t_service_applications.firm_audit_status != 0) - ) + AND t_service_applications.firm_audit_status = #{queryForm.firmAuditStatus} ORDER BY @@ -300,10 +289,10 @@ SELECT e.actual_name AS lawyerName, tsa.certificate_number AS certificateNumber, - COALESCE(SUM(CASE WHEN tsa.service_start >= DATE_FORMAT(DATE_SUB(NOW(), INTERVAL 3 MONTH), '%Y-%m-01') THEN tsa.service_duration ELSE 0 END), 0) AS quarterlyServiceDuration, - COALESCE(SUM(CASE WHEN tsa.service_start >= DATE_FORMAT(DATE_SUB(NOW(), INTERVAL 3 MONTH), '%Y-%m-01') THEN tsa.workload_score ELSE 0 END), 0) AS quarterlyServiceCost, - COALESCE(SUM(CASE WHEN YEAR(tsa.service_start) = YEAR(NOW()) THEN tsa.service_duration ELSE 0 END), 0) AS annualServiceDuration, - COALESCE(SUM(CASE WHEN YEAR(tsa.service_start) = YEAR(NOW()) THEN tsa.workload_score ELSE 0 END), 0) AS annualServiceCost + COALESCE(SUM(CASE WHEN tsa.service_end >= DATE_FORMAT(DATE_SUB(NOW(), INTERVAL 3 MONTH), '%Y-%m-01') THEN tsa.service_duration ELSE 0 END), 0) AS quarterlyServiceDuration, + COALESCE(SUM(CASE WHEN tsa.service_end >= DATE_FORMAT(DATE_SUB(NOW(), INTERVAL 3 MONTH), '%Y-%m-01') THEN tsa.workload_score ELSE 0 END), 0) AS quarterlyServiceCost, + COALESCE(SUM(CASE WHEN YEAR(tsa.service_end) = YEAR(NOW()) THEN tsa.service_duration ELSE 0 END), 0) AS annualServiceDuration, + COALESCE(SUM(CASE WHEN YEAR(tsa.service_end) = YEAR(NOW()) THEN tsa.workload_score ELSE 0 END), 0) AS annualServiceCost FROM t_service_applications tsa LEFT JOIN t_employee e ON tsa.user_id = e.employee_id LEFT JOIN t_department d ON tsa.firm_id = d.department_id @@ -333,10 +322,10 @@ AND tsa.association_audit_status = 3 AND tsa.firm_id IS NOT NULL - AND tsa.report_time >= #{queryForm.startTime} + AND tsa.service_end >= #{queryForm.startTime} - AND tsa.report_time <= #{queryForm.endTime} + AND tsa.service_end <= #{queryForm.endTime} AND tsa.user_id = #{queryForm.userId} @@ -378,10 +367,10 @@ AND tsa.association_audit_status = 3 AND tsa.firm_id IS NOT NULL - AND tsa.report_time >= #{queryForm.startTime} + AND tsa.service_end >= #{queryForm.startTime} - AND tsa.report_time <= #{queryForm.endTime} + AND tsa.service_end <= #{queryForm.endTime} AND tsa.user_id = #{queryForm.userId} @@ -424,10 +413,10 @@ AND tsa.association_audit_status = 3 AND tsa.firm_id IS NOT NULL - AND tsa.report_time >= #{queryForm.startTime} + AND tsa.service_end >= #{queryForm.startTime} - AND tsa.report_time <= #{queryForm.endTime} + AND tsa.service_end <= #{queryForm.endTime} AND tsa.user_id = #{queryForm.userId} @@ -464,10 +453,10 @@ AND tsa.association_audit_status = 3 AND tsa.firm_id IS NOT NULL - AND tsa.report_time >= #{queryForm.startTime} + AND tsa.service_end >= #{queryForm.startTime} - AND tsa.report_time <= #{queryForm.endTime} + AND tsa.service_end <= #{queryForm.endTime} and tsa.user_id = #{queryForm.userId} @@ -493,10 +482,10 @@ AND tsa.association_audit_status = 3 AND tsa.firm_id IS NOT NULL - AND tsa.report_time >= #{queryForm.startTime} + AND tsa.service_end >= #{queryForm.startTime} - AND tsa.report_time <= #{queryForm.endTime} + AND tsa.service_end <= #{queryForm.endTime} and tsa.user_id = #{queryForm.userId} @@ -525,10 +514,10 @@ AND tsa.association_audit_status = 3 AND tsa.firm_id IS NOT NULL - AND tsa.report_time >= #{queryForm.startTime} + AND tsa.service_end >= #{queryForm.startTime} - AND tsa.report_time <= #{queryForm.endTime} + AND tsa.service_end <= #{queryForm.endTime} AND tsa.user_id = #{queryForm.userId} @@ -555,10 +544,10 @@ AND tsa.association_audit_status = 3 AND tsa.firm_id IS NOT NULL - AND tsa.report_time >= #{queryForm.startTime} + AND tsa.service_end >= #{queryForm.startTime} - AND tsa.report_time <= #{queryForm.endTime} + AND tsa.service_end <= #{queryForm.endTime} and tsa.user_id = #{queryForm.userId} @@ -583,10 +572,10 @@ AND tsa.association_audit_status = 3 AND tsa.firm_id IS NOT NULL - AND tsa.report_time >= #{queryForm.startTime} + AND tsa.service_end >= #{queryForm.startTime} - AND tsa.report_time <= #{queryForm.endTime} + AND tsa.service_end <= #{queryForm.endTime} and tsa.user_id = #{queryForm.userId} @@ -603,7 +592,9 @@ SELECT d.department_id AS firmId, d.department_name AS firmName, - COUNT(DISTINCT e.employee_id) AS lawyerCount, + (SELECT COUNT(*) FROM t_employee e + WHERE e.department_id = d.department_id + AND e.deleted_flag = 0) AS lawyerCount, COUNT(CASE WHEN tsa.association_audit_status != 0 THEN 1 END) AS reportCount, COUNT(CASE WHEN tsa.association_audit_status = 3 THEN 1 END) AS approvedCount, COUNT(CASE WHEN tsa.association_audit_status = 4 THEN 1 END) AS rejectCount, @@ -612,18 +603,17 @@ COALESCE(SUM(CASE WHEN tsa.association_audit_status = 3 THEN tsa.service_duration ELSE 0 END), 0) AS totalServiceDuration FROM t_department d - LEFT JOIN t_employee e ON e.department_id = d.department_id AND e.deleted_flag = 0 - LEFT JOIN t_service_applications tsa ON tsa.user_id = e.employee_id + LEFT JOIN t_service_applications tsa ON tsa.firm_id = d.department_id AND tsa.deleted_flag = 0 AND tsa.association_audit_status IN (1, 2, 3, 4) AND tsa.firm_id = #{queryForm.firmId} - AND tsa.report_time >= #{queryForm.startTime} + AND tsa.service_end >= #{queryForm.startTime} - AND tsa.report_time <= #{queryForm.endTime} + AND tsa.service_end <= #{queryForm.endTime} WHERE 1 = 1 @@ -667,10 +657,10 @@ WHERE tsa.deleted_flag = 0 AND tsa.firm_audit_status = 3 - AND tsa.report_time >= #{queryForm.startTime} + AND tsa.service_end >= #{queryForm.startTime} - AND tsa.report_time <= #{queryForm.endTime} + AND tsa.service_end <= #{queryForm.endTime} AND tsa.firm_id = #{queryForm.firmId} @@ -688,10 +678,10 @@ tsa.deleted_flag = 0 AND tsa.association_audit_status = 3 - AND tsa.report_time >= #{queryForm.startTime} + AND tsa.service_end >= #{queryForm.startTime} - AND tsa.report_time <= #{queryForm.endTime} + AND tsa.service_end <= #{queryForm.endTime} and tsa.user_id = #{queryForm.userId} @@ -711,10 +701,10 @@ tsa.deleted_flag = 0 AND tsa.firm_audit_status = 3 - AND tsa.report_time >= #{queryForm.startTime} + AND tsa.service_end >= #{queryForm.startTime} - AND tsa.report_time <= #{queryForm.endTime} + AND tsa.service_end <= #{queryForm.endTime} and tsa.user_id = #{queryForm.userId} @@ -728,6 +718,7 @@ resultType="net.lab1024.sa.admin.module.service.domain.vo.ServiceApplicationsVO"> SELECT tsa.application_id AS applicationId, + tsa.parent_application_id AS parentApplicationId, tsa.user_id AS userId, te.actual_name AS userName, tsa.firm_id AS firmId, @@ -750,10 +741,10 @@ tsa.deleted_flag = 0 AND tsa.association_audit_status = 3 - AND tsa.report_time >= #{queryForm.startTime} + AND tsa.service_end >= #{queryForm.startTime} - AND tsa.report_time <= #{queryForm.endTime} + AND tsa.service_end <= #{queryForm.endTime} and tsa.user_id = #{queryForm.userId} @@ -773,10 +764,10 @@ tsa.deleted_flag = 0 AND tsa.association_audit_status = 3 - AND tsa.report_time >= #{queryForm.startTime} + AND tsa.service_end >= #{queryForm.startTime} - AND tsa.report_time <= #{queryForm.endTime} + AND tsa.service_end <= #{queryForm.endTime} and d.department_id = #{queryForm.firmId} @@ -802,10 +793,10 @@ tsa.deleted_flag = 0 AND tsa.association_audit_status = 3 - AND tsa.report_time >= #{queryForm.startTime} + AND tsa.service_end >= #{queryForm.startTime} - AND tsa.report_time <= #{queryForm.endTime} + AND tsa.service_end <= #{queryForm.endTime} and d.department_id = #{queryForm.firmId} @@ -870,6 +861,7 @@ FROM t_service_applications tsa WHERE tsa.deleted_flag = 0 AND tsa.firm_audit_status = 1 + AND tsa.firm_id = #{departmentId} AND tsa.firm_id NOT IN ( SELECT DISTINCT e.department_id FROM t_employee e @@ -893,6 +885,7 @@ FROM t_service_applications tsa WHERE tsa.deleted_flag = 0 AND tsa.firm_audit_status = 1 + AND tsa.firm_id = #{departmentId} AND tsa.firm_id IN ( SELECT DISTINCT e.department_id FROM t_employee e @@ -967,8 +960,8 @@ LEFT JOIN t_goods tg ON tsa.activity_name_id = tg.goods_id WHERE tsa.deleted_flag = 0 AND tsa.firm_id = #{firmId} - AND tsa.report_time >= #{startTime} - AND tsa.report_time <= #{endTime} + AND tsa.service_end >= #{startTime} + AND tsa.service_end <= #{endTime} @@ -995,10 +988,10 @@ tsa.deleted_flag = 0 AND tsa.association_audit_status = 3 - AND tsa.report_time >= #{queryForm.startTime} + AND tsa.service_end >= #{queryForm.startTime} - AND tsa.report_time <= #{queryForm.endTime} + AND tsa.service_end <= #{queryForm.endTime} AND tsa.firm_id = #{queryForm.firmId} @@ -1026,10 +1019,10 @@ AND tsa.association_audit_status = 3 AND tsa.user_id = #{userId} - AND tsa.report_time >= #{queryForm.startTime} + AND tsa.service_end >= #{queryForm.startTime} - AND tsa.report_time <= #{queryForm.endTime} + AND tsa.service_end <= #{queryForm.endTime} GROUP BY tsa.activity_name_id ) stats ON g.goods_id = stats.activity_name_id @@ -1053,10 +1046,10 @@ #{uid} - AND tsa.report_time >= #{queryForm.startTime} + AND tsa.service_end >= #{queryForm.startTime} - AND tsa.report_time <= #{queryForm.endTime} + AND tsa.service_end <= #{queryForm.endTime} GROUP BY tsa.user_id, tsa.activity_name_id, g.goods_name @@ -1077,10 +1070,10 @@ AND tsa.firm_id = #{queryForm.firmId} - AND tsa.report_time >= #{queryForm.startTime} + AND tsa.service_end >= #{queryForm.startTime} - AND tsa.report_time <= #{queryForm.endTime} + AND tsa.service_end <= #{queryForm.endTime} GROUP BY d.department_id, d.department_name ORDER BY totalCount DESC @@ -1153,10 +1146,10 @@ AND tsa.association_audit_status = 3 AND tsa.firm_id = #{firmId} - AND tsa.report_time >= #{queryForm.startTime} + AND tsa.service_end >= #{queryForm.startTime} - AND tsa.report_time <= #{queryForm.endTime} + AND tsa.service_end <= #{queryForm.endTime} GROUP BY tsa.activity_name_id ) stats ON g.goods_id = stats.activity_name_id @@ -1187,20 +1180,20 @@ WHERE deleted_flag = 0 AND association_audit_status = 3 - AND report_time >= #{queryForm.startTime} + AND service_end >= #{queryForm.startTime} - AND report_time <= #{queryForm.endTime} + AND service_end <= #{queryForm.endTime} GROUP BY user_id ) user_stats ON tsa.user_id = user_stats.user_id WHERE tsa.deleted_flag = 0 AND tsa.association_audit_status = 3 - AND tsa.report_time >= #{queryForm.startTime} + AND tsa.service_end >= #{queryForm.startTime} - AND tsa.report_time <= #{queryForm.endTime} + AND tsa.service_end <= #{queryForm.endTime} AND tsa.firm_id = #{queryForm.firmId} @@ -1229,20 +1222,20 @@ WHERE deleted_flag = 0 AND association_audit_status = 3 - AND report_time >= #{queryForm.startTime} + AND service_end >= #{queryForm.startTime} - AND report_time <= #{queryForm.endTime} + AND service_end <= #{queryForm.endTime} GROUP BY firm_id ) firm_stats ON tsa.firm_id = firm_stats.firm_id WHERE tsa.deleted_flag = 0 AND tsa.association_audit_status = 3 - AND tsa.report_time >= #{queryForm.startTime} + AND tsa.service_end >= #{queryForm.startTime} - AND tsa.report_time <= #{queryForm.endTime} + AND tsa.service_end <= #{queryForm.endTime} AND tsa.firm_id = #{queryForm.firmId} @@ -1315,12 +1308,4 @@ ORDER BY tsa.create_time DESC - - diff --git a/yun-base/src/main/java/net/lab1024/sa/base/common/code/UserErrorCode.java b/yun-base/src/main/java/net/lab1024/sa/base/common/code/UserErrorCode.java index 01f5ac4..8cd8251 100644 --- a/yun-base/src/main/java/net/lab1024/sa/base/common/code/UserErrorCode.java +++ b/yun-base/src/main/java/net/lab1024/sa/base/common/code/UserErrorCode.java @@ -37,7 +37,7 @@ public enum UserErrorCode implements ErrorCode { LOGIN_FAIL_LOCK(30010, "登录连续失败已经被锁定,无法登录"), LOGIN_FAIL_WILL_LOCK(30011, "登录连续失败将会锁定提醒"), - FORM_REPEAT_SUBMIT_PENALTY(30013, "有处罚记录,无法申请证明开具"), + FORM_REPEAT_SUBMIT_PENALTY(30013, "有处分记录,无法申请证明开具"), HAS_EXIST(30014, "本案仅限一人申报,已由"), HAS_EXIST1(30015, "律师申报"), diff --git a/yun-base/src/main/java/net/lab1024/sa/base/module/support/message/controller/MessageController.java b/yun-base/src/main/java/net/lab1024/sa/base/module/support/message/controller/MessageController.java index 3a1548f..0c11b91 100644 --- a/yun-base/src/main/java/net/lab1024/sa/base/module/support/message/controller/MessageController.java +++ b/yun-base/src/main/java/net/lab1024/sa/base/module/support/message/controller/MessageController.java @@ -37,7 +37,8 @@ public class MessageController extends SupportBaseController { return ResponseDTO.userErrorParam("用户未登录"); } - queryForm.setSearchCount(false); + // 消息中心展示分页器,必须查询总条数,否则前端只能停留在第一页。 + queryForm.setSearchCount(true); queryForm.setReceiverUserId(user.getUserId()); queryForm.setReceiverUserType(user.getUserType().getValue()); return ResponseDTO.ok(messageService.query(queryForm));