算力功能
This commit is contained in:
@@ -121,13 +121,13 @@ const SelectionPopup: React.FC<SelectionPopupProps> = ({
|
||||
}
|
||||
} else {
|
||||
// 多选模式:原有的逻辑
|
||||
if (tempSelectedOptions.some(v => v.id === device.id)) {
|
||||
setTempSelectedOptions(
|
||||
tempSelectedOptions.filter(v => v.id !== device.id),
|
||||
);
|
||||
} else {
|
||||
const newSelectedOptions = [...tempSelectedOptions, device];
|
||||
setTempSelectedOptions(newSelectedOptions);
|
||||
if (tempSelectedOptions.some(v => v.id === device.id)) {
|
||||
setTempSelectedOptions(
|
||||
tempSelectedOptions.filter(v => v.id !== device.id),
|
||||
);
|
||||
} else {
|
||||
const newSelectedOptions = [...tempSelectedOptions, device];
|
||||
setTempSelectedOptions(newSelectedOptions);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -34,15 +34,15 @@ const PopupFooter: React.FC<PopupFooterProps> = ({
|
||||
{/* 分页栏 */}
|
||||
<div className={style.paginationRow}>
|
||||
{onSelectAll && (
|
||||
<div className={style.totalCount}>
|
||||
<Checkbox
|
||||
checked={isAllSelected}
|
||||
onChange={e => onSelectAll(e.target.checked)}
|
||||
className={style.selectAllCheckbox}
|
||||
>
|
||||
全选当前页
|
||||
</Checkbox>
|
||||
</div>
|
||||
<div className={style.totalCount}>
|
||||
<Checkbox
|
||||
checked={isAllSelected}
|
||||
onChange={e => onSelectAll(e.target.checked)}
|
||||
className={style.selectAllCheckbox}
|
||||
>
|
||||
全选当前页
|
||||
</Checkbox>
|
||||
</div>
|
||||
)}
|
||||
<div className={style.paginationControls}>
|
||||
<Button
|
||||
|
||||
@@ -106,7 +106,7 @@ export default function ContentForm() {
|
||||
setUseAI(data.aiEnabled === 1);
|
||||
} else {
|
||||
// 兼容旧数据,默认根据是否有 aiPrompt 判断
|
||||
setUseAI(!!data.aiPrompt);
|
||||
setUseAI(!!data.aiPrompt);
|
||||
}
|
||||
setEnabled(data.status === 1);
|
||||
// 时间范围
|
||||
@@ -151,7 +151,7 @@ export default function ContentForm() {
|
||||
.map(s => s.trim())
|
||||
.filter(Boolean),
|
||||
catchType,
|
||||
aiPrompt,
|
||||
aiPrompt,
|
||||
aiEnabled: useAI ? 1 : 0,
|
||||
timeEnabled: dateRange[0] || dateRange[1] ? 1 : 0,
|
||||
startTime: dateRange[0] ? formatDate(dateRange[0]) : "",
|
||||
@@ -347,37 +347,37 @@ export default function ContentForm() {
|
||||
|
||||
<div className={style["form-card"]}>
|
||||
<Collapse className={style["keyword-collapse"]}>
|
||||
<Collapse.Panel
|
||||
key="keywords"
|
||||
<Collapse.Panel
|
||||
key="keywords"
|
||||
title={
|
||||
<div className={style["keyword-header"]}>
|
||||
<span className={style["keyword-title"]}>关键词设置</span>
|
||||
<DownOutlined className={style["keyword-arrow"]} />
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div className={style["form-section"]}>
|
||||
<label className={style["form-label"]}>包含关键词</label>
|
||||
<TextArea
|
||||
placeholder="多个关键词用逗号分隔"
|
||||
value={keywordsInclude}
|
||||
onChange={e => setKeywordsInclude(e.target.value)}
|
||||
className={style["input"]}
|
||||
autoSize={{ minRows: 2, maxRows: 4 }}
|
||||
/>
|
||||
</div>
|
||||
<div className={style["form-section"]}>
|
||||
<label className={style["form-label"]}>排除关键词</label>
|
||||
<TextArea
|
||||
placeholder="多个关键词用逗号分隔"
|
||||
value={keywordsExclude}
|
||||
onChange={e => setKeywordsExclude(e.target.value)}
|
||||
className={style["input"]}
|
||||
autoSize={{ minRows: 2, maxRows: 4 }}
|
||||
/>
|
||||
</div>
|
||||
</Collapse.Panel>
|
||||
</Collapse>
|
||||
>
|
||||
<div className={style["form-section"]}>
|
||||
<label className={style["form-label"]}>包含关键词</label>
|
||||
<TextArea
|
||||
placeholder="多个关键词用逗号分隔"
|
||||
value={keywordsInclude}
|
||||
onChange={e => setKeywordsInclude(e.target.value)}
|
||||
className={style["input"]}
|
||||
autoSize={{ minRows: 2, maxRows: 4 }}
|
||||
/>
|
||||
</div>
|
||||
<div className={style["form-section"]}>
|
||||
<label className={style["form-label"]}>排除关键词</label>
|
||||
<TextArea
|
||||
placeholder="多个关键词用逗号分隔"
|
||||
value={keywordsExclude}
|
||||
onChange={e => setKeywordsExclude(e.target.value)}
|
||||
className={style["input"]}
|
||||
autoSize={{ minRows: 2, maxRows: 4 }}
|
||||
/>
|
||||
</div>
|
||||
</Collapse.Panel>
|
||||
</Collapse>
|
||||
</div>
|
||||
|
||||
{/* 采集内容类型 */}
|
||||
@@ -400,38 +400,38 @@ export default function ContentForm() {
|
||||
);
|
||||
}}
|
||||
>
|
||||
{type === "text"
|
||||
? "文本"
|
||||
: type === "image"
|
||||
? "图片"
|
||||
: "视频"}
|
||||
{type === "text"
|
||||
? "文本"
|
||||
: type === "image"
|
||||
? "图片"
|
||||
: "视频"}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={style["form-card"]}>
|
||||
<div
|
||||
className={style["form-section"]}
|
||||
style={{ display: "flex", alignItems: "center", gap: 12 }}
|
||||
>
|
||||
<Switch checked={useAI} onChange={setUseAI} />
|
||||
<span className={style["ai-desc"]}>
|
||||
<div
|
||||
className={style["form-section"]}
|
||||
style={{ display: "flex", alignItems: "center", gap: 12 }}
|
||||
>
|
||||
<Switch checked={useAI} onChange={setUseAI} />
|
||||
<span className={style["ai-desc"]}>
|
||||
启用后,该内容库下的内容会通过AI生成
|
||||
</span>
|
||||
</div>
|
||||
{useAI && (
|
||||
<div className={style["form-section"]}>
|
||||
<label className={style["form-label"]}>AI提示词</label>
|
||||
<TextArea
|
||||
</span>
|
||||
</div>
|
||||
{useAI && (
|
||||
<div className={style["form-section"]}>
|
||||
<label className={style["form-label"]}>AI提示词</label>
|
||||
<TextArea
|
||||
placeholder="请输入AI提示词"
|
||||
value={aiPrompt}
|
||||
onChange={e => setAIPrompt(e.target.value)}
|
||||
className={style["input"]}
|
||||
autoSize={{ minRows: 4, maxRows: 10 }}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className={style["form-card"]}>
|
||||
@@ -441,51 +441,51 @@ export default function ContentForm() {
|
||||
<div className={style["date-inputs"]}>
|
||||
<div className={style["date-item"]}>
|
||||
<label className={style["date-label"]}>开始时间</label>
|
||||
<AntdInput
|
||||
readOnly
|
||||
<AntdInput
|
||||
readOnly
|
||||
value={
|
||||
dateRange[0]
|
||||
? `${dateRange[0].getFullYear()}/${String(dateRange[0].getMonth() + 1).padStart(2, "0")}/${String(dateRange[0].getDate()).padStart(2, "0")}`
|
||||
: ""
|
||||
}
|
||||
placeholder="年/月/日"
|
||||
placeholder="年/月/日"
|
||||
className={style["date-input"]}
|
||||
onClick={() => setShowStartPicker(true)}
|
||||
/>
|
||||
<DatePicker
|
||||
visible={showStartPicker}
|
||||
title="开始时间"
|
||||
value={dateRange[0]}
|
||||
onClose={() => setShowStartPicker(false)}
|
||||
onConfirm={val => {
|
||||
setDateRange([val, dateRange[1]]);
|
||||
setShowStartPicker(false);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
onClick={() => setShowStartPicker(true)}
|
||||
/>
|
||||
<DatePicker
|
||||
visible={showStartPicker}
|
||||
title="开始时间"
|
||||
value={dateRange[0]}
|
||||
onClose={() => setShowStartPicker(false)}
|
||||
onConfirm={val => {
|
||||
setDateRange([val, dateRange[1]]);
|
||||
setShowStartPicker(false);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className={style["date-item"]}>
|
||||
<label className={style["date-label"]}>结束时间</label>
|
||||
<AntdInput
|
||||
readOnly
|
||||
<AntdInput
|
||||
readOnly
|
||||
value={
|
||||
dateRange[1]
|
||||
? `${dateRange[1].getFullYear()}/${String(dateRange[1].getMonth() + 1).padStart(2, "0")}/${String(dateRange[1].getDate()).padStart(2, "0")}`
|
||||
: ""
|
||||
}
|
||||
placeholder="年/月/日"
|
||||
placeholder="年/月/日"
|
||||
className={style["date-input"]}
|
||||
onClick={() => setShowEndPicker(true)}
|
||||
/>
|
||||
<DatePicker
|
||||
visible={showEndPicker}
|
||||
title="结束时间"
|
||||
value={dateRange[1]}
|
||||
onClose={() => setShowEndPicker(false)}
|
||||
onConfirm={val => {
|
||||
setDateRange([dateRange[0], val]);
|
||||
setShowEndPicker(false);
|
||||
}}
|
||||
/>
|
||||
onClick={() => setShowEndPicker(true)}
|
||||
/>
|
||||
<DatePicker
|
||||
visible={showEndPicker}
|
||||
title="结束时间"
|
||||
value={dateRange[1]}
|
||||
onClose={() => setShowEndPicker(false)}
|
||||
onConfirm={val => {
|
||||
setDateRange([dateRange[0], val]);
|
||||
setShowEndPicker(false);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -493,7 +493,7 @@ export default function ContentForm() {
|
||||
<div className={style["form-card"]}>
|
||||
<div className={style["enable-section"]}>
|
||||
<span className={style["enable-label"]}>是否启用</span>
|
||||
<Switch checked={enabled} onChange={setEnabled} />
|
||||
<Switch checked={enabled} onChange={setEnabled} />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -4,7 +4,7 @@ import request from "@/api/request";
|
||||
export interface PowerPackage {
|
||||
id: number;
|
||||
name: string;
|
||||
tokens: number; // 算力点数
|
||||
tokens: number | string; // 算力点数(可能是字符串,如"2,800")
|
||||
price: number; // 价格(分)
|
||||
originalPrice: number; // 原价(分)
|
||||
unitPrice: number; // 单价
|
||||
@@ -13,7 +13,7 @@ export interface PowerPackage {
|
||||
isRecommend: number; // 是否推荐
|
||||
isHot: number; // 是否热门
|
||||
isVip: number; // 是否VIP
|
||||
features: string[]; // 功能特性
|
||||
features?: string[]; // 功能特性(可选)
|
||||
description: string[]; // 描述关键词
|
||||
status: number;
|
||||
createTime: string;
|
||||
|
||||
@@ -6,6 +6,9 @@ export interface Statistics {
|
||||
monthUsed: number; // 本月使用
|
||||
remainingTokens: number; // 剩余算力
|
||||
totalConsumed: number; // 总消耗
|
||||
yesterdayUsed?: number; // 昨日消耗
|
||||
historyConsumed?: number; // 历史消耗
|
||||
estimatedDays?: number; // 预计可用天数
|
||||
}
|
||||
// 算力统计接口
|
||||
export function getStatistics(): Promise<Statistics> {
|
||||
@@ -143,3 +146,42 @@ export function buyPackage(params: { id: number; price: number }) {
|
||||
export function buyCustomPower(params: { amount: number }) {
|
||||
return request("/v1/power/buy-custom", params, "POST");
|
||||
}
|
||||
|
||||
// 查询订单状态
|
||||
export interface QueryOrderResponse {
|
||||
id: number;
|
||||
mchId: number;
|
||||
companyId: number;
|
||||
userId: number;
|
||||
orderType: number;
|
||||
status: number; // 0: 待支付, 1: 已支付
|
||||
goodsId: number;
|
||||
goodsName: string;
|
||||
goodsSpecs: string;
|
||||
money: number;
|
||||
orderNo: string;
|
||||
payType: number | null;
|
||||
payTime: number | null;
|
||||
payInfo: any;
|
||||
createTime: number;
|
||||
}
|
||||
|
||||
export function queryOrder(orderNo: string): Promise<QueryOrderResponse> {
|
||||
return request("/v1/tokens/queryOrder", { orderNo }, "GET");
|
||||
}
|
||||
|
||||
// 账号信息
|
||||
export interface Account {
|
||||
id: number;
|
||||
userName: string;
|
||||
realName: string;
|
||||
nickname: string;
|
||||
departmentId: number;
|
||||
departmentName: string;
|
||||
avatar: string;
|
||||
}
|
||||
|
||||
// 获取账号列表
|
||||
export function getAccountList(): Promise<{ list: Account[]; total: number }> {
|
||||
return request("/v1/kefu/accounts/list", undefined, "GET");
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -36,6 +36,7 @@ export interface TokensUseRecordItem {
|
||||
|
||||
export interface TokensUseRecordList {
|
||||
list: TokensUseRecordItem[];
|
||||
total?: number;
|
||||
}
|
||||
|
||||
//算力使用明细
|
||||
|
||||
@@ -339,8 +339,8 @@
|
||||
height: 5px;
|
||||
border-radius: 10px 10px 0 0;
|
||||
background: #1677ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.stat-icon-chat {
|
||||
width: 20px;
|
||||
@@ -700,7 +700,7 @@
|
||||
.adm-avatar {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 50%;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -710,13 +710,13 @@
|
||||
}
|
||||
|
||||
.friend-name-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 4px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.friend-name {
|
||||
.friend-name {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #111;
|
||||
@@ -727,7 +727,7 @@
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.friend-tag {
|
||||
font-size: 11px;
|
||||
@@ -735,17 +735,17 @@
|
||||
border-radius: 999px;
|
||||
background: #f5f5f5;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
.friend-id-row {
|
||||
font-size: 12px;
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.friend-status-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
@@ -764,7 +764,7 @@
|
||||
font-size: 11px;
|
||||
color: #999;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.value-amount {
|
||||
font-size: 14px;
|
||||
|
||||
@@ -370,13 +370,13 @@ const ScenarioList: React.FC = () => {
|
||||
title={scenarioName || ""}
|
||||
right={
|
||||
scenarioId !== "10" ? (
|
||||
<Button
|
||||
size="small"
|
||||
color="primary"
|
||||
onClick={handleCreateNewPlan}
|
||||
>
|
||||
<PlusOutlined /> 新建计划
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
color="primary"
|
||||
onClick={handleCreateNewPlan}
|
||||
>
|
||||
<PlusOutlined /> 新建计划
|
||||
</Button>
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
@@ -427,13 +427,13 @@ const ScenarioList: React.FC = () => {
|
||||
{searchTerm ? "没有找到匹配的计划" : "暂无计划"}
|
||||
</div>
|
||||
{scenarioId !== "10" && (
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={handleCreateNewPlan}
|
||||
className={style["create-first-btn"]}
|
||||
>
|
||||
<PlusOutlined /> 创建第一个计划
|
||||
</Button>
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={handleCreateNewPlan}
|
||||
className={style["create-first-btn"]}
|
||||
>
|
||||
<PlusOutlined /> 创建第一个计划
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
|
||||
@@ -16,6 +16,8 @@ class TokensRecordController extends BaseController
|
||||
$limit = $this->request->param('limit', 10);
|
||||
$type = $this->request->param('type', '');
|
||||
$form = $this->request->param('form', '');
|
||||
$startTime = $this->request->param('startTime', '');
|
||||
$endTime = $this->request->param('endTime', '');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
@@ -32,6 +34,26 @@ class TokensRecordController extends BaseController
|
||||
$where[] = ['form','=',$form];
|
||||
}
|
||||
|
||||
// 时间筛选
|
||||
if (!empty($startTime)) {
|
||||
// 支持时间戳或日期字符串格式
|
||||
$startTimestamp = is_numeric($startTime) ? intval($startTime) : strtotime($startTime);
|
||||
if ($startTimestamp !== false) {
|
||||
$where[] = ['createTime', '>=', $startTimestamp];
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($endTime)) {
|
||||
// 支持时间戳或日期字符串格式
|
||||
$endTimestamp = is_numeric($endTime) ? intval($endTime) : strtotime($endTime);
|
||||
if ($endTimestamp !== false) {
|
||||
// 如果是日期字符串,自动设置为当天的23:59:59
|
||||
if (!is_numeric($endTime)) {
|
||||
$endTimestamp = strtotime(date('Y-m-d 23:59:59', $endTimestamp));
|
||||
}
|
||||
$where[] = ['createTime', '<=', $endTimestamp];
|
||||
}
|
||||
}
|
||||
|
||||
$query = TokensRecord::where($where);
|
||||
$total = $query->count();
|
||||
|
||||
@@ -495,7 +495,7 @@ class PaymentService
|
||||
switch ($order['orderType']) {
|
||||
case 1:
|
||||
// 处理购买算力
|
||||
$token = TokensCompany::where(['companyId' => $order->companyId])->find();
|
||||
$token = TokensCompany::where(['companyId' => $order->companyId,'userId' => $order->userId])->find();
|
||||
$goodsSpecs = json_decode($order->goodsSpecs, true);
|
||||
if (!empty($token)) {
|
||||
$token->tokens = $token->tokens + $goodsSpecs['tokens'];
|
||||
@@ -504,6 +504,7 @@ class PaymentService
|
||||
$newTokens = $token->tokens;
|
||||
} else {
|
||||
$tokensCompany = new TokensCompany();
|
||||
$tokensCompany->userId = $order->userId;
|
||||
$tokensCompany->companyId = $order->companyId;
|
||||
$tokensCompany->tokens = $goodsSpecs['tokens'];
|
||||
$tokensCompany->createTime = time();
|
||||
|
||||
@@ -72,7 +72,7 @@ class TokensController extends BaseController
|
||||
|
||||
} else {
|
||||
//获取配置的tokens比例
|
||||
$tokens_multiple = Env::get('payment.tokens_multiple', 28);
|
||||
$tokens_multiple = Env::get('payment.tokens_multiple', 20);
|
||||
$specs = [
|
||||
'id' => 0,
|
||||
'name' => '自定义购买算力',
|
||||
@@ -119,7 +119,7 @@ class TokensController extends BaseController
|
||||
return ResponseHelper::success($order, '订单已支付');
|
||||
} else {
|
||||
$errorMsg = !empty($order['payInfo']) ? $order['payInfo'] : '订单未支付';
|
||||
return ResponseHelper::success($order,$errorMsg,400);
|
||||
return ResponseHelper::success($order,$errorMsg);
|
||||
}
|
||||
} else {
|
||||
return ResponseHelper::success($order, '订单已支付');
|
||||
@@ -140,6 +140,7 @@ class TokensController extends BaseController
|
||||
$status = $this->request->param('status', ''); // 订单状态筛选
|
||||
$keyword = $this->request->param('keyword', ''); // 关键词搜索(订单号)
|
||||
$orderType = $this->request->param('orderType', ''); // 订单类型筛选
|
||||
$payType = $this->request->param('payType', ''); // 支付类型筛选
|
||||
$startTime = $this->request->param('startTime', ''); // 开始时间
|
||||
$endTime = $this->request->param('endTime', ''); // 结束时间
|
||||
|
||||
@@ -166,6 +167,11 @@ class TokensController extends BaseController
|
||||
$where[] = ['orderType', '=', $orderType];
|
||||
}
|
||||
|
||||
// 支付类型筛选
|
||||
if($payType !== '') {
|
||||
$where[] = ['payType', '=', $payType];
|
||||
}
|
||||
|
||||
// 时间范围筛选
|
||||
if (!empty($startTime)) {
|
||||
$where[] = ['createTime', '>=', strtotime($startTime)];
|
||||
@@ -300,16 +306,63 @@ class TokensController extends BaseController
|
||||
])->sum('tokens');
|
||||
$totalRecharged = intval($totalRecharged);
|
||||
|
||||
// 计算预计可用天数(基于过去一个月的平均消耗)
|
||||
$estimatedDays = $this->calculateEstimatedDays($companyId, $remainingTokens);
|
||||
|
||||
return ResponseHelper::success([
|
||||
'totalTokens' => $totalRecharged, // 总算力(累计充值)
|
||||
'todayUsed' => $todayUsed, // 今日使用
|
||||
'monthUsed' => $monthUsed, // 本月使用
|
||||
'remainingTokens' => $remainingTokens, // 剩余算力
|
||||
'totalConsumed' => $totalConsumed, // 累计消费
|
||||
'estimatedDays' => $estimatedDays, // 预计可用天数
|
||||
], '获取成功');
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return ResponseHelper::error('获取算力统计失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算预计可用天数(基于过去一个月的平均消耗)
|
||||
* @param int $companyId 公司ID
|
||||
* @param int $remainingTokens 当前剩余算力
|
||||
* @return int 预计可用天数,-1表示无法计算(无消耗记录或余额为0)
|
||||
*/
|
||||
private function calculateEstimatedDays($companyId, $remainingTokens)
|
||||
{
|
||||
// 如果余额为0或负数,无法计算
|
||||
if ($remainingTokens <= 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 计算过去30天的消耗总量(只统计减少的记录,type=0)
|
||||
$oneMonthAgo = time() - (30 * 24 * 60 * 60); // 30天前的时间戳
|
||||
|
||||
$totalConsumed = TokensRecord::where([
|
||||
['companyId', '=', $companyId],
|
||||
['type', '=', 0], // 只统计减少的记录
|
||||
['createTime', '>=', $oneMonthAgo]
|
||||
])->sum('tokens');
|
||||
|
||||
$totalConsumed = intval($totalConsumed);
|
||||
|
||||
// 如果过去30天没有消耗记录,无法计算
|
||||
if ($totalConsumed <= 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 计算平均每天消耗量
|
||||
$avgDailyConsumption = $totalConsumed / 30;
|
||||
|
||||
// 如果平均每天消耗为0,无法计算
|
||||
if ($avgDailyConsumption <= 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 计算预计可用天数 = 当前余额 / 平均每天消耗量
|
||||
$estimatedDays = floor($remainingTokens / $avgDailyConsumption);
|
||||
|
||||
return $estimatedDays;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user