@ -11,6 +11,7 @@ import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont ;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy ;
import com.alibaba.excel.write.style.column.SimpleColumnWidthStyleStrategy ;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper ;
import org.apache.poi.ss.usermodel.* ;
import org.apache.poi.ss.util.CellRangeAddress ;
import org.apache.poi.ss.util.CellRangeAddressList ;
@ -197,10 +198,11 @@ public class ServiceApplicationsService {
// 协会用户(CEO):可以看到所有律所提交的数据,但只显示associationAuditStatus为待审核(1)、通过(3)和拒绝(4)的数据和自己创建的数据
// CEO作为最高权限用户,不需要限制在特定部门范围内,直接设置includeAssociationReviewed即可
queryForm . setIncludeAssociationReviewed ( true ) ;
// 检查是否只能查看成本填报律所的数据
// 检查审批数据查看范围(1=只看有成本的律所,2=只看无成本的律所)
RequestEmployee requestEmployee = ( RequestEmployee ) requestUser ;
if ( Boolean . TRUE . equals ( requestEmployee . getCostReportViewOnly ( ) ) ) {
queryForm . setCostReportViewOnly ( true ) ;
Integer costReportViewOnly = requestEmployee . getCostReportViewOnly ( ) ;
if ( costReportViewOnly ! = null & & ( costReportViewOnly = = 1 | | costReportViewOnly = = 2 ) ) {
queryForm . setCostReportViewOnly ( costReportViewOnly ) ;
}
} else if ( isFirmRole ) {
// 律所主任或行政:能看到自己部门范围内的数据,但只显示firmAuditStatus为待审核、通过和拒绝的数据和自己创建的数据
@ -242,6 +244,8 @@ public class ServiceApplicationsService {
queryForm . setEmployeeIdList ( longs ) ;
// 普通用户不需要审核状态过滤,可以看到自己所有状态的数据
queryForm . setNoStatusFilter ( true ) ;
// 普通用户只看自己的数据
queryForm . setUserId ( requestUser . getUserId ( ) ) ;
}
queryForm . setCurrentUserId ( requestUser . getUserId ( ) ) ; // 传递当前用户ID用于SQL中的特殊过滤
}
@ -278,64 +282,17 @@ public class ServiceApplicationsService {
public PageResult < ServiceApplicationsVO > queryPageReport ( @Valid ServiceApplicationsQueryForm queryForm ) {
List < Long > longs = new ArrayList < > ( ) ;
Page < ? > page = SmartPageUtil . convert2PageQuery ( queryForm ) ;
//根据用户角色的查询数据范围来查询数据
RequestEmployee requestUser = AdminRequestUtil . getRequestUser ( ) ;
List < RoleVO > roleIdList = roleEmployeeService . getRoleIdList ( requestUser . getUserId ( ) ) ;
String roleCode = AdminRequestUtil . getRoleCode ( roleIdList ) ;
// 检查角色类型
boolean isAssociationRole = AdminRequestUtil . isAssociationRole ( roleIdList ) ;
boolean isFirmRole = AdminRequestUtil . isFirmRole ( roleIdList ) ; // 律所主任或行政
// 服务申报页:非超管仅查看本人创建的数据(含草稿);审批范围逻辑见 queryPage
if ( ! UserTypeEnum . Admin . getDesc ( ) . equals ( roleCode ) ) {
if ( isAssociationRole ) {
// 协会用户(CEO):可以看到所有律所提交的数据,但只显示associationAuditStatus为待审核(1)、通过(3)和拒绝(4)的数据和自己创建的数据
queryForm . setIncludeAssociationReviewed ( true ) ;
} else if ( isFirmRole ) {
// 律所主任或行政:能看到自己部门范围内的数据,但只显示firmAuditStatus为待审核、通过和拒绝的数据和自己创建的数据
Integer oneByRoleId = dataScopeViewService . getOneByRoleId ( roleIdList . get ( 0 ) . getRoleId ( ) ) ;
// 获取自己部门范围内的数据
List < Long > 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 ) ;
// 律所主任或行政可以查看部门内所有已审核数据以及自己创建的有效数据(明确排除未提交的草稿)
queryForm . setIncludeFirmReviewed ( true ) ;
} else {
// 律所普通用户:只能看到自己提交的数据,包括所有firmAuditStatus状态
Integer oneByRoleId = dataScopeViewService . getOneByRoleId ( roleIdList . get ( 0 ) . getRoleId ( ) ) ;
if ( DataScopeViewTypeEnum . ME . getValue ( ) . equals ( oneByRoleId ) ) {
longs = dataScopeViewService . getCanViewEmployeeId ( DataScopeViewTypeEnum . ME , requestUser . getUserId ( ) ) ;
} else if ( DataScopeViewTypeEnum . DEPARTMENT . getValue ( ) . equals ( oneByRoleId ) ) {
longs = dataScopeViewService . getCanViewEmployeeId ( DataScopeViewTypeEnum . DEPARTMENT , requestUser . getUserId ( ) ) ;
}
// 确保普通用户至少能看到自己的数据
if ( longs = = null ) {
longs = new ArrayList < > ( ) ;
}
if ( ! longs . contains ( requestUser . getUserId ( ) ) ) {
longs . add ( requestUser . getUserId ( ) ) ;
}
queryForm . setEmployeeIdList ( longs ) ;
// 普通用户不需要审核状态过滤,可以看到自己所有状态的数据
queryForm . setNoStatusFilter ( true ) ;
}
queryForm . setUserId ( requestUser . getUserId ( ) ) ;
queryForm . setNoStatusFilter ( true ) ;
queryForm . setCurrentUserId ( requestUser . getUserId ( ) ) ;
}
queryForm . setUserId ( requestUser . getUserId ( ) ) ;
List < ServiceApplicationsVO > list = serviceApplicationsDao . queryPage ( page , queryForm ) ;
if ( ! CollectionUtils . isEmpty ( list ) ) {
//翻译单位名称和用户名称
@ -480,7 +437,7 @@ public class ServiceApplicationsService {
}
} else {
// 律所审核完成:发给律师(个人)
title = "你的服务申报「" + serviceBrief + "」律所通过 " ;
title = "你的服务申报「" + serviceBrief + "」律所" + statusText ;
}
net . lab1024 . sa . base . module . support . message . domain . MessageSendForm sendForm = new net . lab1024 . sa . base . module . support . message . domain . MessageSendForm ( ) ;
@ -579,13 +536,18 @@ public class ServiceApplicationsService {
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 ( ) ) ;
// 二次提交时保持原有的提交时间不变,不修改 reportTime
// 首次从草稿/已有记录提交时补提交时间;二次提交保持原有提交时间不变。
if ( serviceApplications . getReportTime ( ) = = null ) {
serviceApplications . setReportTime ( LocalDateTime . now ( ) ) ;
}
serviceApplicationsDao . updateById ( serviceApplications ) ;
serviceApplicationsDao . markSubmitted ( serviceApplications . getApplicationId ( ) , ReviewEnum . APPROVAL . getValue ( ) ) ;
}
return ResponseDTO . ok ( ) ;
}
@ -818,13 +780,22 @@ public class ServiceApplicationsService {
return ResponseDTO . error ( UserErrorCode . NO_PERMISSION ) ;
}
Long targetFirmId = null ;
LocalDateTime firstReportTime = null ;
// 查询这些记录的详细信息,进行权限和状态校验
for ( Long id : idList ) {
ServiceApplicationsVO record = serviceApplicationsDao . queryById ( id ) ;
ServiceApplicationsEntity record = serviceApplicationsDao . select ById( id ) ;
if ( record = = null ) {
return ResponseDTO . error ( UserErrorCode . DATA_NOT_EXIST , "记录ID:" + id + "不存在" ) ;
}
// 记录律所ID和上报时间(用于后续成本校验)
if ( targetFirmId = = null ) {
targetFirmId = record . getFirmId ( ) ;
firstReportTime = record . getReportTime ( ) ;
}
// 校验1:只能上报本所的数据
if ( ! record . getFirmId ( ) . equals ( requestUser . getDepartmentId ( ) ) ) {
return ResponseDTO . error ( UserErrorCode . NO_PERMISSION , "只能上报本所的数据" ) ;
@ -842,6 +813,20 @@ public class ServiceApplicationsService {
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 < String > unsubmittedQuarters = firmReportsDao . findUnsubmittedEarlierQuarters ( targetFirmId . intValue ( ) , reportYear , reportQuarter ) ;
if ( unsubmittedQuarters ! = null & & ! unsubmittedQuarters . isEmpty ( ) ) {
String quarterNames = String . join ( "、" , unsubmittedQuarters ) ;
return ResponseDTO . userErrorParam (
"请先提交" + quarterNames + "的成本报表后,再批量上报服务数据" ) ;
}
}
serviceApplicationsDao . batchSubmitAsAssociation ( idList , ReviewEnum . APPROVAL . getValue ( ) ) ;
@ -883,6 +868,7 @@ public class ServiceApplicationsService {
serviceApplications . setFirmAuditStatus ( ReviewEnum . APPROVAL . getValue ( ) ) ;
serviceApplications . setReportTime ( LocalDateTime . now ( ) ) ;
serviceApplicationsDao . insert ( serviceApplications ) ;
serviceApplicationsDao . markSubmitted ( serviceApplications . getApplicationId ( ) , ReviewEnum . APPROVAL . getValue ( ) ) ;
} else {
//判断活动类型查询案号有没有存在
if ( "TIME" . equals ( addForm . getServiceType ( ) ) ) {
@ -903,8 +889,11 @@ public class ServiceApplicationsService {
ServiceApplicationsEntity serviceApplications = serviceApplicationsDao . selectById ( addForm . getApplicationId ( ) ) ;
SmartBeanUtil . copyProperties ( addForm , serviceApplications ) ;
serviceApplications . setFirmAuditStatus ( ReviewEnum . APPROVAL . getValue ( ) ) ;
serviceApplications . setReportTime ( LocalDateTime . now ( ) ) ;
if ( serviceApplications . getReportTime ( ) = = null ) {
serviceApplications . setReportTime ( LocalDateTime . now ( ) ) ;
}
serviceApplicationsDao . updateById ( serviceApplications ) ;
serviceApplicationsDao . markSubmitted ( serviceApplications . getApplicationId ( ) , ReviewEnum . APPROVAL . getValue ( ) ) ;
}
return ResponseDTO . ok ( ) ;
@ -1038,29 +1027,11 @@ public class ServiceApplicationsService {
}
// ============================================================
// 根据上报时间确定成本归属的年份和季度
// 根据上报时间确定成本归属的年份和季度(直接对应自然账期)
// ============================================================
int reportMonth = reportTime . getMonthValue ( ) ;
int costYear ;
int costQuarter ;
if ( reportMonth > = 1 & & reportMonth < = 3 ) {
// 1-3月上报 → 上年 Q4
costYear = reportTime . getYear ( ) - 1 ;
costQuarter = 4 ;
} else if ( reportMonth > = 4 & & reportMonth < = 6 ) {
// 4-6月上报 → 当年 Q1
costYear = reportTime . getYear ( ) ;
costQuarter = 1 ;
} else if ( reportMonth > = 7 & & reportMonth < = 9 ) {
// 7-9月上报 → 当年 Q2
costYear = reportTime . getYear ( ) ;
costQuarter = 2 ;
} else {
// 10-12月上报 → 当年 Q3
costYear = reportTime . getYear ( ) ;
costQuarter = 3 ;
}
int costYear = reportTime . getYear ( ) ;
int costQuarter = ( reportMonth - 1 ) / 3 + 1 ;
log . info ( "更新成本:firmId={}, reportTime={}, reportMonth={}, 成本归属年份={}, 成本归属季度=Q{}" ,
firmId , reportTime , reportMonth , costYear , costQuarter ) ;
@ -1876,8 +1847,8 @@ public class ServiceApplicationsService {
try {
ServiceApplicationsEntity entity = serviceApplicationsDao . selectById ( Long . valueOf ( applicationId ) ) ;
if ( entity ! = null ) {
// 律协审核通过时,更新成本管理表中的实际公益成本和比例
if ( isAssociationReview & & ReviewEnum . PASS . getValue ( ) . equals ( auditResult ) ) {
// 律协批量通过/拒绝都要重算,保证驳回后实际成本能扣回。
if ( isAssociationReview & & isCostAffectingAssociationAudit ( auditResult ) ) {
updateActualCostInFirmReports ( entity ) ;
}
// 发送通知
@ -1908,6 +1879,20 @@ public class ServiceApplicationsService {
* @return 成本 ( 万元 )
* /
public BigDecimal getServiceApplicationsCost ( ServiceLawyerQueryForm queryForm ) {
if ( queryForm = = null ) {
return BigDecimal . ZERO ;
}
RequestEmployee requestUser = AdminRequestUtil . getRequestUser ( ) ;
if ( requestUser = = null | | ! Boolean . TRUE . equals ( requestUser . getCostVisibleFlag ( ) ) ) {
return BigDecimal . ZERO ;
}
List < RoleVO > roles = roleEmployeeService . getRoleIdList ( requestUser . getEmployeeId ( ) ) ;
boolean isAssociationOrAdmin = AdminRequestUtil . isAssociationRole ( roles )
| | UserTypeEnum . Admin . getDesc ( ) . equals ( AdminRequestUtil . getRoleCode ( roles ) ) ;
if ( ! isAssociationOrAdmin ) {
queryForm . setFirmId ( requestUser . getDepartmentId ( ) ) ;
}
Long firmId = queryForm . getFirmId ( ) ;
int year = queryForm . getYear ( ) ;
Integer quarter = queryForm . getQuarter ( ) ;
@ -2003,32 +1988,68 @@ public class ServiceApplicationsService {
* 按部门批量审核 ( 仅律协角色可用 )
* 前端统一传递 auditResult 字段 ( 3 = 通过 , 4 = 拒绝 )
* /
@Transactional
public ResponseDTO < String > batchReviewByDepartmentId ( @Valid ServiceApplicationsUpdateForm updateForm ) {
RequestEmployee requestUser = AdminRequestUtil . getRequestUser ( ) ;
// 统一从 auditResult 获取审核结果
Integer auditResult = updateForm . getAuditResult ( ) ;
if ( auditResult = = null ) {
auditResult = updateForm . getAssociationAuditStatus ( ) ;
}
if ( auditResult = = null ) {
return ResponseDTO . userErrorParam ( "审核结果不能为空" ) ;
}
if ( updateForm . getFirmId ( ) = = null ) {
return ResponseDTO . userErrorParam ( "律所不能为空" ) ;
}
List < ServiceApplicationsEntity > affectedApplications = serviceApplicationsDao . selectList (
new LambdaQueryWrapper < ServiceApplicationsEntity > ( )
. eq ( ServiceApplicationsEntity : : getFirmId , updateForm . getFirmId ( ) )
. eq ( ServiceApplicationsEntity : : getAssociationAuditStatus , ReviewEnum . APPROVAL . getValue ( ) )
. eq ( ServiceApplicationsEntity : : getDeletedFlag , 0 )
) ;
String auditOpinion = updateForm . getAuditRemark ( ) ;
if ( auditOpinion = = null ) {
auditOpinion = updateForm . getAssociationAuditOpinion ( ) ;
}
updateForm . setAssociationAuditStatus ( auditResult ) ;
updateForm . setAssociationAuditUser ( requestUser . getEmployeeId ( ) ) ;
updateForm . setAssociationAuditTime ( LocalDateTime . now ( ) ) ;
updateForm . setAssociationAuditOpinion ( auditOpinion ) ;
serviceApplicationsDao . batchReviewByDepartmentId ( updateForm ) ;
for ( ServiceApplicationsEntity entity : affectedApplications ) {
if ( isCostAffectingAssociationAudit ( auditResult ) ) {
updateActualCostInFirmReports ( entity ) ;
}
entity . setAssociationAuditStatus ( auditResult ) ;
entity . setAssociationAuditOpinion ( auditOpinion ) ;
sendAuditNotification ( entity , auditResult , auditOpinion , true ) ;
}
return ResponseDTO . ok ( ) ;
}
private boolean isCostAffectingAssociationAudit ( Integer auditResult ) {
return ReviewEnum . PASS . getValue ( ) . equals ( auditResult ) | | ReviewEnum . REFUSE . getValue ( ) . equals ( auditResult ) ;
}
public Long queryNoReview ( ) {
//查询上个月是否有未审核的数据,注意跨年情况
RequestEmployee requestUser = AdminRequestUtil . getRequestUser ( ) ;
Long departmentId = requestUser . getDepartmentId ( ) ;
// 检查是否只能查看成本填报律所的数据
Boolean costReportViewOnly = requestUser . getCostReportViewOnly ( ) ;
if ( Boolean . TRUE . equals ( costReportViewOnly ) ) {
// 只统计律所主任或行政有成本查看权限的律所的未审核数据
// 检查审批数据查看范围(1=只看有成本的律所,2=只看无成本的律所)
Integer costReportViewOnly = requestUser . getCostReportViewOnly ( ) ;
if ( costReportViewOnly ! = null & & costReportViewOnly = = 1 ) {
return serviceApplicationsDao . queryNoReviewWithCostFilter ( ) ;
}
if ( costReportViewOnly ! = null & & costReportViewOnly = = 2 ) {
return serviceApplicationsDao . queryNoReviewWithoutCostFilter ( ) ;
}
return serviceApplicationsDao . queryNoReview ( departmentId ) ;
}
@ -2948,10 +2969,11 @@ public class ServiceApplicationsService {
public PageResult < ServiceReportStatisticsVO > getServiceReportStatistics ( ServiceApplicationsQueryForm queryForm ) {
Page < ? > page = SmartPageUtil . convert2PageQuery ( queryForm ) ;
// 添加权限控制:检查是否只能查看成本填报律所的数据
// 添加权限控制:检查审批数据查看范围(1=只看有成本的律所,2=只看无成本的律所)
RequestEmployee requestUser = AdminRequestUtil . getRequestUser ( ) ;
if ( Boolean . TRUE . equals ( requestUser . getCostReportViewOnly ( ) ) ) {
queryForm . setCostReportViewOnly ( true ) ;
Integer costReportViewOnly = requestUser . getCostReportViewOnly ( ) ;
if ( costReportViewOnly ! = null & & ( costReportViewOnly = = 1 | | costReportViewOnly = = 2 ) ) {
queryForm . setCostReportViewOnly ( costReportViewOnly ) ;
}
// 如果没有指定季度,则使用年度范围,否则使用季度范围
@ -3226,6 +3248,7 @@ public class ServiceApplicationsService {
// 固定列(合并两行)
head . add ( Arrays . asList ( "序号" , " " ) ) ;
head . add ( Arrays . asList ( "律所名称" , " " ) ) ;
head . add ( Arrays . asList ( "律师名称" , " " ) ) ;
// 构建分类表头 - 记录每列的元数据信息(合计列在每个分类的前面)
@ -3261,6 +3284,7 @@ public class ServiceApplicationsService {
for ( LawyerActivityCountVO lawyer : lawyerMap . values ( ) ) {
List < Object > row = new ArrayList < > ( ) ;
row . add ( + + rowIndex ) ; // 序号
row . add ( lawyer . getFirmName ( ) ! = null ? lawyer . getFirmName ( ) : "" ) ; // 律所名称
row . add ( lawyer . getLawyerName ( ) ) ;
// 按列元数据顺序填充数据
@ -3302,6 +3326,7 @@ public class ServiceApplicationsService {
// 添加合计行
List < Object > totalRow = new ArrayList < > ( ) ;
totalRow . add ( "-" ) ;
totalRow . add ( "-" ) ;
totalRow . add ( "合计" ) ;
int grandTotal = 0 ;
@ -3360,7 +3385,7 @@ public class ServiceApplicationsService {
. head ( head )
. registerWriteHandler ( styleStrategy )
. registerWriteHandler ( new SimpleColumnWidthStyleStrategy ( 12 ) )
. registerWriteHandler ( new SubtotalMergeHandler ( columnMetaList , 2 ) )
. registerWriteHandler ( new SubtotalMergeHandler ( columnMetaList , 3 ) )
. registerWriteHandler ( new CustomRowHeightHandler ( ( short ) 500 ) )
. build ( ) ;
excelWriter . write ( data , writeSheet ) ;