feat: update appraisal ordering and payment flows
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace app\controller\admin;
|
||||
|
||||
use app\support\AppraisalServicePricePackageService;
|
||||
use app\support\AppraisalEvidenceService;
|
||||
use app\support\EnterpriseWebhookService;
|
||||
use app\support\MessageDispatcher;
|
||||
@@ -37,6 +38,9 @@ class OrdersController
|
||||
'o.estimated_finish_time',
|
||||
'o.source_channel',
|
||||
'o.source_customer_id',
|
||||
'o.price_package_name',
|
||||
'o.price_package_code',
|
||||
'o.price_package_price',
|
||||
'o.pay_amount',
|
||||
'o.created_at',
|
||||
'p.product_name',
|
||||
@@ -130,6 +134,9 @@ class OrdersController
|
||||
'brand_name' => $item['brand_name'] ?: '',
|
||||
'service_provider' => $item['service_provider'],
|
||||
'service_provider_text' => $item['service_provider'] === 'zhongjian' ? '中检鉴定' : '实物鉴定',
|
||||
'price_package_name' => (string)($item['price_package_name'] ?? ''),
|
||||
'price_package_code' => (string)($item['price_package_code'] ?? ''),
|
||||
'price_package_price' => (float)($item['price_package_price'] ?? 0),
|
||||
'source_channel' => $this->normalizeOrderSourceChannel((string)($item['source_channel'] ?? '')),
|
||||
'source_channel_text' => $this->sourceChannelText((string)($item['source_channel'] ?? '')),
|
||||
'source_customer_id' => (string)($item['source_customer_id'] ?? ''),
|
||||
@@ -312,6 +319,9 @@ class OrdersController
|
||||
'appraisal_no' => $order['appraisal_no'],
|
||||
'service_provider' => $order['service_provider'],
|
||||
'service_provider_text' => $order['service_provider'] === 'zhongjian' ? '中检鉴定' : '实物鉴定',
|
||||
'price_package_name' => (string)($order['price_package_name'] ?? ''),
|
||||
'price_package_code' => (string)($order['price_package_code'] ?? ''),
|
||||
'price_package_price' => (float)($order['price_package_price'] ?? 0),
|
||||
'source_channel' => $this->normalizeOrderSourceChannel((string)($order['source_channel'] ?? '')),
|
||||
'source_channel_text' => $this->sourceChannelText((string)($order['source_channel'] ?? '')),
|
||||
'source_customer_id' => (string)($order['source_customer_id'] ?? ''),
|
||||
@@ -925,6 +935,8 @@ class OrdersController
|
||||
public function createManualOrder(Request $request)
|
||||
{
|
||||
$serviceProvider = $this->normalizeServiceProvider((string)$request->input('service_provider', 'anxinyan'));
|
||||
$pricePackageId = (int)$request->input('price_package_id', 0);
|
||||
$pricePackageCode = trim((string)$request->input('price_package_code', ''));
|
||||
$productInput = $this->requestArray($request, 'product_info');
|
||||
$extraInput = $this->requestArray($request, 'extra_info');
|
||||
$returnAddressInput = $this->requestArray($request, 'return_address');
|
||||
@@ -971,10 +983,14 @@ class OrdersController
|
||||
}
|
||||
|
||||
$now = date('Y-m-d H:i:s');
|
||||
$serviceConfig = $this->serviceConfig($serviceProvider);
|
||||
try {
|
||||
$servicePackage = $this->pricePackageSnapshot($serviceProvider, $pricePackageId, $pricePackageCode);
|
||||
} catch (\RuntimeException $e) {
|
||||
return api_error($e->getMessage(), 422);
|
||||
}
|
||||
$orderNo = $this->generateOrderNo();
|
||||
$appraisalNo = $this->generateAppraisalNo();
|
||||
$estimated = date('Y-m-d H:i:s', strtotime(sprintf('+%d hours', (int)$serviceConfig['sla_hours'])));
|
||||
$estimated = date('Y-m-d H:i:s', strtotime(sprintf('+%d hours', (int)$servicePackage['sla_hours'])));
|
||||
$operatorId = (int)$request->header('x-admin-id', 0) ?: null;
|
||||
|
||||
Db::startTrans();
|
||||
@@ -1001,7 +1017,11 @@ class OrdersController
|
||||
'estimated_finish_time' => $estimated,
|
||||
'source_channel' => self::MANUAL_ENTRY_SOURCE,
|
||||
'source_customer_id' => '',
|
||||
'pay_amount' => (float)$serviceConfig['price'],
|
||||
'price_package_id' => $servicePackage['price_package_id'],
|
||||
'price_package_name' => $servicePackage['price_package_name'],
|
||||
'price_package_code' => $servicePackage['price_package_code'],
|
||||
'price_package_price' => $servicePackage['price_package_price'],
|
||||
'pay_amount' => (float)$servicePackage['pay_amount'],
|
||||
'paid_at' => $now,
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now,
|
||||
@@ -1107,6 +1127,8 @@ class OrdersController
|
||||
'order_no' => $orderNo,
|
||||
'appraisal_no' => $appraisalNo,
|
||||
'user_id' => (int)$user['id'],
|
||||
'price_package_name' => $servicePackage['price_package_name'],
|
||||
'pay_amount' => (float)$servicePackage['pay_amount'],
|
||||
'next_status' => 'pending_shipping',
|
||||
], '补录订单已创建');
|
||||
}
|
||||
@@ -1152,6 +1174,7 @@ class OrdersController
|
||||
'category_ids' => $this->decodeIntList($item['category_ids'] ?? ''),
|
||||
'supported_service_types' => $this->decodeJsonArray($item['supported_service_types'] ?? null),
|
||||
], $brands),
|
||||
'service_price_packages' => (new AppraisalServicePricePackageService())->serviceOptions(),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -1240,14 +1263,9 @@ class OrdersController
|
||||
return in_array($serviceProvider, ['anxinyan', 'zhongjian'], true) ? $serviceProvider : '';
|
||||
}
|
||||
|
||||
private function serviceConfig(string $serviceProvider): array
|
||||
private function pricePackageSnapshot(string $serviceProvider, int $packageId = 0, string $packageCode = ''): array
|
||||
{
|
||||
$configs = [
|
||||
'anxinyan' => ['price' => 99.00, 'sla_hours' => 48],
|
||||
'zhongjian' => ['price' => 199.00, 'sla_hours' => 72],
|
||||
];
|
||||
|
||||
return $configs[$serviceProvider] ?? $configs['anxinyan'];
|
||||
return (new AppraisalServicePricePackageService())->snapshotForOrder($serviceProvider, $packageId, $packageCode);
|
||||
}
|
||||
|
||||
private function generateOrderNo(): string
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace app\controller\admin;
|
||||
|
||||
use app\support\AppraisalServicePricePackageService;
|
||||
use support\Request;
|
||||
|
||||
class ServicePricePackagesController
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
return api_success($this->service()->adminIndex());
|
||||
}
|
||||
|
||||
public function save(Request $request)
|
||||
{
|
||||
$id = (int)$request->input('id', 0);
|
||||
|
||||
try {
|
||||
$packageId = $this->service()->save([
|
||||
'service_provider' => $request->input('service_provider', 'anxinyan'),
|
||||
'package_name' => $request->input('package_name', ''),
|
||||
'package_code' => $request->input('package_code', ''),
|
||||
'price' => $request->input('price', ''),
|
||||
'description' => $request->input('description', ''),
|
||||
'is_enabled' => $request->input('is_enabled', true),
|
||||
'is_default' => $request->input('is_default', false),
|
||||
'sort_order' => $request->input('sort_order', 0),
|
||||
], $id);
|
||||
} catch (\RuntimeException $e) {
|
||||
return api_error($e->getMessage(), 422);
|
||||
} catch (\Throwable $e) {
|
||||
return api_error('服务价格套餐保存失败', 500, [
|
||||
'detail' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
|
||||
return api_success([
|
||||
'id' => $packageId,
|
||||
], $id > 0 ? '服务价格套餐已更新' : '服务价格套餐已创建');
|
||||
}
|
||||
|
||||
public function updateStatus(Request $request)
|
||||
{
|
||||
$id = (int)$request->input('id', 0);
|
||||
if ($id <= 0) {
|
||||
return api_error('套餐 ID 不能为空', 422);
|
||||
}
|
||||
|
||||
try {
|
||||
$this->service()->setEnabled($id, !empty($request->input('is_enabled', true)));
|
||||
} catch (\RuntimeException $e) {
|
||||
return api_error($e->getMessage(), 422);
|
||||
} catch (\Throwable $e) {
|
||||
return api_error('服务价格套餐状态更新失败', 500, [
|
||||
'detail' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
|
||||
return api_success(['id' => $id], '套餐状态已更新');
|
||||
}
|
||||
|
||||
public function setDefault(Request $request)
|
||||
{
|
||||
$id = (int)$request->input('id', 0);
|
||||
if ($id <= 0) {
|
||||
return api_error('套餐 ID 不能为空', 422);
|
||||
}
|
||||
|
||||
try {
|
||||
$this->service()->setDefault($id);
|
||||
} catch (\RuntimeException $e) {
|
||||
return api_error($e->getMessage(), 422);
|
||||
} catch (\Throwable $e) {
|
||||
return api_error('默认套餐设置失败', 500, [
|
||||
'detail' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
|
||||
return api_success(['id' => $id], '默认套餐已更新');
|
||||
}
|
||||
|
||||
private function service(): AppraisalServicePricePackageService
|
||||
{
|
||||
return new AppraisalServicePricePackageService();
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ use support\think\Db;
|
||||
class SystemConfigsController
|
||||
{
|
||||
private const H5_OAUTH_REDIRECT_HASH_PATH = '/#/pages/auth/login';
|
||||
private const SHOUQIANBA_NOTIFY_PATH = '/api/open/shouqianba/payment/notify';
|
||||
|
||||
public function index(Request $request)
|
||||
{
|
||||
@@ -55,6 +56,8 @@ class SystemConfigsController
|
||||
|
||||
public function save(Request $request)
|
||||
{
|
||||
$this->bootstrapDefaults();
|
||||
|
||||
$items = $request->input('items', []);
|
||||
if (!is_array($items) || !$items) {
|
||||
return api_error('配置项不能为空', 422);
|
||||
@@ -423,17 +426,31 @@ class SystemConfigsController
|
||||
],
|
||||
],
|
||||
'payment' => [
|
||||
'group_name' => '支付与商户平台',
|
||||
'group_desc' => '配置微信支付商户号、API 密钥、证书序列号等上线必要参数。',
|
||||
'group_name' => '收钱吧支付',
|
||||
'group_desc' => '配置收钱吧轻 POS 远程收款和小程序收银插件参数,用于用户端 H5 与微信小程序下单付款。',
|
||||
'items' => [
|
||||
['config_key' => 'mch_id', 'title' => '商户号 MchID', 'field_type' => 'text', 'placeholder' => '请输入商户号', 'remark' => '微信支付商户平台分配的商户号', 'is_secret' => false],
|
||||
['config_key' => 'api_v3_key', 'title' => 'APIv3 Key', 'field_type' => 'password', 'placeholder' => '请输入 APIv3 Key', 'remark' => '用于微信支付接口验签与解密', 'is_secret' => true],
|
||||
['config_key' => 'merchant_serial_no', 'title' => '商户证书序列号', 'field_type' => 'text', 'placeholder' => '请输入商户证书序列号', 'remark' => '与商户 API 证书匹配', 'is_secret' => false],
|
||||
['config_key' => 'apiclient_key_path', 'title' => 'apiclient_key.pem', 'field_type' => 'file', 'placeholder' => '请上传 apiclient_key.pem', 'remark' => '上传微信支付商户私钥文件,系统将保存到后端非公开目录', 'is_secret' => true],
|
||||
['config_key' => 'apiclient_cert_path', 'title' => 'apiclient_cert.pem', 'field_type' => 'file', 'placeholder' => '请上传 apiclient_cert.pem', 'remark' => '上传微信支付商户证书文件,系统将保存到后端非公开目录', 'is_secret' => false],
|
||||
['config_key' => 'merchant_private_key', 'title' => '商户私钥', 'field_type' => 'textarea', 'placeholder' => '请输入商户私钥内容', 'remark' => '用于支付签名,请妥善保管', 'is_secret' => true],
|
||||
['config_key' => 'platform_certificate_serial', 'title' => '平台证书序列号', 'field_type' => 'text', 'placeholder' => '请输入微信支付平台证书序列号', 'remark' => '用于平台证书校验', 'is_secret' => false],
|
||||
['config_key' => 'notify_url', 'title' => '支付回调地址', 'field_type' => 'text', 'placeholder' => '请输入支付回调通知地址', 'remark' => '支付成功后用于回调业务系统', 'is_secret' => false],
|
||||
[
|
||||
'config_key' => 'enabled',
|
||||
'title' => '收钱吧支付开关',
|
||||
'field_type' => 'select',
|
||||
'placeholder' => '请选择是否启用',
|
||||
'remark' => '参数未齐时请先保持停用保存草稿;启用后会校验全部必填参数并正式接管用户端支付。',
|
||||
'is_secret' => false,
|
||||
'default_value' => 'disabled',
|
||||
'options' => [
|
||||
['label' => '停用', 'value' => 'disabled'],
|
||||
['label' => '启用', 'value' => 'enabled'],
|
||||
],
|
||||
],
|
||||
['config_key' => 'api_domain', 'title' => 'API 域名', 'field_type' => 'text', 'placeholder' => '例如 https://xxx.shouqianba.com', 'remark' => '收钱吧提供的接口域名,不需要填写末尾斜杠。', 'is_secret' => false, 'visible_when' => ['config_key' => 'enabled', 'equals' => 'enabled']],
|
||||
['config_key' => 'appid', 'title' => '收钱吧 AppID', 'field_type' => 'text', 'placeholder' => '请输入收钱吧 AppID', 'remark' => '请求头 head.appid 使用的应用编号。', 'is_secret' => false, 'visible_when' => ['config_key' => 'enabled', 'equals' => 'enabled']],
|
||||
['config_key' => 'brand_code', 'title' => '品牌编号 brand_code', 'field_type' => 'text', 'placeholder' => '请输入收钱吧分配的品牌编号', 'remark' => '收钱吧系统对接前分配并提供。', 'is_secret' => false, 'visible_when' => ['config_key' => 'enabled', 'equals' => 'enabled']],
|
||||
['config_key' => 'store_sn', 'title' => '门店编号 store_sn', 'field_type' => 'text', 'placeholder' => '请输入门店编号', 'remark' => '商户内部使用的门店编号。', 'is_secret' => false, 'visible_when' => ['config_key' => 'enabled', 'equals' => 'enabled']],
|
||||
['config_key' => 'order_expire_minutes', 'title' => '订单有效期(分钟)', 'field_type' => 'text', 'placeholder' => '默认 1440', 'remark' => '收钱吧订单有效时间,允许 1-43200 分钟。', 'is_secret' => false, 'default_value' => '1440', 'visible_when' => ['config_key' => 'enabled', 'equals' => 'enabled']],
|
||||
['config_key' => 'merchant_private_key', 'title' => '商户 RSA 私钥', 'field_type' => 'textarea', 'placeholder' => '可填 PEM 内容或服务器可读取的 PEM 文件路径', 'remark' => '我们自己生成的私钥,用于请求签名和通知响应签名,请勿提供给收钱吧。', 'is_secret' => true, 'visible_when' => ['config_key' => 'enabled', 'equals' => 'enabled']],
|
||||
['config_key' => 'shouqianba_public_key', 'title' => '收钱吧 RSA 公钥', 'field_type' => 'textarea', 'placeholder' => '请输入收钱吧提供的 RSA 公钥 PEM 内容', 'remark' => '这是收钱吧回传给我们的公钥,不是我们生成并提交给收钱吧的商户公钥。用于验签接口返回和支付通知。', 'is_secret' => false, 'visible_when' => ['config_key' => 'enabled', 'equals' => 'enabled']],
|
||||
['config_key' => 'notify_url', 'title' => '支付通知地址', 'field_type' => 'text', 'placeholder' => '由 API 公开域名自动生成', 'remark' => '由后端 API 公开域名自动拼接生成,仅展示无需手动填写。', 'is_secret' => false, 'read_only' => true, 'visible_when' => ['config_key' => 'enabled', 'equals' => 'enabled']],
|
||||
['config_key' => 'mini_program_plugin_version', 'title' => '小程序插件版本号', 'field_type' => 'text', 'placeholder' => '例如 2.3.xx', 'remark' => '构建微信小程序前同步到 manifest,用于声明收钱吧轻 POS 插件。', 'is_secret' => false, 'visible_when' => ['config_key' => 'enabled', 'equals' => 'enabled']],
|
||||
],
|
||||
],
|
||||
'sms' => [
|
||||
@@ -477,19 +494,13 @@ class SystemConfigsController
|
||||
|
||||
private function uploadableConfigMap(): array
|
||||
{
|
||||
return [
|
||||
'payment.apiclient_key_path' => [
|
||||
'filename' => 'apiclient_key.pem',
|
||||
],
|
||||
'payment.apiclient_cert_path' => [
|
||||
'filename' => 'apiclient_cert.pem',
|
||||
],
|
||||
];
|
||||
return [];
|
||||
}
|
||||
|
||||
private function validateConfigValues(array $configValueMap): void
|
||||
{
|
||||
$driver = (new FileStorageConfigService())->normalizeDriver((string)($configValueMap['file_storage.driver'] ?? 'local'));
|
||||
$this->validatePaymentConfig($configValueMap);
|
||||
if ($driver === 'local') {
|
||||
$this->validateKuaidi100Config($configValueMap);
|
||||
return;
|
||||
@@ -544,6 +555,89 @@ class SystemConfigsController
|
||||
$this->validateKuaidi100Config($configValueMap);
|
||||
}
|
||||
|
||||
private function validatePaymentConfig(array $configValueMap): void
|
||||
{
|
||||
$enabled = (string)($configValueMap['payment.enabled'] ?? 'disabled');
|
||||
if (!in_array($enabled, ['enabled', 'disabled'], true)) {
|
||||
throw new \RuntimeException('收钱吧支付开关配置无效');
|
||||
}
|
||||
if ($enabled !== 'enabled') {
|
||||
return;
|
||||
}
|
||||
|
||||
$required = [
|
||||
'payment.api_domain' => '收钱吧 API 域名',
|
||||
'payment.appid' => '收钱吧 AppID',
|
||||
'payment.brand_code' => '品牌编号',
|
||||
'payment.store_sn' => '门店编号',
|
||||
'payment.order_expire_minutes' => '订单有效分钟数',
|
||||
'payment.merchant_private_key' => '商户 RSA 私钥',
|
||||
'payment.shouqianba_public_key' => '收钱吧 RSA 公钥',
|
||||
'payment.notify_url' => '支付通知地址',
|
||||
];
|
||||
|
||||
foreach ($required as $key => $label) {
|
||||
if (trim((string)($configValueMap[$key] ?? '')) === '') {
|
||||
throw new \RuntimeException(sprintf('当前已启用收钱吧支付,请先填写 %s;如需先保存草稿,请先将收钱吧支付开关设为停用', $label));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (['payment.api_domain' => '收钱吧 API 域名', 'payment.notify_url' => '支付通知地址'] as $key => $label) {
|
||||
if (!preg_match('/^https?:\/\//i', trim((string)$configValueMap[$key]))) {
|
||||
throw new \RuntimeException(sprintf('%s需以 http:// 或 https:// 开头', $label));
|
||||
}
|
||||
}
|
||||
|
||||
$expireMinutes = trim((string)($configValueMap['payment.order_expire_minutes'] ?? ''));
|
||||
if (!ctype_digit($expireMinutes) || (int)$expireMinutes < 1 || (int)$expireMinutes > 43200) {
|
||||
throw new \RuntimeException('收钱吧订单有效分钟数需填写 1-43200 之间的整数');
|
||||
}
|
||||
|
||||
if (!$this->isPemContentOrReadablePath((string)$configValueMap['payment.merchant_private_key'])) {
|
||||
throw new \RuntimeException('商户 RSA 私钥需填写 PEM 内容,或填写服务器可读取的 PEM 文件路径');
|
||||
}
|
||||
if (!$this->isPublicKeyContentOrReadablePath((string)$configValueMap['payment.shouqianba_public_key'])) {
|
||||
throw new \RuntimeException('收钱吧 RSA 公钥需填写 PEM 内容、纯公钥文本,或填写服务器可读取的 PEM 文件路径');
|
||||
}
|
||||
}
|
||||
|
||||
private function isPublicKeyContentOrReadablePath(string $value): bool
|
||||
{
|
||||
$value = trim($value);
|
||||
if ($this->isPemContentOrReadablePath($value)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->looksLikeBase64KeyBody($value);
|
||||
}
|
||||
|
||||
private function isPemContentOrReadablePath(string $value): bool
|
||||
{
|
||||
$value = trim($value);
|
||||
if ($value === '') {
|
||||
return false;
|
||||
}
|
||||
if (str_contains($value, '-----BEGIN')) {
|
||||
return true;
|
||||
}
|
||||
if (!is_file($value) || !is_readable($value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$content = file_get_contents($value);
|
||||
return is_string($content) && str_contains($content, '-----BEGIN');
|
||||
}
|
||||
|
||||
private function looksLikeBase64KeyBody(string $value): bool
|
||||
{
|
||||
$body = preg_replace('/\s+/', '', trim($value));
|
||||
if (!is_string($body) || strlen($body) < 64) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return preg_match('/^[A-Za-z0-9+\/=]+$/', $body) === 1 && base64_decode($body, true) !== false;
|
||||
}
|
||||
|
||||
private function validateKuaidi100Config(array $configValueMap): void
|
||||
{
|
||||
$enabled = (string)($configValueMap['kuaidi100.enabled'] ?? 'disabled');
|
||||
@@ -574,6 +668,7 @@ class SystemConfigsController
|
||||
private function applyDerivedConfigValues(array &$configValueMap): void
|
||||
{
|
||||
$configValueMap['h5.oauth_redirect_url'] = $this->buildH5OAuthRedirectUrl((string)($configValueMap['h5.page_base_url'] ?? ''));
|
||||
$configValueMap['payment.notify_url'] = $this->buildShouqianbaNotifyUrl((string)($configValueMap['file_storage.public_base_url'] ?? ''));
|
||||
}
|
||||
|
||||
private function buildH5OAuthRedirectUrl(string $pageBaseUrl): string
|
||||
@@ -586,6 +681,38 @@ class SystemConfigsController
|
||||
return $baseUrl . self::H5_OAUTH_REDIRECT_HASH_PATH;
|
||||
}
|
||||
|
||||
private function buildShouqianbaNotifyUrl(string $publicBaseUrl): string
|
||||
{
|
||||
$baseUrl = $this->normalizePublicApiBaseUrl($publicBaseUrl);
|
||||
if ($baseUrl === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $baseUrl . self::SHOUQIANBA_NOTIFY_PATH;
|
||||
}
|
||||
|
||||
private function normalizePublicApiBaseUrl(string $publicBaseUrl): string
|
||||
{
|
||||
$baseUrl = trim((string)($_ENV['PUBLIC_FILE_BASE_URL'] ?? ''));
|
||||
if ($baseUrl === '') {
|
||||
$baseUrl = trim($publicBaseUrl);
|
||||
}
|
||||
if ($baseUrl === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$hashPos = strpos($baseUrl, '#');
|
||||
if ($hashPos !== false) {
|
||||
$baseUrl = substr($baseUrl, 0, $hashPos);
|
||||
}
|
||||
|
||||
if (!preg_match('/^https?:\/\//i', $baseUrl)) {
|
||||
$baseUrl = 'https://' . ltrim($baseUrl, '/');
|
||||
}
|
||||
|
||||
return rtrim($baseUrl, '/');
|
||||
}
|
||||
|
||||
private function normalizeH5PageBaseUrl(string $value): string
|
||||
{
|
||||
$baseUrl = trim($value);
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
namespace app\controller\app;
|
||||
|
||||
use app\support\MessageDispatcher;
|
||||
use app\support\ContentService;
|
||||
use app\support\AppraisalServicePricePackageService;
|
||||
use app\support\FileStorageService;
|
||||
use app\support\PublicAssetUrlService;
|
||||
use app\support\WarehouseService;
|
||||
use app\support\ShouqianbaPaymentService;
|
||||
use support\Request;
|
||||
use support\think\Db;
|
||||
use function str_starts_with;
|
||||
@@ -52,16 +52,35 @@ class AppraisalController
|
||||
]);
|
||||
}
|
||||
|
||||
public function serviceConfigs(Request $request)
|
||||
{
|
||||
return api_success([
|
||||
'list' => (new AppraisalServicePricePackageService())->serviceOptions(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function createDraft(Request $request)
|
||||
{
|
||||
$userId = app_user_id($request);
|
||||
$serviceProvider = (string)$request->input('service_provider', 'anxinyan');
|
||||
$serviceMode = (string)$request->input('service_mode', 'physical');
|
||||
$pricePackageId = (int)$request->input('price_package_id', 0);
|
||||
$pricePackageCode = trim((string)$request->input('price_package_code', ''));
|
||||
|
||||
try {
|
||||
$package = $this->pricePackageSnapshot($serviceProvider, $pricePackageId, $pricePackageCode);
|
||||
} catch (\RuntimeException $e) {
|
||||
return api_error($e->getMessage(), 422);
|
||||
}
|
||||
|
||||
$draftId = Db::name('appraisal_drafts')->insertGetId([
|
||||
'user_id' => $userId,
|
||||
'service_mode' => $serviceMode,
|
||||
'service_provider' => $serviceProvider,
|
||||
'price_package_id' => $package['price_package_id'],
|
||||
'price_package_name' => $package['price_package_name'],
|
||||
'price_package_code' => $package['price_package_code'],
|
||||
'price_package_price' => $package['price_package_price'],
|
||||
'current_step' => 1,
|
||||
'status' => 'draft',
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
@@ -72,6 +91,10 @@ class AppraisalController
|
||||
'draft_id' => (int)$draftId,
|
||||
'service_provider' => $serviceProvider,
|
||||
'service_mode' => $serviceMode,
|
||||
'price_package_id' => $package['price_package_id'],
|
||||
'price_package_name' => $package['price_package_name'],
|
||||
'price_package_code' => $package['price_package_code'],
|
||||
'price_package_price' => $package['price_package_price'],
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -112,6 +135,10 @@ class AppraisalController
|
||||
'draft_id' => (int)$draft['id'],
|
||||
'service_provider' => $draft['service_provider'],
|
||||
'service_mode' => $draft['service_mode'],
|
||||
'price_package_id' => (int)($draft['price_package_id'] ?? 0),
|
||||
'price_package_name' => (string)($draft['price_package_name'] ?? ''),
|
||||
'price_package_code' => (string)($draft['price_package_code'] ?? ''),
|
||||
'price_package_price' => (float)($draft['price_package_price'] ?? 0),
|
||||
'current_step' => (int)$draft['current_step'],
|
||||
'product_info' => $product ?: new \stdClass(),
|
||||
'extra_info' => $extra ?: new \stdClass(),
|
||||
@@ -133,11 +160,30 @@ class AppraisalController
|
||||
$productInfo = (array)$request->input('product_info', []);
|
||||
$extraInfo = (array)$request->input('extra_info', []);
|
||||
$uploadInfo = (array)$request->input('upload_info', []);
|
||||
$serviceProvider = (string)$request->input('service_provider', $draft['service_provider']);
|
||||
$hasPackageInput = $request->input('price_package_id', null) !== null
|
||||
|| trim((string)$request->input('price_package_code', '')) !== '';
|
||||
$pricePackageId = (int)$request->input('price_package_id', $draft['price_package_id'] ?? 0);
|
||||
$pricePackageCode = trim((string)$request->input('price_package_code', $draft['price_package_code'] ?? ''));
|
||||
if ($serviceProvider !== (string)$draft['service_provider'] && !$hasPackageInput) {
|
||||
$pricePackageId = 0;
|
||||
$pricePackageCode = '';
|
||||
}
|
||||
|
||||
try {
|
||||
$package = $this->pricePackageSnapshot($serviceProvider, $pricePackageId, $pricePackageCode);
|
||||
} catch (\RuntimeException $e) {
|
||||
return api_error($e->getMessage(), 422);
|
||||
}
|
||||
|
||||
Db::name('appraisal_drafts')
|
||||
->where('id', $draftId)
|
||||
->update([
|
||||
'service_provider' => $request->input('service_provider', $draft['service_provider']),
|
||||
'service_provider' => $serviceProvider,
|
||||
'price_package_id' => $package['price_package_id'],
|
||||
'price_package_name' => $package['price_package_name'],
|
||||
'price_package_code' => $package['price_package_code'],
|
||||
'price_package_price' => $package['price_package_price'],
|
||||
'current_step' => $currentStep,
|
||||
'updated_at' => date('Y-m-d H:i:s'),
|
||||
]);
|
||||
@@ -303,13 +349,24 @@ class AppraisalController
|
||||
return api_error('预览数据不存在', 404);
|
||||
}
|
||||
|
||||
$serviceConfig = $this->serviceConfig((string)$draft['service_provider']);
|
||||
try {
|
||||
$servicePackage = $this->pricePackageSnapshot(
|
||||
(string)$draft['service_provider'],
|
||||
(int)($draft['price_package_id'] ?? 0),
|
||||
(string)($draft['price_package_code'] ?? '')
|
||||
);
|
||||
} catch (\RuntimeException $e) {
|
||||
return api_error($e->getMessage(), 422);
|
||||
}
|
||||
$policyConfig = (new ContentService())->getPolicyConfig();
|
||||
|
||||
return api_success([
|
||||
'service_summary' => [
|
||||
'service_provider' => $draft['service_provider'],
|
||||
'service_provider_text' => $draft['service_provider'] === 'zhongjian' ? '中检鉴定' : '实物鉴定',
|
||||
'service_provider_text' => $servicePackage['service_provider_text'],
|
||||
'price_package_id' => $servicePackage['price_package_id'],
|
||||
'price_package_name' => $servicePackage['price_package_name'],
|
||||
'price_package_code' => $servicePackage['price_package_code'],
|
||||
],
|
||||
'product_summary' => [
|
||||
'product_name' => $this->resolveProductName($product),
|
||||
@@ -321,9 +378,9 @@ class AppraisalController
|
||||
'uploaded_count' => $this->countUploadedDraftItems($draftId),
|
||||
],
|
||||
'fee_detail' => [
|
||||
'service_fee' => (float)$serviceConfig['price'],
|
||||
'service_fee' => (float)$servicePackage['price_package_price'],
|
||||
'discount_fee' => 0,
|
||||
'pay_amount' => (float)$serviceConfig['price'],
|
||||
'pay_amount' => (float)$servicePackage['price_package_price'],
|
||||
],
|
||||
'agreements' => $policyConfig['appraisal_agreements'],
|
||||
]);
|
||||
@@ -343,6 +400,9 @@ class AppraisalController
|
||||
if ($sourceChannel !== 'enterprise_push') {
|
||||
$sourceCustomerId = '';
|
||||
}
|
||||
if (!in_array($sourceChannel, ['mini_program', 'h5'], true)) {
|
||||
return api_error('当前下单渠道暂不支持在线支付', 422);
|
||||
}
|
||||
|
||||
$draft = Db::name('appraisal_drafts')->where('id', $draftId)->where('user_id', $userId)->find();
|
||||
$product = Db::name('appraisal_draft_products')->where('draft_id', $draftId)->find();
|
||||
@@ -352,13 +412,21 @@ class AppraisalController
|
||||
return api_error('提交数据不完整', 422);
|
||||
}
|
||||
|
||||
$serviceConfig = $this->serviceConfig((string)$draft['service_provider']);
|
||||
try {
|
||||
$servicePackage = $this->pricePackageSnapshot(
|
||||
(string)$draft['service_provider'],
|
||||
(int)($draft['price_package_id'] ?? 0),
|
||||
(string)($draft['price_package_code'] ?? '')
|
||||
);
|
||||
} catch (\RuntimeException $e) {
|
||||
return api_error($e->getMessage(), 422);
|
||||
}
|
||||
$now = date('Y-m-d H:i:s');
|
||||
$orderNo = 'AXY' . date('YmdHis') . mt_rand(100, 999);
|
||||
$appraisalNo = 'AXY-APP-' . date('Ymd') . '-' . mt_rand(1000, 9999);
|
||||
$estimated = date('Y-m-d H:i:s', strtotime(sprintf('+%d hours', (int)$serviceConfig['sla_hours'])));
|
||||
$estimated = date('Y-m-d H:i:s', strtotime(sprintf('+%d hours', (int)$servicePackage['sla_hours'])));
|
||||
$productName = $this->resolveProductName($product);
|
||||
$warehouseService = new WarehouseService();
|
||||
$paymentService = new ShouqianbaPaymentService();
|
||||
$defaultAddress = Db::name('user_addresses')
|
||||
->where('user_id', $userId)
|
||||
->where('is_default', 1)
|
||||
@@ -383,6 +451,17 @@ class AppraisalController
|
||||
return api_error('请先添加并确认寄回地址', 422);
|
||||
}
|
||||
|
||||
try {
|
||||
$paymentService->assertReadyForSource($sourceChannel, $userId);
|
||||
} catch (\RuntimeException $e) {
|
||||
return api_error($e->getMessage(), 422);
|
||||
} catch (\Throwable $e) {
|
||||
return api_error('支付配置检查失败,请稍后重试', 500, [
|
||||
'detail' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
|
||||
$orderId = 0;
|
||||
Db::startTrans();
|
||||
try {
|
||||
$orderId = Db::name('orders')->insertGetId([
|
||||
@@ -391,14 +470,18 @@ class AppraisalController
|
||||
'user_id' => $userId,
|
||||
'service_mode' => $draft['service_mode'],
|
||||
'service_provider' => $draft['service_provider'],
|
||||
'payment_status' => 'paid',
|
||||
'order_status' => 'pending_shipping',
|
||||
'display_status' => '待寄送商品',
|
||||
'payment_status' => 'unpaid',
|
||||
'order_status' => 'pending_payment',
|
||||
'display_status' => '待支付',
|
||||
'estimated_finish_time' => $estimated,
|
||||
'source_channel' => $sourceChannel,
|
||||
'source_customer_id' => $sourceCustomerId,
|
||||
'pay_amount' => $serviceConfig['price'],
|
||||
'paid_at' => $now,
|
||||
'price_package_id' => $servicePackage['price_package_id'],
|
||||
'price_package_name' => $servicePackage['price_package_name'],
|
||||
'price_package_code' => $servicePackage['price_package_code'],
|
||||
'price_package_price' => $servicePackage['price_package_price'],
|
||||
'pay_amount' => $servicePackage['pay_amount'],
|
||||
'paid_at' => null,
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now,
|
||||
]);
|
||||
@@ -447,37 +530,15 @@ class AppraisalController
|
||||
]);
|
||||
}
|
||||
|
||||
$shippingTarget = $warehouseService->bindOrderTarget(
|
||||
$orderId,
|
||||
(string)$draft['service_provider'],
|
||||
!empty($product['category_id']) ? (int)$product['category_id'] : null,
|
||||
$defaultAddress ?: null
|
||||
);
|
||||
|
||||
Db::name('order_timelines')->insertAll([
|
||||
[
|
||||
'order_id' => $orderId,
|
||||
'node_code' => 'created',
|
||||
'node_text' => '下单成功',
|
||||
'node_desc' => '订单已生成并完成支付',
|
||||
'operator_type' => 'system',
|
||||
'operator_id' => null,
|
||||
'occurred_at' => $now,
|
||||
'created_at' => $now,
|
||||
],
|
||||
[
|
||||
'order_id' => $orderId,
|
||||
'node_code' => 'pending_shipping',
|
||||
'node_text' => '待寄送商品',
|
||||
'node_desc' => sprintf(
|
||||
'请尽快将商品寄送至%s,以免影响处理时效',
|
||||
$shippingTarget['warehouse_name'] ?: '鉴定中心'
|
||||
),
|
||||
'operator_type' => 'system',
|
||||
'operator_id' => null,
|
||||
'occurred_at' => $now,
|
||||
'created_at' => $now,
|
||||
],
|
||||
Db::name('order_timelines')->insert([
|
||||
'order_id' => $orderId,
|
||||
'node_code' => 'created',
|
||||
'node_text' => '订单已生成',
|
||||
'node_desc' => '订单资料已保存,等待用户完成支付。',
|
||||
'operator_type' => 'system',
|
||||
'operator_id' => null,
|
||||
'occurred_at' => $now,
|
||||
'created_at' => $now,
|
||||
]);
|
||||
|
||||
$draftUploads = Db::name('appraisal_draft_uploads')->where('draft_id', $draftId)->select()->toArray();
|
||||
@@ -514,38 +575,11 @@ class AppraisalController
|
||||
}
|
||||
}
|
||||
|
||||
Db::name('appraisal_tasks')->insert([
|
||||
'order_id' => $orderId,
|
||||
'task_stage' => 'first_review',
|
||||
'service_provider' => $draft['service_provider'],
|
||||
'status' => 'pending',
|
||||
'assignee_id' => null,
|
||||
'assignee_name' => '未分配',
|
||||
'started_at' => null,
|
||||
'submitted_at' => null,
|
||||
'sla_deadline' => $estimated,
|
||||
'is_overtime' => 0,
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now,
|
||||
]);
|
||||
|
||||
Db::name('appraisal_drafts')->where('id', $draftId)->update([
|
||||
'status' => 'submitted',
|
||||
'updated_at' => $now,
|
||||
]);
|
||||
|
||||
(new MessageDispatcher())->sendInboxEvent('order_created', [
|
||||
'user_id' => $userId,
|
||||
'biz_type' => 'order',
|
||||
'biz_id' => (int)$orderId,
|
||||
'order_no' => $orderNo,
|
||||
'appraisal_no' => $appraisalNo,
|
||||
'product_name' => $productName,
|
||||
'pay_amount' => (string)$serviceConfig['price'],
|
||||
'fallback_title' => '订单提交成功',
|
||||
'fallback_content' => '您的鉴定订单已提交成功,可前往订单中心查看进度。',
|
||||
]);
|
||||
|
||||
Db::commit();
|
||||
} catch (\Throwable $e) {
|
||||
Db::rollback();
|
||||
@@ -554,12 +588,28 @@ class AppraisalController
|
||||
]);
|
||||
}
|
||||
|
||||
try {
|
||||
$payment = $paymentService->createOrReusePayment((int)$orderId);
|
||||
} catch (\Throwable $e) {
|
||||
return api_success([
|
||||
'order_id' => (int)$orderId,
|
||||
'order_no' => $orderNo,
|
||||
'appraisal_no' => $appraisalNo,
|
||||
'pay_amount' => (float)$servicePackage['pay_amount'],
|
||||
'next_status' => 'pending_payment',
|
||||
'payment' => null,
|
||||
'payment_launch_failed' => true,
|
||||
'payment_error' => $e->getMessage(),
|
||||
], '订单已生成,请稍后在订单详情中继续支付');
|
||||
}
|
||||
|
||||
return api_success([
|
||||
'order_id' => (int)$orderId,
|
||||
'order_no' => $orderNo,
|
||||
'appraisal_no' => $appraisalNo,
|
||||
'pay_amount' => (float)$serviceConfig['price'],
|
||||
'next_status' => 'pending_shipping',
|
||||
'pay_amount' => (float)$servicePackage['pay_amount'],
|
||||
'next_status' => 'pending_payment',
|
||||
'payment' => $payment,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -607,16 +657,9 @@ class AppraisalController
|
||||
return substr($value, 0, $maxLength);
|
||||
}
|
||||
|
||||
private function serviceConfig(string $serviceProvider): array
|
||||
private function pricePackageSnapshot(string $serviceProvider, int $packageId = 0, string $packageCode = ''): array
|
||||
{
|
||||
$configs = [
|
||||
'anxinyan' => ['price' => 99.00, 'sla_hours' => 48],
|
||||
'zhongjian' => ['price' => 199.00, 'sla_hours' => 72],
|
||||
];
|
||||
if (isset($configs[$serviceProvider])) {
|
||||
return $configs[$serviceProvider];
|
||||
}
|
||||
return $configs['anxinyan'];
|
||||
return (new AppraisalServicePricePackageService())->snapshotForOrder($serviceProvider, $packageId, $packageCode);
|
||||
}
|
||||
|
||||
private function draftUploadItems(int $draftId, Request $request): array
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace app\controller\app;
|
||||
|
||||
use app\support\AppAuthService;
|
||||
use app\support\MiniProgramAuthService;
|
||||
use support\Request;
|
||||
|
||||
class AuthController
|
||||
@@ -119,6 +120,26 @@ class AuthController
|
||||
}
|
||||
}
|
||||
|
||||
public function miniProgramBind(Request $request)
|
||||
{
|
||||
$userId = app_user_id($request);
|
||||
$code = trim((string)$request->input('code', ''));
|
||||
if ($code === '') {
|
||||
return api_error('小程序登录 code 不能为空', 422);
|
||||
}
|
||||
|
||||
try {
|
||||
$payload = (new MiniProgramAuthService())->bindOpenid($userId, $code);
|
||||
return api_success($payload, '小程序身份已绑定');
|
||||
} catch (\RuntimeException $e) {
|
||||
return api_error($e->getMessage(), 422);
|
||||
} catch (\Throwable $e) {
|
||||
return api_error('小程序身份绑定失败', 500, [
|
||||
'detail' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function me(Request $request)
|
||||
{
|
||||
$userInfo = (new AppAuthService())->current($request);
|
||||
|
||||
@@ -9,6 +9,7 @@ use app\model\OrderSupplementTaskItem;
|
||||
use app\model\OrderTimeline;
|
||||
use app\support\OrderLogisticsSyncService;
|
||||
use app\support\PublicAssetUrlService;
|
||||
use app\support\ShouqianbaPaymentService;
|
||||
use support\Request;
|
||||
use support\think\Db;
|
||||
|
||||
@@ -26,6 +27,10 @@ class OrdersController
|
||||
'o.order_no',
|
||||
'o.appraisal_no',
|
||||
'o.service_provider',
|
||||
'o.price_package_name',
|
||||
'o.price_package_code',
|
||||
'o.price_package_price',
|
||||
'o.payment_status',
|
||||
'o.order_status',
|
||||
'o.display_status',
|
||||
'o.estimated_finish_time',
|
||||
@@ -62,10 +67,14 @@ class OrdersController
|
||||
'order_id' => (int)$item['id'],
|
||||
'order_no' => $item['order_no'],
|
||||
'appraisal_no' => $item['appraisal_no'],
|
||||
'payment_status' => $item['payment_status'],
|
||||
'order_status' => $item['order_status'],
|
||||
'product_name' => $item['product_name'] ?: '待补充商品名称',
|
||||
'product_cover' => $item['product_cover'] ?: '',
|
||||
'service_provider' => $item['service_provider'],
|
||||
'price_package_name' => (string)($item['price_package_name'] ?? ''),
|
||||
'price_package_code' => (string)($item['price_package_code'] ?? ''),
|
||||
'price_package_price' => (float)($item['price_package_price'] ?? 0),
|
||||
'display_status' => $this->displayStatus(
|
||||
$item['order_status'],
|
||||
$item['display_status'],
|
||||
@@ -123,6 +132,10 @@ class OrdersController
|
||||
'occurred_at' => $item->occurred_at,
|
||||
])
|
||||
->toArray();
|
||||
$payment = Db::name('shouqianba_payments')
|
||||
->where('order_id', $id)
|
||||
->order('id', 'desc')
|
||||
->find();
|
||||
|
||||
$supplement = OrderSupplementTask::where('order_id', $id)
|
||||
->where('status', 'pending')
|
||||
@@ -221,9 +234,13 @@ class OrdersController
|
||||
'order_no' => $order->order_no,
|
||||
'appraisal_no' => $order->appraisal_no,
|
||||
'service_provider' => $order->service_provider,
|
||||
'price_package_name' => (string)($order->price_package_name ?? ''),
|
||||
'price_package_code' => (string)($order->price_package_code ?? ''),
|
||||
'price_package_price' => (float)($order->price_package_price ?? 0),
|
||||
'source_channel' => $this->normalizeOrderSourceChannel((string)($order->source_channel ?? '')),
|
||||
'source_channel_text' => $this->sourceChannelText((string)($order->source_channel ?? '')),
|
||||
'source_customer_id' => (string)($order->source_customer_id ?? ''),
|
||||
'payment_status' => $order->payment_status,
|
||||
'order_status' => $order->order_status,
|
||||
'display_status' => $this->displayStatus(
|
||||
$order->order_status,
|
||||
@@ -242,12 +259,12 @@ class OrdersController
|
||||
'can_edit_return_address' => empty($returnLogistics['tracking_no']),
|
||||
],
|
||||
'product_info' => [
|
||||
'product_name' => $product?->product_name ?: '',
|
||||
'category_name' => $product?->category_name ?: '',
|
||||
'brand_name' => $product?->brand_name ?: '',
|
||||
'color' => $product?->color ?: '',
|
||||
'size_spec' => $product?->size_spec ?: '',
|
||||
'serial_no' => $product?->serial_no ?: '',
|
||||
'product_name' => $product ? ($product->product_name ?: '') : '',
|
||||
'category_name' => $product ? ($product->category_name ?: '') : '',
|
||||
'brand_name' => $product ? ($product->brand_name ?: '') : '',
|
||||
'color' => $product ? ($product->color ?: '') : '',
|
||||
'size_spec' => $product ? ($product->size_spec ?: '') : '',
|
||||
'serial_no' => $product ? ($product->serial_no ?: '') : '',
|
||||
],
|
||||
'extra_info' => [
|
||||
'purchase_channel' => $extra['purchase_channel'] ?? '',
|
||||
@@ -287,11 +304,85 @@ class OrdersController
|
||||
] : null,
|
||||
'available_actions' => [
|
||||
'primary_action' => $this->primaryAction($order->order_status),
|
||||
'secondary_action' => '联系客服',
|
||||
'secondary_action' => $order->order_status === 'pending_payment' ? '取消订单' : '联系客服',
|
||||
],
|
||||
'payment' => $payment ? [
|
||||
'status' => (string)$payment['status'],
|
||||
'channel' => (string)$payment['source_channel'],
|
||||
'check_sn' => (string)$payment['check_sn'],
|
||||
'order_sn' => (string)$payment['order_sn'],
|
||||
'order_token' => (string)$payment['order_token'],
|
||||
'cashier_url' => (string)$payment['cashier_url'],
|
||||
] : null,
|
||||
]);
|
||||
}
|
||||
|
||||
public function retryPayment(Request $request)
|
||||
{
|
||||
$orderId = (int)$request->input('order_id', $request->input('id', 0));
|
||||
$userId = app_user_id($request);
|
||||
if ($orderId <= 0) {
|
||||
return api_error('订单参数不能为空', 422);
|
||||
}
|
||||
|
||||
$order = Db::name('orders')->where('id', $orderId)->where('user_id', $userId)->find();
|
||||
if (!$order) {
|
||||
return api_error('订单不存在', 404);
|
||||
}
|
||||
|
||||
try {
|
||||
$payment = (new ShouqianbaPaymentService())->createOrReusePayment($orderId);
|
||||
return api_success([
|
||||
'order_id' => $orderId,
|
||||
'payment' => $payment,
|
||||
]);
|
||||
} catch (\RuntimeException $e) {
|
||||
return api_error($e->getMessage(), 422);
|
||||
} catch (\Throwable $e) {
|
||||
return api_error('支付发起失败,请稍后重试', 500, [
|
||||
'detail' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function paymentStatus(Request $request)
|
||||
{
|
||||
$orderId = (int)$request->input('order_id', $request->input('id', 0));
|
||||
$userId = app_user_id($request);
|
||||
if ($orderId <= 0) {
|
||||
return api_error('订单参数不能为空', 422);
|
||||
}
|
||||
|
||||
try {
|
||||
return api_success((new ShouqianbaPaymentService())->syncOrderPaymentStatus($orderId, $userId));
|
||||
} catch (\RuntimeException $e) {
|
||||
return api_error($e->getMessage(), 422);
|
||||
} catch (\Throwable $e) {
|
||||
return api_error('支付状态同步失败,请稍后重试', 500, [
|
||||
'detail' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function cancel(Request $request)
|
||||
{
|
||||
$orderId = (int)$request->input('order_id', $request->input('id', 0));
|
||||
$userId = app_user_id($request);
|
||||
if ($orderId <= 0) {
|
||||
return api_error('订单参数不能为空', 422);
|
||||
}
|
||||
|
||||
try {
|
||||
return api_success((new ShouqianbaPaymentService())->cancelPendingOrder($orderId, $userId), '订单已取消');
|
||||
} catch (\RuntimeException $e) {
|
||||
return api_error($e->getMessage(), 422);
|
||||
} catch (\Throwable $e) {
|
||||
return api_error('订单取消失败,请稍后重试', 500, [
|
||||
'detail' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function saveReturnAddress(Request $request)
|
||||
{
|
||||
$orderId = (int)$request->input('order_id', 0);
|
||||
@@ -376,33 +467,46 @@ class OrdersController
|
||||
|
||||
private function primaryAction(string $status, string $returnTrackingNo = '', string $returnTrackingStatus = ''): string
|
||||
{
|
||||
return match ($status) {
|
||||
if ($status === 'completed') {
|
||||
return ($returnTrackingNo !== '' && $returnTrackingStatus !== 'received') ? '查看物流' : '查看报告';
|
||||
}
|
||||
|
||||
$map = [
|
||||
'pending_payment' => '去支付',
|
||||
'pending_submission' => '去上传',
|
||||
'pending_shipping' => '查看寄送',
|
||||
'pending_supplement' => '去补资料',
|
||||
'report_published' => '查看报告',
|
||||
'completed' => ($returnTrackingNo !== '' && $returnTrackingStatus !== 'received') ? '查看物流' : '查看报告',
|
||||
default => '查看进度',
|
||||
};
|
||||
'cancelled' => '',
|
||||
];
|
||||
|
||||
return $map[$status] ?? '查看进度';
|
||||
}
|
||||
|
||||
private function statusDescription(string $status, string $trackingNo = '', string $returnTrackingNo = '', string $returnTrackingStatus = ''): string
|
||||
{
|
||||
return match ($status) {
|
||||
if ($status === 'pending_shipping') {
|
||||
return $trackingNo !== '' ? '运单已提交,等待鉴定中心签收' : '请尽快将商品寄送至鉴定中心';
|
||||
}
|
||||
if ($status === 'completed') {
|
||||
if ($returnTrackingStatus === 'received') {
|
||||
return '回寄商品已签收,本次订单已完成';
|
||||
}
|
||||
return $returnTrackingNo !== '' ? '鉴定物品已寄回,请留意签收与物流信息' : '正式报告已生成,可立即查看并验真';
|
||||
}
|
||||
|
||||
$map = [
|
||||
'pending_payment' => '请完成支付后继续本次鉴定服务',
|
||||
'cancelled' => '订单已取消,如需鉴定请重新下单',
|
||||
'pending_submission' => '请补充必要资料后继续进入鉴定流程',
|
||||
'pending_shipping' => $trackingNo !== '' ? '运单已提交,等待鉴定中心签收' : '请尽快将商品寄送至鉴定中心',
|
||||
'received' => '商品已由鉴定中心签收,等待鉴定师开始处理',
|
||||
'in_first_review' => '鉴定师正在处理,后续节点会持续同步',
|
||||
'in_final_review' => '鉴定师正在处理,预计 24 小时内出具报告',
|
||||
'pending_supplement' => '鉴定师需要您补充资料后继续处理',
|
||||
'report_published' => '正式报告已生成,待平台安排回寄商品',
|
||||
'completed' => $returnTrackingStatus === 'received'
|
||||
? '回寄商品已签收,本次订单已完成'
|
||||
: ($returnTrackingNo !== '' ? '鉴定物品已寄回,请留意签收与物流信息' : '正式报告已生成,可立即查看并验真'),
|
||||
default => '当前无需操作,请耐心等待',
|
||||
};
|
||||
];
|
||||
|
||||
return $map[$status] ?? '当前无需操作,请耐心等待';
|
||||
}
|
||||
|
||||
private function displayStatus(
|
||||
@@ -410,13 +514,21 @@ class OrdersController
|
||||
string $displayStatus,
|
||||
string $trackingNo = '',
|
||||
string $returnTrackingNo = '',
|
||||
string $returnTrackingStatus = '',
|
||||
string $returnTrackingStatus = ''
|
||||
): string
|
||||
{
|
||||
if ($status === 'pending_shipping' && $trackingNo !== '') {
|
||||
return '已提交运单';
|
||||
}
|
||||
|
||||
if ($status === 'pending_payment') {
|
||||
return '待支付';
|
||||
}
|
||||
|
||||
if ($status === 'cancelled') {
|
||||
return '已取消';
|
||||
}
|
||||
|
||||
if ($status === 'report_published') {
|
||||
return '待寄回';
|
||||
}
|
||||
@@ -435,30 +547,29 @@ class OrdersController
|
||||
|
||||
private function usageStatusText(string $status): string
|
||||
{
|
||||
return match ($status) {
|
||||
$map = [
|
||||
'new' => '全新未使用',
|
||||
'light_use' => '轻微使用痕迹',
|
||||
'used' => '长期使用',
|
||||
default => $status,
|
||||
};
|
||||
];
|
||||
|
||||
return $map[$status] ?? $status;
|
||||
}
|
||||
|
||||
private function materialStatusText(string $status): string
|
||||
{
|
||||
return match ($status) {
|
||||
$map = [
|
||||
'uploaded' => '已上传',
|
||||
'optional' => '选填未上传',
|
||||
'pending' => '待上传',
|
||||
default => $status,
|
||||
};
|
||||
];
|
||||
|
||||
return $map[$status] ?? $status;
|
||||
}
|
||||
|
||||
private function materialSourceTypeText(string $sourceType): string
|
||||
{
|
||||
return match ($sourceType) {
|
||||
'supplement' => '补充资料',
|
||||
default => '下单资料',
|
||||
};
|
||||
return $sourceType === 'supplement' ? '补充资料' : '下单资料';
|
||||
}
|
||||
|
||||
private function normalizeOrderSourceChannel(string $sourceChannel): string
|
||||
@@ -485,13 +596,14 @@ class OrdersController
|
||||
|
||||
private function sourceChannelText(string $sourceChannel): string
|
||||
{
|
||||
return match ($this->normalizeOrderSourceChannel($sourceChannel)) {
|
||||
$map = [
|
||||
'mini_program' => '小程序',
|
||||
'h5' => 'H5',
|
||||
'enterprise_push' => '大客户推送订单',
|
||||
'manual_entry' => '后台补录订单',
|
||||
default => '未知渠道',
|
||||
};
|
||||
];
|
||||
|
||||
return $map[$this->normalizeOrderSourceChannel($sourceChannel)] ?? '未知渠道';
|
||||
}
|
||||
|
||||
private function decodeJsonArray(mixed $value): array
|
||||
@@ -535,20 +647,22 @@ class OrdersController
|
||||
private function trackingStatusText(string $status, string $logisticsType = 'send_to_center'): string
|
||||
{
|
||||
if ($logisticsType === 'return_to_user') {
|
||||
return match ($status) {
|
||||
$map = [
|
||||
'submitted' => '已登记回寄运单',
|
||||
'in_transit' => '回寄途中',
|
||||
'received' => '用户已签收',
|
||||
default => $status === '' ? '待回寄' : $status,
|
||||
};
|
||||
];
|
||||
|
||||
return $map[$status] ?? ($status === '' ? '待回寄' : $status);
|
||||
}
|
||||
|
||||
return match ($status) {
|
||||
$map = [
|
||||
'submitted' => '已提交运单',
|
||||
'in_transit' => '运输中',
|
||||
'received' => '已签收',
|
||||
default => $status === '' ? '待提交' : $status,
|
||||
};
|
||||
];
|
||||
|
||||
return $map[$status] ?? ($status === '' ? '待提交' : $status);
|
||||
}
|
||||
|
||||
private function assetUrlService(): PublicAssetUrlService
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace app\controller\open;
|
||||
|
||||
use app\support\ShouqianbaPaymentService;
|
||||
use support\Request;
|
||||
|
||||
class ShouqianbaPaymentController
|
||||
{
|
||||
public function notify(Request $request)
|
||||
{
|
||||
$service = new ShouqianbaPaymentService();
|
||||
try {
|
||||
$service->handleNotification($request->rawBody());
|
||||
return json($service->notificationResponse(true));
|
||||
} catch (\Throwable $e) {
|
||||
return json($service->notificationResponse(false));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user