feature: 对接openRouter、对接后台基于权限的登录逻辑
This commit is contained in:
89
README.md
89
README.md
@@ -54,6 +54,12 @@ HuLa-Server 是一款基于 SpringCloud、SpringBoot3、Netty、MyBatis-Plus 和
|
||||
|
||||
- **RocketMQ**: 高性能消息中间件,各项服务之间解耦的关键,im场景下实现事务消息保障、顺序消费
|
||||
|
||||
### AI能力
|
||||
- **Spring AI**: 统一的AI接口抽象层,支持多平台切换
|
||||
- **Gitee AI**: 魔力方舟AI平台,支持对话、图片、音频、视频生成
|
||||
- **硅基流动**: 国产AI平台,提供多模态AI能力
|
||||
- **OpenAI/DeepSeek/Kimi**: 主流AI大模型集成
|
||||
|
||||
## 全链路分布式能力
|
||||
|
||||
- **网关层**:luohuo-gateway实现路由鉴权,支持OAuth2.0安全认证,SA-Token权限框架 + XSS过滤(luohuo-xss-starter)保障系统安全。
|
||||
@@ -62,9 +68,79 @@ HuLa-Server 是一款基于 SpringCloud、SpringBoot3、Netty、MyBatis-Plus 和
|
||||
|
||||
- **数据层**:MyBatis-Plus + Dynamic Datasource支持多租户分库分表。
|
||||
|
||||
## AI与IM深度集成
|
||||
## 🤖 AI与IM深度集成
|
||||
|
||||
• 集成Spring AI(deepseek、OpenAI、通义千问等),luohuo-ai-biz调用ai服务,提供调用第三方ai、工作流(TinyFlow)能力,扩展业务场景。
|
||||
### 🎨 多模态AI能力
|
||||
|
||||
**luohuo-ai** 模块提供强大的多模态AI能力,支持文生图、文生音、文生视频等功能,深度集成多个国内外主流AI平台
|
||||
|
||||
#### 📊 支持的AI平台
|
||||
|
||||
**🇨🇳 国内平台**
|
||||
- **Gitee AI (魔力方舟)** - 支持对话、图片生成、音频生成、视频生成
|
||||
- **硅基流动 (SiliconFlow)** - 支持对话、图片生成、音频生成、视频生成
|
||||
- **Kimi (月之暗面)** - 支持对话
|
||||
- **DeepSeek** - 支持对话
|
||||
- **通义千问 (阿里)** - 支持对话、图片生成
|
||||
- **文心一言 (百度)** - 支持对话、图片生成
|
||||
- **智谱 AI** - 支持对话、图片生成
|
||||
- **讯飞星火** - 支持对话
|
||||
- **豆包 (字节)** - 支持对话
|
||||
- **混元 (腾讯)** - 支持对话
|
||||
- **MiniMax (稀宇科技)** - 支持对话
|
||||
- **百川智能** - 支持对话
|
||||
|
||||
**🌍 国外平台**
|
||||
- **OpenAI** - 支持对话、图片生成
|
||||
- **OpenRouter** - 支持对话
|
||||
- **Ollama** - 支持对话
|
||||
- **Stable Diffusion** - 支持图片生成
|
||||
- **Midjourney** - 支持图片生成
|
||||
- **Suno AI** - 支持音乐生成
|
||||
|
||||
#### 🎯 核心功能
|
||||
|
||||
**💬 智能对话**
|
||||
- 多平台对话模型统一接口
|
||||
- 支持流式响应和普通响应
|
||||
- 工具调用能力 (Function Calling)
|
||||
- 对话历史管理
|
||||
- 上下文记忆
|
||||
|
||||
**🎨 文生图 (Text-to-Image)**
|
||||
- 支持多种图片生成模型
|
||||
- 自定义图片尺寸、风格
|
||||
- 异步任务处理
|
||||
- 图片存储管理
|
||||
|
||||
**🎵 文生音 (Text-to-Speech)**
|
||||
- 多音色选择
|
||||
- 语速调节
|
||||
- 多种音频格式输出 (MP3、WAV等)
|
||||
- 异步音频生成
|
||||
- 音频文件存储
|
||||
|
||||
**🎬 文生视频 (Text-to-Video)**
|
||||
- 文本描述生成视频
|
||||
- 异步任务提交
|
||||
- 视频生成状态轮询
|
||||
- 视频存储管理
|
||||
- 支持 OpenRouter、Kimi、GiteeAI、硅基流动等平台
|
||||
|
||||
**🎼 AI音乐生成**
|
||||
- Suno AI 音乐创作
|
||||
- 描述模式和歌词模式
|
||||
- 自定义音乐风格
|
||||
- 音乐任务管理
|
||||
|
||||
#### 🔧 技术特性
|
||||
|
||||
- **统一抽象接口**: 通过 `ChatModel`、`ImageModel`、`AudioModel`、`VideoModel` 等接口统一不同平台的调用方式
|
||||
- **工厂模式**: `AiModelFactory` 动态创建和管理不同平台的模型实例
|
||||
- **异步处理**: 图片、音频、视频生成采用异步任务处理,提升用户体验
|
||||
- **状态管理**: 完善的任务状态追踪机制
|
||||
- **资源管理**: 统一的文件存储和管理
|
||||
- **工作流支持**: 集成 TinyFlow 工作流引擎,支持复杂AI业务场景
|
||||
|
||||
## 🏗️系统架构
|
||||
|
||||
@@ -119,6 +195,15 @@ HuLa-Server 是一款基于 SpringCloud、SpringBoot3、Netty、MyBatis-Plus 和
|
||||
- **系统监控:** 服务健康状态监控
|
||||
- **内容审计:** 消息内容安全审计过滤
|
||||
|
||||
### 🤖 luohuo-ai - AI服务
|
||||
🎨 多模态AI | 🧠 智能对话 | 🎬 内容生成
|
||||
- **智能对话:** 集成20+主流AI平台,支持流式对话、工具调用
|
||||
- **文生图:** 支持 Midjourney、Stable Diffusion、通义万相等多平台图片生成
|
||||
- **文生音:** TTS语音合成,支持多音色、多语速、多格式
|
||||
- **文生视频:** 文本描述生成视频,支持 Kimi、Gitee AI、硅基流动、OpenRouter等平台
|
||||
- **模型管理:** 统一的模型配置、额度管理、使用统计 [部分Ai模型]
|
||||
- **工作流引擎:** TinyFlow 工作流支持,编排复杂AI业务场景
|
||||
|
||||
## 📊 消息执行流程步骤详解
|
||||
|
||||
1. **客户端发送消息到网关**
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
Target Server Version : 80030 (8.0.30)
|
||||
File Encoding : 65001
|
||||
|
||||
Date: 11/11/2025 15:41:19
|
||||
Date: 13/11/2025 19:05:32
|
||||
*/
|
||||
|
||||
SET NAMES utf8mb4;
|
||||
@@ -37,7 +37,7 @@ CREATE TABLE `ai_api_key` (
|
||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 94423954721793 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'AI API 密钥表' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 94780244072449 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'AI API 密钥表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of ai_api_key
|
||||
@@ -137,7 +137,7 @@ CREATE TABLE `ai_chat_message` (
|
||||
`tenant_id` bigint NOT NULL DEFAULT 1 COMMENT '租户编号',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
INDEX `idx_msg_type`(`msg_type` ASC) USING BTREE COMMENT '消息内容类型索引'
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 94449229597698 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'AI 聊天消息表' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 94900830313986 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'AI 聊天消息表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of ai_chat_message
|
||||
@@ -371,7 +371,7 @@ CREATE TABLE `ai_model_usage_record` (
|
||||
UNIQUE INDEX `uk_user_model`(`user_id` ASC, `model_id` ASC, `deleted` ASC) USING BTREE COMMENT '用户-模型唯一索引',
|
||||
INDEX `idx_user_id`(`user_id` ASC) USING BTREE COMMENT '用户ID索引',
|
||||
INDEX `idx_model_id`(`model_id` ASC) USING BTREE COMMENT '模型ID索引'
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'AI 公开模型使用记录表' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'AI 公开模型使用记录表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of ai_model_usage_record
|
||||
@@ -437,12 +437,29 @@ CREATE TABLE `ai_platform` (
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
UNIQUE INDEX `uk_platform`(`platform` ASC, `deleted` ASC) USING BTREE COMMENT '平台代码唯一索引'
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 18 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'AI 平台配置表' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 19 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'AI 平台配置表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of ai_platform
|
||||
-- ----------------------------
|
||||
INSERT INTO `ai_platform` VALUES (1, 'Moonshot', '月之暗灭', 'Moonshot (KIMI)', 'moonshot-v1-8k, moonshot-v1-32k, moonshot-v1-128k', 'https://platform.moonshot.cn/docs', '请前往 Moonshot 官网查看可用模型列表', 1, 0, '', '2025-11-11 01:19:48', '', '2025-11-11 02:33:25', b'0', 1);
|
||||
INSERT INTO `ai_platform` VALUES (2, 'DeepSeek', 'DeepSeek', 'DeepSeek', 'deepseek-chat, deepseek-coder, deepseek-reasoner', 'https://api-docs.deepseek.com/zh-cn/guides/reasoning_model', '请前往 DeepSeek 官网查看可用模型列表', 2, 0, '', '2025-11-11 01:19:48', '', '2025-11-11 03:05:33', b'0', 1);
|
||||
INSERT INTO `ai_platform` VALUES (3, 'YiYan', '文心一言', 'Baidu (文心一言)', 'ernie-bot-4, ernie-bot-turbo, ernie-bot', 'https://cloud.baidu.com/doc/WENXINWORKSHOP/index.html', '请前往百度智能云官网查看可用模型列表', 13, 0, '', '2025-11-11 01:19:48', '', '2025-11-12 11:15:21', b'0', 1);
|
||||
INSERT INTO `ai_platform` VALUES (4, 'TongYi', '通义千问', 'Alibaba (通义千问)', 'qwen-turbo, qwen-plus, qwen-max, qwen-long', 'https://help.aliyun.com/zh/dashscope/developer-reference/model-square', '请前往阿里云官网查看可用模型列表', 4, 0, '', '2025-11-11 01:19:48', '', '2025-11-11 02:33:25', b'0', 1);
|
||||
INSERT INTO `ai_platform` VALUES (5, 'HunYuan', '混元', 'Tencent (混元)', 'hunyuan-lite, hunyuan-standard, hunyuan-pro', 'https://cloud.tencent.com/document/product/1729', '请前往腾讯云官网查看可用模型列表', 5, 0, '', '2025-11-11 01:19:48', '', '2025-11-11 02:33:25', b'0', 1);
|
||||
INSERT INTO `ai_platform` VALUES (6, 'ZhiPu', '智谱', 'Zhipu (智谱)', 'glm-4, glm-3-turbo, glm-4v', 'https://open.bigmodel.cn/dev/api', '请前往智谱 AI 官网查看可用模型列表', 6, 0, '', '2025-11-11 01:19:48', '', '2025-11-11 02:33:25', b'0', 1);
|
||||
INSERT INTO `ai_platform` VALUES (7, 'XingHuo', '星火', 'XingHuo (星火)', 'generalv3.5, generalv3, generalv2.1', 'https://www.xfyun.cn/doc/spark/Web.html', '请前往讯飞星火官网查看可用模型列表', 7, 0, '', '2025-11-11 01:19:48', '', '2025-11-11 02:33:25', b'0', 1);
|
||||
INSERT INTO `ai_platform` VALUES (8, 'DouBao', '豆包', 'DouBao (豆包)', 'doubao-lite-4k, doubao-lite-32k, doubao-pro-4k', 'https://www.volcengine.com/docs/82379', '请前往字节豆包官网查看可用模型列表', 8, 0, '', '2025-11-11 01:19:48', '', '2025-11-11 02:33:25', b'0', 1);
|
||||
INSERT INTO `ai_platform` VALUES (9, 'SiliconFlow', '硅基流动', 'SiliconFlow (硅基流动)', 'Qwen/Qwen2-7B-Instruct, meta-llama/Llama-2-7b-chat-hf, Wan-AI/Wan2.2-T2V-A14B, THUDM/GLM-Z1-9B-0414, Kwai-Kolors/Kolors', 'https://docs.siliconflow.cn/cn/userguide/capabilities/text-generation', '请前往硅基流动官网查看可用模型列表', 2, 0, '', '2025-11-11 01:19:48', '', '2025-11-12 11:15:26', b'0', 1);
|
||||
INSERT INTO `ai_platform` VALUES (10, 'MiniMax', 'MiniMax', 'MiniMax', 'abab6.5-chat, abab5.5-chat, abab5-chat', 'https://www.minimaxi.com/document/guides/chat', '请前往 MiniMax 官网查看可用模型列表', 10, 0, '', '2025-11-11 01:19:48', '', '2025-11-11 02:33:25', b'0', 1);
|
||||
INSERT INTO `ai_platform` VALUES (11, 'BaiChuan', '百川智能', 'BaiChuan (百川)', 'Baichuan2-Turbo, Baichuan2-Turbo-192k, Baichuan2-53B', 'https://platform.baichuan-ai.com/docs/api', '请前往百川智能官网查看可用模型列表', 11, 0, '', '2025-11-11 01:19:48', '', '2025-11-11 02:33:25', b'0', 1);
|
||||
INSERT INTO `ai_platform` VALUES (12, 'GiteeAI', 'Gitee AI', 'Gitee AI (魔力方舟)', 'tts-1, tts-1-hd, gpt-4o, gpt-4o-mini', 'https://ai.gitee.com/docs', '请前往 Gitee AI 魔力方舟官网查看可用模型列表', 2, 0, '', '2025-11-11 01:19:48', '', '2025-11-12 11:15:29', b'0', 1);
|
||||
INSERT INTO `ai_platform` VALUES (13, 'OpenAI', 'OpenAI', 'OpenAI', 'gpt-4, gpt-4-turbo, gpt-3.5-turbo, gpt-4o', 'https://platform.openai.com/docs/models', '请前往 OpenAI 官网查看可用模型列表', 13, 0, '', '2025-11-11 01:19:48', '', '2025-11-11 02:33:25', b'0', 1);
|
||||
INSERT INTO `ai_platform` VALUES (14, 'AzureOpenAI', 'Azure OpenAI', 'Azure OpenAI', 'gpt-4, gpt-35-turbo, gpt-4-turbo', 'https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models', '请前往 Azure OpenAI 官网查看可用模型列表', 14, 0, '', '2025-11-11 01:19:48', '', '2025-11-11 02:33:25', b'0', 1);
|
||||
INSERT INTO `ai_platform` VALUES (15, 'Anthropic', 'Anthropic', 'Anthropic', 'claude-3-opus, claude-3-sonnet, claude-3-haiku', 'https://docs.anthropic.com/claude/docs/models-overview', '请前往 Anthropic 官网查看可用模型列表', 15, 0, '', '2025-11-11 01:19:48', '', '2025-11-11 02:33:25', b'0', 1);
|
||||
INSERT INTO `ai_platform` VALUES (16, 'Google', 'Google', 'Google', 'gemini-pro, gemini-pro-vision, gemini-1.5-pro', 'https://ai.google.dev/models', '请前往 Google AI 官网查看可用模型列表', 16, 0, '', '2025-11-11 01:19:48', '', '2025-11-11 02:33:25', b'0', 1);
|
||||
INSERT INTO `ai_platform` VALUES (17, 'Ollama', 'Ollama', 'Ollama', 'llama2, mistral, neural-chat, dolphin-mixtral', 'https://ollama.ai/library', '请前往 Ollama 官网查看可用模型列表', 17, 0, '', '2025-11-11 01:19:48', '', '2025-11-11 02:33:25', b'0', 1);
|
||||
INSERT INTO `ai_platform` VALUES (18, 'OpenRouter', 'OpenRouter', 'OpenRouter', 'openai/gpt-3.5-turbo, openai/gpt-4, anthropic/claude-3-opus', 'https://openrouter.ai/docs', '请前往 OpenRouter 官网查看可用模型列表', 3, 0, '', '2025-11-12 04:16:46', '', '2025-11-12 11:15:13', b'0', 1);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for ai_tool
|
||||
@@ -665,11 +682,7 @@ CREATE TABLE `base_employee` (
|
||||
-- ----------------------------
|
||||
-- Records of base_employee
|
||||
-- ----------------------------
|
||||
INSERT INTO `base_employee` VALUES (160566476187631622, 0, b'1', 1451532876054003712, 1, 1451532667655815168, NULL, '超管', 0, '', '20', '10', b'1', 1452186486253289472, '2021-11-21 16:45:25', 1452186486253289472, '2021-11-21 16:45:25', 1, 0, 1);
|
||||
INSERT INTO `base_employee` VALUES (1452186486492364800, 0, b'1', 1451532876054003712, 2, 1451532727697276928, 1451532821628715008, '内置超管-啊汤哥', 0, '', '20', '10', b'1', 2, '2021-10-24 16:13:33', 1452186486253289472, '2021-11-09 20:36:44', 2, 0, 1);
|
||||
INSERT INTO `base_employee` VALUES (1454329823978586112, 0, b'1', 1451532876054003712, 1454329823852756992, 1451532773234835456, NULL, '门店管理员-最后哥', 0, '', '20', '10', b'1', 1451549146992345088, '2021-10-30 14:10:25', 1451549146992345088, '2021-10-30 14:10:25', 1, 0, 1);
|
||||
INSERT INTO `base_employee` VALUES (1457904456589901824, 0, b'1', 1458051094994223104, 1457904455960756224, 1451532773234835456, NULL, '普通用户-小沙比', 0, '', '20', '10', b'1', 1451549146992345088, '2021-11-09 10:54:44', 2, '2024-10-08 15:31:36', 0, 0, 1);
|
||||
INSERT INTO `base_employee` VALUES (1457904456589901825, 0, b'1', 1458051094994223104, 649219302184284167, 1451532773234835456, NULL, 'Dawn', 0, '', '20', '10', b'1', 1451549146992345088, '2021-11-09 10:54:44', 2, '2024-10-08 15:31:36', 0, 0, 1);
|
||||
INSERT INTO `base_employee` VALUES (1, 0, b'1', 1458051094994223104, 61170828529941, 1451532773234835456, NULL, '2439646234', 0, '', '20', '10', b'1', 1451549146992345088, '2021-11-09 10:54:44', 2, '2024-10-08 15:31:36', 0, 0, 1);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for base_employee_org_rel
|
||||
@@ -1649,6 +1662,7 @@ CREATE TABLE `def_login_log` (
|
||||
-- ----------------------------
|
||||
-- Records of def_login_log
|
||||
-- ----------------------------
|
||||
INSERT INTO `def_login_log` VALUES (95232381660672, NULL, NULL, NULL, '192.168.1.37', '', '15659754644', '04', '用户不存在!', '2025-11-13', 'ReactorNetty/1.2.2', 'Unknown', '', 'Unknown', '0|0|0|内网IP|内网IP', '2025-11-13 18:59:30', NULL, '2025-11-13 18:59:30', NULL, 0);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for def_msg_template
|
||||
@@ -2008,6 +2022,7 @@ CREATE TABLE `def_resource_api` (
|
||||
INSERT INTO `def_resource_api` VALUES (167906188749438980, 1460537873248813056, 'DefUserController', 'lamp-system-server', 'POST', '用户-查找同一企业下的用户', '/system/defUser/pageUser', b'0', 1452186486253289472, '2021-12-11 13:27:53', 1452186486253289472, '2021-12-11 13:27:53', 0);
|
||||
INSERT INTO `def_resource_api` VALUES (167906188749438981, 1460537873248813056, 'MsgController', 'lamp-base-server', 'GET', '消息表-查询消息中心', '/base/msg/{id}', b'0', 1452186486253289472, '2021-12-11 13:27:53', 1452186486253289472, '2021-12-11 13:27:53', 0);
|
||||
INSERT INTO `def_resource_api` VALUES (167906188749438982, 1460537873248813056, 'BaseRoleController', 'lamp-base-server', 'POST', '角色-批量查询', '/base/baseRole/query', b'0', 1452186486253289472, '2021-12-11 13:27:53', 1452186486253289472, '2021-12-11 13:27:53', 0);
|
||||
INSERT INTO `def_resource_api` VALUES (167990267029221490, 1457665354649042944, 'DefTenantController', 'lamp-base-server', 'GET', '租户列表', '/base/defTenant/listTenantByUserId', b'0', 1452186486253289472, '2021-12-11 23:39:45', 1452186486253289472, '2021-12-11 23:39:45', 0);
|
||||
INSERT INTO `def_resource_api` VALUES (167990267029225477, 1457620585302458368, 'BaseEmployeeController', 'lamp-base-server', 'GET', '员工-单体查询', '/base/baseEmployee/{id}', b'0', 1452186486253289472, '2021-12-11 20:40:32', 1452186486253289472, '2021-12-11 20:40:32', 0);
|
||||
INSERT INTO `def_resource_api` VALUES (167990267029225478, 1457620528469639168, 'BaseEmployeeController', 'lamp-base-server', 'DELETE', '员工-删除', '/base/baseEmployee', b'0', 1452186486253289472, '2021-12-11 20:45:26', 1452186486253289472, '2021-12-11 20:45:26', 0);
|
||||
INSERT INTO `def_resource_api` VALUES (167990267029225479, 1457620470995091456, 'BaseEmployeeController', 'lamp-base-server', 'PUT', '员工-修改', '/base/baseEmployee', b'0', 1452186486253289472, '2021-12-11 20:46:52', 1452186486253289472, '2021-12-11 20:46:52', 0);
|
||||
@@ -2229,6 +2244,7 @@ CREATE TABLE `def_tenant` (
|
||||
`contact_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '联系人',
|
||||
`contact_mobile` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '联系手机',
|
||||
`status` tinyint NOT NULL DEFAULT 0 COMMENT '租户状态(0正常 1停用)',
|
||||
`state` tinyint NOT NULL DEFAULT 0 COMMENT 'state (正式租户、非正式租户的停用启用状态)',
|
||||
`website` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '绑定域名',
|
||||
`package_id` bigint NOT NULL COMMENT '租户套餐编号',
|
||||
`expire_time` datetime NOT NULL COMMENT '过期时间',
|
||||
@@ -2244,7 +2260,7 @@ CREATE TABLE `def_tenant` (
|
||||
-- ----------------------------
|
||||
-- Records of def_tenant
|
||||
-- ----------------------------
|
||||
INSERT INTO `def_tenant` VALUES (1, '系统默认租户', 1, 'aa', 'aa', 1, '', 12, '2028-06-20 14:21:35', 5, NULL, NULL, NULL, NULL, 0);
|
||||
INSERT INTO `def_tenant` VALUES (1, '系统默认租户', 1, '络火科技', '18173516309', 1, 0, '', 12, '2028-06-20 14:21:35', 5, NULL, NULL, NULL, NULL, 0);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for def_tenant_application_rel
|
||||
@@ -2315,8 +2331,8 @@ CREATE TABLE `def_user` (
|
||||
-- ----------------------------
|
||||
-- Records of def_user
|
||||
-- ----------------------------
|
||||
INSERT INTO `def_user` VALUES (61170828519936, 2, 'bot', 'HuLa小管家', '', '022', NULL, NULL, '', NULL, b'0', '', '', '1', b'1', '', '2025-08-11 11:11:03.139', '{\"createIp\": \"206.237.119.215\", \"updateIp\": \"120.231.232.41\", \"createIpDetail\": null, \"updateIpDetail\": null}', '2025-10-28 17:08:01', 2, NULL, 'a4d5c225e6709ba025272a31c7e90e0121d5e5ba16695afe0b61370bedb677d0', 'Dawn', '2025-07-07 15:27:02', 1, '2025-03-27 04:23:08', NULL, '2025-07-16 12:26:15', 0, 1);
|
||||
INSERT INTO `def_user` VALUES (61170828519937, 2, '2439646234', 'Dawn', '2439646234@qq.com', 'https://cdn.hulaspark.com/avatar/2439646234/6ec99d37b8ba1296c325d2d36b46a14d.webp', NULL, NULL, '', NULL, b'0', '', '', '1', b'1', '', '2025-08-11 11:11:03.189', '{\"createIp\": \"206.237.119.215\", \"updateIp\": \"183.15.178.3\", \"createIpDetail\": null, \"updateIpDetail\": {\"ip\": \"183.15.178.3\", \"isp\": \"电信\", \"area\": \"\", \"city\": \"深圳\", \"isp_id\": \"100017\", \"region\": \"广东\", \"city_id\": \"440300\", \"country\": \"中国\", \"region_id\": \"440000\", \"country_id\": \"CN\"}}', NULL, 0, NULL, 'a4d5c225e6709ba025272a31c7e90e0121d5e5ba16695afe0b61370bedb677d0', 'Dawn', '2025-11-10 17:44:08', 1, '2025-03-27 04:23:08', NULL, '2025-11-10 19:31:46', 0, 1);
|
||||
INSERT INTO `def_user` VALUES (61170828519936, 2, '15147891644', 'HuLa小管家', '', '022', NULL, NULL, '', NULL, b'0', '', '', '1', b'1', '', '2025-08-11 11:11:03.139', '{\"createIp\": \"206.237.119.215\", \"updateIp\": \"120.231.232.41\", \"createIpDetail\": null, \"updateIpDetail\": null}', '2025-10-28 17:08:01', 2, NULL, 'a4d5c225e6709ba025272a31c7e90e0121d5e5ba16695afe0b61370bedb677d0', 'Dawn', '2025-07-07 15:27:02', 1, '2025-03-27 04:23:08', NULL, '2025-07-16 12:26:15', 0, 1);
|
||||
INSERT INTO `def_user` VALUES (61170828519937, 2, '13275346112', 'Dawn', '2439646234@qq.com', 'https://cdn.hulaspark.com/avatar/2439646234/6ec99d37b8ba1296c325d2d36b46a14d.webp', NULL, NULL, '', NULL, b'0', '', '', '1', b'1', '', '2025-08-11 11:11:03.189', '{\"createIp\": \"206.237.119.215\", \"updateIp\": \"61.141.64.252\", \"createIpDetail\": null, \"updateIpDetail\": {\"ip\": \"61.141.64.252\", \"isp\": \"电信\", \"area\": \"\", \"city\": \"深圳\", \"isp_id\": \"100017\", \"region\": \"广东\", \"city_id\": \"440300\", \"country\": \"中国\", \"region_id\": \"440000\", \"country_id\": \"CN\"}}', NULL, 0, NULL, 'a4d5c225e6709ba025272a31c7e90e0121d5e5ba16695afe0b61370bedb677d0', 'Dawn', '2025-11-13 18:13:04', 1, '2025-03-27 04:23:08', NULL, '2025-11-13 18:42:30', 0, 1);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for def_user_application
|
||||
@@ -2353,6 +2369,7 @@ CREATE TABLE `def_user_tenant_rel` (
|
||||
`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
|
||||
`update_by` bigint NULL DEFAULT NULL COMMENT '最后更新人',
|
||||
`update_time` datetime NULL DEFAULT NULL COMMENT '最后更新时间',
|
||||
`is_del` bit(1) NOT NULL DEFAULT b'0' COMMENT '逻辑删除',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
UNIQUE INDEX `uk_utr_user_tenant`(`user_id` ASC, `tenant_id` ASC) USING BTREE
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '员工' ROW_FORMAT = Dynamic;
|
||||
@@ -2360,21 +2377,11 @@ CREATE TABLE `def_user_tenant_rel` (
|
||||
-- ----------------------------
|
||||
-- Records of def_user_tenant_rel
|
||||
-- ----------------------------
|
||||
INSERT INTO `def_user_tenant_rel` VALUES (160566476187631622, b'1', 1, b'1', 1, 1, '2021-12-12 12:12:12', 1452186486253289472, '2021-12-12 12:12:12');
|
||||
INSERT INTO `def_user_tenant_rel` VALUES (529156515081825280, b'0', 1457904455960756224, b'1', 528674722830400514, 2, '2024-08-10 23:18:52', 2, '2024-08-10 23:18:52');
|
||||
INSERT INTO `def_user_tenant_rel` VALUES (559374036665933915, b'0', 1454329823852756992, b'1', 559374036665933836, 2, '2024-10-31 11:21:46', 2, '2024-10-31 11:21:46');
|
||||
INSERT INTO `def_user_tenant_rel` VALUES (559374036665933925, b'0', 1454329823852756992, b'1', 1, 2, '2024-10-31 11:23:12', 2, '2024-10-31 11:23:12');
|
||||
INSERT INTO `def_user_tenant_rel` VALUES (561686163590261772, b'0', 1457904455960756224, b'1', 1, 2, '2024-11-06 15:31:11', 2, '2024-11-06 15:31:11');
|
||||
INSERT INTO `def_user_tenant_rel` VALUES (561686163590261775, b'0', 1459157721822527488, b'1', 1, 2, '2024-11-06 15:32:36', 2, '2024-11-06 15:32:36');
|
||||
INSERT INTO `def_user_tenant_rel` VALUES (568844692956704872, b'1', 1457904455960756224, b'1', 568844692956704788, 2, '2024-11-25 22:38:37', 2, '2024-11-25 22:38:37');
|
||||
INSERT INTO `def_user_tenant_rel` VALUES (585361260276598784, b'0', 1457904455960756224, b'1', 585361238801760256, 2, '2025-01-09 11:14:14', 2, '2025-01-09 11:14:14');
|
||||
INSERT INTO `def_user_tenant_rel` VALUES (608003790990285344, b'0', 1457904455960756224, b'1', 607814606270829667, 2, '2025-03-11 14:57:21', 2, '2025-03-11 14:57:21');
|
||||
INSERT INTO `def_user_tenant_rel` VALUES (608003790990285345, b'0', 1454329823852756992, b'1', 607814606270829667, 2, '2025-03-11 14:57:21', 2, '2025-03-11 14:57:21');
|
||||
INSERT INTO `def_user_tenant_rel` VALUES (636720990303838307, b'0', 1459157721822527488, b'1', 636720990303838214, 2, '2025-05-27 20:07:20', 2, '2025-05-27 20:07:20');
|
||||
INSERT INTO `def_user_tenant_rel` VALUES (636720990303838409, b'0', 1459157721822527488, b'1', 636720990303838329, 2, '2025-05-27 20:09:31', 2, '2025-05-27 20:09:31');
|
||||
INSERT INTO `def_user_tenant_rel` VALUES (639195488466789469, b'0', 2, b'1', 639195488466789388, 2, '2025-06-03 12:11:22', 2, '2025-06-03 12:11:22');
|
||||
INSERT INTO `def_user_tenant_rel` VALUES (639195488466789556, b'0', 2, b'1', 639195488466789475, 2, '2025-06-03 12:15:40', 2, '2025-06-03 12:15:40');
|
||||
INSERT INTO `def_user_tenant_rel` VALUES (1452186486492364800, b'1', 2, b'1', 1, 1, '2021-12-12 12:12:12', 1, '2021-12-12 12:12:12');
|
||||
INSERT INTO `def_user_tenant_rel` VALUES (160566476187631622, b'1', 61170828529941, b'1', 1, 1, '2021-12-12 12:12:12', 1452186486253289472, '2021-12-12 12:12:12', b'0');
|
||||
INSERT INTO `def_user_tenant_rel` VALUES (559374036665933925, b'0', 1454329823852756992, b'1', 1, 2, '2024-10-31 11:23:12', 2, '2024-10-31 11:23:12', b'0');
|
||||
INSERT INTO `def_user_tenant_rel` VALUES (561686163590261772, b'0', 1457904455960756224, b'1', 1, 2, '2024-11-06 15:31:11', 2, '2024-11-06 15:31:11', b'0');
|
||||
INSERT INTO `def_user_tenant_rel` VALUES (561686163590261775, b'0', 1459157721822527488, b'1', 1, 2, '2024-11-06 15:32:36', 2, '2024-11-06 15:32:36', b'0');
|
||||
INSERT INTO `def_user_tenant_rel` VALUES (1452186486492364800, b'1', 2, b'1', 1, 1, '2021-12-12 12:12:12', 1, '2021-12-12 12:12:12', b'0');
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for extend_interface_log
|
||||
@@ -2401,7 +2408,7 @@ CREATE TABLE `extend_interface_log` (
|
||||
-- Records of extend_interface_log
|
||||
-- ----------------------------
|
||||
INSERT INTO `extend_interface_log` VALUES (66567882983426, 244439130119864323, '阿里短信', 0, 1, '2025-08-26 16:37:01', '2025-08-26 16:37:00', NULL, '2025-08-26 16:37:00', NULL, 0, 0);
|
||||
INSERT INTO `extend_interface_log` VALUES (655249535051914248, 244881451621810192, '腾讯邮件', 907, 58, '2025-11-11 10:43:50', '2025-07-16 18:41:01', NULL, '2025-07-16 18:41:01', NULL, 0, 0);
|
||||
INSERT INTO `extend_interface_log` VALUES (655249535051914248, 244881451621810192, '腾讯邮件', 977, 58, '2025-11-13 18:50:10', '2025-07-16 18:41:01', NULL, '2025-07-16 18:41:01', NULL, 0, 0);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for extend_interface_logging
|
||||
@@ -2615,10 +2622,33 @@ CREATE TABLE `worker_node` (
|
||||
`created` timestamp NULL DEFAULT NULL COMMENT '创建时间',
|
||||
`is_del` tinyint(1) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 969 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = 'DB;WorkerID Assigner for UID Generator' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 976 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = 'DB;WorkerID Assigner for UID Generator' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of worker_node
|
||||
-- ----------------------------
|
||||
|
||||
-- ----------------------------
|
||||
-- Function structure for generate_account_by_id
|
||||
-- ----------------------------
|
||||
DROP FUNCTION IF EXISTS `generate_account_by_id`;
|
||||
delimiter ;;
|
||||
CREATE FUNCTION `generate_account_by_id`(user_id BIGINT)
|
||||
RETURNS varchar(11) CHARSET utf8mb4 COLLATE utf8mb4_bin
|
||||
DETERMINISTIC
|
||||
BEGIN
|
||||
DECLARE hash_value BIGINT UNSIGNED;
|
||||
DECLARE account_number BIGINT;
|
||||
|
||||
-- 使用 MD5 哈希算法(避免溢出)
|
||||
SET hash_value = CONV(SUBSTRING(MD5(user_id), 1, 10), 16, 10);
|
||||
|
||||
-- 生成11位账号:10000000000 ~ 19999999999
|
||||
SET account_number = 10000000000 + (hash_value % 10000000000);
|
||||
|
||||
RETURN CAST(account_number AS CHAR);
|
||||
END
|
||||
;;
|
||||
delimiter ;
|
||||
|
||||
SET FOREIGN_KEY_CHECKS = 1;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
Target Server Version : 80030 (8.0.30)
|
||||
File Encoding : 65001
|
||||
|
||||
Date: 11/11/2025 15:41:29
|
||||
Date: 13/11/2025 19:05:25
|
||||
*/
|
||||
|
||||
SET NAMES utf8mb4;
|
||||
@@ -74,7 +74,7 @@ CREATE TABLE `im_announcements` (
|
||||
`is_del` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否删除',
|
||||
`tenant_id` bigint NOT NULL DEFAULT 1,
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 91601993052673 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_as_ci COMMENT = '聊天公告表' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 94625993966593 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_as_ci COMMENT = '聊天公告表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of im_announcements
|
||||
@@ -151,7 +151,7 @@ CREATE TABLE `im_contact` (
|
||||
INDEX `idx_create_time`(`create_time` ASC) USING BTREE,
|
||||
INDEX `idx_update_time`(`update_time` ASC) USING BTREE,
|
||||
INDEX `idx_contact_room_uid_hide`(`room_id` ASC, `uid` ASC, `hide` ASC) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 69082079598103 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '会话列表' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 69082079599864 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '会话列表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of im_contact
|
||||
@@ -324,7 +324,7 @@ CREATE TABLE `im_group_member` (
|
||||
INDEX `idx_create_time`(`create_time` ASC) USING BTREE,
|
||||
INDEX `idx_update_time`(`update_time` ASC) USING BTREE,
|
||||
INDEX `idx_group_member_uid_isdel_groupid`(`uid` ASC, `is_del` ASC, `group_id` ASC) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 94383139570178 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '群成员表' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 95230070212610 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '群成员表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of im_group_member
|
||||
@@ -387,7 +387,7 @@ CREATE TABLE `im_message` (
|
||||
INDEX `idx_from_uid`(`from_uid` ASC) USING BTREE,
|
||||
INDEX `idx_create_time`(`create_time` ASC) USING BTREE,
|
||||
INDEX `idx_update_time`(`update_time` ASC) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 94414810759681 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '消息表' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 95230070212616 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '消息表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of im_message
|
||||
@@ -417,7 +417,7 @@ CREATE TABLE `im_message_mark` (
|
||||
INDEX `idx_uid`(`uid` ASC) USING BTREE,
|
||||
INDEX `idx_create_time`(`create_time` ASC) USING BTREE,
|
||||
INDEX `idx_update_time`(`update_time` ASC) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 93850781727233 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '消息标记表' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 95124751238657 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '消息标记表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of im_message_mark
|
||||
@@ -449,7 +449,7 @@ CREATE TABLE `im_notice` (
|
||||
INDEX `idx_receiver_type`(`receiver_id` ASC, `event_type` ASC) USING BTREE,
|
||||
INDEX `idx_sender`(`sender_id` ASC) USING BTREE,
|
||||
INDEX `idx_related`(`apply_id` ASC) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 94118743227395 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '统一通知表' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 95186432673283 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '统一通知表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of im_notice
|
||||
@@ -499,12 +499,12 @@ CREATE TABLE `im_room` (
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
INDEX `idx_create_time`(`create_time` ASC) USING BTREE,
|
||||
INDEX `idx_update_time`(`update_time` ASC) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 94383139570179 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '房间表' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 95230070212611 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '房间表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of im_room
|
||||
-- ----------------------------
|
||||
INSERT INTO `im_room` VALUES (1, 1, 1, '2025-11-11 10:44:54.728', 94383139570183, NULL, '2024-07-10 11:17:15.521', '2025-11-11 02:44:54.757', 1, 1, NULL, 0);
|
||||
INSERT INTO `im_room` VALUES (1, 1, 1, '2025-11-13 18:50:18.862', 95230070212615, NULL, '2024-07-10 11:17:15.521', '2025-11-13 10:50:18.818', 1, 1, NULL, 0);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for im_room_friend
|
||||
@@ -529,7 +529,7 @@ CREATE TABLE `im_room_friend` (
|
||||
INDEX `idx_room_id`(`room_id` ASC) USING BTREE,
|
||||
INDEX `idx_create_time`(`create_time` ASC) USING BTREE,
|
||||
INDEX `idx_update_time`(`update_time` ASC) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 94383139570180 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '单聊房间表' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 95230070212612 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '单聊房间表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of im_room_friend
|
||||
@@ -641,13 +641,13 @@ CREATE TABLE `im_user` (
|
||||
INDEX `idx_update_time`(`update_time` ASC) USING BTREE,
|
||||
INDEX `idx_active_status_last_opt_time`(`last_opt_time` ASC) USING BTREE,
|
||||
INDEX `account_UNIQUE`(`account` ASC) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 94383139570177 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户表' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 95230070212609 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of im_user
|
||||
-- ----------------------------
|
||||
INSERT INTO `im_user` VALUES (1, 61170828519936, 2, 'HuLa小管家', '022', '', 'bot', NULL, '', 0, '2025-07-07 15:27:01.711', '{\"createIp\": \"206.237.119.215\", \"updateIp\": \"120.231.232.41\", \"createIpDetail\": {\"ip\": \"206.237.119.215\", \"isp\": \"\", \"area\": \"\", \"city\": \"\", \"isp_id\": \"\", \"region\": \"\", \"city_id\": \"\", \"country\": \"美国\", \"region_id\": \"\", \"country_id\": \"US\"}, \"updateIpDetail\": {\"ip\": \"120.231.232.41\", \"isp\": \"移动\", \"area\": \"\", \"city\": \"\", \"isp_id\": \"100025\", \"region\": \"广东\", \"city_id\": \"\", \"country\": \"中国\", \"region_id\": \"440000\", \"country_id\": \"CN\"}}', 6, 0, '', '2025-03-27 04:23:08.393', '2025-10-11 10:14:24.150', 'k.23772439646234', 0, NULL, 0, '2025-05-09 18:24:37.089', 0, 1, 10);
|
||||
INSERT INTO `im_user` VALUES (10937855681024, 61170828519937, 3, 'Dawn', 'https://cdn.hulaspark.com/avatar/2439646234/97320189485dca88dcc7a70054445a56.webp', '2439646234@qq.com', '2439646234', NULL, '', 20, '2025-07-30 15:31:57.651', '{\"createIp\": \"206.237.119.215\", \"updateIp\": \"183.15.178.3\", \"createIpDetail\": null, \"updateIpDetail\": {\"ip\": \"183.15.178.3\", \"isp\": \"电信\", \"area\": \"\", \"city\": \"深圳\", \"isp_id\": \"100017\", \"region\": \"广东\", \"city_id\": \"440300\", \"country\": \"中国\", \"region_id\": \"440000\", \"country_id\": \"CN\"}}', 6, 0, '', '2025-03-27 04:23:08.393', '2025-11-10 05:34:11.070', 'k.2439646234', 0, NULL, 0, '2025-09-20 21:35:31.415', 0, 1, 10);
|
||||
INSERT INTO `im_user` VALUES (1, 61170828519936, 2, 'HuLa小管家', '022', '', '15147891644', NULL, '', 0, '2025-07-07 15:27:01.711', '{\"createIp\": \"206.237.119.215\", \"updateIp\": \"120.231.232.41\", \"createIpDetail\": {\"ip\": \"206.237.119.215\", \"isp\": \"\", \"area\": \"\", \"city\": \"\", \"isp_id\": \"\", \"region\": \"\", \"city_id\": \"\", \"country\": \"美国\", \"region_id\": \"\", \"country_id\": \"US\"}, \"updateIpDetail\": {\"ip\": \"120.231.232.41\", \"isp\": \"移动\", \"area\": \"\", \"city\": \"\", \"isp_id\": \"100025\", \"region\": \"广东\", \"city_id\": \"\", \"country\": \"中国\", \"region_id\": \"440000\", \"country_id\": \"CN\"}}', 6, 0, '', '2025-03-27 04:23:08.393', '2025-11-13 10:45:46.525', 'k.23772439646234', 0, NULL, 0, '2025-05-09 18:24:37.089', 0, 1, 10);
|
||||
INSERT INTO `im_user` VALUES (10937855681024, 61170828519937, 3, 'Dawn', 'https://cdn.hulaspark.com/avatar/2439646234/97320189485dca88dcc7a70054445a56.webp', '2439646234@qq.com', '13275346112', NULL, '', 20, '2025-07-30 15:31:57.651', '{\"createIp\": \"206.237.119.215\", \"updateIp\": \"61.141.64.252\", \"createIpDetail\": null, \"updateIpDetail\": {\"ip\": \"61.141.64.252\", \"isp\": \"电信\", \"area\": \"\", \"city\": \"深圳\", \"isp_id\": \"100017\", \"region\": \"广东\", \"city_id\": \"440300\", \"country\": \"中国\", \"region_id\": \"440000\", \"country_id\": \"CN\"}}', 6, 0, '', '2025-03-27 04:23:08.393', '2025-11-13 10:45:46.525', 'k.2439646234', 0, NULL, 0, '2025-09-20 21:35:31.415', 0, 1, 10);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for im_user_apply
|
||||
@@ -676,7 +676,7 @@ CREATE TABLE `im_user_apply` (
|
||||
INDEX `idx_target_id`(`target_id` ASC) USING BTREE,
|
||||
INDEX `idx_create_time`(`create_time` ASC) USING BTREE,
|
||||
INDEX `idx_update_time`(`update_time` ASC) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 94118743227393 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户申请表' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 95186432673281 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户申请表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of im_user_apply
|
||||
@@ -703,7 +703,7 @@ CREATE TABLE `im_user_backpack` (
|
||||
INDEX `idx_uid`(`uid` ASC) USING BTREE,
|
||||
INDEX `idx_create_time`(`create_time` ASC) USING BTREE,
|
||||
INDEX `idx_update_time`(`update_time` ASC) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 94383139570187 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户背包表' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 95230070212619 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户背包表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of im_user_backpack
|
||||
@@ -746,7 +746,7 @@ CREATE TABLE `im_user_emoji` (
|
||||
`update_by` bigint NULL DEFAULT NULL COMMENT '更新者',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
INDEX `IDX_USER_EMOJIS_UID`(`uid` ASC) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 93692199287297 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户表情包' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 94830701167617 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户表情包' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of im_user_emoji
|
||||
@@ -779,7 +779,7 @@ CREATE TABLE `im_user_friend` (
|
||||
INDEX `idx_uid_friend_uid`(`uid` ASC, `friend_uid` ASC) USING BTREE,
|
||||
INDEX `idx_create_time`(`create_time` ASC) USING BTREE,
|
||||
INDEX `idx_update_time`(`update_time` ASC) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 94383139570182 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户联系人表' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 95230070212614 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户联系人表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of im_user_friend
|
||||
@@ -957,7 +957,7 @@ CREATE TABLE `secure_invoke_record` (
|
||||
`is_del` tinyint NOT NULL DEFAULT 0 COMMENT '是否删除',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
INDEX `idx_next_retry_time`(`next_retry_time` ASC) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 94414810759682 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '本地消息表' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 95230070212618 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '本地消息表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of secure_invoke_record
|
||||
@@ -976,7 +976,7 @@ CREATE TABLE `worker_node` (
|
||||
`modified` timestamp NULL DEFAULT NULL COMMENT '修改时间',
|
||||
`created` timestamp NULL DEFAULT NULL COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 227 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = 'DB;WorkerID Assigner for UID Generator' ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 229 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = 'DB;WorkerID Assigner for UID Generator' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of worker_node
|
||||
|
||||
@@ -18,6 +18,8 @@ import com.luohuo.flex.ai.core.model.HunYuanChatModel;
|
||||
import com.luohuo.flex.ai.core.model.MidjourneyApi;
|
||||
import com.luohuo.flex.ai.core.model.SunoApi;
|
||||
import com.luohuo.flex.ai.core.model.XingHuoChatModel;
|
||||
import com.luohuo.flex.ai.core.model.openrouter.OpenRouterApiConstants;
|
||||
import com.luohuo.flex.ai.core.model.openrouter.OpenRouterChatModel;
|
||||
import com.luohuo.flex.ai.core.model.silicon.SiliconFlowApiConstants;
|
||||
import com.luohuo.flex.ai.core.model.silicon.SiliconFlowChatModel;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -236,6 +238,38 @@ public class AiAutoConfiguration {
|
||||
return new BaiChuanChatModel(openAiChatModel);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(value = "luohuo.ai.openrouter.enable", havingValue = "true")
|
||||
public OpenRouterChatModel openRouterChatClient(HulaAiProperties hulaAiProperties) {
|
||||
HulaAiProperties.OpenRouterProperties properties = hulaAiProperties.getOpenrouter();
|
||||
return buildOpenRouterChatClient(properties);
|
||||
}
|
||||
|
||||
public OpenRouterChatModel buildOpenRouterChatClient(HulaAiProperties.OpenRouterProperties properties) {
|
||||
if (StrUtil.isEmpty(properties.getModel())) {
|
||||
properties.setModel(OpenRouterApiConstants.MODEL_DEFAULT);
|
||||
}
|
||||
// 支持自定义 Base URL(可用于代理)
|
||||
String baseUrl = StrUtil.isNotEmpty(properties.getBaseUrl())
|
||||
? properties.getBaseUrl()
|
||||
: OpenRouterApiConstants.DEFAULT_BASE_URL;
|
||||
|
||||
OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
|
||||
.openAiApi(OpenAiApi.builder()
|
||||
.baseUrl(baseUrl)
|
||||
.apiKey(properties.getApiKey())
|
||||
.build())
|
||||
.defaultOptions(OpenAiChatOptions.builder()
|
||||
.model(properties.getModel())
|
||||
.temperature(properties.getTemperature())
|
||||
.maxTokens(properties.getMaxTokens())
|
||||
.topP(properties.getTopP())
|
||||
.build())
|
||||
.toolCallingManager(getToolCallingManager())
|
||||
.build();
|
||||
return new OpenRouterChatModel(openAiChatModel);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(value = "luohuo.ai.midjourney.enable", havingValue = "true")
|
||||
public MidjourneyApi midjourneyApi(HulaAiProperties hulaAiProperties) {
|
||||
|
||||
@@ -55,6 +55,12 @@ public class HulaAiProperties {
|
||||
@SuppressWarnings("SpellCheckingInspection")
|
||||
private SunoProperties suno;
|
||||
|
||||
/**
|
||||
* OpenRouter
|
||||
*/
|
||||
@SuppressWarnings("SpellCheckingInspection")
|
||||
private OpenRouterProperties openrouter;
|
||||
|
||||
@Data
|
||||
public static class DeepSeekProperties {
|
||||
|
||||
@@ -155,4 +161,17 @@ public class HulaAiProperties {
|
||||
private String baseUrl;
|
||||
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class OpenRouterProperties {
|
||||
private String enable;
|
||||
private String apiKey;
|
||||
private String baseUrl; // 支持自定义 Base URL(可用于代理)
|
||||
|
||||
private String model;
|
||||
private Double temperature;
|
||||
private Integer maxTokens;
|
||||
private Double topP;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,9 +71,9 @@ public class AiApiKeyController {
|
||||
}
|
||||
|
||||
@GetMapping("/simple-list")
|
||||
@Operation(summary = "获得 API 密钥分页列表")
|
||||
@Operation(summary = "获得 API 密钥简单列表(包含系统公开密钥和用户私有密钥)")
|
||||
public R<List<AiModelRespVO>> getApiKeySimpleList() {
|
||||
List<AiApiKeyDO> list = apiKeyService.getApiKeyList();
|
||||
List<AiApiKeyDO> list = apiKeyService.getApiKeyList(ContextUtil.getUid());
|
||||
return success(convertList(list, key -> new AiModelRespVO().setId(key.getId()).setName(key.getName()).setPlatform(key.getPlatform())));
|
||||
}
|
||||
|
||||
|
||||
@@ -27,13 +27,12 @@ import com.luohuo.flex.ai.core.model.MidjourneyApi;
|
||||
import com.luohuo.flex.ai.core.model.SunoApi;
|
||||
import com.luohuo.flex.ai.core.model.XingHuoChatModel;
|
||||
import com.luohuo.flex.ai.core.model.audio.AudioModel;
|
||||
import com.luohuo.flex.ai.core.model.openrouter.OpenRouterChatModel;
|
||||
import com.luohuo.flex.ai.core.model.silicon.*;
|
||||
import com.luohuo.flex.ai.core.model.gitee.GiteeAiAudioApi;
|
||||
import com.luohuo.flex.ai.core.model.gitee.GiteeAiAudioModel;
|
||||
import com.luohuo.flex.ai.core.model.gitee.GiteeAiAudioOptions;
|
||||
import com.luohuo.flex.ai.core.model.gitee.GiteeAiVideoApi;
|
||||
import com.luohuo.flex.ai.core.model.gitee.GiteeAiVideoModel;
|
||||
import com.luohuo.flex.ai.core.model.gitee.GiteeAiVideoOptions;
|
||||
import com.luohuo.flex.ai.core.model.video.VideoModel;
|
||||
import com.luohuo.flex.ai.enums.AiPlatformEnum;
|
||||
import com.luohuo.basic.utils.collection.CollectionUtils;
|
||||
@@ -146,7 +145,8 @@ public class AiModelFactoryImpl implements AiModelFactory {
|
||||
case MOONSHOT -> buildMoonshotChatModel(apiKey, url);
|
||||
case XING_HUO -> buildXingHuoChatModel(apiKey);
|
||||
case BAI_CHUAN -> buildBaiChuanChatModel(apiKey);
|
||||
case GITEE_AI -> buildGiteeAiChatModel(apiKey); // Gitee AI 兼容 OpenAI 接口
|
||||
case GITEE_AI -> buildGiteeAiChatModel(apiKey);
|
||||
case OPENROUTER -> buildOpenRouterChatModel(apiKey); // OpenRouter 兼容 OpenAI 接口
|
||||
case OPENAI -> buildOpenAiChatModel(apiKey, url);
|
||||
case AZURE_OPENAI -> buildAzureOpenAiChatModel(apiKey, url);
|
||||
case OLLAMA -> buildOllamaChatModel(url);
|
||||
@@ -180,6 +180,8 @@ public class AiModelFactoryImpl implements AiModelFactory {
|
||||
return SpringUtil.getBean(XingHuoChatModel.class);
|
||||
case BAI_CHUAN:
|
||||
return SpringUtil.getBean(AzureOpenAiChatModel.class);
|
||||
case OPENROUTER:
|
||||
return SpringUtil.getBean(OpenRouterChatModel.class);
|
||||
case OPENAI:
|
||||
return SpringUtil.getBean(OpenAiChatModel.class);
|
||||
case AZURE_OPENAI:
|
||||
@@ -467,6 +469,15 @@ public class AiModelFactoryImpl implements AiModelFactory {
|
||||
return OpenAiChatModel.builder().openAiApi(openAiApi).toolCallingManager(getToolCallingManager()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建 OpenRouter 聊天模型
|
||||
*/
|
||||
private static OpenRouterChatModel buildOpenRouterChatModel(String apiKey) {
|
||||
HulaAiProperties.OpenRouterProperties properties = new HulaAiProperties.OpenRouterProperties();
|
||||
properties.setApiKey(apiKey);
|
||||
return new AiAutoConfiguration().buildOpenRouterChatClient(properties);
|
||||
}
|
||||
|
||||
// TODO @芋艿:手头暂时没密钥,使用建议再测试下
|
||||
/**
|
||||
* 可参考 {@link AzureOpenAiAutoConfiguration}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
package com.luohuo.flex.ai.core.model.audio;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public interface AudioModel {
|
||||
|
||||
byte[] generateSpeech(String prompt, AudioOptions options);
|
||||
|
||||
Set<String> getSupportedVoices();
|
||||
java.util.Set<String> getSupportedVoices();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.luohuo.flex.ai.core.model.openrouter;
|
||||
|
||||
/**
|
||||
* OpenRouter API 常量
|
||||
*
|
||||
* @author 乾乾
|
||||
*/
|
||||
public final class OpenRouterApiConstants {
|
||||
|
||||
/**
|
||||
* OpenRouter API 基础 URL
|
||||
* 注意:OpenAI 客户端会自动添加 /v1,所以这里只用 https://openrouter.ai/api
|
||||
*/
|
||||
public static final String DEFAULT_BASE_URL = "https://openrouter.ai/api";
|
||||
|
||||
/**
|
||||
* 默认模型
|
||||
*/
|
||||
public static final String MODEL_DEFAULT = "openai/gpt-3.5-turbo";
|
||||
|
||||
/**
|
||||
* 提供商名称
|
||||
*/
|
||||
public static final String PROVIDER_NAME = "OpenRouter";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.luohuo.flex.ai.core.model.openrouter;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.ai.chat.model.ChatModel;
|
||||
import org.springframework.ai.chat.model.ChatResponse;
|
||||
import org.springframework.ai.chat.prompt.ChatOptions;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.openai.OpenAiChatModel;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* OpenRouter {@link ChatModel} 实现类
|
||||
*
|
||||
* <p>OpenRouter 兼容 OpenAI 接口,可以直接复用 OpenAiChatModel</p>
|
||||
* <p>API 文档:<a href="https://openrouter.ai/docs/api-reference/overview">OpenRouter API</a></p>
|
||||
*
|
||||
* @author 乾乾
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class OpenRouterChatModel implements ChatModel {
|
||||
|
||||
/**
|
||||
* OpenRouter API 基础 URL
|
||||
*/
|
||||
public static final String BASE_URL = OpenRouterApiConstants.DEFAULT_BASE_URL;
|
||||
|
||||
/**
|
||||
* 默认模型
|
||||
*/
|
||||
public static final String MODEL_DEFAULT = OpenRouterApiConstants.MODEL_DEFAULT;
|
||||
|
||||
/**
|
||||
* 兼容 OpenAI 接口,进行复用
|
||||
*/
|
||||
private final OpenAiChatModel openAiChatModel;
|
||||
|
||||
@Override
|
||||
public ChatResponse call(Prompt prompt) {
|
||||
return openAiChatModel.call(prompt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ChatResponse> stream(Prompt prompt) {
|
||||
return openAiChatModel.stream(prompt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatOptions getDefaultOptions() {
|
||||
return openAiChatModel.getDefaultOptions();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -41,6 +41,8 @@ public enum AiPlatformEnum {
|
||||
|
||||
OPENAI("OpenAI","OpenAI"), // OpenAI 官方
|
||||
|
||||
OPENROUTER("OpenRouter","OpenRouter"), // OpenRouter
|
||||
|
||||
AZURE_OPENAI("AzureOpenAI","AzureOpenAI"), // OpenAI 微软
|
||||
|
||||
OLLAMA("Ollama","Ollama"),
|
||||
|
||||
@@ -203,10 +203,10 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
|
||||
|
||||
// 响应结果
|
||||
String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getText() : null;
|
||||
newContent = StrUtil.nullToDefault(newContent, ""); // 避免 null 的 情况
|
||||
newContent = StrUtil.nullToDefault(newContent, "");
|
||||
contentBuffer.append(newContent);
|
||||
|
||||
// 提取推理内容(从 metadata 中获取,如果存在)
|
||||
// 提取推理内容
|
||||
String newReasoningContent = null;
|
||||
if (chunk.getMetadata() != null && chunk.getMetadata().containsKey("reasoning_content")) {
|
||||
newReasoningContent = String.valueOf(chunk.getMetadata().get("reasoning_content"));
|
||||
@@ -220,25 +220,22 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
|
||||
.setReasoningContent(newReasoningContent)
|
||||
.setSegments(segments)));
|
||||
}).doOnComplete(() -> {
|
||||
// 忽略租户,因为 Flux 异步无法透传租户
|
||||
String content = contentBuffer.toString();
|
||||
String reasoningContent = reasoningBuffer.toString();
|
||||
AiChatMessageDO updateMessage = new AiChatMessageDO()
|
||||
.setId(assistantMessage.getId())
|
||||
.setContent(content);
|
||||
if (StrUtil.isNotEmpty(reasoningContent)) {
|
||||
updateMessage.setReasoningContent(reasoningContent);
|
||||
// 手动设置租户信息(因为 Flux 异步会切换线程,导致 ThreadLocal 丢失)
|
||||
try {
|
||||
ContextUtil.setIgnore(true);
|
||||
|
||||
String content = contentBuffer.toString();
|
||||
String reasoningContent = reasoningBuffer.toString();
|
||||
AiChatMessageDO updateMessage = new AiChatMessageDO().setId(assistantMessage.getId()).setContent(content);
|
||||
if (StrUtil.isNotEmpty(reasoningContent)) {
|
||||
updateMessage.setReasoningContent(reasoningContent);
|
||||
}
|
||||
chatMessageMapper.updateById(updateMessage);
|
||||
} finally {
|
||||
ContextUtil.setIgnore(false);
|
||||
}
|
||||
chatMessageMapper.updateById(updateMessage);
|
||||
ContextUtil.setIgnore(false);
|
||||
}).doOnError(throwable -> {
|
||||
log.error("[sendChatMessageStream][userId({}) sendReqVO({}) 发生异常]", userId, sendReqVO, throwable);
|
||||
// 忽略租户,因为 Flux 异步无法透传租户
|
||||
ContextUtil.setIgnore(false);
|
||||
}).onErrorResume(error -> {
|
||||
ContextUtil.setIgnore(false);
|
||||
return Flux.just(error(ErrorCodeConstants.CHAT_STREAM_ERROR));
|
||||
});
|
||||
}).doOnError(throwable -> log.error("[sendChatMessageStream][userId({}) sendReqVO({}) 发生异常]", userId, sendReqVO, throwable))
|
||||
.onErrorResume(error -> Flux.just(error(ErrorCodeConstants.CHAT_STREAM_ERROR)));
|
||||
}
|
||||
|
||||
private List<AiKnowledgeSegmentSearchRespBO> recallKnowledgeSegment(String content,
|
||||
|
||||
@@ -66,11 +66,12 @@ public interface AiApiKeyService {
|
||||
PageResult<AiApiKeyDO> getApiKeyPage(AiApiKeyPageReqVO pageReqVO, Long userId);
|
||||
|
||||
/**
|
||||
* 获得 API 密钥列表
|
||||
* 获得 API 密钥列表(包含系统公开密钥和用户私有密钥)
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @return API 密钥列表
|
||||
*/
|
||||
List<AiApiKeyDO> getApiKeyList();
|
||||
List<AiApiKeyDO> getApiKeyList(Long userId);
|
||||
|
||||
/**
|
||||
* 获得默认的 API 密钥
|
||||
|
||||
@@ -13,6 +13,7 @@ import com.luohuo.flex.ai.controller.model.vo.apikey.AiApiKeySaveReqVO;
|
||||
import com.luohuo.flex.ai.dal.model.AiApiKeyDO;
|
||||
import com.luohuo.flex.ai.enums.AiPlatformEnum;
|
||||
import com.luohuo.flex.ai.enums.CommonStatusEnum;
|
||||
import com.luohuo.flex.ai.mapper.LambdaQueryWrapperX;
|
||||
import com.luohuo.flex.ai.mapper.model.AiApiKeyMapper;
|
||||
import com.luohuo.flex.ai.utils.BeanUtils;
|
||||
import jakarta.annotation.Resource;
|
||||
@@ -133,8 +134,15 @@ public class AiApiKeyServiceImpl implements AiApiKeyService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AiApiKeyDO> getApiKeyList() {
|
||||
return apiKeyMapper.selectList();
|
||||
public List<AiApiKeyDO> getApiKeyList(Long userId) {
|
||||
// 返回所有公开密钥 + 用户私有密钥
|
||||
return apiKeyMapper.selectList(new LambdaQueryWrapperX<AiApiKeyDO>()
|
||||
.and(w -> w
|
||||
.eq(AiApiKeyDO::getPublicStatus, true)
|
||||
.or()
|
||||
.eq(AiApiKeyDO::getUserId, userId)
|
||||
)
|
||||
.orderByDesc(AiApiKeyDO::getId));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -54,12 +54,14 @@ public class AiUtils {
|
||||
return MoonshotChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens)
|
||||
.functions(toolNames).toolContext(toolContext).build();
|
||||
case OPENAI:
|
||||
case DEEP_SEEK: // 复用 OpenAI 客户端
|
||||
case DOU_BAO: // 复用 OpenAI 客户端
|
||||
case HUN_YUAN: // 复用 OpenAI 客户端
|
||||
case XING_HUO: // 复用 OpenAI 客户端
|
||||
case SILICON_FLOW: // 复用 OpenAI 客户端
|
||||
case BAI_CHUAN: // 复用 OpenAI 客户端
|
||||
case DEEP_SEEK:
|
||||
case DOU_BAO:
|
||||
case HUN_YUAN:
|
||||
case XING_HUO:
|
||||
case SILICON_FLOW:
|
||||
case BAI_CHUAN:
|
||||
case OPENROUTER:
|
||||
case GITEE_AI: // 复用 OpenAI 客户端
|
||||
return OpenAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens)
|
||||
.toolNames(toolNames).toolContext(toolContext).build();
|
||||
case AZURE_OPENAI:
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
package com.luohuo.flex.base.manager.tenant.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.luohuo.basic.base.manager.impl.SuperCacheManagerImpl;
|
||||
import com.luohuo.basic.database.mybatis.conditions.Wraps;
|
||||
import com.luohuo.basic.model.cache.CacheKeyBuilder;
|
||||
import com.luohuo.basic.utils.BeanPlusUtil;
|
||||
import com.luohuo.basic.utils.CollHelper;
|
||||
import com.luohuo.flex.base.entity.tenant.DefTenant;
|
||||
import com.luohuo.flex.base.entity.tenant.DefUserTenantRel;
|
||||
import com.luohuo.flex.base.manager.tenant.DefTenantManager;
|
||||
import com.luohuo.flex.base.mapper.application.DefUserTenantRelMapper;
|
||||
import com.luohuo.flex.base.mapper.tenant.DefTenantMapper;
|
||||
import com.luohuo.flex.base.vo.result.user.DefTenantResultVO;
|
||||
import com.luohuo.flex.common.cache.tenant.application.TenantCacheKeyBuilder;
|
||||
@@ -19,6 +23,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 应用管理
|
||||
@@ -31,6 +36,9 @@ import java.util.Set;
|
||||
@RequiredArgsConstructor
|
||||
@Service(EchoApi.DEF_TENANT_SERVICE_IMPL_CLASS)
|
||||
public class DefTenantManagerImpl extends SuperCacheManagerImpl<DefTenantMapper, DefTenant> implements DefTenantManager {
|
||||
|
||||
private final DefUserTenantRelMapper defUserTenantRelMapper;
|
||||
|
||||
@Override
|
||||
protected CacheKeyBuilder cacheKeyBuilder() {
|
||||
return new TenantCacheKeyBuilder();
|
||||
@@ -54,6 +62,42 @@ public class DefTenantManagerImpl extends SuperCacheManagerImpl<DefTenantMapper,
|
||||
|
||||
@Override
|
||||
public List<DefTenantResultVO> listTenantByUserId(Long userId) {
|
||||
return baseMapper.listTenantByUserId(userId);
|
||||
// 1. 查询用户的所有租户关系(员工信息)
|
||||
List<DefUserTenantRel> userTenantRelList = defUserTenantRelMapper.selectList(
|
||||
Wrappers.<DefUserTenantRel>lambdaQuery()
|
||||
.eq(DefUserTenantRel::getUserId, userId)
|
||||
);
|
||||
|
||||
if (userTenantRelList.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
// 2. 提取租户ID列表
|
||||
List<Long> tenantIds = userTenantRelList.stream()
|
||||
.map(DefUserTenantRel::getTenantId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 3. 查询租户信息
|
||||
List<DefTenant> tenantList = baseMapper.selectList(
|
||||
Wrappers.<DefTenant>lambdaQuery()
|
||||
.in(DefTenant::getId, tenantIds)
|
||||
);
|
||||
|
||||
// 4. 转换为 VO 并填充员工信息
|
||||
Map<Long, DefUserTenantRel> relMap = userTenantRelList.stream()
|
||||
.collect(Collectors.toMap(DefUserTenantRel::getTenantId, rel -> rel));
|
||||
|
||||
return tenantList.stream()
|
||||
.map(tenant -> {
|
||||
DefTenantResultVO vo = BeanPlusUtil.toBean(tenant, DefTenantResultVO.class);
|
||||
DefUserTenantRel rel = relMap.get(tenant.getId());
|
||||
if (rel != null) {
|
||||
vo.setEmployeeId(rel.getId());
|
||||
vo.setEmployeeState(rel.getState());
|
||||
vo.setIsDefault(rel.getIsDefault());
|
||||
}
|
||||
return vo;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,8 @@ package com.luohuo.flex.base.mapper.tenant;
|
||||
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
|
||||
import com.luohuo.basic.base.mapper.SuperMapper;
|
||||
import com.luohuo.flex.base.entity.tenant.DefTenant;
|
||||
import com.luohuo.flex.base.vo.result.user.DefTenantResultVO;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -19,11 +16,4 @@ import java.util.List;
|
||||
@Repository
|
||||
@InterceptorIgnore(tenantLine = "true", dynamicTableName = "true")
|
||||
public interface DefTenantMapper extends SuperMapper<DefTenant> {
|
||||
/**
|
||||
* 查询用户的可用企业
|
||||
*
|
||||
* @param userId 用户id
|
||||
* @return
|
||||
*/
|
||||
List<DefTenantResultVO> listTenantByUserId(@Param("userId") Long userId);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.luohuo.flex.base.service.tenant;
|
||||
|
||||
import cn.hutool.crypto.digest.DigestUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 账号生成器
|
||||
* 生成11位纯数字账号
|
||||
*
|
||||
* @author 乾乾
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class AccountGenerator {
|
||||
|
||||
/**
|
||||
* 基于用户ID生成唯一的11位纯数字账号
|
||||
* 格式:1XXXXXXXXXX(第一位固定为1,后面10位由用户ID哈希生成)
|
||||
*
|
||||
* @param userId 用户ID(def_user.id)
|
||||
* @return 11位纯数字账号
|
||||
*/
|
||||
public String generateAccountByUserId(Long userId) {
|
||||
if (userId == null || userId <= 0) {
|
||||
throw new IllegalArgumentException("用户ID不能为空或小于等于0");
|
||||
}
|
||||
|
||||
// 使用 MD5 哈希算法
|
||||
String md5Hex = DigestUtil.md5Hex(String.valueOf(userId));
|
||||
|
||||
// 取前10位十六进制转换为十进制
|
||||
String hex10 = md5Hex.substring(0, 10);
|
||||
long hashValue = Long.parseLong(hex10, 16);
|
||||
|
||||
// 确保是11位数字,第一位是1
|
||||
long account = 10000000000L + (hashValue % 10000000000L);
|
||||
|
||||
String accountStr = String.valueOf(account);
|
||||
log.debug("生成账号: {} (基于用户ID: {})", accountStr, userId);
|
||||
|
||||
return accountStr;
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import com.luohuo.basic.utils.ArgumentAssert;
|
||||
import com.luohuo.basic.utils.BeanPlusUtil;
|
||||
import com.luohuo.flex.base.entity.tenant.DefUser;
|
||||
import com.luohuo.flex.base.manager.tenant.DefUserManager;
|
||||
import com.luohuo.flex.base.service.tenant.AccountGenerator;
|
||||
import com.luohuo.flex.base.service.tenant.DefUserService;
|
||||
import com.luohuo.flex.base.vo.query.tenant.DefUserPageQuery;
|
||||
import com.luohuo.flex.base.vo.query.tenant.DefUserResultVO;
|
||||
@@ -68,6 +69,7 @@ public class DefUserServiceImpl extends SuperCacheServiceImpl<DefUserManager, Lo
|
||||
|
||||
private final AppendixService appendixService;
|
||||
private final SystemProperties systemProperties;
|
||||
private final AccountGenerator accountGenerator;
|
||||
|
||||
@Override
|
||||
public Map<Serializable, Object> findByIds(Set<Serializable> ids) {
|
||||
@@ -159,23 +161,44 @@ public class DefUserServiceImpl extends SuperCacheServiceImpl<DefUserManager, Lo
|
||||
@Override
|
||||
public String registerByEmail(DefUser defUser) {
|
||||
ArgumentAssert.isFalse(checkMobile(defUser.getEmail(), null), "邮箱:{}已经存在", defUser.getMobile());
|
||||
setDefUser(defUser);
|
||||
defUser.setNickName(defUser.getEmail());
|
||||
|
||||
// 先设置基本信息(不设置账号)
|
||||
defUser.setTenantId(1L);
|
||||
defUser.setSalt(RandomUtil.randomString(20));
|
||||
defUser.setPassword(SecureUtil.sha256(defUser.getPassword() + defUser.getSalt()));
|
||||
defUser.setPasswordErrorNum(0);
|
||||
defUser.setReadonly(false);
|
||||
defUser.setState(true);
|
||||
defUser.setUsername("temp_" + System.currentTimeMillis()); // 临时用户名
|
||||
superManager.save(defUser);
|
||||
|
||||
String account = accountGenerator.generateAccountByUserId(defUser.getId());
|
||||
if (StrUtil.isEmpty(defUser.getNickName())) {
|
||||
defUser.setNickName("hula用户_" + account.substring(account.length() - 6));
|
||||
}
|
||||
|
||||
defUser.setUsername(account);
|
||||
superManager.updateById(defUser);
|
||||
|
||||
log.info("用户注册成功,邮箱:{},用户ID:{},生成账号:{},昵称:{}", defUser.getEmail(), defUser.getId(), account, defUser.getNickName());
|
||||
return defUser.getEmail();
|
||||
}
|
||||
|
||||
private void setDefUser(DefUser defUser) {
|
||||
String account = defUser.getEmail().split("@")[0];
|
||||
defUser.setTenantId(1L);
|
||||
defUser.setSalt(RandomUtil.randomString(20));
|
||||
defUser.setPassword(SecureUtil.sha256(defUser.getPassword() + defUser.getSalt()));
|
||||
defUser.setPasswordErrorNum(0);
|
||||
defUser.setReadonly(false);
|
||||
defUser.setState(true);
|
||||
defUser.setUsername(account);
|
||||
defUser.setNickName(defUser.getNickName());
|
||||
|
||||
// 如果已经有ID,生成账号;否则使用临时账号
|
||||
if (defUser.getId() != null && defUser.getId() > 0) {
|
||||
String account = accountGenerator.generateAccountByUserId(defUser.getId());
|
||||
defUser.setUsername(account);
|
||||
} else {
|
||||
defUser.setUsername("temp_" + System.currentTimeMillis());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.luohuo.flex.controller.tenant;
|
||||
import com.luohuo.basic.annotation.log.WebLog;
|
||||
import com.luohuo.basic.base.R;
|
||||
import com.luohuo.basic.base.controller.SuperCacheController;
|
||||
import com.luohuo.basic.context.ContextUtil;
|
||||
import com.luohuo.basic.database.mybatis.conditions.Wraps;
|
||||
import com.luohuo.basic.interfaces.echo.EchoService;
|
||||
import com.luohuo.flex.base.entity.tenant.DefTenant;
|
||||
@@ -84,4 +85,14 @@ public class DefTenantController extends SuperCacheController<DefTenantService,
|
||||
public R<Boolean> updateState(@NotNull(message = "请修改正确的企业") @RequestParam Long id, @RequestParam @NotNull(message = "请传递状态值") Boolean state) {
|
||||
return success(superService.updateState(id, state));
|
||||
}
|
||||
|
||||
@Operation(summary = "查询用户的可用企业", description = "查询用户的可用企业")
|
||||
@GetMapping("/listTenantByUserId")
|
||||
@WebLog("查询用户的可用企业")
|
||||
public R<List<DefTenantResultVO>> listTenantByUserId(@RequestParam(required = false) Long userId) {
|
||||
if (userId == null) {
|
||||
userId = ContextUtil.getUserId();
|
||||
}
|
||||
return success(superService.listTenantByUserId(userId));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,5 +57,12 @@
|
||||
<optional>true</optional>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 响应式版本(用于 StpInterfaceServiceImpl) -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-reactor-spring-boot3-starter</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
@@ -1,31 +1,49 @@
|
||||
//package com.luohuo.flex.gateway.service;
|
||||
//
|
||||
//import cn.dev33.satoken.stp.StpInterface;
|
||||
//import lombok.RequiredArgsConstructor;
|
||||
//import lombok.extern.slf4j.Slf4j;
|
||||
//import org.springframework.stereotype.Component;
|
||||
//import com.luohuo.flex.oauth.biz.StpInterfaceBiz;
|
||||
//
|
||||
//import java.util.List;
|
||||
//
|
||||
///**
|
||||
// * sa-token 权限网关实现
|
||||
// * @author tangyh
|
||||
// * @since 2024/8/6 21:46
|
||||
// */
|
||||
//@Component
|
||||
//@Slf4j
|
||||
//@RequiredArgsConstructor
|
||||
//public class StpInterfaceServiceImpl implements StpInterface {
|
||||
// private final StpInterfaceBiz stpInterfaceBiz;
|
||||
//
|
||||
// @Override
|
||||
// public List<String> getPermissionList(Object loginId, String loginType) {
|
||||
// return stpInterfaceBiz.getPermissionList();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public List<String> getRoleList(Object loginId, String loginType) {
|
||||
// return stpInterfaceBiz.getRoleList();
|
||||
// }
|
||||
//}
|
||||
package com.luohuo.flex.gateway.service;
|
||||
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.stp.StpInterface;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static com.luohuo.basic.context.ContextConstants.JWT_KEY_PERMISSION_LIST;
|
||||
import static com.luohuo.basic.context.ContextConstants.JWT_KEY_ROLE_LIST;
|
||||
|
||||
/**
|
||||
* sa-token 权限网关实现
|
||||
* Gateway 是响应式的,不能依赖 luohuo-oauth-biz(Servlet)、所以直接从 Session 中读取权限列表(登录时已经存入)
|
||||
*
|
||||
* @author 乾乾
|
||||
* @since 2025/11/13 21:46
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class StpInterfaceServiceImpl implements StpInterface {
|
||||
|
||||
@Override
|
||||
public List<String> getPermissionList(Object loginId, String loginType) {
|
||||
try {
|
||||
SaSession tokenSession = StpUtil.getTokenSession();
|
||||
return tokenSession.get(JWT_KEY_PERMISSION_LIST, Collections.emptyList());
|
||||
} catch (Exception e) {
|
||||
log.error("获取权限列表失败,loginId={}", loginId, e);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getRoleList(Object loginId, String loginType) {
|
||||
try {
|
||||
SaSession tokenSession = StpUtil.getTokenSession();
|
||||
return tokenSession.get(JWT_KEY_ROLE_LIST, Collections.emptyList());
|
||||
} catch (Exception e) {
|
||||
log.error("获取角色列表失败,loginId={}", loginId, e);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import com.luohuo.flex.im.core.user.mapper.UserMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -72,7 +71,4 @@ public class UserDao extends ServiceImpl<UserMapper, User> {
|
||||
return baseMapper.selectCount(wrapper) > 0;
|
||||
}
|
||||
|
||||
public List<User> getByIds(Set<Long> uidSet) {
|
||||
return baseMapper.selectBatchIds(uidSet);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.luohuo.flex.im.core.user.service;
|
||||
|
||||
import com.baidu.fsg.uid.Base62Encoder;
|
||||
import com.baidu.fsg.uid.UidGenerator;
|
||||
import cn.hutool.core.lang.UUID;
|
||||
import com.luohuo.basic.cache.redis2.CacheResult;
|
||||
import com.luohuo.basic.cache.repository.CachePlusOps;
|
||||
import com.luohuo.basic.service.MQProducer;
|
||||
@@ -22,7 +21,6 @@ import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
|
||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
@@ -57,15 +55,9 @@ public class WxMsgService {
|
||||
@Resource
|
||||
private UserDao userDao;
|
||||
|
||||
@Resource
|
||||
private UserService userService;
|
||||
|
||||
// @Resource
|
||||
// private LoginService loginService;
|
||||
|
||||
@Resource
|
||||
private UidGenerator uidGenerator;
|
||||
|
||||
@Resource
|
||||
private CachePlusOps cachePlusOps;
|
||||
|
||||
@@ -121,18 +113,14 @@ public class WxMsgService {
|
||||
}
|
||||
|
||||
private void fillUserInfo(Long uid, WxOAuth2UserInfo userInfo) {
|
||||
User fillUser = UserAdapter.buildAuthorizeUser(uid, Base62Encoder.createAccount(uidGenerator.getUid()), userInfo);
|
||||
// TODO 循环防止账号重复,存在bug
|
||||
for (int i = 0; i < 5; i++) {
|
||||
try {
|
||||
userDao.updateById(fillUser);
|
||||
return;
|
||||
} catch (DuplicateKeyException e) {
|
||||
log.info("fill userInfo duplicate uid:{},info:{}", uid, userInfo);
|
||||
} catch (Exception e) {
|
||||
log.error("fill userInfo fail uid:{},info:{}", uid, userInfo);
|
||||
}
|
||||
fillUser.setAccount(Base62Encoder.createAccount(uidGenerator.getUid()));
|
||||
// 基于用户ID生成11位纯数字账号
|
||||
User fillUser = UserAdapter.buildAuthorizeUser(uid, UUID.fastUUID().toString(), userInfo);
|
||||
|
||||
try {
|
||||
userDao.updateById(fillUser);
|
||||
} catch (Exception e) {
|
||||
log.error("微信用户信息填充失败,uid:{}, info:{}", uid, userInfo, e);
|
||||
throw new RuntimeException("微信用户信息填充失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.luohuo.flex.im.core.user.service.adapter;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import com.luohuo.flex.im.domain.dto.SummeryInfoDTO;
|
||||
import me.chanjar.weixin.common.bean.WxOAuth2UserInfo;
|
||||
import com.luohuo.flex.im.common.enums.YesOrNoEnum;
|
||||
@@ -28,13 +27,12 @@ public class UserAdapter {
|
||||
user.setId(id);
|
||||
user.setAvatar(userInfo.getHeadImgUrl());
|
||||
user.setAccount(account);
|
||||
user.setName(userInfo.getNickname());
|
||||
user.setSex(userInfo.getSex());
|
||||
if (userInfo.getNickname().length() > 6) {
|
||||
user.setName("名字过长" + RandomUtil.randomInt(100000));
|
||||
} else {
|
||||
user.setName(userInfo.getNickname());
|
||||
}
|
||||
|
||||
// hula用户_账号后6位
|
||||
String userName = "hula用户_" + account.substring(account.length() - 6);
|
||||
user.setName(userName);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,6 @@ package com.luohuo.flex.im.core.user.service.impl;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||
import com.luohuo.basic.context.ContextUtil;
|
||||
import com.luohuo.basic.utils.SpringUtils;
|
||||
import com.luohuo.basic.utils.TimeUtils;
|
||||
@@ -313,26 +311,30 @@ public class UserServiceImpl implements UserService {
|
||||
if (userDao.existsByEmailAndIdNot(null, userRegisterVo.getEmail())) {
|
||||
return false;
|
||||
}
|
||||
String account = userRegisterVo.getEmail().split("@")[0];
|
||||
boolean exists = userDao.count(new QueryWrapper<User>().lambda().eq(User::getAccount, account)) > 0;
|
||||
|
||||
// 2. 走注册流程
|
||||
final User newUser = User.builder()
|
||||
// 2. 直接使用 OAuth 服务传递过来的账号和用户名
|
||||
String account = userRegisterVo.getAccount();
|
||||
String userName = userRegisterVo.getName();
|
||||
|
||||
log.info("用户注册,邮箱:{},DefUser ID:{},账号:{},用户名:{}", userRegisterVo.getEmail(), userRegisterVo.getUserId(), account, userName);
|
||||
|
||||
// 3. 创建 IM 用户
|
||||
User newUser = User.builder()
|
||||
.userId(userRegisterVo.getUserId())
|
||||
.avatar(userRegisterVo.getAvatar())
|
||||
.account(exists? userRegisterVo.getEmail(): account)
|
||||
.account(account)
|
||||
.email(userRegisterVo.getEmail())
|
||||
.sex(userRegisterVo.getSex())
|
||||
.userType(userRegisterVo.getUserType())
|
||||
.name(userRegisterVo.getName())
|
||||
.resume("这个人还没有填写个人简介呢")
|
||||
.sex(userRegisterVo.getSex())
|
||||
.userType(userRegisterVo.getUserType())
|
||||
.name(userName)
|
||||
.resume("这个人还没有填写个人简介呢")
|
||||
.openId(userRegisterVo.getOpenId())
|
||||
.tenantId(userRegisterVo.getTenantId())
|
||||
.context(false)
|
||||
.tenantId(userRegisterVo.getTenantId())
|
||||
.context(false)
|
||||
.build();
|
||||
|
||||
// 保存用户
|
||||
newUser.setCreateBy(1L);
|
||||
newUser.setCreateBy(1L);
|
||||
userDao.save(newUser);
|
||||
|
||||
// 注入群组信息
|
||||
|
||||
@@ -33,8 +33,6 @@ public class StpInterfaceBiz {
|
||||
public List<String> getPermissionList() {
|
||||
SaSession tokenSession = StpUtil.getTokenSession();
|
||||
long employeeId = tokenSession.getLong(JWT_KEY_U_ID);
|
||||
// 超管 返回 *
|
||||
|
||||
List<DefResource> list;
|
||||
boolean isAdmin = baseRoleService.checkRole(employeeId, RoleConstant.TENANT_ADMIN);
|
||||
List<String> resourceCodes = Collections.emptyList();
|
||||
|
||||
@@ -14,7 +14,7 @@ import java.util.stream.Stream;
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum LoginEnum {
|
||||
ACCOUNT(1, "管理端登录: admin|RAM"),
|
||||
MANAGER(1, "管理端登录: admin|RAM"),
|
||||
IM(2, "IM系统登录"),
|
||||
;
|
||||
private final Integer val;
|
||||
|
||||
@@ -27,6 +27,7 @@ import com.luohuo.basic.exception.TokenExceedException;
|
||||
import com.luohuo.flex.common.utils.ToolsUtil;
|
||||
import com.luohuo.flex.model.entity.ws.OffLineResp;
|
||||
import com.luohuo.flex.model.event.UserOfflineEvent;
|
||||
import com.luohuo.flex.oauth.biz.StpInterfaceBiz;
|
||||
import com.luohuo.flex.oauth.emuns.LoginEnum;
|
||||
import com.luohuo.flex.model.event.UserOnlineEvent;
|
||||
import com.luohuo.flex.im.api.ImUserApi;
|
||||
@@ -89,6 +90,7 @@ public abstract class AbstractTokenGranter implements TokenGranter {
|
||||
protected final BaseOrgService baseOrgService;
|
||||
protected final SaTokenConfig saTokenConfig;
|
||||
protected final ImUserApi imUserApi;
|
||||
protected final StpInterfaceBiz stpInterfaceBiz;
|
||||
@Override
|
||||
public R<LoginResultVO> login(LoginParamVO loginParam) {
|
||||
// 1. 参数校验
|
||||
@@ -146,7 +148,7 @@ public abstract class AbstractTokenGranter implements TokenGranter {
|
||||
* @return
|
||||
*/
|
||||
private Long findUid(Long defUid, Long tenantId, Integer systemType) {
|
||||
if(LoginEnum.ACCOUNT.getVal().equals(systemType)){
|
||||
if(LoginEnum.MANAGER.getVal().equals(systemType)){
|
||||
return baseEmployeeService.getEmployeeByUser(defUid).getId();
|
||||
} else {
|
||||
return imUserApi.findById(defUid, tenantId).getData();
|
||||
@@ -414,6 +416,14 @@ public abstract class AbstractTokenGranter implements TokenGranter {
|
||||
tokenSession.delete(HEADER_TENANT_ID);
|
||||
}
|
||||
|
||||
// 3. 保存权限列表和角色列表到 Session; Gateway 是响应式的,不能直接查询数据库,所以从 Session 中读取
|
||||
if(userInfo.getSystemType().equals(LoginEnum.MANAGER.getVal())){
|
||||
List<String> permissionList = stpInterfaceBiz.getPermissionList();
|
||||
List<String> roleList = stpInterfaceBiz.getRoleList();
|
||||
tokenSession.set("permissionList", permissionList);
|
||||
tokenSession.set("roleList", roleList);
|
||||
}
|
||||
|
||||
LoginResultVO resultVO = new LoginResultVO();
|
||||
resultVO.setToken(StpUtil.getTokenValue());
|
||||
resultVO.setClient(deviceType);
|
||||
|
||||
@@ -36,8 +36,8 @@ public class CaptchaTokenGranter extends PasswordTokenGranter implements TokenGr
|
||||
@Resource
|
||||
private CaptchaService captchaService;
|
||||
|
||||
public CaptchaTokenGranter(SystemProperties systemProperties, DefClientService defClientService, DefUserService defUserService, BaseEmployeeService baseEmployeeService, BaseOrgService baseOrgService, SaTokenConfig saTokenConfig, ImUserApi userApi) {
|
||||
super(systemProperties, defClientService, defUserService, baseEmployeeService, baseOrgService, saTokenConfig, userApi);
|
||||
public CaptchaTokenGranter(SystemProperties systemProperties, DefClientService defClientService, DefUserService defUserService, BaseEmployeeService baseEmployeeService, BaseOrgService baseOrgService, SaTokenConfig saTokenConfig, ImUserApi userApi, com.luohuo.flex.oauth.biz.StpInterfaceBiz stpInterfaceBiz) {
|
||||
super(systemProperties, defClientService, defUserService, baseEmployeeService, baseOrgService, saTokenConfig, userApi, stpInterfaceBiz);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -54,8 +54,8 @@ public class MobileTokenGranter extends AbstractTokenGranter implements TokenGra
|
||||
@Resource
|
||||
private CaptchaService captchaService;
|
||||
|
||||
public MobileTokenGranter(SystemProperties systemProperties, DefClientService defClientService, DefUserService defUserService, BaseEmployeeService baseEmployeeService, BaseOrgService baseOrgService, SaTokenConfig saTokenConfig, ImUserApi userApi) {
|
||||
super(systemProperties, defClientService, defUserService, baseEmployeeService, baseOrgService, saTokenConfig, userApi);
|
||||
public MobileTokenGranter(SystemProperties systemProperties, DefClientService defClientService, DefUserService defUserService, BaseEmployeeService baseEmployeeService, BaseOrgService baseOrgService, SaTokenConfig saTokenConfig, ImUserApi userApi, com.luohuo.flex.oauth.biz.StpInterfaceBiz stpInterfaceBiz) {
|
||||
super(systemProperties, defClientService, defUserService, baseEmployeeService, baseOrgService, saTokenConfig, userApi, stpInterfaceBiz);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -57,8 +57,8 @@ public class PasswordTokenGranter extends AbstractTokenGranter implements TokenG
|
||||
|
||||
public static final String GRANT_TYPE = "PASSWORD";
|
||||
|
||||
public PasswordTokenGranter(SystemProperties systemProperties, DefClientService defClientService, DefUserService defUserService, BaseEmployeeService baseEmployeeService, BaseOrgService baseOrgService, SaTokenConfig saTokenConfig, ImUserApi userApi) {
|
||||
super(systemProperties, defClientService, defUserService, baseEmployeeService, baseOrgService, saTokenConfig, userApi);
|
||||
public PasswordTokenGranter(SystemProperties systemProperties, DefClientService defClientService, DefUserService defUserService, BaseEmployeeService baseEmployeeService, BaseOrgService baseOrgService, SaTokenConfig saTokenConfig, ImUserApi userApi, com.luohuo.flex.oauth.biz.StpInterfaceBiz stpInterfaceBiz) {
|
||||
super(systemProperties, defClientService, defUserService, baseEmployeeService, baseOrgService, saTokenConfig, userApi, stpInterfaceBiz);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -79,12 +79,12 @@ public class PasswordTokenGranter extends AbstractTokenGranter implements TokenG
|
||||
if (emailLogin) {
|
||||
return defUserService.getUserByEmail(systemType, account);
|
||||
}
|
||||
boolean mobileLogin = ValidatorUtil.isMobile(account);
|
||||
if (mobileLogin) {
|
||||
|
||||
DefUser defUser = defUserService.getUserByUsername(systemType, account);
|
||||
if (defUser == null) {
|
||||
return defUserService.getUserByMobile(systemType, account);
|
||||
}
|
||||
|
||||
return defUserService.getUserByUsername(systemType, account);
|
||||
return defUser;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -48,8 +48,8 @@ public class QrCodeGranter extends AbstractTokenGranter {
|
||||
@Resource
|
||||
protected CachePlusOps cachePlusOps;
|
||||
|
||||
public QrCodeGranter(SystemProperties systemProperties, DefClientService defClientService, DefUserService defUserService, BaseEmployeeService baseEmployeeService, BaseOrgService baseOrgService, SaTokenConfig saTokenConfig, ImUserApi imUserApi) {
|
||||
super(systemProperties, defClientService, defUserService, baseEmployeeService, baseOrgService, saTokenConfig, imUserApi);
|
||||
public QrCodeGranter(SystemProperties systemProperties, DefClientService defClientService, DefUserService defUserService, BaseEmployeeService baseEmployeeService, BaseOrgService baseOrgService, SaTokenConfig saTokenConfig, ImUserApi imUserApi, com.luohuo.flex.oauth.biz.StpInterfaceBiz stpInterfaceBiz) {
|
||||
super(systemProperties, defClientService, defUserService, baseEmployeeService, baseOrgService, saTokenConfig, imUserApi, stpInterfaceBiz);
|
||||
}
|
||||
|
||||
public QrCodeResp generateQRCode() {
|
||||
|
||||
@@ -116,7 +116,7 @@ public class UserInfoServiceImpl implements UserInfoService {
|
||||
userRegisterVo.setAccount(defUser.getUsername());
|
||||
userRegisterVo.setEmail(register.getEmail());
|
||||
userRegisterVo.setUserId(defUser.getId());
|
||||
userRegisterVo.setName(defUser.getUsername());
|
||||
userRegisterVo.setName(defUser.getNickName());
|
||||
userRegisterVo.setOpenId(defUser.getWxOpenId());
|
||||
userRegisterVo.setSex(defUser.getSex());
|
||||
userRegisterVo.setAvatar(defUser.getAvatar());
|
||||
@@ -127,7 +127,7 @@ public class UserInfoServiceImpl implements UserInfoService {
|
||||
}
|
||||
yield defUser.getEmail();
|
||||
}
|
||||
case ACCOUNT -> {
|
||||
case MANAGER -> {
|
||||
// 2.1 注册后台管理员、RAM账号
|
||||
BaseEmployee baseEmployee = new BaseEmployee();
|
||||
baseEmployee.setName(defUser.getUsername());
|
||||
|
||||
@@ -46,4 +46,78 @@ spring:
|
||||
minimax: # Minimax:https://www.minimaxi.com/
|
||||
api-key: xxxx
|
||||
moonshot: # 月之暗灭(KIMI)
|
||||
api-key: sk-abc
|
||||
api-key: sk-abc
|
||||
|
||||
# 自定义 AI 配置
|
||||
luohuo:
|
||||
ai:
|
||||
# DeepSeek
|
||||
deepseek:
|
||||
enable: false
|
||||
api-key: sk-xxx
|
||||
model: deepseek-chat
|
||||
temperature: 0.7
|
||||
max-tokens: 4096
|
||||
top-p: 1.0
|
||||
# 字节豆包
|
||||
doubao:
|
||||
enable: false
|
||||
api-key: sk-xxx
|
||||
model: doubao-pro-4k
|
||||
temperature: 0.7
|
||||
max-tokens: 4096
|
||||
top-p: 1.0
|
||||
# 硅基流动
|
||||
siliconflow:
|
||||
enable: false
|
||||
api-key: sk-xxx
|
||||
model: Qwen/Qwen2.5-7B-Instruct
|
||||
temperature: 0.7
|
||||
max-tokens: 4096
|
||||
top-p: 1.0
|
||||
# 腾讯混元
|
||||
hunyuan:
|
||||
enable: false
|
||||
api-key: sk-xxx
|
||||
base-url: https://api.hunyuan.cloud.tencent.com/v1
|
||||
model: hunyuan-lite
|
||||
temperature: 0.7
|
||||
max-tokens: 4096
|
||||
top-p: 1.0
|
||||
# 讯飞星火
|
||||
xinghuo:
|
||||
enable: false
|
||||
app-id: xxx
|
||||
app-key: xxx
|
||||
secret-key: xxx
|
||||
model: generalv3.5
|
||||
temperature: 0.7
|
||||
max-tokens: 4096
|
||||
top-p: 1.0
|
||||
# 百川智能
|
||||
baichuan:
|
||||
enable: false
|
||||
api-key: sk-xxx
|
||||
model: Baichuan2-Turbo
|
||||
temperature: 0.7
|
||||
max-tokens: 4096
|
||||
top-p: 1.0
|
||||
# OpenRouter
|
||||
openrouter:
|
||||
enable: false
|
||||
api-key: sk-xxx
|
||||
base-url: https://openrouter.ai/api # 可选,默认为 https://openrouter.ai/api,如果需要代理可以修改为代理地址
|
||||
model: openai/gpt-3.5-turbo
|
||||
temperature: 0.7
|
||||
max-tokens: 4096
|
||||
top-p: 1.0
|
||||
# Midjourney
|
||||
midjourney:
|
||||
enable: false
|
||||
base-url: http://127.0.0.1:8080
|
||||
api-key: xxx
|
||||
notify-url: http://127.0.0.1:48080/admin-api/ai/image/midjourney/notify
|
||||
# Suno
|
||||
suno:
|
||||
enable: false
|
||||
base-url: http://127.0.0.1:8080
|
||||
@@ -44,6 +44,16 @@ public final class ContextConstants {
|
||||
public static final String JWT_KEY_DEPT_ID = "CurrentDeptId";
|
||||
public static final String JWT_KEY_DEVICE = "loginDevice";
|
||||
public static final String JWT_KEY_SYSTEM_TYPE = "systemType";
|
||||
|
||||
/**
|
||||
* Session 中存储权限列表的 key
|
||||
*/
|
||||
public static final String JWT_KEY_PERMISSION_LIST = "permissionList";
|
||||
|
||||
/**
|
||||
* Session 中存储角色列表的 key
|
||||
*/
|
||||
public static final String JWT_KEY_ROLE_LIST = "roleList";
|
||||
/**
|
||||
* JWT中封装的 随机数
|
||||
*/
|
||||
|
||||
BIN
preview/wx.png
BIN
preview/wx.png
Binary file not shown.
|
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 213 KiB |
Reference in New Issue
Block a user