package com.yunhai.svchall.model.svc; import lombok.Data; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Pattern; import javax.validation.constraints.Size; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * 办事大厅业务模型收口(管理端/用户面 DTO 与 VO;嵌套静态类收口减少文件数)。 * oapi(供 CMS)模型另见 model/oapi/OapiModels。 */ public final class SvcModels { private SvcModels() { } // ===== 通用 ===== /** 删除入参(siteCode 定位站点 + id) */ @Data public static class DeleteRequest { @NotBlank(message = "siteCode 不得为空") private String siteCode; @NotNull(message = "id 不得为空") private Integer id; } /** 排序项 */ @Data public static class IdSortItem { @NotNull(message = "id 不得为空") private Integer id; @NotNull(message = "sortNo 不得为空") private Integer sortNo; } /** 站点选项(CMS 站点目录代理,管理端站点选择器;T2 起含主题 raw 透传,供用户面弱联动) */ @Data @com.fasterxml.jackson.annotation.JsonIgnoreProperties(ignoreUnknown = true) public static class SiteOptionVo { private String siteCode; private String name; private Long schId; /** CMS 皮肤包名(raw 透传;空=default 由消费方兜底) */ private String theme; /** CMS config_json.theme_params 原样键值(raw,消费方按皮肤声明过滤;未配=空对象) */ private Map themeParams = new java.util.LinkedHashMap<>(); } // ===== 字典 ===== @Data public static class SvcDictSaveRequest { private Integer id; @NotBlank(message = "siteCode 不得为空") private String siteCode; @NotBlank(message = "字典类型不得为空") private String dictType; @NotBlank(message = "字典键不得为空") @Pattern(regexp = "^[A-Za-z0-9_-]{1,64}$", message = "字典键仅限字母/数字/下划线/中划线") private String dictKey; @NotBlank(message = "字典名称不得为空") @Size(max = 128, message = "字典名称过长") private String dictName; private Integer sortNo; private Integer status; } @Data public static class SvcDictVo { private Integer id; private String siteCode; private String dictType; private String dictKey; private String dictName; private Integer sortNo; private Integer status; private Long referenced; public static SvcDictVo of(com.yunhai.svchall.entity.ServiceDict d, Long referenced) { SvcDictVo vo = new SvcDictVo(); vo.setId(d.getId()); vo.setSiteCode(d.getSiteCode()); vo.setDictType(d.getDictType()); vo.setDictKey(d.getDictKey()); vo.setDictName(d.getDictName()); vo.setSortNo(d.getSortNo()); vo.setStatus(d.getStatus()); vo.setReferenced(referenced == null ? 0L : referenced); return vo; } } /** 服务部门选项(UserBase 组树嵌套下发,承袭 CMS SvcDeptVo) */ @Data public static class SvcDeptVo { private Integer id; private String name; private List children = new ArrayList<>(); public SvcDeptVo() { } public SvcDeptVo(Integer id, String name) { this.id = id; this.name = name; } } // ===== 事项 ===== @Data public static class SvcItemPageQuery { @NotBlank(message = "siteCode 不得为空") private String siteCode; private Integer status; private String keyword; private String objectKey; private String categoryKey; private Integer deptGroupId; private Integer page = 1; private Integer pageSize = 10; } @Data public static class SvcItemSaveRequest { private Integer id; @NotBlank(message = "siteCode 不得为空") private String siteCode; @NotBlank(message = "事项编码不得为空") @Pattern(regexp = "^[A-Za-z0-9_-]{2,64}$", message = "事项编码仅限字母/数字/下划线/中划线,2~64 位") private String itemCode; @NotBlank(message = "事项名称不得为空") @Size(max = 200, message = "事项名称过长") private String title; @Size(max = 500, message = "简介过长") private String summary; @Size(max = 64) private String objectKey; @Size(max = 64) private String categoryKey; private Integer deptGroupId; private Integer handleMode; private Integer onlineFlag; @Size(max = 200) private String handlePlace; @Size(max = 64) private String handleTimeLimit; @Size(max = 64) private String promiseTimeLimit; @Size(max = 64) private String contactPhone; @Size(max = 64) private String supervisePhone; private String processHtml; private String materialHtml; private String noticeHtml; private Integer faqCategoryId; private Integer noticeCategoryId; private Integer ruleCategoryId; @Size(max = 500) private String coverImage; /** 绑定自定义表单 code(可空;发起无需表单) */ @Size(max = 64) private String formCode; /** 绑定流程中心流程 flow_guid(可空;onlineFlag=1 时必填) */ @Size(max = 32) private String flowGuid; @Size(max = 500) private String applyUrl; private Integer sortNo; /** 指南附件 id 列表(保存时全量重绑 topic_type=svc_item_guide) */ private List attachmentIds; } @Data public static class SvcItemEditVo { private Integer id; private String siteCode; private String itemCode; private String title; private String summary; private String objectKey; private String categoryKey; private Integer deptGroupId; private String deptName; private Integer handleMode; private Integer onlineFlag; private String handlePlace; private String handleTimeLimit; private String promiseTimeLimit; private String contactPhone; private String supervisePhone; private String processHtml; private String materialHtml; private String noticeHtml; private Integer faqCategoryId; private Integer noticeCategoryId; private Integer ruleCategoryId; private String coverImage; private String formCode; private String flowGuid; private String applyUrl; private Integer sortNo; private Integer status; private LocalDateTime publishTime; private LocalDateTime offlineTime; private Integer viewCount; public static SvcItemEditVo of(com.yunhai.svchall.entity.ServiceItem item) { SvcItemEditVo vo = new SvcItemEditVo(); vo.setId(item.getId()); vo.setSiteCode(item.getSiteCode()); vo.setItemCode(item.getItemCode()); vo.setTitle(item.getTitle()); vo.setSummary(item.getSummary()); vo.setObjectKey(item.getObjectKey()); vo.setCategoryKey(item.getCategoryKey()); vo.setDeptGroupId(item.getDeptGroupId()); vo.setDeptName(item.getDeptName()); vo.setHandleMode(item.getHandleMode()); vo.setOnlineFlag(item.getOnlineFlag()); vo.setHandlePlace(item.getHandlePlace()); vo.setHandleTimeLimit(item.getHandleTimeLimit()); vo.setPromiseTimeLimit(item.getPromiseTimeLimit()); vo.setContactPhone(item.getContactPhone()); vo.setSupervisePhone(item.getSupervisePhone()); vo.setProcessHtml(item.getProcessHtml()); vo.setMaterialHtml(item.getMaterialHtml()); vo.setNoticeHtml(item.getNoticeHtml()); vo.setFaqCategoryId(item.getFaqCategoryId()); vo.setNoticeCategoryId(item.getNoticeCategoryId()); vo.setRuleCategoryId(item.getRuleCategoryId()); vo.setCoverImage(item.getCoverImage()); vo.setFormCode(item.getFormCode()); vo.setFlowGuid(item.getFlowGuid()); vo.setApplyUrl(item.getApplyUrl()); vo.setSortNo(item.getSortNo()); vo.setStatus(item.getStatus()); vo.setPublishTime(item.getPublishTime()); vo.setOfflineTime(item.getOfflineTime()); vo.setViewCount(item.getViewCount()); return vo; } } @Data public static class SvcItemPageItem { private Integer id; private String siteCode; private String itemCode; private String title; private String summary; private String objectKey; private String objectName; private String categoryKey; private String categoryName; private Integer deptGroupId; private String deptName; private Integer handleMode; private Integer onlineFlag; private String formCode; private String flowGuid; private Integer status; private Integer sortNo; private LocalDateTime publishTime; private Integer viewCount; private LocalDateTime createTime; private String createBy; public static SvcItemPageItem of(com.yunhai.svchall.entity.ServiceItem item, String objectName, String categoryName) { SvcItemPageItem vo = new SvcItemPageItem(); vo.setId(item.getId()); vo.setSiteCode(item.getSiteCode()); vo.setItemCode(item.getItemCode()); vo.setTitle(item.getTitle()); vo.setSummary(item.getSummary()); vo.setObjectKey(item.getObjectKey()); vo.setObjectName(objectName); vo.setCategoryKey(item.getCategoryKey()); vo.setCategoryName(categoryName); vo.setDeptGroupId(item.getDeptGroupId()); vo.setDeptName(item.getDeptName()); vo.setHandleMode(item.getHandleMode()); vo.setOnlineFlag(item.getOnlineFlag()); vo.setFormCode(item.getFormCode()); vo.setFlowGuid(item.getFlowGuid()); vo.setStatus(item.getStatus()); vo.setSortNo(item.getSortNo()); vo.setPublishTime(item.getPublishTime()); vo.setViewCount(item.getViewCount()); vo.setCreateTime(item.getCreateTime()); vo.setCreateBy(item.getCreateBy()); return vo; } } @Data public static class SvcItemSimpleVo { private Integer id; private String itemCode; private String title; private Integer status; public static SvcItemSimpleVo of(com.yunhai.svchall.entity.ServiceItem item) { SvcItemSimpleVo vo = new SvcItemSimpleVo(); vo.setId(item.getId()); vo.setItemCode(item.getItemCode()); vo.setTitle(item.getTitle()); vo.setStatus(item.getStatus()); return vo; } } @Data public static class SvcItemAttachmentVo { private Integer id; private String fileName; private String fileUrl; private Long fileSize; public static SvcItemAttachmentVo of(com.yunhai.svchall.entity.ServiceAttachment a) { SvcItemAttachmentVo vo = new SvcItemAttachmentVo(); vo.setId(a.getId()); vo.setFileName(a.getFileName()); vo.setFileUrl(com.yunhai.svchall.utils.FileUtil.buildUrl(a.getFileUrl())); vo.setFileSize(a.getFileSize()); return vo; } } // ===== 表单 ===== @Data public static class SvcFormSaveRequest { private Integer id; @NotBlank(message = "表单编码不得为空") @Pattern(regexp = "^[A-Za-z0-9_-]{2,64}$", message = "表单编码仅限字母/数字/下划线/中划线,2~64 位") private String code; @NotBlank(message = "表单名称不得为空") @Size(max = 128, message = "表单名称过长") private String name; @NotBlank(message = "表单结构不得为空") private String schemaJson; /** 表单级流程绑定(可空;绑定事项未自身绑流时回退用它) */ private String flowGuid; } @Data public static class SvcFormVo { private Integer id; private String code; private String name; private Integer version; private Integer status; private String schemaJson; /** 表单级流程绑定(可空) */ private String flowGuid; private LocalDateTime publishTime; private Boolean referenced; /** 绑定本表单的事项明细(仅 list 填充;表单页反查使用位置) */ private List refItems; public static SvcFormVo of(com.yunhai.svchall.entity.ServiceForm form, Boolean referenced) { SvcFormVo vo = new SvcFormVo(); vo.setId(form.getId()); vo.setCode(form.getCode()); vo.setName(form.getName()); vo.setVersion(form.getVersion()); vo.setStatus(form.getStatus()); vo.setSchemaJson(form.getSchemaJson()); vo.setFlowGuid(form.getFlowGuid()); vo.setPublishTime(form.getPublishTime()); vo.setReferenced(referenced); return vo; } /** 表单绑定事项的摘要行(表单 sch 级共享,跨站点绑定如实展示站点) */ @Data public static class ItemBrief { private Integer id; private String itemCode; private String title; private String siteCode; private Integer status; public static ItemBrief of(com.yunhai.svchall.entity.ServiceItem item) { ItemBrief brief = new ItemBrief(); brief.setId(item.getId()); brief.setItemCode(item.getItemCode()); brief.setTitle(item.getTitle()); brief.setSiteCode(item.getSiteCode()); brief.setStatus(item.getStatus()); return brief; } } } // ===== 办件 ===== /** 申请级材料项(无表单事项的附件;先经 /api/file/upload 上传,再携 attachmentId 绑定) */ @Data public static class ApplyAttachmentItem { /** 表单/材料字段标识(可空) */ private String fieldKey; /** 上传接口返回的附件行 id */ @NotNull(message = "attachmentId 不得为空") private Integer attachmentId; } @Data public static class ApplySubmitRequest { @NotBlank(message = "siteCode 不得为空") private String siteCode; @NotBlank(message = "itemCode 不得为空") private String itemCode; /** 表单数据 JSON 字符串({key:value};无表单事项传空) */ private String formData; /** 申请级材料(可空) */ private List attachments; } @Data public static class ApplyVo { private Integer id; private String siteCode; private Integer itemId; private String itemCode; private String itemTitle; private String itemSnapshotJson; private Integer formVersion; private String formDataJson; private String attachmentsJson; private Integer submitterId; private String submitterName; private String flowGuid; private Long flowInstanceId; private Integer flowStatus; private String flowResultJson; private LocalDateTime submitTime; private LocalDateTime finishTime; private LocalDateTime createTime; /** * 流程中心是否已接入(响应期状态标志,不入库):只在 submit/retry/resubmit 的 * 出参上填。代发起是 AFTER_COMMIT 异步,故提交成功时 flowStatus 必然还是 0(待提交)—— * 单看 flowStatus 无法区分"已排队、秒级翻转"与"流程中心未接入/调用失败", * 前端据此误报"流程中心暂不可用"(BP-R4-1)。有了本标志: * enabled=false → 真的不会翻转,直接告知未接入;enabled=true 且 flowStatus=0 → 排队中,等待轮询。 */ private Boolean flowEnabled; /** * 办理表单 schema(响应期补发,不入库;仅 detail 出参填): * 前端「申请内容」以此为 key→中文 label/分组的唯一事实源(2026-09-17),版本口径= * 办件 formVersion 精确命中、缺失回落最新已发布版本。无表单事项为 null。 */ private String formSchemaJson; public static ApplyVo of(com.yunhai.svchall.entity.ServiceApply a) { ApplyVo vo = new ApplyVo(); vo.setId(a.getId()); vo.setSiteCode(a.getSiteCode()); vo.setItemId(a.getItemId()); vo.setItemCode(a.getItemCode()); vo.setItemTitle(a.getItemTitle()); vo.setItemSnapshotJson(a.getItemSnapshotJson()); vo.setFormVersion(a.getFormVersion()); vo.setFormDataJson(a.getFormDataJson()); vo.setAttachmentsJson(a.getAttachmentsJson()); vo.setSubmitterId(a.getSubmitterId()); vo.setSubmitterName(a.getSubmitterName()); vo.setFlowGuid(a.getFlowGuid()); vo.setFlowInstanceId(a.getFlowInstanceId()); vo.setFlowStatus(a.getFlowStatus()); vo.setFlowResultJson(a.getFlowResultJson()); vo.setSubmitTime(a.getSubmitTime()); vo.setFinishTime(a.getFinishTime()); vo.setCreateTime(a.getCreateTime()); return vo; } } /** * 用户面办理大厅的事项列表/筛选项形状,直接复用 oapi 出参 {@code OapiModels.ListData} / * {@code Filters}(与 CMS 门户同源,不另立 VO)——见 {@code HallController}。 */ @Data public static class ApplyPageQuery { private String siteCode; private String itemCode; private Integer flowStatus; private String keyword; private Integer page = 1; private Integer pageSize = 10; } // ===== 流程(管理端回显 + flow-center 门面出入参) ===== /** 字段单一事实源=SDK 门面 DTO(可直接作 YschFlowOpenApi 泛型出入参) */ @Data @lombok.EqualsAndHashCode(callSuper = true) public static class FlowSummaryVo extends com.yunhai.platform.openapi.flow.model.FlowSummary { } /** 代发起请求体(门面 {@code YschFlowOpenApi.apply} 的 B 绑定;字段继承 SDK FlowApplyBody) */ @Data @lombok.EqualsAndHashCode(callSuper = true) public static class ApplyBody extends com.yunhai.platform.openapi.flow.model.FlowApplyBody { } /** 代发起响应(门面 R 绑定;{@code duplicated=true} 表示命中 biz uk 幂等返回既有办件) */ @Data @lombok.EqualsAndHashCode(callSuper = true) public static class ApplyResultVo extends com.yunhai.platform.openapi.flow.model.FlowApplyResult { } /** 回调注册请求体(门面 {@code YschFlowOpenApi.registerCallback} 的 B 绑定) */ @Data @lombok.EqualsAndHashCode(callSuper = true) public static class CallbackRegBody extends com.yunhai.platform.openapi.flow.model.FlowCallbackRegBody { } /** 回调注册响应(下发的 secret 用于验 X-Flw-Signature) */ @Data @lombok.EqualsAndHashCode(callSuper = true) public static class CallbackRegVo extends com.yunhai.platform.openapi.flow.model.FlowCallbackRegResult { } /** * 办件进度(门面 {@code YschFlowOpenApi.progress} 的 R 绑定;字段单一事实源=SDK FlowProgress): * steps 为"发起申请 + 各节点"时间线,前端直接渲染审批轨迹。 */ @Data @lombok.EqualsAndHashCode(callSuper = true) public static class FlowProgressVo extends com.yunhai.platform.openapi.flow.model.FlowProgress { } /** * 流程示意图免签出图地址(门面 {@code YschFlowOpenApi.diagramUrl} 的 R 绑定)。 * 该出参只在服务端内部使用:token 由后端换取图片字节,不下发前端。 */ @Data @lombok.EqualsAndHashCode(callSuper = true) public static class FlowDiagramVo extends com.yunhai.platform.openapi.flow.model.FlowDiagramUrl { } }