permissionVisaIds;
+
+ /**
+ * 是否需要主单据(即是否被其他单据关联)信息
+ */
+ private Boolean needRelatedBill = false;
+ /**
+ * 变更签证Id
+ */
+ private Long visaId;
+
+ /**
+ * 调用场景
+ *
+ * PAGE: 分页列表页面调用
+ * SELECT: 选择器页面调用
+ *
+ */
+ private FromEnum from;
+
+ /**
+ * 主单据类型
+ *
+ * 仅当{@code from}为{@link FromEnum#SELECT}且{@code needRelatedBill}为{@code true}时,该字段才有效且不能为空
+ *
+ */
+ private VisaTypeEnum visaType;
+
+ public static enum FromEnum {
+ PAGE("分页列表页面调用"),
+ SELECT("选择器页面调用");
+
+ private final String desc;
+
+ FromEnum(String desc) {
+ this.desc = desc;
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+ }
+
+}
diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaChangeTempCreateReq.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaChangeTempCreateReq.java
new file mode 100644
index 00000000..090c2871
--- /dev/null
+++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaChangeTempCreateReq.java
@@ -0,0 +1,258 @@
+package cn.axzo.nanopart.visa.api.request;
+
+import cn.axzo.maokai.common.enums.SaasCooperateShipCooperateTypeEnum;
+import cn.axzo.nanopart.visa.api.enums.VisaBillTypeEnum;
+import cn.axzo.nanopart.visa.api.enums.VisaTypeEnum;
+import cn.axzo.workflow.common.model.dto.UploadFieldDTO;
+import com.alibaba.fastjson.JSONArray;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.SuperBuilder;
+
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author chenwenjian
+ * @version 1.0
+ * @date 2025/1/16 11:53
+ */
+@Data
+@SuperBuilder
+@NoArgsConstructor
+@AllArgsConstructor
+public class VisaChangeTempCreateReq {
+
+
+ /**
+ * 变更单号Id
+ */
+ private Long id;
+
+ /**
+ * 单号
+ */
+ @NotBlank(message = "请输入单号")
+ @Size(max = 100, message = "单号长度不能超过100个字符")
+ private String no;
+
+ /**
+ * 主题
+ */
+ @NotBlank(message = "请输入主题")
+ @Size(max = 100, message = "主题长度不能超过100个字符")
+ private String topic;
+
+ /**
+ * 项目
+ */
+ @NotNull(message = "关联项目不能为空")
+ @Min(value = 1, message = "关联项目不能为空")
+ private Long relationWorkspaceId;
+
+ /**
+ * 工程
+ */
+ @NotNull(message = "请选择工程")
+ @Min(value = 1, message = "请选择工程")
+ private Long relationProject;
+ /**
+ * 工程名称
+ */
+ @NotBlank(message = "工程名称不能为空")
+ private String relationProjectName;
+
+ /**
+ * 区域集合
+ * 示例:
+ *
+ * [
+ * {"areaId": 249556},
+ * {"areaId": 249555}
+ * ]
+ *
+ */
+ private JSONArray relationArea;
+
+ /**
+ * 提出时间
+ */
+ @NotNull(message = "请选择提出时间")
+ private Date happenTime;
+
+ /**
+ * 专业集合
+ * 示例:
+ *
+ * [
+ * {"code":"code1"},
+ * {"code":"code2"},
+ * ]
+ *
+ */
+// @NotNull(message = "请选择专业")
+ private JSONArray relationProfessional;
+
+ /**
+ * 发生原因
+ */
+ @NotBlank(message = "请填写发生原因")
+ @Size(max = 500, message = "发生原因长度不能超过500个字符")
+ private String reason;
+
+ /**
+ * 单据类型
+ */
+ @NotNull(message = "请选择变更签证类型")
+ private VisaTypeEnum type;
+
+ /**
+ * 相关单位及人员
+ */
+ private List relationUnitAndPersonList;
+
+ /**
+ * 发生内容说明
+ */
+ private List changeContextAndDescriptionList;
+
+ /**
+ * 有符号的变更金额
+ */
+ private BigDecimal amountChange;
+
+ /**
+ * 关联的单据,key: 单据类型,value: 单据id集合
+ *
+ * TASK :任务单 RECTIFY :整改单 DESIGN_VISA:变更单
+ * 只有变更签证类型即{@code type}为“技术核定”或“工程签证”时才可能会有关联变更单
+ *
+ *
+ * @see VisaBillTypeEnum
+ */
+ private Map> relationOrderMap;
+
+ /**
+ * 附件,json列表
+ *
+ * {
+ * "fileUrl": "http://www.baidu.com/img1.jpeg",
+ * "fileKey": "jskjksjgnaqkjkgag",
+ * "fileName": "附件1.jpg"
+ * }
+ *
+ */
+ @Size(max = 30, message = "最多支持上传30个附件,已超出请重新选择")
+ private List attach;
+
+ private Long operatorOuId;
+
+ private Integer operatorOuType;
+
+ private Long operatorNodeId;
+
+ /**
+ * 操作人peronId
+ */
+ private Long operatorPersonId;
+
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ public static class OrderSimpleModel {
+ /**
+ * 单据主键 ID
+ */
+ private Long id;
+
+ /**
+ * 单据编号
+ */
+ private String no;
+
+ /**
+ * 单据名称
+ */
+ private String name;
+ }
+
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ public static class RelationUnitAndPerson {
+ /**
+ * 单位类型 1:施工单位 2:建设单位 3:监理单位 4:劳务分包 5:专业分包 9:项目内班组 13:设计单位
+ *
+ * @see cn.axzo.maokai.common.enums.SaasCooperateShipCooperateTypeEnum
+ */
+ private SaasCooperateShipCooperateTypeEnum type;
+
+ /**
+ * 单位 ID
+ */
+ private Long ouId;
+
+ /**
+ * 确认人所在部门节点id或项目内班组节点id
+ */
+ private Long nodeId;
+
+ /**
+ * 确认人personId
+ */
+ private Long personId;
+
+ }
+
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ public static class ChangeContextAndDescription {
+ /**
+ * 内容类型,1:图片 2:图纸批注
+ */
+ private Integer type;
+
+ /**
+ * 图片或图纸url
+ */
+ private String fileUrl;
+
+ /**
+ * 图片或图纸key
+ */
+ private String fileKey;
+
+ /**
+ * 图片或图纸名称
+ */
+ private String fileName;
+
+ /**
+ * 图纸批注id,{@code type}为2时使用
+ */
+ private Long annotationId;
+
+ /**
+ * 图纸图框id,{@code type}为2时使用
+ */
+ private Long borderId;
+
+ /**
+ * 内容说明
+ */
+ private String text;
+
+ }
+}
diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaDetailByIdRequest.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaDetailByIdRequest.java
new file mode 100644
index 00000000..169f0e0a
--- /dev/null
+++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaDetailByIdRequest.java
@@ -0,0 +1,44 @@
+package cn.axzo.nanopart.visa.api.request;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author xudawei
+ * @date 2025/01/16
+ * @desc 变签Id获取变签日志
+ */
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class VisaDetailByIdRequest {
+
+ /**
+ * 变签Id
+ */
+ @NotNull(message = "变签Id不能为空")
+ private Long visaId;
+
+ /**
+ * 人员Id
+ */
+ private Long personId;
+
+ private Long workspaceId;
+
+ private Long ouId;
+
+ private Long projectId;
+
+ /**
+ * 是否需要btnList
+ * true:需要, 则personId/ouId/workspaceId 必填,需要查询确认人信息
+ * false:不需要
+ */
+ private boolean needBtnList = true;
+}
diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaInitiatorListReq.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaInitiatorListReq.java
new file mode 100644
index 00000000..4a8d1686
--- /dev/null
+++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaInitiatorListReq.java
@@ -0,0 +1,36 @@
+package cn.axzo.nanopart.visa.api.request;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author chenwenjian
+ * @version 1.0
+ * @date 2025/1/22 9:41
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class VisaInitiatorListReq {
+
+ @NotNull(message = "workspaceId不能为空")
+ private Long workspaceId;
+
+ /**
+ * 当前登录人的personId
+ */
+ private Long currentPersonId;
+ /**
+ * 当前登录人的项目Id
+ */
+ private Long currentWorkspaceId;
+ /**
+ * 当前登录人的单位Id
+ */
+ private Long currentOuId;
+}
diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaRelationReq.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaRelationReq.java
new file mode 100644
index 00000000..ea9a5c45
--- /dev/null
+++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaRelationReq.java
@@ -0,0 +1,36 @@
+package cn.axzo.nanopart.visa.api.request;
+
+import cn.axzo.nanopart.visa.api.enums.VisaRelationFieldEnum;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * 变洽签关联数据查询入参模型
+ *
+ * @author wangli
+ * @since 2025-01-16 10:46
+ */
+@Data
+@Accessors(chain = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class VisaRelationReq {
+
+ /**
+ * 变洽签单据 ID
+ */
+ @NotNull(message = "变洽签单据 ID不能为空")
+ private Long visaId;
+
+ /**
+ * 关联表中指定类型
+ * 可为空,则查询所有关联的数据
+ */
+ private VisaRelationFieldEnum fieldEnum;
+}
diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaSearchReq.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaSearchReq.java
new file mode 100644
index 00000000..715aad60
--- /dev/null
+++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/request/VisaSearchReq.java
@@ -0,0 +1,81 @@
+package cn.axzo.nanopart.visa.api.request;
+
+import cn.axzo.core.domain.PageRequest;
+import cn.axzo.framework.domain.ServiceException;
+import cn.axzo.nanopart.visa.api.enums.VisaBillTypeEnum;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.util.StringUtils;
+
+import java.util.Objects;
+
+/**
+ * @author xudawei
+ * @date 2025/01/16
+ * @desc 变签Id获取变签日志
+ */
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class VisaSearchReq extends PageRequest {
+
+
+ /**
+ * 变签Id
+ */
+ private Long visaId;
+ /**
+ * 项目Id
+ */
+ private Long workspaceId;
+
+ /**
+ * 变签编号
+ */
+ private String visaNo;
+
+ /**
+ * 单据类型,任务单:TASK;整改单:RECTIFY;变更单:VISA
+ */
+ private VisaBillTypeEnum billType;
+
+ /**
+ * 单据Id
+ */
+ private Long billId;
+
+ /**
+ * 单据编号
+ */
+ private String billNo;
+
+ /**
+ * 校验
+ */
+ public void check() {
+ if (Objects.isNull(this.getBillId())
+ && Objects.isNull(this.getBillNo())
+ && Objects.isNull(this.getBillType())
+ && Objects.isNull(this.getVisaId())
+ && !StringUtils.hasText(this.getVisaNo())) {
+ throw new ServiceException("参数不合法");
+ }
+ repairPage();
+ }
+
+ /**
+ * 修复page
+ */
+ public void repairPage() {
+ if (Objects.isNull(this.page)) {
+ this.setPage(1);
+ }
+ if (Objects.isNull(this.pageSize)) {
+ this.setPageSize(20);
+ }
+ }
+
+}
diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/FetchVisaLogByVisaIdResponse.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/FetchVisaLogByVisaIdResponse.java
new file mode 100644
index 00000000..5bbcf828
--- /dev/null
+++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/FetchVisaLogByVisaIdResponse.java
@@ -0,0 +1,36 @@
+package cn.axzo.nanopart.visa.api.response;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+/**
+ * @author xudawei
+ * @date 2025/01/16
+ * @desc 变签Id获取变签日志
+ */
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class FetchVisaLogByVisaIdResponse {
+
+ /**
+ * 操作标题
+ */
+ private String title;
+
+ /**
+ * 操作内容
+ */
+ private String content;
+
+ /**
+ * 创建时间
+ */
+ private Date createAt;
+
+}
diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/ImGroupButton.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/ImGroupButton.java
new file mode 100644
index 00000000..dc13b2f8
--- /dev/null
+++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/ImGroupButton.java
@@ -0,0 +1,29 @@
+package cn.axzo.nanopart.visa.api.response;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * IM 群聊中的按钮
+ *
+ * @author wangli
+ * @since 2025-02-05 16:47
+ */
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class ImGroupButton {
+ /**
+ * 按钮文案
+ */
+ private String name;
+
+ /**
+ * 按钮类型
+ */
+ private String type;
+
+}
diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/ImGroupTipsResp.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/ImGroupTipsResp.java
new file mode 100644
index 00000000..3dfb0181
--- /dev/null
+++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/ImGroupTipsResp.java
@@ -0,0 +1,36 @@
+package cn.axzo.nanopart.visa.api.response;
+
+import cn.axzo.nanopart.visa.api.enums.VisaStatusEnum;
+import cn.axzo.nanopart.visa.api.enums.VisaTypeEnum;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+/**
+ * IM 群聊上方横条
+ *
+ * @author wangli
+ * @since 2025-02-05 16:37
+ */
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class ImGroupTipsResp {
+
+ private VisaStatusEnum status;
+
+ private String statusText;
+
+ private String tipsText;
+
+ private List buttonList;
+
+ private VisaTypeEnum visaType;
+
+ private String visaTypeText;
+
+}
diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/VisaChangeDiscussCreateResp.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/VisaChangeDiscussCreateResp.java
new file mode 100644
index 00000000..bb0f3633
--- /dev/null
+++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/VisaChangeDiscussCreateResp.java
@@ -0,0 +1,33 @@
+package cn.axzo.nanopart.visa.api.response;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @author chenwenjian
+ * @version 1.0
+ * @date 2025/2/10 14:17
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class VisaChangeDiscussCreateResp {
+
+ /**
+ * 变洽签单据id
+ */
+ private Long visaId;
+
+ /**
+ * 群聊id
+ */
+ private Long imGroupId;
+
+ /**
+ * 群聊名称
+ */
+ private String imGroupName;
+}
diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/VisaChangeInitiatorResp.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/VisaChangeInitiatorResp.java
new file mode 100644
index 00000000..eafeefc5
--- /dev/null
+++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/VisaChangeInitiatorResp.java
@@ -0,0 +1,64 @@
+package cn.axzo.nanopart.visa.api.response;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @author chenwenjian
+ * @version 1.0
+ * @date 2025/1/16 18:43
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class VisaChangeInitiatorResp {
+
+ /**
+ * 发起人personId
+ */
+ private Long personId;
+
+ /**
+ * 发起人姓名
+ */
+ private String name;
+
+ /**
+ * 发起人头像
+ */
+ private String avatar;
+
+ /**
+ * 岗位id
+ */
+ private Long jobId;
+
+ /**
+ * 岗位名称
+ */
+ private String jobName;
+
+ /**
+ * 岗位编码
+ */
+ private String jobCode;
+
+ /**
+ * 单位id
+ */
+ private Long ouId;
+
+ /**
+ * 所属单位节点id或班组节点id
+ */
+ private Long nodeId;
+
+ /**
+ * 所属单位名称或班组名称
+ */
+ private String unitOrTeamName;
+
+}
diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/VisaChangePageSearchResp.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/VisaChangePageSearchResp.java
new file mode 100644
index 00000000..1ea06939
--- /dev/null
+++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/VisaChangePageSearchResp.java
@@ -0,0 +1,155 @@
+package cn.axzo.nanopart.visa.api.response;
+
+import cn.axzo.nanopart.visa.api.enums.VisaButtonTypeEnum;
+import cn.axzo.nanopart.visa.api.enums.VisaStatusEnum;
+import cn.axzo.nanopart.visa.api.enums.VisaTypeEnum;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * @author chenwenjian
+ * @version 1.0
+ * @date 2025/1/16 16:29
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class VisaChangePageSearchResp {
+
+ private Long id;
+
+ /**
+ * 单号
+ */
+ private String no;
+
+ /**
+ * 主题
+ */
+ private String topic;
+
+ /**
+ * 项目
+ */
+ private Long relationWorkspaceId;
+
+ /**
+ * 工程
+ */
+ private Long relationProject;
+
+ /**
+ * 工程名称
+ */
+ private String relationProjectName;
+
+ /**
+ * 提出时间
+ */
+ private Date happenTime;
+
+
+ /**
+ * 最终审批时间
+ */
+ private Date approvalCompleteTime;
+
+ /**
+ * 单据类型
+ *
+ * DESIGN_CHANGE: 设计变更
+ * TECHNOLOGY_APPROVED: 技术核定
+ * PROJECT_VISA: 工程签证
+ *
+ *
+ * @see VisaTypeEnum
+ */
+ private VisaTypeEnum type;
+
+ /**
+ * 单据类型描述
+ */
+ private String typeDesc;
+
+ /**
+ * 状态
+ *
+ * TO_REPORT: 待提报
+ * DECIDING: 决策中
+ * EXECUTING: 执行中
+ * APPROVING: 审批中
+ * COMPLETED: 已完成
+ * FORBIDED: 已废除
+ *
+ *
+ * @see VisaStatusEnum
+ */
+ private VisaStatusEnum status;
+
+ /**
+ * 状态描述
+ */
+ private String statusDesc;
+
+ /**
+ * 有符号的变更金额
+ */
+ private BigDecimal amountChange;
+
+
+ /**
+ * 发起人
+ */
+ private VisaChangeInitiatorResp initiator;
+
+ /**
+ * 发生原因
+ */
+ private String reason;
+ /**
+ * 是否被其他变更签证单单据关联
+ */
+ private Boolean isRelated;
+
+ /**
+ * 操作按钮
+ *
+ * UPDATE: 编辑
+ * DETAIL: 详情
+ * UPLOAD_FILE: 上传附件
+ * APPROVE_RECORD: 查看审批记录
+ *
+ *
+ * @see VisaButtonTypeEnum
+ */
+ private Set operateBtnSet;
+
+ public String getTypeDesc() {
+ if (Objects.isNull(type)) {
+ return null;
+ }
+ return type.getDesc();
+ }
+
+ public String getStatusDesc() {
+ if (Objects.isNull(status)) {
+ return null;
+ }
+ if (VisaStatusEnum.REPORT_FROM_APPROVE.equals(status)) {
+ return VisaStatusEnum.TO_REPORT.getDesc();
+ }
+ if (VisaStatusEnum.DECIDING_FROM_APPROVE.equals(status)) {
+ return VisaStatusEnum.DECIDING.getDesc();
+ }
+ return status.getDesc();
+ }
+
+}
diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/VisaDetailByIdResponse.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/VisaDetailByIdResponse.java
new file mode 100644
index 00000000..d05a1f43
--- /dev/null
+++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/VisaDetailByIdResponse.java
@@ -0,0 +1,613 @@
+package cn.axzo.nanopart.visa.api.response;
+
+import cn.axzo.maokai.common.enums.SaasCooperateShipCooperateTypeEnum;
+import cn.axzo.nanopart.visa.api.enums.VisaButtonTypeEnum;
+import cn.axzo.nanopart.visa.api.enums.VisaStatusEnum;
+import cn.axzo.nanopart.visa.api.enums.VisaTypeEnum;
+import cn.axzo.nanopart.visa.api.request.VisaChangeTempCreateReq;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * @author xudawei
+ * @date 2025/01/16
+ * @desc 变签Id获取变签日志
+ */
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class VisaDetailByIdResponse {
+
+ /**
+ * 变签Id
+ */
+ private Long visaId;
+
+
+ /**
+ * 单号
+ */
+ private String no;
+
+ /**
+ * 单据类型
+ */
+ private VisaTypeEnum type;
+
+ /**
+ * 发生原因
+ */
+ private String reason;
+
+ /**
+ * 发起人姓名
+ */
+ private String createName;
+
+ /**
+ * 发起人头像
+ */
+ private String createAvatarUrl;
+
+ /**
+ * 发起人岗位名称
+ */
+ private String createJobName;
+
+ /**
+ * 发起人岗位code
+ */
+ private String createJobCode;
+
+ /**
+ * 发起人单位名称
+ */
+ private String createOuName;
+
+ /**
+ * 发起人单位Id
+ */
+ private Long createOuId;
+
+ /**
+ * 发起人Id
+ */
+ private Long createBy;
+
+ /**
+ * 所属工程
+ */
+ private Long relationProject;
+
+ /**
+ * 所属工程名称
+ */
+ private String relationProjectName;
+
+ /**
+ * 主题
+ */
+ private String topic;
+
+ /**
+ * 项目
+ */
+ private Long relationWorkspaceId;
+
+ /**
+ * 项目名称
+ */
+ private String relationWorkspaceName;
+
+ /**
+ * 区域集合
+ */
+ private List relationAreaList;
+
+ /**
+ * 区域集合名称
+ */
+ private String relationAreaName;
+
+ /**
+ * 提出时间
+ */
+ private String happenTime;
+
+
+ /**
+ * 最终审批时间
+ */
+ private String approvalCompleteTime;
+
+ /**
+ * 专业集合
+ */
+ private List relationProfessionalList;
+
+ /**
+ * 专业集合名称
+ */
+ private String relationProfessionalName;
+
+ /**
+ * 相关单位及人员
+ */
+ private List relationOuAndPersonList;
+
+ /**
+ * 发生内容说明
+ */
+ private List changeContextAndDescriptionList;
+
+ /**
+ * 有符号的变更金额
+ */
+ private String amountChange;
+
+ /**
+ * 附件
+ */
+ private List attach;
+
+ /**
+ * 按钮
+ */
+ private List btnList;
+
+
+ /**
+ * 任务单
+ */
+ private List relationTaskList;
+
+ /**
+ * 整改单
+ */
+ private List relationRectifyList;
+
+ /**
+ * 变更单
+ */
+ private List relationVisaList;
+
+ /**
+ * 关联无效的变更单
+ */
+ private List relationInvalidVisaList;
+
+ /**
+ * 状态
+ */
+ private VisaStatusEnum status;
+
+ /**
+ * 群组ID
+ */
+ private Long imGroupId;
+
+ /**
+ * 最终审批ID
+ */
+ private String approvalId;
+
+ /**
+ * 最终审批状态
+ */
+ private String approvalStatus;
+
+ /**
+ * 当前登录人的personId
+ */
+ private Long currentPersonId;
+
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ public static class RelationOuAndPerson {
+ /**
+ * 相关单位
+ */
+ private RelationUnit relationOu;
+
+ /**
+ * 相关确认人
+ */
+ private List relationPersonList;
+ }
+
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ public static class RelationUnit {
+ /**
+ * 类型
+ */
+ private String type;
+
+ /**
+ * 单位id
+ */
+ private Long ouId;
+
+ /**
+ * 单位名称
+ */
+ private String ouName;
+ /**
+ * 节点Id
+ */
+ private Long nodeId;
+ }
+
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ public static class RelationPerson {
+ /**
+ * 类型
+ */
+ private SaasCooperateShipCooperateTypeEnum type;
+
+ /**
+ * 单位id
+ */
+ private Long ouId;
+
+ /**
+ * 确认人名称
+ */
+ private String realName;
+
+ /**
+ * 头像
+ */
+ private String avatarUrl;
+
+ /**
+ * 节点id
+ */
+ private Long nodeId;
+
+ /**
+ * 确认人personId
+ */
+ private Long personId;
+
+ /**
+ * 项目Id
+ */
+ private Long workspaceId;
+
+ /**
+ * 工程Id
+ */
+ private Long projectId;
+
+ /**
+ * 岗位Id
+ */
+ private Long jobId;
+ /**
+ * 岗位名称
+ */
+ private String jobName;
+ }
+
+
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ public static class ChangeContextAndDescription {
+ /**
+ * 内容类型,1:图片 2:图纸批注
+ */
+ private Integer Type;
+
+ /**
+ * 图片或图纸
+ */
+ private String imgUrl;
+
+ /**
+ * 内容说明
+ */
+ private String text;
+
+ }
+
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ public static class RelationProfessional {
+
+ /**
+ * code
+ */
+ private String code;
+ }
+
+
+
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ public static class RelationProfessionalDetail {
+ /**
+ * code
+ */
+ private String code;
+
+ /**
+ * 构建专业集合-对象集合
+ */
+ public static List buildProfessionWithObject(JSONArray relationProfessional) {
+
+ if (CollectionUtils.isEmpty(relationProfessional)) {
+ return Lists.newArrayList();
+ }
+ List professionalList = relationProfessional.toJavaList(VisaDetailByIdResponse.RelationProfessionalDetail.class);
+
+ if (CollectionUtils.isEmpty(professionalList)) {
+ return Lists.newArrayList();
+ }
+
+ List filter = professionalList.stream().filter(item -> Objects.nonNull(item) && StringUtils.isNotBlank(item.getCode())).collect(Collectors.toList());
+ if (CollectionUtils.isEmpty(filter)) {
+ return Lists.newArrayList();
+ }
+ return filter;
+ }
+
+ /**
+ * 构建专业集合-code集合
+ */
+ public static Set buildProfessionWithCode(JSONArray relationProfessional) {
+ List relationProfessionalDetails = RelationProfessionalDetail.buildProfessionWithObject(relationProfessional);
+ if (CollectionUtils.isEmpty(relationProfessionalDetails)) {
+ return Sets.newHashSet();
+ }
+
+ return relationProfessionalDetails.stream()
+ .filter(item -> Objects.nonNull(item) && StringUtils.isNotBlank(item.getCode()))
+ .map(RelationProfessionalDetail::getCode).collect(Collectors.toSet());
+ }
+
+ }
+
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ public static class RelationAreaDetail {
+ /**
+ * id
+ */
+ private Long areaId;
+
+
+ public static List buildAreaWithObject(JSONArray relationArea) {
+ if (CollectionUtils.isEmpty(relationArea)) {
+ return Lists.newArrayList();
+ }
+ List relationAreaList = relationArea.toJavaList(VisaDetailByIdResponse.RelationAreaDetail.class);
+ if (CollectionUtils.isEmpty(relationAreaList)) {
+ return Lists.newArrayList();
+ }
+ List filter = relationAreaList.stream().filter(item -> Objects.nonNull(item) && Objects.nonNull(item.getAreaId())).collect(Collectors.toList());
+ if (CollectionUtils.isEmpty(filter)) {
+ return Lists.newArrayList();
+ }
+ return filter;
+ }
+
+ public static List buildAreaWithId(JSONArray relationArea) {
+ List areaDetailList = RelationAreaDetail.buildAreaWithObject(relationArea);
+
+ if (CollectionUtils.isEmpty(areaDetailList)) {
+ return Lists.newArrayList();
+ }
+ return areaDetailList.stream().map(VisaDetailByIdResponse.RelationAreaDetail::getAreaId).collect(Collectors.toList());
+ }
+
+ }
+
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ public static class RelationRectifyOrder {
+
+ /**
+ * 整改单id
+ */
+ private Long id;
+
+ /**
+ * 整改单号
+ */
+ private String rectifyNo;
+
+ /**
+ * 名称
+ */
+ private String name;
+
+ /**
+ * 开始时间
+ */
+ private String startTime;
+
+ /**
+ * 结束时间
+ */
+ private String endTime;
+
+ /**
+ * 共计天数
+ */
+ private String totalDays;
+
+ /**
+ * 发起人
+ */
+ private String createName;
+ /**
+ * 发起人Id
+ */
+ private Long createBy;
+ /**
+ * 发起人岗位名称
+ */
+ private String createJobName;
+ /**
+ * 发起人岗位Id
+ */
+ private Long createJobId;
+ /**
+ * 发起人单位名称
+ */
+ private String createOuName;
+ /**
+ * 发起人单位Id
+ */
+ private String createOuId;
+
+ /**
+ * 责任人
+ */
+ private String rectifierName;
+ /**
+ * 责任人Id
+ */
+ private Long rectifier;
+ /**
+ * 责任人岗位名称
+ */
+ private String rectifierJobName;
+ /**
+ * 责任人岗位Id
+ */
+ private Long rectifierJobId;
+ /**
+ * 责任人单位名称
+ */
+ private String rectifierOuName;
+ /**
+ * 责任人单位Id
+ */
+ private String rectifierOuId;
+
+ /**
+ * 销项人
+ */
+ private String verifierName;
+ /**
+ * 销项人Id
+ */
+ private Long verifier;
+ /**
+ * 销项人岗位名称
+ */
+ private String verifierJobName;
+ /**
+ * 销项人岗位Id
+ */
+ private Long verifierJobId;
+ /**
+ * 销项人单位名称
+ */
+ private String verifierOuName;
+ /**
+ * 销项人单位Id
+ */
+ private String verifierOuId;
+
+
+ }
+
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ public static class RelationVisaDetail {
+ /**
+ * 主题
+ */
+ private String topic;
+
+ /**
+ * 单号
+ */
+ private String no;
+ /**
+ * 有符号的变更金额
+ */
+ private String amountChange;
+
+ /**
+ * 发起人
+ */
+ private String createName;
+
+ /**
+ * 提出时间
+ */
+ private Date happenTime;
+
+ }
+
+ @Data
+ @Builder
+ @AllArgsConstructor
+ @NoArgsConstructor
+ public static class VisaUploadFile {
+ /**
+ * 文件Url
+ */
+ private String fileUrl;
+ /**
+ * 文件key
+ */
+ private String fileKey;
+ /**
+ * 文件名称
+ */
+ private String fileName;
+
+ /**
+ * 创建集合
+ */
+ public static List createListByJson(List jsonObjectList) {
+ if (CollectionUtils.isEmpty(jsonObjectList)) {
+ return Lists.newArrayList();
+ }
+ List fileDtoList = Lists.newArrayList();
+ for(JSONObject json : jsonObjectList) {
+ fileDtoList.add(json.toJavaObject(VisaUploadFile.class));
+ }
+ return fileDtoList;
+ }
+ }
+}
diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/VisaRelationResp.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/VisaRelationResp.java
new file mode 100644
index 00000000..4a03db07
--- /dev/null
+++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/VisaRelationResp.java
@@ -0,0 +1,58 @@
+package cn.axzo.nanopart.visa.api.response;
+
+import cn.axzo.nanopart.visa.api.enums.VisaRelationFieldEnum;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+
+import java.util.Date;
+
+/**
+ * 变洽签关联的行数据相应模型
+ *
+ * @author wangli
+ * @since 2025-01-16 09:50
+ */
+@Data
+@Accessors(chain = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class VisaRelationResp {
+
+ private Long id;
+ /**
+ * 变洽签主单据编号
+ */
+ private String visaId;
+ /**
+ * 变洽签单据关联的行数据类型
+ * {@link VisaRelationFieldEnum}
+ */
+ private String varName;
+
+ /**
+ * 数据类型的扩展内容
+ */
+ private String varExt;
+ /**
+ * 数据值的 java 类型
+ */
+ private String varType;
+ /**
+ * 数据值
+ */
+ private String content;
+ /**
+ * 数据值的一些额外描述
+ */
+ private String contentExt;
+
+ /**
+ * 创建时间
+ */
+ private Date createAt;
+
+}
diff --git a/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/VisaSearchResp.java b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/VisaSearchResp.java
new file mode 100644
index 00000000..f4bada46
--- /dev/null
+++ b/visa/visa-api/src/main/java/cn/axzo/nanopart/visa/api/response/VisaSearchResp.java
@@ -0,0 +1,144 @@
+package cn.axzo.nanopart.visa.api.response;
+
+import cn.axzo.nanopart.visa.api.enums.VisaStatusEnum;
+import cn.axzo.nanopart.visa.api.enums.VisaTypeEnum;
+import com.alibaba.fastjson.JSONObject;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author xudawei@axzo.cn
+ * @date 2025/2/18
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class VisaSearchResp {
+
+ /**
+ * 变更单据Id
+ */
+ private Long visaId;
+
+ /**
+ * 单据号
+ */
+ private String no;
+
+ /**
+ * 主题
+ */
+ private String topic;
+
+ /**
+ * 项目
+ */
+ private Long relationWorkspaceId;
+
+ /**
+ * 工程
+ */
+ private Long relationProject;
+
+ /**
+ * 区域集合
+ * 示例:
+ *
+ * [
+ * {"areaId": 249556},
+ * {"areaId": 249555}
+ * ]
+ *
+ */
+ private List relationArea;
+
+ /**
+ * 专业集合
+ * 示例:
+ *
+ * [
+ * {"code":"code1"},
+ * {"code":"code2"},
+ * ]
+ *
+ */
+ private List relationProfessional;
+
+ /**
+ * 提出时间
+ */
+ private Date happenTime;
+
+ /**
+ * 有符号的变更金额
+ */
+ private String amountChange;
+
+ /**
+ * 发生原因
+ */
+ private String reason;
+
+ /**
+ * 单据类型
+ */
+ private VisaTypeEnum type;
+
+ /**
+ * 状态
+ */
+ private VisaStatusEnum status;
+
+ /**
+ * 最终审批ID
+ */
+ private String approvalId;
+
+ /**
+ * 最终审批状态
+ */
+ private String approvalStatus;
+
+ /**
+ * 最终审批时间
+ */
+ private Date approvalCompleteTime;
+
+ /**
+ * IM群组ID
+ */
+ private Long imGroupId;
+
+ /**
+ * 额外信息
+ */
+ private JSONObject extra;
+
+ /**
+ * 创建人id
+ */
+ private Long createBy;
+
+ /**
+ * 修改人id
+ */
+ private Long updateBy;
+
+ /**
+ * 创建时间
+ */
+ protected Date createAt;
+
+ /**
+ * 修改时间
+ */
+ protected Date updateAt;
+
+
+}
diff --git a/visa/visa-api/src/main/resources/META-INF/spring.factories b/visa/visa-api/src/main/resources/META-INF/spring.factories
new file mode 100644
index 00000000..c3000f13
--- /dev/null
+++ b/visa/visa-api/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+cn.axzo.nanopart.visa.api.config.VisaApiAutoConfiguration
\ No newline at end of file
diff --git a/visa/visa-server/pom.xml b/visa/visa-server/pom.xml
new file mode 100644
index 00000000..b27ba208
--- /dev/null
+++ b/visa/visa-server/pom.xml
@@ -0,0 +1,132 @@
+
+
+
+ cn.axzo.nanopart
+ visa
+ ${revision}
+ ../pom.xml
+
+ 4.0.0
+
+ visa-server
+ jar
+
+ visa-server
+
+
+
+ cn.axzo.framework
+ axzo-web-spring-boot-starter
+
+
+ cn.axzo.framework
+ axzo-spring-cloud-starter
+
+
+ cn.axzo.framework
+ axzo-consumer-spring-cloud-starter
+
+
+ cn.axzo.framework
+ axzo-processor-spring-boot-starter
+
+
+
+ cn.axzo.framework
+ axzo-mybatisplus-spring-boot-starter
+
+
+
+ cn.axzo.framework
+ axzo-swagger-yapi-spring-boot-starter
+
+
+ mysql
+ mysql-connector-java
+
+
+ cn.hutool
+ hutool-all
+
+
+ cn.axzo.framework
+ axzo-logger-spring-boot-starter
+
+
+ cn.axzo.framework.rocketmq
+ axzo-common-rocketmq
+
+
+ cn.axzo.pokonyan
+ pokonyan
+
+
+ cn.axzo.workflow
+ workflow-engine-spring-boot-starter
+
+
+ cn.axzo.maokai
+ maokai-api
+
+
+ cn.axzo.nanopart
+ visa-api
+ ${project.version}
+
+
+ cn.axzo.basics
+ basics-profiles-api
+
+
+ cn.axzo.maokai
+ maokai-common
+ ${project.version}
+
+
+ cn.axzo
+ thor-api
+ 1.0.0-SNAPSHOT
+
+
+ cn.axzo.apollo
+ apollo-api
+ 2.0.0-SNAPSHOT
+
+
+ cn.axzo.basics
+ basics-domainless-api
+ 2.0.0-SNAPSHOT
+
+
+ cn.axzo.org
+ org-api
+ 1.0.0-SNAPSHOT
+
+
+ org.apache.commons
+ commons-lang3
+ 3.12.0
+
+
+
+ cn.axzo.karma
+ karma-api
+ 2.0.0-SNAPSHOT
+
+
+
+ cn.axzo.platform
+ axzo-log-api
+ 1.0.0-SNAPSHOT
+
+
+
+ cn.axzo.digital
+ digital-api
+ 1.0.0-SNAPSHOT
+
+
+
+
+
diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/config/RefreshableConfiguration.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/config/RefreshableConfiguration.java
new file mode 100644
index 00000000..e4bf9023
--- /dev/null
+++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/config/RefreshableConfiguration.java
@@ -0,0 +1,53 @@
+package cn.axzo.nanopart.visa.server.config;
+
+import lombok.Data;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.stereotype.Component;
+
+/**
+ * 可 nacos 动态更新的对象
+ *
+ * @author wangli
+ * @since 2025-02-07 16:03
+ */
+@Data
+@Component
+@RefreshScope
+public class RefreshableConfiguration {
+
+ /**
+ * IM 群创建成功后发送卡片模板
+ */
+ @Value("${visa.im.card:261c7fa3db344fb69f3553f695a5a19c}")
+ private String imGroupCardTemplateCode;
+ /**
+ * 变更签证洽商通知
+ * IM 群添加成员,给新成员发送的通知
+ */
+ @Value("${visa.im.notice.addMember:2a4fe042020f4dd783b73fdbb6e6ac98}")
+ private String imGroupAddMemberNoticeTemplateCode;
+ /**
+ * 配合上面的通知发送使用
+ */
+ @Value("${visa.im.event.addMember:visaNegotiation}")
+ private String imGroupAddMemberNoticeEventCode;
+
+ /**
+ * 相关单位确认人人数限制
+ */
+ @Value("${visa.verifyPersonLimit: 8}")
+ private Integer verifyPersonLimit;
+
+ /**
+ * 关联变更单个数限制
+ */
+ @Value("${visa.verifyVisaLimit: 5}")
+ private Integer verifyVisaLimit;
+
+ /**
+ * 发生内容及说明数量限制
+ */
+ @Value("${visa.verifyChangeContextLimit: 50}")
+ private Integer verifyChangeContextLimit;
+}
diff --git a/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/controller/ChangeRecordController.java b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/controller/ChangeRecordController.java
new file mode 100644
index 00000000..a071f049
--- /dev/null
+++ b/visa/visa-server/src/main/java/cn/axzo/nanopart/visa/server/controller/ChangeRecordController.java
@@ -0,0 +1,184 @@
+package cn.axzo.nanopart.visa.server.controller;
+
+import cn.axzo.framework.domain.web.result.ApiPageResult;
+import cn.axzo.framework.domain.web.result.ApiResult;
+import cn.axzo.framework.domain.web.result.PageData;
+import cn.axzo.nanopart.visa.api.changerecord.ChangeRecordApi;
+import cn.axzo.nanopart.visa.api.enums.VisaStatusEnum;
+import cn.axzo.nanopart.visa.api.request.ChangeRecordButtonOperationReq;
+import cn.axzo.nanopart.visa.api.request.ChangeStatusRequest;
+import cn.axzo.nanopart.visa.api.request.CheckVisaWithVisaIdReq;
+import cn.axzo.nanopart.visa.api.request.FetchVisaAllConfirmReq;
+import cn.axzo.nanopart.visa.api.request.GetBillIfRelatedReq;
+import cn.axzo.nanopart.visa.api.request.VisaChangeApproveCreateReq;
+import cn.axzo.nanopart.visa.api.request.VisaChangeApproveOnlyReq;
+import cn.axzo.nanopart.visa.api.request.VisaChangeDiscussCreateReq;
+import cn.axzo.nanopart.visa.api.request.VisaChangeExportReq;
+import cn.axzo.nanopart.visa.api.request.VisaChangePageSearchReq;
+import cn.axzo.nanopart.visa.api.request.VisaChangeTempCreateReq;
+import cn.axzo.nanopart.visa.api.request.VisaDetailByIdRequest;
+import cn.axzo.nanopart.visa.api.request.VisaInitiatorListReq;
+import cn.axzo.nanopart.visa.api.request.VisaSearchReq;
+import cn.axzo.nanopart.visa.api.response.VisaChangeDiscussCreateResp;
+import cn.axzo.nanopart.visa.api.response.VisaChangeInitiatorResp;
+import cn.axzo.nanopart.visa.api.response.VisaChangePageSearchResp;
+import cn.axzo.nanopart.visa.api.response.VisaDetailByIdResponse;
+import cn.axzo.nanopart.visa.api.response.VisaSearchResp;
+import cn.axzo.nanopart.visa.server.service.ChangeRecordBillService;
+import cn.axzo.nanopart.visa.server.service.ChangeRecordConfirmService;
+import cn.axzo.nanopart.visa.server.service.ChangeRecordService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections.CollectionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.validation.Valid;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * @author xudawei
+ * @date 2025/01/15
+ * @desc 变更签证记录
+ */
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+public class ChangeRecordController implements ChangeRecordApi {
+
+ @Autowired
+ private ChangeRecordService changeRecordService;
+
+ @Autowired
+ private ChangeRecordConfirmService changeRecordConfirmService;
+
+ @Autowired
+ private ChangeRecordBillService changeRecordBillService;
+
+ @Override
+ public ApiResult tempCreateVisaChangeRecord(VisaChangeTempCreateReq req) {
+ return ApiResult.ok(changeRecordService.tempCreateVisaChangeRecord(req));
+ }
+
+ @Override
+ public ApiResult checkVisaWithVisaId(CheckVisaWithVisaIdReq req) {
+ changeRecordService.checkVisaWithVisaId(req);
+ return ApiResult.ok();
+ }
+
+ @Override
+ public ApiResult discussCreateVisaChangeRecord(VisaChangeDiscussCreateReq req) {
+ return ApiResult.ok(changeRecordService.discussCreateVisaChangeRecord(req));
+ }
+
+ @Override
+ public ApiResult approveCreateVisaChangeRecord(VisaChangeApproveCreateReq req) {
+ return ApiResult.ok(changeRecordService.approveCreateVisaChangeRecord(req));
+ }
+
+ /**
+ * 变签状态变更
+ */
+ @Override
+ public ApiResult changeStatus(@RequestBody @Valid ChangeStatusRequest request) {
+ return ApiResult.ok(changeRecordService.changeStatus(request));
+ }
+
+
+ /**
+ * 变签详情
+ */
+ @Override
+ public ApiResult visaDetailById(@RequestBody @Valid VisaDetailByIdRequest req) {
+ VisaDetailByIdResponse resp = changeRecordService.detailById(req.getVisaId());
+
+ if (req.isNeedBtnList()) {
+// resp.setBtnList(VisaButtonTypeEnum.sort(VisaButtonTypeEnum.all()));
+ resp.setBtnList(changeRecordConfirmService.fetchBtnsByCondition(req.getVisaId(), req.getPersonId(), req.getOuId(), req.getWorkspaceId(), resp.getStatus(), resp.getApprovalId()));
+ }
+ return ApiResult.ok(resp);
+ }
+
+ @Override
+ public ApiPageResult pageSearchVisaChangeRecord(VisaChangePageSearchReq req) {
+ //重新赋值属性,待提报:TO_REPORT/REPORT_FROM_APPROVE,决策中:DECIDING/DECIDING_FROM_APPROVE
+ this.rechangeAttr(req);
+ PageData pageData = changeRecordService.page(req);
+ return ApiPageResult.ok(pageData.getList(), pageData.getTotalCount(), pageData.getPage(), pageData.getPageSize());
+ }
+
+ @Override
+ public ApiPageResult list(VisaSearchReq req) {
+ PageData page = changeRecordService.list(req);
+ return ApiPageResult.ok(page.getList(), page.getTotalCount(), page.getPage(), page.getPageSize());
+ }
+
+ /**
+ * 重新赋值属性
+ * 待提报:TO_REPORT/REPORT_FROM_APPROVE
+ * 决策中:DECIDING/DECIDING_FROM_APPROVE
+ */
+ private void rechangeAttr(VisaChangePageSearchReq req) {
+ if (Objects.nonNull(req) && CollectionUtils.isNotEmpty(req.getStatuses()) && req.getStatuses().contains(VisaStatusEnum.TO_REPORT)) {
+ req.getStatuses().add(VisaStatusEnum.REPORT_FROM_APPROVE);
+ }
+
+ if (Objects.nonNull(req) && CollectionUtils.isNotEmpty(req.getStatuses()) && req.getStatuses().contains(VisaStatusEnum.DECIDING)) {
+ req.getStatuses().add(VisaStatusEnum.DECIDING_FROM_APPROVE);
+ }
+ }
+
+ @Override
+ public ApiResult exportVisaChangeRecord(VisaChangeExportReq req) {
+ changeRecordService.export(req);
+ return ApiResult.ok();
+ }
+
+ @Override
+ public ApiResult> listInitiator(VisaInitiatorListReq req) {
+ return ApiResult.ok(changeRecordService.listInitiator(req));
+ }
+
+ @Override
+ public ApiResult> getVisaAllConfirm(FetchVisaAllConfirmReq req) {
+ return ApiResult.ok(changeRecordConfirmService.listAllConfirmByVisaId(req.getVisaId()));
+ }
+
+ @Override
+ public ApiResult approveCreateOnlyVisaChangeRecord(VisaChangeApproveOnlyReq req) {
+ return ApiResult.ok(changeRecordService.approveCreateOnlyVisaChangeRecord(req));
+ }
+
+ @Override
+ public ApiResult forbidChangeRecord(ChangeRecordButtonOperationReq req) {
+ changeRecordService.forbid(req);
+ return ApiResult.ok();
+ }
+
+ @Override
+ public ApiResult reDecisionChangeRecord(ChangeRecordButtonOperationReq req) {
+ changeRecordService.reDecision(req);
+ return ApiResult.ok();
+ }
+
+ @Override
+ public ApiResult executeChangeRecord(ChangeRecordButtonOperationReq req) {
+ changeRecordService.doExecute(req);
+ return ApiResult.ok();
+ }
+
+ @Override
+ public ApiResult deleteChangeRecord(ChangeRecordButtonOperationReq req) {
+ changeRecordService.delete(req);
+ return ApiResult.ok();
+ }
+
+ @Override
+ public ApiResult