mirror of
https://github.com/hpd840321/craftlabs-authorization-sdk.git
synced 2026-06-09 10:00:30 +08:00
feat(m1): add project stakeholder CRUD
This commit is contained in:
+98
@@ -0,0 +1,98 @@
|
||||
package cn.craftlabs.platform.api.persistence.project;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
@TableName("platform_project_stakeholder")
|
||||
public class PlatformProjectStakeholder {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
@TableField("project_id")
|
||||
private Long projectId;
|
||||
|
||||
@TableField("contact_name")
|
||||
private String contactName;
|
||||
|
||||
@TableField("contact_role")
|
||||
private String contactRole;
|
||||
|
||||
private String phone;
|
||||
|
||||
private String email;
|
||||
|
||||
@TableField("is_internal")
|
||||
private Boolean isInternal;
|
||||
|
||||
@TableField("created_at")
|
||||
private OffsetDateTime createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getProjectId() {
|
||||
return projectId;
|
||||
}
|
||||
|
||||
public void setProjectId(Long projectId) {
|
||||
this.projectId = projectId;
|
||||
}
|
||||
|
||||
public String getContactName() {
|
||||
return contactName;
|
||||
}
|
||||
|
||||
public void setContactName(String contactName) {
|
||||
this.contactName = contactName;
|
||||
}
|
||||
|
||||
public String getContactRole() {
|
||||
return contactRole;
|
||||
}
|
||||
|
||||
public void setContactRole(String contactRole) {
|
||||
this.contactRole = contactRole;
|
||||
}
|
||||
|
||||
public String getPhone() {
|
||||
return phone;
|
||||
}
|
||||
|
||||
public void setPhone(String phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public Boolean getIsInternal() {
|
||||
return isInternal;
|
||||
}
|
||||
|
||||
public void setIsInternal(Boolean isInternal) {
|
||||
this.isInternal = isInternal;
|
||||
}
|
||||
|
||||
public OffsetDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(OffsetDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package cn.craftlabs.platform.api.persistence.project;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface PlatformProjectStakeholderMapper extends BaseMapper<PlatformProjectStakeholder> {}
|
||||
+33
-1
@@ -4,6 +4,8 @@ import cn.craftlabs.platform.api.service.ProjectService;
|
||||
import cn.craftlabs.platform.api.web.dto.PageResponse;
|
||||
import cn.craftlabs.platform.api.web.dto.ProjectRequest;
|
||||
import cn.craftlabs.platform.api.web.dto.ProjectResponse;
|
||||
import cn.craftlabs.platform.api.web.dto.StakeholderRequest;
|
||||
import cn.craftlabs.platform.api.web.dto.StakeholderResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
@@ -20,7 +22,8 @@ import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/** 项目 API。{@code DELETE /{id}} 为<strong>物理删除</strong>。 */
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/projects")
|
||||
@Validated
|
||||
@@ -62,4 +65,33 @@ public class ProjectController {
|
||||
public void delete(@PathVariable("id") long id) {
|
||||
projectService.delete(id);
|
||||
}
|
||||
|
||||
@GetMapping("/{projectId}/stakeholders")
|
||||
public List<StakeholderResponse> listStakeholders(@PathVariable("projectId") long projectId) {
|
||||
return projectService.listStakeholders(projectId);
|
||||
}
|
||||
|
||||
@PostMapping("/{projectId}/stakeholders")
|
||||
@ResponseStatus(HttpStatus.CREATED)
|
||||
public StakeholderResponse addStakeholder(
|
||||
@PathVariable("projectId") long projectId,
|
||||
@Valid @RequestBody StakeholderRequest request) {
|
||||
return projectService.addStakeholder(projectId, request);
|
||||
}
|
||||
|
||||
@PutMapping("/{projectId}/stakeholders/{id}")
|
||||
public StakeholderResponse updateStakeholder(
|
||||
@PathVariable("projectId") long projectId,
|
||||
@PathVariable("id") long id,
|
||||
@Valid @RequestBody StakeholderRequest request) {
|
||||
return projectService.updateStakeholder(id, request);
|
||||
}
|
||||
|
||||
@DeleteMapping("/{projectId}/stakeholders/{id}")
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
public void deleteStakeholder(
|
||||
@PathVariable("projectId") long projectId,
|
||||
@PathVariable("id") long id) {
|
||||
projectService.deleteStakeholder(id);
|
||||
}
|
||||
}
|
||||
|
||||
+75
-1
@@ -2,9 +2,13 @@ package cn.craftlabs.platform.api.service;
|
||||
|
||||
import cn.craftlabs.platform.api.persistence.project.PlatformProject;
|
||||
import cn.craftlabs.platform.api.persistence.project.PlatformProjectMapper;
|
||||
import cn.craftlabs.platform.api.persistence.project.PlatformProjectStakeholder;
|
||||
import cn.craftlabs.platform.api.persistence.project.PlatformProjectStakeholderMapper;
|
||||
import cn.craftlabs.platform.api.web.dto.PageResponse;
|
||||
import cn.craftlabs.platform.api.web.dto.ProjectRequest;
|
||||
import cn.craftlabs.platform.api.web.dto.ProjectResponse;
|
||||
import cn.craftlabs.platform.api.web.dto.StakeholderRequest;
|
||||
import cn.craftlabs.platform.api.web.dto.StakeholderResponse;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
@@ -26,10 +30,12 @@ public class ProjectService {
|
||||
private static final String DEFAULT_PHASE = "PLANNING";
|
||||
|
||||
private final PlatformProjectMapper projectMapper;
|
||||
private final PlatformProjectStakeholderMapper stakeholderMapper;
|
||||
private final CustomerService customerService;
|
||||
|
||||
public ProjectService(PlatformProjectMapper projectMapper, CustomerService customerService) {
|
||||
public ProjectService(PlatformProjectMapper projectMapper, PlatformProjectStakeholderMapper stakeholderMapper, CustomerService customerService) {
|
||||
this.projectMapper = projectMapper;
|
||||
this.stakeholderMapper = stakeholderMapper;
|
||||
this.customerService = customerService;
|
||||
}
|
||||
|
||||
@@ -101,6 +107,74 @@ public class ProjectService {
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<StakeholderResponse> listStakeholders(long projectId) {
|
||||
LambdaQueryWrapper<PlatformProjectStakeholder> q =
|
||||
Wrappers.lambdaQuery(PlatformProjectStakeholder.class)
|
||||
.eq(PlatformProjectStakeholder::getProjectId, projectId)
|
||||
.orderByAsc(PlatformProjectStakeholder::getId);
|
||||
return stakeholderMapper.selectList(q).stream()
|
||||
.map(this::toStakeholderResponse)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public StakeholderResponse addStakeholder(long projectId, StakeholderRequest request) {
|
||||
requireProjectExists(projectId);
|
||||
PlatformProjectStakeholder s = new PlatformProjectStakeholder();
|
||||
s.setProjectId(projectId);
|
||||
s.setContactName(request.getContactName().trim());
|
||||
s.setContactRole(request.getContactRole());
|
||||
s.setPhone(request.getPhone());
|
||||
s.setEmail(request.getEmail());
|
||||
s.setIsInternal(request.getIsInternal() != null && request.getIsInternal());
|
||||
s.setCreatedAt(OffsetDateTime.now(ZoneOffset.UTC));
|
||||
stakeholderMapper.insert(s);
|
||||
return toStakeholderResponse(s);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public StakeholderResponse updateStakeholder(long stakeholderId, StakeholderRequest request) {
|
||||
PlatformProjectStakeholder s = stakeholderMapper.selectById(stakeholderId);
|
||||
if (s == null) {
|
||||
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "stakeholder not found");
|
||||
}
|
||||
s.setContactName(request.getContactName().trim());
|
||||
s.setContactRole(request.getContactRole());
|
||||
s.setPhone(request.getPhone());
|
||||
s.setEmail(request.getEmail());
|
||||
s.setIsInternal(request.getIsInternal() != null && request.getIsInternal());
|
||||
stakeholderMapper.updateById(s);
|
||||
return toStakeholderResponse(s);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void deleteStakeholder(long stakeholderId) {
|
||||
int rows = stakeholderMapper.deleteById(stakeholderId);
|
||||
if (rows == 0) {
|
||||
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "stakeholder not found");
|
||||
}
|
||||
}
|
||||
|
||||
private void requireProjectExists(long projectId) {
|
||||
if (projectMapper.selectById(projectId) == null) {
|
||||
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "project not found");
|
||||
}
|
||||
}
|
||||
|
||||
private StakeholderResponse toStakeholderResponse(PlatformProjectStakeholder s) {
|
||||
StakeholderResponse r = new StakeholderResponse();
|
||||
r.setId(s.getId());
|
||||
r.setProjectId(s.getProjectId());
|
||||
r.setContactName(s.getContactName());
|
||||
r.setContactRole(s.getContactRole());
|
||||
r.setPhone(s.getPhone());
|
||||
r.setEmail(s.getEmail());
|
||||
r.setIsInternal(s.getIsInternal());
|
||||
r.setCreatedAt(s.getCreatedAt());
|
||||
return r;
|
||||
}
|
||||
|
||||
private String resolvePhase(String phase) {
|
||||
return StringUtils.hasText(phase) ? phase.trim() : DEFAULT_PHASE;
|
||||
}
|
||||
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
package cn.craftlabs.platform.api.web.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
public class StakeholderRequest {
|
||||
|
||||
@NotBlank
|
||||
@Size(max = 128)
|
||||
private String contactName;
|
||||
|
||||
@Size(max = 64)
|
||||
private String contactRole;
|
||||
|
||||
@Size(max = 32)
|
||||
private String phone;
|
||||
|
||||
@Size(max = 128)
|
||||
private String email;
|
||||
|
||||
private Boolean isInternal;
|
||||
|
||||
public String getContactName() {
|
||||
return contactName;
|
||||
}
|
||||
|
||||
public void setContactName(String contactName) {
|
||||
this.contactName = contactName;
|
||||
}
|
||||
|
||||
public String getContactRole() {
|
||||
return contactRole;
|
||||
}
|
||||
|
||||
public void setContactRole(String contactRole) {
|
||||
this.contactRole = contactRole;
|
||||
}
|
||||
|
||||
public String getPhone() {
|
||||
return phone;
|
||||
}
|
||||
|
||||
public void setPhone(String phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public Boolean getIsInternal() {
|
||||
return isInternal;
|
||||
}
|
||||
|
||||
public void setIsInternal(Boolean isInternal) {
|
||||
this.isInternal = isInternal;
|
||||
}
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
package cn.craftlabs.platform.api.web.dto;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
public class StakeholderResponse {
|
||||
|
||||
private Long id;
|
||||
private Long projectId;
|
||||
private String contactName;
|
||||
private String contactRole;
|
||||
private String phone;
|
||||
private String email;
|
||||
private Boolean isInternal;
|
||||
private OffsetDateTime createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getProjectId() {
|
||||
return projectId;
|
||||
}
|
||||
|
||||
public void setProjectId(Long projectId) {
|
||||
this.projectId = projectId;
|
||||
}
|
||||
|
||||
public String getContactName() {
|
||||
return contactName;
|
||||
}
|
||||
|
||||
public void setContactName(String contactName) {
|
||||
this.contactName = contactName;
|
||||
}
|
||||
|
||||
public String getContactRole() {
|
||||
return contactRole;
|
||||
}
|
||||
|
||||
public void setContactRole(String contactRole) {
|
||||
this.contactRole = contactRole;
|
||||
}
|
||||
|
||||
public String getPhone() {
|
||||
return phone;
|
||||
}
|
||||
|
||||
public void setPhone(String phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public Boolean getIsInternal() {
|
||||
return isInternal;
|
||||
}
|
||||
|
||||
public void setIsInternal(Boolean isInternal) {
|
||||
this.isInternal = isInternal;
|
||||
}
|
||||
|
||||
public OffsetDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(OffsetDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
CREATE TABLE platform_project_stakeholder (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
project_id BIGINT NOT NULL REFERENCES platform_project(id),
|
||||
contact_name VARCHAR(128) NOT NULL,
|
||||
contact_role VARCHAR(64),
|
||||
phone VARCHAR(32),
|
||||
email VARCHAR(128),
|
||||
is_internal BOOLEAN DEFAULT FALSE,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
CREATE INDEX idx_stakeholder_project ON platform_project_stakeholder(project_id);
|
||||
Reference in New Issue
Block a user