Files
MBTI_wang/api/app/controller/superadmin/Finance.php
2026-03-17 11:53:41 +08:00

338 lines
13 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace app\controller\superadmin;
use app\BaseController;
use think\facade\Request;
use think\facade\Db;
/**
* 财务管理控制器(超管专用)
* 数据来源mbti_orders金额单位
*/
class Finance extends BaseController
{
private const PAID_STATUS = ['paid', 'completed'];
/**
* 获取财务概览
* 金额单位:分
*/
public function overview()
{
$user = $this->request->user ?? null;
if (!$user || ($user['role'] ?? '') !== 'superadmin') {
return error('无权限访问', 403);
}
try {
$currentMonthStart = mktime(0, 0, 0, (int) date('n'), 1, (int) date('Y'));
$currentMonthEnd = mktime(23, 59, 59, (int) date('n'), (int) date('t'), (int) date('Y'));
$basePaid = Db::name('orders')->whereIn('status', self::PAID_STATUS);
$totalRevenue = (int) ((clone $basePaid)->sum('amount') ?? 0);
$paidOrderCount = (int) ((clone $basePaid)->count());
$monthRevenue = (int) (Db::name('orders')
->whereIn('status', self::PAID_STATUS)
->where('payTime', '>=', $currentMonthStart)
->where('payTime', '<=', $currentMonthEnd)
->sum('amount') ?? 0);
// 成本:无成本表时按收入比例估算(约 30%
$totalCost = (int) round($totalRevenue * 0.3);
$monthCost = (int) round($monthRevenue * 0.3);
$netProfit = $totalRevenue - $totalCost;
$monthProfit = $monthRevenue - $monthCost;
$profitRate = $totalRevenue > 0 ? round(($netProfit / $totalRevenue) * 100, 1) : 0;
$lastMonthStart = mktime(0, 0, 0, (int) date('n') - 1, 1, (int) date('Y'));
$lastMonthEnd = mktime(23, 59, 59, (int) date('n') - 1, (int) date('t', $lastMonthStart), (int) date('Y'));
$lastMonthRevenue = (int) (Db::name('orders')
->whereIn('status', self::PAID_STATUS)
->where('payTime', '>=', $lastMonthStart)
->where('payTime', '<=', $lastMonthEnd)
->sum('amount') ?? 0);
$lastMonthCost = (int) round($lastMonthRevenue * 0.3);
$lastMonthProfit = $lastMonthRevenue - $lastMonthCost;
$monthGrowth = $lastMonthProfit > 0
? round(($monthProfit - $lastMonthProfit) / $lastMonthProfit * 100, 1)
: ($monthProfit > 0 ? 100 : 0);
return success([
'totalRevenue' => $totalRevenue,
'totalCost' => $totalCost,
'netProfit' => $netProfit,
'profitRate' => $profitRate,
'monthRevenue' => $monthRevenue,
'monthCost' => $monthCost,
'monthProfit' => $monthProfit,
'monthGrowth' => $monthGrowth,
'paidOrderCount' => $paidOrderCount,
]);
} catch (\Throwable $e) {
return error('获取财务概览失败:' . $e->getMessage(), 500);
}
}
/**
* 收入明细:按产品类型汇总(已支付订单),金额单位:分
*/
public function revenueDetails()
{
$user = $this->request->user ?? null;
if (!$user || ($user['role'] ?? '') !== 'superadmin') {
return error('无权限访问', 403);
}
try {
$rows = Db::name('orders')
->whereIn('status', self::PAID_STATUS)
->field('productType, SUM(amount) as total')
->group('productType')
->select()
->toArray();
$typeLabel = [
'face' => 'AI人脸分析',
'mbti' => 'MBTI',
'disc' => 'DISC',
'pdp' => 'PDP',
'resume' => '简历综合分析',
'report' => '完整报告',
];
$totalSum = 0;
$byType = [];
foreach ($rows as $r) {
$type = $r['productType'] ?? 'other';
$amount = (int) ($r['total'] ?? 0);
$totalSum += $amount;
$byType[$type] = $amount;
}
$details = [];
foreach ($typeLabel as $key => $label) {
$amount = $byType[$key] ?? 0;
$details[] = [
'type' => $label,
'amount' => $amount,
'percent' => $totalSum > 0 ? round($amount / $totalSum * 100, 1) : 0,
];
}
$otherAmount = 0;
foreach ($byType as $key => $amount) {
if (!isset($typeLabel[$key])) {
$otherAmount += $amount;
}
}
if ($otherAmount > 0) {
$details[] = [
'type' => '其他',
'amount' => $otherAmount,
'percent' => $totalSum > 0 ? round($otherAmount / $totalSum * 100, 1) : 0,
];
}
return success($details);
} catch (\Throwable $e) {
return error('获取收入明细失败:' . $e->getMessage(), 500);
}
}
/**
* 成本明细:当前为估算(基于收入的 30% 拆分),金额单位:分
*/
public function costDetails()
{
$user = $this->request->user ?? null;
if (!$user || ($user['role'] ?? '') !== 'superadmin') {
return error('无权限访问', 403);
}
try {
$totalRevenue = (int) Db::name('orders')
->whereIn('status', self::PAID_STATUS)
->sum('amount');
$totalCost = (int) round($totalRevenue * 0.3);
$items = [
['type' => 'AI 调用(人脸/分析等)', 'ratio' => 0.15],
['type' => '服务器及运维', 'ratio' => 0.08],
['type' => '其他支出', 'ratio' => 0.07],
];
$details = [];
foreach ($items as $item) {
$amount = (int) round($totalRevenue * $item['ratio']);
$details[] = [
'type' => $item['type'],
'amount' => $amount,
'percent' => $totalCost > 0 ? round($amount / $totalCost * 100, 1) : 0,
];
}
return success($details);
} catch (\Throwable $e) {
return error('获取成本明细失败:' . $e->getMessage(), 500);
}
}
/**
* 企业支付记录(已支付且 enterpriseId 不为空的订单),金额单位:分
*/
public function rechargeRecords()
{
$user = $this->request->user ?? null;
if (!$user || ($user['role'] ?? '') !== 'superadmin') {
return error('无权限访问', 403);
}
try {
$page = max(1, (int) Request::param('page', 1));
$pageSize = min(100, max(1, (int) Request::param('pageSize', 20)));
$query = Db::name('orders')
->whereIn('status', self::PAID_STATUS)
->whereNotNull('enterpriseId')
->where('enterpriseId', '<>', '')
->order('payTime', 'desc');
$total = (int) (clone $query)->count();
$list = (clone $query)->page($page, $pageSize)
->field('id, orderNo, enterpriseId, amount, payMethod, payTime')
->select()
->toArray();
$eids = array_values(array_unique(array_filter(array_column($list, 'enterpriseId'))));
$enterprises = [];
if (!empty($eids)) {
$entList = Db::name('enterprises')->where('id', 'in', $eids)->column('name', 'id');
$enterprises = $entList ?: [];
}
$result = [];
foreach ($list as $r) {
$eid = $r['enterpriseId'] ?? null;
$result[] = [
'orderNo' => $r['orderNo'] ?? '',
'enterprise' => $eid ? ($enterprises[$eid] ?? '企业#' . $eid) : '—',
'amount' => (int) ($r['amount'] ?? 0),
'method' => $r['payMethod'] === 'wechat' ? '微信支付' : ($r['payMethod'] ?? '—'),
'date' => !empty($r['payTime']) ? date('Y-m-d H:i', $r['payTime']) : '—',
];
}
return success([
'list' => $result,
'total' => $total,
'page' => $page,
'pageSize' => $pageSize,
]);
} catch (\Throwable $e) {
return error('获取企业支付记录失败:' . $e->getMessage(), 500);
}
}
/**
* 支付记录(全部已支付订单,分页),金额单位:分
*/
public function paymentRecords()
{
$user = $this->request->user ?? null;
if (!$user || ($user['role'] ?? '') !== 'superadmin') {
return error('无权限访问', 403);
}
try {
$page = max(1, (int) Request::param('page', 1));
$pageSize = min(100, max(1, (int) Request::param('pageSize', 20)));
$keyword = trim(Request::param('keyword', ''));
$query = Db::name('orders')
->whereIn('status', self::PAID_STATUS)
->order('payTime', 'desc');
if ($keyword !== '') {
$query->where(function ($q) use ($keyword) {
$q->whereLike('orderNo', '%' . $keyword . '%');
if (is_numeric($keyword)) {
$q->whereOr('userId', (int) $keyword);
}
});
}
$total = (int) (clone $query)->count();
$list = (clone $query)->page($page, $pageSize)
->field('id, orderNo, userId, enterpriseId, productType, productTitle, amount, payMethod, payTime')
->select()
->toArray();
$userIds = array_values(array_unique(array_filter(array_column($list, 'userId'))));
$eids = array_values(array_unique(array_filter(array_column($list, 'enterpriseId'))));
$usersMap = [];
$entMap = [];
if (!empty($userIds)) {
$users = Db::name('wechat_users')->where('id', 'in', $userIds)->field('id, nickname, phone')->select()->toArray();
foreach ($users as $u) {
$usersMap[(int) $u['id']] = $u;
}
}
if (!empty($eids)) {
$entList = Db::name('enterprises')->where('id', 'in', $eids)->column('name', 'id');
$entMap = $entList ?: [];
}
$productTypeLabel = [
'face' => 'AI人脸分析',
'mbti' => 'MBTI',
'disc' => 'DISC',
'pdp' => 'PDP',
'report' => '完整报告',
'deep_personal' => '个人深度服务',
'deep_team' => '团队深度服务',
];
$result = [];
foreach ($list as $r) {
$uid = (int) ($r['userId'] ?? 0);
$eid = isset($r['enterpriseId']) && $r['enterpriseId'] !== '' ? (int) $r['enterpriseId'] : null;
if ($eid === 0) {
$eid = null;
}
$u = $usersMap[$uid] ?? null;
$enterpriseName = $eid ? ($entMap[$eid] ?? '企业#' . $eid) : '个人';
$result[] = [
'orderNo' => $r['orderNo'] ?? '',
'userName' => $u ? ($u['nickname'] ?? ('用户' . $uid)) : ('用户' . $uid),
'enterprise' => $enterpriseName,
'enterpriseId' => $eid,
'productType' => $productTypeLabel[$r['productType'] ?? ''] ?? ($r['productType'] ?? '—'),
'productTitle' => $r['productTitle'] ?? '',
'amount' => (int) ($r['amount'] ?? 0),
'method' => $r['payMethod'] === 'wechat' ? '微信支付' : ($r['payMethod'] ?? '—'),
'date' => !empty($r['payTime']) ? date('Y-m-d H:i', $r['payTime']) : '—',
];
}
return success([
'list' => $result,
'total' => $total,
'page' => $page,
'pageSize' => $pageSize,
]);
} catch (\Throwable $e) {
return error('获取支付记录失败:' . $e->getMessage(), 500);
}
}
/**
* 导出财务报表
*/
public function export()
{
$user = $this->request->user ?? null;
if (!$user || ($user['role'] ?? '') !== 'superadmin') {
return error('无权限访问', 403);
}
return success(null, '财务报表导出功能开发中');
}
}