Browse Source

feat:

1. 增加会议添加项目、移除项目接口;
tags/24121201
WendyYang 1 month ago
parent
commit
dae6bdf29b
8 changed files with 155 additions and 16 deletions
  1. +6
    -0
      hz-pm-api/src/main/java/com/hz/pm/api/common/statemachine/action/ProjectStateChangeAction.java
  2. +5
    -0
      hz-pm-api/src/main/java/com/hz/pm/api/common/statemachine/builder/impl/ProjectStateMachineBuilderImpl.java
  3. +5
    -0
      hz-pm-api/src/main/java/com/hz/pm/api/common/statemachine/event/ProjectStateChangeEvent.java
  4. +6
    -2
      hz-pm-api/src/main/java/com/hz/pm/api/common/statemachine/util/AbstractStateMachineUtil.java
  5. +14
    -0
      hz-pm-api/src/main/java/com/hz/pm/api/meeting/controller/MeetingController.java
  6. +29
    -0
      hz-pm-api/src/main/java/com/hz/pm/api/meeting/entity/req/MeetingAppendProjectReq.java
  7. +4
    -0
      hz-pm-api/src/main/java/com/hz/pm/api/meeting/entity/req/MeetingOptionProjectReq.java
  8. +86
    -14
      hz-pm-api/src/main/java/com/hz/pm/api/meeting/manage/MeetingManage.java

+ 6
- 0
hz-pm-api/src/main/java/com/hz/pm/api/common/statemachine/action/ProjectStateChangeAction.java View File

@@ -73,6 +73,12 @@ public class ProjectStateChangeAction {
project.setStatus(ProjectStatus.WITHOUT_EXPERT_REVIEW.getCode()); project.setStatus(ProjectStatus.WITHOUT_EXPERT_REVIEW.getCode());
} }


@OnTransition(source = "ON_EXPERT_REVIEW", target = "WITHOUT_EXPERT_REVIEW")
public void EXPERT_REVIEW_WITHDRAW(Message<ProjectStateChangeEvent> message) {
Project project = getProject(message);
project.setStatus(ProjectStatus.WITHOUT_EXPERT_REVIEW.getCode());
}

@OnTransition(source = "ON_EXPERT_REVIEW", target = "EXPERT_REVIEW_FAILED") @OnTransition(source = "ON_EXPERT_REVIEW", target = "EXPERT_REVIEW_FAILED")
public void EXPERT_REVIEW_FAILED(Message<ProjectStateChangeEvent> message) { public void EXPERT_REVIEW_FAILED(Message<ProjectStateChangeEvent> message) {
Project project = getProject(message); Project project = getProject(message);


+ 5
- 0
hz-pm-api/src/main/java/com/hz/pm/api/common/statemachine/builder/impl/ProjectStateMachineBuilderImpl.java View File

@@ -104,6 +104,11 @@ public class ProjectStateMachineBuilderImpl implements BaseStateMachineBuilder<P
.source(ProjectStatus.WITHOUT_EXPERT_REVIEW) .source(ProjectStatus.WITHOUT_EXPERT_REVIEW)
.target(ProjectStatus.ON_EXPERT_REVIEW) .target(ProjectStatus.ON_EXPERT_REVIEW)
.event(ProjectStateChangeEvent.EXPERT_REVIEW_SUBMIT).and() .event(ProjectStateChangeEvent.EXPERT_REVIEW_SUBMIT).and()
// 专家评审撤回
.withExternal()
.source(ProjectStatus.ON_EXPERT_REVIEW)
.target(ProjectStatus.WITHOUT_EXPERT_REVIEW)
.event(ProjectStateChangeEvent.EXPERT_REVIEW_WITHDRAW).and()
// 重新提交专家评审 // 重新提交专家评审
.withExternal() .withExternal()
.source(ProjectStatus.EXPERT_REVIEW_AFTER_MODIFY) .source(ProjectStatus.EXPERT_REVIEW_AFTER_MODIFY)


+ 5
- 0
hz-pm-api/src/main/java/com/hz/pm/api/common/statemachine/event/ProjectStateChangeEvent.java View File

@@ -54,6 +54,11 @@ public enum ProjectStateChangeEvent implements AbstractStateChangeEvent {
EXPERT_REVIEW_FAILED(null, ProjectStatus.ON_EXPERT_REVIEW.getCode(), null), EXPERT_REVIEW_FAILED(null, ProjectStatus.ON_EXPERT_REVIEW.getCode(), null),


/** /**
* @see ProjectStateChangeAction#EXPERT_REVIEW_WITHDRAW(Message)
*/
EXPERT_REVIEW_WITHDRAW(null, null, ProjectStatus.ON_EXPERT_REVIEW.getCode()),

/**
* @see ProjectStateChangeAction#EXPERT_REVIEW_PASS(Message) * @see ProjectStateChangeAction#EXPERT_REVIEW_PASS(Message)
*/ */
EXPERT_REVIEW_PASS(ProjectStatus.ON_EXPERT_REVIEW.getCode(), null, null), EXPERT_REVIEW_PASS(ProjectStatus.ON_EXPERT_REVIEW.getCode(), null, null),


+ 6
- 2
hz-pm-api/src/main/java/com/hz/pm/api/common/statemachine/util/AbstractStateMachineUtil.java View File

@@ -67,11 +67,15 @@ public interface AbstractStateMachineUtil<O, E extends Enum<E> & AbstractStateCh
} }
} }


//撤回
default void withDraw(O obj) { default void withDraw(O obj) {
withDraw(obj, null);
}

//撤回
default <T> void withDraw(O obj, T extraArgs) {
try { try {
synchronized (getLockClass()) { synchronized (getLockClass()) {
execute(obj, AbstractStateChangeEvent.getWithdrawEvent(eventClass(), statusFunction().apply(obj)));
execute(obj, AbstractStateChangeEvent.getWithdrawEvent(eventClass(), statusFunction().apply(obj)), extraArgs);
} }
} catch (Exception e) { } catch (Exception e) {
LOG.error("状态机撤回失败", e); LOG.error("状态机撤回失败", e);


+ 14
- 0
hz-pm-api/src/main/java/com/hz/pm/api/meeting/controller/MeetingController.java View File

@@ -192,6 +192,20 @@ public class MeetingController {
return meetingManage.optionProject(req); return meetingManage.optionProject(req);
} }


@PostMapping("/project/append")
@WebLog("添加项目")
@ApiOperation("添加项目")
public void appendProject(@RequestBody @Valid MeetingAppendProjectReq req) {
meetingManage.appendProject(req);
}

@PostMapping("/project/remove")
@WebLog("移除项目")
@ApiOperation("移除项目")
public void removeProject(@RequestBody @Valid MeetingAppendProjectReq req) {
meetingManage.removeProject(req);
}

@GetMapping("/{meetingId}/projects") @GetMapping("/{meetingId}/projects")
@ApiOperation("会议关联项目列表") @ApiOperation("会议关联项目列表")
public List<MeetingReviewProjectVO> projectsByMeetingId(@PathVariable Long meetingId) { public List<MeetingReviewProjectVO> projectsByMeetingId(@PathVariable Long meetingId) {


+ 29
- 0
hz-pm-api/src/main/java/com/hz/pm/api/meeting/entity/req/MeetingAppendProjectReq.java View File

@@ -0,0 +1,29 @@
package com.hz.pm.api.meeting.entity.req;

import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.List;

/**
* <p>
* MeetingAppendProjectReq
* </p>
*
* @author WendyYang
* @since 17:36 2024/12/5
*/
@Data
public class MeetingAppendProjectReq {

@NotNull(message = "会议ID不能为空")
@ApiModelProperty("会议ID")
private Long meetingId;

@NotEmpty(message = "项目编码列表不能为空")
@ApiModelProperty("项目编码列表")
private List<String> projectCodes;

}

+ 4
- 0
hz-pm-api/src/main/java/com/hz/pm/api/meeting/entity/req/MeetingOptionProjectReq.java View File

@@ -1,6 +1,7 @@
package com.hz.pm.api.meeting.entity.req; package com.hz.pm.api.meeting.entity.req;


import com.ningdatech.basic.model.PagePo; import com.ningdatech.basic.model.PagePo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;


@@ -18,4 +19,7 @@ public class MeetingOptionProjectReq extends PagePo {


private String meetingType; private String meetingType;


@ApiModelProperty("会议ID")
private Long meetingId;

} }

+ 86
- 14
hz-pm-api/src/main/java/com/hz/pm/api/meeting/manage/MeetingManage.java View File

@@ -31,10 +31,7 @@ import com.hz.pm.api.meeting.builder.MeetingReviewProjectBuilder;
import com.hz.pm.api.meeting.builder.ProjectReviewResultBuilder; import com.hz.pm.api.meeting.builder.ProjectReviewResultBuilder;
import com.hz.pm.api.meeting.entity.domain.*; import com.hz.pm.api.meeting.entity.domain.*;
import com.hz.pm.api.meeting.entity.dto.*; import com.hz.pm.api.meeting.entity.dto.*;
import com.hz.pm.api.meeting.entity.enumeration.ExpertAttendStatusEnum;
import com.hz.pm.api.meeting.entity.enumeration.ExpertInviteTypeEnum;
import com.hz.pm.api.meeting.entity.enumeration.MeetingReviewTypeEnum;
import com.hz.pm.api.meeting.entity.enumeration.MeetingStatusEnum;
import com.hz.pm.api.meeting.entity.enumeration.*;
import com.hz.pm.api.meeting.entity.req.*; import com.hz.pm.api.meeting.entity.req.*;
import com.hz.pm.api.meeting.entity.vo.*; import com.hz.pm.api.meeting.entity.vo.*;
import com.hz.pm.api.meeting.helper.ExpertInviteHelper; import com.hz.pm.api.meeting.helper.ExpertInviteHelper;
@@ -164,21 +161,19 @@ public class MeetingManage {
meetingService.save(meeting); meetingService.save(meeting);
if (Boolean.TRUE.equals(meetingBasic.getIsInnerProject())) { if (Boolean.TRUE.equals(meetingBasic.getIsInnerProject())) {
List<Long> projectIds = new ArrayList<>(); List<Long> projectIds = new ArrayList<>();
List<MeetingInnerProject> meetingInnerProjects = meetingBasic.getInnerProjects().stream().map(w -> {
List<MeetingInnerProject> mips = meetingBasic.getInnerProjects().stream().map(w -> {
MeetingInnerProject project = BeanUtil.copyProperties(w, MeetingInnerProject.class); MeetingInnerProject project = BeanUtil.copyProperties(w, MeetingInnerProject.class);
project.setMeetingId(meeting.getId()); project.setMeetingId(meeting.getId());
projectIds.add(w.getProjectId()); projectIds.add(w.getProjectId());
return project; return project;
}).collect(Collectors.toList()); }).collect(Collectors.toList());
List<Project> projects = projectService.listByIds(projectIds); List<Project> projects = projectService.listByIds(projectIds);
boolean allMatch = projects.stream().allMatch(w -> ProjectStatus.EXPERT_REVIEW_FAILED.eq(w.getStatus())
|| ProjectStatus.WITHOUT_EXPERT_REVIEW.eq(w.getStatus()));
if (!allMatch) {
if (CollUtil.anyMatch(projects, w -> !ProjectStatus.WITHOUT_EXPERT_REVIEW.eq(w.getStatus()))) {
throw ReturnException.wrap("当前关联项目包含暂不可评审项目"); throw ReturnException.wrap("当前关联项目包含暂不可评审项目");
} }
projects.forEach(projectStateMachineUtil::pass); projects.forEach(projectStateMachineUtil::pass);
projectService.updateBatchById(projects); projectService.updateBatchById(projects);
meetingInnerProjectService.saveBatch(meetingInnerProjects);
meetingInnerProjectService.saveBatch(mips);
} }
// 抽取专家 // 抽取专家
inviteRule.setMeetingId(meeting.getId()); inviteRule.setMeetingId(meeting.getId());
@@ -1009,6 +1004,79 @@ public class MeetingManage {
return PageVo.of(page.getRecords(), page.getTotal()); return PageVo.of(page.getRecords(), page.getTotal());
} }


@Transactional(rollbackFor = Exception.class)
public synchronized void appendProject(MeetingAppendProjectReq req) {
Meeting meeting = meetingService.getById(req.getMeetingId());
Assert.notNull(meeting, "会议不存在");
Assert.isTrue(MeetingStatusEnum.NORMAL.eq(meeting.getStatus()), "当前会议无法添加项目");
List<String> projectCodes = req.getProjectCodes();
Wrapper<MeetingInnerProject> query = Wrappers.lambdaQuery(MeetingInnerProject.class)
.select(MeetingInnerProject::getMeetingId, MeetingInnerProject::getProjectCode,
MeetingInnerProject::getId, MeetingInnerProject::getReviewResult)
.in(MeetingInnerProject::getProjectCode, projectCodes);
Collection<MeetingInnerProject> linkedProjList = BizUtils.groupFirst(meetingInnerProjectService.list(query),
MeetingInnerProject::getProjectId, Comparator.comparing(MeetingInnerProject::getId).reversed());
// 移除已关联当前会议的项目
projectCodes.removeIf(w -> CollUtil.anyMatch(linkedProjList,
x -> x.getProjectCode().equals(w) && x.getMeetingId().equals(meeting.getId())));
if (projectCodes.isEmpty()) {
return;
}
linkedProjList.removeIf(w -> w.getMeetingId().equals(meeting.getId()));
if (CollUtil.isNotEmpty(linkedProjList)) {
Set<Long> linkedMeetingIds = CollUtils.fieldSet(linkedProjList, MeetingInnerProject::getMeetingId);
if (!linkedMeetingIds.isEmpty()) {
List<Meeting> meetings = meetingService.listByIds(linkedMeetingIds);
Map<Long, Meeting> meetingMap = CollUtils.listToMap(meetings, Meeting::getId);
if (CollUtil.anyMatch(linkedProjList, w -> {
Meeting linkedMeeting = meetingMap.get(w.getMeetingId());
return MeetingStatusEnum.NORMAL.eq(linkedMeeting.getStatus())
&& ExpertReviewResultEnum.PASS.eq(w.getReviewResult());
})) {
throw ReturnException.wrap("所选项目包含已评审通过的项目");
}
}
}
List<Project> projects = projectService.listNewestByProjectCodes(projectCodes);
if (CollUtil.anyMatch(projects, w -> !ProjectStatus.WITHOUT_EXPERT_REVIEW.eq(w.getStatus()))) {
throw ReturnException.wrap("所选项目必须为待专家评审状态");
}
List<MeetingInnerProject> mips = CollUtils.convert(projects, w -> {
projectStateMachineUtil.pass(w);
MeetingInnerProject mip = new MeetingInnerProject();
mip.setProjectId(w.getId());
mip.setMeetingId(req.getMeetingId());
mip.setProjectCode(w.getProjectCode());
return mip;
});
projectService.updateBatchById(projects);
meetingInnerProjectService.saveBatch(mips);
}

@Transactional(rollbackFor = Exception.class)
public synchronized void removeProject(MeetingAppendProjectReq req) {
Meeting meeting = meetingService.getById(req.getMeetingId());
Assert.notNull(meeting, "会议不存在");
Assert.isTrue(MeetingStatusEnum.NORMAL.eq(meeting.getStatus()), "当前会议无法移除项目");
List<String> projectCodes = req.getProjectCodes();
Wrapper<MeetingInnerProject> query = Wrappers.lambdaQuery(MeetingInnerProject.class)
.select(MeetingInnerProject::getId, MeetingInnerProject::getProjectCode, MeetingInnerProject::getReviewResult)
.in(MeetingInnerProject::getProjectCode, projectCodes)
.eq(MeetingInnerProject::getMeetingId, meeting.getId());
List<MeetingInnerProject> linkedProjList = meetingInnerProjectService.list(query);
if (projectCodes.size() != linkedProjList.size()) {
throw ReturnException.wrap("所选项目与当前会议未关联");
}
if (CollUtil.anyMatch(linkedProjList, w -> w.getReviewResult() != null)) {
throw ReturnException.wrap("已上传评审结果的项目不能移除");
}
Set<Long> mipIds = CollUtils.fieldSet(linkedProjList, MeetingInnerProject::getId);
meetingInnerProjectService.removeByIds(mipIds);
List<Project> projects = projectService.listNewestByProjectCodes(projectCodes);
projects.forEach(projectStateMachineUtil::withDraw);
projectService.updateBatchById(projects);
}

public PageVo<MeetingReviewProjectVO> optionProject(MeetingOptionProjectReq req) { public PageVo<MeetingReviewProjectVO> optionProject(MeetingOptionProjectReq req) {
String meetingType = req.getMeetingType(); String meetingType = req.getMeetingType();
LambdaQueryWrapper<Project> query = Wrappers.lambdaQuery(Project.class) LambdaQueryWrapper<Project> query = Wrappers.lambdaQuery(Project.class)
@@ -1035,8 +1103,8 @@ public class MeetingManage {
case PRELIMINARY_SCHEME_REVIEW: case PRELIMINARY_SCHEME_REVIEW:
case CONSTRUCTION_SCHEME_REVIEW: case CONSTRUCTION_SCHEME_REVIEW:
case DEPT_JOIN_REVIEW: case DEPT_JOIN_REVIEW:
buildOptionProjectQuery(query, meetingType, null);
buildOptionProjectQuery(query, meetingType, null);
buildOptionProjectQuery(query, meetingType);
buildOptionProjectQuery(query, meetingType);
break; break;
case ACCEPTANCE_SCHEME_REVIEW: case ACCEPTANCE_SCHEME_REVIEW:
buildOptionProjectQuery(query, meetingType, ProjectStatus.ON_FINALLY_INSPECTED); buildOptionProjectQuery(query, meetingType, ProjectStatus.ON_FINALLY_INSPECTED);
@@ -1046,11 +1114,15 @@ public class MeetingManage {
case PROJECT_REVIEW: case PROJECT_REVIEW:
case EXPERT_REVIEW: case EXPERT_REVIEW:
query.isNotNull(Project::getConstructionPlanSealFile); query.isNotNull(Project::getConstructionPlanSealFile);
buildOptionProjectQuery(query, meetingType, ProjectStatus.WITHOUT_EXPERT_REVIEW, ProjectStatus.EXPERT_REVIEW_FAILED);
buildOptionProjectQuery(query, meetingType, ProjectStatus.WITHOUT_EXPERT_REVIEW);
break; break;
default: default:
return PageVo.empty(); return PageVo.empty();
} }
if (req.getMeetingId() != null) {
query.notExists("select 1 from meeting_inner_project mip on mip.project_code = nd_project.project_code" +
" and nd_project.newest = 1 and mip.meeting_id = {0}", req.getMeetingId());
}
ProjectManageUtil.projectBaseQuery(query); ProjectManageUtil.projectBaseQuery(query);
Page<Project> page = projectService.page(req.page(), query); Page<Project> page = projectService.page(req.page(), query);
PageVo<MeetingReviewProjectVO> result = PageVo.of(null, page.getTotal()); PageVo<MeetingReviewProjectVO> result = PageVo.of(null, page.getTotal());
@@ -1099,9 +1171,9 @@ public class MeetingManage {
query.in(Project::getStatus, statusCodeList); query.in(Project::getStatus, statusCodeList);
} }
String sql = String.format("select 1 from meeting m inner join meeting_inner_project mip on" + String sql = String.format("select 1 from meeting m inner join meeting_inner_project mip on" +
" m.is_inner_project = 1 and m.id = mip.meeting_id and nd_project.id = mip.project_id" +
" m.is_inner_project = 1 and m.id = mip.meeting_id and nd_project.project_code = mip.project_code" +
" and m.type = %s and m.status != 3", meetingType); " and m.type = %s and m.status != 3", meetingType);
String sql2 = String.format("1 = (select case when review_result in (0,2) then 1 else 0 end from " +
String sql2 = String.format("1 = (select case when review_result in (2) then 1 else 0 end from " +
" (select ner.review_result from meeting m inner join meeting_inner_project mip on" + " (select ner.review_result from meeting m inner join meeting_inner_project mip on" +
" m.is_inner_project = 1 and m.id = mip.meeting_id and nd_project.id = mip.project_id" + " m.is_inner_project = 1 and m.id = mip.meeting_id and nd_project.id = mip.project_id" +
" and m.type = %s and m.status != 3 left join nd_expert_review ner on ner.meeting_id = m.id " + " and m.type = %s and m.status != 3 left join nd_expert_review ner on ner.meeting_id = m.id " +


Loading…
Cancel
Save