This commit is contained in:
wushumin
2026-05-11 15:28:27 +08:00
commit edd1a02157
302 changed files with 67193 additions and 0 deletions

View File

@@ -0,0 +1,393 @@
<?php
namespace app\controller\admin;
use app\support\EnterpriseCustomerService;
use app\support\EnterpriseOrderService;
use app\support\EnterpriseWebhookService;
use support\Request;
use support\think\Db;
class CustomersController
{
public function index(Request $request)
{
$keyword = trim((string)$request->input('keyword', ''));
$status = trim((string)$request->input('status', ''));
$query = Db::name('enterprise_customers')->order('id', 'desc');
if ($keyword !== '') {
$query->where(function ($builder) use ($keyword) {
$builder->whereRaw(
'(customer_code LIKE :keyword_code OR customer_name LIKE :keyword_name OR contact_name LIKE :keyword_contact OR contact_mobile LIKE :keyword_mobile)',
[
'keyword_code' => "%{$keyword}%",
'keyword_name' => "%{$keyword}%",
'keyword_contact' => "%{$keyword}%",
'keyword_mobile' => "%{$keyword}%",
]
);
});
}
if (in_array($status, ['enabled', 'disabled'], true)) {
$query->where('status', $status);
}
$rows = $query->select()->toArray();
$customerIds = array_map(static fn(array $item) => (int)$item['id'], $rows);
$appCountMap = $this->countMap('enterprise_customer_apps', $customerIds);
$orderCountMap = $this->countMap('enterprise_customer_order_refs', $customerIds);
$eventCountMap = $this->countMap('enterprise_order_events', $customerIds);
return api_success([
'list' => array_map(function (array $item) use ($appCountMap, $orderCountMap, $eventCountMap) {
$customer = $this->customerService()->formatCustomer($item);
$id = (int)$customer['id'];
$customer['app_count'] = (int)($appCountMap[$id] ?? 0);
$customer['order_count'] = (int)($orderCountMap[$id] ?? 0);
$customer['event_count'] = (int)($eventCountMap[$id] ?? 0);
return $customer;
}, $rows),
]);
}
public function detail(Request $request)
{
$id = (int)$request->input('id', 0);
if ($id <= 0) {
return api_error('客户 ID 不能为空', 422);
}
$customer = Db::name('enterprise_customers')->where('id', $id)->find();
if (!$customer) {
return api_error('客户不存在', 404);
}
$apps = Db::name('enterprise_customer_apps')
->where('customer_id', $id)
->order('id', 'desc')
->select()
->toArray();
return api_success([
'customer' => $this->customerService()->formatCustomer($customer),
'apps' => array_map(fn(array $item) => $this->customerService()->formatApp($item), $apps),
]);
}
public function save(Request $request)
{
$id = (int)$request->input('id', 0);
$customerName = trim((string)$request->input('customer_name', ''));
if ($customerName === '') {
return api_error('客户名称不能为空', 422);
}
$status = trim((string)$request->input('status', 'enabled'));
if (!in_array($status, ['enabled', 'disabled'], true)) {
return api_error('客户状态不正确', 422);
}
$webhookUrl = trim((string)$request->input('webhook_url', ''));
if ($webhookUrl !== '' && !preg_match('/^https?:\/\//i', $webhookUrl)) {
return api_error('Webhook URL 必须以 http 或 https 开头', 422);
}
$now = date('Y-m-d H:i:s');
$payload = [
'customer_name' => $customerName,
'contact_name' => trim((string)$request->input('contact_name', '')),
'contact_mobile' => trim((string)$request->input('contact_mobile', '')),
'contact_email' => trim((string)$request->input('contact_email', '')),
'settlement_type' => 'monthly',
'webhook_url' => $webhookUrl,
'webhook_enabled' => $request->input('webhook_enabled', false) ? 1 : 0,
'status' => $status,
'remark' => trim((string)$request->input('remark', '')),
'updated_at' => $now,
];
Db::startTrans();
try {
if ($id > 0) {
$customer = Db::name('enterprise_customers')->where('id', $id)->find();
if (!$customer) {
Db::rollback();
return api_error('客户不存在', 404);
}
Db::name('enterprise_customers')->where('id', $id)->update($payload);
} else {
$payload['customer_code'] = $this->customerService()->generateCustomerCode();
$payload['created_at'] = $now;
$id = (int)Db::name('enterprise_customers')->insertGetId($payload);
}
$customer = Db::name('enterprise_customers')->where('id', $id)->find();
if ($customer) {
$this->customerService()->ensureVirtualUser($customer);
}
Db::commit();
} catch (\Throwable $e) {
Db::rollback();
return api_error('客户保存失败', 500, [
'detail' => $e->getMessage(),
]);
}
return api_success([
'id' => $id,
], $request->input('id', 0) ? '客户已更新' : '客户已创建');
}
public function createApp(Request $request)
{
$customerId = (int)$request->input('customer_id', 0);
$appName = trim((string)$request->input('app_name', '默认应用'));
if ($customerId <= 0) {
return api_error('客户 ID 不能为空', 422);
}
if ($appName === '') {
$appName = '默认应用';
}
$customer = Db::name('enterprise_customers')->where('id', $customerId)->find();
if (!$customer) {
return api_error('客户不存在', 404);
}
$secret = $this->customerService()->generateAppSecret();
$now = date('Y-m-d H:i:s');
$appId = (int)Db::name('enterprise_customer_apps')->insertGetId([
'customer_id' => $customerId,
'app_name' => $appName,
'app_key' => $this->customerService()->generateAppKey(),
'app_secret_cipher' => $this->customerService()->encryptSecret($secret),
'secret_last4' => substr($secret, -4),
'status' => 'enabled',
'last_used_at' => null,
'created_at' => $now,
'updated_at' => $now,
]);
$app = Db::name('enterprise_customer_apps')->where('id', $appId)->find();
return api_success([
'app' => $this->customerService()->formatApp($app),
'app_secret' => $secret,
], '应用 Key 已创建,请立即复制保存 Secret');
}
public function updateAppStatus(Request $request)
{
$id = (int)$request->input('id', 0);
$status = trim((string)$request->input('status', ''));
if ($id <= 0 || !in_array($status, ['enabled', 'disabled'], true)) {
return api_error('应用 ID 或状态不正确', 422);
}
$app = Db::name('enterprise_customer_apps')->where('id', $id)->find();
if (!$app) {
return api_error('应用不存在', 404);
}
Db::name('enterprise_customer_apps')->where('id', $id)->update([
'status' => $status,
'updated_at' => date('Y-m-d H:i:s'),
]);
return api_success([
'id' => $id,
'status' => $status,
], $status === 'enabled' ? '应用已启用' : '应用已停用');
}
public function resetAppSecret(Request $request)
{
$id = (int)$request->input('id', 0);
if ($id <= 0) {
return api_error('应用 ID 不能为空', 422);
}
$app = Db::name('enterprise_customer_apps')->where('id', $id)->find();
if (!$app) {
return api_error('应用不存在', 404);
}
$secret = $this->customerService()->generateAppSecret();
Db::name('enterprise_customer_apps')->where('id', $id)->update([
'app_secret_cipher' => $this->customerService()->encryptSecret($secret),
'secret_last4' => substr($secret, -4),
'updated_at' => date('Y-m-d H:i:s'),
]);
$fresh = Db::name('enterprise_customer_apps')->where('id', $id)->find();
return api_success([
'app' => $this->customerService()->formatApp($fresh),
'app_secret' => $secret,
], '应用 Secret 已重置,请立即复制保存');
}
public function orders(Request $request)
{
$customerId = (int)$request->input('customer_id', 0);
if ($customerId <= 0) {
return api_error('客户 ID 不能为空', 422);
}
$rows = Db::name('enterprise_customer_order_refs')
->alias('r')
->leftJoin('orders o', 'o.id = r.order_id')
->leftJoin('order_products p', 'p.order_id = r.order_id')
->field([
'r.id',
'r.customer_id',
'r.external_order_no',
'r.order_id',
'r.order_no',
'r.appraisal_no',
'r.created_at',
'o.order_status',
'o.display_status',
'o.pay_amount',
'p.product_name',
])
->where('r.customer_id', $customerId)
->order('r.id', 'desc')
->select()
->toArray();
return api_success([
'list' => array_map(static fn(array $item) => [
'id' => (int)$item['id'],
'customer_id' => (int)$item['customer_id'],
'external_order_no' => (string)$item['external_order_no'],
'order_id' => (int)$item['order_id'],
'order_no' => (string)$item['order_no'],
'appraisal_no' => (string)$item['appraisal_no'],
'product_name' => (string)($item['product_name'] ?: '待完善物品信息'),
'order_status' => (string)($item['order_status'] ?? ''),
'display_status' => (string)($item['display_status'] ?? ''),
'pay_amount' => (float)($item['pay_amount'] ?? 0),
'created_at' => (string)$item['created_at'],
], $rows),
]);
}
public function events(Request $request)
{
$customerId = (int)$request->input('customer_id', 0);
if ($customerId <= 0) {
return api_error('客户 ID 不能为空', 422);
}
$rows = Db::name('enterprise_order_events')
->where('customer_id', $customerId)
->order('id', 'desc')
->limit(200)
->select()
->toArray();
return api_success([
'list' => array_map(fn(array $item) => $this->webhookService()->formatEvent($item), $rows),
]);
}
public function deliveries(Request $request)
{
$customerId = (int)$request->input('customer_id', 0);
$eventId = (int)$request->input('event_id', 0);
if ($customerId <= 0 && $eventId <= 0) {
return api_error('客户 ID 或事件 ID 不能为空', 422);
}
$query = Db::name('enterprise_webhook_deliveries')->order('id', 'desc')->limit(200);
if ($customerId > 0) {
$query->where('customer_id', $customerId);
}
if ($eventId > 0) {
$query->where('event_id', $eventId);
}
$rows = $query->select()->toArray();
return api_success([
'list' => array_map(fn(array $item) => $this->webhookService()->formatDelivery($item), $rows),
]);
}
public function resendEvent(Request $request)
{
$eventId = (int)$request->input('event_id', 0);
if ($eventId <= 0) {
return api_error('事件 ID 不能为空', 422);
}
try {
$result = $this->webhookService()->deliverEvent($eventId, true);
} catch (\RuntimeException $e) {
return api_error($e->getMessage(), 422);
} catch (\Throwable $e) {
return api_error('事件补发失败', 500, [
'detail' => $e->getMessage(),
]);
}
return api_success([
'delivery' => $this->webhookService()->formatDelivery($result['delivery']),
'sent' => (bool)$result['sent'],
], $result['sent'] ? '事件已补发成功' : '事件补发未成功,请查看推送记录');
}
public function orderProgress(Request $request)
{
$customerId = (int)$request->input('customer_id', 0);
$externalOrderNo = trim((string)$request->input('external_order_no', ''));
if ($customerId <= 0 || $externalOrderNo === '') {
return api_error('客户 ID 和外部订单号不能为空', 422);
}
$customer = Db::name('enterprise_customers')->where('id', $customerId)->find();
if (!$customer) {
return api_error('客户不存在', 404);
}
try {
$order = (new EnterpriseOrderService())->findOrder($customer, $externalOrderNo, '');
} catch (\Throwable $e) {
return api_error($e->getMessage(), 404);
}
return api_success([
'order' => $order,
]);
}
private function countMap(string $table, array $customerIds): array
{
if (!$customerIds) {
return [];
}
$rows = Db::name($table)
->field('customer_id, COUNT(*) AS total')
->whereIn('customer_id', $customerIds)
->group('customer_id')
->select()
->toArray();
$map = [];
foreach ($rows as $row) {
$map[(int)$row['customer_id']] = (int)$row['total'];
}
return $map;
}
private function customerService(): EnterpriseCustomerService
{
return new EnterpriseCustomerService();
}
private function webhookService(): EnterpriseWebhookService
{
return new EnterpriseWebhookService();
}
}