From d3923edd750cf7d5411297d4d3a028c5f97e7839 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A2=81=E6=9C=9D=E4=BC=9F?= Date: Thu, 14 Nov 2024 18:16:27 +0800 Subject: [PATCH] init --- ...024_11_13_160651_create_bydauto_tables.php | 3 +- packages/bydauto/routes/api.php | 3 + .../src/Commands/CheryTransferCommand.php | 2 +- .../bydauto/src/Commands/GeocodingCommand.php | 8 +- .../src/Controllers/BYDAutoController.php | 92 ++++++++++++------- packages/bydauto/src/Models/Activity.php | 5 +- packages/bydauto/src/Models/Contact.php | 9 +- packages/bydauto/src/Models/Dealer.php | 6 +- packages/bydauto/src/Models/Testdrive.php | 87 +++++++++++++++++- packages/bydauto/src/Models/Vehicle.php | 4 +- .../Notifications/TestdirveNotification.php | 12 +-- 11 files changed, 176 insertions(+), 55 deletions(-) diff --git a/packages/bydauto/migrations/2024_11_13_160651_create_bydauto_tables.php b/packages/bydauto/migrations/2024_11_13_160651_create_bydauto_tables.php index 5fe2045..780a515 100644 --- a/packages/bydauto/migrations/2024_11_13_160651_create_bydauto_tables.php +++ b/packages/bydauto/migrations/2024_11_13_160651_create_bydauto_tables.php @@ -4,8 +4,7 @@ use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -return new class extends Migration -{ +return new class extends Migration { public function up(): void { Schema::create('bydauto_contact', function (Blueprint $table) { diff --git a/packages/bydauto/routes/api.php b/packages/bydauto/routes/api.php index 15252be..906101a 100644 --- a/packages/bydauto/routes/api.php +++ b/packages/bydauto/routes/api.php @@ -11,4 +11,7 @@ Route::get('province', [BYDAutoController::class, 'province']); Route::get('city', [BYDAutoController::class, 'city']); Route::post('testdrive', [BYDAutoController::class, 'testdrive']); + Route::post('isaas', [BYDAutoController::class, 'isaas']); + Route::post('cluepackage', [BYDAutoController::class, 'cluepackage']); + Route::post('notice', [BYDAutoController::class, 'notice']); }); diff --git a/packages/bydauto/src/Commands/CheryTransferCommand.php b/packages/bydauto/src/Commands/CheryTransferCommand.php index d3259cb..70ddda9 100644 --- a/packages/bydauto/src/Commands/CheryTransferCommand.php +++ b/packages/bydauto/src/Commands/CheryTransferCommand.php @@ -75,6 +75,6 @@ public function action_dispatch_by_mobile() protected function showTable($items) { $columns = ['id', 'status', 'name', 'mobile']; - $this->table($columns, $items->map(fn($item) => $item->only($columns))); + $this->table($columns, $items->map(fn ($item) => $item->only($columns))); } } diff --git a/packages/bydauto/src/Commands/GeocodingCommand.php b/packages/bydauto/src/Commands/GeocodingCommand.php index f0622e1..031d697 100644 --- a/packages/bydauto/src/Commands/GeocodingCommand.php +++ b/packages/bydauto/src/Commands/GeocodingCommand.php @@ -91,7 +91,7 @@ public function xlsx($chunk, $index) $sheet->setCellValue('J' . $sn, '09:00-18:00'); $sheet->setCellValue('K' . $sn, ''); - $vehicles = $dealer->vehicles->map(fn($v) => $v->extra_code)->join(','); + $vehicles = $dealer->vehicles->map(fn ($v) => $v->extra_code)->join(','); $vehicles = trim($vehicles, ','); $sheet->setCellValue('L' . $sn, $vehicles); @@ -159,14 +159,14 @@ public function action_csv() { $items = Dealer::query()->orderBy('id')->get(); $chunks = $items->chunk(200); - $chunks->each(fn($chunk, $index) => $this->xlsx($chunk, $index)); + $chunks->each(fn ($chunk, $index) => $this->xlsx($chunk, $index)); } public function action_xlsx() { $items = Dealer::query()->orderBy('id')->get(); $chunks = $items->chunk(200); - $chunks->each(fn($chunk, $index) => $this->xlsx($chunk, $index)); + $chunks->each(fn ($chunk, $index) => $this->xlsx($chunk, $index)); } public function action_dump() @@ -179,7 +179,7 @@ public function action_dump() public function action_show() { $items = Dealer::query()->take(4)->get(); - $this->table(['CODE', '经销商名称', '在售车型'], $items->map(fn(Dealer $d) => [$d->erp, $d->alias, $d->vehicles->map(fn($v) => $v->spec_id)->join(',')])); + $this->table(['CODE', '经销商名称', '在售车型'], $items->map(fn (Dealer $d) => [$d->erp, $d->alias, $d->vehicles->map(fn ($v) => $v->spec_id)->join(',')])); } public function action_fix() diff --git a/packages/bydauto/src/Controllers/BYDAutoController.php b/packages/bydauto/src/Controllers/BYDAutoController.php index e79650e..c50c63e 100644 --- a/packages/bydauto/src/Controllers/BYDAutoController.php +++ b/packages/bydauto/src/Controllers/BYDAutoController.php @@ -3,8 +3,8 @@ namespace BYDAuto\Controllers; use BYDAuto\Actions\TestdriveCreateAction; -use BYDAuto\Models\Dealer; -use BYDAuto\Models\Vehicle; +use BYDAuto\Foundation\Encryption; +use BYDAuto\Models\Testdrive; use Illuminate\Foundation\Validation\ValidatesRequests; use Illuminate\Http\Request; use Illuminate\Routing\Controller; @@ -29,41 +29,71 @@ public function testdrive(Request $request) return response()->json(['code' => 0, 'message' => '提交成功', 'request_id' => $requestId]); } - public function vehicle(Request $request) + // 行业云留资SPI接口 实现 + // https://vm1112app.arsk.net/pkg/bydauto/isaas + public function isaas(Request $request) { - $items = Vehicle::cachedItems(); - return response()->json($items); - } + $input = $request->all(); + info('===== bydautoController::isaas', $input); - public function province(Request $request) - { - $vehicle = Vehicle::query()->firstWhere('name', $request->vehicle); - $dealers = $vehicle->dealers; - $province = $dealers->groupBy('province')->keys(); - $data = $province->map(fn($item) => ['name' => $item]); - return response()->json($data); - } + $biz_content = $input['biz_content'] ?? ''; + throw_if(empty($biz_content), ValidationException::withMessages(['biz_content' => 'missing biz_content'])); - public function city(Request $request) - { - $vehicle = Vehicle::query()->firstWhere('name', $request->vehicle); - if (!$vehicle) { - throw ValidationException::withMessages(['vehicle' => '车型不存在']); + $key = 'f4LNNKURSfT4/p5ne4xXdW45vcUahojQDEb+dejxUq8='; + try { + $iv = str_repeat("\0", 16); + $data = openssl_decrypt(base64_decode($biz_content), 'AES-256-CBC', base64_decode($key), OPENSSL_RAW_DATA, $iv); + $data = json_decode($data, true); + Testdrive::isaas($data); + } catch (\Throwable $th) { + info('===== [biz_content decrypt fail]', [$th->getMessage()]); } - $dealers = $vehicle->dealers; - $city = $dealers->where('province', $request->province)->groupBy('city')->keys(); - $data = $city->map(fn($item) => ['name' => $item]); - return response()->json($data); + // 留咨成功 success 留咨失败 failed 重复留咨 duplicate submit 系统异常 system error + $response = ['msg' => 'success', 'code' => '10000']; + $output['response'] = json_encode($response); + $output['sign'] = $this->sign($response); + return response()->json($output); } - public function dealer(Request $request) + // 实现官方标准API 专享产品-灯火平台广告投放 + // 提供给特定广告代理商(例如 天猫),为广告主提供广告投放服务 + // https://vm1112app.arsk.net/pkg/bydauto/cluepackage + // 官方定义的标准API 行业线索数据推送 + public function cluepackage(Request $request) { - $vehicle = Vehicle::query()->firstWhere('name', $request->vehicle); - if (!$vehicle) { - throw ValidationException::withMessages(['vehicle' => '车型不存在']); - } - $dealer = Dealer::query()->where('province', $request->province)->where('city', $request->city)->get(); - $data = $dealer->map(fn($item) => ['name' => $item->name, 'address' => $item->address, 'erp' => $item->erp]); - return response()->json($data); + Testdrive::cluepackage($request->all()); + return response()->json(['response' => ['code' => '10000', 'msg' => 'Success']]); + } + + // 支付宝支付通知(应用网关)https://vm1112app.arsk.net/pkg/bydauto/notice + // 用于接收支付宝异步通知消息(例如From平台消息等),需要传入http(s)公网可访问的网页地址。选填,若不设置,则无法接收相应的异步通知消息 + public function notice(Request $request) + { + $input = $request->all(); + info('===== notice', $input); + return response(''); + } + + protected function sign($input) + { + $pubkey = file_get_contents(storage_path('app/pkg/chery-crm/isaas/app_pub_RSA2048.txt')); + $pubkey = "-----BEGIN PUBLIC KEY-----\n" . wordwrap($pubkey, 64, "\n", true) . "\n-----END PUBLIC KEY-----"; + + $privkey = file_get_contents(storage_path('app/pkg/chery-crm/isaas/app_priv_RSA2048.txt')); + $privkey = "-----BEGIN PRIVATE-----\n" . wordwrap($privkey, 64, "\n", true) . "\n-----END PRIVATE-----"; + + $encryption = new Encryption($pubkey, $privkey); + + $key = 'f4LNNKURSfT4/p5ne4xXdW45vcUahojQDEb+dejxUq8='; + ksort($input); + $input = json_encode($input, JSON_UNESCAPED_UNICODE); + $secretText = $this->encrypt($input, $key); + return $encryption->encrypt($secretText); + } + + protected function encrypt($input, $key) + { + $iv = str_repeat("\0", 16); + return base64_encode(openssl_encrypt($input, 'AES-256-CBC', base64_decode($key), OPENSSL_RAW_DATA, $iv)); } } diff --git a/packages/bydauto/src/Models/Activity.php b/packages/bydauto/src/Models/Activity.php index 8b6103e..8242025 100644 --- a/packages/bydauto/src/Models/Activity.php +++ b/packages/bydauto/src/Models/Activity.php @@ -5,7 +5,7 @@ use Illuminate\Database\Eloquent\Model; /** - * Activity + * Activity. * * @property int id * @property string name @@ -16,7 +16,10 @@ class Activity extends Model { public $timestamps = false; + protected $table = 'bydauto_activity'; + protected $fillable = ['name', 'content', 'related_id', 'related_type']; + protected $casts = ['content' => 'json']; } diff --git a/packages/bydauto/src/Models/Contact.php b/packages/bydauto/src/Models/Contact.php index 8ac4c66..c6bb70b 100644 --- a/packages/bydauto/src/Models/Contact.php +++ b/packages/bydauto/src/Models/Contact.php @@ -9,10 +9,15 @@ class Contact extends Model { use Notifiable; - const STATUS_PUBLISHED = 'published'; - const STATUS_DRAFT = 'draft'; + public const STATUS_PUBLISHED = 'published'; + + public const STATUS_DRAFT = 'draft'; + public const CREATED_AT = 'date_created'; + public const UPDATED_AT = 'date_updated'; + protected $table = 'bydauto_contact'; + protected $fillable = ['remark', 'email', 'status']; } diff --git a/packages/bydauto/src/Models/Dealer.php b/packages/bydauto/src/Models/Dealer.php index 57def0d..a878071 100644 --- a/packages/bydauto/src/Models/Dealer.php +++ b/packages/bydauto/src/Models/Dealer.php @@ -37,18 +37,18 @@ public function vehicles() public static function cachedItems() { - return Cache::remember('BYDAuto:dealers', 120, fn() => static::query()->get()); + return Cache::remember('BYDAuto:dealers', 120, fn () => static::query()->get()); } public static function findByAlias($alias) { $items = self::cachedItems(); - return $items->first(fn($d) => $d->alias === $alias); + return $items->first(fn ($d) => $d->alias === $alias); } public static function findByName($name) { $items = self::cachedItems(); - return $items->first(fn($d) => $d->name === $name); + return $items->first(fn ($d) => $d->name === $name); } } diff --git a/packages/bydauto/src/Models/Testdrive.php b/packages/bydauto/src/Models/Testdrive.php index 52b13ab..3d14b8e 100644 --- a/packages/bydauto/src/Models/Testdrive.php +++ b/packages/bydauto/src/Models/Testdrive.php @@ -2,8 +2,8 @@ namespace BYDAuto\Models; -use Carbon\Carbon; use BYDAuto\Observers\TestdriveObserver; +use Carbon\Carbon; use Illuminate\Database\Eloquent\Attributes\ObservedBy; use Illuminate\Database\Eloquent\Model; @@ -34,4 +34,89 @@ class Testdrive extends Model protected $fillable = ['status', 'name', 'mobile', 'platform', 'source', 'meta', 'rawdata', 'request_id']; protected $casts = ['meta' => 'json', 'rawdata' => 'json']; + + // 行业运SPI接口数据处理 + public static function isaas($data) + { + // { + // "alipayUserId": "2088002970810315", + // "extInfo": "", + // "formName": "预约试驾", + // "formResultBizCode": "formResult_a91a68a38acc49f1adf202ea15676909", + // "gmtCreate": "2024-09-04 17:38:48", + // "merchantAppId": "2021004170670447", + // "merchantPid": "2088511783986035", + // "propertyList": [ + // { "key": "CID_20240904111701423264", "name": "您的姓名", "outCode": "", "value": "张伊琳" }, + // { "key": "CID_20240904111701378536", "name": "联系电话", "outCode": "", "value": "13585623001" }, + // { "key": "CID_20240904111701273727", "name": "意向车型", "outCode": "QR100", "value": "探索06 都市版" }, + // { "key": "CID_20240904111701876422", "name": "选择门店", "outCode": "22486", "propertyExt": { "address": { "cityCode": "310100", "cityName": "上海城区", "provinceCode": "310000", "provinceName": "上海市" } }, "value": "上海远程汽车维修有限公司" } + // ], + // "responseClass": "com.alipay.adc.proxyopsplatform.biz.model.ecospi.response.FormDataQuerySpiResponse" + // } + + // $externalId = $data['formResultBizCode']; + // $model = static::query()->where('external_id', $externalId)->first(); + // if ($model) { + // info('===== isaas exists', $data); + // return $model; + // } + $model = new static(['source' => 'isaas', 'rawdata' => $data]); + foreach ($data['propertyList'] as $prop) { + // 手机号 + if ($prop['name'] === '联系电话') { + $model->mobile = $prop['value']; + } + // 姓名 + if ($prop['name'] === '您的姓名') { + $model->name = $prop['value']; + } + // // 意向车型 + // if ($prop['name'] === '意向车型') { + // $model->vehicle = $prop['value']; + // } + // // 门店 + // if ($prop['name'] === '选择门店') { + // $model->province = $prop['propertyExt']['address']['provinceName'] ?? ''; + // $model->city = $prop['propertyExt']['address']['cityName'] ?? ''; + // $model->dealer = $prop['value'] ?? ''; + // $model->dealer_code = $prop['outCode']; + // } + } + $model->save(); + return $model; + } + + public static function cluepackage($data) + { + // { + // "id": "5100421", + // "sign": "M9XNP7B49mldvBqGkASL/PFp/0HITi7zwShy78FzArS9H+3WuUTNewy3QIIwMhWXS5sEaePjw5BtC0A7xYO4pYP2HVCcospiOTXO5boz/Ki03xeY7OhbY1YrhI5fhYqgQOj6Efn2mM44ijtXzMh4phDMgcu0e1HcgQFTDvIgAZZ5D6IvXyLR245WGMjSDnyqtMJj87MCrTj6ZmkfiLKvGH8mSWtKd0WFS6UgfQIs/Rruw0ZQNT5+mawTT8CZE1Va3Wt0xGc6CegeNtI4f/eFNHsqgppwXO0dtKPcWRZeNvNYUqQT1G9M1epC6lvxdPD+rr8i6SEVaBkxmQVF+eN8Iw==", + // "method": "spi.alipay.data.dataservice.ad.cluepackage.send", + // "charset": "UTF-8", + // "version": "1.0", + // "clue_time": "2024-08-26 22:11:59", + // "sign_type": "RSA2", + // "user_city": "河北省", + // "user_name": "滏", + // "extend_info": "{\"brandName\":\"奇瑞\",\"merchantId\":\"23408\",\"seriesName\":\"瑞虎8L\",\"merchantName\":\"唐山荣泽\"}", + // "user_province": "唐山市", + // "utc_timestamp": "1724681530", + // "user_mobile_no": "15318418738" + // } + + // $externalId = $data['id']; + // $model = static::query()->where('external_id', $externalId)->first(); + // if ($model) { + // info('===== cluepackage exists', $data); + // return $model; + // } + $model = new static(['source' => 'cluepackage', 'rawdata' => $data]); + $model->name = $data['user_name'] ?? ''; + $model->mobile = $data['user_mobile_no'] ?? ''; + $model->meta = $data['extend_info'] ?? []; + $model->save(); + + return $model; + } } diff --git a/packages/bydauto/src/Models/Vehicle.php b/packages/bydauto/src/Models/Vehicle.php index 32c7200..39fff41 100644 --- a/packages/bydauto/src/Models/Vehicle.php +++ b/packages/bydauto/src/Models/Vehicle.php @@ -31,14 +31,14 @@ public function dealers() public static function cachedItems() { return Cache::remember('BYDAuto:vechicle', 3600, function () { - return Vehicle::query()->get()->map(fn($d) => ['id' => $d->id, 'name' => $d->name, 'alias' => $d->alias, 'spec_id' => $d->spec_id]); + return Vehicle::query()->get()->map(fn ($d) => ['id' => $d->id, 'name' => $d->name, 'alias' => $d->alias, 'spec_id' => $d->spec_id]); }); } public static function specId($name) { $items = self::cachedItems(); - $item = $items->first(fn($d) => in_array($name, $d['alias']) || $d['name'] === $name); + $item = $items->first(fn ($d) => in_array($name, $d['alias']) || $d['name'] === $name); return $item ? $item['spec_id'] : null; } } diff --git a/packages/bydauto/src/Notifications/TestdirveNotification.php b/packages/bydauto/src/Notifications/TestdirveNotification.php index ba1fbff..95610e2 100644 --- a/packages/bydauto/src/Notifications/TestdirveNotification.php +++ b/packages/bydauto/src/Notifications/TestdirveNotification.php @@ -4,18 +4,15 @@ use BYDAuto\Models\Testdrive; use Illuminate\Bus\Queueable; -use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Notification; +use Illuminate\Support\HtmlString; class TestdirveNotification extends Notification { use Queueable; - public function __construct(protected Testdrive $model) - { - // - } + public function __construct(protected Testdrive $model) {} public function via(object $notifiable): array { @@ -25,8 +22,8 @@ public function via(object $notifiable): array public function toMail(object $notifiable): MailMessage { $text = sprintf("姓名:%s
电话:%s", $this->model->name, $this->model->mobile, $this->model->mobile); - $text = new \Illuminate\Support\HtmlString($text); - return (new MailMessage) + $text = new HtmlString($text); + return (new MailMessage()) ->subject("预约试驾线索通知") ->line("收到一条新的试驾预约信息") ->line($text) @@ -37,7 +34,6 @@ public function toMail(object $notifiable): MailMessage public function toArray(object $notifiable): array { return [ - // ]; } }