This commit is contained in:
Your Name
2026-03-19 12:19:00 +08:00
parent 7832514f28
commit 7147c8e148
243 changed files with 1662 additions and 437 deletions
@@ -6,7 +6,10 @@ namespace app\adminapi\logic\order;
use app\common\model\Order;
use app\common\model\OrderDetail;
use app\common\model\auth\Admin;
use app\common\service\wechat\WechatWorkExternalPayService;
use think\facade\Db;
use think\facade\Log;
/**
* 订单业务逻辑
@@ -49,6 +52,180 @@ class OrderLogic
}
}
/**
* @notes 创建企业微信对外收款订单(参考 https://developer.work.weixin.qq.com/document/path/93665
* 创建后返回 order_no,供员工在企业微信发起收款时作为 out_trade_no 使用
* 收款成功后,微信支付回调会自动更新订单为已支付
* @param array $params patient_id, order_type, amount, creator_id, remark
* @return array|false ['order_no' => '...', 'order' => Order]
*/
public static function createForWechatWork(array $params)
{
$order = self::create($params);
if (!$order) {
return false;
}
return [
'order_no' => $order->order_no,
'order' => $order->toArray(),
];
}
/**
* @notes 从企业微信对外收款账单同步订单(员工直接在企业微信发起收款,未经过后台创建)
* 拉取 get_bill_list 接口的已支付记录,创建或更新本地订单
* @param int $beginTime 开始时间戳(秒)
* @param int $endTime 结束时间戳(秒)
* @return array ['created' => int, 'updated' => int, 'skipped' => int, 'errors' => string[]]
*/
public static function syncFromWechatWorkBills(int $beginTime, int $endTime): array
{
$result = ['created' => 0, 'updated' => 0, 'skipped' => 0, 'errors' => []];
$service = new WechatWorkExternalPayService();
$wechatWorkConfig = config('pay.wechat_work', []);
$defaultCreatorId = $wechatWorkConfig['default_creator_id'] ?? '';
$defaultOrderType = $wechatWorkConfig['default_order_type'] ?? 6;
$payeeUserids = [];
$firstResp = $service->getBillList($beginTime, $endTime, '', '', 100);
if (count($firstResp['bill_list'] ?? []) === 0 && !empty($firstResp['next_cursor'])) {
$admins = Admin::where('work_wechat_userid', '<>', '')->whereNull('delete_time')->column('work_wechat_userid');
$payeeUserids = array_values(array_unique(array_filter($admins)));
}
if (empty($payeeUserids)) {
$payeeUserids = [''];
}
foreach ($payeeUserids as $payeeUserid) {
$cursor = '';
do {
$resp = $service->getBillList($beginTime, $endTime, $payeeUserid, $cursor, 100);
if (($resp['errcode'] ?? -1) !== 0) {
$result['errors'][] = '获取账单失败: ' . ($resp['errmsg'] ?? '未知错误');
break;
}
$billList = $resp['bill_list'] ?? [];
$cursor = $resp['next_cursor'] ?? '';
foreach ($billList as $bill) {
$tradeState = (int)($bill['trade_state'] ?? 0);
if (!in_array($tradeState, [1, 3], true)) {
$result['skipped']++;
continue;
}
$orderNo = (string)($bill['out_trade_no'] ?? $bill['trade_no'] ?? '');
$totalFee = (int)($bill['total_fee'] ?? $bill['total_amount'] ?? 0);
$tradeNo = (string)($bill['transaction_id'] ?? $bill['trade_no'] ?? '');
$payTime = $bill['pay_time'] ?? $bill['time_end'] ?? '';
$billPayeeUserid = (string)($bill['payee_userid'] ?? '');
if (empty($orderNo) || $totalFee <= 0) {
$result['skipped']++;
continue;
}
$amount = round($totalFee / 100, 2);
$orderType = ($amount == 5.00) ? 1 : $defaultOrderType;
$creatorId = 0;
if ($billPayeeUserid) {
$admin = Admin::where('work_wechat_userid', $billPayeeUserid)->whereNull('delete_time')->find();
if ($admin) {
$creatorId = (int)$admin->id;
}
}
$payerExternalUserid = (string)($bill['external_userid'] ?? '');
$order = Order::where('order_no', $orderNo)->find();
if ($order) {
if ($order->status == 1) {
$order->status = 2;
$order->payment_method = 'wechat';
$order->payment_time = $payTime ? (is_numeric($payTime) ? date('Y-m-d H:i:s', (int)$payTime) : $payTime) : date('Y-m-d H:i:s');
$order->trade_no = $tradeNo;
$order->payee_userid = $billPayeeUserid;
$order->payer_external_userid = $payerExternalUserid;
$order->creator_id = $creatorId;
$order->order_type = $orderType;
$order->save();
$result['updated']++;
} else {
$needSave = false;
if ($billPayeeUserid && $creatorId !== $defaultCreatorId && (int)$order->creator_id === $defaultCreatorId) {
$order->payee_userid = $billPayeeUserid;
$order->payer_external_userid = $payerExternalUserid;
$order->creator_id = $creatorId;
$needSave = true;
}
if ($amount == 5.00 && (int)$order->order_type !== 1) {
$order->order_type = 1;
$needSave = true;
}
if ($needSave) {
$order->save();
}
$result['skipped']++;
}
} else {
try {
$order = new Order();
$order->order_no = $orderNo;
$order->patient_id = null;
$order->creator_id = $creatorId;
$order->order_type = $orderType;
$order->amount = $amount;
$order->status = 2;
$order->payment_method = 'wechat';
$order->payment_time = $payTime ? (is_numeric($payTime) ? date('Y-m-d H:i:s', (int)$payTime) : $payTime) : date('Y-m-d H:i:s');
$order->trade_no = $tradeNo;
$order->remark = '企业微信直接收款同步,待关联患者';
$order->payee_userid = $billPayeeUserid;
$order->payer_external_userid = $payerExternalUserid;
$order->save();
$result['created']++;
} catch (\Exception $e) {
$result['errors'][] = "订单 {$orderNo}: " . $e->getMessage();
Log::error("企业微信账单同步创建订单失败: {$orderNo}, " . $e->getMessage());
}
}
}
} while ($cursor);
}
return $result;
}
/**
* @notes 从回调数据创建订单(员工直接在企业微信发起收款时,订单不存在则自动创建)
* @param string $orderNo 商户订单号(out_trade_no
* @param float $amount 金额(元)
* @param string $tradeNo 微信交易号
* @return Order|null
*/
public static function createFromCallback(string $orderNo, float $amount, string $tradeNo = ''): ?Order
{
try {
$wechatWorkConfig = config('pay.wechat_work', []);
$creatorId = $wechatWorkConfig['default_creator_id'] ?? 1;
$orderType = $wechatWorkConfig['default_order_type'] ?? 6;
$order = new Order();
$order->order_no = $orderNo;
$order->patient_id = null; // 待关联,管理员可后续编辑
$order->creator_id = $creatorId;
$order->order_type = $orderType;
$order->amount = $amount;
$order->status = 2; // 已支付
$order->payment_method = 'wechat';
$order->payment_time = date('Y-m-d H:i:s');
$order->trade_no = $tradeNo;
$order->remark = '企业微信直接收款,待关联患者';
$order->save();
return $order;
} catch (\Exception $e) {
return null;
}
}
/**
* @notes 编辑订单
* @param int $id
@@ -67,6 +244,14 @@ class OrderLogic
if (isset($params['remark'])) {
$order->remark = $params['remark'];
}
// 支持关联患者(待关联订单可后续关联)
if (array_key_exists('patient_id', $params)) {
$order->patient_id = $params['patient_id'] ? (int)$params['patient_id'] : null;
}
// 支持修改订单类型
if (isset($params['order_type'])) {
$order->order_type = (int)$params['order_type'];
}
$order->save();
return true;
@@ -190,6 +375,33 @@ class OrderLogic
}
}
/**
* @notes 今日收益(按角色权限:超管/主管看全部,普通员工看自己)
* @param int $adminId 当前管理员ID
* @param array $adminInfo 管理员信息(含 root、role 等)
* @return array ['amount' => string, 'count' => int]
*/
public static function todayRevenue(int $adminId, array $adminInfo = []): array
{
$query = Order::where('status', 2)
->whereBetweenTime('payment_time', date('Y-m-d') . ' 00:00:00', date('Y-m-d') . ' 23:59:59')
->whereNull('delete_time');
if (empty($adminInfo['root']) || $adminInfo['root'] != 1) {
$supervisorRoles = config('project.order_edit_all_roles', [0, 3]);
$roleIds = \app\common\model\auth\AdminRole::where('admin_id', $adminId)->column('role_id');
if (count(array_intersect($roleIds, $supervisorRoles)) === 0) {
$query->where('creator_id', $adminId);
}
}
$row = $query->fieldRaw('COUNT(*) as cnt, COALESCE(SUM(amount), 0) as total')->find();
$total = is_object($row) ? ($row->total ?? 0) : ($row['total'] ?? 0);
$cnt = is_object($row) ? ($row->cnt ?? 0) : ($row['cnt'] ?? 0);
return [
'amount' => number_format((float)$total, 2, '.', ''),
'count' => (int)$cnt,
];
}
/**
* @notes 获取订单详情
* @param int $id