mirror of
https://gitcode.com/ageerle/ruoyi-ai.git
synced 2026-04-11 18:47:20 +00:00
feat(更新日志):
更新日志 1. 移除个人微信模块 2. 移除直播模块 3. 移除gpts模块 4. 移除应用商店模块 5. 移除套餐管理模块 6. 移除兑换管理模块 ## 微信相关 小程序相关功能迁移至企业版 微信公众号/微信机器人迁移至企业版 微信支付迁移至企业版 ## 功能模块 智能体模块迁移至企业版 插件管理改为MCP应用并迁移至企业版 知识库: excel解析迁移至企业版 pdf图片解析迁移至企业版 milvus qdrant扩展 迁移至企业版
This commit is contained in:
@@ -21,7 +21,6 @@
|
||||
<module>ruoyi-chat</module>
|
||||
<module>ruoyi-system</module>
|
||||
<module>ruoyi-generator</module>
|
||||
<module>ruoyi-wechat</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
package org.ruoyi.chat.controller.chat;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.*;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import org.ruoyi.common.excel.utils.ExcelUtil;
|
||||
import org.ruoyi.common.idempotent.annotation.RepeatSubmit;
|
||||
import org.ruoyi.core.page.TableDataInfo;
|
||||
import org.ruoyi.common.web.core.BaseController;
|
||||
import org.ruoyi.domain.bo.ChatAgentManageBo;
|
||||
import org.ruoyi.domain.vo.ChatAgentManageVo;
|
||||
import org.ruoyi.service.IChatAgentManageService;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.ruoyi.common.log.annotation.Log;
|
||||
import org.ruoyi.core.page.PageQuery;
|
||||
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.log.enums.BusinessType;
|
||||
|
||||
/**
|
||||
* 智能体管理
|
||||
*
|
||||
* @author ageerle
|
||||
* @date 2025-04-08
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/system/agentManage")
|
||||
public class ChatAgentManageController extends BaseController {
|
||||
|
||||
private final IChatAgentManageService chatAgentManageService;
|
||||
|
||||
/**
|
||||
* 查询智能体管理列表
|
||||
*/
|
||||
@SaCheckPermission("system:agentManage:list")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<ChatAgentManageVo> list(ChatAgentManageBo bo, PageQuery pageQuery) {
|
||||
return chatAgentManageService.queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出智能体管理列表
|
||||
*/
|
||||
@SaCheckPermission("system:agentManage:export")
|
||||
@Log(title = "智能体管理", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(ChatAgentManageBo bo, HttpServletResponse response) {
|
||||
List<ChatAgentManageVo> list = chatAgentManageService.queryList(bo);
|
||||
ExcelUtil.exportExcel(list, "智能体管理", ChatAgentManageVo.class, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取智能体管理详细信息
|
||||
*
|
||||
* @param id 主键
|
||||
*/
|
||||
@SaCheckPermission("system:agentManage:query")
|
||||
@GetMapping("/{id}")
|
||||
public R<ChatAgentManageVo> getInfo(@NotNull(message = "主键不能为空")
|
||||
@PathVariable Long id) {
|
||||
return R.ok(chatAgentManageService.queryById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增智能体管理
|
||||
*/
|
||||
@SaCheckPermission("system:agentManage:add")
|
||||
@Log(title = "智能体管理", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping()
|
||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody ChatAgentManageBo bo) {
|
||||
return toAjax(chatAgentManageService.insertByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改智能体管理
|
||||
*/
|
||||
@SaCheckPermission("system:agentManage:edit")
|
||||
@Log(title = "智能体管理", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping()
|
||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody ChatAgentManageBo bo) {
|
||||
return toAjax(chatAgentManageService.updateByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除智能体管理
|
||||
*
|
||||
* @param ids 主键串
|
||||
*/
|
||||
@SaCheckPermission("system:agentManage:remove")
|
||||
@Log(title = "智能体管理", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空")
|
||||
@PathVariable Long[] ids) {
|
||||
return toAjax(chatAgentManageService.deleteWithValidByIds(List.of(ids), true));
|
||||
}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
package org.ruoyi.chat.controller.chat;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.*;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import org.ruoyi.common.excel.utils.ExcelUtil;
|
||||
import org.ruoyi.common.idempotent.annotation.RepeatSubmit;
|
||||
import org.ruoyi.core.page.TableDataInfo;
|
||||
import org.ruoyi.domain.bo.ChatAppStoreBo;
|
||||
import org.ruoyi.domain.vo.ChatAppStoreVo;
|
||||
import org.ruoyi.service.IChatAppStoreService;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.ruoyi.common.log.annotation.Log;
|
||||
import org.ruoyi.common.web.core.BaseController;
|
||||
import org.ruoyi.core.page.PageQuery;
|
||||
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.log.enums.BusinessType;
|
||||
|
||||
/**
|
||||
* 应用商店
|
||||
*
|
||||
* @author ageerle
|
||||
* @date 2025-04-08
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/system/appStore")
|
||||
public class ChatAppStoreController extends BaseController {
|
||||
|
||||
private final IChatAppStoreService chatAppStoreService;
|
||||
|
||||
/**
|
||||
* 查询应用商店列表
|
||||
*/
|
||||
@SaCheckPermission("system:appStore:list")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<ChatAppStoreVo> list(ChatAppStoreBo bo, PageQuery pageQuery) {
|
||||
return chatAppStoreService.queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出应用商店列表
|
||||
*/
|
||||
@SaCheckPermission("system:appStore:export")
|
||||
@Log(title = "应用商店", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(ChatAppStoreBo bo, HttpServletResponse response) {
|
||||
List<ChatAppStoreVo> list = chatAppStoreService.queryList(bo);
|
||||
ExcelUtil.exportExcel(list, "应用商店", ChatAppStoreVo.class, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取应用商店详细信息
|
||||
*
|
||||
* @param id 主键
|
||||
*/
|
||||
@SaCheckPermission("system:appStore:query")
|
||||
@GetMapping("/{id}")
|
||||
public R<ChatAppStoreVo> getInfo(@NotNull(message = "主键不能为空")
|
||||
@PathVariable Long id) {
|
||||
return R.ok(chatAppStoreService.queryById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增应用商店
|
||||
*/
|
||||
@SaCheckPermission("system:appStore:add")
|
||||
@Log(title = "应用商店", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping()
|
||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody ChatAppStoreBo bo) {
|
||||
return toAjax(chatAppStoreService.insertByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改应用商店
|
||||
*/
|
||||
@SaCheckPermission("system:appStore:edit")
|
||||
@Log(title = "应用商店", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping()
|
||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody ChatAppStoreBo bo) {
|
||||
return toAjax(chatAppStoreService.updateByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除应用商店
|
||||
*
|
||||
* @param ids 主键串
|
||||
*/
|
||||
@SaCheckPermission("system:appStore:remove")
|
||||
@Log(title = "应用商店", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空")
|
||||
@PathVariable Long[] ids) {
|
||||
return toAjax(chatAppStoreService.deleteWithValidByIds(List.of(ids), true));
|
||||
}
|
||||
}
|
||||
@@ -10,15 +10,6 @@ import org.ruoyi.common.chat.request.ChatRequest;
|
||||
import org.ruoyi.common.chat.entity.Tts.TextToSpeech;
|
||||
import org.ruoyi.common.chat.entity.files.UploadFileResponse;
|
||||
import org.ruoyi.common.chat.entity.whisper.WhisperResponse;
|
||||
import org.ruoyi.common.core.domain.R;
|
||||
import org.ruoyi.common.core.domain.model.LoginUser;
|
||||
import org.ruoyi.common.core.exception.base.BaseException;
|
||||
import org.ruoyi.core.page.PageQuery;
|
||||
import org.ruoyi.core.page.TableDataInfo;
|
||||
import org.ruoyi.common.satoken.utils.LoginHelper;
|
||||
import org.ruoyi.domain.bo.ChatMessageBo;
|
||||
import org.ruoyi.domain.vo.ChatMessageVo;
|
||||
import org.ruoyi.service.IChatMessageService;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
@@ -41,8 +32,6 @@ public class ChatController {
|
||||
|
||||
private final ISseService sseService;
|
||||
|
||||
private final IChatMessageService chatMessageService;
|
||||
|
||||
/**
|
||||
* 聊天接口
|
||||
*/
|
||||
@@ -84,21 +73,4 @@ public class ChatController {
|
||||
return sseService.textToSpeed(textToSpeech);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 聊天记录
|
||||
*/
|
||||
@PostMapping("/chatList")
|
||||
@ResponseBody
|
||||
public R<TableDataInfo<ChatMessageVo>> list(@RequestBody @Valid ChatMessageBo chatRequest, @RequestBody PageQuery pageQuery) {
|
||||
// 默认查询当前登录用户消息记录
|
||||
LoginUser loginUser = LoginHelper.getLoginUser();
|
||||
if (loginUser == null) {
|
||||
throw new BaseException("用户未登录!");
|
||||
}
|
||||
chatRequest.setUserId(loginUser.getUserId());
|
||||
TableDataInfo<ChatMessageVo> chatMessageVoTableDataInfo = chatMessageService.queryPageList(chatRequest, pageQuery);
|
||||
return R.ok(chatMessageVoTableDataInfo);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
package org.ruoyi.chat.controller.chat;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.*;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import org.ruoyi.common.excel.utils.ExcelUtil;
|
||||
import org.ruoyi.common.idempotent.annotation.RepeatSubmit;
|
||||
import org.ruoyi.core.page.TableDataInfo;
|
||||
import org.ruoyi.domain.bo.ChatGptsBo;
|
||||
import org.ruoyi.domain.vo.ChatGptsVo;
|
||||
import org.ruoyi.service.IChatGptsService;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.ruoyi.common.log.annotation.Log;
|
||||
import org.ruoyi.common.web.core.BaseController;
|
||||
import org.ruoyi.core.page.PageQuery;
|
||||
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.log.enums.BusinessType;
|
||||
|
||||
/**
|
||||
* 应用管理
|
||||
*
|
||||
* @author ageerle
|
||||
* @date 2025-04-08
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/system/gpts")
|
||||
public class ChatGptsController extends BaseController {
|
||||
|
||||
private final IChatGptsService chatGptsService;
|
||||
|
||||
/**
|
||||
* 查询应用管理列表
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<ChatGptsVo> list(ChatGptsBo bo, PageQuery pageQuery) {
|
||||
return chatGptsService.queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出应用管理列表
|
||||
*/
|
||||
@SaCheckPermission("system:gpts:export")
|
||||
@Log(title = "应用管理", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(ChatGptsBo bo, HttpServletResponse response) {
|
||||
List<ChatGptsVo> list = chatGptsService.queryList(bo);
|
||||
ExcelUtil.exportExcel(list, "应用管理", ChatGptsVo.class, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取应用管理详细信息
|
||||
*
|
||||
* @param id 主键
|
||||
*/
|
||||
@SaCheckPermission("system:gpts:query")
|
||||
@GetMapping("/{id}")
|
||||
public R<ChatGptsVo> getInfo(@NotNull(message = "主键不能为空")
|
||||
@PathVariable Long id) {
|
||||
return R.ok(chatGptsService.queryById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增应用管理
|
||||
*/
|
||||
@SaCheckPermission("system:gpts:add")
|
||||
@Log(title = "应用管理", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping()
|
||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody ChatGptsBo bo) {
|
||||
return toAjax(chatGptsService.insertByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改应用管理
|
||||
*/
|
||||
@SaCheckPermission("system:gpts:edit")
|
||||
@Log(title = "应用管理", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping()
|
||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody ChatGptsBo bo) {
|
||||
return toAjax(chatGptsService.updateByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除应用管理
|
||||
*
|
||||
* @param ids 主键串
|
||||
*/
|
||||
@SaCheckPermission("system:gpts:remove")
|
||||
@Log(title = "应用管理", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空")
|
||||
@PathVariable Long[] ids) {
|
||||
return toAjax(chatGptsService.deleteWithValidByIds(List.of(ids), true));
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import lombok.RequiredArgsConstructor;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.*;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import org.ruoyi.chat.service.chat.UserModelService;
|
||||
import org.ruoyi.chat.enums.DisplayType;
|
||||
import org.ruoyi.common.excel.utils.ExcelUtil;
|
||||
import org.ruoyi.common.idempotent.annotation.RepeatSubmit;
|
||||
import org.ruoyi.core.page.TableDataInfo;
|
||||
@@ -37,8 +37,6 @@ public class ChatModelController extends BaseController {
|
||||
|
||||
private final IChatModelService chatModelService;
|
||||
|
||||
private final UserModelService modelService;
|
||||
|
||||
/**
|
||||
* 查询聊天模型列表
|
||||
*/
|
||||
@@ -53,7 +51,8 @@ public class ChatModelController extends BaseController {
|
||||
*/
|
||||
@GetMapping("/modelList")
|
||||
public R<List<ChatModelVo>> modelList(ChatModelBo bo) {
|
||||
return R.ok(modelService.modelList(bo));
|
||||
bo.setModelShow(DisplayType.VISIBLE.getCode());
|
||||
return R.ok(chatModelService.queryList(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
package org.ruoyi.chat.controller.chat;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.*;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import org.ruoyi.common.excel.utils.ExcelUtil;
|
||||
import org.ruoyi.common.idempotent.annotation.RepeatSubmit;
|
||||
import org.ruoyi.core.page.TableDataInfo;
|
||||
import org.ruoyi.domain.bo.ChatPackagePlanBo;
|
||||
import org.ruoyi.domain.vo.ChatPackagePlanVo;
|
||||
import org.ruoyi.service.IChatPackagePlanService;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.ruoyi.common.log.annotation.Log;
|
||||
import org.ruoyi.common.web.core.BaseController;
|
||||
import org.ruoyi.core.page.PageQuery;
|
||||
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.log.enums.BusinessType;
|
||||
|
||||
/**
|
||||
* 套餐管理
|
||||
*
|
||||
* @author ageerle
|
||||
* @date 2025-04-08
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/system/packagePlan")
|
||||
public class ChatPackagePlanController extends BaseController {
|
||||
|
||||
private final IChatPackagePlanService chatPackagePlanService;
|
||||
|
||||
/**
|
||||
* 查询套餐管理列表
|
||||
*/
|
||||
@SaCheckPermission("system:packagePlan:list")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<ChatPackagePlanVo> list(ChatPackagePlanBo bo, PageQuery pageQuery) {
|
||||
return chatPackagePlanService.queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询套餐列表-不分页
|
||||
*/
|
||||
@GetMapping("/listPlan")
|
||||
public R<List<ChatPackagePlanVo>> listPlan() {
|
||||
return R.ok(chatPackagePlanService.queryList(new ChatPackagePlanBo()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出套餐管理列表
|
||||
*/
|
||||
@SaCheckPermission("system:packagePlan:export")
|
||||
@Log(title = "套餐管理", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(ChatPackagePlanBo bo, HttpServletResponse response) {
|
||||
List<ChatPackagePlanVo> list = chatPackagePlanService.queryList(bo);
|
||||
ExcelUtil.exportExcel(list, "套餐管理", ChatPackagePlanVo.class, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取套餐管理详细信息
|
||||
*
|
||||
* @param id 主键
|
||||
*/
|
||||
@SaCheckPermission("system:packagePlan:query")
|
||||
@GetMapping("/{id}")
|
||||
public R<ChatPackagePlanVo> getInfo(@NotNull(message = "主键不能为空")
|
||||
@PathVariable Long id) {
|
||||
return R.ok(chatPackagePlanService.queryById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增套餐管理
|
||||
*/
|
||||
@SaCheckPermission("system:packagePlan:add")
|
||||
@Log(title = "套餐管理", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping()
|
||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody ChatPackagePlanBo bo) {
|
||||
return toAjax(chatPackagePlanService.insertByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改套餐管理
|
||||
*/
|
||||
@SaCheckPermission("system:packagePlan:edit")
|
||||
@Log(title = "套餐管理", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping()
|
||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody ChatPackagePlanBo bo) {
|
||||
return toAjax(chatPackagePlanService.updateByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除套餐管理
|
||||
*
|
||||
* @param ids 主键串
|
||||
*/
|
||||
@SaCheckPermission("system:packagePlan:remove")
|
||||
@Log(title = "套餐管理", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空")
|
||||
@PathVariable Long[] ids) {
|
||||
return toAjax(chatPackagePlanService.deleteWithValidByIds(List.of(ids), true));
|
||||
}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
package org.ruoyi.chat.controller.chat;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.*;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import org.ruoyi.common.excel.utils.ExcelUtil;
|
||||
import org.ruoyi.common.idempotent.annotation.RepeatSubmit;
|
||||
import org.ruoyi.core.page.TableDataInfo;
|
||||
import org.ruoyi.domain.bo.ChatPluginBo;
|
||||
import org.ruoyi.domain.vo.ChatPluginVo;
|
||||
import org.ruoyi.service.IChatPluginService;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.ruoyi.common.log.annotation.Log;
|
||||
import org.ruoyi.common.web.core.BaseController;
|
||||
import org.ruoyi.core.page.PageQuery;
|
||||
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.log.enums.BusinessType;
|
||||
|
||||
/**
|
||||
* 插件管理
|
||||
*
|
||||
* @author ageerle
|
||||
* @date 2025-04-08
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/system/plugin")
|
||||
public class ChatPluginController extends BaseController {
|
||||
|
||||
private final IChatPluginService chatPluginService;
|
||||
|
||||
/**
|
||||
* 查询插件管理列表
|
||||
*/
|
||||
@SaCheckPermission("system:plugin:list")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<ChatPluginVo> list(ChatPluginBo bo, PageQuery pageQuery) {
|
||||
return chatPluginService.queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出插件管理列表
|
||||
*/
|
||||
@SaCheckPermission("system:plugin:export")
|
||||
@Log(title = "插件管理", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(ChatPluginBo bo, HttpServletResponse response) {
|
||||
List<ChatPluginVo> list = chatPluginService.queryList(bo);
|
||||
ExcelUtil.exportExcel(list, "插件管理", ChatPluginVo.class, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取插件管理详细信息
|
||||
*
|
||||
* @param id 主键
|
||||
*/
|
||||
@SaCheckPermission("system:plugin:query")
|
||||
@GetMapping("/{id}")
|
||||
public R<ChatPluginVo> getInfo(@NotNull(message = "主键不能为空")
|
||||
@PathVariable Long id) {
|
||||
return R.ok(chatPluginService.queryById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增插件管理
|
||||
*/
|
||||
@SaCheckPermission("system:plugin:add")
|
||||
@Log(title = "插件管理", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping()
|
||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody ChatPluginBo bo) {
|
||||
return toAjax(chatPluginService.insertByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改插件管理
|
||||
*/
|
||||
@SaCheckPermission("system:plugin:edit")
|
||||
@Log(title = "插件管理", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping()
|
||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody ChatPluginBo bo) {
|
||||
return toAjax(chatPluginService.updateByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除插件管理
|
||||
*
|
||||
* @param ids 主键串
|
||||
*/
|
||||
@SaCheckPermission("system:plugin:remove")
|
||||
@Log(title = "插件管理", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空")
|
||||
@PathVariable Long[] ids) {
|
||||
return toAjax(chatPluginService.deleteWithValidByIds(List.of(ids), true));
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package org.ruoyi.chat.controller.chat;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.ruoyi.common.core.domain.R;
|
||||
import org.ruoyi.common.web.core.BaseController;
|
||||
import org.ruoyi.domain.bo.ChatAppStoreBo;
|
||||
import org.ruoyi.domain.vo.ChatAppStoreVo;
|
||||
import org.ruoyi.service.IChatAppStoreService;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
package org.ruoyi.chat.controller.chat;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.*;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import org.ruoyi.common.idempotent.annotation.RepeatSubmit;
|
||||
import org.ruoyi.core.page.TableDataInfo;
|
||||
import org.ruoyi.domain.bo.ChatVoucherBo;
|
||||
import org.ruoyi.domain.vo.ChatVoucherVo;
|
||||
import org.ruoyi.service.IChatVoucherService;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.ruoyi.common.log.annotation.Log;
|
||||
import org.ruoyi.common.web.core.BaseController;
|
||||
import org.ruoyi.core.page.PageQuery;
|
||||
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.log.enums.BusinessType;
|
||||
import org.ruoyi.common.excel.utils.ExcelUtil;
|
||||
/**
|
||||
* 用户兑换记录
|
||||
*
|
||||
* @author ageerle
|
||||
* @date 2025-04-08
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/system/voucher")
|
||||
public class ChatVoucherController extends BaseController {
|
||||
|
||||
private final IChatVoucherService chatVoucherService;
|
||||
|
||||
/**
|
||||
* 查询用户兑换记录列表
|
||||
*/
|
||||
@SaCheckPermission("system:voucher:list")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<ChatVoucherVo> list(ChatVoucherBo bo, PageQuery pageQuery) {
|
||||
return chatVoucherService.queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出用户兑换记录列表
|
||||
*/
|
||||
@SaCheckPermission("system:voucher:export")
|
||||
@Log(title = "用户兑换记录", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(ChatVoucherBo bo, HttpServletResponse response) {
|
||||
List<ChatVoucherVo> list = chatVoucherService.queryList(bo);
|
||||
ExcelUtil.exportExcel(list, "用户兑换记录", ChatVoucherVo.class, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户兑换记录详细信息
|
||||
*
|
||||
* @param id 主键
|
||||
*/
|
||||
@SaCheckPermission("system:voucher:query")
|
||||
@GetMapping("/{id}")
|
||||
public R<ChatVoucherVo> getInfo(@NotNull(message = "主键不能为空")
|
||||
@PathVariable Long id) {
|
||||
return R.ok(chatVoucherService.queryById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增用户兑换记录
|
||||
*/
|
||||
@SaCheckPermission("system:voucher:add")
|
||||
@Log(title = "用户兑换记录", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping()
|
||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody ChatVoucherBo bo) {
|
||||
return toAjax(chatVoucherService.insertByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改用户兑换记录
|
||||
*/
|
||||
@SaCheckPermission("system:voucher:edit")
|
||||
@Log(title = "用户兑换记录", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping()
|
||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody ChatVoucherBo bo) {
|
||||
return toAjax(chatVoucherService.updateByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户兑换记录
|
||||
*
|
||||
* @param ids 主键串
|
||||
*/
|
||||
@SaCheckPermission("system:voucher:remove")
|
||||
@Log(title = "用户兑换记录", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空")
|
||||
@PathVariable Long[] ids) {
|
||||
return toAjax(chatVoucherService.deleteWithValidByIds(List.of(ids), true));
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,9 @@
|
||||
package org.ruoyi.chat.controller.knowledge;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.io.IOException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.ruoyi.common.core.domain.R;
|
||||
import org.ruoyi.common.core.validate.AddGroup;
|
||||
@@ -17,7 +14,6 @@ import org.ruoyi.common.satoken.utils.LoginHelper;
|
||||
import org.ruoyi.common.web.core.BaseController;
|
||||
import org.ruoyi.core.page.PageQuery;
|
||||
import org.ruoyi.core.page.TableDataInfo;
|
||||
import org.ruoyi.domain.PdfFileContentResult;
|
||||
import org.ruoyi.domain.bo.KnowledgeAttachBo;
|
||||
import org.ruoyi.domain.bo.KnowledgeFragmentBo;
|
||||
import org.ruoyi.domain.bo.KnowledgeInfoBo;
|
||||
@@ -28,7 +24,6 @@ import org.ruoyi.domain.vo.KnowledgeInfoVo;
|
||||
import org.ruoyi.service.IKnowledgeAttachService;
|
||||
import org.ruoyi.service.IKnowledgeFragmentService;
|
||||
import org.ruoyi.service.IKnowledgeInfoService;
|
||||
import org.ruoyi.service.PdfImageExtractService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
@@ -52,8 +47,6 @@ public class KnowledgeController extends BaseController {
|
||||
|
||||
private final IKnowledgeFragmentService fragmentService;
|
||||
|
||||
// private final PdfImageExtractService pdfImageExtractService;
|
||||
|
||||
/**
|
||||
* 根据用户信息查询本地知识库
|
||||
*/
|
||||
@@ -164,17 +157,4 @@ public class KnowledgeController extends BaseController {
|
||||
return attachService.translationByFile(file, targetLanguage);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取PDF中的图片并调用gpt-4o-mini,识别图片内容并返回
|
||||
*
|
||||
* @param file PDF文件
|
||||
* @return 文件名称和图片内容
|
||||
*/
|
||||
// @PostMapping("/extract-images")
|
||||
// @Operation(summary = "提取PDF中的图片并调用大模型,识别图片内容并返回", description = "提取PDF中的图片并调用gpt-4o-mini,识别图片内容并返回")
|
||||
// public R<List<PdfFileContentResult>> extractImages(
|
||||
// @RequestPart("file") MultipartFile file
|
||||
// ) throws IOException {
|
||||
// return R.ok(pdfImageExtractService.extractImages(file));
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ public class FaceController {
|
||||
@PostMapping("/insight-face/swap")
|
||||
public String insightFace(@RequestBody InsightFace insightFace) {
|
||||
// 扣除接口费用并且保存消息记录
|
||||
chatCostService.taskDeduct("mj","Face Changing", NumberUtils.toDouble(mjOkHttpUtil.getKey("faceSwapping"), 0.1));
|
||||
chatCostService.taskDeduct("mj","Face Changing", 0.0);
|
||||
// 创建请求体(这里使用JSON作为媒体类型)
|
||||
String insightFaceJson = JSONUtil.toJsonStr(insightFace);
|
||||
String url = "mj/insight-face/swap";
|
||||
|
||||
@@ -6,7 +6,6 @@ import io.swagger.annotations.ApiOperation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import okhttp3.Request;
|
||||
import org.apache.commons.lang3.math.NumberUtils;
|
||||
import org.ruoyi.chat.domain.dto.*;
|
||||
import org.ruoyi.chat.enums.ActionType;
|
||||
import org.ruoyi.chat.service.chat.IChatCostService;
|
||||
@@ -50,17 +49,17 @@ public class SubmitController {
|
||||
type -> {
|
||||
switch (type) {
|
||||
case UP_SAMPLE:
|
||||
chatCostService.taskDeduct("mj","enlarge", NumberUtils.toDouble(mjOkHttpUtil.getKey("upsample"), 0.3));
|
||||
chatCostService.taskDeduct("mj","enlarge", 0.0);
|
||||
break;
|
||||
case IN_PAINT:
|
||||
// 局部重绘已经扣费,不执行任何操作
|
||||
break;
|
||||
default:
|
||||
chatCostService.taskDeduct("mj","change", NumberUtils.toDouble(mjOkHttpUtil.getKey("change"), 0.3));
|
||||
chatCostService.taskDeduct("mj","change", 0.0);
|
||||
break;
|
||||
}
|
||||
},
|
||||
() -> chatCostService.taskDeduct("mj","change", NumberUtils.toDouble(mjOkHttpUtil.getKey("change"), 0.3))
|
||||
() -> chatCostService.taskDeduct("mj","change", 0.0)
|
||||
);
|
||||
|
||||
String jsonStr = JSONUtil.toJsonStr(changeDTO);
|
||||
@@ -81,7 +80,7 @@ public class SubmitController {
|
||||
@ApiOperation(value = "提交图生图、混图任务")
|
||||
@PostMapping("/blend")
|
||||
public String blend(@RequestBody SubmitBlendDTO blendDTO) {
|
||||
chatCostService.taskDeduct("mj","blend", NumberUtils.toDouble(mjOkHttpUtil.getKey("blend"), 0.3));
|
||||
chatCostService.taskDeduct("mj","blend", 0.0);
|
||||
String jsonStr = JSONUtil.toJsonStr(blendDTO);
|
||||
String url = "mj/submit/blend";
|
||||
Request request = mjOkHttpUtil.createPostRequest(url, jsonStr);
|
||||
@@ -91,7 +90,7 @@ public class SubmitController {
|
||||
@ApiOperation(value = "提交图生文任务")
|
||||
@PostMapping("/describe")
|
||||
public String describe(@RequestBody SubmitDescribeDTO describeDTO) {
|
||||
chatCostService.taskDeduct("mj","describe", NumberUtils.toDouble(mjOkHttpUtil.getKey("describe"), 0.1));
|
||||
chatCostService.taskDeduct("mj","describe",0.0);
|
||||
String jsonStr = JSONUtil.toJsonStr(describeDTO);
|
||||
String url = "mj/submit/describe";
|
||||
Request request = mjOkHttpUtil.createPostRequest(url, jsonStr);
|
||||
@@ -101,7 +100,7 @@ public class SubmitController {
|
||||
@ApiOperation(value = "提交文生图任务")
|
||||
@PostMapping("/imagine")
|
||||
public String imagine(@RequestBody SubmitImagineDTO imagineDTO) {
|
||||
chatCostService.taskDeduct("mj",imagineDTO.getPrompt(), NumberUtils.toDouble(mjOkHttpUtil.getKey("imagine"), 0.3));
|
||||
chatCostService.taskDeduct("mj",imagineDTO.getPrompt(), 0.0);
|
||||
String jsonStr = JSONUtil.toJsonStr(imagineDTO);
|
||||
String url = "mj/submit/imagine";
|
||||
Request request = mjOkHttpUtil.createPostRequest(url, jsonStr);
|
||||
@@ -111,7 +110,7 @@ public class SubmitController {
|
||||
@ApiOperation(value = "提交局部重绘任务")
|
||||
@PostMapping("/modal")
|
||||
public String modal(@RequestBody SubmitModalDTO submitModalDTO) {
|
||||
chatCostService.taskDeduct("mj","repaint ", NumberUtils.toDouble(mjOkHttpUtil.getKey("inpaint"), 0.1));
|
||||
chatCostService.taskDeduct("mj","repaint ", 0.0);
|
||||
String jsonStr = JSONUtil.toJsonStr(submitModalDTO);
|
||||
String url = "mj/submit/modal";
|
||||
Request request = mjOkHttpUtil.createPostRequest(url, jsonStr);
|
||||
@@ -121,7 +120,7 @@ public class SubmitController {
|
||||
@ApiOperation(value = "提交提示词分析任务")
|
||||
@PostMapping("/shorten")
|
||||
public String shorten(@RequestBody SubmitShortenDTO submitShortenDTO) {
|
||||
chatCostService.taskDeduct("mj","shorten", NumberUtils.toDouble(mjOkHttpUtil.getKey("shorten"), 0.1));
|
||||
chatCostService.taskDeduct("mj","shorten", 0.0);
|
||||
String jsonStr = JSONUtil.toJsonStr(submitShortenDTO);
|
||||
String url = "mj/submit/shorten";
|
||||
Request request = mjOkHttpUtil.createPostRequest(url, jsonStr);
|
||||
|
||||
@@ -34,12 +34,19 @@ import java.util.Objects;
|
||||
@Component
|
||||
public class SSEEventSourceListener extends EventSourceListener {
|
||||
|
||||
private SseEmitter emitter;
|
||||
|
||||
private Long userId;
|
||||
|
||||
private Long sessionId;
|
||||
|
||||
@Autowired(required = false)
|
||||
public SSEEventSourceListener(SseEmitter emitter) {
|
||||
public SSEEventSourceListener(SseEmitter emitter,Long userId,Long sessionId) {
|
||||
this.emitter = emitter;
|
||||
this.userId = userId;
|
||||
this.sessionId = sessionId;
|
||||
}
|
||||
|
||||
private SseEmitter emitter;
|
||||
|
||||
private StringBuilder stringBuffer = new StringBuilder();
|
||||
|
||||
@@ -70,6 +77,8 @@ public class SSEEventSourceListener extends EventSourceListener {
|
||||
// 设置对话角色
|
||||
chatRequest.setRole(Message.Role.ASSISTANT.getName());
|
||||
chatRequest.setModel(modelName);
|
||||
chatRequest.setUserId(userId);
|
||||
chatRequest.setSessionId(sessionId);
|
||||
chatRequest.setPrompt(stringBuffer.toString());
|
||||
chatCostService.deductToken(chatRequest);
|
||||
return;
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
package org.ruoyi.chat.service.chat;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.ruoyi.chat.enums.DisplayType;
|
||||
import org.ruoyi.domain.bo.ChatModelBo;
|
||||
import org.ruoyi.domain.vo.ChatModelVo;
|
||||
import org.ruoyi.service.IChatModelService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 用户模型信息
|
||||
*
|
||||
* @author ageerle@163.com
|
||||
* date 2025/4/10
|
||||
*/
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class UserModelService {
|
||||
|
||||
private final IChatModelService chatModelService;
|
||||
|
||||
public List<ChatModelVo> modelList(ChatModelBo bo) {
|
||||
bo.setModelShow(DisplayType.VISIBLE.getCode());
|
||||
return chatModelService.queryList(bo);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,7 +13,7 @@ import org.ruoyi.common.core.domain.model.LoginUser;
|
||||
import org.ruoyi.common.core.exception.ServiceException;
|
||||
import org.ruoyi.common.core.exception.base.BaseException;
|
||||
import org.ruoyi.common.satoken.utils.LoginHelper;
|
||||
import org.ruoyi.domain.ChatToken;
|
||||
import org.ruoyi.domain.ChatUsageToken;
|
||||
import org.ruoyi.domain.bo.ChatMessageBo;
|
||||
import org.ruoyi.domain.vo.ChatModelVo;
|
||||
import org.ruoyi.service.IChatMessageService;
|
||||
@@ -48,6 +48,9 @@ public class ChatCostServiceImpl implements IChatCostService {
|
||||
*/
|
||||
@Override
|
||||
public void deductToken(ChatRequest chatRequest) {
|
||||
if(chatRequest.getUserId()==null || chatRequest.getSessionId()==null){
|
||||
return;
|
||||
}
|
||||
|
||||
int tokens = TikTokensUtil.tokens(chatRequest.getModel(), chatRequest.getPrompt());
|
||||
|
||||
@@ -55,24 +58,19 @@ public class ChatCostServiceImpl implements IChatCostService {
|
||||
|
||||
ChatMessageBo chatMessageBo = new ChatMessageBo();
|
||||
|
||||
if(chatRequest.getSessionId() == null){
|
||||
Object sessionId = LocalCache.CACHE.get("sessionId");
|
||||
chatRequest.setSessionId((Long) sessionId);
|
||||
}
|
||||
|
||||
Object userId = LocalCache.CACHE.get("userId");
|
||||
chatMessageBo.setUserId((Long) userId);
|
||||
|
||||
// 设置用户id
|
||||
chatMessageBo.setUserId(chatRequest.getUserId());
|
||||
// 设置对话角色
|
||||
chatMessageBo.setRole(chatRequest.getRole());
|
||||
|
||||
// 设置会话id
|
||||
chatMessageBo.setSessionId(chatRequest.getSessionId());
|
||||
// 设置对话内容
|
||||
chatMessageBo.setContent(chatRequest.getPrompt());
|
||||
|
||||
// 计算总token数
|
||||
ChatToken chatToken = chatTokenService.queryByUserId(chatMessageBo.getUserId(), modelName);
|
||||
ChatUsageToken chatToken = chatTokenService.queryByUserId(chatMessageBo.getUserId(), modelName);
|
||||
if (chatToken == null) {
|
||||
chatToken = new ChatToken();
|
||||
chatToken = new ChatUsageToken();
|
||||
chatToken.setToken(0);
|
||||
}
|
||||
int totalTokens = chatToken.getToken() + tokens;
|
||||
|
||||
@@ -55,7 +55,7 @@ public class OpenAIServiceImpl implements IChatService {
|
||||
Message userMessage = Message.builder().content("工具返回信息:"+toolString).role(Message.Role.USER).build();
|
||||
messages.add(userMessage);
|
||||
}
|
||||
SSEEventSourceListener listener = new SSEEventSourceListener(emitter);
|
||||
SSEEventSourceListener listener = new SSEEventSourceListener(emitter,chatRequest.getUserId(),chatRequest.getSessionId());
|
||||
ChatCompletion completion = ChatCompletion
|
||||
.builder()
|
||||
.messages(messages)
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.ruoyi.common.core.utils.DateUtils;
|
||||
import org.ruoyi.common.core.utils.StringUtils;
|
||||
import org.ruoyi.common.core.utils.file.FileUtils;
|
||||
import org.ruoyi.common.core.utils.file.MimeTypeUtils;
|
||||
import org.ruoyi.common.satoken.utils.LoginHelper;
|
||||
import org.ruoyi.domain.bo.ChatSessionBo;
|
||||
import org.ruoyi.domain.bo.QueryVectorBo;
|
||||
import org.ruoyi.domain.vo.ChatModelVo;
|
||||
@@ -77,23 +78,22 @@ public class SseServiceImpl implements ISseService {
|
||||
try {
|
||||
// 构建消息列表
|
||||
buildChatMessageList(chatRequest);
|
||||
|
||||
LocalCache.CACHE.put("userId", chatCostService.getUserId());
|
||||
chatRequest.setUserId(chatCostService.getUserId());
|
||||
// 保存会话信息
|
||||
if(chatRequest.getSessionId()==null){
|
||||
ChatSessionBo chatSessionBo = new ChatSessionBo();
|
||||
chatSessionBo.setUserId(chatCostService.getUserId());
|
||||
chatSessionBo.setSessionTitle(getFirst10Characters(chatRequest.getPrompt()));
|
||||
chatSessionBo.setSessionContent(chatRequest.getPrompt());
|
||||
chatSessionService.insertByBo(chatSessionBo);
|
||||
chatRequest.setSessionId(chatSessionBo.getId());
|
||||
}
|
||||
LocalCache.CACHE.put("sessionId", chatRequest.getSessionId());
|
||||
// 设置对话角色
|
||||
chatRequest.setRole(Message.Role.USER.getName());
|
||||
// 保存消息记录 并扣除费用
|
||||
chatCostService.deductToken(chatRequest);
|
||||
|
||||
if(LoginHelper.isLogin()){
|
||||
// 保存消息记录 并扣除费用
|
||||
chatCostService.deductToken(chatRequest);
|
||||
chatRequest.setUserId(chatCostService.getUserId());
|
||||
if(chatRequest.getSessionId()==null){
|
||||
ChatSessionBo chatSessionBo = new ChatSessionBo();
|
||||
chatSessionBo.setUserId(chatCostService.getUserId());
|
||||
chatSessionBo.setSessionTitle(getFirst10Characters(chatRequest.getPrompt()));
|
||||
chatSessionBo.setSessionContent(chatRequest.getPrompt());
|
||||
chatSessionService.insertByBo(chatSessionBo);
|
||||
chatRequest.setSessionId(chatSessionBo.getId());
|
||||
}
|
||||
}
|
||||
// 根据模型分类调用不同的处理逻辑
|
||||
IChatService chatService = chatServiceFactory.getChatService(chatModelVo.getCategory());
|
||||
chatService.chat(chatRequest, sseEmitter);
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
package org.ruoyi.system.config;
|
||||
|
||||
import cn.binarywang.wx.miniapp.api.WxMaService;
|
||||
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
|
||||
import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage;
|
||||
import cn.binarywang.wx.miniapp.bean.WxMaSubscribeMessage;
|
||||
import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
|
||||
import cn.binarywang.wx.miniapp.message.WxMaMessageHandler;
|
||||
import cn.binarywang.wx.miniapp.message.WxMaMessageRouter;
|
||||
import com.google.common.collect.Lists;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
|
||||
import me.chanjar.weixin.common.error.WxErrorException;
|
||||
import me.chanjar.weixin.common.error.WxRuntimeException;
|
||||
import org.ruoyi.system.properties.WxMaProperties;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author Admin
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(WxMaProperties.class)
|
||||
public class WxMaConfiguration {
|
||||
private final WxMaProperties properties;
|
||||
|
||||
@Autowired
|
||||
public WxMaConfiguration(WxMaProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public WxMaService wxMaService() {
|
||||
List<WxMaProperties.Config> configs = this.properties.getConfigs();
|
||||
if (configs == null) {
|
||||
throw new WxRuntimeException("大哥,拜托先看下项目首页的说明(readme文件),添加下相关配置,注意别配错了!");
|
||||
}
|
||||
WxMaService maService = new WxMaServiceImpl();
|
||||
maService.setMultiConfigs(
|
||||
configs.stream()
|
||||
.map(a -> {
|
||||
WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
|
||||
// WxMaDefaultConfigImpl config = new WxMaRedisConfigImpl(new JedisPool());
|
||||
// 使用上面的配置时,需要同时引入jedis-lock的依赖,否则会报类无法找到的异常
|
||||
config.setAppid(a.getAppid());
|
||||
config.setSecret(a.getSecret());
|
||||
config.setToken(a.getToken());
|
||||
config.setAesKey(a.getAesKey());
|
||||
config.setMsgDataFormat(a.getMsgDataFormat());
|
||||
return config;
|
||||
}).collect(Collectors.toMap(WxMaDefaultConfigImpl::getAppid, a -> a, (o, n) -> o)));
|
||||
return maService;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public WxMaMessageRouter wxMaMessageRouter(WxMaService wxMaService) {
|
||||
final WxMaMessageRouter router = new WxMaMessageRouter(wxMaService);
|
||||
router
|
||||
.rule().handler(logHandler).next()
|
||||
.rule().async(false).content("订阅消息").handler(subscribeMsgHandler).end()
|
||||
.rule().async(false).content("文本").handler(textHandler).end()
|
||||
.rule().async(false).content("图片").handler(picHandler).end()
|
||||
.rule().async(false).content("二维码").handler(qrcodeHandler).end();
|
||||
return router;
|
||||
}
|
||||
|
||||
private final WxMaMessageHandler subscribeMsgHandler = (wxMessage, context, service, sessionManager) -> {
|
||||
service.getMsgService().sendSubscribeMsg(WxMaSubscribeMessage.builder()
|
||||
.templateId("此处更换为自己的模板id")
|
||||
.data(Lists.newArrayList(
|
||||
new WxMaSubscribeMessage.MsgData("keyword1", "339208499")))
|
||||
.toUser(wxMessage.getFromUser())
|
||||
.build());
|
||||
return null;
|
||||
};
|
||||
|
||||
private final WxMaMessageHandler logHandler = (wxMessage, context, service, sessionManager) -> {
|
||||
log.info("收到消息:" + wxMessage.toString());
|
||||
service.getMsgService().sendKefuMsg(WxMaKefuMessage.newTextBuilder().content("收到信息为:" + wxMessage.toJson())
|
||||
.toUser(wxMessage.getFromUser()).build());
|
||||
return null;
|
||||
};
|
||||
|
||||
private final WxMaMessageHandler textHandler = (wxMessage, context, service, sessionManager) -> {
|
||||
service.getMsgService().sendKefuMsg(WxMaKefuMessage.newTextBuilder().content("回复文本消息")
|
||||
.toUser(wxMessage.getFromUser()).build());
|
||||
return null;
|
||||
};
|
||||
|
||||
private final WxMaMessageHandler picHandler = (wxMessage, context, service, sessionManager) -> {
|
||||
try {
|
||||
WxMediaUploadResult uploadResult = service.getMediaService()
|
||||
.uploadMedia("image", "png",
|
||||
ClassLoader.getSystemResourceAsStream("tmp.png"));
|
||||
service.getMsgService().sendKefuMsg(
|
||||
WxMaKefuMessage
|
||||
.newImageBuilder()
|
||||
.mediaId(uploadResult.getMediaId())
|
||||
.toUser(wxMessage.getFromUser())
|
||||
.build());
|
||||
} catch (WxErrorException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
private final WxMaMessageHandler qrcodeHandler = (wxMessage, context, service, sessionManager) -> {
|
||||
try {
|
||||
final File file = service.getQrcodeService().createQrcode("123", 430);
|
||||
WxMediaUploadResult uploadResult = service.getMediaService().uploadMedia("image", file);
|
||||
service.getMsgService().sendKefuMsg(
|
||||
WxMaKefuMessage
|
||||
.newImageBuilder()
|
||||
.mediaId(uploadResult.getMediaId())
|
||||
.toUser(wxMessage.getFromUser())
|
||||
.build());
|
||||
} catch (WxErrorException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
package org.ruoyi.system.properties;
|
||||
|
||||
/**
|
||||
* 微信小程序属性配置类
|
||||
*
|
||||
* @author: wangle
|
||||
* @date: 2023/5/18
|
||||
*/
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="https://github.com/binarywang">Binary Wang</a>
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "wx.miniapp")
|
||||
public class WxMaProperties {
|
||||
|
||||
private List<Config> configs;
|
||||
|
||||
@Data
|
||||
public static class Config {
|
||||
/**
|
||||
* 设置微信小程序的appid
|
||||
*/
|
||||
private String appid;
|
||||
|
||||
/**
|
||||
* 设置微信小程序的Secret
|
||||
*/
|
||||
private String secret;
|
||||
|
||||
/**
|
||||
* 设置微信小程序消息服务器配置的token
|
||||
*/
|
||||
private String token;
|
||||
|
||||
/**
|
||||
* 设置微信小程序消息服务器配置的EncodingAESKey
|
||||
*/
|
||||
private String aesKey;
|
||||
|
||||
/**
|
||||
* 消息格式,XML或者JSON
|
||||
*/
|
||||
private String msgDataFormat;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,8 +1,5 @@
|
||||
package org.ruoyi.system.service;
|
||||
|
||||
import cn.binarywang.wx.miniapp.api.WxMaService;
|
||||
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
|
||||
import cn.binarywang.wx.miniapp.util.WxMaConfigHolder;
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import cn.dev33.satoken.secure.BCrypt;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
@@ -11,8 +8,6 @@ import cn.hutool.core.util.ObjectUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import me.chanjar.weixin.common.error.WxErrorException;
|
||||
import org.apache.commons.lang3.math.NumberUtils;
|
||||
import org.ruoyi.common.core.constant.Constants;
|
||||
import org.ruoyi.common.core.constant.GlobalConstants;
|
||||
import org.ruoyi.common.core.constant.TenantConstants;
|
||||
@@ -53,7 +48,6 @@ import java.util.function.Supplier;
|
||||
public class SysLoginService {
|
||||
|
||||
private final SysUserMapper userMapper;
|
||||
private final WxMaService wxMaService;
|
||||
private final ISysPermissionService permissionService;
|
||||
private final ISysTenantService tenantService;
|
||||
|
||||
@@ -62,19 +56,6 @@ public class SysLoginService {
|
||||
@Value("${user.password.lockTime}")
|
||||
private Integer lockTime;
|
||||
|
||||
/**
|
||||
* 获取微信code
|
||||
* @param xcxCode 获取xcxCode
|
||||
*/
|
||||
public String getOpenidFromCode(String xcxCode) {
|
||||
try {
|
||||
WxMaJscode2SessionResult sessionInfo = wxMaService.getUserService().getSessionInfo(xcxCode);
|
||||
return sessionInfo.getOpenid();
|
||||
} catch (WxErrorException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 登录验证
|
||||
*
|
||||
@@ -131,32 +112,6 @@ public class SysLoginService {
|
||||
return StpUtil.getTokenValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信小程序登录
|
||||
*
|
||||
* @param loginBody
|
||||
* @return String
|
||||
* @Date 2023/5/18
|
||||
**/
|
||||
public void visitorLogin(VisitorLoginBody loginBody) {
|
||||
String openid = "";
|
||||
// PC端游客登录
|
||||
if (LoginUserType.PC.getCode().equals(loginBody.getType())) {
|
||||
openid = loginBody.getCode();
|
||||
} else {
|
||||
// 小程序匿名登录
|
||||
try {
|
||||
WxMaJscode2SessionResult session = wxMaService.getUserService().getSessionInfo(loginBody.getCode());
|
||||
openid = session.getOpenid();
|
||||
} catch (WxErrorException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
} finally {
|
||||
// 清理ThreadLocal
|
||||
WxMaConfigHolder.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出登录
|
||||
*/
|
||||
|
||||
@@ -2,7 +2,6 @@ package org.ruoyi.system.service;
|
||||
|
||||
import cn.dev33.satoken.secure.BCrypt;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.math.NumberUtils;
|
||||
import org.ruoyi.common.core.constant.Constants;
|
||||
import org.ruoyi.common.core.constant.GlobalConstants;
|
||||
import org.ruoyi.common.core.domain.model.RegisterBody;
|
||||
@@ -10,7 +9,6 @@ import org.ruoyi.common.core.exception.base.BaseException;
|
||||
import org.ruoyi.common.core.exception.user.CaptchaException;
|
||||
import org.ruoyi.common.core.exception.user.CaptchaExpireException;
|
||||
import org.ruoyi.common.core.exception.user.UserException;
|
||||
import org.ruoyi.common.core.service.ConfigService;
|
||||
import org.ruoyi.common.core.utils.MessageUtils;
|
||||
import org.ruoyi.common.core.utils.ServletUtils;
|
||||
import org.ruoyi.common.core.utils.SpringUtils;
|
||||
@@ -37,7 +35,6 @@ public class SysRegisterService {
|
||||
|
||||
private final SysUserRoleMapper userRoleMapper;
|
||||
|
||||
// private final ConfigService configService;
|
||||
/**
|
||||
* 注册
|
||||
*/
|
||||
@@ -61,9 +58,7 @@ public class SysRegisterService {
|
||||
if (!userService.checkUserNameUnique(sysUser)) {
|
||||
throw new UserException("添加用户失败", username);
|
||||
}
|
||||
// String configValue = configService.getConfigValue("mail", "amount");
|
||||
|
||||
sysUser.setUserBalance(NumberUtils.toDouble("configValue",1));
|
||||
sysUser.setUserBalance(1.0);
|
||||
SysUser user = userService.registerUser(sysUser, tenantId);
|
||||
if (user == null) {
|
||||
throw new UserException("用户注册失败!");
|
||||
@@ -106,7 +101,6 @@ public class SysRegisterService {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 校验验证码
|
||||
*
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.ruoyi</groupId>
|
||||
<artifactId>ruoyi-modules</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>ruoyi-wechat</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.ruoyi</groupId>
|
||||
<artifactId>ruoyi-chat-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ruoyi</groupId>
|
||||
<artifactId>ruoyi-system-api</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -1,12 +0,0 @@
|
||||
package org.ruoyi.builder;
|
||||
|
||||
import me.chanjar.weixin.cp.api.WxCpService;
|
||||
import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage;
|
||||
import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage;
|
||||
|
||||
/**
|
||||
* @author <a href="https://github.com/binarywang">Binary Wang</a>
|
||||
*/
|
||||
public abstract class AbstractBuilder {
|
||||
public abstract WxCpXmlOutMessage build(String content, WxCpXmlMessage wxMessage, WxCpService service);
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package org.ruoyi.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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package org.ruoyi.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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
package org.ruoyi.config;
|
||||
|
||||
|
||||
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.WxCpService;
|
||||
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.WxCpMessageHandler;
|
||||
import me.chanjar.weixin.cp.message.WxCpMessageRouter;
|
||||
import org.ruoyi.handler.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
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 final LogHandler logHandler;
|
||||
private NullHandler nullHandler;
|
||||
private LocationHandler locationHandler;
|
||||
private MenuHandler menuHandler;
|
||||
private MsgHandler msgHandler;
|
||||
private final 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((WxCpMessageHandler) 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((WxCpMessageHandler) 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package org.ruoyi.config;
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package org.ruoyi.controller;
|
||||
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.ruoyi.service.WeixinUserService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 企业微信应用
|
||||
*
|
||||
* @author ageerle
|
||||
* @date 2025-05-03
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
public class WeixinServerController {
|
||||
|
||||
@Autowired
|
||||
private WeixinUserService weixinUserService;
|
||||
|
||||
@GetMapping(value = "/weixin/check")
|
||||
public String weixinCheck(HttpServletRequest request) {
|
||||
String signature = request.getParameter("signature");
|
||||
String timestamp = request.getParameter("timestamp");
|
||||
String nonce = request.getParameter("nonce");
|
||||
String echostr = request.getParameter("echostr");
|
||||
|
||||
if (StringUtils.isEmpty(signature) || StringUtils.isEmpty(timestamp) || StringUtils.isEmpty(nonce)) {
|
||||
return "";
|
||||
}
|
||||
weixinUserService.checkSignature(signature, timestamp, nonce);
|
||||
return echostr;
|
||||
}
|
||||
|
||||
@PostMapping(value = "/weixin/check")
|
||||
public String weixinMsg(@RequestBody String requestBody, @RequestParam("signature") String signature,
|
||||
@RequestParam("timestamp") String timestamp, @RequestParam("nonce") String nonce) {
|
||||
|
||||
log.debug("requestBody:{}", requestBody);
|
||||
log.debug("signature:{}", signature);
|
||||
log.debug("timestamp:{}", timestamp);
|
||||
log.debug("nonce:{}", nonce);
|
||||
|
||||
weixinUserService.checkSignature(signature, timestamp, nonce);
|
||||
return weixinUserService.handleWeixinMsg(requestBody);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
package org.ruoyi.controller;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.ruoyi.common.core.domain.R;
|
||||
import org.ruoyi.domin.WeixinQrCode;
|
||||
import org.ruoyi.service.VxLoginService;
|
||||
import org.ruoyi.system.domain.vo.LoginVo;
|
||||
import org.ruoyi.util.WeixinApiUtil;
|
||||
import org.ruoyi.util.WeixinQrCodeCacheUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 微信公众号登录
|
||||
*
|
||||
* @author ageerle
|
||||
* @date 2025-05-03
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
public class WeixinUserController {
|
||||
|
||||
@Autowired
|
||||
private WeixinApiUtil weixinApiUtil;
|
||||
|
||||
@Autowired
|
||||
private VxLoginService loginService;
|
||||
|
||||
@GetMapping(value = "/user/qrcode")
|
||||
public R<WeixinQrCode> getQrCode() {
|
||||
WeixinQrCode qrCode = weixinApiUtil.getQrCode();
|
||||
qrCode.setUrl(null);
|
||||
qrCode.setExpireSeconds(null);
|
||||
return R.ok(qrCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验是否扫描完成
|
||||
* 完成,返回 JWT
|
||||
* 未完成,返回 check failed
|
||||
*/
|
||||
@GetMapping(value = "/user/login/qrcode")
|
||||
public R<LoginVo> userLogin(String ticket) {
|
||||
String openId = WeixinQrCodeCacheUtil.get(ticket);
|
||||
if (StringUtils.isNotEmpty(openId)) {
|
||||
log.info("login success,open id:{}", openId);
|
||||
LoginVo loginVo = loginService.mpLogin(openId);
|
||||
return R.ok(loginVo);
|
||||
}
|
||||
log.info("login error,ticket:{}", ticket);
|
||||
return R.fail("check failed");
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
package org.ruoyi.controller;
|
||||
|
||||
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.config.WxCpConfiguration;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 微信公众号登录校验
|
||||
*
|
||||
* @author ageerle
|
||||
* @date 2025-05-03
|
||||
*/
|
||||
@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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
package org.ruoyi.domin;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ReceiveMessage {
|
||||
/**
|
||||
* 开发者微信号
|
||||
*/
|
||||
private String toUserName;
|
||||
/**
|
||||
* 发送方账号(一个openid)
|
||||
*/
|
||||
private String fromUserName;
|
||||
/**
|
||||
* 消息创建时间(整形)
|
||||
*/
|
||||
private String createTime;
|
||||
/**
|
||||
* 消息类型
|
||||
*/
|
||||
private String msgType;
|
||||
/**
|
||||
* 文本消息内容
|
||||
*/
|
||||
private String content;
|
||||
/**
|
||||
* 消息ID 64位
|
||||
*/
|
||||
String msgId;
|
||||
/**
|
||||
* 消息的数据ID 消息来自文章才有
|
||||
*/
|
||||
private String msgDataId;
|
||||
/**
|
||||
* 多图文时第几篇文章,从1开始 消息如果来自文章才有
|
||||
*/
|
||||
private String idx;
|
||||
/**
|
||||
* 订阅事件 subscribe 订阅 unsbscribe 取消订阅
|
||||
*/
|
||||
private String event;
|
||||
/**
|
||||
* 扫码 - ticket
|
||||
*/
|
||||
private String ticket;
|
||||
|
||||
public String getReplyTextMsg(String msg) {
|
||||
String xml = "<xml>\n"
|
||||
+ " <ToUserName><![CDATA[" + getFromUserName() + "]]></ToUserName>\n"
|
||||
+ " <FromUserName><![CDATA[" + getToUserName() + "]]></FromUserName>\n"
|
||||
+ " <CreateTime>" + System.currentTimeMillis() + "</CreateTime>\n"
|
||||
+ " <MsgType><![CDATA[text]]></MsgType>\n"
|
||||
+ " <Content><![CDATA[" + msg + "]]></Content>\n"
|
||||
+ " </xml>";
|
||||
return xml;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package org.ruoyi.domin;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author https://www.wdbyte.com
|
||||
*/
|
||||
@Data
|
||||
public class WeixinQrCode {
|
||||
|
||||
private String ticket;
|
||||
private Long expireSeconds;
|
||||
private String url;
|
||||
private String qrCodeUrl;
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package org.ruoyi.handler;
|
||||
|
||||
import me.chanjar.weixin.cp.message.WxCpMessageHandler;
|
||||
|
||||
/**
|
||||
* @author <a href="https://github.com/binarywang">Binary Wang</a>
|
||||
*/
|
||||
public abstract class AbstractHandler implements WxCpMessageHandler {
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package org.ruoyi.handler;
|
||||
|
||||
|
||||
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.builder.TextBuilder;
|
||||
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 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package org.ruoyi.handler;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package org.ruoyi.handler;
|
||||
|
||||
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.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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package org.ruoyi.handler;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package org.ruoyi.handler;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package org.ruoyi.handler;
|
||||
|
||||
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.builder.TextBuilder;
|
||||
import org.ruoyi.service.IChatVxService;
|
||||
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 IChatVxService chatVxService;
|
||||
|
||||
@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 = chatVxService.chat(wxMessage.getContent());
|
||||
|
||||
return new TextBuilder().build(content, wxMessage, cpService);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package org.ruoyi.handler;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package org.ruoyi.handler;
|
||||
|
||||
/**
|
||||
* @author <a href="https://github.com/binarywang">Binary Wang</a>
|
||||
*/
|
||||
public abstract class ScanHandler extends AbstractHandler {
|
||||
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
package org.ruoyi.handler;
|
||||
|
||||
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.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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package org.ruoyi.handler;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
package org.ruoyi.service;
|
||||
|
||||
import cn.dev33.satoken.secure.BCrypt;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.math.NumberUtils;
|
||||
import org.ruoyi.common.core.constant.Constants;
|
||||
import org.ruoyi.common.core.domain.model.VisitorLoginUser;
|
||||
import org.ruoyi.common.core.enums.DeviceType;
|
||||
import org.ruoyi.common.core.enums.UserType;
|
||||
import org.ruoyi.common.core.service.ConfigService;
|
||||
import org.ruoyi.common.core.utils.MessageUtils;
|
||||
import org.ruoyi.common.core.utils.ServletUtils;
|
||||
import org.ruoyi.common.core.utils.SpringUtils;
|
||||
import org.ruoyi.common.log.event.LogininforEvent;
|
||||
import org.ruoyi.common.satoken.utils.LoginHelper;
|
||||
import org.ruoyi.system.domain.SysUser;
|
||||
import org.ruoyi.system.domain.bo.SysUserBo;
|
||||
import org.ruoyi.system.domain.vo.LoginVo;
|
||||
import org.ruoyi.system.domain.vo.SysUserVo;
|
||||
import org.ruoyi.system.service.ISysUserService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 微信公众号登录
|
||||
*
|
||||
* @author ageerle@163.com
|
||||
* date 2025/4/30
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class VxLoginService {
|
||||
|
||||
private final ISysUserService userService;
|
||||
|
||||
private final ConfigService configService;
|
||||
|
||||
public LoginVo mpLogin(String openid) {
|
||||
// 使用 openid 查询绑定用户 如未绑定用户 则根据业务自行处理 例如 创建默认用户
|
||||
SysUserVo user = userService.selectUserByOpenId(openid);
|
||||
VisitorLoginUser loginUser = new VisitorLoginUser();
|
||||
if (ObjectUtil.isNull(user)) {
|
||||
SysUserBo sysUser = new SysUserBo();
|
||||
String name = "用户" + UUID.randomUUID().toString().replace("-", "");
|
||||
// 设置默认用户名
|
||||
sysUser.setUserName(name);
|
||||
// 设置默认昵称
|
||||
sysUser.setNickName(name);
|
||||
// 设置默认密码
|
||||
sysUser.setPassword(BCrypt.hashpw("123456"));
|
||||
// 设置微信openId
|
||||
sysUser.setOpenId(openid);
|
||||
String configValue = configService.getConfigValue("mail", "amount");
|
||||
// 设置默认余额
|
||||
sysUser.setUserBalance(NumberUtils.toDouble(configValue, 1));
|
||||
// 注册用户,设置默认租户为0
|
||||
SysUser registerUser = userService.registerUser(sysUser, "0");
|
||||
|
||||
// 构建登录用户信息
|
||||
loginUser.setTenantId("0");
|
||||
loginUser.setUserId(registerUser.getUserId());
|
||||
loginUser.setUsername(registerUser.getUserName());
|
||||
loginUser.setUserType(UserType.APP_USER.getUserType());
|
||||
loginUser.setOpenid(openid);
|
||||
loginUser.setNickName(registerUser.getNickName());
|
||||
} else {
|
||||
// 此处可根据登录用户的数据不同 自行创建 loginUser
|
||||
loginUser.setTenantId(user.getTenantId());
|
||||
loginUser.setUserId(user.getUserId());
|
||||
loginUser.setUsername(user.getUserName());
|
||||
loginUser.setUserType(user.getUserType());
|
||||
loginUser.setNickName(user.getNickName());
|
||||
loginUser.setAvatar(user.getWxAvatar());
|
||||
loginUser.setOpenid(openid);
|
||||
}
|
||||
// 生成token
|
||||
LoginHelper.loginByDevice(loginUser, DeviceType.XCX);
|
||||
recordLogininfor(loginUser.getTenantId(), loginUser.getUsername(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"));
|
||||
LoginVo loginVo = new LoginVo();
|
||||
// 生成令牌
|
||||
loginVo.setToken(StpUtil.getTokenValue());
|
||||
loginVo.setUserInfo(loginUser);
|
||||
return loginVo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录登录信息
|
||||
*
|
||||
* @param tenantId 租户ID
|
||||
* @param username 用户名
|
||||
* @param status 状态
|
||||
* @param message 消息内容
|
||||
*/
|
||||
private void recordLogininfor(String tenantId, String username, String status, String message) {
|
||||
LogininforEvent logininforEvent = new LogininforEvent();
|
||||
logininforEvent.setTenantId(tenantId);
|
||||
logininforEvent.setUsername(username);
|
||||
logininforEvent.setStatus(status);
|
||||
logininforEvent.setMessage(message);
|
||||
logininforEvent.setRequest(ServletUtils.getRequest());
|
||||
SpringUtils.context().publishEvent(logininforEvent);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package org.ruoyi.service;
|
||||
|
||||
|
||||
public interface WeixinUserService {
|
||||
|
||||
void checkSignature(String signature, String timestamp, String nonce);
|
||||
|
||||
String handleWeixinMsg(String body);
|
||||
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
package org.ruoyi.service.impl;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.ruoyi.domin.ReceiveMessage;
|
||||
import org.ruoyi.service.WeixinUserService;
|
||||
import org.ruoyi.util.WeixinMsgUtil;
|
||||
import org.ruoyi.util.WeixinQrCodeCacheUtil;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class WeixinUserServiceImpl implements WeixinUserService {
|
||||
|
||||
private String token = "panda";
|
||||
|
||||
@Override
|
||||
public void checkSignature(String signature, String timestamp, String nonce) {
|
||||
String[] arr = new String[] {token, timestamp, nonce};
|
||||
Arrays.sort(arr);
|
||||
StringBuilder content = new StringBuilder();
|
||||
for (String str : arr) {
|
||||
content.append(str);
|
||||
}
|
||||
String tmpStr = DigestUtils.sha1Hex(content.toString());
|
||||
if (tmpStr.equals(signature)) {
|
||||
log.info("check success");
|
||||
return;
|
||||
}
|
||||
log.error("check fail");
|
||||
throw new RuntimeException("check fail");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String handleWeixinMsg(String requestBody) {
|
||||
ReceiveMessage receiveMessage = WeixinMsgUtil.msgToReceiveMessage(requestBody);
|
||||
// 扫码登录
|
||||
if (WeixinMsgUtil.isScanQrCode(receiveMessage)) {
|
||||
return handleScanLogin(receiveMessage);
|
||||
}
|
||||
// 关注
|
||||
if (WeixinMsgUtil.isEventAndSubscribe(receiveMessage)) {
|
||||
return receiveMessage.getReplyTextMsg("感谢您的关注!");
|
||||
}
|
||||
return receiveMessage.getReplyTextMsg("收到(自动回复)");
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理扫码登录
|
||||
*
|
||||
* @param receiveMessage
|
||||
* @return
|
||||
*/
|
||||
private String handleScanLogin(ReceiveMessage receiveMessage) {
|
||||
String qrCodeTicket = WeixinMsgUtil.getQrCodeTicket(receiveMessage);
|
||||
if (WeixinQrCodeCacheUtil.get(qrCodeTicket) == null) {
|
||||
String openId = receiveMessage.getFromUserName();
|
||||
WeixinQrCodeCacheUtil.put(qrCodeTicket, openId);
|
||||
}
|
||||
return receiveMessage.getReplyTextMsg("你已成功登录!");
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package org.ruoyi.util;
|
||||
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author https://www.wdbyte.com
|
||||
*/
|
||||
public class KeyUtils {
|
||||
|
||||
public synchronized static String key6() {
|
||||
return RandomStringUtils.randomAlphanumeric(6);
|
||||
}
|
||||
|
||||
public synchronized static String key16() {
|
||||
return RandomStringUtils.randomAlphanumeric(16);
|
||||
}
|
||||
|
||||
public static String uuid32() {
|
||||
return UUID.randomUUID().toString().replace("-", "");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
package org.ruoyi.util;
|
||||
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.ruoyi.common.core.service.ConfigService;
|
||||
import org.ruoyi.domin.WeixinQrCode;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.net.URI;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* @author https://www.wdbyte.com
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class WeixinApiUtil {
|
||||
|
||||
private final ConfigService configService;
|
||||
|
||||
private static String QR_CODE_URL_PREFIX = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=";
|
||||
|
||||
private static String ACCESS_TOKEN = null;
|
||||
private static LocalDateTime ACCESS_TOKEN_EXPIRE_TIME = null;
|
||||
/**
|
||||
* 二维码 Ticket 过期时间
|
||||
*/
|
||||
private static int QR_CODE_TICKET_TIMEOUT = 10 * 60;
|
||||
|
||||
/**
|
||||
* 获取 access token
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public synchronized String getAccessToken() {
|
||||
if (ACCESS_TOKEN != null && ACCESS_TOKEN_EXPIRE_TIME.isAfter(LocalDateTime.now())) {
|
||||
return ACCESS_TOKEN;
|
||||
}
|
||||
String api = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + getKey("appid") + "&secret="
|
||||
+ getKey("secret");
|
||||
String result = HttpUtil.get(api);
|
||||
JSONObject jsonObject = JSON.parseObject(result);
|
||||
ACCESS_TOKEN = jsonObject.getString("access_token");
|
||||
ACCESS_TOKEN_EXPIRE_TIME = LocalDateTime.now().plusSeconds(jsonObject.getLong("expires_in") - 10);
|
||||
return ACCESS_TOKEN;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取二维码 Ticket
|
||||
*
|
||||
* https://developers.weixin.qq.com/doc/offiaccount/Account_Management/Generating_a_Parametric_QR_Code.html
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public WeixinQrCode getQrCode() {
|
||||
String api = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=" + getAccessToken();
|
||||
String jsonBody = String.format("{\n"
|
||||
+ " \"expire_seconds\": %d,\n"
|
||||
+ " \"action_name\": \"QR_STR_SCENE\",\n"
|
||||
+ " \"action_info\": {\n"
|
||||
+ " \"scene\": {\n"
|
||||
+ " \"scene_str\": \"%s\"\n"
|
||||
+ " }\n"
|
||||
+ " }\n"
|
||||
+ "}", QR_CODE_TICKET_TIMEOUT, KeyUtils.uuid32());
|
||||
String result = HttpUtil.post(api, jsonBody);
|
||||
log.info("get qr code params:{}", jsonBody);
|
||||
log.info("get qr code result:{}", result);
|
||||
WeixinQrCode weixinQrCode = JSON.parseObject(result, WeixinQrCode.class);
|
||||
weixinQrCode.setQrCodeUrl(QR_CODE_URL_PREFIX + URI.create(weixinQrCode.getTicket()).toASCIIString());
|
||||
return weixinQrCode;
|
||||
}
|
||||
|
||||
public String getKey(String key) {
|
||||
return configService.getConfigValue("weixin", key);
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package org.ruoyi.util;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.ruoyi.domin.ReceiveMessage;
|
||||
|
||||
/**
|
||||
* @author https://www.wdbyte.com
|
||||
*/
|
||||
public class WeixinMsgUtil {
|
||||
|
||||
// 事件-关注
|
||||
private static String EVENT_SUBSCRIBE = "subscribe";
|
||||
|
||||
/**
|
||||
* 微信消息转对象
|
||||
*
|
||||
* @param xml
|
||||
* @return
|
||||
*/
|
||||
public static ReceiveMessage msgToReceiveMessage(String xml) {
|
||||
JSONObject jsonObject = JSON.parseObject(XmlUtil.xml2json(xml));
|
||||
ReceiveMessage receiveMessage = new ReceiveMessage();
|
||||
receiveMessage.setToUserName(jsonObject.getString("ToUserName"));
|
||||
receiveMessage.setFromUserName(jsonObject.getString("FromUserName"));
|
||||
receiveMessage.setCreateTime(jsonObject.getString("CreateTime"));
|
||||
receiveMessage.setMsgType(jsonObject.getString("MsgType"));
|
||||
receiveMessage.setContent(jsonObject.getString("Content"));
|
||||
receiveMessage.setMsgId(jsonObject.getString("MsgId"));
|
||||
receiveMessage.setEvent(jsonObject.getString("Event"));
|
||||
receiveMessage.setTicket(jsonObject.getString("Ticket"));
|
||||
return receiveMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是订阅事件
|
||||
*
|
||||
* @param receiveMessage
|
||||
* @return
|
||||
*/
|
||||
public static boolean isEventAndSubscribe(ReceiveMessage receiveMessage) {
|
||||
return StringUtils.equals(receiveMessage.getEvent(), EVENT_SUBSCRIBE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是二维码扫描事件
|
||||
*
|
||||
* @param receiveMessage
|
||||
* @return
|
||||
*/
|
||||
public static boolean isScanQrCode(ReceiveMessage receiveMessage) {
|
||||
return StringUtils.isNotEmpty(receiveMessage.getTicket());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取扫描的二维码 Ticket
|
||||
*
|
||||
* @param receiveMessage
|
||||
* @return
|
||||
*/
|
||||
public static String getQrCodeTicket(ReceiveMessage receiveMessage) {
|
||||
return receiveMessage.getTicket();
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user