From 3e145ca123f415c9ac6d7386bc255f336568e882 Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Tue, 9 Dec 2025 15:01:38 +0800 Subject: [PATCH] =?UTF-8?q?=E7=AE=97=E5=8A=9B=E5=8A=9F=E8=83=BD=E6=94=B9?= =?UTF-8?q?=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cunkebao/src/pages/mobile/home/index.tsx | 6 +- Cunkebao/src/pages/mobile/mine/devices/api.ts | 4 + .../src/pages/mobile/mine/devices/index.tsx | 77 ++- .../pages/mobile/mine/recharge/index/api.ts | 14 + .../mine/recharge/index/index.module.scss | 92 ++-- .../mobile/mine/recharge/index/index.tsx | 478 +++++++++++++----- .../mobile/mine/wechat-accounts/detail/api.ts | 2 + .../mine/wechat-accounts/detail/index.tsx | 47 +- .../controller/AccountsController.php | 2 + .../chukebao/controller/AiChatController.php | 4 +- .../controller/TokensRecordController.php | 5 +- .../controller/WechatChatroomController.php | 2 +- .../common/controller/Attachment.php | 6 +- .../common/controller/PaymentService.php | 9 + Server/application/cunkebao/config/route.php | 2 + .../cunkebao/controller/StatsController.php | 4 +- .../cunkebao/controller/TokensController.php | 173 ++++++- .../controller/wechat/PostTransferFriends.php | 39 +- 18 files changed, 763 insertions(+), 203 deletions(-) diff --git a/Cunkebao/src/pages/mobile/home/index.tsx b/Cunkebao/src/pages/mobile/home/index.tsx index 9f4a65b6..6cd19c00 100644 --- a/Cunkebao/src/pages/mobile/home/index.tsx +++ b/Cunkebao/src/pages/mobile/home/index.tsx @@ -149,14 +149,14 @@ const Home: React.FC = () => {
设备数量
- {dashboard.deviceNum || 42} + {dashboard.deviceNum || 0}
微信号数量
- {dashboard.wechatNum || 42} + {dashboard.wechatNum || 0}
@@ -166,7 +166,7 @@ const Home: React.FC = () => { >
在线微信号
- {dashboard.aliveWechatNum || 35} + {dashboard.aliveWechatNum || 0}
diff --git a/Cunkebao/src/pages/mobile/mine/devices/api.ts b/Cunkebao/src/pages/mobile/mine/devices/api.ts index c8e91198..fd8a26ab 100644 --- a/Cunkebao/src/pages/mobile/mine/devices/api.ts +++ b/Cunkebao/src/pages/mobile/mine/devices/api.ts @@ -42,3 +42,7 @@ export const fetchDeviceQRCode = (accountId: string) => // 通过IMEI添加设备 export const addDeviceByImei = (imei: string, name: string) => request("/v1/api/device/add-by-imei", { imei, name }, "POST"); + +// 获取设备添加结果(用于轮询检查) +export const fetchAddResults = (params: { accountId?: string }) => + request("/v1/devices/add-results", params, "GET"); diff --git a/Cunkebao/src/pages/mobile/mine/devices/index.tsx b/Cunkebao/src/pages/mobile/mine/devices/index.tsx index f84b30c7..802821f5 100644 --- a/Cunkebao/src/pages/mobile/mine/devices/index.tsx +++ b/Cunkebao/src/pages/mobile/mine/devices/index.tsx @@ -15,6 +15,7 @@ import { fetchDeviceQRCode, addDeviceByImei, deleteDevice, + fetchAddResults, } from "./api"; import type { Device } from "@/types/device"; import { comfirm } from "@/utils/common"; @@ -44,12 +45,18 @@ const Devices: React.FC = () => { const [name, setName] = useState(""); const [addLoading, setAddLoading] = useState(false); + // 轮询监听相关 + const [isPolling, setIsPolling] = useState(false); + const pollingRef = useRef(null); + const loadDevicesRef = useRef<((reset?: boolean) => Promise) | null>(null); + // 删除弹窗 const [delVisible, setDelVisible] = useState(false); const [delLoading, setDelLoading] = useState(false); const navigate = useNavigate(); const { user } = useUserStore(); + // 加载设备列表 const loadDevices = useCallback( async (reset = false) => { @@ -74,6 +81,11 @@ const Devices: React.FC = () => { [loading, search, page], ); + // 更新 loadDevices 的 ref + useEffect(() => { + loadDevicesRef.current = loadDevices; + }, [loadDevices]); + // 首次加载和搜索 useEffect(() => { loadDevices(true); @@ -110,6 +122,56 @@ const Devices: React.FC = () => { return true; }); + // 开始轮询监听设备状态 + const startPolling = useCallback(() => { + if (isPolling) return; + + setIsPolling(true); + + const pollDeviceStatus = async () => { + try { + const res = await fetchAddResults({ accountId: user?.s2_accountId }); + if (res.added) { + Toast.show({ content: "设备添加成功!", position: "top" }); + setAddVisible(false); + setIsPolling(false); + if (pollingRef.current) { + clearInterval(pollingRef.current); + pollingRef.current = null; + } + // 刷新设备列表 + if (loadDevicesRef.current) { + await loadDevicesRef.current(true); + } + return; + } + } catch (error) { + console.error("轮询检查设备状态失败:", error); + } + }; + + // 每3秒检查一次设备状态 + pollingRef.current = setInterval(pollDeviceStatus, 3000); + }, [isPolling, user?.s2_accountId]); + + // 停止轮询 + const stopPolling = useCallback(() => { + setIsPolling(false); + if (pollingRef.current) { + clearInterval(pollingRef.current); + pollingRef.current = null; + } + }, []); + + // 组件卸载时清理轮询 + useEffect(() => { + return () => { + if (pollingRef.current) { + clearInterval(pollingRef.current); + } + }; + }, []); + // 获取二维码 const handleGetQr = async () => { setQrLoading(true); @@ -119,6 +181,8 @@ const Devices: React.FC = () => { if (!accountId) throw new Error("未获取到用户信息"); const res = await fetchDeviceQRCode(accountId); setQrCode(res.qrCode); + // 获取二维码后开始轮询监听 + startPolling(); } catch (e: any) { Toast.show({ content: e.message || "获取二维码失败", position: "top" }); } finally { @@ -362,7 +426,11 @@ const Devices: React.FC = () => { {/* 添加设备弹窗 */} setAddVisible(false)} + onMaskClick={() => { + setAddVisible(false); + stopPolling(); + setQrCode(null); + }} bodyStyle={{ borderTopLeftRadius: 16, borderTopRightRadius: 16, @@ -403,6 +471,13 @@ const Devices: React.FC = () => {
请用手机扫码添加设备
+ {isPolling && ( +
+ 正在监听设备添加状态... +
+ )}
)} diff --git a/Cunkebao/src/pages/mobile/mine/recharge/index/api.ts b/Cunkebao/src/pages/mobile/mine/recharge/index/api.ts index 055808cb..9297d518 100644 --- a/Cunkebao/src/pages/mobile/mine/recharge/index/api.ts +++ b/Cunkebao/src/pages/mobile/mine/recharge/index/api.ts @@ -173,6 +173,8 @@ export function queryOrder(orderNo: string): Promise { // 账号信息 export interface Account { id: number; + uid?: number; // 用户ID(用于分配算力) + userId?: number; // 用户ID(别名) userName: string; realName: string; nickname: string; @@ -185,3 +187,15 @@ export interface Account { export function getAccountList(): Promise<{ list: Account[]; total: number }> { return request("/v1/kefu/accounts/list", undefined, "GET"); } + +// 分配算力接口参数 +export interface AllocateTokensParams { + targetUserId: number; // 目标用户ID + tokens: number; // 分配的算力数量 + remarks?: string; // 备注 +} + +// 分配算力 +export function allocateTokens(params: AllocateTokensParams): Promise { + return request("/v1/tokens/allocate", params, "POST"); +} diff --git a/Cunkebao/src/pages/mobile/mine/recharge/index/index.module.scss b/Cunkebao/src/pages/mobile/mine/recharge/index/index.module.scss index 6cc1bc1c..efa18cf9 100644 --- a/Cunkebao/src/pages/mobile/mine/recharge/index/index.module.scss +++ b/Cunkebao/src/pages/mobile/mine/recharge/index/index.module.scss @@ -85,6 +85,10 @@ border-bottom: 1px solid #f0f0f0; padding: 0 16px; margin-bottom: 16px; + position: sticky; + top: 0; + z-index: 100; + transition: box-shadow 0.3s ease; } .navTab { @@ -218,115 +222,87 @@ margin-bottom: 12px; border-radius: 12px; padding: 16px; - border: none; - box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06); // 更柔和的阴影 - cursor: pointer; - transition: all 0.3s; - display: flex; - justify-content: space-between; - align-items: flex-start; background-color: #ffffff; + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06); + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + cursor: pointer; + transition: all 0.2s ease; } .recordItem:active { transform: scale(0.98); - box-shadow: 0 1px 4px rgba(0, 0, 0, 0.12); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); } +/* 左侧区域 */ .recordLeft { display: flex; - align-items: flex-start; + align-items: center; gap: 12px; flex: 1; min-width: 0; } .recordIconWrapper { - width: 40px; - height: 40px; + width: 44px; + height: 44px; border-radius: 50%; display: flex; align-items: center; justify-content: center; flex-shrink: 0; - margin-top: 0; // 图标顶部与标题顶部对齐 -} - -.recordIconWrapperConsume { - background-color: #fff4e6; // 浅橙色背景 -} - -.recordIconWrapperRecharge { - background-color: #f6ffed; // 浅绿色背景 } .recordIcon { - font-size: 20px; -} - -.recordIconWrapperConsume .recordIcon { - color: #ff7a00; // 橙色图标 -} - -.recordIconWrapperRecharge .recordIcon { - color: #52c41a; // 绿色图标 + font-size: 22px; } .recordInfo { flex: 1; min-width: 0; - overflow: hidden; + display: flex; + flex-direction: column; + gap: 6px; } .recordTitle { - font-size: 15px; + font-size: 16px; font-weight: 500; - margin-bottom: 4px; line-height: 1.4; - color: #333; // 深灰色标题文字 + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } .recordTime { - font-size: 13px; + font-size: 12px; color: #999; line-height: 1.4; } +/* 右侧区域 */ .recordRight { - display: flex; - align-items: flex-start; - justify-content: flex-end; - flex-shrink: 0; - padding-top: 19px; // 与时间戳对齐:标题高度(15px * 1.4) + margin-bottom(4px) ≈ 19px - margin-left: 16px; // 增加左右间距 -} - -.recordPowerWrapper { display: flex; flex-direction: column; align-items: flex-end; - text-align: right; + justify-content: center; + flex-shrink: 0; } .recordPower { - font-size: 15px; - font-weight: 500; - line-height: 1.4; // 与时间戳行高一致 -} - -.recordPowerConsume { - color: #ff7a00; // 橙色 - 扣除 -} - -.recordPowerRecharge { - color: #52c41a; // 绿色 - 增加 + font-size: 20px; + font-weight: 700; + line-height: 1.2; + margin-bottom: 4px; } .recordPowerUnit { - font-size: 13px; + font-size: 12px; color: #999; - margin-top: 2px; - line-height: 1.4; // 与时间戳行高一致 + line-height: 1.2; } .loadingContainer { diff --git a/Cunkebao/src/pages/mobile/mine/recharge/index/index.tsx b/Cunkebao/src/pages/mobile/mine/recharge/index/index.tsx index 26db1844..02f65703 100644 --- a/Cunkebao/src/pages/mobile/mine/recharge/index/index.tsx +++ b/Cunkebao/src/pages/mobile/mine/recharge/index/index.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from "react"; -import { Card, Toast, Picker } from "antd-mobile"; +import { Card, Toast, Picker, InfiniteScroll } from "antd-mobile"; import style from "./index.module.scss"; import { ThunderboltOutlined, @@ -25,7 +25,8 @@ import { import { Button, Dialog, Input, Popup } from "antd-mobile"; import NavCommon from "@/components/NavCommon"; import Layout from "@/components/Layout/Layout"; -import { getStatistics, getOrderList, queryOrder, getAccountList } from "./api"; +import { useUserStore } from "@/store"; +import { getStatistics, getOrderList, queryOrder, getAccountList, allocateTokens } from "./api"; import type { QueryOrderResponse, Account } from "./api"; import { getTokensUseRecord } from "../usage-records/api"; import { getTaocanList, buyPackage } from "../buy-power/api"; @@ -116,10 +117,20 @@ const getRecordTitle = (item: TokensUseRecordItem): string => { }; const PowerManagement: React.FC = () => { + const user = useUserStore(state => state.user); + const isAdmin = user?.isAdmin === 1; // 判断是否为管理员 const [activeTab, setActiveTab] = useState("details"); // details, buy, orders, allocation const [loading, setLoading] = useState(false); const [stats, setStats] = useState(null); const [records, setRecords] = useState([]); + const [recordPage, setRecordPage] = useState(1); + const [recordTotal, setRecordTotal] = useState(0); + const [recordHasMore, setRecordHasMore] = useState(true); + const [recordLoadingMore, setRecordLoadingMore] = useState(false); + const [orderPage, setOrderPage] = useState(1); + const [orderTotal, setOrderTotal] = useState(0); + const [orderHasMore, setOrderHasMore] = useState(true); + const [orderLoadingMore, setOrderLoadingMore] = useState(false); const [packages, setPackages] = useState([]); const [buyLoading, setBuyLoading] = useState(false); const [customAmount, setCustomAmount] = useState(""); @@ -135,7 +146,11 @@ const PowerManagement: React.FC = () => { const [allocationAccount, setAllocationAccount] = useState(null); const [allocationAmount, setAllocationAmount] = useState(""); const [allocationAccountVisible, setAllocationAccountVisible] = useState(false); - const [allocationRecords, setAllocationRecords] = useState([]); + const [allocationRecords, setAllocationRecords] = useState([]); + const [allocationPage, setAllocationPage] = useState(1); + const [allocationTotal, setAllocationTotal] = useState(0); + const [allocationHasMore, setAllocationHasMore] = useState(true); + const [allocationLoadingMore, setAllocationLoadingMore] = useState(false); const [allocationAccountFilter, setAllocationAccountFilter] = useState("all"); const [allocationTimeFilter, setAllocationTimeFilter] = useState("7days"); const [allocationAccountFilterVisible, setAllocationAccountFilterVisible] = useState(false); @@ -182,6 +197,9 @@ const PowerManagement: React.FC = () => { { label: "AI改写", value: "12" }, { label: "AI客服", value: "13" }, { label: "生成群公告", value: "14" }, + { label: "商家", value: "1001" }, + { label: "充值", value: "1002" }, + { label: "系统", value: "1003" }, ]; const actionOptions = [ @@ -197,30 +215,48 @@ const PowerManagement: React.FC = () => { { label: "全部时间", value: "all" }, ]; - // 导航标签数据 + // 导航标签数据(根据管理员权限过滤) const navTabs = [ { key: "details", label: "算力明细" }, { key: "buy", label: "购买算力" }, { key: "orders", label: "购买记录" }, - { key: "allocation", label: "算力分配" }, + // 只有管理员才能看到算力分配 + ...(isAdmin ? [{ key: "allocation", label: "算力分配" }] : []), ]; useEffect(() => { fetchStats(); }, []); + useEffect(() => { + // 如果非管理员尝试访问分配页面,自动跳转到明细页面 + if (activeTab === "allocation" && !isAdmin) { + setActiveTab("details"); + return; + } + if (activeTab === "details") { - fetchRecords(); + // 筛选条件变化时重置 + setRecordPage(1); + setRecordHasMore(true); + fetchRecords(1, false); } else if (activeTab === "buy") { fetchPackages(); } else if (activeTab === "orders") { - fetchOrders(); - } else if (activeTab === "allocation") { + // 筛选条件变化时重置 + setOrderPage(1); + setOrderHasMore(true); + fetchOrders(1, false); + } else if (activeTab === "allocation" && isAdmin) { fetchAccounts(); + // 重置分配记录 + setAllocationPage(1); + setAllocationHasMore(true); + fetchAllocationRecords(1, false); } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [activeTab, filterType, filterAction, filterTime, orderStatus, orderTime, orderPayType]); + }, [activeTab, filterType, filterAction, filterTime, orderStatus, orderTime, orderPayType, orderKeyword, isAdmin]); const fetchStats = async () => { try { @@ -232,13 +268,17 @@ const PowerManagement: React.FC = () => { } }; - const fetchRecords = async () => { - setLoading(true); + const fetchRecords = async (page: number = 1, append: boolean = false) => { + if (append) { + setRecordLoadingMore(true); + } else { + setLoading(true); + } try { // 根据筛选条件构建参数 const params: any = { - page: "1", - limit: "50", // 显示更多记录 + page: String(page), + limit: "10", type: filterAction === "consume" ? "0" : filterAction === "recharge" ? "1" : undefined, }; @@ -266,15 +306,31 @@ const PowerManagement: React.FC = () => { // 处理返回数据:request拦截器会返回 res.data.data,所以直接使用 res.list let list = Array.isArray(res?.list) ? res.list : []; - console.log("处理后的列表:", list, "列表长度:", list.length); + const total = res?.total || 0; - console.log("最终列表:", list); - setRecords(list); + console.log("处理后的列表:", list, "列表长度:", list.length, "总数:", total); + + if (append) { + // 追加数据 + setRecords(prev => [...prev, ...list]); + } else { + // 替换数据 + setRecords(list); + } + + setRecordTotal(total); + // 判断是否还有更多数据 + const hasMore = records.length + list.length < total; + setRecordHasMore(hasMore); } catch (error) { console.error("获取使用记录失败:", error); Toast.show({ content: "获取使用记录失败", position: "top" }); } finally { - setLoading(false); + if (append) { + setRecordLoadingMore(false); + } else { + setLoading(false); + } } }; @@ -291,12 +347,16 @@ const PowerManagement: React.FC = () => { } }; - const fetchOrders = async () => { - setLoading(true); + const fetchOrders = async (page: number = 1, append: boolean = false) => { + if (append) { + setOrderLoadingMore(true); + } else { + setLoading(true); + } try { const params: any = { - page: "1", - limit: "50", + page: String(page), + limit: "10", orderType: "1", // 算力充值 }; @@ -331,12 +391,34 @@ const PowerManagement: React.FC = () => { const res = await getOrderList(params); const list = res.list || []; - setOrders(list); + const total = res.total || 0; + + if (append) { + // 追加数据 + setOrders(prev => [...prev, ...list]); + } else { + // 替换数据 + setOrders(list); + } + + setOrderTotal(total); + // 判断是否还有更多数据 + if (append) { + const hasMore = orders.length + list.length < total; + setOrderHasMore(hasMore); + } else { + const hasMore = list.length < total; + setOrderHasMore(hasMore); + } } catch (error) { console.error("获取订单列表失败:", error); Toast.show({ content: "获取订单列表失败", position: "top" }); } finally { - setLoading(false); + if (append) { + setOrderLoadingMore(false); + } else { + setLoading(false); + } } }; @@ -556,14 +638,72 @@ const PowerManagement: React.FC = () => { return account.nickname || `账号${account.id}`; }; - // 获取分配记录 - const fetchAllocationRecords = async () => { - // TODO: 实现真实的分配记录接口 - setAllocationRecords([]); + // 获取分配记录(form=1001的算力明细记录) + const fetchAllocationRecords = async (page: number = 1, append: boolean = false) => { + if (append) { + setAllocationLoadingMore(true); + } else { + setLoading(true); + } + try { + const params: any = { + page: String(page), + limit: "10", + form: "1001", // 商家类型 + }; + + // 账号筛选 + if (allocationAccountFilter !== "all") { + // 这里需要根据账号ID筛选,可能需要后端支持 + // 暂时先不处理,等后端接口支持 + } + + // 时间筛选 + if (allocationTimeFilter !== "all") { + const now = new Date(); + const daysMap: Record = { + "7days": 7, + "30days": 30, + "90days": 90, + }; + const days = daysMap[allocationTimeFilter] || 7; + const startTime = new Date(now.getTime() - days * 24 * 60 * 60 * 1000); + params.startTime = startTime.toISOString().slice(0, 10); + params.endTime = now.toISOString().slice(0, 10); + } + + const res = await getTokensUseRecord(params); + let list = Array.isArray(res?.list) ? res.list : []; + const total = res?.total || 0; + + if (append) { + setAllocationRecords(prev => [...prev, ...list]); + } else { + setAllocationRecords(list); + } + + setAllocationTotal(total); + if (append) { + const hasMore = allocationRecords.length + list.length < total; + setAllocationHasMore(hasMore); + } else { + const hasMore = list.length < total; + setAllocationHasMore(hasMore); + } + } catch (error) { + console.error("获取分配记录失败:", error); + Toast.show({ content: "获取分配记录失败", position: "top" }); + } finally { + if (append) { + setAllocationLoadingMore(false); + } else { + setLoading(false); + } + } }; // 处理确认分配 - const handleConfirmAllocation = () => { + const handleConfirmAllocation = async () => { if (!allocationAccount) { Toast.show({ content: "请选择账号", position: "top" }); return; @@ -578,18 +718,38 @@ const PowerManagement: React.FC = () => { return; } - // TODO: 调用真实的分配接口 - Toast.show({ content: "分配成功", position: "top" }); - setAllocationAccount(null); - setAllocationAmount(""); - // 重新加载记录 - fetchAllocationRecords(); + setLoading(true); + try { + // 调用分配接口 + await allocateTokens({ + targetUserId: allocationAccount.uid || allocationAccount.userId || allocationAccount.id, + tokens: amount, + remarks: `分配给${getAccountDisplayName(allocationAccount)}`, + }); + + Toast.show({ content: "分配成功", position: "top" }); + setAllocationAccount(null); + setAllocationAmount(""); + + // 刷新统计数据 + fetchStats(); + + // 重新加载记录 + setAllocationPage(1); + setAllocationHasMore(true); + fetchAllocationRecords(1, false); + } catch (error) { + console.error("分配失败:", error); + Toast.show({ content: "分配失败,请重试", position: "top" }); + } finally { + setLoading(false); + } }; // 分配记录筛选选项 const allocationAccountOptions = [ { label: "全部账号", value: "all" }, - ...accounts.map(acc => ({ label: getAccountDisplayName(acc), value: acc.id.toString() })), + ...accounts.map(acc => ({ label: getAccountDisplayName(acc), value: (acc.uid || acc.userId || acc.id).toString() })), ]; const allocationTimeOptions = [ @@ -765,6 +925,30 @@ const PowerManagement: React.FC = () => { // 移除导航逻辑,只切换tab }; + // 触底加载更多 - 算力明细 + const loadMoreRecords = async () => { + if (recordLoadingMore || !recordHasMore) return; + const nextPage = recordPage + 1; + setRecordPage(nextPage); + await fetchRecords(nextPage, true); + }; + + // 触底加载更多 - 订单 + const loadMoreOrders = async () => { + if (orderLoadingMore || !orderHasMore) return; + const nextPage = orderPage + 1; + setOrderPage(nextPage); + await fetchOrders(nextPage, true); + }; + + // 触底加载更多 - 分配记录 + const loadMoreAllocationRecords = async () => { + if (allocationLoadingMore || !allocationHasMore) return; + const nextPage = allocationPage + 1; + setAllocationPage(nextPage); + await fetchAllocationRecords(nextPage, true); + }; + return ( {
加载中...
) : records.length > 0 ? ( - records.map(record => ( - -
-
- -
-
-
- {getRecordTitle(record)} + <> + {records.map(record => { + const isConsume = record.type === 0; + const powerColor = isConsume ? '#ff7a00' : '#52c41a'; + const iconBgColor = isConsume ? '#fff4e6' : '#f6ffed'; + + return ( +
+ {/* 左侧:图标 + 文字信息 */} +
+
+ +
+
+
+ {getRecordTitle(record)} +
+
+ {formatDateTime(record.createTime)} +
+
-
- {formatDateTime(record.createTime)} + {/* 右侧:算力数值 */} +
+
+ {isConsume ? '-' : '+'}{formatNumber(Math.abs(record.tokens))} +
+
算力点
-
-
-
-
- {record.type === 0 ? '-' : '+'}{formatNumber(Math.abs(record.tokens))} -
-
算力点
-
-
- - )) + ); + })} + + ) : (
暂无消耗记录
@@ -1140,7 +1349,11 @@ const PowerManagement: React.FC = () => { onChange={value => { setOrderKeyword(value); }} - onEnterPress={() => fetchOrders()} + onEnterPress={() => { + setOrderPage(1); + setOrderHasMore(true); + fetchOrders(1, false); + }} className={style.searchInput} />
@@ -1221,53 +1434,60 @@ const PowerManagement: React.FC = () => {
加载中...
) : orders.length > 0 ? ( - orders.map(order => { - const statusInfo = getOrderStatusInfo(order.status); - // tokens 已经是格式化好的字符串,如 "280,000" - const tokens = order.tokens || "0"; + <> + {orders.map(order => { + const statusInfo = getOrderStatusInfo(order.status); + // tokens 已经是格式化好的字符串,如 "280,000" + const tokens = order.tokens || "0"; - return ( - -
-
- {order.goodsName || "算力充值"} -
-
- +{tokens} -
-
-
-
-
- 订单号: {order.orderNo || "-"} + return ( + +
+
+ {order.goodsName || "算力充值"}
-
- {order.payType === 1 ? ( - - ) : order.payType === 2 ? ( - - ) : null} - {order.payType ? getPayTypeText(order.payType) : order.payTypeText || "未支付"} +
+ +{tokens}
-
-
- {order.statusText || statusInfo.text} +
+
+
+ 订单号: {order.orderNo || "-"} +
+
+ {order.payType === 1 ? ( + + ) : order.payType === 2 ? ( + + ) : null} + {order.payType ? getPayTypeText(order.payType) : order.payTypeText || "未支付"} +
-
- {formatDateTime(order.createTime)} +
+
+ {order.statusText || statusInfo.text} +
+
+ {formatDateTime(order.createTime)} +
-
- - ); - }) + + ); + })} + + ) : (
暂无订单记录
@@ -1292,12 +1512,12 @@ const PowerManagement: React.FC = () => {
({ label: getAccountDisplayName(acc), value: acc.id }))]} + columns={[accounts.map(acc => ({ label: getAccountDisplayName(acc), value: acc.uid || acc.userId || acc.id }))]} visible={allocationAccountVisible} onClose={() => setAllocationAccountVisible(false)} - value={allocationAccount ? [allocationAccount.id] : []} + value={allocationAccount ? [allocationAccount.uid || allocationAccount.userId || allocationAccount.id] : []} onConfirm={value => { - const selectedAccount = accounts.find(acc => acc.id === value[0]); + const selectedAccount = accounts.find(acc => (acc.uid || acc.userId || acc.id) === value[0]); setAllocationAccount(selectedAccount || null); setAllocationAccountVisible(false); }} @@ -1353,6 +1573,10 @@ const PowerManagement: React.FC = () => { onConfirm={value => { setAllocationAccountFilter(value[0] as string); setAllocationAccountFilterVisible(false); + // 重新加载记录 + setAllocationPage(1); + setAllocationHasMore(true); + fetchAllocationRecords(1, false); }} > {() => ( @@ -1374,6 +1598,10 @@ const PowerManagement: React.FC = () => { onConfirm={value => { setAllocationTimeFilter(value[0] as string); setAllocationTimeFilterVisible(false); + // 重新加载记录 + setAllocationPage(1); + setAllocationHasMore(true); + fetchAllocationRecords(1, false); }} > {() => ( @@ -1391,25 +1619,41 @@ const PowerManagement: React.FC = () => { {/* 记录列表 */}
- {allocationRecords.length > 0 ? ( - allocationRecords.map(record => ( -
-
- -
-
- {record.accountName} + {loading && allocationRecords.length === 0 ? ( +
+
加载中...
+
+ ) : allocationRecords.length > 0 ? ( + <> + {allocationRecords.map(record => { + // 从remarks中提取账号信息,格式可能是 "账号名 - 其他信息" 或直接是账号名 + const accountName = record.remarks || `账号${record.wechatAccountId || record.id}`; + + return ( +
+
+ +
+
+ {accountName} +
+
+ {formatDateTime(record.createTime)} +
+
-
- {record.createTime} +
+ +{formatNumber(Math.abs(record.tokens))}
-
-
- {record.amount} -
-
- )) + ); + })} + + ) : (
暂无分配记录
diff --git a/Cunkebao/src/pages/mobile/mine/wechat-accounts/detail/api.ts b/Cunkebao/src/pages/mobile/mine/wechat-accounts/detail/api.ts index 7a9a1ce3..10dfb60a 100644 --- a/Cunkebao/src/pages/mobile/mine/wechat-accounts/detail/api.ts +++ b/Cunkebao/src/pages/mobile/mine/wechat-accounts/detail/api.ts @@ -49,6 +49,8 @@ export function transferWechatFriends(params: { wechatId: string; devices: number[]; inherit: boolean; + greeting?: string; + firstMessage?: string; }) { return request("/v1/wechats/transfer-friends", params, "POST"); } diff --git a/Cunkebao/src/pages/mobile/mine/wechat-accounts/detail/index.tsx b/Cunkebao/src/pages/mobile/mine/wechat-accounts/detail/index.tsx index 333eb353..1f6e9e28 100644 --- a/Cunkebao/src/pages/mobile/mine/wechat-accounts/detail/index.tsx +++ b/Cunkebao/src/pages/mobile/mine/wechat-accounts/detail/index.tsx @@ -47,6 +47,8 @@ const WechatAccountDetail: React.FC = () => { const [showTransferConfirm, setShowTransferConfirm] = useState(false); const [selectedDevices, setSelectedDevices] = useState([]); const [inheritInfo, setInheritInfo] = useState(true); + const [greeting, setGreeting] = useState(""); + const [firstMessage, setFirstMessage] = useState(""); const [transferLoading, setTransferLoading] = useState(false); const [searchQuery, setSearchQuery] = useState(""); const [activeTab, setActiveTab] = useState("overview"); @@ -294,6 +296,10 @@ const WechatAccountDetail: React.FC = () => { const handleTransferFriends = () => { setSelectedDevices([]); setInheritInfo(true); + // 设置默认打招呼内容,使用当前微信账号昵称 + const nickname = accountInfo?.nickname || "未知"; + setGreeting(`这个是${nickname}的新号,之前那个号没用了,重新加一下您`); + setFirstMessage(""); setShowTransferConfirm(true); }; @@ -321,7 +327,9 @@ const WechatAccountDetail: React.FC = () => { await transferWechatFriends({ wechatId: id, devices: selectedDevices.map(device => device.id), - inherit: inheritInfo + inherit: inheritInfo, + greeting: greeting.trim(), + firstMessage: firstMessage.trim() }); Toast.show({ @@ -330,6 +338,7 @@ const WechatAccountDetail: React.FC = () => { }); setShowTransferConfirm(false); setSelectedDevices([]); + setFirstMessage(""); navigate("/scenarios"); } catch (error) { console.error("好友转移失败:", error); @@ -1049,6 +1058,38 @@ const WechatAccountDetail: React.FC = () => {
+ + {/* 打招呼 */} +
+
打招呼
+
+ setGreeting(e.target.value)} + rows={3} + maxLength={200} + showCount + style={{ resize: "none" }} + /> +
+
+ + {/* 通过后首次消息 */} +
+
好友通过后的首次消息
+
+ setFirstMessage(e.target.value)} + rows={3} + maxLength={200} + showCount + style={{ resize: "none" }} + /> +
+
@@ -1068,6 +1109,10 @@ const WechatAccountDetail: React.FC = () => { onClick={() => { setShowTransferConfirm(false); setSelectedDevices([]); + // 重置为默认打招呼内容 + const nickname = accountInfo?.nickname || "未知"; + setGreeting(`这个是${nickname}的新号,之前那个号没用了,重新加一下您`); + setFirstMessage(""); }} > 取消 diff --git a/Server/application/chukebao/controller/AccountsController.php b/Server/application/chukebao/controller/AccountsController.php index c69d9534..d65e998d 100644 --- a/Server/application/chukebao/controller/AccountsController.php +++ b/Server/application/chukebao/controller/AccountsController.php @@ -29,6 +29,7 @@ class AccountsController extends BaseController $query = Db::table('s2_company_account') ->alias('a') + ->join('users u', 'a.id = u.s2_accountId') ->where([ ['a.departmentId', '=', $companyId], ['a.status', '=', 0], @@ -48,6 +49,7 @@ class AccountsController extends BaseController $total = (clone $query)->count(); $list = $query->field([ 'a.id', + 'u.id as uid', 'a.userName', 'a.realName', 'a.nickname', diff --git a/Server/application/chukebao/controller/AiChatController.php b/Server/application/chukebao/controller/AiChatController.php index 4090d578..7b4dd78e 100644 --- a/Server/application/chukebao/controller/AiChatController.php +++ b/Server/application/chukebao/controller/AiChatController.php @@ -562,7 +562,7 @@ class AiChatController extends BaseController $data = [ 'tokens' => $tokenCount * 20, 'type' => 0, - 'form' => 1, + 'form' => 13, 'wechatAccountId' => $params['wechatAccountId'], 'friendIdOrGroupId' => $params['friendId'], 'remarks' => $remarks, @@ -816,7 +816,7 @@ class AiChatController extends BaseController $data = [ 'tokens' => $res['data']['token'], 'type' => 0, - 'form' => 1, + 'form' => 13, 'wechatAccountId' => $wechatAccountId, 'friendIdOrGroupId' => $friendId, 'remarks' => $remarks, diff --git a/Server/application/chukebao/controller/TokensRecordController.php b/Server/application/chukebao/controller/TokensRecordController.php index af49f99a..03b7a2ee 100644 --- a/Server/application/chukebao/controller/TokensRecordController.php +++ b/Server/application/chukebao/controller/TokensRecordController.php @@ -100,9 +100,6 @@ class TokensRecordController extends BaseController return ResponseHelper::error('类型参数错误,0为减少,1为增加'); } - if (!in_array($form, [0, 1, 2, 3, 4, 5])) { - return ResponseHelper::error('来源参数错误'); - } // 重试机制,最多重试3次 $maxRetries = 3; @@ -130,7 +127,7 @@ class TokensRecordController extends BaseController Db::startTrans(); try { // 使用悲观锁获取用户当前tokens余额,确保并发安全 - $userInfo = TokensCompany::where('companyId', $companyId)->lock(true)->find(); + $userInfo = TokensCompany::where(['companyId'=> $companyId,'userId' => $userId])->lock(true)->find(); if (!$userInfo) { throw new \Exception('用户不存在'); } diff --git a/Server/application/chukebao/controller/WechatChatroomController.php b/Server/application/chukebao/controller/WechatChatroomController.php index 8461904b..9c33baed 100644 --- a/Server/application/chukebao/controller/WechatChatroomController.php +++ b/Server/application/chukebao/controller/WechatChatroomController.php @@ -185,7 +185,7 @@ class WechatChatroomController extends BaseController $data = [ 'tokens' => $res['data']['token'], 'type' => 0, - 'form' => 3, + 'form' => 14, 'wechatAccountId' => $wechatAccountId, 'friendIdOrGroupId' => $groupId, 'remarks' => $remarks, diff --git a/Server/application/common/controller/Attachment.php b/Server/application/common/controller/Attachment.php index 39e7984f..b3ffabbf 100644 --- a/Server/application/common/controller/Attachment.php +++ b/Server/application/common/controller/Attachment.php @@ -51,7 +51,8 @@ class Attachment extends Controller 'data' => [ 'id' => $existFile['id'], 'name' => $existFile['name'], - 'url' => $existFile['source'] + 'url' => $existFile['source'], + 'size' => isset($existFile['size']) ? $existFile['size'] : 0 ] ]); } @@ -97,7 +98,8 @@ class Attachment extends Controller 'data' => [ 'id' => $attachment->id, 'name' => $attachmentData['name'], - 'url' => $attachmentData['source'] + 'url' => $attachmentData['source'], + 'size' => $attachmentData['size'] ] ]); diff --git a/Server/application/common/controller/PaymentService.php b/Server/application/common/controller/PaymentService.php index 7df8f23d..c8a030e6 100644 --- a/Server/application/common/controller/PaymentService.php +++ b/Server/application/common/controller/PaymentService.php @@ -9,6 +9,7 @@ use app\common\util\PaymentUtil; use think\facade\Env; use think\facade\Request; use app\common\model\Order; +use app\common\model\User; /** * 支付服务(内部调用) @@ -495,6 +496,13 @@ class PaymentService switch ($order['orderType']) { case 1: // 处理购买算力 + // 查询用户信息,判断是否为管理员(需要同时匹配userId和companyId) + $user = User::where([ + 'id' => $order->userId, + 'companyId' => $order->companyId + ])->find(); + $isAdmin = (!empty($user) && isset($user->isAdmin) && $user->isAdmin == 1) ? 1 : 0; + $token = TokensCompany::where(['companyId' => $order->companyId,'userId' => $order->userId])->find(); $goodsSpecs = json_decode($order->goodsSpecs, true); if (!empty($token)) { @@ -507,6 +515,7 @@ class PaymentService $tokensCompany->userId = $order->userId; $tokensCompany->companyId = $order->companyId; $tokensCompany->tokens = $goodsSpecs['tokens']; + $tokensCompany->isAdmin = $isAdmin; $tokensCompany->createTime = time(); $tokensCompany->updateTime = time(); $tokensCompany->save(); diff --git a/Server/application/cunkebao/config/route.php b/Server/application/cunkebao/config/route.php index 056667da..041d596a 100644 --- a/Server/application/cunkebao/config/route.php +++ b/Server/application/cunkebao/config/route.php @@ -130,6 +130,7 @@ Route::group('v1/', function () { Route::get('get-item-detail', 'app\cunkebao\controller\ContentLibraryController@getItemDetail'); // 获取内容库素材详情 Route::post('update-item', 'app\cunkebao\controller\ContentLibraryController@updateItem'); // 更新内容库素材 Route::any('aiEditContent', 'app\cunkebao\controller\ContentLibraryController@aiEditContent'); + Route::post('import-excel', 'app\cunkebao\controller\ContentLibraryController@importExcel'); // 导入Excel表格(支持图片) }); // 好友相关 @@ -162,6 +163,7 @@ Route::group('v1/', function () { Route::get('queryOrder', 'app\cunkebao\controller\TokensController@queryOrder'); // 查询订单(扫码付款) Route::get('orderList', 'app\cunkebao\controller\TokensController@getOrderList'); // 获取订单列表 Route::get('statistics', 'app\cunkebao\controller\TokensController@getTokensStatistics'); // 获取算力统计 + Route::post('allocate', 'app\cunkebao\controller\TokensController@allocateTokens'); // 分配token(仅管理员) }); diff --git a/Server/application/cunkebao/controller/StatsController.php b/Server/application/cunkebao/controller/StatsController.php index 3855da66..ecbd26e7 100644 --- a/Server/application/cunkebao/controller/StatsController.php +++ b/Server/application/cunkebao/controller/StatsController.php @@ -29,7 +29,7 @@ class StatsController extends Controller $where = [ ['departmentId','=',$this->request->userInfo['companyId']] ]; - if (!empty($this->request->userInfo['isAdmin'])){ + if (empty($this->request->userInfo['isAdmin'])){ $where[] = ['id','=',$this->request->userInfo['s2_accountId']]; } $accounts = Db::table('s2_company_account')->where($where)->column('id'); @@ -407,7 +407,7 @@ class StatsController extends Controller $where = [ ['departmentId','=',$companyId] ]; - if (!empty($this->request->userInfo['isAdmin'])){ + if (empty($this->request->userInfo['isAdmin'])){ $where[] = ['id','=',$this->request->userInfo['s2_accountId']]; } $accounts = Db::table('s2_company_account')->where($where)->column('id'); diff --git a/Server/application/cunkebao/controller/TokensController.php b/Server/application/cunkebao/controller/TokensController.php index 6594f577..8934c3cf 100644 --- a/Server/application/cunkebao/controller/TokensController.php +++ b/Server/application/cunkebao/controller/TokensController.php @@ -4,10 +4,12 @@ namespace app\cunkebao\controller; use app\common\controller\PaymentService; use app\common\model\Order; +use app\common\model\User; use app\cunkebao\model\TokensPackage; use app\chukebao\model\TokensCompany; use app\chukebao\model\TokensRecord; use library\ResponseHelper; +use think\Db; use think\facade\Env; class TokensController extends BaseController @@ -149,6 +151,7 @@ class TokensController extends BaseController // 构建查询条件 $where = [ + ['userId', '=', $userId], ['companyId', '=', $companyId] ]; @@ -257,13 +260,14 @@ class TokensController extends BaseController public function getTokensStatistics() { try { + $userId = $this->getUserInfo('id'); $companyId = $this->getUserInfo('companyId'); if (empty($companyId)) { return ResponseHelper::error('公司信息获取失败'); } // 获取公司算力余额 - $tokensCompany = TokensCompany::where('companyId', $companyId)->find(); + $tokensCompany = TokensCompany::where(['companyId' => $companyId,'userId' => $userId])->find(); $remainingTokens = $tokensCompany ? intval($tokensCompany->tokens) : 0; // 获取今日开始和结束时间戳 @@ -276,6 +280,7 @@ class TokensController extends BaseController // 统计今日消费(type=0表示消费) $todayUsed = TokensRecord::where([ + ['userId', '=', $userId], ['companyId', '=', $companyId], ['type', '=', 0], // 0为减少(消费) ['createTime', '>=', $todayStart], @@ -285,6 +290,7 @@ class TokensController extends BaseController // 统计本月消费 $monthUsed = TokensRecord::where([ + ['userId', '=', $userId], ['companyId', '=', $companyId], ['type', '=', 0], // 0为减少(消费) ['createTime', '>=', $monthStart], @@ -294,6 +300,7 @@ class TokensController extends BaseController // 计算总算力(当前剩余 + 历史总消费) $totalConsumed = TokensRecord::where([ + ['userId', '=', $userId], ['companyId', '=', $companyId], ['type', '=', 0] ])->sum('tokens'); @@ -301,13 +308,14 @@ class TokensController extends BaseController // 总充值算力 $totalRecharged = TokensRecord::where([ + ['userId', '=', $userId], ['companyId', '=', $companyId], ['type', '=', 1] // 1为增加(充值) ])->sum('tokens'); $totalRecharged = intval($totalRecharged); // 计算预计可用天数(基于过去一个月的平均消耗) - $estimatedDays = $this->calculateEstimatedDays($companyId, $remainingTokens); + $estimatedDays = $this->calculateEstimatedDays($userId,$companyId, $remainingTokens); return ResponseHelper::success([ 'totalTokens' => $totalRecharged, // 总算力(累计充值) @@ -325,11 +333,12 @@ class TokensController extends BaseController /** * 计算预计可用天数(基于过去一个月的平均消耗) + * @param int $userId 用户ID * @param int $companyId 公司ID * @param int $remainingTokens 当前剩余算力 * @return int 预计可用天数,-1表示无法计算(无消耗记录或余额为0) */ - private function calculateEstimatedDays($companyId, $remainingTokens) + private function calculateEstimatedDays($userId,$companyId, $remainingTokens) { // 如果余额为0或负数,无法计算 if ($remainingTokens <= 0) { @@ -340,6 +349,7 @@ class TokensController extends BaseController $oneMonthAgo = time() - (30 * 24 * 60 * 60); // 30天前的时间戳 $totalConsumed = TokensRecord::where([ + ['userId', '=', $userId], ['companyId', '=', $companyId], ['type', '=', 0], // 只统计减少的记录 ['createTime', '>=', $oneMonthAgo] @@ -365,4 +375,161 @@ class TokensController extends BaseController return $estimatedDays; } + + /** + * 分配token(仅管理员可用) + * @return \think\response\Json + */ + public function allocateTokens() + { + try { + $userId = $this->getUserInfo('id'); + $companyId = $this->getUserInfo('companyId'); + $targetUserId = (int)$this->request->param('targetUserId', 0); + $tokens = (int)$this->request->param('tokens', 0); + $remarks = $this->request->param('remarks', ''); + + // 验证参数 + if (empty($targetUserId)) { + return ResponseHelper::error('目标用户ID不能为空'); + } + + if ($tokens <= 0) { + return ResponseHelper::error('分配的token数量必须大于0'); + } + + if (empty($companyId)) { + return ResponseHelper::error('公司信息获取失败'); + } + + // 验证当前用户是否为管理员 + $currentUser = User::where([ + 'id' => $userId, + 'companyId' => $companyId + ])->find(); + + if (empty($currentUser)) { + return ResponseHelper::error('用户信息不存在'); + } + + if (empty($currentUser->isAdmin) || $currentUser->isAdmin != 1) { + return ResponseHelper::error('只有管理员才能分配token'); + } + + // 验证目标用户是否存在且属于同一公司 + $targetUser = User::where([ + 'id' => $targetUserId, + 'companyId' => $companyId + ])->find(); + + if (empty($targetUser)) { + return ResponseHelper::error('目标用户不存在或不属于同一公司'); + } + + // 检查分配者的token余额 + $allocatorTokens = TokensCompany::where([ + 'companyId' => $companyId, + 'userId' => $userId + ])->find(); + + $allocatorBalance = $allocatorTokens ? intval($allocatorTokens->tokens) : 0; + + if ($allocatorBalance < $tokens) { + return ResponseHelper::error('token余额不足,当前余额:' . $allocatorBalance); + } + + // 开始事务 + Db::startTrans(); + + try { + // 1. 减少分配者的token + if (!empty($allocatorTokens)) { + $allocatorTokens->tokens = $allocatorBalance - $tokens; + $allocatorTokens->updateTime = time(); + $allocatorTokens->save(); + $allocatorNewBalance = $allocatorTokens->tokens; + } else { + // 如果分配者没有记录,创建一条(余额为0) + $allocatorTokens = new TokensCompany(); + $allocatorTokens->userId = $userId; + $allocatorTokens->companyId = $companyId; + $allocatorTokens->tokens = 0; + $allocatorTokens->isAdmin = 1; + $allocatorTokens->createTime = time(); + $allocatorTokens->updateTime = time(); + $allocatorTokens->save(); + $allocatorNewBalance = 0; + } + + // 2. 记录分配者的减少记录 + $targetUserAccount = $targetUser->account ?? $targetUser->phone ?? '用户ID[' . $targetUserId . ']'; + $allocatorRecord = new TokensRecord(); + $allocatorRecord->companyId = $companyId; + $allocatorRecord->userId = $userId; + $allocatorRecord->type = 0; // 0为减少 + $allocatorRecord->form = 1001; // 1001表示分配 + $allocatorRecord->wechatAccountId = 0; + $allocatorRecord->friendIdOrGroupId = $targetUserId; + $allocatorRecord->remarks = !empty($remarks) ? $remarks : '分配给' . $targetUserAccount; + $allocatorRecord->tokens = $tokens; + $allocatorRecord->balanceTokens = $allocatorNewBalance; + $allocatorRecord->createTime = time(); + $allocatorRecord->save(); + + // 3. 增加接收者的token + $receiverTokens = TokensCompany::where([ + 'companyId' => $companyId, + 'userId' => $targetUserId + ])->find(); + + if (!empty($receiverTokens)) { + $receiverTokens->tokens = intval($receiverTokens->tokens) + $tokens; + $receiverTokens->updateTime = time(); + $receiverTokens->save(); + $receiverNewBalance = $receiverTokens->tokens; + } else { + // 如果接收者没有记录,创建一条 + $receiverTokens = new TokensCompany(); + $receiverTokens->userId = $targetUserId; + $receiverTokens->companyId = $companyId; + $receiverTokens->tokens = $tokens; + $receiverTokens->isAdmin = (!empty($targetUser->isAdmin) && $targetUser->isAdmin == 1) ? 1 : 0; + $receiverTokens->createTime = time(); + $receiverTokens->updateTime = time(); + $receiverTokens->save(); + $receiverNewBalance = $tokens; + } + + // 4. 记录接收者的增加记录 + $adminAccount = $currentUser->account ?? $currentUser->phone ?? '管理员'; + $receiverRecord = new TokensRecord(); + $receiverRecord->companyId = $companyId; + $receiverRecord->userId = $targetUserId; + $receiverRecord->type = 1; // 1为增加 + $receiverRecord->form = 1001; // 1001表示分配 + $receiverRecord->wechatAccountId = 0; + $receiverRecord->friendIdOrGroupId = $userId; + $receiverRecord->remarks = !empty($remarks) ? '管理员分配:' . $remarks : '管理员分配'; + $receiverRecord->tokens = $tokens; + $receiverRecord->balanceTokens = $receiverNewBalance; + $receiverRecord->createTime = time(); + $receiverRecord->save(); + + Db::commit(); + + return ResponseHelper::success([ + 'allocatorBalance' => $allocatorNewBalance, + 'receiverBalance' => $receiverNewBalance, + 'allocatedTokens' => $tokens + ], '分配成功'); + + } catch (\Exception $e) { + Db::rollback(); + return ResponseHelper::error('分配失败:' . $e->getMessage()); + } + + } catch (\Exception $e) { + return ResponseHelper::error('分配失败:' . $e->getMessage()); + } + } } \ No newline at end of file diff --git a/Server/application/cunkebao/controller/wechat/PostTransferFriends.php b/Server/application/cunkebao/controller/wechat/PostTransferFriends.php index 6bb0007a..99ef4d7b 100644 --- a/Server/application/cunkebao/controller/wechat/PostTransferFriends.php +++ b/Server/application/cunkebao/controller/wechat/PostTransferFriends.php @@ -13,9 +13,12 @@ class PostTransferFriends extends BaseController { $wechatId = $this->request->param('wechatId', ''); $inherit = $this->request->param('inherit', ''); + $greeting = $this->request->param('greeting', ''); + $firstMessage = $this->request->param('firstMessage', ''); $devices = $this->request->param('devices', []); $companyId = $this->getUserInfo('companyId'); + if (empty($wechatId)){ return ResponseHelper::error('迁移的微信不能为空'); } @@ -23,13 +26,16 @@ class PostTransferFriends extends BaseController if (empty($devices)){ return ResponseHelper::error('迁移的设备不能为空'); } + if (empty($greeting)){ + return ResponseHelper::error('打招呼不能为空'); + } if (!is_array($devices)){ return ResponseHelper::error('迁移的设备必须为数组'); } $wechat = Db::name('wechat_customer')->alias('wc') ->join('wechat_account wa', 'wc.wechatId = wa.wechatId') - ->where(['wc.wechatId' => $wechatId, 'wc.companyId' => $companyId]) + ->where(['wc.wechatId' => $wechatId]) ->field('wa.*') ->find(); @@ -53,11 +59,6 @@ class PostTransferFriends extends BaseController 'id' => 'poster-3', 'name' => '点击咨询', 'src' => 'https://hebbkx1anhila5yf.public.blob.vercel-storage.com/%E7%82%B9%E5%87%BB%E5%92%A8%E8%AF%A2-FTiyAMAPop2g9LvjLOLDz0VwPg3KVu.gif' - ], - '$posters' => [ - 'id' => 'poster-3', - 'name' => '点击咨询', - 'src' => 'https://hebbkx1anhila5yf.public.blob.vercel-storage.com/%E7%82%B9%E5%87%BB%E5%92%A8%E8%AF%A2-FTiyAMAPop2g9LvjLOLDz0VwPg3KVu.gif' ] ]; $reqConf = [ @@ -66,18 +67,38 @@ class PostTransferFriends extends BaseController 'endTime' => '18:00', 'remarkType' => 'phone', 'addFriendInterval' => 60, - 'greeting' => '您好,我是'. $wechat['nickname'] .'的辅助客服,请通过' + 'greeting' => !empty($greeting) ? $greeting :'这个是'. $wechat['nickname'] .'的新号,之前那个号没用了,重新加一下您' ]; + if (!empty($firstMessage)){ + $msgConf = [ + [ + 'day' => 0, + 'messages' => [ + [ + 'id' => 1, + 'type' => 'text', + 'content' => $firstMessage, + 'intervalUnit' => 'seconds', + 'sendInterval' => 5, + ] + ] + ] + ]; + }else{ + $msgConf = []; + } + // 使用容器获取控制器实例,而不是直接实例化 $createAddFriendPlan = app('app\cunkebao\controller\plan\PostCreateAddFriendPlanV1Controller'); $taskId = Db::name('customer_acquisition_task')->insertGetId([ 'name' => '迁移好友('. $wechat['nickname'] .')', 'sceneId' => 10, - 'sceneConf' => json_encode($sceneConf), - 'reqConf' => json_encode($reqConf), + 'sceneConf' => json_encode($sceneConf,256), + 'reqConf' => json_encode($reqConf,256), 'tagConf' => json_encode([]), + 'msgConf' => json_encode($msgConf,256), 'userId' => $this->getUserInfo('id'), 'companyId' => $companyId, 'status' => 0,