From c3e6af56dc33028367c5edb10082b218c065ee68 Mon Sep 17 00:00:00 2001 From: DaxPay Dev Date: Wed, 24 Jun 2026 21:36:27 +0800 Subject: [PATCH] =?UTF-8?q?feat(system):=20=E6=96=B0=E5=A2=9E=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E5=8D=8F=E8=AE=AE=E7=89=88=E6=9C=AC=E7=AE=A1=E7=90=86?= =?UTF-8?q?(=E7=89=88=E6=9C=AC=E8=A1=A8/=E5=A4=9A=E8=AF=AD=E8=A8=80/?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E6=9C=BA)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../i18n/en-US/enum/user_protocol_status.json | 5 + .../i18n/en-US/error/system/protocol.json | 8 +- .../main/resources/i18n/en-US/validation.json | 3 + .../i18n/zh-CN/enum/user_protocol_status.json | 5 + .../i18n/zh-CN/error/system/protocol.json | 8 +- .../main/resources/i18n/zh-CN/validation.json | 3 + .../protocol/UserProtocolController.java | 30 +++- .../UserProtocolVersionController.java | 99 ++++++++++++ .../protocol/UserProtocolVersionConvert.java | 36 +++++ .../protocol/UserProtocolVersionManager.java | 96 ++++++++++++ .../protocol/UserProtocolVersionMapper.java | 11 ++ .../system/entity/protocol/UserProtocol.java | 9 +- .../entity/protocol/UserProtocolVersion.java | 64 ++++++++ .../enums/UserProtocolVersionStatusEnum.java | 36 +++++ .../param/protocol/UserProtocolParam.java | 12 +- .../protocol/UserProtocolVersionParam.java | 58 +++++++ .../protocol/UserProtocolVersionQuery.java | 28 ++++ .../protocol/UserProtocolContentResult.java | 66 ++++++++ .../result/protocol/UserProtocolResult.java | 10 +- .../protocol/UserProtocolVersionResult.java | 62 ++++++++ .../service/protocol/UserProtocolService.java | 110 ++++++++++--- .../protocol/UserProtocolVersionService.java | 145 ++++++++++++++++++ 22 files changed, 850 insertions(+), 54 deletions(-) create mode 100644 daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/enum/user_protocol_status.json create mode 100644 daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/enum/user_protocol_status.json create mode 100644 daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/controller/protocol/UserProtocolVersionController.java create mode 100644 daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/convert/protocol/UserProtocolVersionConvert.java create mode 100644 daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/dao/protocol/UserProtocolVersionManager.java create mode 100644 daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/dao/protocol/UserProtocolVersionMapper.java create mode 100644 daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/entity/protocol/UserProtocolVersion.java create mode 100644 daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/enums/UserProtocolVersionStatusEnum.java create mode 100644 daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/param/protocol/UserProtocolVersionParam.java create mode 100644 daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/param/protocol/UserProtocolVersionQuery.java create mode 100644 daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/result/protocol/UserProtocolContentResult.java create mode 100644 daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/result/protocol/UserProtocolVersionResult.java create mode 100644 daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/service/protocol/UserProtocolVersionService.java diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/enum/user_protocol_status.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/enum/user_protocol_status.json new file mode 100644 index 000000000..321b27101 --- /dev/null +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/enum/user_protocol_status.json @@ -0,0 +1,5 @@ +{ + "DRAFT": "Draft", + "PUBLISHED": "Published", + "ARCHIVED": "Archived" +} diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/error/system/protocol.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/error/system/protocol.json index 3b72186b7..3caa303eb 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/error/system/protocol.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/error/system/protocol.json @@ -2,5 +2,11 @@ "defaultCannotDelete": "Default protocol cannot be deleted", "notExist": "User protocol not found", "itemNotExist": "User protocol item not found", - "defaultItemNotExist": "No default protocol exists for this type" + "defaultItemNotExist": "No default protocol exists for this type", + "versionNotExist": "User protocol version not found", + "draftOnlyCanEdit": "Only draft versions can be edited", + "draftOnlyCanDelete": "Only draft versions can be deleted", + "draftOnlyCanPublish": "Only draft versions can be published", + "publishedOnlyCanArchive": "Only published versions can be archived", + "sameClientType": "Target client type is the same as the source" } diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/validation.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/validation.json index 956beea02..3f7ff437e 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/validation.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/validation.json @@ -404,6 +404,9 @@ "clientType": { "notBlank": "Client type cannot be blank" }, + "language": { + "notBlank": "Language cannot be blank" + }, "email": { "notBlank": "Email cannot be blank", "format": "Invalid email format" diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/enum/user_protocol_status.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/enum/user_protocol_status.json new file mode 100644 index 000000000..640e2bc78 --- /dev/null +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/enum/user_protocol_status.json @@ -0,0 +1,5 @@ +{ + "DRAFT": "草稿", + "PUBLISHED": "已发布", + "ARCHIVED": "已归档" +} diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/error/system/protocol.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/error/system/protocol.json index 22d214cbc..25ea0df56 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/error/system/protocol.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/error/system/protocol.json @@ -2,5 +2,11 @@ "defaultCannotDelete": "默认协议不可删除", "notExist": "用户协议不存在", "itemNotExist": "用户协议项不存在", - "defaultItemNotExist": "该类型下不存在默认协议" + "defaultItemNotExist": "该类型下不存在默认协议", + "versionNotExist": "用户协议版本不存在", + "draftOnlyCanEdit": "仅草稿状态的版本可编辑", + "draftOnlyCanDelete": "仅草稿状态的版本可删除", + "draftOnlyCanPublish": "仅草稿状态的版本可发布", + "publishedOnlyCanArchive": "仅已发布状态的版本可归档", + "sameClientType": "目标端类型与源端类型一致" } diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/validation.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/validation.json index e83cc81b4..40bf16cd2 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/validation.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/validation.json @@ -407,6 +407,9 @@ "clientType": { "notBlank": "端类型不能为空" }, + "language": { + "notBlank": "语言不能为空" + }, "email": { "notBlank": "邮箱不可为空", "format": "邮箱格式不正确" diff --git a/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/controller/protocol/UserProtocolController.java b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/controller/protocol/UserProtocolController.java index 519c9d059..d485963f8 100644 --- a/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/controller/protocol/UserProtocolController.java +++ b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/controller/protocol/UserProtocolController.java @@ -4,6 +4,7 @@ import cn.daxpay.open.platform.system.enums.UserProtocolClientTypeEnum; import cn.daxpay.open.platform.system.enums.UserProtocolTypeEnum; import cn.daxpay.open.platform.system.param.protocol.UserProtocolParam; import cn.daxpay.open.platform.system.param.protocol.UserProtocolQuery; +import cn.daxpay.open.platform.system.result.protocol.UserProtocolContentResult; import cn.daxpay.open.platform.system.result.protocol.UserProtocolResult; import cn.daxpay.open.platform.system.service.protocol.UserProtocolService; import cn.daxpay.open.platform.core.annotation.IgnoreAuth; @@ -88,16 +89,19 @@ public class UserProtocolController { return Res.ok(userProtocolService.findById(id)); } - /// 查询默认协议 + /// 查询默认协议内容(对外展示, 各端通过 type+clientType+language 拉取当前生效版本) /// /// @param type 协议类型 - /// @return 默认协议信息 + /// @param clientType 端类型 + /// @param language 语言(可选, 为空时回退到协议默认语言) + /// @return 协议内容 @IgnoreAuth - @Operation(summary = "查询默认协议") + @Operation(summary = "查询默认协议内容") @GetMapping("/find-default") - public Result findDefault(@NotNull(message = "{validation.field.type.notBlank}") String type, - @NotNull(message = "{validation.field.clientType.notBlank}") String clientType){ - return Res.ok(userProtocolService.findDefault(type, clientType)); + public Result findDefault(@NotNull(message = "{validation.field.type.notBlank}") String type, + @NotNull(message = "{validation.field.clientType.notBlank}") String clientType, + @RequestParam(required = false) String language){ + return Res.ok(userProtocolService.findDefault(type, clientType, language)); } @Operation(summary = "协议类型列表") @@ -138,6 +142,16 @@ public class UserProtocolController { return Res.ok(); } + /// 复制协议到其他端(连同各语言的当前生效版本一起复制) + /// + /// @param id 源协议ID + /// @param clientType 目标端类型 + /// @return 目标协议ID + @Operation(summary = "复制到其他端") + @PostMapping("/copy-to-client") + public Result copyToClient(@NotNull(message = "{validation.field.id.notNull}") Long id, + @NotNull(message = "{validation.field.clientType.notBlank}") String clientType){ + return Res.ok(userProtocolService.copyToClient(id, clientType)); + } + } - - diff --git a/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/controller/protocol/UserProtocolVersionController.java b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/controller/protocol/UserProtocolVersionController.java new file mode 100644 index 000000000..60e09c677 --- /dev/null +++ b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/controller/protocol/UserProtocolVersionController.java @@ -0,0 +1,99 @@ +package cn.daxpay.open.platform.system.controller.protocol; + +import cn.daxpay.open.platform.core.rest.Res; +import cn.daxpay.open.platform.core.rest.param.PageParam; +import cn.daxpay.open.platform.core.rest.result.PageResult; +import cn.daxpay.open.platform.core.rest.result.Result; +import cn.daxpay.open.platform.core.validation.ValidationGroup; +import cn.daxpay.open.platform.system.param.protocol.UserProtocolVersionParam; +import cn.daxpay.open.platform.system.param.protocol.UserProtocolVersionQuery; +import cn.daxpay.open.platform.system.result.protocol.UserProtocolVersionResult; +import cn.daxpay.open.platform.system.service.protocol.UserProtocolVersionService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +/// # 用户协议版本控制器 +/// +@Validated +@Tag(name = "用户协议版本") +@RestController +@RequestMapping("/user/protocol/version") +@RequiredArgsConstructor +public class UserProtocolVersionController { + private final UserProtocolVersionService userProtocolVersionService; + + /// 分页查询版本 + /// + /// @param pageParam 分页参数 + /// @param query 查询条件 + /// @return 版本分页结果 + @Operation(summary = "分页") + @GetMapping("/page") + public Result> page(PageParam pageParam, UserProtocolVersionQuery query){ + return Res.ok(userProtocolVersionService.page(pageParam, query)); + } + + /// 新建草稿 + /// + /// @param param 版本参数 + @Operation(summary = "新建草稿") + @PostMapping("/add") + public Result add(@RequestBody @Validated(ValidationGroup.add.class) UserProtocolVersionParam param){ + userProtocolVersionService.add(param); + return Res.ok(); + } + + /// 编辑草稿内容(仅草稿可编辑) + /// + /// @param param 版本参数 + @Operation(summary = "编辑草稿") + @PostMapping("/update") + public Result update(@RequestBody @Validated(ValidationGroup.edit.class) UserProtocolVersionParam param){ + userProtocolVersionService.update(param); + return Res.ok(); + } + + /// 删除草稿(仅草稿可删除) + /// + /// @param id 版本ID + @Operation(summary = "删除草稿") + @PostMapping("/delete") + public Result delete(@NotNull(message = "{validation.field.id.notNull}") Long id){ + userProtocolVersionService.delete(id); + return Res.ok(); + } + + /// 根据ID查询版本 + /// + /// @param id 版本ID + /// @return 版本信息 + @Operation(summary = "查询") + @GetMapping("/get") + public Result findById(@NotNull(message = "{validation.field.id.notNull}") Long id){ + return Res.ok(userProtocolVersionService.findById(id)); + } + + /// 发布版本(草稿 -> 已发布, 同协议同语言原已发布自动归档) + /// + /// @param id 版本ID + @Operation(summary = "发布版本") + @PostMapping("/publish") + public Result publish(@NotNull(message = "{validation.field.id.notNull}") Long id){ + userProtocolVersionService.publish(id); + return Res.ok(); + } + + /// 归档版本(已发布 -> 归档) + /// + /// @param id 版本ID + @Operation(summary = "归档版本") + @PostMapping("/archive") + public Result archive(@NotNull(message = "{validation.field.id.notNull}") Long id){ + userProtocolVersionService.archive(id); + return Res.ok(); + } +} diff --git a/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/convert/protocol/UserProtocolVersionConvert.java b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/convert/protocol/UserProtocolVersionConvert.java new file mode 100644 index 000000000..106e65574 --- /dev/null +++ b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/convert/protocol/UserProtocolVersionConvert.java @@ -0,0 +1,36 @@ +package cn.daxpay.open.platform.system.convert.protocol; + +import cn.daxpay.open.platform.system.entity.protocol.UserProtocolVersion; +import cn.daxpay.open.platform.system.param.protocol.UserProtocolVersionParam; +import cn.daxpay.open.platform.system.result.protocol.UserProtocolVersionResult; +import org.mapstruct.BeanMapping; +import org.mapstruct.Mapper; +import org.mapstruct.MappingTarget; +import org.mapstruct.NullValuePropertyMappingStrategy; +import org.mapstruct.factory.Mappers; + +/// # 用户协议版本转换类 +/// +@Mapper +public interface UserProtocolVersionConvert { + UserProtocolVersionConvert CONVERT = Mappers.getMapper(UserProtocolVersionConvert.class); + + /// 实体转结果 + /// + /// @param entity 用户协议版本实体 + /// @return 用户协议版本结果 + UserProtocolVersionResult toResult(UserProtocolVersion entity); + + /// 参数转实体 + /// + /// @param param 用户协议版本参数 + /// @return 用户协议版本实体 + UserProtocolVersion toEntity(UserProtocolVersionParam param); + + /// 复制属性 + /// + /// @param param 用户协议版本参数 + /// @param entity 用户协议版本实体 + @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) + void copy(UserProtocolVersionParam param, @MappingTarget UserProtocolVersion entity); +} diff --git a/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/dao/protocol/UserProtocolVersionManager.java b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/dao/protocol/UserProtocolVersionManager.java new file mode 100644 index 000000000..630ec247a --- /dev/null +++ b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/dao/protocol/UserProtocolVersionManager.java @@ -0,0 +1,96 @@ +package cn.daxpay.open.platform.system.dao.protocol; + +import cn.daxpay.open.platform.common.mybatisplus.base.MpRealDelEntity; +import cn.daxpay.open.platform.common.mybatisplus.impl.BaseManager; +import cn.daxpay.open.platform.common.mybatisplus.query.generator.QueryGenerator; +import cn.daxpay.open.platform.common.mybatisplus.util.MpUtil; +import cn.daxpay.open.platform.core.rest.param.PageParam; +import cn.daxpay.open.platform.system.entity.protocol.UserProtocolVersion; +import cn.daxpay.open.platform.system.param.protocol.UserProtocolVersionQuery; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Repository; + +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Optional; + +/// # 用户协议版本管理 +/// +@Slf4j +@Repository +@RequiredArgsConstructor +public class UserProtocolVersionManager extends BaseManager { + + /// 分页 + public Page page(PageParam pageParam, UserProtocolVersionQuery query){ + Page mpPage = MpUtil.getMpPage(pageParam, UserProtocolVersion.class); + QueryWrapper generator = QueryGenerator.generator(query); + return this.page(mpPage,generator); + } + + /// 查询当前生效版本(已发布) + public Optional findPublished(Long protocolId, String language){ + return this.lambdaQuery() + .eq(UserProtocolVersion::getProtocolId,protocolId) + .eq(UserProtocolVersion::getLanguage,language) + .eq(UserProtocolVersion::getStatus, "PUBLISHED") + .oneOpt(); + } + + /// 查询某协议下所有版本(级联删除/复制用) + public List findAllByProtocol(Long protocolId){ + return this.lambdaQuery() + .eq(UserProtocolVersion::getProtocolId,protocolId) + .list(); + } + + /// 获取下一个版本号(同协议同语言自增) + public Integer nextVersionNo(Long protocolId, String language){ + Long count = this.lambdaQuery() + .eq(UserProtocolVersion::getProtocolId,protocolId) + .eq(UserProtocolVersion::getLanguage,language) + .count(); + return count.intValue() + 1; + } + + /// 将同协议同语言的原已发布版本归档 + public void archivePublished(Long protocolId, String language){ + this.lambdaUpdate() + .eq(UserProtocolVersion::getProtocolId,protocolId) + .eq(UserProtocolVersion::getLanguage,language) + .eq(UserProtocolVersion::getStatus, "PUBLISHED") + .set(UserProtocolVersion::getStatus, "ARCHIVED") + .setIncrBy(MpRealDelEntity::getVersion, 1) + .update(); + } + + /// 发布版本 + public void publish(Long id, Integer versionNo){ + this.lambdaUpdate() + .eq(UserProtocolVersion::getId,id) + .set(UserProtocolVersion::getStatus, "PUBLISHED") + .set(UserProtocolVersion::getVersionNo, versionNo) + .set(UserProtocolVersion::getEffectiveTime, OffsetDateTime.now()) + .setIncrBy(MpRealDelEntity::getVersion, 1) + .update(); + } + + /// 归档版本 + public void archive(Long id){ + this.lambdaUpdate() + .eq(UserProtocolVersion::getId,id) + .set(UserProtocolVersion::getStatus, "ARCHIVED") + .setIncrBy(MpRealDelEntity::getVersion, 1) + .update(); + } + + /// 根据协议ID删除全部版本(级联) + public void deleteByProtocolId(Long protocolId){ + this.lambdaUpdate() + .eq(UserProtocolVersion::getProtocolId,protocolId) + .remove(); + } +} diff --git a/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/dao/protocol/UserProtocolVersionMapper.java b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/dao/protocol/UserProtocolVersionMapper.java new file mode 100644 index 000000000..a1ea5c1ac --- /dev/null +++ b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/dao/protocol/UserProtocolVersionMapper.java @@ -0,0 +1,11 @@ +package cn.daxpay.open.platform.system.dao.protocol; + +import cn.daxpay.open.platform.system.entity.protocol.UserProtocolVersion; +import com.github.yulichang.base.MPJBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/// # 用户协议版本Mapper +/// +@Mapper +public interface UserProtocolVersionMapper extends MPJBaseMapper { +} diff --git a/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/entity/protocol/UserProtocol.java b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/entity/protocol/UserProtocol.java index 2c2cf70e7..7c513b8fa 100644 --- a/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/entity/protocol/UserProtocol.java +++ b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/entity/protocol/UserProtocol.java @@ -30,15 +30,12 @@ public class UserProtocol extends MpBaseEntity implements ToResult { + + /// 协议ID + private Long protocolId; + + /// 语言 + private String language; + + /// 版本号 + private Integer versionNo; + + /// 版本标签 + private String versionLabel; + + /// 标题 + private String title; + + /// 内容(Markdown) + private String content; + + /// 渲染后的HTML + private String contentHtml; + + /// 内容格式 + private String contentFormat; + + /// 状态 + private String status; + + /// 生效时间 + private OffsetDateTime effectiveTime; + + /// 变更说明 + private String summary; + + @Override + public UserProtocolVersionResult toResult() { + return UserProtocolVersionConvert.CONVERT.toResult(this); + } + + public static UserProtocolVersion init(UserProtocolVersionParam param) { + return UserProtocolVersionConvert.CONVERT.toEntity(param); + } +} diff --git a/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/enums/UserProtocolVersionStatusEnum.java b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/enums/UserProtocolVersionStatusEnum.java new file mode 100644 index 000000000..9287afe23 --- /dev/null +++ b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/enums/UserProtocolVersionStatusEnum.java @@ -0,0 +1,36 @@ +package cn.daxpay.open.platform.system.enums; + +import cn.daxpay.open.platform.core.exception.BizException; +import cn.daxpay.open.platform.core.i18n.I18nSupport; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; + +/// 用户协议版本状态 +@Getter +@RequiredArgsConstructor +public enum UserProtocolVersionStatusEnum implements I18nSupport { + + /// 草稿 + DRAFT("DRAFT"), + /// 已发布 + PUBLISHED("PUBLISHED"), + /// 已归档 + ARCHIVED("ARCHIVED"); + + private final String code; + + @Override + public String getI18nPrefix() { + return "enum.user_protocol_status"; + } + + public static UserProtocolVersionStatusEnum findByCode(String code) { + return Arrays.stream(values()) + .filter(e -> e.getCode().equalsIgnoreCase(code)) + .findFirst() + // 通用: 未知的用户协议版本状态 + .orElseThrow(() -> new BizException("error.common.enumUnknown", "UserProtocolVersionStatus")); + } +} diff --git a/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/param/protocol/UserProtocolParam.java b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/param/protocol/UserProtocolParam.java index 75e6a2a5c..048616e11 100644 --- a/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/param/protocol/UserProtocolParam.java +++ b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/param/protocol/UserProtocolParam.java @@ -40,13 +40,9 @@ public class UserProtocolParam { @Schema(description = "端类型") private String clientType; - /// 内容格式 - @Schema(description = "内容格式") - private String contentFormat; - - /// 内容 - @NotBlank(message = "{validation.field.content.notBlank}") - @Schema(description = "内容") - private String content; + /// 默认语言 + @NotBlank(message = "{validation.field.language.notBlank}") + @Schema(description = "默认语言") + private String defaultLanguage; } diff --git a/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/param/protocol/UserProtocolVersionParam.java b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/param/protocol/UserProtocolVersionParam.java new file mode 100644 index 000000000..78b9150bc --- /dev/null +++ b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/param/protocol/UserProtocolVersionParam.java @@ -0,0 +1,58 @@ +package cn.daxpay.open.platform.system.param.protocol; + +import cn.daxpay.open.platform.core.validation.ValidationGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Null; +import lombok.Data; +import lombok.experimental.Accessors; + +/// # 用户协议版本 +/// +@Data +@Accessors(chain = true) +@Schema(title = "用户协议版本") +public class UserProtocolVersionParam { + + @Null(message = "{validation.field.id.mustBeNullOnAdd}", groups = ValidationGroup.add.class) + @NotNull(message = "{validation.field.id.notNull}", groups = ValidationGroup.edit.class) + @Schema(description = "主键") + private Long id; + + /// 协议ID + @NotNull(message = "{validation.field.protocolId.notNull}") + @Schema(description = "协议ID") + private Long protocolId; + + /// 语言 + @NotBlank(message = "{validation.field.language.notBlank}") + @Schema(description = "语言") + private String language; + + /// 版本标签 + @Schema(description = "版本标签") + private String versionLabel; + + /// 标题 + @NotBlank(message = "{validation.field.title.notBlank}") + @Schema(description = "标题") + private String title; + + /// 内容 + @NotBlank(message = "{validation.field.content.notBlank}") + @Schema(description = "内容") + private String content; + + /// 渲染后的HTML + @Schema(description = "渲染后的HTML") + private String contentHtml; + + /// 内容格式 + @Schema(description = "内容格式") + private String contentFormat; + + /// 变更说明 + @Schema(description = "变更说明") + private String summary; +} diff --git a/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/param/protocol/UserProtocolVersionQuery.java b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/param/protocol/UserProtocolVersionQuery.java new file mode 100644 index 000000000..e5afa6c44 --- /dev/null +++ b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/param/protocol/UserProtocolVersionQuery.java @@ -0,0 +1,28 @@ +package cn.daxpay.open.platform.system.param.protocol; + +import cn.daxpay.open.platform.common.mybatisplus.query.entity.SortParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/// # 用户协议版本查询 +/// +@EqualsAndHashCode(callSuper = true) +@Data +@Accessors(chain = true) +@Schema(title = "用户协议版本查询") +public class UserProtocolVersionQuery extends SortParam { + + /// 协议ID + @Schema(description = "协议ID") + private Long protocolId; + + /// 语言 + @Schema(description = "语言") + private String language; + + /// 状态 + @Schema(description = "状态") + private String status; +} diff --git a/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/result/protocol/UserProtocolContentResult.java b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/result/protocol/UserProtocolContentResult.java new file mode 100644 index 000000000..ade3ef10f --- /dev/null +++ b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/result/protocol/UserProtocolContentResult.java @@ -0,0 +1,66 @@ +package cn.daxpay.open.platform.system.result.protocol; + +import cn.daxpay.open.platform.core.result.BaseResult; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.time.OffsetDateTime; + +/// # 用户协议内容(对外展示) +/// +@EqualsAndHashCode(callSuper = true) +@Data +@Accessors(chain = true) +@Schema(title = "用户协议内容") +public class UserProtocolContentResult extends BaseResult { + + /// 名称 + @Schema(description = "名称") + private String name; + + /// 显示名称 + @Schema(description = "显示名称") + private String showName; + + /// 类型 + @Schema(description = "类型") + private String type; + + /// 端类型 + @Schema(description = "端类型") + private String clientType; + + /// 语言 + @Schema(description = "语言") + private String language; + + /// 版本号 + @Schema(description = "版本号") + private Integer versionNo; + + /// 版本标签 + @Schema(description = "版本标签") + private String versionLabel; + + /// 标题 + @Schema(description = "标题") + private String title; + + /// 内容 + @Schema(description = "内容") + private String content; + + /// 渲染后的HTML + @Schema(description = "渲染后的HTML") + private String contentHtml; + + /// 内容格式 + @Schema(description = "内容格式") + private String contentFormat; + + /// 生效时间 + @Schema(description = "生效时间") + private OffsetDateTime effectiveTime; +} diff --git a/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/result/protocol/UserProtocolResult.java b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/result/protocol/UserProtocolResult.java index b30737fe8..31505742a 100644 --- a/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/result/protocol/UserProtocolResult.java +++ b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/result/protocol/UserProtocolResult.java @@ -30,16 +30,12 @@ public class UserProtocolResult extends BaseResult { @Schema(description = "端类型") private String clientType; - /// 内容格式 - @Schema(description = "内容格式") - private String contentFormat; - /// 默认协议 @Schema(description = "默认协议") private Boolean defaultProtocol; - /// 内容 - @Schema(description = "内容") - private String content; + /// 默认语言 + @Schema(description = "默认语言") + private String defaultLanguage; } diff --git a/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/result/protocol/UserProtocolVersionResult.java b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/result/protocol/UserProtocolVersionResult.java new file mode 100644 index 000000000..145dd3eaa --- /dev/null +++ b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/result/protocol/UserProtocolVersionResult.java @@ -0,0 +1,62 @@ +package cn.daxpay.open.platform.system.result.protocol; + +import cn.daxpay.open.platform.core.result.BaseResult; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.time.OffsetDateTime; + +/// # 用户协议版本 +/// +@EqualsAndHashCode(callSuper = true) +@Data +@Accessors(chain = true) +@Schema(title = "用户协议版本") +public class UserProtocolVersionResult extends BaseResult { + + /// 协议ID + @Schema(description = "协议ID") + private Long protocolId; + + /// 语言 + @Schema(description = "语言") + private String language; + + /// 版本号 + @Schema(description = "版本号") + private Integer versionNo; + + /// 版本标签 + @Schema(description = "版本标签") + private String versionLabel; + + /// 标题 + @Schema(description = "标题") + private String title; + + /// 内容 + @Schema(description = "内容") + private String content; + + /// 渲染后的HTML + @Schema(description = "渲染后的HTML") + private String contentHtml; + + /// 内容格式 + @Schema(description = "内容格式") + private String contentFormat; + + /// 状态 + @Schema(description = "状态") + private String status; + + /// 生效时间 + @Schema(description = "生效时间") + private OffsetDateTime effectiveTime; + + /// 变更说明 + @Schema(description = "变更说明") + private String summary; +} diff --git a/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/service/protocol/UserProtocolService.java b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/service/protocol/UserProtocolService.java index ef807dfb2..adde7c703 100644 --- a/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/service/protocol/UserProtocolService.java +++ b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/service/protocol/UserProtocolService.java @@ -1,25 +1,31 @@ package cn.daxpay.open.platform.system.service.protocol; -import cn.daxpay.open.platform.system.convert.protocol.UserProtocolConvert; -import cn.daxpay.open.platform.system.dao.protocol.UserProtocolManager; -import cn.daxpay.open.platform.system.entity.protocol.UserProtocol; -import cn.daxpay.open.platform.system.enums.UserProtocolClientTypeEnum; -import cn.daxpay.open.platform.system.enums.UserProtocolContentFormatEnum; -import cn.daxpay.open.platform.system.enums.UserProtocolTypeEnum; -import cn.daxpay.open.platform.system.param.protocol.UserProtocolParam; -import cn.daxpay.open.platform.system.param.protocol.UserProtocolQuery; -import cn.daxpay.open.platform.system.result.protocol.UserProtocolResult; import cn.daxpay.open.platform.common.mybatisplus.util.MpUtil; +import cn.daxpay.open.platform.core.code.CommonCode; import cn.daxpay.open.platform.core.exception.BizException; import cn.daxpay.open.platform.core.exception.DataNotExistException; import cn.daxpay.open.platform.core.rest.param.PageParam; import cn.daxpay.open.platform.core.rest.result.PageResult; +import cn.daxpay.open.platform.system.convert.protocol.UserProtocolConvert; +import cn.daxpay.open.platform.system.dao.protocol.UserProtocolManager; +import cn.daxpay.open.platform.system.dao.protocol.UserProtocolVersionManager; +import cn.daxpay.open.platform.system.entity.protocol.UserProtocol; +import cn.daxpay.open.platform.system.entity.protocol.UserProtocolVersion; +import cn.daxpay.open.platform.system.enums.UserProtocolClientTypeEnum; +import cn.daxpay.open.platform.system.enums.UserProtocolTypeEnum; +import cn.daxpay.open.platform.system.enums.UserProtocolVersionStatusEnum; +import cn.daxpay.open.platform.system.param.protocol.UserProtocolParam; +import cn.daxpay.open.platform.system.param.protocol.UserProtocolQuery; +import cn.daxpay.open.platform.system.result.protocol.UserProtocolContentResult; +import cn.daxpay.open.platform.system.result.protocol.UserProtocolResult; import cn.hutool.core.util.StrUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import cn.daxpay.open.platform.core.code.CommonCode; + +import java.util.HashMap; +import java.util.Map; /// # 用户协议管理服务 /// @@ -28,6 +34,11 @@ import cn.daxpay.open.platform.core.code.CommonCode; @RequiredArgsConstructor public class UserProtocolService { private final UserProtocolManager userProtocolManager; + private final UserProtocolVersionManager userProtocolVersionManager; + private final UserProtocolVersionService userProtocolVersionService; + + /// 默认语言 + private static final String DEFAULT_LANGUAGE = "zh-CN"; /// 分页 public PageResult page(PageParam pageParam, UserProtocolQuery query){ @@ -39,7 +50,7 @@ public class UserProtocolService { public void add(UserProtocolParam param){ this.validateParam(param); var userProtocol = UserProtocol.init(param); - userProtocol.setContentFormat(this.getContentFormat(param.getContentFormat())); + userProtocol.setDefaultLanguage(this.getLanguage(param.getDefaultLanguage())); userProtocolManager.save(userProtocol); } @@ -50,11 +61,12 @@ public class UserProtocolService { // 系统: 协议不存在 .orElseThrow(() -> new DataNotExistException("error.system.protocol.notExist")); UserProtocolConvert.CONVERT.copy(param, userProtocol); - userProtocol.setContentFormat(this.getContentFormat(param.getContentFormat())); + userProtocol.setDefaultLanguage(this.getLanguage(param.getDefaultLanguage())); userProtocolManager.updateById(userProtocol); } - /// 删除 + /// 删除(级联删除版本) + @Transactional(rollbackFor = Exception.class) public void delete(Long id){ // 默认不可被删除 var userProtocol = userProtocolManager.findById(id) @@ -64,6 +76,8 @@ public class UserProtocolService { // 默认协议不可删除 throw new BizException(CommonCode.FAIL_CODE, "error.system.protocol.defaultCannotDelete"); } + // 级联删除版本 + userProtocolVersionManager.deleteByProtocolId(id); userProtocolManager.deleteById(id); } @@ -75,14 +89,33 @@ public class UserProtocolService { .orElseThrow(() -> new DataNotExistException("error.system.protocol.notExist")); } - /// 根据分类查询默认协议 - public UserProtocolResult findDefault(String type, String clientType){ + /// 根据分类查询默认协议内容(对外展示), language 为空时回退到协议默认语言 + public UserProtocolContentResult findDefault(String type, String clientType, String language){ UserProtocolTypeEnum.findByCode(type); UserProtocolClientTypeEnum.findByCode(clientType); - return userProtocolManager.findDefault(type, clientType) - .map(UserProtocol::toResult) + var protocol = userProtocolManager.findDefault(type, clientType) // 系统: 协议不存在 .orElseThrow(() -> new DataNotExistException("error.system.protocol.notExist")); + String lang = StrUtil.isBlank(language) ? protocol.getDefaultLanguage() : language; + var version = userProtocolVersionManager.findPublished(protocol.getId(), lang).orElse(null); + // 组装对外内容结果 + var result = new UserProtocolContentResult(); + result.setId(protocol.getId()); + result.setName(protocol.getName()); + result.setShowName(protocol.getShowName()); + result.setType(protocol.getType()); + result.setClientType(protocol.getClientType()); + result.setLanguage(lang); + if (version != null){ + result.setVersionNo(version.getVersionNo()); + result.setVersionLabel(version.getVersionLabel()); + result.setTitle(version.getTitle()); + result.setContent(version.getContent()); + result.setContentHtml(version.getContentHtml()); + result.setContentFormat(version.getContentFormat()); + result.setEffectiveTime(version.getEffectiveTime()); + } + return result; } /// 设置默认协议 @@ -103,16 +136,47 @@ public class UserProtocolService { userProtocolManager.cancelDefault(id); } + /// 复制协议到目标端(含每种语言的当前生效版本) + @Transactional(rollbackFor = Exception.class) + public Long copyToClient(Long sourceProtocolId, String targetClientType){ + var source = userProtocolManager.findById(sourceProtocolId) + // 系统: 协议不存在 + .orElseThrow(() -> new DataNotExistException("error.system.protocol.notExist")); + UserProtocolClientTypeEnum.findByCode(targetClientType); + if (targetClientType.equalsIgnoreCase(source.getClientType())){ + // 系统: 目标端类型与源端一致 + throw new BizException(CommonCode.FAIL_CODE, "error.system.protocol.sameClientType"); + } + // 创建目标协议 + var target = new UserProtocol(); + target.setName(source.getName()); + target.setShowName(source.getShowName()); + target.setType(source.getType()); + target.setClientType(targetClientType); + target.setDefaultProtocol(false); + target.setDefaultLanguage(source.getDefaultLanguage()); + userProtocolManager.save(target); + // 复制每种语言的已发布版本(同语言仅一条已发布) + var versions = userProtocolVersionService.findAllByProtocol(source.getId()); + Map publishedByLang = new HashMap<>(); + for (var v : versions){ + if (UserProtocolVersionStatusEnum.PUBLISHED.getCode().equals(v.getStatus())){ + publishedByLang.put(v.getLanguage(), v); + } + } + for (var v : publishedByLang.values()){ + userProtocolVersionService.copyVersion(v, target.getId()); + } + return target.getId(); + } + private void validateParam(UserProtocolParam param) { UserProtocolTypeEnum.findByCode(param.getType()); UserProtocolClientTypeEnum.findByCode(param.getClientType()); - this.getContentFormat(param.getContentFormat()); + this.getLanguage(param.getDefaultLanguage()); } - private String getContentFormat(String contentFormat) { - if (StrUtil.isBlank(contentFormat)) { - return UserProtocolContentFormatEnum.MARKDOWN.getCode(); - } - return UserProtocolContentFormatEnum.findByCode(contentFormat).getCode(); + private String getLanguage(String language) { + return StrUtil.isBlank(language) ? DEFAULT_LANGUAGE : language; } } diff --git a/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/service/protocol/UserProtocolVersionService.java b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/service/protocol/UserProtocolVersionService.java new file mode 100644 index 000000000..26de8044f --- /dev/null +++ b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/service/protocol/UserProtocolVersionService.java @@ -0,0 +1,145 @@ +package cn.daxpay.open.platform.system.service.protocol; + +import cn.daxpay.open.platform.common.mybatisplus.util.MpUtil; +import cn.daxpay.open.platform.core.code.CommonCode; +import cn.daxpay.open.platform.core.exception.BizException; +import cn.daxpay.open.platform.core.exception.DataNotExistException; +import cn.daxpay.open.platform.core.rest.param.PageParam; +import cn.daxpay.open.platform.core.rest.result.PageResult; +import cn.daxpay.open.platform.system.convert.protocol.UserProtocolVersionConvert; +import cn.daxpay.open.platform.system.dao.protocol.UserProtocolVersionManager; +import cn.daxpay.open.platform.system.entity.protocol.UserProtocolVersion; +import cn.daxpay.open.platform.system.enums.UserProtocolContentFormatEnum; +import cn.daxpay.open.platform.system.enums.UserProtocolVersionStatusEnum; +import cn.daxpay.open.platform.system.param.protocol.UserProtocolVersionParam; +import cn.daxpay.open.platform.system.param.protocol.UserProtocolVersionQuery; +import cn.daxpay.open.platform.system.result.protocol.UserProtocolVersionResult; +import cn.hutool.core.util.StrUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/// # 用户协议版本管理服务 +/// +@Slf4j +@Service +@RequiredArgsConstructor +public class UserProtocolVersionService { + private final UserProtocolVersionManager userProtocolVersionManager; + + /// 分页 + public PageResult page(PageParam pageParam, UserProtocolVersionQuery query){ + return MpUtil.toPageResult(userProtocolVersionManager.page(pageParam,query)); + } + + /// 创建草稿 + public void add(UserProtocolVersionParam param){ + var version = UserProtocolVersion.init(param); + version.setContentFormat(this.getContentFormat(param.getContentFormat())); + version.setStatus(UserProtocolVersionStatusEnum.DRAFT.getCode()); + userProtocolVersionManager.save(version); + } + + /// 更新草稿内容(仅草稿可编辑) + public void update(UserProtocolVersionParam param){ + var version = userProtocolVersionManager.findById(param.getId()) + // 系统: 协议版本不存在 + .orElseThrow(() -> new DataNotExistException("error.system.protocol.versionNotExist")); + if (!UserProtocolVersionStatusEnum.DRAFT.getCode().equals(version.getStatus())) { + // 系统: 仅草稿状态可编辑 + throw new BizException(CommonCode.FAIL_CODE, "error.system.protocol.draftOnlyCanEdit"); + } + UserProtocolVersionConvert.CONVERT.copy(param, version); + version.setContentFormat(this.getContentFormat(param.getContentFormat())); + userProtocolVersionManager.updateById(version); + } + + /// 删除(仅草稿可删除) + public void delete(Long id){ + var version = userProtocolVersionManager.findById(id) + // 系统: 协议版本不存在 + .orElseThrow(() -> new DataNotExistException("error.system.protocol.versionNotExist")); + if (!UserProtocolVersionStatusEnum.DRAFT.getCode().equals(version.getStatus())) { + // 系统: 仅草稿状态可删除 + throw new BizException(CommonCode.FAIL_CODE, "error.system.protocol.draftOnlyCanDelete"); + } + userProtocolVersionManager.deleteById(id); + } + + /// 详情 + public UserProtocolVersionResult findById(Long id){ + return userProtocolVersionManager.findById(id) + .map(UserProtocolVersion::toResult) + // 系统: 协议版本不存在 + .orElseThrow(() -> new DataNotExistException("error.system.protocol.versionNotExist")); + } + + /// 发布版本: 草稿 -> 已发布, 同协议同语言原已发布 -> 归档 + @Transactional(rollbackFor = Exception.class) + public void publish(Long id){ + var version = userProtocolVersionManager.findById(id) + // 系统: 协议版本不存在 + .orElseThrow(() -> new DataNotExistException("error.system.protocol.versionNotExist")); + if (!UserProtocolVersionStatusEnum.DRAFT.getCode().equals(version.getStatus())) { + // 系统: 仅草稿状态可发布 + throw new BizException(CommonCode.FAIL_CODE, "error.system.protocol.draftOnlyCanPublish"); + } + // 分配版本号 + Integer versionNo = userProtocolVersionManager.nextVersionNo(version.getProtocolId(), version.getLanguage()); + // 旧生效版本归档 + userProtocolVersionManager.archivePublished(version.getProtocolId(), version.getLanguage()); + // 发布当前版本 + userProtocolVersionManager.publish(id, versionNo); + } + + /// 归档版本(已发布 -> 归档) + public void archive(Long id){ + var version = userProtocolVersionManager.findById(id) + // 系统: 协议版本不存在 + .orElseThrow(() -> new DataNotExistException("error.system.protocol.versionNotExist")); + if (!UserProtocolVersionStatusEnum.PUBLISHED.getCode().equals(version.getStatus())) { + // 系统: 仅已发布状态可归档 + throw new BizException(CommonCode.FAIL_CODE, "error.system.protocol.publishedOnlyCanArchive"); + } + userProtocolVersionManager.archive(id); + } + + /// 根据协议ID和语言查询当前生效版本 + public UserProtocolVersionResult findPublished(Long protocolId, String language){ + return userProtocolVersionManager.findPublished(protocolId, language) + .map(UserProtocolVersion::toResult) + .orElse(null); + } + + /// 复制版本到目标协议(用于复制到其他端, 直接以已发布状态写入) + @Transactional(rollbackFor = Exception.class) + public void copyVersion(UserProtocolVersion source, Long targetProtocolId){ + var copy = new UserProtocolVersion(); + copy.setProtocolId(targetProtocolId); + copy.setLanguage(source.getLanguage()); + copy.setVersionNo(userProtocolVersionManager.nextVersionNo(targetProtocolId, source.getLanguage())); + copy.setVersionLabel(source.getVersionLabel()); + copy.setTitle(source.getTitle()); + copy.setContent(source.getContent()); + copy.setContentHtml(source.getContentHtml()); + copy.setContentFormat(source.getContentFormat()); + copy.setStatus(UserProtocolVersionStatusEnum.PUBLISHED.getCode()); + copy.setSummary(source.getSummary()); + userProtocolVersionManager.save(copy); + } + + /// 根据协议ID查询全部版本(复制时用) + public List findAllByProtocol(Long protocolId){ + return userProtocolVersionManager.findAllByProtocol(protocolId); + } + + private String getContentFormat(String contentFormat) { + if (StrUtil.isBlank(contentFormat)) { + return UserProtocolContentFormatEnum.MARKDOWN.getCode(); + } + return UserProtocolContentFormatEnum.findByCode(contentFormat).getCode(); + } +}