Files
zyt/server/app/adminapi/logic/order/OrderLogic.php
T
2026-03-14 15:39:34 +08:00

640 lines
20 KiB
PHP

<?php
declare(strict_types=1);
namespace app\adminapi\logic\order;
use app\common\model\Order;
use app\common\model\OrderDetail;
use think\facade\Db;
/**
* 订单业务逻辑
* Class OrderLogic
* @package app\adminapi\logic\order
*/
class OrderLogic
{
/**
* @notes 生成订单号
* @return string
*/
public static function generateOrderNo(): string
{
return 'ORD' . date('YmdHis') . mt_rand(100000, 999999);
}
/**
* @notes 创建订单
* @param array $params
* @return Order|false
*/
public static function create(array $params)
{
try {
$order = new Order();
$order->order_no = self::generateOrderNo();
$order->patient_id = $params['patient_id'];
$order->creator_id = $params['creator_id'];
$order->order_type = $params['order_type'];
$order->amount = $params['amount'];
$order->status = 1; // 待支付
$order->remark = $params['remark'] ?? '';
$order->save();
return $order;
} catch (\Exception $e) {
self::setError($e->getMessage());
return false;
}
}
/**
* @notes 编辑订单
* @param int $id
* @param array $params
* @return bool
*/
public static function edit(int $id, array $params): bool
{
try {
$order = Order::find($id);
if (!$order) {
self::setError('订单不存在');
return false;
}
if (isset($params['remark'])) {
$order->remark = $params['remark'];
}
$order->save();
return true;
} catch (\Exception $e) {
self::setError($e->getMessage());
return false;
}
}
/**
* @notes 支付订单
* @param int $id
* @param string $paymentMethod
* @return bool
*/
public static function pay(int $id, string $paymentMethod): bool
{
try {
$order = Order::find($id);
if (!$order) {
self::setError('订单不存在');
return false;
}
if ($order->status != 1) {
self::setError('订单状态不允许支付');
return false;
}
$order->status = 2; // 已支付
$order->payment_method = $paymentMethod;
$order->payment_time = date('Y-m-d H:i:s');
$order->save();
return true;
} catch (\Exception $e) {
self::setError($e->getMessage());
return false;
}
}
/**
* @notes 取消订单
* @param int $id
* @return bool
*/
public static function cancel(int $id): bool
{
try {
$order = Order::find($id);
if (!$order) {
self::setError('订单不存在');
return false;
}
if ($order->status != 1) {
self::setError('只有待支付的订单才能取消');
return false;
}
$order->status = 3; // 已取消
$order->save();
return true;
} catch (\Exception $e) {
self::setError($e->getMessage());
return false;
}
}
/**
* @notes 退款订单
* @param int $id
* @return bool
*/
public static function refund(int $id): bool
{
try {
$order = Order::find($id);
if (!$order) {
self::setError('订单不存在');
return false;
}
if ($order->status != 2) {
self::setError('只有已支付的订单才能退款');
return false;
}
$order->status = 4; // 已退款
$order->save();
return true;
} catch (\Exception $e) {
self::setError($e->getMessage());
return false;
}
}
/**
* @notes 删除订单
* @param int $id
* @return bool
*/
public static function delete(int $id): bool
{
try {
$order = Order::find($id);
if (!$order) {
self::setError('订单不存在');
return false;
}
// 删除订单
$order->delete();
return true;
} catch (\Exception $e) {
self::setError($e->getMessage());
return false;
}
}
/**
* @notes 获取订单详情
* @param int $id
* @return array|null
*/
public static function detail(int $id): ?array
{
$order = Order::with(['patient', 'creator'])
->find($id);
if (!$order) {
return null;
}
return $order->toArray();
}
private static $error = '';
public static function setError(string $error): void
{
self::$error = $error;
}
public static function getError(): string
{
return self::$error;
}
/**
* @notes 生成支付宝签名
* @param array $params
* @param string $privateKey
* @return string
*/
private static function generateAlipaySign(array $params, string $privateKey): string
{
// 移除sign字段
unset($params['sign']);
// 按键排序
ksort($params);
// 构建签名字符串
$signStr = '';
foreach ($params as $key => $value) {
if ($value !== '' && $value !== null) {
$signStr .= $key . '=' . $value . '&';
}
}
$signStr = rtrim($signStr, '&');
// 使用私钥签名
$privateKeyResource = openssl_pkey_get_private($privateKey);
openssl_sign($signStr, $sign, $privateKeyResource, OPENSSL_ALGO_SHA256);
openssl_free_key($privateKeyResource);
return base64_encode($sign);
}
/**
* @notes 生成微信签名
* @param array $params
* @param string $apiKey
* @return string
*/
private static function generateWechatSign(array $params, string $apiKey): string
{
// 移除sign字段
unset($params['sign']);
// 按键排序
ksort($params);
// 构建签名字符串
$signStr = '';
foreach ($params as $key => $value) {
if ($value !== '' && $value !== null) {
$signStr .= $key . '=' . $value . '&';
}
}
$signStr .= 'key=' . $apiKey;
// MD5签名
return strtoupper(md5($signStr));
}
/**
* @notes 生成随机字符串
* @param int $length
* @return string
*/
private static function generateNonceStr(int $length = 32): string
{
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$str = '';
for ($i = 0; $i < $length; $i++) {
$str .= $chars[mt_rand(0, strlen($chars) - 1)];
}
return $str;
}
/**
* @notes 支付宝支付
* @param $order
* @return string|false
*/
public static function alipayPay($order)
{
try {
// 这里调用支付宝SDK生成支付链接
// 示例:生成支付宝支付URL
$payUrl = self::generateAlipayUrl($order);
return $payUrl;
} catch (\Exception $e) {
self::setError($e->getMessage());
return false;
}
}
/**
* @notes 微信支付
* @param $order
* @return array|false
*/
public static function wechatPay($order)
{
try {
// 这里调用微信SDK生成支付二维码或链接
// 示例:生成微信支付数据
$payData = self::generateWechatPayData($order);
return $payData;
} catch (\Exception $e) {
self::setError($e->getMessage());
return false;
}
}
/**
* @notes 生成支付宝支付URL
* @param $order
* @return string
*/
private static function generateAlipayUrl($order): string
{
try {
// 获取订单号
$orderNo = is_array($order) ? $order['order_no'] : $order->order_no;
$amount = is_array($order) ? ($order['amount'] ?? 0) : $order->amount;
// 获取支付宝配置
$alipayConfig = config('pay.alipay');
if (empty($alipayConfig['app_id']) || empty($alipayConfig['merchant_private_key'])) {
throw new \Exception('支付宝配置不完整');
}
// 使用支付宝SDK生成支付URL
$params = [
'app_id' => $alipayConfig['app_id'],
'method' => 'alipay.trade.page.pay',
'charset' => 'UTF-8',
'sign_type' => 'RSA2',
'timestamp' => date('Y-m-d H:i:s'),
'version' => '1.0',
'notify_url' => $alipayConfig['notify_url'] ?: config('app.url') . '/api/order/alipay-notify',
'return_url' => $alipayConfig['return_url'] ?: config('app.url') . '/order',
'biz_content' => json_encode([
'out_trade_no' => $orderNo,
'product_code' => 'FAST_INSTANT_TRADE_PAY',
'total_amount' => $amount,
'subject' => '订单支付',
'body' => '订单号:' . $orderNo,
], JSON_UNESCAPED_UNICODE),
];
// 生成签名
$sign = self::generateAlipaySign($params, $alipayConfig['merchant_private_key']);
$params['sign'] = $sign;
// 生成支付URL
$payUrl = $alipayConfig['gateway_url'] . '?' . http_build_query($params);
return $payUrl;
} catch (\Exception $e) {
self::setError($e->getMessage());
return '';
}
}
/**
* @notes 生成微信支付数据
* @param $order
* @return array
*/
private static function generateWechatPayData($order): array
{
try {
// 获取订单号
$orderNo = is_array($order) ? $order['order_no'] : $order->order_no;
$amount = is_array($order) ? ($order['amount'] ?? 0) : $order->amount;
// 获取微信配置
$wechatConfig = config('pay.wechat');
if (empty($wechatConfig['app_id']) || empty($wechatConfig['mch_id']) || empty($wechatConfig['api_key'])) {
throw new \Exception('微信支付配置不完整');
}
// 生成微信支付数据
$payData = [
'appid' => $wechatConfig['app_id'],
'mch_id' => $wechatConfig['mch_id'],
'nonce_str' => self::generateNonceStr(),
'body' => '订单支付',
'out_trade_no' => $orderNo,
'total_fee' => (int)($amount * 100), // 转换为分
'spbill_create_ip' => request()->ip() ?: '127.0.0.1',
'notify_url' => $wechatConfig['notify_url'] ?: config('app.url') . '/api/order/wechat-notify',
'trade_type' => 'NATIVE', // 二维码支付
];
// 生成签名
$sign = self::generateWechatSign($payData, $wechatConfig['api_key']);
$payData['sign'] = $sign;
// 返回支付数据
return [
'pay_url' => $wechatConfig['gateway_url'] . '/pay/unifiedorder',
'pay_data' => $payData,
'order_no' => $orderNo,
];
} catch (\Exception $e) {
self::setError($e->getMessage());
return [];
}
}
private static $orderTypeNames = [
0 => '退款统计',
1 => '挂号费',
2 => '问诊费',
3 => '药品费用',
4 => '首付费用',
5 => '尾款费用',
6 => '其他费用',
];
/**
* @notes 订单统计(按订单类型或退款)
* @param array $params order_type(0退款,1挂号费,2问诊费,3药品费用,4首付,5尾款,6其他), days(0今天,7,30)
* @return array
*/
public static function orderStats(array $params = [])
{
$orderType = isset($params['order_type']) ? (int)$params['order_type'] : 1;
if (!array_key_exists($orderType, self::$orderTypeNames)) {
$orderType = 1;
}
$days = isset($params['days']) ? (int)$params['days'] : 7;
$endTime = !empty($params['end_time']) ? strtotime($params['end_time']) : time();
if ($days === 0) {
$startTime = strtotime(date('Y-m-d'));
} else {
$days = $days > 0 ? min($days, 90) : 7;
$startTime = strtotime("-{$days} days", $endTime);
}
$startStr = date('Y-m-d H:i:s', $startTime);
$endStr = date('Y-m-d H:i:s', $endTime);
$depts = \app\common\model\dept\Dept::where('status', 1)
->whereNull('delete_time')
->order('sort desc, id asc')
->column('name', 'id');
$query = Order::where('create_time', 'between', [$startStr, $endStr])
->whereNull('delete_time');
if ($orderType === 0) {
$query->where('status', 4);
} else {
$query->where('order_type', $orderType)->where('status', 2);
}
$orderRows = $query
->field('creator_id, count(*) as cnt, sum(amount) as total_amount')
->group('creator_id')
->select()
->toArray();
$creatorIds = array_unique(array_column($orderRows, 'creator_id'));
$creatorIds = array_filter($creatorIds);
if (empty($creatorIds)) {
return [
'order_type' => $orderType,
'order_type_name' => self::$orderTypeNames[$orderType],
'date_range' => [date('Y-m-d', $startTime), date('Y-m-d', $endTime)],
'today_total' => 0,
'today_amount' => '0.00',
'today_ranking' => [],
'departments' => [],
'ranking' => [],
'chart' => ['xAxis' => [], 'series' => []],
];
}
$counts = [];
$amounts = [];
foreach ($orderRows as $row) {
$counts[$row['creator_id']] = (int)$row['cnt'];
$amounts[$row['creator_id']] = round((float)$row['total_amount'], 2);
}
$adminDepts = \app\common\model\auth\AdminDept::whereIn('admin_id', $creatorIds)
->select()
->toArray();
$adminToDepts = [];
foreach ($adminDepts as $ad) {
$adminId = $ad['admin_id'];
if (!isset($adminToDepts[$adminId])) {
$adminToDepts[$adminId] = [];
}
$adminToDepts[$adminId][] = (int)$ad['dept_id'];
}
$admins = \app\common\model\auth\Admin::whereIn('id', $creatorIds)
->whereNull('delete_time')
->column('name', 'id');
$todayStart = strtotime(date('Y-m-d'));
$todayEnd = time();
$todayStr = date('Y-m-d H:i:s', $todayStart);
$todayEndStr = date('Y-m-d H:i:s', $todayEnd);
$todayQuery = Order::where('create_time', 'between', [$todayStr, $todayEndStr])
->whereNull('delete_time');
if ($orderType === 0) {
$todayQuery->where('status', 4);
} else {
$todayQuery->where('order_type', $orderType)->where('status', 2);
}
$todayRows = $todayQuery
->field('creator_id, count(*) as cnt, sum(amount) as total_amount')
->group('creator_id')
->select()
->toArray();
$todayCounts = [];
$todayAmounts = [];
foreach ($todayRows as $row) {
$todayCounts[$row['creator_id']] = (int)$row['cnt'];
$todayAmounts[$row['creator_id']] = round((float)$row['total_amount'], 2);
}
$todayTotal = array_sum($todayCounts);
$todayAmount = array_sum($todayAmounts);
$todayRanking = [];
foreach ($creatorIds as $cid) {
$cnt = (int)($todayCounts[$cid] ?? 0);
$amt = (float)($todayAmounts[$cid] ?? 0);
if ($cnt > 0 || $amt > 0) {
$todayRanking[] = [
'id' => (int)$cid,
'name' => $admins[$cid] ?? '未知',
'count' => $cnt,
'amount' => number_format($amt, 2, '.', ''),
];
}
}
usort($todayRanking, fn($a, $b) => $b['count'] <=> $a['count']);
$deptData = [];
$assignedIds = [];
foreach ($depts as $deptId => $deptName) {
$members = [];
foreach ($adminToDepts as $adminId => $deptIdsArr) {
if (!in_array((int)$deptId, $deptIdsArr)) {
continue;
}
$assignedIds[] = $adminId;
$members[] = [
'id' => (int)$adminId,
'name' => $admins[$adminId] ?? '未知',
'count' => (int)($counts[$adminId] ?? 0),
'amount' => number_format((float)($amounts[$adminId] ?? 0), 2, '.', ''),
];
}
$deptTotal = array_sum(array_column($members, 'count'));
$deptAmount = array_sum(array_map(fn($m) => (float)$m['amount'], $members));
$deptData[] = [
'dept_id' => (int)$deptId,
'dept_name' => $deptName,
'members' => $members,
'total' => $deptTotal,
'amount' => number_format($deptAmount, 2, '.', ''),
];
}
$unassignedIds = array_diff($creatorIds, array_unique($assignedIds));
if (!empty($unassignedIds)) {
$members = [];
foreach ($unassignedIds as $adminId) {
$members[] = [
'id' => (int)$adminId,
'name' => $admins[$adminId] ?? '未知',
'count' => (int)($counts[$adminId] ?? 0),
'amount' => number_format((float)($amounts[$adminId] ?? 0), 2, '.', ''),
];
}
$deptData[] = [
'dept_id' => 0,
'dept_name' => '未分配部门',
'members' => $members,
'total' => array_sum(array_column($members, 'count')),
'amount' => number_format(array_sum(array_map(fn($m) => (float)$m['amount'], $members)), 2, '.', ''),
];
}
$ranking = [];
foreach ($creatorIds as $adminId) {
$ranking[] = [
'id' => (int)$adminId,
'name' => $admins[$adminId] ?? '未知',
'count' => (int)($counts[$adminId] ?? 0),
'amount' => number_format((float)($amounts[$adminId] ?? 0), 2, '.', ''),
];
}
usort($ranking, fn($a, $b) => $b['count'] <=> $a['count']);
$chartNames = [];
$chartData = [];
foreach ($ranking as $i => $r) {
$chartNames[] = ($i + 1) . '.' . $r['name'];
$chartData[] = $r['count'];
}
return [
'order_type' => $orderType,
'order_type_name' => self::$orderTypeNames[$orderType],
'date_range' => [date('Y-m-d', $startTime), date('Y-m-d', $endTime)],
'today_total' => $todayTotal,
'today_amount' => number_format($todayAmount, 2, '.', ''),
'today_ranking' => $todayRanking,
'departments' => $deptData,
'ranking' => $ranking,
'chart' => [
'xAxis' => $chartNames,
'series' => [['name' => '单数', 'data' => $chartData]],
],
];
}
}