feat(知识库): 增加知识库模块

This commit is contained in:
ageer
2025-03-02 11:19:29 +08:00
parent 04f579d033
commit 1385b165c9
1421 changed files with 166583 additions and 64636 deletions

View File

@@ -0,0 +1,16 @@
package org.ruoyi.system.builder;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage;
import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public abstract class AbstractBuilder {
protected final Logger logger = LoggerFactory.getLogger(getClass());
public abstract WxCpXmlOutMessage build(String content, WxCpXmlMessage wxMessage, WxCpService service);
}

View File

@@ -0,0 +1,25 @@
package org.ruoyi.system.builder;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage;
import me.chanjar.weixin.cp.bean.message.WxCpXmlOutImageMessage;
import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage;
/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class ImageBuilder extends AbstractBuilder {
@Override
public WxCpXmlOutMessage build(String content, WxCpXmlMessage wxMessage,
WxCpService service) {
WxCpXmlOutImageMessage m = WxCpXmlOutMessage.IMAGE().mediaId(content)
.fromUser(wxMessage.getToUserName()).toUser(wxMessage.getFromUserName())
.build();
return m;
}
}

View File

@@ -0,0 +1,22 @@
package org.ruoyi.system.builder;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage;
import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage;
import me.chanjar.weixin.cp.bean.message.WxCpXmlOutTextMessage;
/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class TextBuilder extends AbstractBuilder {
@Override
public WxCpXmlOutMessage build(String content, WxCpXmlMessage wxMessage,
WxCpService service) {
WxCpXmlOutTextMessage m = WxCpXmlOutMessage.TEXT().content(content)
.fromUser(wxMessage.getToUserName()).toUser(wxMessage.getFromUserName())
.build();
return m;
}
}

View File

@@ -26,6 +26,7 @@ public class OkHttpConfig {
public void init() {
initializeOkHttpUtil("suno");
initializeOkHttpUtil("luma");
initializeOkHttpUtil("ppt");
}
private void initializeOkHttpUtil(String modelName) {

View File

@@ -0,0 +1,130 @@
package org.ruoyi.system.cofing;
import com.google.common.collect.Maps;
import jakarta.annotation.PostConstruct;
import lombok.val;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl;
import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl;
import me.chanjar.weixin.cp.constant.WxCpConsts;
import me.chanjar.weixin.cp.message.WxCpMessageRouter;
import org.ruoyi.system.handler.wxcp.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import me.chanjar.weixin.cp.api.WxCpService;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 单实例配置
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@Configuration
@EnableConfigurationProperties(WxCpProperties.class)
public class WxCpConfiguration {
private LogHandler logHandler;
private NullHandler nullHandler;
private LocationHandler locationHandler;
private MenuHandler menuHandler;
private MsgHandler msgHandler;
private UnsubscribeHandler unsubscribeHandler;
private SubscribeHandler subscribeHandler;
private WxCpProperties properties;
private static Map<Integer, WxCpMessageRouter> routers = Maps.newHashMap();
private static Map<Integer, WxCpService> cpServices = Maps.newHashMap();
@Autowired
public WxCpConfiguration(LogHandler logHandler, NullHandler nullHandler, LocationHandler locationHandler,
MenuHandler menuHandler, MsgHandler msgHandler, UnsubscribeHandler unsubscribeHandler,
SubscribeHandler subscribeHandler, WxCpProperties properties) {
this.logHandler = logHandler;
this.nullHandler = nullHandler;
this.locationHandler = locationHandler;
this.menuHandler = menuHandler;
this.msgHandler = msgHandler;
this.unsubscribeHandler = unsubscribeHandler;
this.subscribeHandler = subscribeHandler;
this.properties = properties;
}
public static Map<Integer, WxCpMessageRouter> getRouters() {
return routers;
}
public static WxCpService getCpService(Integer agentId) {
return cpServices.get(agentId);
}
@PostConstruct
public void initServices() {
cpServices = this.properties.getAppConfigs().stream().map(a -> {
val configStorage = new WxCpDefaultConfigImpl();
configStorage.setCorpId(this.properties.getCorpId());
configStorage.setAgentId(a.getAgentId());
configStorage.setCorpSecret(a.getSecret());
configStorage.setToken(a.getToken());
configStorage.setAesKey(a.getAesKey());
val service = new WxCpServiceImpl();
service.setWxCpConfigStorage(configStorage);
routers.put(a.getAgentId(), this.newRouter(service));
return service;
}).collect(Collectors.toMap(service -> service.getWxCpConfigStorage().getAgentId(), a -> a));
}
private WxCpMessageRouter newRouter(WxCpService wxCpService) {
final val newRouter = new WxCpMessageRouter(wxCpService);
// 记录所有事件的日志 (异步执行)
newRouter.rule().handler(this.logHandler).next();
// 自定义菜单事件
newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
.event(WxConsts.MenuButtonType.CLICK).handler(this.menuHandler).end();
// 点击菜单链接事件(这里使用了一个空的处理器,可以根据自己需要进行扩展)
newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
.event(WxConsts.MenuButtonType.VIEW).handler(this.nullHandler).end();
// 关注事件
newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
.event(WxConsts.EventType.SUBSCRIBE).handler(this.subscribeHandler)
.end();
// 取消关注事件
newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
.event(WxConsts.EventType.UNSUBSCRIBE)
.handler(this.unsubscribeHandler).end();
// 上报地理位置事件
newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
.event(WxConsts.EventType.LOCATION).handler(this.locationHandler)
.end();
// 接收地理位置消息
newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.LOCATION)
.handler(this.locationHandler).end();
// 扫码事件(这里使用了一个空的处理器,可以根据自己需要进行扩展)
newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
.event(WxConsts.EventType.SCAN).handler(this.nullHandler).end();
newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
.event(WxCpConsts.EventType.CHANGE_CONTACT).handler(new ContactChangeHandler()).end();
newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
.event(WxCpConsts.EventType.ENTER_AGENT).handler(new EnterAgentHandler()).end();
// 默认
newRouter.rule().async(false).handler(this.msgHandler).end();
return newRouter;
}
}

View File

@@ -0,0 +1,48 @@
package org.ruoyi.system.cofing;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.List;
/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@Data
@ConfigurationProperties(prefix = "wechat.cp")
public class WxCpProperties {
/**
* 设置企业微信的corpId
*/
private String corpId;
private List<AppConfig> appConfigs;
@Getter
@Setter
public static class AppConfig {
/**
* 设置企业微信应用的AgentId
*/
private Integer agentId;
/**
* 设置企业微信应用的Secret
*/
private String secret;
/**
* 设置企业微信应用的token
*/
private String token;
/**
* 设置企业微信应用的EncodingAESKey
*/
private String aesKey;
}
}

View File

@@ -36,8 +36,8 @@ public class ChatConfigController extends BaseController {
*/
@GetMapping("/list")
@SaCheckPermission("system:config:list")
public R<List<ChatConfigVo>> list(ChatConfigBo bo) {
return R.ok(chatConfigService.queryList(bo));
public List<ChatConfigVo> list(ChatConfigBo bo) {
return chatConfigService.queryList(bo);
}
/**
@@ -46,7 +46,8 @@ public class ChatConfigController extends BaseController {
* @param id 主键
*/
@GetMapping("/{id}")
public R<ChatConfigVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) {
public R<ChatConfigVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(chatConfigService.queryById(id));
}

View File

@@ -0,0 +1,42 @@
package org.ruoyi.system.controller.system;
import lombok.RequiredArgsConstructor;
import org.ruoyi.common.core.domain.R;
import org.ruoyi.common.web.core.BaseController;
import org.ruoyi.system.domain.bo.ChatAppStoreBo;
import org.ruoyi.system.domain.bo.ChatMessageBo;
import org.ruoyi.system.domain.vo.ChatAppStoreVo;
import org.ruoyi.system.service.IChatAppStoreService;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 应用商店
*
* @author Lion Li
* @date 2024-03-19
*/
@RequiredArgsConstructor
@RestController
@RequestMapping("/system/store")
public class ChatStoreController extends BaseController {
private final IChatAppStoreService appStoreService;
/**
* 应用商店
*/
@GetMapping("/appList")
public R<List<ChatAppStoreVo>> appList(ChatAppStoreBo bo) {
return R.ok(appStoreService.queryList(bo));
}
/**
* 收藏应用
*/
@PostMapping("/copyApp")
public R<String> copyApp() {
return R.ok();
}
}

View File

@@ -15,6 +15,7 @@ import org.ruoyi.common.log.enums.BusinessType;
import org.ruoyi.common.mybatis.core.page.PageQuery;
import org.ruoyi.common.mybatis.core.page.TableDataInfo;
import org.ruoyi.common.web.core.BaseController;
import org.ruoyi.common.wechat.web.utils.UUIDShortUtil;
import org.ruoyi.system.domain.bo.ChatVoucherBo;
import org.ruoyi.system.domain.vo.ChatVoucherVo;
import org.ruoyi.system.service.IChatVoucherService;
@@ -22,7 +23,6 @@ import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.UUID;
/**
* 用户兑换记录
@@ -78,7 +78,7 @@ public class ChatVoucherController extends BaseController {
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody ChatVoucherBo bo) {
bo.setCode(UUID.randomUUID().toString().replace("-", ""));
bo.setCode(UUIDShortUtil.generateShortUuid());
return toAjax(chatVoucherService.insertByBo(bo));
}

View File

@@ -0,0 +1,81 @@
package org.ruoyi.system.controller.system;
import cn.dev33.satoken.annotation.SaIgnore;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.ruoyi.common.core.exception.ServiceException;
import org.ruoyi.common.wechat.itchat4j.api.WechatTools;
import org.ruoyi.common.wechat.itchat4j.controller.LoginController;
import org.ruoyi.common.wechat.itchat4j.core.MsgCenter;
import org.ruoyi.common.wechat.itchat4j.face.IMsgHandlerFace;
import org.ruoyi.common.wechat.web.base.BaseException;
import org.ruoyi.system.domain.bo.WxRobConfigBo;
import org.ruoyi.system.domain.vo.WxRobConfigVo;
import org.ruoyi.system.handler.MyMsgHandler;
import org.ruoyi.system.service.ISseService;
import org.ruoyi.system.service.IWxRobConfigService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* 个人微信扩展控制器
*
* @author WangLe
*/
@SaIgnore
@Slf4j
@Validated
@RequiredArgsConstructor
@RestController
public class WeChatController {
private final ISseService sseService;
private final IWxRobConfigService wxRobConfigService;
/**
* 登录第一步,获取二维码链接
* @throws BaseException
*/
@PostMapping("/getQr")
public String getQr(@RequestParam String uniqueKey) {
LoginController login = new LoginController(uniqueKey);
try {
return login.login_1();
} catch (BaseException e) {
throw new ServiceException("获取二维码失败:"+ e.getMessage());
}
}
@PostMapping("/wxLogin")
public Boolean wxLogin(@RequestParam String uniqueKey) {
LoginController login = new LoginController(uniqueKey);
return login.login_2();
}
@PostMapping("/wxInit")
public Boolean wxInit(@RequestParam String uniqueKey) {
LoginController login = new LoginController(uniqueKey);
// 开启消息处理线程
WxRobConfigBo wxRobConfigBo = new WxRobConfigBo();
wxRobConfigBo.setUniqueKey(uniqueKey);
List<WxRobConfigVo> wxRobConfigVos = wxRobConfigService.queryList(wxRobConfigBo);
//查询机器人对应的用户
start(uniqueKey,new MyMsgHandler(uniqueKey,sseService,wxRobConfigVos.get(0)));
return login.login_3();
}
@PostMapping("/wxLogout")
public void wxLogout(@RequestParam String uniqueKey) {
WechatTools.logout(uniqueKey);
}
public void start(String uniqueKey,IMsgHandlerFace msgHandler) {
log.info("7.+++开启消息处理线程["+uniqueKey+"]+++");
new Thread(() -> MsgCenter.handleMsg(uniqueKey,msgHandler)).start();
}
}

View File

@@ -0,0 +1,114 @@
package org.ruoyi.system.controller.system;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.ruoyi.common.core.domain.R;
import org.ruoyi.common.core.validate.AddGroup;
import org.ruoyi.common.core.validate.EditGroup;
import org.ruoyi.common.excel.utils.ExcelUtil;
import org.ruoyi.common.idempotent.annotation.RepeatSubmit;
import org.ruoyi.common.log.annotation.Log;
import org.ruoyi.common.log.enums.BusinessType;
import org.ruoyi.common.mybatis.core.page.PageQuery;
import org.ruoyi.common.mybatis.core.page.TableDataInfo;
import org.ruoyi.common.satoken.utils.LoginHelper;
import org.ruoyi.common.web.core.BaseController;
import org.ruoyi.common.wechat.web.utils.UUIDShortUtil;
import org.ruoyi.system.domain.bo.WxRobConfigBo;
import org.ruoyi.system.domain.vo.WxRobConfigVo;
import org.ruoyi.system.service.IWxRobConfigService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 机器人
*
* @author Lion Li
* @date 2024-05-01
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/system/robConfig")
public class WxRobConfigController extends BaseController {
private final IWxRobConfigService wxRobConfigService;
/**
* 查询机器人列表
*/
@GetMapping("/list")
public TableDataInfo<WxRobConfigVo> list(WxRobConfigBo bo, PageQuery pageQuery) {
return wxRobConfigService.queryPageList(bo, pageQuery);
}
/**
* 查询当前用户绑定的机器人信息
* @param bo 查询参数
* @return 机器人信息
*/
@GetMapping("/getRobConfig")
public R<List<WxRobConfigVo>> botList(WxRobConfigBo bo) {
bo.setUserId(LoginHelper.getUserId());
return R.ok(wxRobConfigService.queryList(bo));
}
/**
* 导出机器人列表
*/
@Log(title = "机器人", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(WxRobConfigBo bo, HttpServletResponse response) {
List<WxRobConfigVo> list = wxRobConfigService.queryList(bo);
ExcelUtil.exportExcel(list, "机器人", WxRobConfigVo.class, response);
}
/**
* 获取机器人详细信息
*
* @param id 主键
*/
@GetMapping("/{id}")
public R<WxRobConfigVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(wxRobConfigService.queryById(id));
}
/**
* 新增机器人
*/
@Log(title = "机器人", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody WxRobConfigBo bo) {
String uniKey = UUIDShortUtil.generateShortUuid();
bo.setUniqueKey(uniKey);
return toAjax(wxRobConfigService.insertByBo(bo));
}
/**
* 修改机器人
*/
@Log(title = "机器人", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody WxRobConfigBo bo) {
return toAjax(wxRobConfigService.updateByBo(bo));
}
/**
* 删除机器人
*
* @param ids 主键串
*/
@Log(title = "删除机器人", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(wxRobConfigService.deleteWithValidByIds(List.of(ids), true));
}
}

View File

@@ -0,0 +1,62 @@
package org.ruoyi.system.controller.wxsingle;
import me.chanjar.weixin.common.bean.WxJsapiSignature;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.api.WxCpService;
import org.ruoyi.system.cofing.WxCpConfiguration;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import java.nio.charset.StandardCharsets;
import java.util.Formatter;
import java.util.HashMap;
import java.util.Map;
/**
* @author <a href="https://github.com/0katekate0">Wang_Wong</a>
*/
//@RestController
//@RequiredArgsConstructor
//@RequestMapping("/wx/cp/js/{corpId}/{agentId}/getJsConf")
public class WxJsController {
@PostMapping("/getJsConf")
public Map getJsConf(
@PathVariable String corpId,
@PathVariable Integer agentId,
String uri) throws WxErrorException {
final WxCpService wxCpService = WxCpConfiguration.getCpService(agentId);
if (wxCpService == null) {
throw new IllegalArgumentException(String.format("未找到对应agentId=[%d]的配置,请核实!", agentId));
}
WxJsapiSignature wxJsapiSignature = wxCpService.createJsapiSignature(uri);
String signature = wxJsapiSignature.getSignature();
String nonceStr = wxJsapiSignature.getNonceStr();
long timestamp = wxJsapiSignature.getTimestamp();
Map res = new HashMap<String, String>();
res.put("appId", corpId); // 必填企业微信的corpID
res.put("timestamp", timestamp); // 必填,生成签名的时间戳
res.put("nonceStr", nonceStr); // 必填,生成签名的随机串
res.put("signature", signature); // 必填,签名,见 附录-JS-SDK使用权限签名算法
return res;
}
public static String genNonce() {
return bytesToHex(Long.toString(System.nanoTime()).getBytes(StandardCharsets.UTF_8));
}
public static String bytesToHex(final byte[] hash) {
Formatter formatter = new Formatter();
for (byte b : hash) {
formatter.format("%02x", b);
}
String result = formatter.toString();
formatter.close();
return result;
}
}

View File

@@ -0,0 +1,85 @@
package org.ruoyi.system.controller.wxsingle;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage;
import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage;
import me.chanjar.weixin.cp.util.crypto.WxCpCryptUtil;
import org.apache.commons.lang3.StringUtils;
import org.ruoyi.common.core.utils.JsonUtils;
import org.ruoyi.system.cofing.WxCpConfiguration;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@RestController
@RequestMapping("/wx/cp")
@Slf4j
public class WxPortalController {
@Value("${wechat.cp.appConfigs[0].agentId}")
private Integer agentId;
@GetMapping(produces = "text/plain;charset=utf-8")
public String authGet(
@RequestParam(name = "msg_signature", required = false) String signature,
@RequestParam(name = "timestamp", required = false) String timestamp,
@RequestParam(name = "nonce", required = false) String nonce,
@RequestParam(name = "echostr", required = false) String echostr) {
log.info("\n接收到来自微信服务器的认证消息signature = [{}], timestamp = [{}], nonce = [{}], echostr = [{}]",
signature, timestamp, nonce, echostr);
if (StringUtils.isAnyBlank(signature, timestamp, nonce, echostr)) {
throw new IllegalArgumentException("请求参数非法,请核实!");
}
final WxCpService wxCpService = WxCpConfiguration.getCpService(agentId);
if (wxCpService == null) {
throw new IllegalArgumentException(String.format("未找到对应agentId=[%d]的配置,请核实!", agentId));
}
if (wxCpService.checkSignature(signature, timestamp, nonce, echostr)) {
return new WxCpCryptUtil(wxCpService.getWxCpConfigStorage()).decrypt(echostr);
}
return "非法请求";
}
@PostMapping(produces = "application/xml; charset=UTF-8")
public String post(
@RequestBody String requestBody,
@RequestParam("msg_signature") String signature,
@RequestParam("timestamp") String timestamp,
@RequestParam("nonce") String nonce) {
log.info("\n接收微信请求[signature=[{}], timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ",
signature, timestamp, nonce, requestBody);
final WxCpService wxCpService = WxCpConfiguration.getCpService(1000002);
WxCpXmlMessage inMessage = WxCpXmlMessage.fromEncryptedXml(requestBody, wxCpService.getWxCpConfigStorage(),
timestamp, nonce, signature);
log.debug("\n消息解密后内容为\n{} ", JsonUtils.toJson(inMessage));
WxCpXmlOutMessage outMessage = this.route(1000002, inMessage);
if (outMessage == null) {
return "";
}
String out = outMessage.toEncryptedXml(wxCpService.getWxCpConfigStorage());
log.debug("\n组装回复信息{}", out);
return out;
}
private WxCpXmlOutMessage route(Integer agentId, WxCpXmlMessage message) {
try {
return WxCpConfiguration.getRouters().get(agentId).route(message);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return null;
}
}

View File

@@ -9,32 +9,32 @@ import lombok.EqualsAndHashCode;
import java.io.Serial;
/**
* 配音角色对象 voice_role
* 应用市场
*
* @author Lion Li
* @date 2024-03-19
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("chat_audio_role")
public class VoiceRole extends BaseEntity {
@TableName("chat_app_store")
public class ChatAppStore extends BaseEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* id
* 主键
*/
@TableId(value = "id")
private Long id;
/**
* 角色名称
* 名称
*/
private String name;
/**
* 角色描述
* 描述
*/
private String description;
@@ -44,14 +44,9 @@ public class VoiceRole extends BaseEntity {
private String avatar;
/**
* 角色id
* 应用地址
*/
private String voiceId;
/**
* 音频地址
*/
private String fileUrl;
private String appUrl;
/**

View File

@@ -10,8 +10,7 @@ import org.ruoyi.common.tenant.core.TenantEntity;
import java.io.Serial;
/**
* 对话配置信息
对象 chat_config
* 对话配置信息对象 chat_config
*
* @author Lion Li
* @date 2024-04-13

View File

@@ -0,0 +1,58 @@
package org.ruoyi.system.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.ruoyi.common.mybatis.core.domain.BaseEntity;
import java.io.Serial;
/**
* 翻唱对象
*
* @author NSL
* @date 2024-12-25
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("chat_cover")
public class Cover extends BaseEntity {
@Serial
private static final long serialVersionUID = 1L;
@TableId(value = "id")
private Long id;
/**
* 歌曲名称
*/
private String coverMusicName;
/**
* 歌曲地址
*/
private String coverMusicUrl;
/**
* 歌手性别 枚举 FEMALE 女性 MALE 男性
*/
private String coverSingerGender;
/**
* 歌手姓名
*/
private String coverSingerName;
/**
* 用户性别 FEMALE MALE
*/
private String userGender;
/**
* 用户id
*/
private Long userId;
/**
* 本次消费金额
*/
private String cost;
/**
* 翻唱后的URL
*/
private String coverUrl;
}

View File

@@ -0,0 +1,38 @@
package org.ruoyi.system.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.ruoyi.common.mybatis.core.domain.BaseEntity;
import java.io.Serial;
/**
* 翻唱用户参考音频对象
*
* @author NSL
* @since 2024-12-25
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("chat_cover_prompt_audio")
public class CoverPromptAudio extends BaseEntity {
@Serial
private static final long serialVersionUID = 1L;
@TableId(value = "id")
private Long id;
/**
* 翻唱主表id
*/
private Long coverId;
/**
* 用户id
*/
private Long userId;
/**
* 参考音频
*/
private String promptAudioUrl;
}

View File

@@ -1,7 +1,7 @@
package org.ruoyi.system.domain.bo;
import org.ruoyi.common.mybatis.core.domain.BaseEntity;
import org.ruoyi.system.domain.VoiceRole;
import org.ruoyi.system.domain.ChatAppStore;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
@@ -9,15 +9,15 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 配音角色业务对象 voice_role
* 应用市场业务对象 voice_role
*
* @author Lion Li
* @date 2024-03-19
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = VoiceRole.class, reverseConvertGenerate = false)
public class VoiceRoleBo extends BaseEntity {
@AutoMapper(target = ChatAppStore.class, reverseConvertGenerate = false)
public class ChatAppStoreBo extends BaseEntity {
/**
* id
@@ -28,13 +28,13 @@ public class VoiceRoleBo extends BaseEntity {
/**
* 角色名称
*/
@NotBlank(message = "角色名称不能为空")
@NotBlank(message = "名称不能为空")
private String name;
/**
* 角色描述
*/
@NotBlank(message = "角色描述不能为空")
@NotBlank(message = "描述不能为空")
private String description;
/**
@@ -43,18 +43,11 @@ public class VoiceRoleBo extends BaseEntity {
@NotBlank(message = "头像不能为空")
private String avatar;
/**
* 角色id
*/
@NotBlank(message = "角色id不能为空")
private String voiceId;
/**
* 音频地址
*/
@NotBlank(message = "音频地址不能为空")
private String fileUrl;
@NotBlank(message = "应用地址不能为空")
private String appUrl;
/**
* 备注

View File

@@ -0,0 +1,34 @@
package org.ruoyi.system.domain.request.translation;
import lombok.Data;
/**
* 描述:翻译请求对象
*
* @author ageerle@163.com
* date 2025/1/13
*/
@Data
public class TranslationRequest {
/**
* 提示词
*/
private String prompt;
/**
* 模型名称
*/
private String model;
/**
* 源语言
*/
private String sourceLanguage;
/**
* 目标语言
*/
private String targetLanguage;
}

View File

@@ -4,7 +4,7 @@ import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.ruoyi.system.domain.VoiceRole;
import org.ruoyi.system.domain.ChatAppStore;
import java.io.Serial;
import java.io.Serializable;
@@ -12,15 +12,15 @@ import java.io.Serializable;
/**
* 配音角色视图对象 voice_role
* 应用市场视图对象
*
* @author Lion Li
* @date 2024-03-19
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = VoiceRole.class)
public class VoiceRoleVo implements Serializable {
@AutoMapper(target = ChatAppStore.class)
public class ChatAppStoreVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@@ -34,13 +34,13 @@ public class VoiceRoleVo implements Serializable {
/**
* 角色名称
*/
@ExcelProperty(value = "角色名称")
@ExcelProperty(value = "名称")
private String name;
/**
* 角色描述
*/
@ExcelProperty(value = "角色描述")
@ExcelProperty(value = "描述")
private String description;
/**
@@ -49,23 +49,11 @@ public class VoiceRoleVo implements Serializable {
@ExcelProperty(value = "头像")
private String avatar;
/**
* 角色id
*/
@ExcelProperty(value = "角色id")
private String voiceId;
/**
* 音频地址
*/
@ExcelProperty(value = "音频地址")
private String fileUrl;
/**
* 音频预处理实验性
*/
@ExcelProperty(value = "音频预处理")
private String preProcess;
@ExcelProperty(value = "应用地址")
private String appUrl;
/**
* 备注

View File

@@ -0,0 +1,24 @@
package org.ruoyi.system.domain.vo.cover;
import lombok.Data;
/**
* 翻唱回调VO
*
* @author NSL
* @since 2024-12-26
*/
@Data
public class CoverCallbackVo {
/** 本次请求的订单号 */
private String orderId;
/** 用户ID */
private String userId;
/** 本次消费金额 */
private String cost;
/** 翻唱后的URL */
private String coverUrl;
}

View File

@@ -0,0 +1,60 @@
package org.ruoyi.system.domain.vo.cover;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* 翻唱歌曲入参
*
* @author NSL
* @since 2024-12-25
*/
@Data
public class CoverParamVo implements Serializable {
/**
* 歌曲名称
*/
@ExcelProperty(value = "歌曲名称")
private String coverMusicName;
/**
* 歌曲地址
*/
@ExcelProperty(value = "歌曲地址")
private String coverMusicUrl;
/**
* 歌手性别 枚举 FEMALE 女性 MALE 男性
*/
@ExcelProperty(value = "歌手性别")
private String coverSingerGender;
/**
* 歌手姓名
*/
@ExcelProperty(value = "歌手姓名")
private String coverSingerName;
/**
* 参考音频:用户首次翻唱,必填;第二次及之后翻唱:若为空则沿用最近一次参考音频训练模型,若不为空则使用该参考音频训练模型。
* 可多传
*/
@ExcelProperty(value = "参考音频")
private List<String> promptAudioUrl;
/**
* 用户性别 FEMALE MALE
*/
@ExcelProperty(value = "用户性别")
private String userGender;
/**
* 用户id
*/
@ExcelProperty(value = "用户id")
private String userId;
/**
* 业务主键id
*/
@ExcelProperty(value = "业务主键id")
private String orderId;
}

View File

@@ -0,0 +1,44 @@
package org.ruoyi.system.domain.vo.cover;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.ruoyi.system.domain.Cover;
import java.io.Serial;
import java.io.Serializable;
/**
* 翻唱用户参考音频视图对象
*
* @author NSL
* @since 2024-12-25
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = Cover.class)
public class CoverPromptAudioVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@ExcelProperty(value = "id")
private Long id;
/**
* 用户id
*/
@ExcelProperty(value = "用户id")
private Long userId;
/**
* 翻唱主表id
*/
@ExcelProperty(value = "翻唱主表id")
private Long coverId;
/**
* 翻唱后的URL
*/
@ExcelProperty(value = "参考音频")
private String promptAudioUrl;
}

View File

@@ -0,0 +1,68 @@
package org.ruoyi.system.domain.vo.cover;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.ruoyi.system.domain.Cover;
import java.io.Serial;
import java.io.Serializable;
/**
* 翻唱视图对象
*
* @author NSL
* @date 2024-12-25
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = Cover.class)
public class CoverVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@ExcelProperty(value = "id")
private Long id;
/**
* 歌曲名称
*/
@ExcelProperty(value = "歌曲名称")
private String coverMusicName;
/**
* 歌曲地址
*/
@ExcelProperty(value = "歌曲地址")
private String coverMusicUrl;
/**
* 歌手性别 枚举 FEMALE 女性 MALE 男性
*/
@ExcelProperty(value = "歌手性别")
private String coverSingerGender;
/**
* 歌手姓名
*/
@ExcelProperty(value = "歌手姓名")
private String coverSingerName;
/**
* 用户性别 FEMALE MALE
*/
@ExcelProperty(value = "用户性别")
private String userGender;
/**
* 用户id
*/
@ExcelProperty(value = "用户id")
private Long userId;
/**
* 本次消费金额
*/
@ExcelProperty(value = "本次消费金额")
private String cost;
/**
* 翻唱后的URL
*/
@ExcelProperty(value = "翻唱后的URL")
private String coverUrl;
}

View File

@@ -0,0 +1,35 @@
package org.ruoyi.system.domain.vo.cover;
import lombok.Data;
/**
* @author NSL
* @since 2024-12-25
*/
@Data
public class MusicVo {
/**
* 歌曲名称
*/
private String musicName;
/**
* 音乐地址
*/
private String mp3Url;
/**
* 歌曲图片
*/
private String pic;
/**
* 歌手或乐队名
*/
private String singerName;
/**
* 歌手性别
*/
private String singerGender;
}

View File

@@ -0,0 +1,29 @@
package org.ruoyi.system.domain.vo.ppt;
import lombok.Data;
/**
* 查询所有PPT列表查询参数
*
* @author NSL
* @since 2024/12/31
*/
@Data
public class PptAllQueryDto {
// 分页
private Integer page;
// 每页大小最大不超过100
private Integer size;
// ppt id非必填
private String id;
// 第三方用户ID非必填
private String uid;
// 模板ID非必填
private String templateId;
// 创建开始时间(非必填)
private String startDate;
// 创建结束时间(非必填)
private String endDate;
// 按时间倒序返回(非必填)
private boolean desc;
}

View File

@@ -0,0 +1,27 @@
package org.ruoyi.system.domain.vo.ppt;
import lombok.Data;
/**
* 生成大纲内容参数
*
* @author NSL
* @since 2024/12/31
*/
@Data
public class PptGenerateContentDto {
// 模板ID非必填
private String templateId;
// 大纲 markdown 文本
private String outlineMarkdown;
// 异步生成PPT这里必须为 true 才会流式生成)
private boolean asyncGenPptx = false;
// 用户要求
private String prompt;
// 文件数据url调用解析文件内容接口返回
private String dataUrl;
}

View File

@@ -0,0 +1,25 @@
package org.ruoyi.system.domain.vo.ppt;
import lombok.Data;
/**
* 生成PPT大纲参数
*
* @author NSL
* @since 2024/12/31
*/
@Data
public class PptGenerateOutlineDto {
// 是否流式生成(默认流式)
private boolean stream = true;
// 篇幅长度short/medium/long, 默认 medium, 分别对应: 10-15页/20-30页/25-35页
private String length = "medium";
// 语言: zh/zh-Hant/en/ja/ko/ar/de/fr/it/pt/es/ru
private String lang;
// 用户要求小于50字
private String prompt;
// 方式一:通过主题创建 主题与dataUrl可同时存在
private String subject;
// 方式二:通过文件内容创建 文件数据url通过解析文件内容接口返回与subject可同时存在
private String dataUrl;
}

View File

@@ -0,0 +1,24 @@
package org.ruoyi.system.domain.vo.ppt;
import lombok.Data;
/**
* 生成PPT参数
*
* @author NSL
* @since 2024/12/31
*/
@Data
public class PptGeneratePptxDto {
// 模板ID非必填
private String templateId;
// 是否返回PPT数据结构
private boolean pptxProperty;
// 大纲内容markdown
private String outlineContentMarkdown;
// 备注PPT页面备注非必填数组 ["内容页面一备注", "内容页面二备注"]
private String notes;
}

View File

@@ -0,0 +1,24 @@
package org.ruoyi.system.domain.vo.ppt;
import lombok.Data;
/**
* PPT模板筛选查询参数
*
* @author NSL
* @since 2024/12/31
*/
@Data
public class PptTemplateFilterDto {
// 模板类型必传1系统模板、4用户自定义模板
private Integer type;
// 类目筛选 ['年终总结', '教育培训', '医学医疗', '商业计划书', '企业介绍', '毕业答辩', '营销推广', '晚会表彰', '个人简历']
private String category;
// 风格筛选 ['扁平简约', '商务科技', '文艺清新', '卡通手绘', '中国风', '创意时尚', '创意趣味']
private String style;
// 主题颜色筛选 ['#FA920A', '#589AFD', '#7664FA', '#65E5EC', '#61D328', '#F5FD59', '#E05757', '#8F5A0B', '#FFFFFF', '#000000']
private String themeColor;
}

View File

@@ -0,0 +1,19 @@
package org.ruoyi.system.domain.vo.ppt;
import lombok.Data;
/**
* 分页查询 PPT 模板查询参数
*
* @author NSL
* @since 2024/12/31
*/
@Data
public class PptTemplateQueryDto {
// 分页
private Integer page;
// 每页大小最大不超过100
private Integer size;
// 模型筛选参数
private PptTemplateFilterDto filters;
}

View File

@@ -0,0 +1,230 @@
package org.ruoyi.system.handler;
import com.jfinal.kit.PropKit;
import org.ruoyi.common.chat.domain.request.ChatRequest;
import org.ruoyi.common.chat.entity.chat.ChatCompletion;
import org.ruoyi.common.chat.entity.images.Item;
import org.ruoyi.common.wechat.itchat4j.beans.BaseMsg;
import org.ruoyi.common.wechat.itchat4j.core.CoreManage;
import org.ruoyi.common.wechat.itchat4j.face.IMsgHandlerFace;
import org.ruoyi.common.wechat.itchat4j.utils.LogInterface;
import org.ruoyi.common.wechat.itchat4j.utils.enums.SendMsgType;
import org.ruoyi.common.wechat.itchat4j.utils.tools.CommonTools;
import org.ruoyi.common.wechat.web.constant.ConfigKeys;
import org.ruoyi.common.wechat.web.model.WxRobConfig;
import org.ruoyi.common.wechat.web.model.WxRobKeyword;
import org.ruoyi.system.domain.vo.WxRobConfigVo;
import org.ruoyi.system.service.ISseService;
import org.apache.commons.lang3.StringUtils;
import java.io.File;
import java.util.List;
import java.util.regex.Matcher;
/**
* 消息处理实现 默认方案
* @author WesleyOne
* @create 2018/12/11
*/
public class MyMsgHandler implements IMsgHandlerFace,LogInterface {
private String uniqueKey;
private final ISseService sseService;
private WxRobConfigVo robConfigVo;
public MyMsgHandler(String uniqueKey,ISseService sseService,WxRobConfigVo robConfigVo){
this.uniqueKey = uniqueKey;
this.sseService = sseService;
this.robConfigVo = robConfigVo;
}
private String getDownloadPath(String fileName) {
String download_path = PropKit.get("download_path");
return download_path+ File.separator + uniqueKey +File.separator + fileName;
}
@Override
public void textMsgHandle(BaseMsg msg) {
String fromNickName = msg.getFromNickName();
String fromUserName = msg.getFromUserName();
boolean groupMsg = msg.isGroupMsg();
String text = msg.getText().trim();
if (groupMsg && text.contains("@" + fromNickName)) {
handleGroupMessage(fromNickName, fromUserName, text);
} else {
respondWithChatGPT(fromUserName, text);
}
}
private void handleGroupMessage(String fromNickName, String fromUserName, String text) {
String prompt = text.replace("@" + fromNickName + " ", "");
if (prompt.startsWith("")) {
generateAndSendImage(fromUserName, prompt.replaceFirst("", ""));
} else {
respondWithChatGPT(fromUserName, prompt);
}
}
private void generateAndSendImage(String fromUserName, String prompt) {
List<Item> items = sseService.wxDall(prompt,robConfigVo.getUserId().toString());
WxRobKeyword robKeyword = new WxRobKeyword();
robKeyword.setTypeData(SendMsgType.IMG.toValue());
robKeyword.setValueData(items.get(0).getUrl());
sendDataByType(fromUserName, robKeyword);
}
private void respondWithChatGPT(String fromUserName, String prompt) {
ChatRequest chatBO = new ChatRequest();
//chatBO.setPrompt(prompt);
chatBO.setModel(ChatCompletion.Model.GPT_3_5_TURBO.getName());
String chat = sseService.chat(chatBO,robConfigVo.getUserId().toString());
WxRobKeyword robKeyword = new WxRobKeyword();
robKeyword.setTypeData(SendMsgType.TEXT.toValue());
robKeyword.setValueData(chat);
sendDataByType(fromUserName, robKeyword);
}
private boolean sendDataByType(String fromUserName, WxRobKeyword robKeyword) {
String data;
String type;
if (robKeyword != null){
data = robKeyword.getValueData();
type = robKeyword.getTypeData();
CoreManage.addSendMsg4UserName(uniqueKey,fromUserName,data,SendMsgType.fromValue(type));
return true;
}
return false;
}
@Override
public void sysMsgHandle(BaseMsg msg) {
String fromNickName = msg.getFromNickName();
String fromUserName = msg.getFromUserName();
boolean groupMsg = msg.isGroupMsg();
/**
* 群里的新人进群消息处理
* 优先发专门这个群的欢迎词
* 没有发通用的
* 欢迎词内容实质就是在最前面加上@昵称\n
*
* 欢迎词的关键字
* @see ConfigKeys#DEAFAULT_WELCOME
*/
// 解析新人名字
String text = msg.getContent();
String newNickName = "";
Matcher matcher = CommonTools.getMatcher("邀请\"(.+?)\"加入了群聊", text);
if (matcher.find()){
newNickName = matcher.group(1);
}else{
matcher = CommonTools.getMatcher("\"(.+?)\"通过扫描(.+?)分享的二维码加入群聊", text);
if (matcher.find()){
newNickName = matcher.group(1);
}
}
if (StringUtils.isNotEmpty(newNickName)){
WxRobConfig robConfig = WxRobConfig.dao.findFirst("SELECT * FROM wx_rob_config WHERE unique_key = ? LIMIT 1", uniqueKey);
if(robConfig != null && robConfig.getEnable()){
// 判断是否要回复
boolean isOpen = false;
// 判断是群聊的话是否允许回复 昵称关键字
if (robConfig.getToGroup() && groupMsg){
isOpen = true;
}
if (isOpen){
WxRobKeyword robKeyword = WxRobKeyword.dao.findFirst("SELECT * FROM wx_rob_keyword WHERE unique_key = ? AND key_data = ? AND nick_name = ? AND enable = 1 AND to_group = ? ORDER BY id DESC LIMIT 1", uniqueKey, ConfigKeys.DEAFAULT_WELCOME,fromNickName,msg.isGroupMsg()?1:0);
if (sendSysWelcomeMsg(fromUserName, newNickName, robKeyword)){ return;}
}
// 没有专门的关键字,则使用默认关键字
isOpen = false;
// 判断是群聊的话是否允许回复 昵称关键字
if (robConfig.getDefaultGroup() && groupMsg){
isOpen = true;
}
if (isOpen){
WxRobKeyword defaultRobKeyword = WxRobKeyword.dao.findFirst("SELECT * FROM wx_rob_keyword WHERE unique_key = ? AND key_data = ? AND nick_name = ? AND enable = 1 AND to_group = ? ORDER BY id DESC LIMIT 1", uniqueKey, ConfigKeys.DEAFAULT_WELCOME, ConfigKeys.DEAFAULT_KEYWORD,msg.isGroupMsg()?1:0);
if (sendSysWelcomeMsg(fromUserName, newNickName, defaultRobKeyword)){ return;}
}
}
}
}
/**
* 发送欢迎内容
* @param fromUserName
* @param newNickName
* @param robKeyword
* @return
*/
private boolean sendSysWelcomeMsg(String fromUserName, String newNickName, WxRobKeyword robKeyword) {
if (robKeyword != null){
if (robKeyword.getTypeData().equals(SendMsgType.TEXT.toValue())){
robKeyword.setValueData(String.format("@%s\n%s",newNickName,robKeyword.getValueData()));
}
if (sendDataByType(fromUserName, robKeyword)) {
return true;
}
}
return false;
}
@Override
public void picMsgHandle(BaseMsg msg) {
// // 这里使用收到图片的时间作为文件名
// String fileName = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date()) + ".jpg";
// // 保存图片的路径
// String picPath = getDownloadPath(fileName);
// // 调用此方法来保存图片
// DownloadTools.getDownloadFn(msg, MsgTypeEnum.PIC.getType(), picPath, this.uniqueKey);
// CoreManage.addSendMsg4UserName(uniqueKey,msg.getFromUserName(),"图片保存成功",SendMsgType.TEXT);
return;
}
@Override
public void voiceMsgHandle(BaseMsg msg) {
// // 这里使用收到语音的时间作为文件名
// String fileName = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date()) + ".mp3";
// // 保存语音的路径
// String voicePath = getDownloadPath(fileName);
// // 调用此方法来保存语音
// DownloadTools.getDownloadFn(msg, MsgTypeEnum.VOICE.getType(), voicePath, this.uniqueKey);
// CoreManage.addSendMsg4UserName(uniqueKey,msg.getFromUserName(),"声音保存成功",SendMsgType.TEXT);
return;
}
@Override
public void videoMsgHandle(BaseMsg msg) {
// // 这里使用收到小视频的时间作为文件名
// String fileName = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date()) + ".mp4";
// // 保存小视频的路径
// String viedoPath = getDownloadPath(fileName);
// // 调用此方法来保存小视频
// DownloadTools.getDownloadFn(msg, MsgTypeEnum.VIEDO.getType(), viedoPath,this.uniqueKey);
// CoreManage.addSendMsg4UserName(uniqueKey,msg.getFromUserName(),"视频保存成功",SendMsgType.TEXT);
return;
}
@Override
public void nameCardMsgHandle(BaseMsg msg) {
return ;
}
@Override
public void verifyAddFriendMsgHandle(BaseMsg msg) {
return ;
}
@Override
public void mediaMsgHandle(BaseMsg msg) {
return ;
}
}

View File

@@ -0,0 +1,9 @@
package org.ruoyi.system.handler.wxcp;
import me.chanjar.weixin.cp.message.WxCpMessageHandler;
/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public abstract class AbstractHandler implements WxCpMessageHandler {
}

View File

@@ -0,0 +1,33 @@
package org.ruoyi.system.handler.wxcp;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage;
import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage;
import org.ruoyi.common.core.utils.JsonUtils;
import org.ruoyi.system.builder.TextBuilder;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* 通讯录变更事件处理器.
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@Slf4j
@Component
public class ContactChangeHandler extends AbstractHandler {
@Override
public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService cpService,
WxSessionManager sessionManager) {
String content = "收到通讯录变更事件,内容:" + JsonUtils.toJson(wxMessage);
log.info(content);
return new TextBuilder().build(content, wxMessage, cpService);
}
}

View File

@@ -0,0 +1,29 @@
package org.ruoyi.system.handler.wxcp;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage;
import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage;
import java.util.Map;
/**
* <pre>
*
* Created by Binary Wang on 2018/8/27.
* </pre>
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@Slf4j
public class EnterAgentHandler extends AbstractHandler {
private static final int TEST_AGENT = 1000002;
@Override
public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService wxCpService, WxSessionManager sessionManager) throws WxErrorException {
// do something
return null;
}
}

View File

@@ -0,0 +1,44 @@
package org.ruoyi.system.handler.wxcp;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage;
import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage;
import org.ruoyi.system.builder.TextBuilder;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@Slf4j
@Component
public class LocationHandler extends AbstractHandler {
@Override
public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService cpService,
WxSessionManager sessionManager) {
if (wxMessage.getMsgType().equals(WxConsts.XmlMsgType.LOCATION)) {
//TODO 接收处理用户发送的地理位置消息
try {
String content = "感谢反馈,您的的地理位置已收到!";
return new TextBuilder().build(content, wxMessage, null);
} catch (Exception e) {
log.error("位置消息接收处理失败", e);
return null;
}
}
//上报地理位置事件
log.info("\n上报地理位置纬度 : {}\n经度 : {}\n精度 : {}",
wxMessage.getLatitude(), wxMessage.getLongitude(), String.valueOf(wxMessage.getPrecision()));
//TODO 可以将用户地理位置信息保存到本地数据库,以便以后使用
return null;
}
}

View File

@@ -0,0 +1,26 @@
package org.ruoyi.system.handler.wxcp;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage;
import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage;
import org.ruoyi.common.core.utils.JsonUtils;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@Slf4j
@Component
public class LogHandler extends AbstractHandler {
@Override
public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService cpService,
WxSessionManager sessionManager) {
log.info("\n接收到请求消息内容{}", JsonUtils.toJson(wxMessage));
return null;
}
}

View File

@@ -0,0 +1,34 @@
package org.ruoyi.system.handler.wxcp;
import me.chanjar.weixin.common.api.WxConsts.MenuButtonType;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage;
import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@Component
public class MenuHandler extends AbstractHandler {
@Override
public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService cpService,
WxSessionManager sessionManager) {
String msg = String.format("type:%s, event:%s, key:%s",
wxMessage.getMsgType(), wxMessage.getEvent(),
wxMessage.getEventKey());
if (MenuButtonType.VIEW.equals(wxMessage.getEvent())) {
return null;
}
return WxCpXmlOutMessage.TEXT().content(msg)
.fromUser(wxMessage.getToUserName()).toUser(wxMessage.getFromUserName())
.build();
}
}

View File

@@ -0,0 +1,44 @@
package org.ruoyi.system.handler.wxcp;
import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage;
import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage;
import org.ruoyi.system.builder.TextBuilder;
import org.ruoyi.system.service.ISseService;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@Component
@RequiredArgsConstructor
public class MsgHandler extends AbstractHandler {
private final ISseService sseService;
@Override
public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService cpService,
WxSessionManager sessionManager) {
final String msgType = wxMessage.getMsgType();
if (msgType == null) {
// 如果msgType没有就自己根据具体报文内容做处理
}
if (!msgType.equals(WxConsts.XmlMsgType.EVENT)) {
//TODO 可以选择将消息保存到本地
}
//TODO 组装回复消息
String content = sseService.wxCpChat(wxMessage.getContent());
return new TextBuilder().build(content, wxMessage, cpService);
}
}

View File

@@ -0,0 +1,23 @@
package org.ruoyi.system.handler.wxcp;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage;
import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@Component
public class NullHandler extends AbstractHandler {
@Override
public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService cpService,
WxSessionManager sessionManager) {
return null;
}
}

View File

@@ -0,0 +1,8 @@
package org.ruoyi.system.handler.wxcp;
/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public abstract class ScanHandler extends AbstractHandler {
}

View File

@@ -0,0 +1,63 @@
package org.ruoyi.system.handler.wxcp;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.WxCpUser;
import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage;
import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage;
import org.ruoyi.system.builder.TextBuilder;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@Slf4j
@Component
public class SubscribeHandler extends AbstractHandler {
@Override
public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService cpService,
WxSessionManager sessionManager) throws WxErrorException {
log.info("新关注用户 OPENID: " + wxMessage.getFromUserName());
// 获取微信用户基本信息
WxCpUser userWxInfo = cpService.getUserService().getById(wxMessage.getFromUserName());
if (userWxInfo != null) {
// TODO 可以添加关注用户到本地
}
WxCpXmlOutMessage responseResult = null;
try {
responseResult = handleSpecial(wxMessage);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
if (responseResult != null) {
return responseResult;
}
try {
return new TextBuilder().build("感谢关注", wxMessage, cpService);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return null;
}
/**
* 处理特殊请求,比如如果是扫码进来的,可以做相应处理
*/
private WxCpXmlOutMessage handleSpecial(WxCpXmlMessage wxMessage) {
//TODO
return null;
}
}

View File

@@ -0,0 +1,28 @@
package org.ruoyi.system.handler.wxcp;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage;
import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@Slf4j
@Component
public class UnsubscribeHandler extends AbstractHandler {
@Override
public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService cpService,
WxSessionManager sessionManager) {
String openId = wxMessage.getFromUserName();
log.info("取消关注用户 OPENID: " + openId);
// TODO 可以更新本地数据库为取消关注状态
return null;
}
}

View File

@@ -104,8 +104,8 @@ public class SSEEventSourceListener extends EventSourceListener {
if(completionResponse == null || CollectionUtil.isEmpty(completionResponse.getChoices())){
return;
}
String content = "completionResponse.getChoices().get(0).getDelta().getContent()";
if(StringUtils.isEmpty(content)){
Object content = completionResponse.getChoices().get(0).getDelta().getContent();
if(content == null){
return;
}
if(StringUtils.isEmpty(modelName)){

View File

@@ -0,0 +1,15 @@
package org.ruoyi.system.mapper;
import org.ruoyi.common.mybatis.core.mapper.BaseMapperPlus;
import org.ruoyi.system.domain.ChatAppStore;
import org.ruoyi.system.domain.vo.ChatAppStoreVo;
/**
* 应用市场Mapper接口
*
* @author Lion Li
* @date 2024-03-19
*/
public interface ChatAppStoreMapper extends BaseMapperPlus<ChatAppStore, ChatAppStoreVo> {
}

View File

@@ -0,0 +1,15 @@
package org.ruoyi.system.mapper;
import org.ruoyi.common.mybatis.core.mapper.BaseMapperPlus;
import org.ruoyi.system.domain.Cover;
import org.ruoyi.system.domain.vo.cover.CoverVo;
/**
* 翻唱Mapper接口
*
* @author NSL
* @since 2024-12-25
*/
public interface CoverMapper extends BaseMapperPlus<Cover, CoverVo> {
}

View File

@@ -0,0 +1,23 @@
package org.ruoyi.system.mapper;
import org.ruoyi.common.mybatis.core.mapper.BaseMapperPlus;
import org.ruoyi.system.domain.CoverPromptAudio;
import org.ruoyi.system.domain.vo.cover.CoverPromptAudioVo;
import java.util.List;
/**
* 翻唱用户参考音频Mapper接口
*
* @author NSL
* @since 2024-12-25
*/
public interface CoverPromptAudioMapper extends BaseMapperPlus<CoverPromptAudio, CoverPromptAudioVo> {
/**
* 获取最近一次翻唱记录
* @param userId 用户id
* @return 翻唱记录
*/
List<CoverPromptAudioVo> selectLatestVoByUserId(Long userId);
}

View File

@@ -1,15 +0,0 @@
package org.ruoyi.system.mapper;
import org.ruoyi.common.mybatis.core.mapper.BaseMapperPlus;
import org.ruoyi.system.domain.VoiceRole;
import org.ruoyi.system.domain.vo.VoiceRoleVo;
/**
* 配音角色Mapper接口
*
* @author Lion Li
* @date 2024-03-19
*/
public interface VoiceRoleMapper extends BaseMapperPlus<VoiceRole, VoiceRoleVo> {
}

View File

@@ -0,0 +1,19 @@
package org.ruoyi.system.response.rolelist;
import lombok.Data;
@Data
public class ChatAppStoreVO {
private String name;
private String description;
private String voicesId;
private String avatar;
private String appUrl;
}

View File

@@ -1,32 +0,0 @@
package org.ruoyi.system.response.rolelist;
import lombok.Data;
/**
* 描述:
*
* @author ageerle@163.com
* date 2024/4/27
*/
@Data
public class RoleListVO {
private String name;
private String description;
private String voicesId;
private String avatar;
private String previewAudio;
public RoleListVO(String name, String description, String voicesId, String previewAudio,String avatar) {
this.name = name;
this.description = description;
this.voicesId = voicesId;
this.previewAudio = previewAudio;
this.avatar = avatar;
}
}

Some files were not shown because too many files have changed in this diff Show More