Ver código fonte

Merge branch 'master' of http://git.jhcs888.com/ph/esign_serve

lihang 1 mês atrás
pai
commit
15e46d1db7

+ 1 - 0
.gitignore

@@ -10,3 +10,4 @@ Homestead.json
 Homestead.yaml
 npm-debug.log
 yarn-error.log
+.user.ini

+ 1 - 2
.idea/deployment.xml

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project version="4">
-  <component name="PublishConfigData" autoUpload="Always" serverName="zs" remoteFilesAllowedToDisappearOnAutoupload="false" confirmBeforeUploading="false">
+  <component name="PublishConfigData" serverName="zs" remoteFilesAllowedToDisappearOnAutoupload="false" confirmBeforeUploading="false">
     <option name="confirmBeforeUploading" value="false" />
     <serverData>
       <paths name="esign_serve">
@@ -24,6 +24,5 @@
         </serverdata>
       </paths>
     </serverData>
-    <option name="myAutoUpload" value="ALWAYS" />
   </component>
 </project>

+ 299 - 0
app/Console/Commands/ReplaceOssUrl.php

@@ -0,0 +1,299 @@
+<?php
+
+namespace App\Console\Commands;
+
+use Illuminate\Console\Command;
+use Illuminate\Support\Facades\DB;
+
+class ReplaceOssUrl extends Command
+{
+    protected $signature = 'oss:replace-url
+                            {--dry-run : 仅预览将要修改的数据,不实际执行更新}
+                            {--model= : 仅处理指定模型,例如:ShopModels\\\\Shop}';
+
+    protected $description = '将数据库中所有aliyuncs.com域名统一替换为https://cqzhw.oss-cn-chengdu.aliyuncs.com';
+
+    private $targetDomain = 'https://cqzhw.oss-cn-chengdu.aliyuncs.com';
+
+    private $totalUpdated = 0;
+    private $totalRecords = 0;
+    private $scannedTables = 0;
+    private $scannedColumns = 0;
+    private $hitTables = 0;
+
+    public function handle()
+    {
+        $dryRun = $this->option('dry-run');
+        $specificModel = $this->option('model');
+
+        if ($dryRun) {
+            $this->warn('=== 预览模式(--dry-run):不会实际修改数据库 ===');
+        }
+        $this->line('');
+
+        $this->info("目标域名: {$this->targetDomain}");
+        $this->info("匹配规则: 字段值包含 aliyuncs.com 即替换其域名部分");
+        $this->line('');
+
+        // ============ Step 1: 发现模型 ============
+        $this->info('━━━ Step 1: 发现模型 ━━━');
+
+        if ($specificModel) {
+            $models = $this->resolveModels([$specificModel]);
+        } else {
+            $models = $this->discoverAllModels();
+        }
+
+        if (empty($models)) {
+            $this->error('未找到任何模型,请检查 app/Models 目录');
+            return 1;
+        }
+
+        $this->info("发现 " . count($models) . " 个 Eloquent 模型");
+        $this->line('');
+
+        // ============ Step 2: 扫描每张表 ============
+        $this->info('━━━ Step 2: 扫描数据表 ━━━');
+
+        $bar = $this->output->createProgressBar(count($models));
+        $bar->setFormat('%current%/%max% [%bar%] %percent:3s%% -- %message%');
+        $bar->setMessage('开始扫描...');
+        $bar->start();
+
+        foreach ($models as $modelClass) {
+            $shortName = (new \ReflectionClass($modelClass))->getShortName();
+            $bar->setMessage($shortName);
+            $this->processModel($modelClass, $dryRun);
+            $bar->advance();
+        }
+
+        $bar->finish();
+
+        // ============ Step 3: 汇总 ============
+        $this->line('');
+        $this->line('');
+        $this->info('━━━ 扫描汇总 ━━━');
+        $this->info("模型总数: " . count($models));
+        $this->info("已扫表数: {$this->scannedTables}");
+        $this->info("已扫字段: {$this->scannedColumns}");
+        $this->info("命中表数: {$this->hitTables}");
+        $this->info("命中记录: {$this->totalRecords}");
+
+        if (!$dryRun && $this->totalUpdated > 0) {
+            $this->info("已更新记录: {$this->totalUpdated}");
+        }
+    }
+
+    private function processModel($modelClass, $dryRun)
+    {
+        try {
+            if (!class_exists($modelClass)) {
+                $this->line('');
+                $this->warn("  [跳过] 类不存在: {$modelClass}");
+                return;
+            }
+
+            /** @var \Illuminate\Database\Eloquent\Model $instance */
+            $instance = new $modelClass;
+
+            $table = $instance->getTable();
+            $connection = $instance->getConnectionName() ?: config('database.default');
+
+            // 跳过视图
+            if ($table === 'member_perfor_view') {
+                return;
+            }
+
+            // 获取该表所有文本类型字段
+            $textColumns = $this->getTextColumns($table, $connection);
+
+            if (empty($textColumns)) {
+                $this->line('');
+                $this->warn("  [{$connection}:{$table}] 无文本字段或查询失败");
+                return;
+            }
+
+            $this->scannedTables++;
+            $this->scannedColumns += count($textColumns);
+
+            $tableHit = false;
+
+            foreach ($textColumns as $column) {
+                $count = DB::connection($connection)
+                    ->table($table)
+                    ->where($column, 'like', '%aliyuncs.com%')
+                    ->count();
+
+                if ($count === 0) {
+                    continue;
+                }
+
+                if (!$tableHit) {
+                    $tableHit = true;
+                    $this->hitTables++;
+                    $this->line('');
+                }
+
+                $this->totalRecords += $count;
+                $this->info("  [{$connection}:{$table}.{$column}] 发现 {$count} 条");
+
+                if ($dryRun) {
+                    $samples = DB::connection($connection)
+                        ->table($table)
+                        ->select('id', $column)
+                        ->where($column, 'like', '%aliyuncs.com%')
+                        ->limit(3)
+                        ->get();
+
+                    foreach ($samples as $sample) {
+                        $original = $sample->$column;
+                        $replaced = $this->replaceOssDomain($original);
+                        $this->line("    #{$sample->id}");
+                        $this->line("      旧: " . mb_strcut($original, 0, 150));
+                        $this->line("      新: " . mb_strcut($replaced, 0, 150));
+                    }
+                } else {
+                    $updated = DB::connection($connection)
+                        ->table($table)
+                        ->where($column, 'like', '%aliyuncs.com%')
+                        ->update([
+                            $column => DB::raw("
+                                CASE
+                                    WHEN {$column} LIKE '%aliyuncs.com/%' THEN
+                                        CONCAT('{$this->targetDomain}/', SUBSTRING_INDEX({$column}, 'aliyuncs.com/', -1))
+                                    ELSE
+                                        '{$this->targetDomain}'
+                                END
+                            ")
+                        ]);
+                    $this->totalUpdated += $updated;
+                    $this->line("    → 已更新 {$updated} 条");
+                }
+            }
+        } catch (\Exception $e) {
+            $this->line('');
+            $this->error("  [异常] {$modelClass}: " . $e->getMessage());
+        }
+    }
+
+    /**
+     * 获取指定表的所有文本类型字段
+     *
+     * 注意:information_schema 存的是物理表名(含前缀 x_),
+     * 而 getTable() 返回的是不含前缀的逻辑表名,查询时必须补上。
+     */
+    private function getTextColumns($table, $connection)
+    {
+        try {
+            $db = DB::connection($connection);
+            $database = $db->getDatabaseName();
+            $prefix = $db->getTablePrefix();
+            $physicalTable = $prefix . $table;
+
+            $rows = $db->select(
+                "SELECT COLUMN_NAME FROM information_schema.COLUMNS
+                 WHERE TABLE_SCHEMA = ?
+                 AND TABLE_NAME = ?
+                 AND DATA_TYPE IN ('varchar', 'char', 'text', 'mediumtext', 'longtext', 'json')",
+                [$database, $physicalTable]
+            );
+
+            return array_column($rows, 'COLUMN_NAME');
+        } catch (\Exception $e) {
+            $this->line('');
+            $this->error("  [information_schema查询失败] connection={$connection} table={$table}: " . $e->getMessage());
+            return [];
+        }
+    }
+
+    /**
+     * PHP层面的域名替换(预览用)
+     */
+    private function replaceOssDomain($value)
+    {
+        if (empty($value) || !is_string($value)) {
+            return $value;
+        }
+        return preg_replace(
+            '#https?://[^/]*aliyuncs\.com#i',
+            $this->targetDomain,
+            $value
+        );
+    }
+
+    /**
+     * 解析用户指定的模型类名
+     */
+    private function resolveModels($input)
+    {
+        $models = [];
+        foreach ($input as $name) {
+            // 完整类名
+            if (strpos($name, 'App\\Models\\') === 0 && class_exists($name)) {
+                $models[] = $name;
+                continue;
+            }
+            // 相对写法 App\Models\ShopModels\Shop
+            $fullName = 'App\\Models\\' . ltrim($name, '\\');
+            if (class_exists($fullName)) {
+                $models[] = $fullName;
+                continue;
+            }
+            $this->error("模型类不存在: {$name}");
+        }
+        return $models;
+    }
+
+    /**
+     * 自动发现 app/Models 下所有 Eloquent 模型
+     */
+    private function discoverAllModels()
+    {
+        $models = [];
+        $basePath = app_path('Models');
+
+        if (!is_dir($basePath)) {
+            $this->error("目录不存在: {$basePath}");
+            return $models;
+        }
+
+        $this->line("  扫描目录: {$basePath}");
+
+        $iterator = new \RecursiveIteratorIterator(
+            new \RecursiveDirectoryIterator($basePath, \RecursiveDirectoryIterator::SKIP_DOTS)
+        );
+
+        $fileCount = 0;
+        foreach ($iterator as $file) {
+            if ($file->getExtension() !== 'php') {
+                continue;
+            }
+
+            $fileCount++;
+            $pathname = $file->getPathname();
+            // 统一处理 Windows / Linux 路径分隔符
+            $relative = str_replace([$basePath . DIRECTORY_SEPARATOR, $basePath . '/', $basePath . '\\'], '', $pathname);
+            $relative = str_replace(['/', '\\', '.php'], ['\\', '\\', ''], $relative);
+            $class = 'App\\Models\\' . $relative;
+
+            if (class_exists($class) && is_subclass_of($class, \Illuminate\Database\Eloquent\Model::class)) {
+                $models[] = $class;
+            }
+        }
+
+        $this->line("  扫描到 {$fileCount} 个 PHP 文件,其中 " . count($models) . " 个是 Eloquent 模型");
+
+        // 打印前5个模型作为样例
+        $samples = array_slice($models, 0, 5);
+        foreach ($samples as $m) {
+            $instance = new $m;
+            $conn = $instance->getConnectionName() ?: config('database.default');
+            $this->line("    - {$m}  [{$conn}:{$instance->getTable()}]");
+        }
+        if (count($models) > 5) {
+            $this->line("    ... 还有 " . (count($models) - 5) . " 个");
+        }
+
+        return $models;
+    }
+}

+ 1 - 0
app/Http/Controllers/AdminApi/ContractController.php

@@ -21,6 +21,7 @@ class ContractController extends AdminController
 
         $list = Contract::where($where)
             ->select(['id','name','created_at'])
+            ->orderBy('id','desc')
             ->paginate(10);
         return $this->apiResponseSuccess('获取列表成功', [
             'items' => $list->items(),

+ 2 - 1
app/Http/Controllers/AdminApi/FirmController.php

@@ -19,7 +19,8 @@ class FirmController extends AdminController
         $where = [['is_del',0]];
 
         $list = Firm::where($where)
-            ->select(['id','firm_name','license_number','legal_name','orgId','realnameStatus','authorizeUserInfo','created_at'])
+            ->select(['id','firm_name','license_number','legal_name','orgId','authUrl','authShortUrl','realnameStatus','authorizeUserInfo','created_at'])
+            ->orderBy('id','desc')
             ->paginate(10);
         return $this->apiResponseSuccess('获取列表成功', [
             'items' => $list->items(),

+ 2 - 1
app/Http/Controllers/AdminApi/MemberController.php

@@ -19,7 +19,8 @@ class MemberController extends AdminController
         $where = [['is_del',0]];
 
         $list = Member::where($where)
-            ->select(['id','name','phone','id_number','psnId','realnameStatus','authorizeUserInfo','created_at'])
+            ->select(['id','name','phone','id_number','psnId','authUrl','authShortUrl','realnameStatus','authorizeUserInfo','created_at'])
+            ->orderBy('id','desc')
             ->paginate(10);
         return $this->apiResponseSuccess('获取列表成功', [
             'items' => $list->items(),

+ 1 - 1
app/Http/Controllers/AdminApi/NotifyController.php

@@ -84,7 +84,7 @@ class NotifyController extends ApiController
             if (!empty($signFlowId)){
                 $sign = SignContract::where('flow_id',$signFlowId)->first();
                 $sign_id = $sign->{'id'};
-                if (!empty($operator)){
+                if (!empty($operator) && empty($organization)){//个人签署回调没有$organization信息
                     $psnId = $operator['psnId'];
 //                    SignRecord::where('sign_id',$sign_id)->where('authType','PSN')->where('id_no',$psnId)->update(['status'=>$signFlowStatus]);
                     SignRecord::where('sign_id',$sign_id)->where('authType','PSN')->update(['status'=>$signFlowStatus]);

+ 33 - 0
app/Http/Controllers/AdminApi/SignContractController.php

@@ -7,6 +7,7 @@ use App\Models\Contract;
 use App\Models\Firm;
 use App\Models\Member;
 use App\Models\SignContract;
+use App\Models\SignRecord;
 use App\Servers\ESign\ESignContractServer;
 use App\Servers\Wps\WordServer;
 
@@ -29,6 +30,7 @@ class SignContractController extends AdminController
 
         $list = Contract::where($where)
             ->select(['id','name','texts','sign_party'])
+            ->orderBy('id','desc')
             ->paginate(10);
         return $this->apiResponseSuccess('获取列表成功', [
             'items' => $list->items(),
@@ -52,6 +54,7 @@ class SignContractController extends AdminController
 
         $list = Firm::where($where)
             ->select(['id','firm_name','license_number','legal_name','authType','orgId','realnameStatus','authorizeUserInfo','created_at'])
+            ->orderBy('id','desc')
             ->paginate(10);
         return $this->apiResponseSuccess('获取列表成功', [
             'items' => $list->items(),
@@ -74,6 +77,7 @@ class SignContractController extends AdminController
 
         $list = Member::where($where)
             ->select(['id','name','phone','id_number','authType','psnId','realnameStatus','authorizeUserInfo','created_at'])
+            ->orderBy('id','desc')
             ->paginate(10);
         return $this->apiResponseSuccess('获取列表成功', [
             'items' => $list->items(),
@@ -97,10 +101,39 @@ class SignContractController extends AdminController
 
         $list = SignContract::where($where)
             ->select(['id','contract_no','status','contract_id','sign_url','created_at'])
+            ->orderBy('id','desc')
             ->paginate(10);
 
         foreach ($list as $key => $value) {
             $value->{'contract_name'} = Contract::where('id',$value->{'contract_id'})->value('name');
+            //获取用户签署链接
+            $psnInfo = SignRecord::where('sign_id',$value->{'id'})->where('authType','PSN')->select(['status','sign_url','id_no'])->first();
+            if (!empty($psnInfo)){
+                $psnInfo->{'name'} = Member::where('psnId',$psnInfo->{'id_no'})->value('name');
+            }else{
+                $psnInfo = [
+                    'status' => 1,
+                    'sign_url' => '',
+                    'id_no' => '',
+                    'name' => '',
+                ];
+            }
+            $psnInfo->initializeModel();
+            //获取企业签署链接
+            $orgInfo = SignRecord::where('sign_id',$value->{'id'})->where('authType','ORG')->select(['status','sign_url','id_no'])->first();
+            if (!empty($orgInfo)){
+                $orgInfo->{'name'} = Firm::where('orgId',$orgInfo->{'id_no'})->value('firm_name');
+            }else{
+                $orgInfo = [
+                    'status' => 1,
+                    'sign_url' => '',
+                    'id_no' => '',
+                    'name' => '',
+                ];
+            }
+            $orgInfo->initializeModel();
+            $value->{'psnInfo'} = $psnInfo;
+            $value->{'orgInfo'} = $orgInfo;
             $value->initializeModel();
         }
 

+ 20 - 0
app/Models/SignRecord.php

@@ -25,6 +25,26 @@ class SignRecord extends Model
     protected $table = 'sign_records';
 
 
+    private $statusEnum=[
+        0=>'文件生成中',
+        1=>'待签署',
+        2=>'已签署',
+        3=>'已撤销',
+        4=>'已拒签',
+        5=>'已过期',
+        7=>'已拒签',
+        10=>'签署失败',
+    ];
+
+    /**
+     * 序列化数据字典
+     */
+    function initializeModel(){
+        if($this->{'status'}!==null){
+            $this->{'status_str'}=empty($this->statusEnum[$this->{'status'}])?'无':$this->statusEnum[$this->{'status'}];
+        }
+    }
+
     /**
      * 为数组 / JSON 序列化准备日期。
      *

+ 3 - 0
app/Servers/ESign/ESignContractServer.php

@@ -195,6 +195,7 @@ class ESignContractServer
                     'promissoryName'=>$member['name'],
                     'psnAccount'=>$member['phone'],
                     'orgName'=>'',
+                    'id_no'=>$member['psnId'],
                 ];
                 //获取签章
                 $m_id = $member->{'id'};
@@ -238,6 +239,7 @@ class ESignContractServer
                     'promissoryName'=>$firm['legal_name'],
                     'psnAccount'=>$firm['phone'],
                     'orgName'=>$firm['firm_name'],
+                    'id_no'=>$firm['orgId'],
                 ];
                 //获取用户签章
                 $firm_id = $firm->{'id'};
@@ -315,6 +317,7 @@ class ESignContractServer
                     $content='尊敬的'.$item['promissoryName'].',您有待签署合同《'.$file_name.'》,点击签署 '.$zh_url.'。拒收请回复R';
                     SmsServer::creatServer()->sendContent($item['psnAccount'],$content);
                     SignContract::where('id',$sign_id)->update(['sign_url'=>$urlRet]);
+                    SignRecord::where('sign_id',$sign_id)->where('id_no',$item['id_no'])->update(['sign_url'=>$urlRet]);
                 }
             }
 

+ 1 - 0
app/Servers/ESign/ESignFirmServer.php

@@ -208,6 +208,7 @@ class ESignFirmServer
                 'zh_url'=>$zh_url,
                 'authFlowId'=>$authFlowId,
             ]);
+
             return true;
         }else{//认证授权错误
             return false;

+ 6 - 2
app/Servers/ESign/ESignServer.php

@@ -13,6 +13,7 @@ use App\Servers\ESign\emun\HttpEmun;
 use App\Servers\System\JobServer;
 use App\Servers\System\RedisDataServer;
 use App\Servers\System\SmsServer;
+use Illuminate\Support\Facades\Log;
 
 class ESignServer
 {
@@ -96,9 +97,10 @@ class ESignServer
         $signAndBuildSignAndJsonHeader = EsignHttpHelper::signAndBuildSignAndJsonHeader($config['eSignAppId'], $config['eSignAppSecret'], $paramStr, $requestType, $apiaddr);
         //发起接口请求
         $response = EsignHttpHelper::doCommHttp($config['eSignHost'], $apiaddr, $requestType, $signAndBuildSignAndJsonHeader, $paramStr);
+//        dd($response->getBody());
         $ret=json_decode($response->getBody(),true);
 
-
+        Log::channel('daily')->info('个人实名认证授权页面链接:',$ret);
         if(isset($ret['code']) && $ret['code']==0){
             $authShortUrl=RedisDataServer::creatServer()->getData($m_id.'_memberUrl');
             if($authShortUrl){
@@ -127,7 +129,6 @@ class ESignServer
                     'authFlowId'=>$authFlowId,
                 ]);
             }
-
             return true;
         }else{//认证授权错误
             return false;
@@ -154,7 +155,10 @@ class ESignServer
         $signAndBuildSignAndJsonHeader = EsignHttpHelper::signAndBuildSignAndJsonHeader($config['eSignAppId'], $config['eSignAppSecret'], "", $requestType, $apiaddr);
         //发起接口请求
         $response = EsignHttpHelper::doCommHttp($config['eSignHost'], $apiaddr, $requestType, $signAndBuildSignAndJsonHeader, "");
+
         $ret=json_decode($response->getBody(),true);
+
+        Log::channel('daily')->info('查询个人实名认证:',$ret);
         if(isset($ret['code']) && $ret['code']==0){
             //realnameStatus 0 - 未实名,1 - 已实名
             $realnameStatus = $ret['data']['realnameStatus'];

+ 1 - 0
app/Servers/System/SmsServer.php

@@ -109,6 +109,7 @@ class SmsServer
             $this->errorMsg = '短信发送失败';
             return false;
         }
+
         return true;
     }