代码提交同步

This commit is contained in:
wong
2025-09-13 10:45:32 +08:00
parent a2a0cb8461
commit cc432ba18f
10 changed files with 8043 additions and 131 deletions

View File

@@ -2,6 +2,7 @@
namespace app\ai\controller;
use app\common\util\JwtUtil;
use think\facade\Env;
class DouBaoAI
@@ -31,12 +32,17 @@ class DouBaoAI
{
$this->__init();
$content = input('content','');
if (empty($content)){
return json_encode(['code' => 500, 'msg' => '提示词缺失']);
}
// 发送请求
$params = [
'model' => 'doubao-1-5-pro-32k-250115',
'messages' => [
['role' => 'system', 'content' => '你是人工智能助手.'],
['role' => 'user', 'content' => '厦门天气'],
['role' => 'user', 'content' => $content],
],
/*'extra_headers' => [
'x-is-encrypted' => true

View File

@@ -0,0 +1,28 @@
<?php
// +----------------------------------------------------------------------
// | 设备管理模块路由配置
// +----------------------------------------------------------------------
use think\facade\Route;
// 定义RESTful风格的API路由
Route::group('v1/', function () {
Route::group('kefu/', function () {
});
})->middleware(['jwt']);
// 客服登录
Route::group('v1/kefu', function () {
Route::post('login', 'app\chukebao\controller\LoginController@index'); // 获取好友列表
});
return [];

View File

@@ -0,0 +1,101 @@
<?php
namespace app\chukebao\controller;
use app\common\controller\BaseController;
use app\common\util\JwtUtil;
use Exception;
use library\ResponseHelper;
use app\api\controller\UserController;
use think\Db;
/**
* 认证控制器
* 处理用户登录和身份验证
*/
class LoginController extends BaseController
{
/**
* 用户登录
*
* @return \think\response\Json
*/
public function index($username = '', $password = '',$verifySessionId = '',$verifyCode = '')
{
$username = !empty($username) ? $username : $this->request->param('account', '');
$password = !empty($password) ? $password : $this->request->param('password', '');
$verifySessionId =!empty($verifySessionId) ? $verifySessionId : $this->request->param('verifySessionId', '');
$verifyCode = !empty($verifyCode) ? $verifyCode : $this->request->param('verifyCode', '');
if (empty($username) || empty($password)) {
return ResponseHelper::error('请输入账号密码');
}
// 验证账号是否存在(支持账号或手机号登录)
$user = Db::name('users')
->where(function ($query) use ($username) {
$query->where('account', $username)->whereOr('phone', $username);
})
->where('passwordMd5', md5($password))
->find();
if (empty($user)) {
return ResponseHelper::error('账号不存在或密码错误');
}
if($user['status'] != 1){
return ResponseHelper::error('账号已禁用');
}
//登录参数
$params = [
'grant_type' => 'password',
'username' => $user['account'],
'password' => !empty($user['passwordLocal']) ? localDecrypt($user['passwordLocal']) : $password
];
try {
// 调用登录接口获取token
$headerData = ['client:kefu-client'];
if (!empty($verifySessionId) && !empty($verifyCode)){
$headerData[] = 'verifysessionid:'.$verifySessionId;
$headerData[] = 'verifycode:'.$verifyCode;
}
$header = setHeader($headerData, '', 'plain');
$result = requestCurl('https://s2.siyuguanli.com:9991/token', $params, 'POST', $header);
$result = handleApiResponse($result);
if (isset($result['access_token']) && !empty($result['access_token'])) {
$userData['kefuData']['token'] = $result;
$headerData = ['client:kefu-client'];
$header = setHeader($headerData, $result['access_token']);
$result2 = requestCurl('https://s2.siyuguanli.com:9991/api/account/self', [], 'GET', $header, 'json');
$self = handleApiResponse($result2);
$userData['kefuData']['self'] = $self;
}else{
return ResponseHelper::error($result['error_description']);
}
unset($user['passwordMd5'],$user['deleteTime'],$user['passwordLocal']);
$userData['member'] = $user;
// 生成JWT令牌
$expired = 86400 * 30;
$token = JwtUtil::createToken($user, $expired);
$token_expired = time() + $expired;
$userData['token'] = $token;
$userData['token_expired'] = $token_expired;
return ResponseHelper::success($userData, '登录成功');
} catch (Exception $e) {
return ResponseHelper::error($e->getMessage(), $e->getCode());
}
}
}

View File

@@ -138,7 +138,7 @@ class PasswordLoginController extends BaseController
$params['typeId']
);
//同时登录客服系统
if (!empty($userData['member']['passwordLocal'])){
/* if (!empty($userData['member']['passwordLocal'])){
$params = [
'grant_type' => 'password',
'username' => $userData['member']['account'],
@@ -157,7 +157,7 @@ class PasswordLoginController extends BaseController
$self = handleApiResponse($result);
$userData['kefuData']['self'] = $self;
}
}
}*/
return ResponseHelper::success($userData, '登录成功');
} catch (Exception $e) {
return ResponseHelper::error($e->getMessage(), $e->getCode());

View File

@@ -145,12 +145,6 @@ Route::group('v1/', function () {
})->middleware(['jwt']);
// 客服登录
Route::group('v1/kefu', function () {
Route::post('login', 'app\cunkebao\controller\KeFuLoginController@index'); // 获取好友列表
});
Route::group('v1/api/scenarios', function () {

View File

@@ -1,78 +0,0 @@
<?php
namespace app\cunkebao\controller;
use app\common\controller\BaseController;
use Exception;
use library\ResponseHelper;
use app\api\controller\UserController;
/**
* 认证控制器
* 处理用户登录和身份验证
*/
class KeFuLoginController extends BaseController
{
/**
* 用户登录
*
* @return \think\response\Json
*/
public function index($username = '', $password = '',$verifySessionId = '',$verifyCode = '')
{
$username = !empty($username) ? $username : $this->request->param('username', '');
$password = !empty($password) ? $password : $this->request->param('password', '');
$verifySessionId =!empty($verifySessionId) ? $verifySessionId : $this->request->param('verifySessionId', '');
$verifyCode = !empty($verifyCode) ? $verifyCode : $this->request->param('verifyCode', '');
if (empty($username) || empty($password)) {
return ResponseHelper::error('请输入账号密码');
}
//登录参数
$params = [
'grant_type' => 'password',
'username' => $username,
'password' => $password
];
if (!empty($verifySessionId) && !empty($verifyCode)){
$params[] = 'verifysessionid:' . $verifySessionId;
$params[] = 'verifycode:' . $verifyCode;
}
//获取验证码
// $UserController = new UserController();
// $verifyCode = $UserController->getVerifyCode(true);
// $verifyCode = json_decode($verifyCode, true);
// if ($verifyCode['code'] != 200) {
// exit_data($verifyCode);
// }
try {
// 调用登录接口获取token
$headerData = ['client:kefu-client'];
$header = setHeader($headerData, '', 'plain');
$result = requestCurl('https://s2.siyuguanli.com:9991/token', $params, 'POST', $header);
$token = handleApiResponse($result);
$userData['kefuData']['token'] = $token;
if (isset($token['access_token']) && !empty($token['access_token'])) {
$headerData = ['client:kefu-client'];
$header = setHeader($headerData, $token['access_token']);
$result = requestCurl('https://s2.siyuguanli.com:9991/api/account/self', [], 'GET', $header, 'json');
$self = handleApiResponse($result);
$userData['kefuData']['self'] = $self;
}
return ResponseHelper::success($userData, '登录成功');
} catch (Exception $e) {
return ResponseHelper::error($e->getMessage(), $e->getCode());
}
}
}

View File

@@ -36,7 +36,10 @@ include __DIR__ . '/../application/superadmin/config/route.php';
// 加载CozeAI模块路由配置
include __DIR__ . '/../application/cozeai/config/route.php';
// 加载OpenAiAI模块路由配置
// 加载AI模块路由配置
include __DIR__ . '/../application/ai/config/route.php';
// 加载存客宝模块路由配置
include __DIR__ . '/../application/chukebao/config/route.php';
return [];

7634
Touchkebao/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

41
Touchkebao/src/api/ai.ts Normal file
View File

@@ -0,0 +1,41 @@
import axios from "axios";
import { useUserStore } from "@/store/module/user";
/**
* AI文本生成接口
* @param {string} content - 提示词内容
* @returns {Promise<string>} - AI生成的文本内容
*/
export async function generateAiText(content: string): Promise<string> {
try {
// 获取用户token
const { token } = useUserStore.getState();
// 获取AI接口基础URL
const apiBaseUrl = (import.meta as any).env?.VITE_API_BASE_URL || "/api";
const fullUrl = `${apiBaseUrl}/v1/ai/doubao/text`;
// 发送POST请求
const response = await axios.post(
fullUrl,
{
content,
},
{
headers: {
"Content-Type": "application/json",
Authorization: token ? `Bearer ${token}` : undefined,
},
timeout: 30000, // AI生成可能需要更长时间
}
);
// 返回生成的文本内容
// 根据接口返回格式data.choices[0].message.content
return response?.data?.data?.choices?.[0]?.message?.content || "";
} catch (error: any) {
const errorMessage =
error.response?.data?.message || error.message || "AI生成失败";
throw new Error(errorMessage);
}
}

View File

@@ -27,6 +27,7 @@ import { useCkChatStore } from "@/store/module/ckchat/ckchat";
import { useWebSocketStore } from "@/store/module/websocket/websocket";
import styles from "./Person.module.scss";
import { useWeChatStore } from "@/store/module/weChat/weChat";
import { generateAiText } from "@/api/ai";
import TwoColumnSelection from "@/components/TwoColumnSelection/TwoColumnSelection";
import TwoColumnMemberSelection from "@/components/MemberSelection/TwoColumnMemberSelection";
import { FriendSelectionItem } from "@/components/FriendSelection/data";
@@ -72,6 +73,20 @@ const Person: React.FC<PersonProps> = ({
const [isGroupNoticeModalVisible, setIsGroupNoticeModalVisible] =
useState(false);
const [confirmLoading, setConfirmLoading] = useState(false);
const [aiGenerating, setAiGenerating] = useState(false);
const [isAiModalVisible, setIsAiModalVisible] = useState(false);
const [aiPrompt, setAiPrompt] = useState(
`请为我们的微信群生成一个友好、专业的群公告。
群公告应该包含:
1. 欢迎新成员加入
2. 群聊的基本规则和礼仪
3. 鼓励大家积极交流
4. 保持群聊环境和谐
请用温馨友好的语调字数控制在200字以内。`
);
const [aiGeneratedContent, setAiGeneratedContent] = useState("");
const currentGroupMembers = useWeChatStore(
state => state.currentGroupMembers,
@@ -285,6 +300,10 @@ const Person: React.FC<PersonProps> = ({
// 处理群名称保存
const handleSaveGroupName = () => {
if (!hasGroupManagePermission()) {
messageApi.error("只有群主才能修改群名称");
return;
}
sendCommand("CmdChatroomOperate", {
wechatAccountId: contract.wechatAccountId,
wechatChatroomId: contract.id,
@@ -298,12 +317,20 @@ const Person: React.FC<PersonProps> = ({
// 点击编辑群名称按钮
const handleEditGroupName = () => {
if (!hasGroupManagePermission()) {
messageApi.error("只有群主才能修改群名称");
return;
}
setGroupNameValue(contractInfo.name || "");
setIsEditingGroupName(true);
};
// 处理群公告保存
const handleSaveGroupNotice = () => {
if (!hasGroupManagePermission()) {
messageApi.error("只有群主才能修改群公告");
return;
}
setConfirmLoading(true);
sendCommand("CmdChatroomOperate", {
wechatAccountId: contract.wechatAccountId,
@@ -320,8 +347,46 @@ const Person: React.FC<PersonProps> = ({
}, 1000);
};
// 打开AI编写弹框
const handleOpenAiModal = () => {
if (!hasGroupManagePermission()) {
messageApi.error("只有群主才能使用AI编写功能");
return;
}
setIsAiModalVisible(true);
setAiGeneratedContent(""); // 清空之前生成的内容
};
// 处理AI生成群公告
const handleAiGenerateNotice = async () => {
setAiGenerating(true);
try {
// 调用AI接口生成群公告
const aiResponse = await generateAiText(aiPrompt);
setAiGeneratedContent(aiResponse);
messageApi.success("AI生成群公告成功");
} catch (error: any) {
console.error("AI生成失败:", error);
messageApi.error(error.message || "AI生成失败请重试");
} finally {
setAiGenerating(false);
}
};
// 确认使用AI生成的内容
const handleConfirmAiContent = () => {
setGroupNoticeValue(aiGeneratedContent);
setIsAiModalVisible(false);
messageApi.success("已应用AI生成的群公告内容");
};
// 点击编辑群公告按钮
const handleEditGroupNotice = () => {
if (!hasGroupManagePermission()) {
messageApi.error("只有群主才能修改群公告");
return;
}
setGroupNoticeValue(contract.notice || "");
setIsGroupNoticeModalVisible(true);
};
@@ -585,12 +650,14 @@ const Person: React.FC<PersonProps> = ({
{contractInfo.nickname || contractInfo.name}
</h4>
</Tooltip>
<Button
type="text"
size="small"
icon={<EditOutlined />}
onClick={handleEditGroupName}
/>
{hasGroupManagePermission() && (
<Button
type="text"
size="small"
icon={<EditOutlined />}
onClick={handleEditGroupName}
/>
)}
</div>
)}
</div>
@@ -835,20 +902,20 @@ const Person: React.FC<PersonProps> = ({
</Card>
)}
{/* 个人简介或群公告 */}
<Card
title={isGroup ? "群公告" : "个人简介"}
className={styles.profileCard}
>
{isGroup ? (
// 群聊简介(原群公告)
{/* 群公告 - 仅在群聊时显示 */}
{isGroup && (
<Card
title="群公告"
className={styles.profileCard}
>
{/* 群聊简介(原群公告) */}
<div
className={styles.infoValue}
onClick={() => {
onClick={hasGroupManagePermission() ? () => {
setGroupNoticeValue(contractInfo.notice || "");
setIsGroupNoticeModalVisible(true);
}}
style={{ cursor: "pointer" }}
} : undefined}
style={{ cursor: hasGroupManagePermission() ? "pointer" : "default" }}
>
<div
style={{
@@ -858,29 +925,31 @@ const Person: React.FC<PersonProps> = ({
}}
>
<div
className={styles.bioText}
style={{
maxHeight: "120px",
overflowY: "auto",
paddingRight: "5px",
flex: 1,
}}
>
{contractInfo.notice || "点击添加群公告"}
</div>
<Button
type="text"
size="small"
icon={<EditOutlined />}
style={{ marginLeft: "8px" }}
/>
className={styles.bioText}
style={{
maxHeight: "120px",
overflowY: "auto",
paddingRight: "5px",
flex: 1,
whiteSpace: "pre-wrap",
wordBreak: "break-word",
lineHeight: "1.5",
}}
>
{contractInfo.notice || "点击添加群公告"}
</div>
{hasGroupManagePermission() && (
<Button
type="text"
size="small"
icon={<EditOutlined />}
style={{ marginLeft: "8px" }}
/>
)}
</div>
</div>
) : (
// 个人简介
<p className={styles.bioText}>{contractInfo.bio}</p>
)}
</Card>
</Card>
)}
{isGroup && (
<Card title="群成员" className={styles.profileCard}>
@@ -1103,22 +1172,136 @@ const Person: React.FC<PersonProps> = ({
>
</Button>,
hasGroupManagePermission() && (
<Button
key="ai-generate"
onClick={handleOpenAiModal}
style={{
background: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
border: "none",
color: "white",
}}
>
🤖 AI编写
</Button>
),
<Button
key="submit"
type="primary"
loading={confirmLoading}
onClick={handleSaveGroupNotice}
disabled={!hasGroupManagePermission()}
>
</Button>,
].filter(Boolean)}
>
{!hasGroupManagePermission() && (
<div style={{
marginBottom: "16px",
padding: "12px",
backgroundColor: "#fff7e6",
border: "1px solid #ffd591",
borderRadius: "6px",
color: "#d46b08"
}}>
</div>
)}
<div style={{ marginBottom: "12px" }}>
<Input.TextArea
value={groupNoticeValue}
onChange={e => setGroupNoticeValue(e.target.value)}
placeholder={hasGroupManagePermission() ? "请输入群公告内容或点击AI编写按钮自动生成" : "仅群主可以修改群公告"}
rows={8}
style={{
resize: "none",
whiteSpace: "pre-wrap",
wordBreak: "break-word",
lineHeight: "1.5"
}}
disabled={!hasGroupManagePermission()}
/>
</div>
<div style={{ fontSize: "12px", color: "#999", lineHeight: "1.4" }}>
💡 {hasGroupManagePermission() ? "AI编写功能将根据默认模板生成专业的群公告内容您可以在生成后进行个性化修改。" : "只有群主才能编辑群公告内容。"}
</div>
</Modal>
{/* AI编写群公告弹框 */}
<Modal
title="AI编写群公告"
open={isAiModalVisible}
onCancel={() => setIsAiModalVisible(false)}
width={800}
footer={[
<Button
key="cancel"
onClick={() => setIsAiModalVisible(false)}
>
</Button>,
<Button
key="generate"
type="primary"
loading={aiGenerating}
onClick={handleAiGenerateNotice}
disabled={!aiPrompt.trim()}
style={{
background: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
border: "none",
}}
>
{aiGenerating ? "生成中..." : "🤖 生成内容"}
</Button>,
<Button
key="confirm"
type="primary"
onClick={handleConfirmAiContent}
disabled={!aiGeneratedContent}
>
使
</Button>,
]}
>
<Input.TextArea
value={groupNoticeValue}
onChange={e => setGroupNoticeValue(e.target.value)}
placeholder="请输入内容"
rows={6}
/>
<div style={{ display: "flex", flexDirection: "column", gap: "16px" }}>
{/* 提示词区域 */}
<div>
<div style={{ marginBottom: "8px", fontWeight: "500" }}>
📝 AI提示词
</div>
<Input.TextArea
value={aiPrompt}
onChange={e => setAiPrompt(e.target.value)}
placeholder="请输入AI生成群公告的提示词..."
rows={6}
style={{ resize: "none" }}
/>
<div style={{ fontSize: "12px", color: "#999", marginTop: "4px" }}>
💡 AI生成更符合您需求的群公告内容
</div>
</div>
{/* 生成内容区域 */}
<div>
<div style={{ marginBottom: "8px", fontWeight: "500" }}>
🤖 AI生成的群公告
</div>
<Input.TextArea
value={aiGeneratedContent}
onChange={e => setAiGeneratedContent(e.target.value)}
placeholder={aiGenerating ? "AI正在生成中请稍候..." : "点击上方'生成内容'按钮AI将根据提示词生成群公告"}
rows={8}
style={{ resize: "none" }}
disabled={aiGenerating}
/>
{aiGeneratedContent && (
<div style={{ fontSize: "12px", color: "#52c41a", marginTop: "4px" }}>
"确认使用"
</div>
)}
</div>
</div>
</Modal>
{/* 群管理弹窗组件 */}