+206
@@ -0,0 +1,206 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.transport.pojo.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldStrategy;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.springblade.core.tenant.mp.TenantEntity;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 项目变更实体类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("blade_project_change")
|
||||
@Schema(description = "项目变更")
|
||||
public class ProjectChange extends TenantEntity {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "原项目ID")
|
||||
private Long projectId;
|
||||
|
||||
@Schema(description = "变更编号")
|
||||
private String changeNo;
|
||||
|
||||
@Schema(description = "变更类型")
|
||||
private String changeType;
|
||||
|
||||
@Schema(description = "变更日期")
|
||||
private LocalDate changeDate;
|
||||
|
||||
@Schema(description = "变更内容")
|
||||
private String changeContent;
|
||||
|
||||
@Schema(description = "变更原因")
|
||||
private String changeReason;
|
||||
|
||||
@Schema(description = "变更前快照JSON")
|
||||
private String beforeDataJson;
|
||||
|
||||
@Schema(description = "项目编号")
|
||||
private String projectCode;
|
||||
|
||||
@Schema(description = "项目名称")
|
||||
private String projectName;
|
||||
|
||||
@Schema(description = "项目简称")
|
||||
private String projectShortName;
|
||||
|
||||
@Schema(description = "项目类型")
|
||||
private String projectType;
|
||||
|
||||
@Schema(description = "业务部门ID")
|
||||
private Long businessDeptId;
|
||||
|
||||
@Schema(description = "业务部门")
|
||||
private String businessDeptName;
|
||||
|
||||
@Schema(description = "承办部门/平台公司ID,多个逗号分隔")
|
||||
private String undertakeDeptId;
|
||||
|
||||
@Schema(description = "承办部门/平台公司,多个顿号分隔")
|
||||
private String undertakeDeptName;
|
||||
|
||||
@Schema(description = "项目由来")
|
||||
private String projectSource;
|
||||
|
||||
@Schema(description = "项目由来说明")
|
||||
private String sourceRemark;
|
||||
|
||||
@Schema(description = "项目资金使用额度(万元)")
|
||||
private BigDecimal fundLimit;
|
||||
|
||||
@Schema(description = "项目应收账款额度(万元)")
|
||||
private BigDecimal receivableLimit;
|
||||
|
||||
@Schema(description = "应收账款回款期限(天)")
|
||||
@TableField(insertStrategy = FieldStrategy.ALWAYS, updateStrategy = FieldStrategy.ALWAYS)
|
||||
private Integer receivableDays;
|
||||
|
||||
@Schema(description = "回款账期(天)")
|
||||
@TableField(insertStrategy = FieldStrategy.ALWAYS, updateStrategy = FieldStrategy.ALWAYS)
|
||||
private Integer paymentDays;
|
||||
|
||||
@Schema(description = "货物类型")
|
||||
private String cargoType;
|
||||
|
||||
@Schema(description = "预估货物数量")
|
||||
@TableField(insertStrategy = FieldStrategy.ALWAYS, updateStrategy = FieldStrategy.ALWAYS)
|
||||
private String cargoQuantity;
|
||||
|
||||
@Schema(description = "业务周期开始日期")
|
||||
private LocalDate businessStartDate;
|
||||
|
||||
@Schema(description = "业务周期结束日期")
|
||||
private LocalDate businessEndDate;
|
||||
|
||||
@Schema(description = "运输线路")
|
||||
private String transportRoute;
|
||||
|
||||
@Schema(description = "运输类型")
|
||||
private String transportType;
|
||||
|
||||
@Schema(description = "业务类型")
|
||||
private String businessType;
|
||||
|
||||
@Schema(description = "业务模式")
|
||||
private String businessMode;
|
||||
|
||||
@Schema(description = "项目规模(万元)")
|
||||
@TableField(insertStrategy = FieldStrategy.ALWAYS, updateStrategy = FieldStrategy.ALWAYS)
|
||||
private BigDecimal projectScale;
|
||||
|
||||
@Schema(description = "预计利润(万元)")
|
||||
@TableField(insertStrategy = FieldStrategy.ALWAYS, updateStrategy = FieldStrategy.ALWAYS)
|
||||
private BigDecimal estimatedProfit;
|
||||
|
||||
@Schema(description = "利润率(%)")
|
||||
@TableField(insertStrategy = FieldStrategy.ALWAYS, updateStrategy = FieldStrategy.ALWAYS)
|
||||
private BigDecimal profitRate;
|
||||
|
||||
@Schema(description = "资金需求(万元)")
|
||||
@TableField(insertStrategy = FieldStrategy.ALWAYS, updateStrategy = FieldStrategy.ALWAYS)
|
||||
private BigDecimal fundDemand;
|
||||
|
||||
@Schema(description = "结算方式")
|
||||
private String settlementMode;
|
||||
|
||||
@Schema(description = "项目经办人ID")
|
||||
private Long handlerUserId;
|
||||
|
||||
@Schema(description = "项目经办人")
|
||||
private String handlerUserName;
|
||||
|
||||
@Schema(description = "项目负责人ID")
|
||||
private Long principalUserId;
|
||||
|
||||
@Schema(description = "项目负责人")
|
||||
private String principalUserName;
|
||||
|
||||
@Schema(description = "客户名称")
|
||||
private String customerNames;
|
||||
|
||||
@Schema(description = "下游承运商")
|
||||
private String carrierNames;
|
||||
|
||||
@Schema(description = "客户信息JSON")
|
||||
private String customerJson;
|
||||
|
||||
@Schema(description = "承运商信息JSON")
|
||||
private String carrierJson;
|
||||
|
||||
@Schema(description = "项目情况说明")
|
||||
private String situationRemark;
|
||||
|
||||
@Schema(description = "项目附件JSON")
|
||||
private String attachmentsJson;
|
||||
|
||||
@Schema(description = "审批状态")
|
||||
private String approvalStatus;
|
||||
|
||||
@Schema(description = "当前节点")
|
||||
private String currentNode;
|
||||
|
||||
@Schema(description = "当前处理人")
|
||||
private String currentProcessor;
|
||||
|
||||
@Schema(description = "审核通过时间")
|
||||
private LocalDateTime approvedTime;
|
||||
|
||||
@Schema(description = "同步状态")
|
||||
private String syncStatus;
|
||||
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.transport.pojo.vo;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.springblade.transport.pojo.entity.ProjectChange;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 项目变更视图实体类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "项目变更")
|
||||
public class ProjectChangeVO extends ProjectChange {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableField(exist = false)
|
||||
@Schema(description = "是否查看全部组织")
|
||||
private Integer allDept;
|
||||
|
||||
@TableField(exist = false)
|
||||
@Schema(description = "是否只读")
|
||||
private Boolean readonly;
|
||||
|
||||
@TableField(exist = false)
|
||||
@Schema(description = "创建人姓名")
|
||||
private String createUserName;
|
||||
|
||||
@TableField(exist = false)
|
||||
@Schema(description = "更新人姓名")
|
||||
private String updateUserName;
|
||||
|
||||
@TableField(exist = false)
|
||||
@Schema(description = "审批状态名称")
|
||||
private String approvalStatusName;
|
||||
|
||||
@TableField(exist = false)
|
||||
@Schema(description = "同步状态名称")
|
||||
private String syncStatusName;
|
||||
|
||||
@TableField(exist = false)
|
||||
@Schema(description = "货物类型字典,公开页回显使用")
|
||||
private List<Map<String, String>> cargoTypeOptions;
|
||||
|
||||
@TableField(exist = false)
|
||||
@Schema(description = "运输类型字典,公开页回显使用")
|
||||
private List<Map<String, String>> transportTypeOptions;
|
||||
|
||||
@TableField(exist = false)
|
||||
@Schema(description = "结算方式字典,公开页回显使用")
|
||||
private List<Map<String, String>> settlementModeOptions;
|
||||
|
||||
}
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.transport.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.boot.ctrl.BladeController;
|
||||
import org.springblade.core.mp.support.Condition;
|
||||
import org.springblade.core.mp.support.Query;
|
||||
import org.springblade.core.secure.annotation.PreAuth;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.transport.pojo.entity.ProjectChange;
|
||||
import org.springblade.transport.pojo.vo.ProjectChangeVO;
|
||||
import org.springblade.transport.service.IProjectChangeService;
|
||||
import org.springblade.transport.support.hcs.HcsImmediatePushSupport;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 项目变更 控制器
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@RestController
|
||||
@AllArgsConstructor
|
||||
@PreAuth(menu = "project_change")
|
||||
@RequestMapping("/project-change")
|
||||
@Tag(name = "项目变更", description = "项目变更")
|
||||
public class ProjectChangeController extends BladeController {
|
||||
|
||||
private final IProjectChangeService projectChangeService;
|
||||
private final HcsImmediatePushSupport hcsImmediatePushSupport;
|
||||
|
||||
@GetMapping("/detail")
|
||||
@ApiOperationSupport(order = 1)
|
||||
@Operation(summary = "详情", description = "传入id")
|
||||
public R<ProjectChangeVO> detail(@Parameter(description = "主键", required = true) @RequestParam Long id) {
|
||||
return R.data(projectChangeService.detail(id));
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
@ApiOperationSupport(order = 2)
|
||||
@Operation(summary = "分页", description = "传入projectChange")
|
||||
public R<IPage<ProjectChangeVO>> list(ProjectChangeVO projectChange, Query query) {
|
||||
return R.data(projectChangeService.selectProjectChangePage(Condition.getPage(query), projectChange));
|
||||
}
|
||||
|
||||
@PostMapping("/create-from-project")
|
||||
@ApiOperationSupport(order = 3)
|
||||
@Operation(summary = "基于项目创建变更草稿", description = "传入projectId、changeType")
|
||||
public R<ProjectChangeVO> createFromProject(@Parameter(description = "项目ID", required = true) @RequestParam Long projectId,
|
||||
@RequestParam(required = false) String changeType) {
|
||||
return R.data(projectChangeService.createFromProject(projectId, changeType));
|
||||
}
|
||||
|
||||
@PostMapping("/save-draft")
|
||||
@ApiOperationSupport(order = 4)
|
||||
@Operation(summary = "保存草稿", description = "传入projectChange")
|
||||
public R saveDraft(@RequestBody ProjectChange projectChange) {
|
||||
return R.status(projectChangeService.saveDraft(projectChange));
|
||||
}
|
||||
|
||||
@PostMapping("/submit")
|
||||
@ApiOperationSupport(order = 5)
|
||||
@Operation(summary = "保存并提交审批", description = "传入projectChange")
|
||||
public R submit(@RequestBody ProjectChange projectChange) {
|
||||
return projectChangeService.submit(projectChange) ? R.data(projectChange) : R.fail("提交失败");
|
||||
}
|
||||
|
||||
@PostMapping("/submit-approval")
|
||||
@ApiOperationSupport(order = 6)
|
||||
@Operation(summary = "提交审批", description = "传入id")
|
||||
public R submitApproval(@Parameter(description = "主键", required = true) @RequestParam Long id) {
|
||||
return R.status(projectChangeService.submitApproval(id));
|
||||
}
|
||||
|
||||
@PostMapping("/approve")
|
||||
@ApiOperationSupport(order = 7)
|
||||
@Operation(summary = "审批通过", description = "传入id,通过后回写原项目并推送合规")
|
||||
public R approve(@Parameter(description = "主键", required = true) @RequestParam Long id) {
|
||||
Long projectId = projectChangeService.approve(id);
|
||||
hcsImmediatePushSupport.pushProjectAndWait(projectId);
|
||||
String syncFailure = HcsImmediatePushSupport.pollSyncFailure();
|
||||
if (syncFailure != null) {
|
||||
ProjectChange projectChange = projectChangeService.getById(id);
|
||||
if (projectChange != null) {
|
||||
projectChange.setSyncStatus("failed");
|
||||
projectChangeService.updateById(projectChange);
|
||||
}
|
||||
return R.data(Boolean.FALSE, "审批通过,但合规推送失败:" + syncFailure);
|
||||
}
|
||||
ProjectChange projectChange = projectChangeService.getById(id);
|
||||
if (projectChange != null) {
|
||||
projectChange.setSyncStatus("synced");
|
||||
projectChangeService.updateById(projectChange);
|
||||
}
|
||||
return R.success("审批通过");
|
||||
}
|
||||
|
||||
@PostMapping("/reject")
|
||||
@ApiOperationSupport(order = 8)
|
||||
@Operation(summary = "审批驳回", description = "传入id")
|
||||
public R reject(@Parameter(description = "主键", required = true) @RequestParam Long id) {
|
||||
return R.status(projectChangeService.reject(id));
|
||||
}
|
||||
|
||||
@PostMapping("/withdraw")
|
||||
@ApiOperationSupport(order = 9)
|
||||
@Operation(summary = "撤回审批", description = "传入id")
|
||||
public R withdraw(@Parameter(description = "主键", required = true) @RequestParam Long id) {
|
||||
return R.status(projectChangeService.withdraw(id));
|
||||
}
|
||||
|
||||
@PostMapping("/remove")
|
||||
@ApiOperationSupport(order = 10)
|
||||
@Operation(summary = "逻辑删除", description = "传入ids")
|
||||
public R remove(@Parameter(description = "主键集合", required = true) @RequestParam String ids) {
|
||||
return R.status(projectChangeService.removeDraft(ids));
|
||||
}
|
||||
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.transport.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.springblade.transport.pojo.entity.ProjectChange;
|
||||
|
||||
/**
|
||||
* 项目变更 Mapper 接口
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public interface ProjectChangeMapper extends BaseMapper<ProjectChange> {
|
||||
}
|
||||
+74
@@ -17,6 +17,7 @@ import org.springblade.transport.pojo.entity.InvoiceReceipt;
|
||||
import org.springblade.transport.pojo.entity.PaymentApplication;
|
||||
import org.springblade.transport.pojo.entity.PreSettlement;
|
||||
import org.springblade.transport.pojo.entity.ProjectApply;
|
||||
import org.springblade.transport.pojo.entity.ProjectChange;
|
||||
import org.springblade.transport.service.IBillPaymentService;
|
||||
import org.springblade.transport.service.IContractManageService;
|
||||
import org.springblade.transport.service.ICustomerArchiveService;
|
||||
@@ -27,6 +28,7 @@ import org.springblade.transport.service.IPaymentApplicationService;
|
||||
import org.springblade.transport.service.IPreSettlementService;
|
||||
import org.springblade.transport.constant.HcsSyncConstant;
|
||||
import org.springblade.transport.service.IProjectApplyService;
|
||||
import org.springblade.transport.service.IProjectChangeService;
|
||||
import org.springblade.transport.service.IWaybillService;
|
||||
import org.springblade.transport.support.hcs.HcsImmediatePushSupport;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -150,6 +152,78 @@ public final class MkProcessBizHandlers {
|
||||
}
|
||||
}
|
||||
|
||||
@Component
|
||||
@TenantIgnore
|
||||
@RequiredArgsConstructor
|
||||
public static class ProjectChangeHandler implements IMkProcessBizHandler {
|
||||
private final IProjectChangeService service;
|
||||
private final HcsImmediatePushSupport hcsImmediatePushSupport;
|
||||
|
||||
@Override
|
||||
public String bizType() {
|
||||
return "project-change";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object publicDetail(Long id) {
|
||||
return service.publicDetail(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean syncFromProcessInfo(Long id, Object processInfo) {
|
||||
ProjectChange entity = service.getById(id);
|
||||
if (entity == null) {
|
||||
return false;
|
||||
}
|
||||
String node = MkProcessNodeHelper.currentNode(processInfo);
|
||||
String processor = MkProcessNodeHelper.currentProcessor(processInfo);
|
||||
if (Func.isEmpty(node) && Func.isEmpty(processor)) {
|
||||
return false;
|
||||
}
|
||||
if (Func.isNotEmpty(node)) {
|
||||
entity.setCurrentNode(node);
|
||||
}
|
||||
if (Func.isNotEmpty(processor)) {
|
||||
entity.setCurrentProcessor(processor);
|
||||
}
|
||||
entity.setApprovalStatus("reviewing");
|
||||
return service.updateById(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean approveFromProcess(Long id, String processorName) {
|
||||
ProjectChange entity = service.getById(id);
|
||||
if (entity == null || "approved".equals(entity.getApprovalStatus())) {
|
||||
return entity != null;
|
||||
}
|
||||
Long projectId = service.approve(id);
|
||||
entity = service.getById(id);
|
||||
if (entity != null) {
|
||||
entity.setCurrentProcessor(MkProcessNodeHelper.text(processorName, "系统"));
|
||||
service.updateById(entity);
|
||||
}
|
||||
if (projectId != null) {
|
||||
hcsImmediatePushSupport.pushProjectAfterCommit(projectId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean rejectFromProcess(Long id, String processorName) {
|
||||
ProjectChange entity = service.getById(id);
|
||||
if (entity == null || "rejected".equals(entity.getApprovalStatus())) {
|
||||
return entity != null;
|
||||
}
|
||||
boolean rejected = service.reject(id);
|
||||
entity = service.getById(id);
|
||||
if (entity != null) {
|
||||
entity.setCurrentProcessor(MkProcessNodeHelper.text(processorName, "系统"));
|
||||
service.updateById(entity);
|
||||
}
|
||||
return rejected;
|
||||
}
|
||||
}
|
||||
|
||||
@Component
|
||||
@TenantIgnore
|
||||
@RequiredArgsConstructor
|
||||
|
||||
+10
@@ -26,6 +26,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import org.springblade.core.mp.base.BaseService;
|
||||
import org.springblade.transport.excel.ProjectApplyExcel;
|
||||
import org.springblade.transport.pojo.entity.ProjectApply;
|
||||
import org.springblade.transport.pojo.entity.ProjectChange;
|
||||
import org.springblade.transport.pojo.vo.ProjectApplyVO;
|
||||
|
||||
import java.util.List;
|
||||
@@ -60,6 +61,15 @@ public interface IProjectApplyService extends BaseService<ProjectApply> {
|
||||
boolean saveChange(ProjectApplyVO projectApply);
|
||||
boolean submitChange(ProjectApplyVO projectApply);
|
||||
boolean startChange(Long id, String changeContent, String changeReason);
|
||||
|
||||
/**
|
||||
* 将独立项目变更单审批通过后的内容回写到原项目,并追加变更记录。
|
||||
*
|
||||
* @param change 已审批通过的项目变更单
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean applyChangeFromRecord(ProjectChange change);
|
||||
|
||||
boolean removeDraft(String ids);
|
||||
List<ProjectApplyExcel> exportProjectApply(ProjectApplyVO projectApply, String ids);
|
||||
|
||||
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.transport.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import org.springblade.core.mp.base.BaseService;
|
||||
import org.springblade.transport.pojo.entity.ProjectChange;
|
||||
import org.springblade.transport.pojo.vo.ProjectChangeVO;
|
||||
|
||||
/**
|
||||
* 项目变更 服务类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public interface IProjectChangeService extends BaseService<ProjectChange> {
|
||||
|
||||
IPage<ProjectChangeVO> selectProjectChangePage(IPage<ProjectChange> page, ProjectChangeVO projectChange);
|
||||
|
||||
ProjectChangeVO detail(Long id);
|
||||
|
||||
/**
|
||||
* 公开详情(MK 回调/公开页)
|
||||
*/
|
||||
ProjectChangeVO publicDetail(Long id);
|
||||
|
||||
/**
|
||||
* 基于正式项目创建变更草稿
|
||||
*/
|
||||
ProjectChangeVO createFromProject(Long projectId, String changeType);
|
||||
|
||||
boolean saveDraft(ProjectChange projectChange);
|
||||
|
||||
boolean submit(ProjectChange projectChange);
|
||||
|
||||
boolean submitApproval(Long id);
|
||||
|
||||
/**
|
||||
* 审批通过:回写原项目并标记同步
|
||||
*
|
||||
* @return 原项目ID,供 Controller 推送合规
|
||||
*/
|
||||
Long approve(Long id);
|
||||
|
||||
boolean reject(Long id);
|
||||
|
||||
boolean withdraw(Long id);
|
||||
|
||||
boolean removeDraft(String ids);
|
||||
|
||||
}
|
||||
+67
@@ -44,6 +44,7 @@ import org.springblade.transport.mapper.FormalSettlementMapper;
|
||||
import org.springblade.transport.mapper.ReceiptClaimSettlementMapper;
|
||||
import org.springblade.transport.mapper.TemporaryCreditLimitMapper;
|
||||
import org.springblade.transport.pojo.entity.ProjectApply;
|
||||
import org.springblade.transport.pojo.entity.ProjectChange;
|
||||
import org.springblade.transport.pojo.entity.FormalSettlement;
|
||||
import org.springblade.transport.pojo.entity.ReceiptClaimSettlement;
|
||||
import org.springblade.transport.pojo.entity.TemporaryCreditLimit;
|
||||
@@ -307,6 +308,72 @@ public class ProjectApplyServiceImpl extends BaseServiceImpl<ProjectApplyMapper,
|
||||
return updateById(projectApply);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean applyChangeFromRecord(ProjectChange change) {
|
||||
if (change == null || Func.isEmpty(change.getProjectId())) {
|
||||
throw new ServiceException("项目变更单无效");
|
||||
}
|
||||
ProjectApply projectApply = loadExists(change.getProjectId());
|
||||
Map<String, Object> beforeData = buildProjectChangeSnapshot(projectApply);
|
||||
copyChangeFields(projectApply, toProjectApplySnapshot(change));
|
||||
Map<String, Object> afterData = buildProjectChangeSnapshot(projectApply);
|
||||
String changeType = Func.isBlank(change.getChangeType()) ? CHANGE_TYPE_PROJECT : change.getChangeType();
|
||||
appendProjectChangeRecord(projectApply, changeType, change.getChangeReason(),
|
||||
STATUS_CHANGE_APPROVED, "变更审批通过", beforeData, afterData);
|
||||
projectApply.setApprovalStatus(STATUS_CHANGE_APPROVED);
|
||||
projectApply.setChangeContent(TransportBusinessSupport.trimToNull(change.getChangeContent()));
|
||||
projectApply.setChangeReason(TransportBusinessSupport.trimToNull(change.getChangeReason()));
|
||||
projectApply.setEffectiveType(EFFECTIVE_FORMAL);
|
||||
projectApply.setCurrentNode("变更审批通过");
|
||||
String processor = TransportBusinessSupport.trimToNull(AuthUtil.getUserName());
|
||||
projectApply.setCurrentProcessor(processor == null ? "系统" : processor);
|
||||
projectApply.setApprovedTime(LocalDateTime.now());
|
||||
projectApply.setHcsSyncStatus(HcsSyncConstant.HCS_STATUS_UNSYNCED);
|
||||
return updateById(projectApply);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将变更单可回写字段映射为临时 ProjectApply,供 copyChangeFields 复用。
|
||||
*/
|
||||
private ProjectApply toProjectApplySnapshot(ProjectChange change) {
|
||||
ProjectApply snapshot = new ProjectApply();
|
||||
snapshot.setBusinessDeptId(change.getBusinessDeptId());
|
||||
snapshot.setBusinessDeptName(change.getBusinessDeptName());
|
||||
snapshot.setUndertakeDeptId(change.getUndertakeDeptId());
|
||||
snapshot.setUndertakeDeptName(change.getUndertakeDeptName());
|
||||
snapshot.setFundLimit(change.getFundLimit());
|
||||
snapshot.setReceivableLimit(change.getReceivableLimit());
|
||||
snapshot.setReceivableDays(change.getReceivableDays());
|
||||
snapshot.setPaymentDays(change.getPaymentDays());
|
||||
snapshot.setCargoType(change.getCargoType());
|
||||
snapshot.setCargoQuantity(change.getCargoQuantity());
|
||||
snapshot.setBusinessStartDate(change.getBusinessStartDate());
|
||||
snapshot.setBusinessEndDate(change.getBusinessEndDate());
|
||||
snapshot.setTransportRoute(change.getTransportRoute());
|
||||
snapshot.setTransportType(change.getTransportType());
|
||||
snapshot.setBusinessType(change.getBusinessType());
|
||||
snapshot.setBusinessMode(change.getBusinessMode());
|
||||
snapshot.setProjectScale(change.getProjectScale());
|
||||
snapshot.setSettlementMode(change.getSettlementMode());
|
||||
snapshot.setEstimatedProfit(change.getEstimatedProfit());
|
||||
snapshot.setProfitRate(change.getProfitRate());
|
||||
snapshot.setFundDemand(change.getFundDemand());
|
||||
snapshot.setHandlerUserId(change.getHandlerUserId());
|
||||
snapshot.setHandlerUserName(change.getHandlerUserName());
|
||||
snapshot.setPrincipalUserId(change.getPrincipalUserId());
|
||||
snapshot.setPrincipalUserName(change.getPrincipalUserName());
|
||||
snapshot.setCustomerNames(change.getCustomerNames());
|
||||
snapshot.setCarrierNames(change.getCarrierNames());
|
||||
snapshot.setCustomerJson(change.getCustomerJson());
|
||||
snapshot.setCarrierJson(change.getCarrierJson());
|
||||
snapshot.setSituationRemark(change.getSituationRemark());
|
||||
snapshot.setAttachmentsJson(change.getAttachmentsJson());
|
||||
snapshot.setChangeContent(change.getChangeContent());
|
||||
snapshot.setChangeReason(change.getChangeReason());
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean removeDraft(String ids) {
|
||||
|
||||
+517
@@ -0,0 +1,517 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.transport.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.log.exception.ServiceException;
|
||||
import org.springblade.core.mp.base.BaseServiceImpl;
|
||||
import org.springblade.core.secure.utils.AuthUtil;
|
||||
import org.springblade.core.tenant.annotation.TenantIgnore;
|
||||
import org.springblade.core.tool.jackson.JsonUtil;
|
||||
import org.springblade.core.tool.utils.BeanUtil;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.transport.constant.HcsSyncConstant;
|
||||
import org.springblade.transport.mapper.ProjectChangeMapper;
|
||||
import org.springblade.transport.pojo.entity.ProjectApply;
|
||||
import org.springblade.transport.pojo.entity.ProjectChange;
|
||||
import org.springblade.transport.pojo.vo.ProjectApplyVO;
|
||||
import org.springblade.transport.pojo.vo.ProjectChangeVO;
|
||||
import org.springblade.transport.service.IProjectApplyService;
|
||||
import org.springblade.transport.service.IProjectChangeService;
|
||||
import org.springblade.transport.support.TransportBusinessSupport;
|
||||
import org.springblade.transport.wrapper.ProjectChangeWrapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 项目变更 服务实现类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ProjectChangeServiceImpl extends BaseServiceImpl<ProjectChangeMapper, ProjectChange> implements IProjectChangeService {
|
||||
|
||||
private static final String STATUS_DRAFT = "draft";
|
||||
private static final String STATUS_REVIEWING = "reviewing";
|
||||
private static final String STATUS_WITHDRAWN = "withdrawn";
|
||||
private static final String STATUS_APPROVED = "approved";
|
||||
private static final String STATUS_REJECTED = "rejected";
|
||||
private static final String CHANGE_TYPE_PROJECT = "项目变更";
|
||||
private static final String CHANGE_TYPE_RECORD = "项目备案调整";
|
||||
private static final DateTimeFormatter CODE_DATE = DateTimeFormatter.ofPattern("yyyyMMdd");
|
||||
|
||||
private final IProjectApplyService projectApplyService;
|
||||
|
||||
@Override
|
||||
public IPage<ProjectChangeVO> selectProjectChangePage(IPage<ProjectChange> page, ProjectChangeVO projectChange) {
|
||||
IPage<ProjectChange> entityPage = page(page, buildQuery(projectChange).orderByDesc(ProjectChange::getCreateTime));
|
||||
IPage<ProjectChangeVO> voPage = ProjectChangeWrapper.build().pageVO(entityPage);
|
||||
voPage.getRecords().forEach(this::fillReadonly);
|
||||
return voPage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProjectChangeVO detail(Long id) {
|
||||
ProjectChangeVO projectChangeVO = ProjectChangeWrapper.build().entityVO(loadExists(id));
|
||||
fillReadonly(projectChangeVO);
|
||||
return projectChangeVO;
|
||||
}
|
||||
|
||||
@Override
|
||||
@TenantIgnore
|
||||
public ProjectChangeVO publicDetail(Long id) {
|
||||
ProjectChangeVO projectChangeVO = ProjectChangeWrapper.build().entityVO(loadExists(id));
|
||||
projectChangeVO.setReadonly(Boolean.TRUE);
|
||||
return projectChangeVO;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ProjectChangeVO createFromProject(Long projectId, String changeType) {
|
||||
ProjectApplyVO projectApply = projectApplyService.detail(projectId);
|
||||
validateProjectChangeable(projectApply);
|
||||
ProjectChange reviewing = findLatestByStatuses(projectId, List.of(STATUS_REVIEWING));
|
||||
if (reviewing != null) {
|
||||
throw new ServiceException("该项目已有审批中的变更单,请先完成后再发起");
|
||||
}
|
||||
ProjectChange unfinished = findLatestByStatuses(projectId, List.of(STATUS_DRAFT, STATUS_WITHDRAWN, STATUS_REJECTED));
|
||||
if (unfinished != null) {
|
||||
// 已有未完结变更单时复用,避免重复建单
|
||||
return detail(unfinished.getId());
|
||||
}
|
||||
String resolvedType = Func.isBlank(changeType) ? CHANGE_TYPE_PROJECT : changeType.trim();
|
||||
if (!List.of(CHANGE_TYPE_PROJECT, CHANGE_TYPE_RECORD).contains(resolvedType)) {
|
||||
throw new ServiceException("请选择变更类型");
|
||||
}
|
||||
ProjectChange projectChange = copyFromProject(projectApply);
|
||||
projectChange.setId(null);
|
||||
projectChange.setProjectId(projectApply.getId());
|
||||
projectChange.setChangeNo(nextChangeNo());
|
||||
projectChange.setChangeType(resolvedType);
|
||||
projectChange.setChangeDate(LocalDate.now());
|
||||
projectChange.setChangeContent(null);
|
||||
projectChange.setChangeReason(null);
|
||||
projectChange.setBeforeDataJson(JsonUtil.toJson(buildSnapshot(projectApply)));
|
||||
projectChange.setApprovalStatus(STATUS_DRAFT);
|
||||
projectChange.setCurrentNode("草稿");
|
||||
projectChange.setCurrentProcessor(AuthUtil.getUserName());
|
||||
projectChange.setApprovedTime(null);
|
||||
projectChange.setSyncStatus(null);
|
||||
save(projectChange);
|
||||
return detail(projectChange.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean saveDraft(ProjectChange projectChange) {
|
||||
ProjectChange oldRecord = loadEditable(projectChange.getId());
|
||||
prepareEditableFields(oldRecord, projectChange);
|
||||
validateDraft(oldRecord);
|
||||
if (Func.isEmpty(oldRecord.getApprovalStatus()) || STATUS_REVIEWING.equals(oldRecord.getApprovalStatus())) {
|
||||
oldRecord.setApprovalStatus(STATUS_DRAFT);
|
||||
}
|
||||
if (List.of(STATUS_WITHDRAWN, STATUS_REJECTED).contains(oldRecord.getApprovalStatus())) {
|
||||
// 驳回/撤回后编辑仍保持原状态,提交时再进入审批中
|
||||
} else {
|
||||
oldRecord.setApprovalStatus(STATUS_DRAFT);
|
||||
oldRecord.setCurrentNode("草稿");
|
||||
}
|
||||
return updateById(oldRecord);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean submit(ProjectChange projectChange) {
|
||||
ProjectChange oldRecord = loadEditable(projectChange.getId());
|
||||
prepareEditableFields(oldRecord, projectChange);
|
||||
validateSubmit(oldRecord);
|
||||
oldRecord.setApprovalStatus(STATUS_REVIEWING);
|
||||
oldRecord.setCurrentNode(resolveApprovalNode(oldRecord));
|
||||
oldRecord.setCurrentProcessor("待处理");
|
||||
oldRecord.setApprovedTime(null);
|
||||
return updateById(oldRecord);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean submitApproval(Long id) {
|
||||
ProjectChange projectChange = loadEditable(id);
|
||||
validateSubmit(projectChange);
|
||||
projectChange.setApprovalStatus(STATUS_REVIEWING);
|
||||
projectChange.setCurrentNode(resolveApprovalNode(projectChange));
|
||||
projectChange.setCurrentProcessor("待处理");
|
||||
projectChange.setApprovedTime(null);
|
||||
return updateById(projectChange);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long approve(Long id) {
|
||||
ProjectChange projectChange = loadReviewing(id);
|
||||
projectChange.setApprovalStatus(STATUS_APPROVED);
|
||||
projectChange.setCurrentNode("审批通过");
|
||||
String processor = TransportBusinessSupport.trimToNull(AuthUtil.getUserName());
|
||||
projectChange.setCurrentProcessor(processor == null ? "系统" : processor);
|
||||
projectChange.setApprovedTime(LocalDateTime.now());
|
||||
projectChange.setSyncStatus(HcsSyncConstant.HCS_STATUS_UNSYNCED);
|
||||
updateById(projectChange);
|
||||
projectApplyService.applyChangeFromRecord(projectChange);
|
||||
return projectChange.getProjectId();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean reject(Long id) {
|
||||
ProjectChange projectChange = loadReviewing(id);
|
||||
projectChange.setApprovalStatus(STATUS_REJECTED);
|
||||
projectChange.setCurrentNode("审核不通过");
|
||||
String processor = TransportBusinessSupport.trimToNull(AuthUtil.getUserName());
|
||||
projectChange.setCurrentProcessor(processor == null ? "系统" : processor);
|
||||
return updateById(projectChange);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean withdraw(Long id) {
|
||||
ProjectChange projectChange = loadReviewing(id);
|
||||
projectChange.setApprovalStatus(STATUS_WITHDRAWN);
|
||||
projectChange.setCurrentNode("已撤回");
|
||||
projectChange.setCurrentProcessor(AuthUtil.getUserName());
|
||||
return updateById(projectChange);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean removeDraft(String ids) {
|
||||
List<Long> idList = Func.toLongList(ids);
|
||||
if (Func.isEmpty(idList)) {
|
||||
throw new ServiceException("请选择需要删除的数据");
|
||||
}
|
||||
for (ProjectChange projectChange : listByIds(idList)) {
|
||||
if (!List.of(STATUS_DRAFT, STATUS_WITHDRAWN, STATUS_REJECTED).contains(projectChange.getApprovalStatus())) {
|
||||
throw new ServiceException("仅草稿、已撤回、已驳回的项目变更允许删除");
|
||||
}
|
||||
}
|
||||
return deleteLogic(idList);
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<ProjectChange> buildQuery(ProjectChangeVO projectChange) {
|
||||
TransportBusinessSupport.validateAllDept(projectChange.getAllDept(), "项目变更");
|
||||
LambdaQueryWrapper<ProjectChange> queryWrapper = Wrappers.<ProjectChange>lambdaQuery().eq(ProjectChange::getIsDeleted, 0);
|
||||
if (TransportBusinessSupport.isAdministrator()) {
|
||||
if (Objects.equals(projectChange.getAllDept(), 1) && Func.isNotEmpty(projectChange.getUndertakeDeptId())) {
|
||||
applyUndertakeDeptContains(queryWrapper, projectChange.getUndertakeDeptId());
|
||||
}
|
||||
} else if (!Objects.equals(projectChange.getAllDept(), 1)) {
|
||||
Long currentDeptId = TransportBusinessSupport.currentDeptId("项目变更");
|
||||
Long currentUserId = AuthUtil.getUserId();
|
||||
queryWrapper.and(wrapper -> {
|
||||
if (currentDeptId != null) {
|
||||
wrapper.apply("FIND_IN_SET({0}, IFNULL(undertake_dept_id,''))", String.valueOf(currentDeptId));
|
||||
}
|
||||
wrapper.or().eq(ProjectChange::getCreateUser, currentUserId)
|
||||
.or().eq(ProjectChange::getHandlerUserId, currentUserId)
|
||||
.or().eq(ProjectChange::getPrincipalUserId, currentUserId);
|
||||
});
|
||||
} else if (Func.isNotEmpty(projectChange.getUndertakeDeptId())) {
|
||||
applyUndertakeDeptContains(queryWrapper, projectChange.getUndertakeDeptId());
|
||||
}
|
||||
queryWrapper.like(Func.isNotEmpty(projectChange.getProjectCode()), ProjectChange::getProjectCode, projectChange.getProjectCode())
|
||||
.like(Func.isNotEmpty(projectChange.getProjectName()), ProjectChange::getProjectName, projectChange.getProjectName())
|
||||
.like(Func.isNotEmpty(projectChange.getUndertakeDeptName()), ProjectChange::getUndertakeDeptName, projectChange.getUndertakeDeptName())
|
||||
.like(Func.isNotEmpty(projectChange.getPrincipalUserName()), ProjectChange::getPrincipalUserName, projectChange.getPrincipalUserName())
|
||||
.eq(Func.isNotEmpty(projectChange.getProjectType()), ProjectChange::getProjectType, projectChange.getProjectType())
|
||||
.eq(Func.isNotEmpty(projectChange.getApprovalStatus()), ProjectChange::getApprovalStatus, projectChange.getApprovalStatus())
|
||||
.eq(Func.isNotEmpty(projectChange.getChangeType()), ProjectChange::getChangeType, projectChange.getChangeType())
|
||||
.eq(Func.isNotEmpty(projectChange.getProjectId()), ProjectChange::getProjectId, projectChange.getProjectId())
|
||||
.like(Func.isNotEmpty(projectChange.getChangeNo()), ProjectChange::getChangeNo, projectChange.getChangeNo());
|
||||
return queryWrapper;
|
||||
}
|
||||
|
||||
private void applyUndertakeDeptContains(LambdaQueryWrapper<ProjectChange> queryWrapper, String undertakeDeptId) {
|
||||
for (String deptId : undertakeDeptId.split(",")) {
|
||||
if (Func.isNotBlank(deptId)) {
|
||||
queryWrapper.apply("FIND_IN_SET({0}, IFNULL(undertake_dept_id,''))", deptId.trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ProjectChange copyFromProject(ProjectApply projectApply) {
|
||||
ProjectChange projectChange = Objects.requireNonNull(BeanUtil.copyProperties(projectApply, ProjectChange.class));
|
||||
projectChange.setId(null);
|
||||
projectChange.setCreateUser(null);
|
||||
projectChange.setCreateDept(null);
|
||||
projectChange.setCreateTime(null);
|
||||
projectChange.setUpdateUser(null);
|
||||
projectChange.setUpdateTime(null);
|
||||
projectChange.setStatus(1);
|
||||
projectChange.setIsDeleted(0);
|
||||
return projectChange;
|
||||
}
|
||||
|
||||
private void prepareEditableFields(ProjectChange target, ProjectChange source) {
|
||||
target.setChangeType(TransportBusinessSupport.trimToNull(source.getChangeType()));
|
||||
target.setChangeReason(TransportBusinessSupport.trimToNull(source.getChangeReason()));
|
||||
target.setChangeContent(TransportBusinessSupport.trimToNull(source.getChangeContent()));
|
||||
target.setBusinessDeptId(source.getBusinessDeptId());
|
||||
target.setBusinessDeptName(source.getBusinessDeptName());
|
||||
normalizeUndertakeDept(source);
|
||||
target.setUndertakeDeptId(source.getUndertakeDeptId());
|
||||
target.setUndertakeDeptName(source.getUndertakeDeptName());
|
||||
target.setFundLimit(source.getFundLimit());
|
||||
target.setReceivableLimit(source.getReceivableLimit());
|
||||
target.setReceivableDays(source.getReceivableDays());
|
||||
target.setPaymentDays(source.getPaymentDays());
|
||||
target.setCargoType(source.getCargoType());
|
||||
target.setCargoQuantity(source.getCargoQuantity());
|
||||
target.setBusinessStartDate(source.getBusinessStartDate());
|
||||
target.setBusinessEndDate(source.getBusinessEndDate());
|
||||
target.setTransportRoute(source.getTransportRoute());
|
||||
target.setTransportType(source.getTransportType());
|
||||
target.setBusinessType(source.getBusinessType());
|
||||
target.setBusinessMode(source.getBusinessMode());
|
||||
target.setProjectScale(source.getProjectScale());
|
||||
target.setSettlementMode(source.getSettlementMode());
|
||||
target.setEstimatedProfit(source.getEstimatedProfit());
|
||||
target.setProfitRate(source.getProfitRate());
|
||||
target.setFundDemand(source.getFundDemand());
|
||||
target.setHandlerUserId(source.getHandlerUserId());
|
||||
target.setHandlerUserName(source.getHandlerUserName());
|
||||
target.setPrincipalUserId(source.getPrincipalUserId());
|
||||
target.setPrincipalUserName(source.getPrincipalUserName());
|
||||
target.setCustomerNames(source.getCustomerNames());
|
||||
target.setCarrierNames(source.getCarrierNames());
|
||||
target.setCustomerJson(source.getCustomerJson());
|
||||
target.setCarrierJson(source.getCarrierJson());
|
||||
target.setSituationRemark(source.getSituationRemark());
|
||||
target.setAttachmentsJson(source.getAttachmentsJson());
|
||||
if (source.getReceivableDays() != null && source.getReceivableDays() == -1) {
|
||||
target.setReceivableDays(null);
|
||||
}
|
||||
if (source.getPaymentDays() != null && source.getPaymentDays() == -1) {
|
||||
target.setPaymentDays(null);
|
||||
}
|
||||
if (source.getCargoQuantity() != null && "-1".equals(source.getCargoQuantity().trim())) {
|
||||
target.setCargoQuantity(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void normalizeUndertakeDept(ProjectChange projectChange) {
|
||||
if (Func.isEmpty(projectChange.getUndertakeDeptId())) {
|
||||
return;
|
||||
}
|
||||
String[] ids = projectChange.getUndertakeDeptId().split(",");
|
||||
StringBuilder idBuilder = new StringBuilder();
|
||||
for (String id : ids) {
|
||||
if (Func.isBlank(id)) {
|
||||
continue;
|
||||
}
|
||||
if (!idBuilder.isEmpty()) {
|
||||
idBuilder.append(",");
|
||||
}
|
||||
idBuilder.append(id.trim());
|
||||
}
|
||||
projectChange.setUndertakeDeptId(idBuilder.toString());
|
||||
}
|
||||
|
||||
private Map<String, Object> buildSnapshot(ProjectApply projectApply) {
|
||||
Map<String, Object> data = new LinkedHashMap<>();
|
||||
data.put("projectType", projectApply.getProjectType());
|
||||
data.put("projectName", projectApply.getProjectName());
|
||||
data.put("projectShortName", projectApply.getProjectShortName());
|
||||
data.put("businessDeptName", projectApply.getBusinessDeptName());
|
||||
data.put("undertakeDeptName", projectApply.getUndertakeDeptName());
|
||||
data.put("fundLimit", projectApply.getFundLimit());
|
||||
data.put("receivableLimit", projectApply.getReceivableLimit());
|
||||
data.put("receivableDays", projectApply.getReceivableDays());
|
||||
data.put("paymentDays", projectApply.getPaymentDays());
|
||||
data.put("cargoType", projectApply.getCargoType());
|
||||
data.put("cargoQuantity", projectApply.getCargoQuantity());
|
||||
data.put("businessStartDate", projectApply.getBusinessStartDate());
|
||||
data.put("businessEndDate", projectApply.getBusinessEndDate());
|
||||
data.put("transportRoute", projectApply.getTransportRoute());
|
||||
data.put("transportType", projectApply.getTransportType());
|
||||
data.put("businessType", projectApply.getBusinessType());
|
||||
data.put("businessMode", projectApply.getBusinessMode());
|
||||
data.put("projectScale", projectApply.getProjectScale());
|
||||
data.put("settlementMode", projectApply.getSettlementMode());
|
||||
data.put("estimatedProfit", projectApply.getEstimatedProfit());
|
||||
data.put("profitRate", projectApply.getProfitRate());
|
||||
data.put("fundDemand", projectApply.getFundDemand());
|
||||
data.put("handlerUserName", projectApply.getHandlerUserName());
|
||||
data.put("principalUserName", projectApply.getPrincipalUserName());
|
||||
data.put("customerNames", projectApply.getCustomerNames());
|
||||
data.put("carrierNames", projectApply.getCarrierNames());
|
||||
return data;
|
||||
}
|
||||
|
||||
private void validateProjectChangeable(ProjectApply projectApply) {
|
||||
boolean approved = List.of("approved", "change_approved").contains(projectApply.getApprovalStatus());
|
||||
if (!approved || !Objects.equals(projectApply.getEffectiveType(), "formal")) {
|
||||
throw new ServiceException("仅正式生效且审批通过的项目允许发起变更");
|
||||
}
|
||||
if ("change_reviewing".equals(projectApply.getApprovalStatus())) {
|
||||
throw new ServiceException("项目存在进行中的变更审批,请先完成后再发起");
|
||||
}
|
||||
}
|
||||
|
||||
private ProjectChange findLatestByStatuses(Long projectId, List<String> statuses) {
|
||||
return getOne(Wrappers.<ProjectChange>lambdaQuery()
|
||||
.eq(ProjectChange::getIsDeleted, 0)
|
||||
.eq(ProjectChange::getProjectId, projectId)
|
||||
.in(ProjectChange::getApprovalStatus, statuses)
|
||||
.orderByDesc(ProjectChange::getCreateTime)
|
||||
.last("limit 1"), false);
|
||||
}
|
||||
|
||||
private void validateDraft(ProjectChange projectChange) {
|
||||
TransportBusinessSupport.validateRequired(projectChange.getChangeType(), "请选择变更类型");
|
||||
if (!List.of(CHANGE_TYPE_PROJECT, CHANGE_TYPE_RECORD).contains(projectChange.getChangeType())) {
|
||||
throw new ServiceException("请选择变更类型");
|
||||
}
|
||||
TransportBusinessSupport.validateLength(projectChange.getChangeReason(), 2000, "变更原因不能超过2000个字");
|
||||
TransportBusinessSupport.validateLength(projectChange.getChangeContent(), 2000, "变更内容不能超过2000个字");
|
||||
}
|
||||
|
||||
private void validateSubmit(ProjectChange projectChange) {
|
||||
validateDraft(projectChange);
|
||||
TransportBusinessSupport.validateRequired(projectChange.getUndertakeDeptName(), "请选择承办部门");
|
||||
TransportBusinessSupport.validateRequired(projectChange.getBusinessMode(), "请选择业务模式");
|
||||
TransportBusinessSupport.validateRequired(projectChange.getCustomerNames(), "请选择客户");
|
||||
TransportBusinessSupport.validateRequired(projectChange.getCarrierNames(), "请选择下游承运商");
|
||||
validateAmount(projectChange.getFundLimit(), "项目资金使用额度", false);
|
||||
validateAmount(projectChange.getReceivableLimit(), "项目应收账款额度", false);
|
||||
validateDays(projectChange.getReceivableDays(), "应收账款回款期限");
|
||||
validateDays(projectChange.getPaymentDays(), "回款账期");
|
||||
validateAmount(projectChange.getProjectScale(), "项目规模", true);
|
||||
validateAmount(projectChange.getEstimatedProfit(), "预计利润", true);
|
||||
validateAmount(projectChange.getProfitRate(), "利润率", true);
|
||||
validateAmount(projectChange.getFundDemand(), "资金需求", true);
|
||||
TransportBusinessSupport.validateDateRange(projectChange.getBusinessStartDate(), projectChange.getBusinessEndDate(), "业务周期开始日期不能晚于结束日期");
|
||||
if (Func.isEmpty(projectChange.getHandlerUserId()) || Func.isEmpty(projectChange.getPrincipalUserId())) {
|
||||
throw new ServiceException("项目经办人和项目负责人不能为空");
|
||||
}
|
||||
TransportBusinessSupport.validateRequired(projectChange.getChangeReason(), "请输入变更原因");
|
||||
}
|
||||
|
||||
private void validateAmount(BigDecimal value, String label, boolean nullable) {
|
||||
if (!nullable && value == null) {
|
||||
throw new ServiceException(label + "不能为空");
|
||||
}
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
if (value.compareTo(BigDecimal.ZERO) < 0) {
|
||||
throw new ServiceException(label + "不能小于0");
|
||||
}
|
||||
if (value.scale() > 2) {
|
||||
throw new ServiceException(label + "最多保留2位小数");
|
||||
}
|
||||
}
|
||||
|
||||
private void validateDays(Integer value, String label) {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
if (value < 0 || value > 999) {
|
||||
throw new ServiceException(label + "范围为0到999天");
|
||||
}
|
||||
}
|
||||
|
||||
private String resolveApprovalNode(ProjectChange projectChange) {
|
||||
return Func.isBlank(projectChange.getChangeType()) ? "项目变更审批" : projectChange.getChangeType() + "审批";
|
||||
}
|
||||
|
||||
private synchronized String nextChangeNo() {
|
||||
return "LXBG" + LocalDate.now().format(CODE_DATE) + String.format("%06d", count() + 1);
|
||||
}
|
||||
|
||||
private ProjectChange loadExists(Long id) {
|
||||
if (Func.isEmpty(id)) {
|
||||
throw new ServiceException("主键不能为空");
|
||||
}
|
||||
ProjectChange projectChange = getById(id);
|
||||
if (Func.isEmpty(projectChange) || Objects.equals(projectChange.getIsDeleted(), 1)) {
|
||||
throw new ServiceException("项目变更不存在");
|
||||
}
|
||||
return projectChange;
|
||||
}
|
||||
|
||||
private ProjectChange loadEditable(Long id) {
|
||||
ProjectChange projectChange = loadExists(id);
|
||||
if (!canCurrentUserOperate(projectChange)) {
|
||||
throw new ServiceException("无权操作其他组织项目变更");
|
||||
}
|
||||
if (!List.of(STATUS_DRAFT, STATUS_WITHDRAWN, STATUS_REJECTED).contains(projectChange.getApprovalStatus())) {
|
||||
throw new ServiceException("当前状态不允许编辑");
|
||||
}
|
||||
return projectChange;
|
||||
}
|
||||
|
||||
private ProjectChange loadReviewing(Long id) {
|
||||
ProjectChange projectChange = loadExists(id);
|
||||
if (!Objects.equals(projectChange.getApprovalStatus(), STATUS_REVIEWING)) {
|
||||
throw new ServiceException("仅审批中的项目变更允许该操作");
|
||||
}
|
||||
return projectChange;
|
||||
}
|
||||
|
||||
private boolean canCurrentUserOperate(ProjectChange projectChange) {
|
||||
if (TransportBusinessSupport.isAdministrator()) {
|
||||
return true;
|
||||
}
|
||||
Long currentUserId = AuthUtil.getUserId();
|
||||
if (Objects.equals(projectChange.getCreateUser(), currentUserId)
|
||||
|| Objects.equals(projectChange.getHandlerUserId(), currentUserId)
|
||||
|| Objects.equals(projectChange.getPrincipalUserId(), currentUserId)) {
|
||||
return true;
|
||||
}
|
||||
Long currentDeptId = TransportBusinessSupport.currentDeptId("项目变更");
|
||||
if (currentDeptId == null || Func.isBlank(projectChange.getUndertakeDeptId())) {
|
||||
return false;
|
||||
}
|
||||
return List.of(projectChange.getUndertakeDeptId().split(",")).stream()
|
||||
.map(String::trim)
|
||||
.anyMatch(deptId -> Objects.equals(deptId, String.valueOf(currentDeptId)));
|
||||
}
|
||||
|
||||
private void fillReadonly(ProjectChangeVO projectChangeVO) {
|
||||
projectChangeVO.setReadonly(!canCurrentUserOperate(projectChangeVO));
|
||||
}
|
||||
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.transport.wrapper;
|
||||
|
||||
import org.springblade.core.mp.support.BaseEntityWrapper;
|
||||
import org.springblade.core.tool.utils.BeanUtil;
|
||||
import org.springblade.system.cache.UserCache;
|
||||
import org.springblade.transport.pojo.entity.ProjectChange;
|
||||
import org.springblade.transport.pojo.vo.ProjectChangeVO;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 项目变更包装类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public class ProjectChangeWrapper extends BaseEntityWrapper<ProjectChange, ProjectChangeVO> {
|
||||
|
||||
public static ProjectChangeWrapper build() {
|
||||
return new ProjectChangeWrapper();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProjectChangeVO entityVO(ProjectChange projectChange) {
|
||||
ProjectChangeVO projectChangeVO = Objects.requireNonNull(BeanUtil.copyProperties(projectChange, ProjectChangeVO.class));
|
||||
projectChangeVO.setCreateUserName(UserCache.getUserRealName(projectChange.getCreateUser()));
|
||||
projectChangeVO.setUpdateUserName(UserCache.getUserRealName(projectChange.getUpdateUser()));
|
||||
projectChangeVO.setApprovalStatusName(statusName(projectChange.getApprovalStatus()));
|
||||
projectChangeVO.setSyncStatusName(syncStatusName(projectChange.getSyncStatus()));
|
||||
return projectChangeVO;
|
||||
}
|
||||
|
||||
private String statusName(String status) {
|
||||
if (status == null) {
|
||||
return "未知";
|
||||
}
|
||||
return switch (status) {
|
||||
case "draft" -> "草稿";
|
||||
case "reviewing" -> "审批中";
|
||||
case "withdrawn" -> "已撤回";
|
||||
case "approved" -> "审批通过";
|
||||
case "rejected" -> "已驳回";
|
||||
default -> "未知";
|
||||
};
|
||||
}
|
||||
|
||||
private String syncStatusName(String syncStatus) {
|
||||
if (syncStatus == null || syncStatus.isBlank()) {
|
||||
return "";
|
||||
}
|
||||
return switch (syncStatus) {
|
||||
case "unsynced" -> "待推送";
|
||||
case "synced" -> "已推送";
|
||||
case "failed" -> "推送失败";
|
||||
default -> syncStatus;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
-- 项目变更独立单据 + 菜单
|
||||
-- 审批通过后再回写 blade_project_apply
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `blade_project_change` (
|
||||
`id` bigint(20) NOT NULL COMMENT '主键',
|
||||
`tenant_id` varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '000000' COMMENT '租户ID',
|
||||
`create_user` bigint(20) DEFAULT NULL COMMENT '创建人',
|
||||
`create_dept` bigint(20) DEFAULT NULL COMMENT '创建部门',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_user` bigint(20) DEFAULT NULL COMMENT '修改人',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '修改时间',
|
||||
`status` int(11) DEFAULT '1' COMMENT '状态',
|
||||
`is_deleted` int(11) DEFAULT '0' COMMENT '是否已删除',
|
||||
`project_id` bigint(20) NOT NULL COMMENT '原项目ID',
|
||||
`change_no` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '变更编号',
|
||||
`change_type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '变更类型',
|
||||
`change_date` date DEFAULT NULL COMMENT '变更日期',
|
||||
`change_content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '变更内容',
|
||||
`change_reason` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '变更原因',
|
||||
`before_data_json` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '变更前快照JSON',
|
||||
`project_code` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '项目编号',
|
||||
`project_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '项目名称',
|
||||
`project_short_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '项目简称',
|
||||
`project_type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '项目类型',
|
||||
`business_dept_id` bigint(20) DEFAULT NULL COMMENT '业务部门ID',
|
||||
`business_dept_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '业务部门',
|
||||
`undertake_dept_id` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '承办部门/平台公司ID,多个逗号分隔',
|
||||
`undertake_dept_name` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '承办部门/平台公司,多个顿号分隔',
|
||||
`project_source` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '项目由来',
|
||||
`source_remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '项目由来说明',
|
||||
`fund_limit` decimal(18,2) DEFAULT NULL COMMENT '项目资金使用额度(万元)',
|
||||
`receivable_limit` decimal(18,2) DEFAULT NULL COMMENT '项目应收账款额度(万元)',
|
||||
`receivable_days` int(11) DEFAULT NULL COMMENT '应收账款回款期限(天)',
|
||||
`payment_days` int(11) DEFAULT NULL COMMENT '回款账期(天)',
|
||||
`cargo_type` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '货物类型',
|
||||
`cargo_quantity` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '预估货物数量',
|
||||
`business_start_date` date DEFAULT NULL COMMENT '业务周期开始日期',
|
||||
`business_end_date` date DEFAULT NULL COMMENT '业务周期结束日期',
|
||||
`transport_route` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '运输线路',
|
||||
`transport_type` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '运输类型',
|
||||
`business_type` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '业务类型',
|
||||
`business_mode` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '业务模式',
|
||||
`project_scale` decimal(18,2) DEFAULT NULL COMMENT '项目规模(万元)',
|
||||
`estimated_profit` decimal(18,2) DEFAULT NULL COMMENT '预计利润(万元)',
|
||||
`profit_rate` decimal(18,2) DEFAULT NULL COMMENT '利润率(%)',
|
||||
`fund_demand` decimal(18,2) DEFAULT NULL COMMENT '资金需求(万元)',
|
||||
`settlement_mode` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '结算方式',
|
||||
`handler_user_id` bigint(20) DEFAULT NULL COMMENT '项目经办人ID',
|
||||
`handler_user_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '项目经办人',
|
||||
`principal_user_id` bigint(20) DEFAULT NULL COMMENT '项目负责人ID',
|
||||
`principal_user_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '项目负责人',
|
||||
`customer_names` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '客户名称',
|
||||
`carrier_names` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '下游承运商',
|
||||
`customer_json` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '客户信息JSON',
|
||||
`carrier_json` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '承运商信息JSON',
|
||||
`situation_remark` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '项目情况说明',
|
||||
`attachments_json` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '项目附件JSON',
|
||||
`approval_status` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '审批状态',
|
||||
`current_node` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '当前节点',
|
||||
`current_processor` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '当前处理人',
|
||||
`approved_time` datetime DEFAULT NULL COMMENT '审核通过时间',
|
||||
`sync_status` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '同步状态 unsynced/synced/failed',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
KEY `idx_project_change_project` (`project_id`) USING BTREE,
|
||||
KEY `idx_project_change_status` (`approval_status`) USING BTREE,
|
||||
KEY `idx_project_change_type` (`change_type`) USING BTREE,
|
||||
KEY `idx_project_change_create_time` (`create_time`) USING BTREE,
|
||||
KEY `idx_project_change_no` (`change_no`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='项目变更';
|
||||
|
||||
-- 菜单:项目管理下增加「项目变更」
|
||||
INSERT IGNORE INTO `blade_menu` (`id`, `parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `component`, `remark`, `is_deleted`) VALUES
|
||||
(2100000000000000300, 2100000000000000000, 'project_change', '项目变更', 'project_change', '/business/project-change', 'iconfont icon-caidanguanli', 15, 1, 0, 1, NULL, '', 0),
|
||||
(2100000000000000301, 2100000000000000300, 'project_change_view', '查看', 'project_change_view', '', '', 1, 2, 0, 1, NULL, '', 0),
|
||||
(2100000000000000302, 2100000000000000300, 'project_change_add', '新增', 'project_change_add', '', '', 2, 2, 0, 1, NULL, '', 0),
|
||||
(2100000000000000303, 2100000000000000300, 'project_change_edit', '编辑', 'project_change_edit', '', '', 3, 2, 0, 1, NULL, '', 0),
|
||||
(2100000000000000304, 2100000000000000300, 'project_change_delete', '删除', 'project_change_delete', '', '', 4, 2, 0, 1, NULL, '', 0),
|
||||
(2100000000000000305, 2100000000000000300, 'project_change_submit', '提交', 'project_change_submit', '', '', 5, 2, 0, 1, NULL, '', 0),
|
||||
(2100000000000000306, 2100000000000000300, 'project_change_withdraw', '撤回', 'project_change_withdraw', '', '', 6, 2, 0, 1, NULL, '', 0),
|
||||
(2100000000000000307, 2100000000000000300, 'project_change_approve', '审批通过', 'project_change_approve', '', '', 7, 2, 0, 1, NULL, '', 0),
|
||||
(2100000000000000308, 2100000000000000300, 'project_change_reject', '审批驳回', 'project_change_reject', '', '', 8, 2, 0, 1, NULL, '', 0);
|
||||
|
||||
-- 业务字典 mk_template:提交项目变更审核流(dict_key 需按实际 MK 模板编码配置)
|
||||
INSERT IGNORE INTO `blade_dict_biz` (`id`, `tenant_id`, `parent_id`, `code`, `dict_key`, `dict_value`, `sort`, `remark`, `is_sealed`, `status`, `is_deleted`)
|
||||
SELECT 2100000000000000399, '000000', id, 'mk_template', 'PROJECT_CHANGE_TEMPLATE', '提交项目变更审核流', 99, '项目变更MK模板编码,请替换为真实 templateCode', 0, 1, 0
|
||||
FROM `blade_dict_biz`
|
||||
WHERE `code` = 'mk_template' AND `parent_id` = 0 AND `is_deleted` = 0
|
||||
LIMIT 1;
|
||||
Reference in New Issue
Block a user