Commit 1676f671d8170144ec64acfe9ef451913ed0dd47
1 parent
6bc1f8d0
【新增】AI:聊天角色管理(50%)
Showing
19 changed files
with
353 additions
and
572 deletions
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/ErrorCodeConstants.java
| @@ -18,8 +18,9 @@ public interface ErrorCodeConstants { | @@ -18,8 +18,9 @@ public interface ErrorCodeConstants { | ||
| 18 | ErrorCode CHAT_MODAL_NOT_EXIST = new ErrorCode(1_040_001_000, "AI 模型不存在!"); | 18 | ErrorCode CHAT_MODAL_NOT_EXIST = new ErrorCode(1_040_001_000, "AI 模型不存在!"); |
| 19 | ErrorCode CHAT_MODAL_DISABLE = new ErrorCode(1_040_001_001, "AI 模型({})已禁用!"); | 19 | ErrorCode CHAT_MODAL_DISABLE = new ErrorCode(1_040_001_001, "AI 模型({})已禁用!"); |
| 20 | 20 | ||
| 21 | - // ErrorCode AI_MODAL_CONFIG_PARAMS_INCORRECT = new ErrorCode(1_022_000_081, "AI 模型 config 参数不正确! {} "); | ||
| 22 | -// ErrorCode AI_MODAL_PLATFORM_PARAMS_INCORRECT = new ErrorCode(1_022_000_083, "AI 平台参数不正确! {} "); | 21 | + // ========== API 聊天模型 1-040-002-000 ========== |
| 22 | + ErrorCode CHAT_ROLE_NOT_EXISTS = new ErrorCode(1_040_002_000, "AI 聊天角色不存在"); | ||
| 23 | + ErrorCode CHAT_ROLE_DISABLE = new ErrorCode(1_040_001_001, "AI 聊天角色({})已禁用!"); | ||
| 23 | 24 | ||
| 24 | // conversation | 25 | // conversation |
| 25 | 26 | ||
| @@ -31,14 +32,7 @@ public interface ErrorCodeConstants { | @@ -31,14 +32,7 @@ public interface ErrorCodeConstants { | ||
| 31 | ErrorCode AI_MIDJOURNEY_OPERATION_NOT_EXISTS = new ErrorCode(1_022_000_040, "midjourney 操作不存在!"); | 32 | ErrorCode AI_MIDJOURNEY_OPERATION_NOT_EXISTS = new ErrorCode(1_022_000_040, "midjourney 操作不存在!"); |
| 32 | ErrorCode AI_MIDJOURNEY_MESSAGE_ID_INCORRECT = new ErrorCode(1_022_000_040, "midjourney message id 不正确!"); | 33 | ErrorCode AI_MIDJOURNEY_MESSAGE_ID_INCORRECT = new ErrorCode(1_022_000_040, "midjourney message id 不正确!"); |
| 33 | 34 | ||
| 34 | - // role | ||
| 35 | - | ||
| 36 | - ErrorCode AI_CHAT_ROLE_NOT_EXIST = new ErrorCode(1_022_000_060, "AI 角色不存在!"); | ||
| 37 | - ErrorCode AI_CHAT_ROLE_NOT_PUBLIC = new ErrorCode(1_022_000_060, "AI 角色未公开!"); | ||
| 38 | - | ||
| 39 | // chat | 35 | // chat |
| 40 | - | ||
| 41 | ErrorCode AI_CHAT_MESSAGE_NOT_EXIST = new ErrorCode(1_022_000_100, "AI 提问的 MessageId 不存在!"); | 36 | ErrorCode AI_CHAT_MESSAGE_NOT_EXIST = new ErrorCode(1_022_000_100, "AI 提问的 MessageId 不存在!"); |
| 42 | 37 | ||
| 43 | - | ||
| 44 | } | 38 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatRoleController.java
| 1 | package cn.iocoder.yudao.module.ai.controller.admin.model; | 1 | package cn.iocoder.yudao.module.ai.controller.admin.model; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; | 4 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 5 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | ||
| 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 6 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.role.*; | ||
| 6 | -import cn.iocoder.yudao.module.ai.service.AiChatRoleService; | 7 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 8 | +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.role.AiChatRolePageReqVO; | ||
| 9 | +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.role.AiChatRoleRespVO; | ||
| 10 | +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.role.AiChatRoleSaveReqVO; | ||
| 11 | +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; | ||
| 12 | +import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService; | ||
| 7 | import io.swagger.v3.oas.annotations.Operation; | 13 | import io.swagger.v3.oas.annotations.Operation; |
| 14 | +import io.swagger.v3.oas.annotations.Parameter; | ||
| 8 | import io.swagger.v3.oas.annotations.tags.Tag; | 15 | import io.swagger.v3.oas.annotations.tags.Tag; |
| 9 | import jakarta.annotation.Resource; | 16 | import jakarta.annotation.Resource; |
| 10 | -import lombok.AllArgsConstructor; | ||
| 11 | -import lombok.extern.slf4j.Slf4j; | 17 | +import jakarta.servlet.http.HttpServletResponse; |
| 18 | +import jakarta.validation.Valid; | ||
| 19 | +import org.springframework.security.access.prepost.PreAuthorize; | ||
| 12 | import org.springframework.validation.annotation.Validated; | 20 | import org.springframework.validation.annotation.Validated; |
| 13 | import org.springframework.web.bind.annotation.*; | 21 | import org.springframework.web.bind.annotation.*; |
| 14 | 22 | ||
| 23 | +import java.io.IOException; | ||
| 24 | +import java.util.List; | ||
| 25 | + | ||
| 26 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | ||
| 27 | + | ||
| 15 | @Tag(name = "管理后台 - AI 聊天角色") | 28 | @Tag(name = "管理后台 - AI 聊天角色") |
| 16 | @RestController | 29 | @RestController |
| 17 | @RequestMapping("/ai/chat-role") | 30 | @RequestMapping("/ai/chat-role") |
| @@ -21,42 +34,55 @@ public class AiChatRoleController { | @@ -21,42 +34,55 @@ public class AiChatRoleController { | ||
| 21 | @Resource | 34 | @Resource |
| 22 | private AiChatRoleService chatRoleService; | 35 | private AiChatRoleService chatRoleService; |
| 23 | 36 | ||
| 24 | - @Operation(summary = "chat角色 - 角色列表") | ||
| 25 | - @GetMapping("/list") | ||
| 26 | - public PageResult<AiChatRoleListRespVO> list(@Validated @ModelAttribute AiChatRoleListReqVO req) { | ||
| 27 | - return chatRoleService.list(req); | ||
| 28 | - } | 37 | + // TODO 芋艿:我的分页 |
| 29 | 38 | ||
| 30 | - @Operation(summary = "chat角色 - 添加") | ||
| 31 | - @PutMapping("/add") | ||
| 32 | - public CommonResult<Void> add(@Validated @RequestBody AiChatRoleAddReqVO req) { | ||
| 33 | - chatRoleService.add(req); | ||
| 34 | - return CommonResult.success(null); | ||
| 35 | - } | 39 | + // TODO 芋艿:我的新增 |
| 40 | + | ||
| 41 | + // TODO 芋艿:我的修改 | ||
| 42 | + | ||
| 43 | + // TODO 芋艿:我的删除 | ||
| 36 | 44 | ||
| 37 | - @Operation(summary = "chat角色 - 修改") | ||
| 38 | - @PostMapping("/update") | ||
| 39 | - public CommonResult<Void> update(@Validated @RequestBody AiChatRoleUpdateReqVO req) { | ||
| 40 | - chatRoleService.update(req); | ||
| 41 | - return CommonResult.success(null); | 45 | + // ========== 角色管理 ========== |
| 46 | + | ||
| 47 | + @PostMapping("/create") | ||
| 48 | + @Operation(summary = "创建聊天角色") | ||
| 49 | + @PreAuthorize("@ss.hasPermission('ai:chat-role:create')") | ||
| 50 | + public CommonResult<Long> createChatRole(@Valid @RequestBody AiChatRoleSaveReqVO createReqVO) { | ||
| 51 | + return success(chatRoleService.createChatRole(createReqVO)); | ||
| 42 | } | 52 | } |
| 43 | 53 | ||
| 44 | - @Operation(summary = "chat角色 - 修改可见性") | ||
| 45 | - @PostMapping("/update-public-status") | ||
| 46 | - public CommonResult<Void> updatePublicStatus(@Validated @RequestBody AiChatRoleUpdatePublicStatusReqVO req) { | ||
| 47 | - chatRoleService.updatePublicStatus(req); | ||
| 48 | - return CommonResult.success(null); | 54 | + @PutMapping("/update") |
| 55 | + @Operation(summary = "更新聊天角色") | ||
| 56 | + @PreAuthorize("@ss.hasPermission('ai:chat-role:update')") | ||
| 57 | + public CommonResult<Boolean> updateChatRole(@Valid @RequestBody AiChatRoleSaveReqVO updateReqVO) { | ||
| 58 | + chatRoleService.updateChatRole(updateReqVO); | ||
| 59 | + return success(true); | ||
| 49 | } | 60 | } |
| 50 | 61 | ||
| 51 | - @Operation(summary = "chat角色 - 删除") | ||
| 52 | @DeleteMapping("/delete") | 62 | @DeleteMapping("/delete") |
| 53 | - public CommonResult<Void> delete(@RequestParam("id") Long id) { | ||
| 54 | - chatRoleService.delete(id); | ||
| 55 | - return CommonResult.success(null); | 63 | + @Operation(summary = "删除聊天角色") |
| 64 | + @Parameter(name = "id", description = "编号", required = true) | ||
| 65 | + @PreAuthorize("@ss.hasPermission('ai:chat-role:delete')") | ||
| 66 | + public CommonResult<Boolean> deleteChatRole(@RequestParam("id") Long id) { | ||
| 67 | + chatRoleService.deleteChatRole(id); | ||
| 68 | + return success(true); | ||
| 56 | } | 69 | } |
| 57 | 70 | ||
| 58 | - // ========== 角色管理 ========== | ||
| 59 | - | 71 | + @GetMapping("/get") |
| 72 | + @Operation(summary = "获得聊天角色") | ||
| 73 | + @Parameter(name = "id", description = "编号", required = true, example = "1024") | ||
| 74 | + @PreAuthorize("@ss.hasPermission('ai:chat-role:query')") | ||
| 75 | + public CommonResult<AiChatRoleRespVO> getChatRole(@RequestParam("id") Long id) { | ||
| 76 | + AiChatRoleDO chatRole = chatRoleService.getChatRole(id); | ||
| 77 | + return success(BeanUtils.toBean(chatRole, AiChatRoleRespVO.class)); | ||
| 78 | + } | ||
| 60 | 79 | ||
| 80 | + @GetMapping("/page") | ||
| 81 | + @Operation(summary = "获得聊天角色分页") | ||
| 82 | + @PreAuthorize("@ss.hasPermission('ai:chat-role:query')") | ||
| 83 | + public CommonResult<PageResult<AiChatRoleRespVO>> getChatRolePage(@Valid AiChatRolePageReqVO pageReqVO) { | ||
| 84 | + PageResult<AiChatRoleDO> pageResult = chatRoleService.getChatRolePage(pageReqVO); | ||
| 85 | + return success(BeanUtils.toBean(pageResult, AiChatRoleRespVO.class)); | ||
| 86 | + } | ||
| 61 | 87 | ||
| 62 | } | 88 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/role/AiChatRoleAddReqVO.java deleted
| 1 | -package cn.iocoder.yudao.module.ai.controller.admin.model.vo.role; | ||
| 2 | - | ||
| 3 | -import io.swagger.v3.oas.annotations.media.Schema; | ||
| 4 | -import jakarta.validation.constraints.NotNull; | ||
| 5 | -import lombok.Data; | ||
| 6 | -import lombok.experimental.Accessors; | ||
| 7 | - | ||
| 8 | -/** | ||
| 9 | - * chat 角色列表 | ||
| 10 | - * | ||
| 11 | - * @fansili | ||
| 12 | - * @since v1.0 | ||
| 13 | - */ | ||
| 14 | -@Data | ||
| 15 | -@Accessors(chain = true) | ||
| 16 | -public class AiChatRoleAddReqVO { | ||
| 17 | - | ||
| 18 | - @NotNull | ||
| 19 | - @Schema(description = "角色名,角色的显示名称") | ||
| 20 | - private String name; | ||
| 21 | - | ||
| 22 | - @Schema(description = "头像") | ||
| 23 | - private String avatar; | ||
| 24 | - | ||
| 25 | - @NotNull | ||
| 26 | - @Schema(description = "分类,角色所属的类别,如娱乐、创作等") | ||
| 27 | - private String category; | ||
| 28 | - | ||
| 29 | - @NotNull | ||
| 30 | - @Schema(description = "角色描述") | ||
| 31 | - private String description; | ||
| 32 | - | ||
| 33 | - @Schema(description = "角色欢迎语") | ||
| 34 | - private String welcomeMessage; | ||
| 35 | - | ||
| 36 | - @NotNull | ||
| 37 | - @Schema(description = "角色设定(消息)") | ||
| 38 | - private String systemMessage; | ||
| 39 | - | ||
| 40 | - @NotNull | ||
| 41 | - @Schema(description = "模型编号") | ||
| 42 | - private Long modelId; | ||
| 43 | - | ||
| 44 | - @NotNull | ||
| 45 | - @Schema(description = "开启状态 0、open 1、close") | ||
| 46 | - private Boolean publicStatus; | ||
| 47 | - | ||
| 48 | - @NotNull | ||
| 49 | - @Schema(description = "排序") | ||
| 50 | - private Integer sort; | ||
| 51 | - | ||
| 52 | -} |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/role/AiChatRoleListReqVO.java deleted
| 1 | -package cn.iocoder.yudao.module.ai.controller.admin.model.vo.role; | ||
| 2 | - | ||
| 3 | -import cn.iocoder.yudao.framework.common.pojo.PageParam; | ||
| 4 | -import io.swagger.v3.oas.annotations.media.Schema; | ||
| 5 | -import lombok.Data; | ||
| 6 | -import lombok.experimental.Accessors; | ||
| 7 | - | ||
| 8 | -/** | ||
| 9 | - * chat 角色列表 | ||
| 10 | - * | ||
| 11 | - * @fansili | ||
| 12 | - * @since v1.0 | ||
| 13 | - */ | ||
| 14 | -@Data | ||
| 15 | -@Accessors(chain = true) | ||
| 16 | -public class AiChatRoleListReqVO extends PageParam { | ||
| 17 | - | ||
| 18 | - @Schema(description = "查询") | ||
| 19 | - private String search; | ||
| 20 | -} |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/role/AiChatRoleListRespVO.java deleted
| 1 | -package cn.iocoder.yudao.module.ai.controller.admin.model.vo.role; | ||
| 2 | - | ||
| 3 | -import io.swagger.v3.oas.annotations.media.Schema; | ||
| 4 | -import lombok.Data; | ||
| 5 | -import lombok.experimental.Accessors; | ||
| 6 | - | ||
| 7 | -/** | ||
| 8 | - * chat 角色列表 | ||
| 9 | - * | ||
| 10 | - * @fansili | ||
| 11 | - * @since v1.0 | ||
| 12 | - */ | ||
| 13 | -@Data | ||
| 14 | -@Accessors(chain = true) | ||
| 15 | -public class AiChatRoleListRespVO { | ||
| 16 | - | ||
| 17 | - @Schema(description = "编号", example = "1") | ||
| 18 | - private Long id; | ||
| 19 | - | ||
| 20 | - @Schema(description = "角色名称", example = "小红书写作") | ||
| 21 | - private String name; | ||
| 22 | - | ||
| 23 | - @Schema(description = "角色头像", example = "http://...") | ||
| 24 | - private String avatar; | ||
| 25 | - | ||
| 26 | - @Schema(description = "角色分类", example = "writing") | ||
| 27 | - private String category; | ||
| 28 | - | ||
| 29 | - @Schema(description = "角色描述", example = "角色描述") | ||
| 30 | - private String description; | ||
| 31 | - | ||
| 32 | - @Schema(description = "角色欢迎语", example = "欢迎...") | ||
| 33 | - private String welcomeMessage; | ||
| 34 | - | ||
| 35 | - @Schema(description = "角色设定(消息)", example = "你是拥有丰富的小红书写作经验作者xxxx") | ||
| 36 | - private String systemMessage; | ||
| 37 | - | ||
| 38 | - @Schema(description = "用户编号", example = "1") | ||
| 39 | - private Long userId; | ||
| 40 | - | ||
| 41 | - @Schema(description = "模型编号", example = "1") | ||
| 42 | - private Long modelId; | ||
| 43 | - | ||
| 44 | - @Schema(description = "是否公开 true - 公开;false - 私有", example = "true") | ||
| 45 | - private Boolean publicStatus; | ||
| 46 | - | ||
| 47 | - @Schema(description = "排序值 asc", example = "1") | ||
| 48 | - private Integer sort; | ||
| 49 | - | ||
| 50 | - @Schema(description = "状态 0、开启 1、关闭", example = "1") | ||
| 51 | - private Integer status; | ||
| 52 | -} |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/role/AiChatRolePageReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.role; | ||
| 2 | + | ||
| 3 | +import lombok.*; | ||
| 4 | +import java.util.*; | ||
| 5 | +import io.swagger.v3.oas.annotations.media.Schema; | ||
| 6 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | ||
| 7 | +import org.springframework.format.annotation.DateTimeFormat; | ||
| 8 | +import java.time.LocalDateTime; | ||
| 9 | + | ||
| 10 | +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | ||
| 11 | + | ||
| 12 | +@Schema(description = "管理后台 - AI 聊天角色分页 Request VO") | ||
| 13 | +@Data | ||
| 14 | +@EqualsAndHashCode(callSuper = true) | ||
| 15 | +@ToString(callSuper = true) | ||
| 16 | +public class AiChatRolePageReqVO extends PageParam { | ||
| 17 | + | ||
| 18 | + @Schema(description = "角色名称", example = "李四") | ||
| 19 | + private String name; | ||
| 20 | + | ||
| 21 | + @Schema(description = "角色类别", example = "创作") | ||
| 22 | + private String category; | ||
| 23 | + | ||
| 24 | + @Schema(description = "是否公开", example = "1") | ||
| 25 | + private Boolean publicStatus; | ||
| 26 | + | ||
| 27 | +} | ||
| 0 | \ No newline at end of file | 28 | \ No newline at end of file |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/role/AiChatRoleRespVO.java
| @@ -2,36 +2,50 @@ package cn.iocoder.yudao.module.ai.controller.admin.model.vo.role; | @@ -2,36 +2,50 @@ package cn.iocoder.yudao.module.ai.controller.admin.model.vo.role; | ||
| 2 | 2 | ||
| 3 | import io.swagger.v3.oas.annotations.media.Schema; | 3 | import io.swagger.v3.oas.annotations.media.Schema; |
| 4 | import lombok.Data; | 4 | import lombok.Data; |
| 5 | -import lombok.experimental.Accessors; | ||
| 6 | - | ||
| 7 | -/** | ||
| 8 | - * chat 角色 | ||
| 9 | - * | ||
| 10 | - * @fansili | ||
| 11 | - * @since v1.0 | ||
| 12 | - */ | 5 | + |
| 6 | +import java.time.LocalDateTime; | ||
| 7 | + | ||
| 8 | +@Schema(description = "管理后台 - AI 聊天角色 Response VO") | ||
| 13 | @Data | 9 | @Data |
| 14 | -@Accessors(chain = true) | ||
| 15 | public class AiChatRoleRespVO { | 10 | public class AiChatRoleRespVO { |
| 16 | 11 | ||
| 17 | - @Schema(description = "id") | 12 | + @Schema(description = "角色编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "32746") |
| 18 | private Long id; | 13 | private Long id; |
| 19 | 14 | ||
| 20 | - @Schema(description = "用户id") | 15 | + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "9442") |
| 21 | private Long userId; | 16 | private Long userId; |
| 22 | 17 | ||
| 23 | - @Schema(description = "角色名字") | 18 | + @Schema(description = "模型编号", example = "17640") |
| 19 | + private Long modelId; | ||
| 20 | + | ||
| 21 | + @Schema(description = "角色名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四") | ||
| 24 | private String name; | 22 | private String name; |
| 25 | 23 | ||
| 26 | - @Schema(description = "角色介绍,详细描述角色的功能或用途") | ||
| 27 | - private String introduce; | 24 | + @Schema(description = "角色头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") |
| 25 | + private String avatar; | ||
| 26 | + | ||
| 27 | + @Schema(description = "角色类别", requiredMode = Schema.RequiredMode.REQUIRED, example = "创作") | ||
| 28 | + private String category; | ||
| 29 | + | ||
| 30 | + @Schema(description = "角色排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | ||
| 31 | + private Integer sort; | ||
| 32 | + | ||
| 33 | + @Schema(description = "角色描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "你说的对") | ||
| 34 | + private String description; | ||
| 35 | + | ||
| 36 | + @Schema(description = "角色欢迎语", requiredMode = Schema.RequiredMode.REQUIRED) | ||
| 37 | + private String welcomeMessage; | ||
| 38 | + | ||
| 39 | + @Schema(description = "角色上下文", requiredMode = Schema.RequiredMode.REQUIRED) | ||
| 40 | + private String systemMessage; | ||
| 41 | + | ||
| 42 | + @Schema(description = "是否公开", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | ||
| 43 | + private Boolean publicStatus; | ||
| 28 | 44 | ||
| 29 | - @Schema(description = "分类,角色所属的类别,如娱乐、创作等") | ||
| 30 | - private String classify; | 45 | + @Schema(description = "状态", example = "1") |
| 46 | + private Integer status; | ||
| 31 | 47 | ||
| 32 | - @Schema(description = "状态 open、close") | ||
| 33 | - private String enable; | 48 | + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) |
| 49 | + private LocalDateTime createTime; | ||
| 34 | 50 | ||
| 35 | - @Schema(description = "角色的使用次数统计") | ||
| 36 | - private Integer useCount; | ||
| 37 | -} | 51 | +} |
| 38 | \ No newline at end of file | 52 | \ No newline at end of file |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/role/AiChatRoleSaveReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.role; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||
| 4 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | ||
| 5 | +import io.swagger.v3.oas.annotations.media.Schema; | ||
| 6 | +import lombok.*; | ||
| 7 | +import java.util.*; | ||
| 8 | +import jakarta.validation.constraints.*; | ||
| 9 | + | ||
| 10 | +@Schema(description = "管理后台 - AI 聊天角色新增/修改 Request VO") | ||
| 11 | +@Data | ||
| 12 | +public class AiChatRoleSaveReqVO { | ||
| 13 | + | ||
| 14 | + @Schema(description = "角色编号", example = "32746") | ||
| 15 | + private Long id; | ||
| 16 | + | ||
| 17 | + @Schema(description = "模型编号", example = "17640") | ||
| 18 | + private Long modelId; | ||
| 19 | + | ||
| 20 | + @Schema(description = "角色名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四") | ||
| 21 | + @NotEmpty(message = "角色名称不能为空") | ||
| 22 | + private String name; | ||
| 23 | + | ||
| 24 | + @Schema(description = "角色头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") | ||
| 25 | + @NotEmpty(message = "角色头像不能为空") | ||
| 26 | + private String avatar; | ||
| 27 | + | ||
| 28 | + @Schema(description = "角色类别", requiredMode = Schema.RequiredMode.REQUIRED, example = "创作") | ||
| 29 | + @NotEmpty(message = "角色类别不能为空") | ||
| 30 | + private String category; | ||
| 31 | + | ||
| 32 | + @Schema(description = "角色排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | ||
| 33 | + @NotNull(message = "角色排序不能为空") | ||
| 34 | + private Integer sort; | ||
| 35 | + | ||
| 36 | + @Schema(description = "角色描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "你说的对") | ||
| 37 | + @NotEmpty(message = "角色描述不能为空") | ||
| 38 | + private String description; | ||
| 39 | + | ||
| 40 | + @Schema(description = "角色欢迎语", requiredMode = Schema.RequiredMode.REQUIRED, example = "Talk is cheap, i will show code") | ||
| 41 | + @NotEmpty(message = "角色欢迎语不能为空") | ||
| 42 | + private String welcomeMessage; | ||
| 43 | + | ||
| 44 | + @Schema(description = "角色上下文", requiredMode = Schema.RequiredMode.REQUIRED, example = "现在开始你扮演一位程序员,你是一名优秀的程序员,具有很强的逻辑思维能力,总能高效的解决问题") | ||
| 45 | + @NotEmpty(message = "角色上下文不能为空") | ||
| 46 | + private String systemMessage; | ||
| 47 | + | ||
| 48 | + @Schema(description = "是否公开", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | ||
| 49 | + @NotNull(message = "是否公开不能为空") | ||
| 50 | + private Boolean publicStatus; | ||
| 51 | + | ||
| 52 | + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | ||
| 53 | + @NotNull(message = "状态不能为空") | ||
| 54 | + @InEnum(CommonStatusEnum.class) | ||
| 55 | + private Integer status; | ||
| 56 | + | ||
| 57 | +} | ||
| 0 | \ No newline at end of file | 58 | \ No newline at end of file |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/role/AiChatRoleUpdatePublicStatusReqVO.java deleted
| 1 | -package cn.iocoder.yudao.module.ai.controller.admin.model.vo.role; | ||
| 2 | - | ||
| 3 | -import cn.iocoder.yudao.framework.common.pojo.PageParam; | ||
| 4 | -import io.swagger.v3.oas.annotations.media.Schema; | ||
| 5 | -import jakarta.validation.constraints.NotNull; | ||
| 6 | -import lombok.Data; | ||
| 7 | -import lombok.experimental.Accessors; | ||
| 8 | - | ||
| 9 | -/** | ||
| 10 | - * chat 角色 - 修改可见性 | ||
| 11 | - * | ||
| 12 | - * @fansili | ||
| 13 | - * @since v1.0 | ||
| 14 | - */ | ||
| 15 | -@Data | ||
| 16 | -@Accessors(chain = true) | ||
| 17 | -public class AiChatRoleUpdatePublicStatusReqVO extends PageParam { | ||
| 18 | - | ||
| 19 | - | ||
| 20 | - @NotNull(message = "角色编号不能为空") | ||
| 21 | - @Schema(description = "角色编号") | ||
| 22 | - private Long id; | ||
| 23 | - | ||
| 24 | - @NotNull(message = "开启状态不能为空") | ||
| 25 | - @Schema(description = "开启状态 open、close") | ||
| 26 | - private Boolean publicStatus; | ||
| 27 | -} |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/role/AiChatRoleUpdateReqVO.java deleted
| 1 | -package cn.iocoder.yudao.module.ai.controller.admin.model.vo.role; | ||
| 2 | - | ||
| 3 | -import cn.iocoder.yudao.framework.common.pojo.PageParam; | ||
| 4 | -import io.swagger.v3.oas.annotations.media.Schema; | ||
| 5 | -import jakarta.validation.constraints.NotNull; | ||
| 6 | -import lombok.Data; | ||
| 7 | -import lombok.experimental.Accessors; | ||
| 8 | - | ||
| 9 | -/** | ||
| 10 | - * chat 角色 - 更新 | ||
| 11 | - * | ||
| 12 | - * @fansili | ||
| 13 | - * @since v1.0 | ||
| 14 | - */ | ||
| 15 | -@Data | ||
| 16 | -@Accessors(chain = true) | ||
| 17 | -public class AiChatRoleUpdateReqVO extends PageParam { | ||
| 18 | - | ||
| 19 | - | ||
| 20 | - @NotNull(message = "角色编号不能为空") | ||
| 21 | - @Schema(description = "角色编号") | ||
| 22 | - private Long id; | ||
| 23 | - | ||
| 24 | - @NotNull | ||
| 25 | - @Schema(description = "角色名,角色的显示名称") | ||
| 26 | - private String name; | ||
| 27 | - | ||
| 28 | - @Schema(description = "头像") | ||
| 29 | - private String avatar; | ||
| 30 | - | ||
| 31 | - @NotNull | ||
| 32 | - @Schema(description = "分类,角色所属的类别,如娱乐、创作等") | ||
| 33 | - private String category; | ||
| 34 | - | ||
| 35 | - @NotNull | ||
| 36 | - @Schema(description = "角色描述") | ||
| 37 | - private String description; | ||
| 38 | - | ||
| 39 | - @Schema(description = "角色欢迎语") | ||
| 40 | - private String welcomeMessage; | ||
| 41 | - | ||
| 42 | - @NotNull | ||
| 43 | - @Schema(description = "角色设定(消息)") | ||
| 44 | - private String systemMessage; | ||
| 45 | - | ||
| 46 | - @NotNull | ||
| 47 | - @Schema(description = "模型编号") | ||
| 48 | - private Long modelId; | ||
| 49 | - | ||
| 50 | - @NotNull | ||
| 51 | - @Schema(description = "开启状态 open、close") | ||
| 52 | - private Boolean publicStatus; | ||
| 53 | - | ||
| 54 | - @NotNull | ||
| 55 | - @Schema(description = "排序") | ||
| 56 | - private Integer sort; | ||
| 57 | - | ||
| 58 | - @NotNull | ||
| 59 | - @Schema(description = "状态") | ||
| 60 | - private Integer status; | ||
| 61 | - | ||
| 62 | -} |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/convert/AiChatRoleConvert.java deleted
| 1 | -package cn.iocoder.yudao.module.ai.convert; | ||
| 2 | - | ||
| 3 | -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; | ||
| 4 | -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.role.AiChatRoleAddReqVO; | ||
| 5 | -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.role.AiChatRoleRespVO; | ||
| 6 | -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.role.AiChatRoleUpdateReqVO; | ||
| 7 | -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.role.AiChatRoleListRespVO; | ||
| 8 | -import org.mapstruct.Mapper; | ||
| 9 | -import org.mapstruct.factory.Mappers; | ||
| 10 | - | ||
| 11 | -import java.util.List; | ||
| 12 | - | ||
| 13 | -/** | ||
| 14 | - * 聊天 对话 convert | ||
| 15 | - * | ||
| 16 | - * @author fansili | ||
| 17 | - * @time 2024/4/18 16:39 | ||
| 18 | - * @since 1.0 | ||
| 19 | - */ | ||
| 20 | -@Mapper | ||
| 21 | -public interface AiChatRoleConvert { | ||
| 22 | - | ||
| 23 | - AiChatRoleConvert INSTANCE = Mappers.getMapper(AiChatRoleConvert.class); | ||
| 24 | - | ||
| 25 | - /** | ||
| 26 | - * 转换 - ChatRoleListRes | ||
| 27 | - * | ||
| 28 | - * @param roleList | ||
| 29 | - * @return | ||
| 30 | - */ | ||
| 31 | - List<AiChatRoleListRespVO> convertChatRoleListRes(List<AiChatRoleDO> roleList); | ||
| 32 | - | ||
| 33 | - /** | ||
| 34 | - * 转换 - AiChatRoleDO | ||
| 35 | - * | ||
| 36 | - * @param req | ||
| 37 | - * @return | ||
| 38 | - */ | ||
| 39 | - AiChatRoleDO convertAiChatRoleDO(AiChatRoleAddReqVO req); | ||
| 40 | - | ||
| 41 | - /** | ||
| 42 | - * 转换 - AiChatRoleDO | ||
| 43 | - * | ||
| 44 | - * @param req | ||
| 45 | - * @return | ||
| 46 | - */ | ||
| 47 | - AiChatRoleDO convertAiChatRoleDO(AiChatRoleUpdateReqVO req); | ||
| 48 | - | ||
| 49 | - /** | ||
| 50 | - * 转换 - AiChatRoleRes | ||
| 51 | - * | ||
| 52 | - * @param aiChatRoleDO | ||
| 53 | - * @return | ||
| 54 | - */ | ||
| 55 | - AiChatRoleRespVO convertAiChatRoleRes(AiChatRoleDO aiChatRoleDO); | ||
| 56 | -} |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatRoleDO.java
| @@ -46,7 +46,7 @@ public class AiChatRoleDO extends BaseDO { | @@ -46,7 +46,7 @@ public class AiChatRoleDO extends BaseDO { | ||
| 46 | */ | 46 | */ |
| 47 | private String welcomeMessage; | 47 | private String welcomeMessage; |
| 48 | /** | 48 | /** |
| 49 | - * 角色设定(消息) | 49 | + * 角色上下文 |
| 50 | */ | 50 | */ |
| 51 | private String systemMessage; | 51 | private String systemMessage; |
| 52 | 52 | ||
| @@ -65,9 +65,10 @@ public class AiChatRoleDO extends BaseDO { | @@ -65,9 +65,10 @@ public class AiChatRoleDO extends BaseDO { | ||
| 65 | private Long modelId; | 65 | private Long modelId; |
| 66 | 66 | ||
| 67 | /** | 67 | /** |
| 68 | - * 是否公开 true - 公开;false - 私有 | 68 | + * 是否公开 |
| 69 | * | 69 | * |
| 70 | - * true - 公开;false - 私有 | 70 | + * 1. true - 公开;由管理员在【角色管理】所创建 |
| 71 | + * 2. false - 私有;由个人在【我的角色】所创建 | ||
| 71 | */ | 72 | */ |
| 72 | private Boolean publicStatus; | 73 | private Boolean publicStatus; |
| 73 | 74 |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiChatRoleMapper.java
| 1 | package cn.iocoder.yudao.module.ai.dal.mysql; | 1 | package cn.iocoder.yudao.module.ai.dal.mysql; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 3 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | 4 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| 5 | +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | ||
| 6 | +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.role.AiChatRolePageReqVO; | ||
| 4 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; | 7 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; |
| 5 | import org.apache.ibatis.annotations.Mapper; | 8 | import org.apache.ibatis.annotations.Mapper; |
| 6 | import org.springframework.stereotype.Repository; | 9 | import org.springframework.stereotype.Repository; |
| 7 | 10 | ||
| 8 | /** | 11 | /** |
| 9 | - * role mapper | 12 | + * AI 聊天角色 Mapper |
| 10 | * | 13 | * |
| 11 | - * @fansili | ||
| 12 | - * @since v1.0 | 14 | + * @author fansili |
| 13 | */ | 15 | */ |
| 14 | -@Repository | ||
| 15 | @Mapper | 16 | @Mapper |
| 16 | public interface AiChatRoleMapper extends BaseMapperX<AiChatRoleDO> { | 17 | public interface AiChatRoleMapper extends BaseMapperX<AiChatRoleDO> { |
| 18 | + | ||
| 19 | + default PageResult<AiChatRoleDO> selectPage(AiChatRolePageReqVO reqVO) { | ||
| 20 | + return selectPage(reqVO, new LambdaQueryWrapperX<AiChatRoleDO>() | ||
| 21 | + .likeIfPresent(AiChatRoleDO::getName, reqVO.getName()) | ||
| 22 | + .eqIfPresent(AiChatRoleDO::getCategory, reqVO.getCategory()) | ||
| 23 | + .eqIfPresent(AiChatRoleDO::getPublicStatus, reqVO.getPublicStatus()) | ||
| 24 | + .orderByDesc(AiChatRoleDO::getId)); | ||
| 25 | + } | ||
| 26 | + | ||
| 17 | } | 27 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/AiChatRoleService.java deleted
| 1 | -package cn.iocoder.yudao.module.ai.service; | ||
| 2 | - | ||
| 3 | -import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 4 | -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.role.*; | ||
| 5 | -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; | ||
| 6 | - | ||
| 7 | -/** | ||
| 8 | - * chat 角色 | ||
| 9 | - * | ||
| 10 | - * @fansili | ||
| 11 | - * @since v1.0 | ||
| 12 | - */ | ||
| 13 | -public interface AiChatRoleService { | ||
| 14 | - | ||
| 15 | - /** | ||
| 16 | - * 获取聊天角色列表 | ||
| 17 | - * | ||
| 18 | - * @param req | ||
| 19 | - * @return | ||
| 20 | - */ | ||
| 21 | - PageResult<AiChatRoleListRespVO> list(AiChatRoleListReqVO req); | ||
| 22 | - | ||
| 23 | - /** | ||
| 24 | - * chat角色 - 添加 | ||
| 25 | - * | ||
| 26 | - * @param req | ||
| 27 | - * @return | ||
| 28 | - */ | ||
| 29 | - void add(AiChatRoleAddReqVO req); | ||
| 30 | - | ||
| 31 | - /** | ||
| 32 | - * chat角色 - 修改 | ||
| 33 | - * | ||
| 34 | - * @param req | ||
| 35 | - */ | ||
| 36 | - void update(AiChatRoleUpdateReqVO req); | ||
| 37 | - | ||
| 38 | - | ||
| 39 | - /** | ||
| 40 | - * chat角色 - 修改可见性 | ||
| 41 | - * | ||
| 42 | - * @param req | ||
| 43 | - */ | ||
| 44 | - void updatePublicStatus(AiChatRoleUpdatePublicStatusReqVO req); | ||
| 45 | - | ||
| 46 | - /** | ||
| 47 | - * chat角色 - 删除 | ||
| 48 | - * | ||
| 49 | - * @param chatRoleId | ||
| 50 | - */ | ||
| 51 | - void delete(Long chatRoleId); | ||
| 52 | - | ||
| 53 | - /** | ||
| 54 | - * 获取角色 | ||
| 55 | - * | ||
| 56 | - * @param roleId | ||
| 57 | - * @return | ||
| 58 | - */ | ||
| 59 | - AiChatRoleRespVO getChatRole(Long roleId); | ||
| 60 | - | ||
| 61 | - /** | ||
| 62 | - * 校验 - 角色是否存在 | ||
| 63 | - * | ||
| 64 | - * @param id | ||
| 65 | - * @return | ||
| 66 | - */ | ||
| 67 | - AiChatRoleDO validateExists(Long id); | ||
| 68 | - | ||
| 69 | - /** | ||
| 70 | - * 校验 - 角色是否公开 | ||
| 71 | - * | ||
| 72 | - * @param aiChatRoleDO | ||
| 73 | - */ | ||
| 74 | - void validateIsPublic(AiChatRoleDO aiChatRoleDO); | ||
| 75 | - | ||
| 76 | -} |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatConversationServiceImpl.java renamed to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatConversationServiceImpl.java
| 1 | -package cn.iocoder.yudao.module.ai.service.model; | 1 | +package cn.iocoder.yudao.module.ai.service.impl; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; | 3 | import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; |
| 4 | import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; | 4 | import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; |
| @@ -7,14 +7,15 @@ import cn.iocoder.yudao.module.ai.ErrorCodeConstants; | @@ -7,14 +7,15 @@ import cn.iocoder.yudao.module.ai.ErrorCodeConstants; | ||
| 7 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateReqVO; | 7 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateReqVO; |
| 8 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationRespVO; | 8 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationRespVO; |
| 9 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationUpdateReqVO; | 9 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationUpdateReqVO; |
| 10 | -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.role.AiChatRoleRespVO; | ||
| 11 | import cn.iocoder.yudao.module.ai.convert.AiChatConversationConvert; | 10 | import cn.iocoder.yudao.module.ai.convert.AiChatConversationConvert; |
| 12 | import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; | 11 | import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; |
| 13 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; | 12 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; |
| 13 | +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; | ||
| 14 | import cn.iocoder.yudao.module.ai.dal.mysql.AiChatConversationMapper; | 14 | import cn.iocoder.yudao.module.ai.dal.mysql.AiChatConversationMapper; |
| 15 | import cn.iocoder.yudao.module.ai.dal.mysql.AiChatModelMapper; | 15 | import cn.iocoder.yudao.module.ai.dal.mysql.AiChatModelMapper; |
| 16 | import cn.iocoder.yudao.module.ai.service.AiChatConversationService; | 16 | import cn.iocoder.yudao.module.ai.service.AiChatConversationService; |
| 17 | -import cn.iocoder.yudao.module.ai.service.AiChatRoleService; | 17 | +import cn.iocoder.yudao.module.ai.service.model.AiChatModelService; |
| 18 | +import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService; | ||
| 18 | import lombok.AllArgsConstructor; | 19 | import lombok.AllArgsConstructor; |
| 19 | import lombok.extern.slf4j.Slf4j; | 20 | import lombok.extern.slf4j.Slf4j; |
| 20 | import org.jetbrains.annotations.NotNull; | 21 | import org.jetbrains.annotations.NotNull; |
| @@ -45,10 +46,7 @@ public class AiChatConversationServiceImpl implements AiChatConversationService | @@ -45,10 +46,7 @@ public class AiChatConversationServiceImpl implements AiChatConversationService | ||
| 45 | // 默认使用 sort 排序第一个模型 | 46 | // 默认使用 sort 排序第一个模型 |
| 46 | AiChatModelDO aiChatModalDO = aiChatModalMapper.selectFirstModal(); | 47 | AiChatModelDO aiChatModalDO = aiChatModalMapper.selectFirstModal(); |
| 47 | // 查询角色 | 48 | // 查询角色 |
| 48 | - AiChatRoleRespVO chatRoleRes = null; | ||
| 49 | - if (req.getRoleId() != null) { | ||
| 50 | - chatRoleRes = aiChatRoleService.getChatRole(req.getRoleId()); | ||
| 51 | - } | 49 | + AiChatRoleDO chatRoleRes = req.getRoleId() != null ? aiChatRoleService.getChatRole(req.getRoleId()) : null; |
| 52 | Long chatRoleId = chatRoleRes != null ? chatRoleRes.getId() : null; | 50 | Long chatRoleId = chatRoleRes != null ? chatRoleRes.getId() : null; |
| 53 | // 创建新的 Conversation | 51 | // 创建新的 Conversation |
| 54 | AiChatConversationDO insertConversation = saveConversation(AiCommonConstants.CONVERSATION_DEFAULT_TITLE, | 52 | AiChatConversationDO insertConversation = saveConversation(AiCommonConstants.CONVERSATION_DEFAULT_TITLE, |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatRoleServiceImpl.java deleted
| 1 | -package cn.iocoder.yudao.module.ai.service.impl; | ||
| 2 | - | ||
| 3 | -import cn.hutool.core.util.StrUtil; | ||
| 4 | -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||
| 5 | -import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; | ||
| 6 | -import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 7 | -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | ||
| 8 | -import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; | ||
| 9 | -import cn.iocoder.yudao.module.ai.ErrorCodeConstants; | ||
| 10 | -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.role.*; | ||
| 11 | -import cn.iocoder.yudao.module.ai.convert.AiChatRoleConvert; | ||
| 12 | -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; | ||
| 13 | -import cn.iocoder.yudao.module.ai.dal.mysql.AiChatRoleMapper; | ||
| 14 | -import cn.iocoder.yudao.module.ai.enums.AiChatRoleCategoryEnum; | ||
| 15 | -import cn.iocoder.yudao.module.ai.service.model.AiChatModelService; | ||
| 16 | -import cn.iocoder.yudao.module.ai.service.AiChatRoleService; | ||
| 17 | -import lombok.AllArgsConstructor; | ||
| 18 | -import lombok.extern.slf4j.Slf4j; | ||
| 19 | -import org.springframework.stereotype.Service; | ||
| 20 | - | ||
| 21 | -import java.util.List; | ||
| 22 | - | ||
| 23 | -/** | ||
| 24 | - * chat 角色 | ||
| 25 | - * | ||
| 26 | - * @fansili | ||
| 27 | - * @since v1.0 | ||
| 28 | - */ | ||
| 29 | -@Service | ||
| 30 | -@AllArgsConstructor | ||
| 31 | -@Slf4j | ||
| 32 | -public class AiChatRoleServiceImpl implements AiChatRoleService { | ||
| 33 | - | ||
| 34 | - private final AiChatRoleMapper aiChatRoleMapper; | ||
| 35 | - private final AiChatModelService aiChatModalService; | ||
| 36 | - | ||
| 37 | - @Override | ||
| 38 | - public PageResult<AiChatRoleListRespVO> list(AiChatRoleListReqVO req) { | ||
| 39 | - // 查询条件 | ||
| 40 | - LambdaQueryWrapperX<AiChatRoleDO> queryWrapperX = new LambdaQueryWrapperX<>(); | ||
| 41 | - // search 查询 | ||
| 42 | - if (!StrUtil.isBlank(req.getSearch())) { | ||
| 43 | - queryWrapperX.eq(AiChatRoleDO::getName, req.getSearch()); | ||
| 44 | - } | ||
| 45 | - // 默认排序id desc | ||
| 46 | - queryWrapperX.orderByDesc(AiChatRoleDO::getId); | ||
| 47 | - // | ||
| 48 | - PageResult<AiChatRoleDO> aiChatRoleDOPageResult = aiChatRoleMapper.selectPage(req, queryWrapperX); | ||
| 49 | - Long total = aiChatRoleDOPageResult.getTotal(); | ||
| 50 | - List<AiChatRoleDO> roleList = aiChatRoleDOPageResult.getList(); | ||
| 51 | - // 换货res | ||
| 52 | - List<AiChatRoleListRespVO> chatRoleListResList = AiChatRoleConvert.INSTANCE.convertChatRoleListRes(roleList); | ||
| 53 | - return new PageResult<>(chatRoleListResList, total); | ||
| 54 | - } | ||
| 55 | - | ||
| 56 | - @Override | ||
| 57 | - public void add(AiChatRoleAddReqVO req) { | ||
| 58 | - // 转换enum,并校验enum | ||
| 59 | - AiChatRoleCategoryEnum.valueOfCategory(req.getCategory()); | ||
| 60 | - // 校验模型是否存在 TODO | ||
| 61 | -// aiChatModalService.validateExists(req.getModelId()); | ||
| 62 | - // 转换do | ||
| 63 | - AiChatRoleDO insertAiChatRoleDO = AiChatRoleConvert.INSTANCE.convertAiChatRoleDO(req); | ||
| 64 | - insertAiChatRoleDO.setUserId(SecurityFrameworkUtils.getLoginUserId()); | ||
| 65 | - insertAiChatRoleDO.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||
| 66 | - // 保存 | ||
| 67 | - aiChatRoleMapper.insert(insertAiChatRoleDO); | ||
| 68 | - } | ||
| 69 | - | ||
| 70 | - @Override | ||
| 71 | - public void update(AiChatRoleUpdateReqVO req) { | ||
| 72 | - // 检查角色是否存在 | ||
| 73 | - validateExists(req.getId()); | ||
| 74 | - // 转换enum,并校验enum | ||
| 75 | - AiChatRoleCategoryEnum.valueOfCategory(req.getCategory()); | ||
| 76 | - // 校验模型是否存在 TODO | ||
| 77 | -// aiChatModalService.validateExists(req.getModelId()); | ||
| 78 | - // 转换do | ||
| 79 | - AiChatRoleDO updateChatRole = AiChatRoleConvert.INSTANCE.convertAiChatRoleDO(req); | ||
| 80 | - updateChatRole.setId(req.getId()); | ||
| 81 | - aiChatRoleMapper.updateById(updateChatRole); | ||
| 82 | - } | ||
| 83 | - | ||
| 84 | - | ||
| 85 | - @Override | ||
| 86 | - public void updatePublicStatus(AiChatRoleUpdatePublicStatusReqVO req) { | ||
| 87 | - // 检查角色是否存在 | ||
| 88 | - validateExists(req.getId()); | ||
| 89 | - // 更新 | ||
| 90 | - aiChatRoleMapper.updateById( | ||
| 91 | - new AiChatRoleDO() | ||
| 92 | - .setId(req.getId()) | ||
| 93 | - .setPublicStatus(req.getPublicStatus()) | ||
| 94 | - ); | ||
| 95 | - } | ||
| 96 | - | ||
| 97 | - @Override | ||
| 98 | - public void delete(Long chatRoleId) { | ||
| 99 | - // 检查角色是否存在 | ||
| 100 | - validateExists(chatRoleId); | ||
| 101 | - // 删除 | ||
| 102 | - aiChatRoleMapper.deleteById(chatRoleId); | ||
| 103 | - } | ||
| 104 | - | ||
| 105 | - @Override | ||
| 106 | - public AiChatRoleRespVO getChatRole(Long roleId) { | ||
| 107 | - // 检查角色是否存在 | ||
| 108 | - AiChatRoleDO aiChatRoleDO = validateExists(roleId); | ||
| 109 | - return AiChatRoleConvert.INSTANCE.convertAiChatRoleRes(aiChatRoleDO); | ||
| 110 | - } | ||
| 111 | - | ||
| 112 | - public AiChatRoleDO validateExists(Long id) { | ||
| 113 | - AiChatRoleDO aiChatRoleDO = aiChatRoleMapper.selectById(id); | ||
| 114 | - if (aiChatRoleDO == null) { | ||
| 115 | - throw ServiceExceptionUtil.exception(ErrorCodeConstants.AI_CHAT_ROLE_NOT_EXIST); | ||
| 116 | - } | ||
| 117 | - return aiChatRoleDO; | ||
| 118 | - } | ||
| 119 | - | ||
| 120 | - public void validateIsPublic(AiChatRoleDO aiChatRoleDO) { | ||
| 121 | - if (aiChatRoleDO == null) { | ||
| 122 | - return; | ||
| 123 | - } | ||
| 124 | - if (!aiChatRoleDO.getPublicStatus()) { | ||
| 125 | - throw ServiceExceptionUtil.exception(ErrorCodeConstants.AI_CHAT_ROLE_NOT_PUBLIC); | ||
| 126 | - } | ||
| 127 | - } | ||
| 128 | -} | ||
| 129 | - |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatServiceImpl.java
| @@ -23,12 +23,11 @@ import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; | @@ -23,12 +23,11 @@ import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; | ||
| 23 | import cn.iocoder.yudao.module.ai.dal.mysql.AiChatConversationMapper; | 23 | import cn.iocoder.yudao.module.ai.dal.mysql.AiChatConversationMapper; |
| 24 | import cn.iocoder.yudao.module.ai.dal.mysql.AiChatMessageMapper; | 24 | import cn.iocoder.yudao.module.ai.dal.mysql.AiChatMessageMapper; |
| 25 | import cn.iocoder.yudao.module.ai.service.AiChatConversationService; | 25 | import cn.iocoder.yudao.module.ai.service.AiChatConversationService; |
| 26 | -import cn.iocoder.yudao.module.ai.service.AiChatRoleService; | 26 | +import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService; |
| 27 | import cn.iocoder.yudao.module.ai.service.AiChatService; | 27 | import cn.iocoder.yudao.module.ai.service.AiChatService; |
| 28 | import cn.iocoder.yudao.module.ai.service.model.AiChatModelService; | 28 | import cn.iocoder.yudao.module.ai.service.model.AiChatModelService; |
| 29 | import lombok.AllArgsConstructor; | 29 | import lombok.AllArgsConstructor; |
| 30 | import lombok.extern.slf4j.Slf4j; | 30 | import lombok.extern.slf4j.Slf4j; |
| 31 | -import org.springframework.boot.autoconfigure.http.HttpMessageConverters; | ||
| 32 | import org.springframework.stereotype.Service; | 31 | import org.springframework.stereotype.Service; |
| 33 | import org.springframework.transaction.annotation.Transactional; | 32 | import org.springframework.transaction.annotation.Transactional; |
| 34 | import reactor.core.publisher.Flux; | 33 | import reactor.core.publisher.Flux; |
| @@ -53,12 +52,12 @@ import java.util.stream.Collectors; | @@ -53,12 +52,12 @@ import java.util.stream.Collectors; | ||
| 53 | public class AiChatServiceImpl implements AiChatService { | 52 | public class AiChatServiceImpl implements AiChatService { |
| 54 | 53 | ||
| 55 | private final AiChatClientFactory aiChatClientFactory; | 54 | private final AiChatClientFactory aiChatClientFactory; |
| 55 | + | ||
| 56 | private final AiChatMessageMapper aiChatMessageMapper; | 56 | private final AiChatMessageMapper aiChatMessageMapper; |
| 57 | private final AiChatConversationMapper aiChatConversationMapper; | 57 | private final AiChatConversationMapper aiChatConversationMapper; |
| 58 | private final AiChatConversationService chatConversationService; | 58 | private final AiChatConversationService chatConversationService; |
| 59 | private final AiChatModelService aiChatModalService; | 59 | private final AiChatModelService aiChatModalService; |
| 60 | - private final AiChatRoleService aiChatRoleService; | ||
| 61 | - private final HttpMessageConverters messageConverters; | 60 | + private final AiChatRoleService chatRoleService; |
| 62 | 61 | ||
| 63 | @Transactional(rollbackFor = Exception.class) | 62 | @Transactional(rollbackFor = Exception.class) |
| 64 | public AiChatMessageRespVO chat(AiChatMessageSendReqVO req) { | 63 | public AiChatMessageRespVO chat(AiChatMessageSendReqVO req) { |
| @@ -68,12 +67,7 @@ public class AiChatServiceImpl implements AiChatService { | @@ -68,12 +67,7 @@ public class AiChatServiceImpl implements AiChatService { | ||
| 68 | // 获取对话模型 | 67 | // 获取对话模型 |
| 69 | AiChatModelDO chatModel = aiChatModalService.validateChatModel(conversation.getModelId()); | 68 | AiChatModelDO chatModel = aiChatModalService.validateChatModel(conversation.getModelId()); |
| 70 | // 获取角色信息 | 69 | // 获取角色信息 |
| 71 | - AiChatRoleDO aiChatRoleDO = null; | ||
| 72 | - if (conversation.getRoleId() != null) { | ||
| 73 | - aiChatRoleDO = aiChatRoleService.validateExists(conversation.getRoleId()); | ||
| 74 | - } | ||
| 75 | - // 校验角色是否公开 | ||
| 76 | - aiChatRoleService.validateIsPublic(aiChatRoleDO); | 70 | + AiChatRoleDO chatRoleDO = conversation.getRoleId() != null ? chatRoleService.validateChatRole(conversation.getRoleId()) : null; |
| 77 | // 获取 client 类型 | 71 | // 获取 client 类型 |
| 78 | AiPlatformEnum platformEnum = AiPlatformEnum.validatePlatform(chatModel.getPlatform()); | 72 | AiPlatformEnum platformEnum = AiPlatformEnum.validatePlatform(chatModel.getPlatform()); |
| 79 | // 保存 chat message | 73 | // 保存 chat message |
| @@ -142,12 +136,7 @@ public class AiChatServiceImpl implements AiChatService { | @@ -142,12 +136,7 @@ public class AiChatServiceImpl implements AiChatService { | ||
| 142 | // 获取对话模型 | 136 | // 获取对话模型 |
| 143 | AiChatModelDO chatModel = aiChatModalService.validateChatModel(conversation.getModelId()); | 137 | AiChatModelDO chatModel = aiChatModalService.validateChatModel(conversation.getModelId()); |
| 144 | // 获取角色信息 | 138 | // 获取角色信息 |
| 145 | - AiChatRoleDO aiChatRoleDO = null; | ||
| 146 | - if (conversation.getRoleId() != null) { | ||
| 147 | - aiChatRoleDO = aiChatRoleService.validateExists(conversation.getRoleId()); | ||
| 148 | - } | ||
| 149 | - // 校验角色是否公开 | ||
| 150 | - aiChatRoleService.validateIsPublic(aiChatRoleDO); | 139 | + AiChatRoleDO chatRoleDO = conversation.getRoleId() != null ? chatRoleService.validateChatRole(conversation.getRoleId()) : null; |
| 151 | // 创建 chat 需要的 Prompt | 140 | // 创建 chat 需要的 Prompt |
| 152 | Prompt prompt = new Prompt(aiChatMessageDO.getContent()); | 141 | Prompt prompt = new Prompt(aiChatMessageDO.getContent()); |
| 153 | // 提前创建一个 system message | 142 | // 提前创建一个 system message |
| @@ -204,13 +193,6 @@ public class AiChatServiceImpl implements AiChatService { | @@ -204,13 +193,6 @@ public class AiChatServiceImpl implements AiChatService { | ||
| 204 | AiChatConversationRespVO conversation = chatConversationService.getConversationOfValidate(req.getConversationId()); | 193 | AiChatConversationRespVO conversation = chatConversationService.getConversationOfValidate(req.getConversationId()); |
| 205 | // 获取对话模型 | 194 | // 获取对话模型 |
| 206 | AiChatModelDO chatModel = aiChatModalService.validateChatModel(conversation.getModelId()); | 195 | AiChatModelDO chatModel = aiChatModalService.validateChatModel(conversation.getModelId()); |
| 207 | - // 获取角色信息 | ||
| 208 | - AiChatRoleDO aiChatRoleDO = null; | ||
| 209 | - if (conversation.getRoleId() != null) { | ||
| 210 | - aiChatRoleDO = aiChatRoleService.validateExists(conversation.getRoleId()); | ||
| 211 | - } | ||
| 212 | - // 校验角色是否公开 | ||
| 213 | - aiChatRoleService.validateIsPublic(aiChatRoleDO); | ||
| 214 | AiChatMessageDO userMessage = insertChatMessage(conversation.getId(), MessageType.USER, loginUserId, conversation.getRoleId(), | 196 | AiChatMessageDO userMessage = insertChatMessage(conversation.getId(), MessageType.USER, loginUserId, conversation.getRoleId(), |
| 215 | chatModel.getModel(), chatModel.getId(), req.getContent(), | 197 | chatModel.getModel(), chatModel.getId(), req.getContent(), |
| 216 | null, conversation.getTemperature(), conversation.getMaxTokens(), conversation.getMaxContexts()); | 198 | null, conversation.getTemperature(), conversation.getMaxTokens(), conversation.getMaxContexts()); |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatRoleService.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.ai.service.model; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 4 | +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.role.AiChatRolePageReqVO; | ||
| 5 | +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.role.AiChatRoleSaveReqVO; | ||
| 6 | +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; | ||
| 7 | +import jakarta.validation.Valid; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * AI 聊天角色 Service 接口 | ||
| 11 | + * | ||
| 12 | + * @author fansili | ||
| 13 | + */ | ||
| 14 | +public interface AiChatRoleService { | ||
| 15 | + | ||
| 16 | + /** | ||
| 17 | + * 创建聊天角色 | ||
| 18 | + * | ||
| 19 | + * @param createReqVO 创建信息 | ||
| 20 | + * @return 编号 | ||
| 21 | + */ | ||
| 22 | + Long createChatRole(@Valid AiChatRoleSaveReqVO createReqVO); | ||
| 23 | + | ||
| 24 | + /** | ||
| 25 | + * 更新聊天角色 | ||
| 26 | + * | ||
| 27 | + * @param updateReqVO 更新信息 | ||
| 28 | + */ | ||
| 29 | + void updateChatRole(@Valid AiChatRoleSaveReqVO updateReqVO); | ||
| 30 | + | ||
| 31 | + /** | ||
| 32 | + * 删除聊天角色 | ||
| 33 | + * | ||
| 34 | + * @param id 编号 | ||
| 35 | + */ | ||
| 36 | + void deleteChatRole(Long id); | ||
| 37 | + | ||
| 38 | + /** | ||
| 39 | + * 获得聊天角色 | ||
| 40 | + * | ||
| 41 | + * @param id 编号 | ||
| 42 | + * @return AI 聊天角色 | ||
| 43 | + */ | ||
| 44 | + AiChatRoleDO getChatRole(Long id); | ||
| 45 | + | ||
| 46 | + /** | ||
| 47 | + * 校验角色是否存在 | ||
| 48 | + * | ||
| 49 | + * @param id 角色编号 | ||
| 50 | + */ | ||
| 51 | + AiChatRoleDO validateChatRole(Long id); | ||
| 52 | + | ||
| 53 | + /** | ||
| 54 | + * 获得AI 聊天角色分页 | ||
| 55 | + * | ||
| 56 | + * @param pageReqVO 分页查询 | ||
| 57 | + * @return AI 聊天角色分页 | ||
| 58 | + */ | ||
| 59 | + PageResult<AiChatRoleDO> getChatRolePage(AiChatRolePageReqVO pageReqVO); | ||
| 60 | + | ||
| 61 | +} | ||
| 0 | \ No newline at end of file | 62 | \ No newline at end of file |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatRoleServiceImpl.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.ai.service.model; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||
| 4 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | ||
| 5 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 6 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | ||
| 7 | +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.role.AiChatRolePageReqVO; | ||
| 8 | +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.role.AiChatRoleSaveReqVO; | ||
| 9 | +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; | ||
| 10 | +import cn.iocoder.yudao.module.ai.dal.mysql.AiChatRoleMapper; | ||
| 11 | +import jakarta.annotation.Resource; | ||
| 12 | +import lombok.AllArgsConstructor; | ||
| 13 | +import lombok.extern.slf4j.Slf4j; | ||
| 14 | +import org.springframework.stereotype.Service; | ||
| 15 | + | ||
| 16 | +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | ||
| 17 | +import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.CHAT_ROLE_DISABLE; | ||
| 18 | +import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.CHAT_ROLE_NOT_EXISTS; | ||
| 19 | + | ||
| 20 | +/** | ||
| 21 | + * AI 聊天角色 Service 实现类 | ||
| 22 | + * | ||
| 23 | + * @author fansili | ||
| 24 | + */ | ||
| 25 | +@Service | ||
| 26 | +@AllArgsConstructor | ||
| 27 | +@Slf4j | ||
| 28 | +public class AiChatRoleServiceImpl implements AiChatRoleService { | ||
| 29 | + | ||
| 30 | + @Resource | ||
| 31 | + private AiChatRoleMapper chatRoleMapper; | ||
| 32 | + | ||
| 33 | + @Override | ||
| 34 | + public Long createChatRole(AiChatRoleSaveReqVO createReqVO) { | ||
| 35 | + AiChatRoleDO chatRole = BeanUtils.toBean(createReqVO, AiChatRoleDO.class); | ||
| 36 | + chatRoleMapper.insert(chatRole); | ||
| 37 | + return chatRole.getId(); | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + @Override | ||
| 41 | + public void updateChatRole(AiChatRoleSaveReqVO updateReqVO) { | ||
| 42 | + // 校验存在 | ||
| 43 | + validateChatRoleExists(updateReqVO.getId()); | ||
| 44 | + // 更新 | ||
| 45 | + AiChatRoleDO updateObj = BeanUtils.toBean(updateReqVO, AiChatRoleDO.class); | ||
| 46 | + chatRoleMapper.updateById(updateObj); | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + @Override | ||
| 50 | + public void deleteChatRole(Long id) { | ||
| 51 | + // 校验存在 | ||
| 52 | + validateChatRoleExists(id); | ||
| 53 | + // 删除 | ||
| 54 | + chatRoleMapper.deleteById(id); | ||
| 55 | + } | ||
| 56 | + | ||
| 57 | + private AiChatRoleDO validateChatRoleExists(Long id) { | ||
| 58 | + AiChatRoleDO chatRole = chatRoleMapper.selectById(id); | ||
| 59 | + if (chatRole == null) { | ||
| 60 | + throw exception(CHAT_ROLE_NOT_EXISTS); | ||
| 61 | + } | ||
| 62 | + return chatRole; | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | + @Override | ||
| 66 | + public AiChatRoleDO getChatRole(Long id) { | ||
| 67 | + return chatRoleMapper.selectById(id); | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + @Override | ||
| 71 | + public AiChatRoleDO validateChatRole(Long id) { | ||
| 72 | + AiChatRoleDO chatRole = validateChatRoleExists(id); | ||
| 73 | + if (CommonStatusEnum.isDisable(chatRole.getStatus())) { | ||
| 74 | + throw exception(CHAT_ROLE_DISABLE, chatRole.getName()); | ||
| 75 | + } | ||
| 76 | + return chatRole; | ||
| 77 | + } | ||
| 78 | + | ||
| 79 | + @Override | ||
| 80 | + public PageResult<AiChatRoleDO> getChatRolePage(AiChatRolePageReqVO pageReqVO) { | ||
| 81 | + return chatRoleMapper.selectPage(pageReqVO); | ||
| 82 | + } | ||
| 83 | + | ||
| 84 | +} | ||
| 85 | + |