This commit is contained in:
Your Name
2026-04-11 18:06:02 +08:00
parent 4909ec6daa
commit abcecf66e7
325 changed files with 4199 additions and 633 deletions
@@ -925,7 +925,7 @@ class OrderLogic
}
/**
* 某诊单下已支付支付单,供关联业务订单多选(主管/诊单医助看该诊单全部;否则仅本人创建)
* 某诊单下可关联的支付单(待支付/已支付),供关联业务订单多选(主管/诊单医助看该诊单全部;否则仅本人创建)
* 已占用(关联到未撤回/未取消的业务订单)的支付单会排除;编辑某业务订单时传 $exceptPrescriptionOrderId 以保留当前单已选中的项
*
* @return array<int, array<string, mixed>>
@@ -941,7 +941,7 @@ class OrderLogic
}
$q = Order::where('patient_id', $diagnosisId)
->where('status', 2)
->whereIn('status', [2]) // 1=待支付, 2=已支付, 5=待审核
->whereNull('delete_time');
if (!self::isOrderListSupervisor($adminId, $adminInfo)) {
@@ -0,0 +1,265 @@
<?php
declare(strict_types=1);
namespace app\adminapi\logic\qywx;
use app\common\logic\BaseLogic;
use app\common\model\QywxExternalContact;
use app\common\model\QywxSyncSettings;
use app\common\service\wechat\WechatWorkService;
use think\facade\Db;
use think\facade\Log;
/**
* 企业微信客户逻辑层
*/
class CustomerLogic extends BaseLogic
{
/**
* @notes 同步企业微信客户
*/
public static function syncCustomers()
{
try {
$service = new WechatWorkService();
// 1. 获取部门成员列表(从根部门开始,递归获取所有成员)
$departmentId = config('project.qywx_sync_department_id', 1);
Log::info('开始同步企业微信客户 - 部门ID: ' . $departmentId);
$userList = $service->getDepartmentUserList($departmentId, true);
Log::info('获取到企业成员数量: ' . count($userList));
if (empty($userList)) {
Log::error('未获取到企业成员列表');
self::$error = '未获取到企业成员列表';
return false;
}
$syncCount = 0;
$updateCount = 0;
$newCount = 0;
$processedCustomers = []; // 记录已处理的客户,避免重复
Db::startTrans();
try {
// 2. 遍历每个成员,获取其客户列表
foreach ($userList as $user) {
$userId = $user['userid'] ?? '';
if (empty($userId)) {
continue;
}
Log::info('获取成员客户列表 - userId: ' . $userId . ', name: ' . ($user['name'] ?? ''));
// 获取该成员的客户列表
$customerList = $service->getExternalContactList($userId);
// 如果返回false,说明API调用失败(权限问题)
if ($customerList === false) {
Log::error('获取客户列表失败 - 可能是API权限未开启(错误码48002)');
throw new \Exception('企业微信API权限不足,请在企业微信管理后台开启"客户联系"权限');
}
Log::info('成员 ' . $userId . ' 的客户数量: ' . count($customerList));
if (empty($customerList)) {
continue;
}
// 3. 遍历客户列表,获取详情并保存
foreach ($customerList as $externalUserId) {
// 避免重复处理同一个客户
if (isset($processedCustomers[$externalUserId])) {
continue;
}
$processedCustomers[$externalUserId] = true;
// 获取客户详情
$detail = $service->getExternalContactDetail($externalUserId);
if (empty($detail)) {
continue;
}
$externalContact = $detail['external_contact'] ?? [];
$followUsers = $detail['follow_user'] ?? [];
// 保存或更新客户信息
$data = [
'external_userid' => $externalContact['external_userid'] ?? '',
'name' => $externalContact['name'] ?? '',
'avatar' => $externalContact['avatar'] ?? '',
'type' => $externalContact['type'] ?? 1,
'gender' => $externalContact['gender'] ?? 0,
'unionid' => $externalContact['unionid'] ?? '',
'position' => $externalContact['position'] ?? '',
'corp_name' => $externalContact['corp_name'] ?? '',
'corp_full_name' => $externalContact['corp_full_name'] ?? '',
'external_profile' => json_encode($externalContact['external_profile'] ?? [], JSON_UNESCAPED_UNICODE),
'follow_users' => json_encode($followUsers, JSON_UNESCAPED_UNICODE),
'update_time' => time(),
];
$existing = QywxExternalContact::where('external_userid', $data['external_userid'])->find();
if ($existing) {
$existing->save($data);
$updateCount++;
} else {
$data['create_time'] = time();
QywxExternalContact::create($data);
$newCount++;
}
$syncCount++;
}
}
// 4. 更新同步设置
self::updateSyncStatus('success', $syncCount);
Db::commit();
Log::info('同步完成 - 总数: ' . $syncCount . ', 新增: ' . $newCount . ', 更新: ' . $updateCount);
return [
'sync_count' => $syncCount,
'new_count' => $newCount,
'update_count' => $updateCount,
];
} catch (\Throwable $e) {
Db::rollback();
Log::error('同步企业微信客户失败: ' . $e->getMessage());
self::$error = '同步失败: ' . $e->getMessage();
self::updateSyncStatus('failed', 0, $e->getMessage());
return false;
}
} catch (\Throwable $e) {
Log::error('同步企业微信客户异常: ' . $e->getMessage());
self::$error = '同步异常: ' . $e->getMessage();
self::updateSyncStatus('failed', 0, $e->getMessage());
return false;
}
}
/**
* @notes 获取统计信息
*/
public static function getStats()
{
$total = QywxExternalContact::whereNull('delete_time')->count();
$todayStart = strtotime(date('Y-m-d 00:00:00'));
$today = QywxExternalContact::where('create_time', '>=', $todayStart)
->whereNull('delete_time')
->count();
$settings = self::getSyncSettings();
$lastSyncTime = $settings['last_sync_time'] ?? 0;
$lastSync = $lastSyncTime > 0 ? date('Y-m-d H:i:s', $lastSyncTime) : '';
return [
'total' => $total,
'today' => $today,
'lastSync' => $lastSync,
'syncStatus' => $settings['sync_status'] ?? 'idle',
'syncStatusText' => self::getSyncStatusText($settings['sync_status'] ?? 'idle'),
];
}
/**
* @notes 获取同步设置
*/
public static function getSyncSettings()
{
$setting = QywxSyncSettings::where('setting_key', 'customer_sync')->find();
if (!$setting) {
return [
'auto_sync' => false,
'interval' => 3600,
'last_sync_time' => 0,
'sync_status' => 'idle',
];
}
$value = json_decode($setting->setting_value, true);
return $value ?: [
'auto_sync' => false,
'interval' => 3600,
'last_sync_time' => 0,
'sync_status' => 'idle',
];
}
/**
* @notes 保存同步设置
*/
public static function saveSyncSettings(array $params)
{
try {
$currentSettings = self::getSyncSettings();
$newSettings = array_merge($currentSettings, [
'auto_sync' => (bool)($params['auto_sync'] ?? false),
'interval' => (int)($params['interval'] ?? 3600),
]);
$setting = QywxSyncSettings::where('setting_key', 'customer_sync')->find();
if ($setting) {
$setting->setting_value = json_encode($newSettings, JSON_UNESCAPED_UNICODE);
$setting->update_time = time();
$setting->save();
} else {
QywxSyncSettings::create([
'setting_key' => 'customer_sync',
'setting_value' => json_encode($newSettings, JSON_UNESCAPED_UNICODE),
'create_time' => time(),
'update_time' => time(),
]);
}
return true;
} catch (\Throwable $e) {
self::$error = '保存失败: ' . $e->getMessage();
return false;
}
}
/**
* @notes 更新同步状态
*/
private static function updateSyncStatus(string $status, int $syncCount = 0, string $error = '')
{
try {
$settings = self::getSyncSettings();
$settings['sync_status'] = $status;
$settings['last_sync_time'] = time();
$settings['last_sync_count'] = $syncCount;
if ($error) {
$settings['last_error'] = $error;
}
$setting = QywxSyncSettings::where('setting_key', 'customer_sync')->find();
if ($setting) {
$setting->setting_value = json_encode($settings, JSON_UNESCAPED_UNICODE);
$setting->update_time = time();
$setting->save();
}
} catch (\Throwable $e) {
Log::error('更新同步状态失败: ' . $e->getMessage());
}
}
/**
* @notes 获取同步状态文本
*/
private static function getSyncStatusText(string $status): string
{
$map = [
'idle' => '未同步',
'syncing' => '同步中',
'success' => '同步成功',
'failed' => '同步失败',
];
return $map[$status] ?? '未知';
}
}
@@ -357,6 +357,7 @@ class PrescriptionOrderLogic
2 => '已支付',
3 => '已取消',
4 => '已退款',
5 => '待审核',
];
foreach ($rows as &$r) {
$ot = (int) ($r['order_type'] ?? 0);
@@ -689,6 +690,15 @@ class PrescriptionOrderLogic
if (array_key_exists('pay_order_ids', $params)) {
$payOrderIds = self::normalizePayOrderIds($params['pay_order_ids']);
// 过滤掉已删除的支付单ID(拒绝审核时可能软删除了待审核支付单)
if (!empty($payOrderIds)) {
$existingIds = Order::whereIn('id', $payOrderIds)
->whereNull('delete_time')
->column('id');
$payOrderIds = array_values(array_intersect($payOrderIds, $existingIds));
}
$linkErr = OrderLogic::assertPayOrdersLinkable($payOrderIds, $diagId, $adminId, $adminInfo);
if ($linkErr !== null) {
self::$error = $linkErr;
@@ -745,6 +755,14 @@ class PrescriptionOrderLogic
return false;
}
// 编辑后重置支付审核状态为待审核(需要重新审核)
// 但如果当前状态是"已发货"(5),则不重置审核状态
if ((int) $order->fulfillment_status !== 5) {
$order->payment_slip_audit_status = 0;
$order->payment_slip_audit_remark = '';
}
self::syncFulfillmentStatus($order);
try {
$order->save();
} catch (\Throwable $e) {
@@ -1005,13 +1023,61 @@ class PrescriptionOrderLogic
if ($action === 'approve') {
$order->payment_slip_audit_status = 1;
$order->payment_slip_audit_remark = mb_substr(trim($remark), 0, 500);
// 将关联的待审核支付单(status=5)更新为已支付(status=2
$payOrderIds = self::linkedPayOrderIdList($id);
if (!empty($payOrderIds)) {
Order::whereIn('id', $payOrderIds)
->where('status', 5) // 只更新待审核状态的
->update([
'status' => 2, // 已支付
'payment_time' => date('Y-m-d H:i:s')
]);
}
// 如果有完单申请,自动完成订单
if ((int) $order->completion_request === 1 && (int) $order->fulfillment_status === 5) {
$order->fulfillment_status = 3; // 已完成
$order->completion_request = 0; // 清除完单申请标记
self::writeLog((int) $order->id, $adminId, $adminInfo, 'auto_complete',
'支付审核通过,根据完单申请自动完成订单');
}
} else {
$order->payment_slip_audit_status = 2;
$order->payment_slip_audit_remark = mb_substr(trim($remark), 0, 500);
// 拒绝审核时,软删除待审核状态的关联支付单(status=5)
$payOrderIds = self::linkedPayOrderIdList($id);
if (!empty($payOrderIds)) {
Order::whereIn('id', $payOrderIds)
->where('status', 5) // 只删除待审核状态的
->update([
'delete_time' => date('Y-m-d H:i:s')
]);
}
// 拒绝审核时,清除完单申请
if ((int) $order->completion_request === 1) {
$order->completion_request = 0;
$order->completion_request_time = 0;
$order->completion_request_by = 0;
$order->completion_request_by_name = '';
}
}
self::syncFulfillmentStatus($order);
if ($action === 'reject') {
self::clearPayOrderLinks($order);
// 状态同步和关联清除逻辑:
// - 如果是"已发货"状态(5),无论同意还是拒绝,都保持状态不变
// - 如果不是"已发货"状态,同步状态;拒绝时清除所有支付单关联关系
$currentStatus = (int) $order->fulfillment_status;
if ($currentStatus === 5) {
// 已发货状态:保持已发货状态不变
// 不调用 syncFulfillmentStatus
// 拒绝时也不清除关联(只删除了待审核支付单)
} else {
self::syncFulfillmentStatus($order);
if ($action === 'reject') {
self::clearPayOrderLinks($order);
}
}
try {
@@ -1137,6 +1203,18 @@ class PrescriptionOrderLogic
$order->payment_slip_audit_status = 0;
$order->payment_slip_audit_remark = '';
self::syncFulfillmentStatus($order);
// 将关联的已支付支付单(status=2,且payment_method='manual')恢复为待审核(status=5
$payOrderIds = self::linkedPayOrderIdList($id);
if (!empty($payOrderIds)) {
Order::whereIn('id', $payOrderIds)
->where('status', 2) // 只恢复已支付状态的
->where('payment_method', 'manual') // 只恢复手动创建的
->update([
'status' => 5, // 待审核
'payment_time' => null
]);
}
try {
$order->save();
@@ -1207,22 +1285,23 @@ class PrescriptionOrderLogic
$orderType = (int) ($params['order_type'] ?? 3);
$amount = round((float) ($params['pay_amount'] ?? 0), 2);
$remark = mb_substr(trim((string) ($params['pay_remark'] ?? '')), 0, 200);
$completionRequest = (int) ($params['completion_request'] ?? 0);
if ($amount <= 0) {
self::$error = '支付单金额须大于 0';
return false;
}
// 创建 zyt_order 并标记为已支付(后台直接确认
// 创建 zyt_order 并标记为待审核(需要审核后才算已支付)
$payOrder = new Order();
$payOrder->order_no = OrderLogic::generateOrderNo();
$payOrder->patient_id = (int) $order->diagnosis_id;
$payOrder->creator_id = $adminId;
$payOrder->order_type = $orderType;
$payOrder->amount = $amount;
$payOrder->status = 2; // 已支付
$payOrder->status = 5; // 待审核
$payOrder->payment_method = 'manual';
$payOrder->payment_time = date('Y-m-d H:i:s');
$payOrder->payment_time = null; // 审核通过后再设置支付时间
$payOrder->remark = $remark;
try {
@@ -1242,6 +1321,14 @@ class PrescriptionOrderLogic
$order->payment_slip_audit_status = 0;
$order->payment_slip_audit_remark = '';
// 处理完单申请
if ($completionRequest === 1) {
$order->completion_request = 1;
$order->completion_request_time = time();
$order->completion_request_by = $adminId;
$order->completion_request_by_name = (string) ($adminInfo['name'] ?? '');
}
try {
$order->save();
} catch (\Throwable $e) {
@@ -1249,8 +1336,114 @@ class PrescriptionOrderLogic
return false;
}
self::writeLog($id, $adminId, $adminInfo, 'add_pay_order',
'新增支付单 #' . $payOrder->id . '(¥' . $amount . '),类型:' . $orderType . ',等待支付审核');
$logMsg = '新增支付单 #' . $payOrder->id . '(¥' . $amount . '),类型:' . $orderType . ',等待支付审核';
if ($completionRequest === 1) {
$logMsg .= ',并申请完成订单';
}
self::writeLog($id, $adminId, $adminInfo, 'add_pay_order', $logMsg);
$out = $order->toArray();
self::maskInternalCostIfNeeded($out, $adminInfo);
self::attachLinkedPayOrders($out);
return $out;
}
/**
* 为「已发货」(fulfillment_status=5) 的业务订单关联已有支付单,
* 关联后将支付审核状态重置为待审核以启动再次审核流程。
*
* @param array<string,mixed> $params id, pay_order_id
* @return array<string,mixed>|false
*/
public static function linkPayOrder(array $params, int $adminId, array $adminInfo)
{
self::$error = '';
$id = (int) $params['id'];
$completionRequest = (int) ($params['completion_request'] ?? 0);
// 支持单个或多个支付单ID
$payOrderIds = [];
if (isset($params['pay_order_ids']) && is_array($params['pay_order_ids'])) {
// 多个支付单(数组)
$payOrderIds = array_values(array_unique(array_filter(array_map('intval', $params['pay_order_ids']))));
} elseif (isset($params['pay_order_id'])) {
// 单个支付单(兼容旧接口)
$payOrderIds = [(int) $params['pay_order_id']];
}
if (empty($payOrderIds)) {
self::$error = '请选择要关联的支付单';
return false;
}
$order = PrescriptionOrder::where('id', $id)->whereNull('delete_time')->find();
if (!$order) {
self::$error = '订单不存在';
return false;
}
if (!self::canAccessOrder($order, $adminId, $adminInfo)) {
self::$error = '无权限操作';
return false;
}
if ((int) $order->fulfillment_status !== 5) {
self::$error = '仅「已发货」状态的订单可关联支付单';
return false;
}
$diagId = (int) $order->diagnosis_id;
// 验证所有支付单是否可关联
$linkErr = OrderLogic::assertPayOrdersLinkable($payOrderIds, $diagId, $adminId, $adminInfo);
if ($linkErr !== null) {
self::$error = $linkErr;
return false;
}
// 验证所有支付单是否已被其他业务订单占用
$exErr = self::assertPayOrdersExclusive($payOrderIds, $id);
if ($exErr !== null) {
self::$error = $exErr;
return false;
}
// 将支付单追加到业务订单关联列表
$existingIds = self::linkedPayOrderIdList($id);
$newIds = array_unique(array_merge($existingIds, $payOrderIds));
self::replacePayOrderLinks($id, $newIds);
$order->linked_pay_order_id = $newIds[0];
// 重置支付单审核为待审核,以启动新一轮审核(处方审核保持通过状态不变)
$order->payment_slip_audit_status = 0;
$order->payment_slip_audit_remark = '';
// 处理完单申请
if ($completionRequest === 1) {
$order->completion_request = 1;
$order->completion_request_time = time();
$order->completion_request_by = $adminId;
$order->completion_request_by_name = (string) ($adminInfo['name'] ?? '');
}
try {
$order->save();
} catch (\Throwable $e) {
self::$error = $e->getMessage();
return false;
}
// 计算总金额
$totalAmount = 0;
$payOrders = Order::whereIn('id', $payOrderIds)->select();
foreach ($payOrders as $po) {
$totalAmount += round((float) $po->amount, 2);
}
$logMsg = '关联支付单 ' . count($payOrderIds) . ' 笔(共¥' . $totalAmount . '),等待支付审核';
if ($completionRequest === 1) {
$logMsg .= ',并申请完成订单';
}
self::writeLog($id, $adminId, $adminInfo, 'link_pay_order', $logMsg);
$out = $order->toArray();
self::maskInternalCostIfNeeded($out, $adminInfo);