visuddhinanda 2 лет назад
Родитель
Сommit
f08c6e3407

+ 48 - 0
patch/db_fix/.vscode/launch.json

@@ -0,0 +1,48 @@
+{
+    // Use IntelliSense to learn about possible attributes.
+    // Hover to view descriptions of existing attributes.
+    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "name": "Listen for Xdebug",
+            "type": "php",
+            "request": "launch",
+            "port": 9000
+        },
+        {
+            "name": "Launch currently open script",
+            "type": "php",
+            "request": "launch",
+            "program": "${file}",
+            "cwd": "${fileDirname}",
+            "port": 0,
+            "runtimeArgs": [
+                "-dxdebug.start_with_request=yes"
+            ],
+            "env": {
+                "XDEBUG_MODE": "debug,develop",
+                "XDEBUG_CONFIG": "client_port=${port}"
+            }
+        },
+        {
+            "name": "Launch Built-in web server",
+            "type": "php",
+            "request": "launch",
+            "runtimeArgs": [
+                "-dxdebug.mode=debug",
+                "-dxdebug.start_with_request=yes",
+                "-S",
+                "localhost:0"
+            ],
+            "program": "",
+            "cwd": "${workspaceRoot}",
+            "port": 9000,
+            "serverReadyAction": {
+                "pattern": "Development Server \\(http://localhost:([0-9]+)\\) started",
+                "uriFormat": "http://localhost:%s",
+                "action": "openExternally"
+            }
+        }
+    ]
+}

+ 112 - 0
patch/db_fix/compare.php

@@ -0,0 +1,112 @@
+<?php
+
+require_once "tables.php";
+require_once "config.php";
+require_once "function.php";
+
+if(php_sapi_name() !== "cli") {
+    echo 'no cli';
+    return;
+}
+
+if(count($argv)<3){
+    echo 'expect 2 db '.(count($argv)-1).' gave';
+    return;
+}
+$src_db = $argv[1];
+$dest_db = $argv[2];
+
+#打开源数据库
+$PDO_SRC = openDb($config[$src_db]);
+$PDO_SRC->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+
+#打开目标数据库
+$PDO_DEST = openDb($config[$dest_db]);
+$PDO_DEST->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+
+foreach ($tables as $tableName => $table) {
+    //A -> B 差异
+    fwrite(STDOUT,$tableName.PHP_EOL);
+    if($table['user'] === false){
+        fwrite(STDOUT,'not user data ignore'.PHP_EOL);
+        continue;
+    }
+    $keys = array();
+    if(is_array($table['key'])){
+        $keys = $table['key'];
+    }else{
+        $keys[] = $table['key'];
+    }
+    $select = $keys;
+    if(!empty($table['time1'])){
+        $select[] = $table['time1'];
+    }
+    if(!empty($table['time2'])){
+        $select[] = $table['time2'];
+    }
+    $query = "SELECT * FROM {$tableName} ";
+    $stmtSrc = $PDO_SRC->prepare($query);
+    $stmtSrc->execute();
+    $count = 0;
+
+    $where = [];
+    foreach ($keys as $value) {
+        $where[] = "{$value} = ? ";
+    }
+    $query = "SELECT ".implode(',',$select)." FROM {$tableName} where ". implode(' and ',$where);
+    $stmtDest = $PDO_DEST->prepare($query);
+    $countUpdate = 0;
+    $countDown = 0;
+    $countNew = 0;
+    while($srcData = $stmtSrc->fetch(PDO::FETCH_ASSOC)){
+        $count++;
+        if($count % 1000 === 0){
+            fwrite(STDOUT,$count.PHP_EOL);
+        }
+        $param  = [];
+        foreach ($keys as $value) {
+            $param[] = $srcData[$value];
+        }
+        $stmtDest->execute($param);
+        $row = $stmtDest->fetch(PDO::FETCH_ASSOC);
+        $realKey = implode(' and ',$where).' = '.implode(',',$param);
+        if($row) {
+            //时间
+            $t1a=0;
+            $t1b=0;
+            $t2a=0;
+            $t2b=0;
+            $ta=0;
+            $tb=0;
+            if(!empty($table['time1'])){
+                $t1a=$srcData[$table['time1']]/1000;
+                $t1b=$row[$table['time1']]/1000;
+            }
+            if(!empty($table['time2'])){
+                $t2a=strtotime($srcData[$table['time2']]);
+                $t2b=strtotime($row[$table['time2']]);
+            }
+            //两个时间戳,取较新的。
+            $ta = $t1a > $t2a? $t1a:$t2a;
+            $tb = $t1b > $t2b? $t1b:$t2b;
+
+            if($ta > $tb ){
+                fwrite(STDOUT,$tableName.' update '.$realKey.PHP_EOL);
+                fwrite(STDOUT,' update '.$row[$table['time2']].' -> '.$srcData[$table['time2']].PHP_EOL);
+                $countUpdate++;
+            }else if($ta < $tb){
+                fwrite(STDOUT,$tableName.' down date '.$realKey.PHP_EOL);
+                $countDown++;
+            }
+        }else{
+            //缺失
+            fwrite(STDERR,$tableName.' new '.$realKey.PHP_EOL);
+            $countNew++;
+        }
+    }
+    fwrite(STDOUT,"table={$tableName}".PHP_EOL);
+    fwrite(STDOUT,"Update={$countUpdate}".PHP_EOL);
+    fwrite(STDOUT,"Down={$countDown}".PHP_EOL);
+    fwrite(STDOUT,"New={$countNew}".PHP_EOL);
+    fwrite(STDOUT,PHP_EOL);
+}

+ 67 - 0
patch/db_fix/config.php

@@ -0,0 +1,67 @@
+<?php
+
+/*
+数据库设置
+*/
+
+$config = [
+	'db_a' => [
+		'driver' => 'pgsql',
+		'host' => 'localhost',
+		'port' => 5432,
+		'database' => 'wikipali',
+		'username' => 'postgres',
+		'password' => '123456',
+	],
+	'db_b' => [
+		'driver' => 'pgsql',
+		'host' => 'localhost',
+		'port' => 5432,
+		'database' => 'wikipali',
+		'username' => 'postgres',
+		'password' => '123456',
+	],
+	'db_c' => [
+		'driver' => 'pgsql',
+		'host' => 'localhost',
+		'port' => 5432,
+		'database' => 'wikipali',
+		'username' => 'postgres',
+		'password' => '123456',
+	],
+	'db_d' => [
+		'driver' => 'pgsql',
+		'host' => 'localhost',
+		'port' => 5432,
+		'database' => 'wikipali',
+		'username' => 'postgres',
+		'password' => '123456',
+	],
+];
+
+define("DB_A", 'pgsql:'.
+				"host=localhost;".
+				"port=5432;".
+				"dbname=wikipali;".
+				"user=postgres;".
+				"password=123456;");
+
+define("DB_B", 'pgsql:'.
+				"host=localhost;".
+				"port=5432;".
+				"dbname=wikipali;".
+				"user=postgres;".
+				"password=123456;");
+
+define("DB_C", 'pgsql:'.
+				"host=localhost;".
+				"port=5432;".
+				"dbname=wikipali;".
+				"user=postgres;".
+				"password=123456;");
+define("DB_D", 'pgsql:'.
+				"host=localhost;".
+				"port=5432;".
+				"dbname=wikipali;".
+				"user=postgres;".
+				"password=123456;");

+ 17 - 0
patch/db_fix/function.php

@@ -0,0 +1,17 @@
+<?php
+
+function dbInfo($db){
+    return $db['driver'].':'.
+    "host={$db['host']};".
+    "port={$db['port']};".
+    "dbname={$db['database']};".
+    "user={$db['username']};".
+    "password={$db['password']};";
+}
+
+function openDb($dbInfo){
+    return new PDO(dbInfo($dbInfo),
+                $dbInfo['username'],
+                $dbInfo['password'],
+                array(PDO::ATTR_PERSISTENT=>true));
+}

+ 1302 - 0
patch/db_fix/tables.php

@@ -0,0 +1,1302 @@
+<?php
+
+$tables=[
+    'article_collections' => [
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>true,
+        'fields'=>[
+                'id',
+                'collect_id',
+                'article_id',
+                'level',
+                'title',
+                'children',
+                'editor_id',
+                'created_at',
+                'updated_at',
+                'deleted_at',
+        ],
+    ],
+
+    'articles' =>[
+        'key'=>'uid',
+        'time1'=>'modify_time',
+        'time2'=>'updated_at',
+        'user'=>true,
+        'fields'=>[
+            'id',
+            'uid' ,
+            'parent_id' ,
+            'default_channel_id' ,
+            'title' ,
+            'subtitle' ,
+            'summary' ,
+            'cover' ,
+            'content' ,
+            'content_type' ,
+            'owner' ,
+            'owner_id',
+            'editor_id',
+            'setting' ,
+            'status' ,
+            'lang' ,
+            'create_time',
+            'modify_time',
+            'created_at' ,
+            'updated_at' ,
+            'deleted_at' ,
+        ],
+    ],
+
+    'attachments' =>[
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>true,
+        'fields'=>[
+            'id' ,
+            'user_uid' ,
+            'bucket' ,
+            'name' ,
+            'title' ,
+            'size',
+            'content_type' ,
+            'status' ,
+            'version' ,
+            'deleted_at' ,
+            'created_at' ,
+            'updated_at' ,
+        ]
+    ],
+
+    'bolds' =>[
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'created_at',
+        'user'=>false,
+        'fields'=>[
+            'id',
+            'book' ,
+            'paragraph' ,
+            'word'  ,
+            'word2'  ,
+            'word_en'  ,
+            'pali'  ,
+            'base'  ,
+            'created_at' ,
+        ],
+    ],
+
+    'book_titles' =>[
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>false,
+        'fields'=>[
+            'id',
+            'book' ,
+            'paragraph' ,
+            'title'  ,
+            'created_at' ,
+            'updated_at' ,
+            'sn' 
+        ],
+    ],
+
+    'book_words' =>[
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>false,
+        'fields'=>[
+            'id',
+            'book' ,
+            'wordindex' ,
+            'count' ,
+            'created_at' ,
+            'updated_at' 
+        ]
+    ],
+
+    'channels' =>[
+        'key'=>'uid',
+        'time1'=>'modify_time',
+        'time2'=>'updated_at',
+        'user'=>true,
+        'fields'=>[
+            'id',
+            'type'  ,
+            'owner_uid' ,
+            'editor_id',
+            'name' ,
+            'summary' ,
+            'lang' ,
+            'status' ,
+            'setting' ,
+            'create_time',
+            'modify_time',
+            'created_at' ,
+            'updated_at' ,
+            'deleted_at' ,
+            'uid'  ,
+            'config' ,
+        ]
+    ],
+
+    'collections' =>[
+        'key'=>'uid',
+        'time1'=>'modify_time',
+        'time2'=>'updated_at',
+        'user'=>true,
+        'fields'=>[
+            'id',
+            'uid' ,
+            'parent_id' ,
+            'default_channel_id' ,
+            'title' ,
+            'subtitle' ,
+            'summary' ,
+            'cover' ,
+            'article_list' ,
+            'owner' ,
+            'owner_id',
+            'editor_id',
+            'setting' ,
+            'status' ,
+            'lang' ,
+            'create_time',
+            'modify_time',
+            'created_at' ,
+            'updated_at' ,
+            'deleted_at' 
+        ]
+    ],
+
+    'course_members' =>[
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>true,
+        'fields'=>[
+            'id'  ,
+            'user_id' ,
+            'role'  ,
+            'course_id' ,
+            'channel_id' ,
+            'status'   ,
+            'created_at' ,
+            'updated_at'         
+        ]
+    ],
+
+    'courses' =>[
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>true,
+        'fields'=>[
+            'id'  ,
+            'title'  ,
+            'subtitle' ,
+            'cover' ,
+            'content' ,
+            'content_type' ,
+            'teacher' ,
+            'start_at' ,
+            'end_at' ,
+            'studio_id' ,
+            'created_at' ,
+            'updated_at' ,
+            'anthology_id' ,
+            'publicity' ,
+            'channel_id' ,
+            "join"  ,
+            'request_exp' ,
+            'summary' ,
+        ]
+    ],
+
+
+    'custom_book_ids' =>[
+        'key'=>'key',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>true,
+        'fields'=>[
+            'id',
+            'key'   ,
+            'value' ,
+            'created_at' ,
+            'updated_at'         
+        ]
+    ],
+
+
+    'custom_book_sentences' =>[
+        'key'=>'id',
+        'time1'=>'modify_time',
+        'time2'=>'updated_at',
+        'user'=>true,
+        'fields'=>[
+            'id',
+            'book' ,
+            'paragraph' ,
+            'word_start' ,
+            'word_end' ,
+            'content'  ,
+            'content_type' ,
+            'length' ,
+            'owner' ,
+            'lang' ,
+            'status' ,
+            'create_time',
+            'modify_time',
+            'created_at' ,
+            'updated_at' ,        
+        ]
+    ],
+
+    'custom_books' =>[
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>true,
+        'fields'=>[
+            'id',
+            'book_id' ,
+            'title' ,
+            'owner' ,
+            'editor_id',
+            'lang' ,
+            'status' ,
+            'created_at' ,
+            'updated_at'         
+        ]
+    ],
+
+    'dhamma_terms' =>[
+        'key'=>'guid',
+        'time1'=>'modify_time',
+        'time2'=>'updated_at',
+        'user'=>true,
+        'fields'=>[
+            'id',
+            'guid' ,
+            'word'  ,
+            'word_en'  ,
+            'meaning'  ,
+            'other_meaning' ,
+            'note' ,
+            'tag' ,
+            'channal' ,
+            'language'  ,
+            'owner' ,
+            'editor_id',
+            'create_time',
+            'modify_time',
+            'created_at' ,
+            'updated_at' ,
+            'deleted_at'         
+        ]
+    ],
+
+    'dict_infos' =>[
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>false,
+        'fields'=>[
+            'id'  ,
+            'name'  ,
+            'shortname' ,
+            'description' ,
+            'src_lang'  ,
+            'dest_lang'  ,
+            'rows' ,
+            'owner_id' ,
+            'meta'  ,
+            'created_at' ,
+            'updated_at'         
+        ]
+    ],
+
+    'discussions' =>[
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>true,
+        'fields'=>[
+            'id'  ,
+            'res_id' ,
+            'res_type'  ,
+            'parent' ,
+            'title' ,
+            'content' ,
+            'children_count' ,
+            'editor_uid' ,
+            'publicity'   ,
+            'created_at' ,
+            'updated_at' ,
+            'content_type' ,
+            'status'  ,
+            'tpl_id' ,        
+        ]
+    ],
+
+    'file_indices' =>[
+        'key'=>'uid',
+        'time1'=>'modify_time',
+        'time2'=>'updated_at',
+        'user'=>true,
+        'fields'=>[
+            'id',
+            'uid' ,
+            'parent_id' ,
+            'user_id',
+            'book' ,
+            'paragraph' ,
+            'channal' ,
+            'file_name' ,
+            'title' ,
+            'tag' ,
+            'status' ,
+            'file_size' ,
+            'share' ,
+            'doc_info'  ,
+            'doc_block'  ,
+            'create_time',
+            'modify_time',
+            'accese_time',
+            'created_at' ,
+            'accesed_at' ,
+            'updated_at' ,
+            'deleted_at'         
+        ]
+    ],
+
+    'fts_texts' =>[
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>false,
+        'fields'=>[
+            'id',
+            'book' ,
+            'paragraph' ,
+            'wid'  ,
+            'bold_single'  ,
+            'bold_double'  ,
+            'bold_multiple'  ,
+            'content'  ,
+            'created_at' ,
+            'updated_at' ,
+            'pcd_book_id' ,        
+        ]
+    ],
+
+    'group_infos' =>[
+        'key'=>'uid',
+        'time1'=>'modify_time',
+        'time2'=>'updated_at',
+        'user'=>true,
+        'fields'=>[
+            'id',
+            'uid' ,
+            'name'  ,
+            'description' ,
+            'status'  ,
+            'owner' ,
+            'create_time',
+            'modify_time',
+            'created_at' ,
+            'updated_at'         
+        ] 
+    ],
+
+    'group_members' =>[
+        'key'=>['user_id','group_id'],
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>true,
+        'fields'=>[
+            'id',
+            'user_id' ,
+            'group_id' ,
+            'group_name' ,
+            'power'  ,
+            'level' ,
+            'status'  ,
+            'created_at' ,
+            'updated_at'         
+        ]
+    ],
+
+    'invites' =>[
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>true,
+        'fields'=>[
+            'id' ,
+            'user_uid' ,
+            'email'  ,
+            'status' ,
+            'created_at' ,
+            'updated_at'         
+        ]
+    ],
+
+    'likes' =>[
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>true,
+        'fields'=>[
+            'id'  ,
+            'type'  ,
+            'target_id' ,
+            'target_type'  ,
+            'context' ,
+            'user_id' ,
+            'created_at' ,
+            'updated_at'         
+        ]   
+    ],
+
+    'nissaya_endings' =>[
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>true,
+        'fields'=>[
+            'id'  ,
+            'ending'  ,
+            'lang' ,
+            'relation' ,
+            "case" ,
+            'strlen' ,
+            'count' ,
+            'editor_id' ,
+            'created_at' ,
+            'updated_at' ,
+            "from"         
+        ]
+    ],
+
+
+    'page_numbers' =>[
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>false,
+        'fields'=>[
+            'id',
+            'type' ,
+            'volume' ,
+            'page' ,
+            'book' ,
+            'paragraph' ,
+            'wid' ,
+            'pcd_book_id' ,
+            'created_at' ,
+            'updated_at'         
+        ]
+    ],
+
+    'pali_sent_indices' =>[
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>false,
+        'fields'=>[ 
+            'id',
+            'book' ,
+            'para' ,
+            'strlen' ,
+            'created_at' ,
+            'updated_at'         
+        ]
+    ],
+
+    'pali_sent_orgs' =>[
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>false,
+        'fields'=>[
+            'id' ,
+            'book' ,
+            'paragraph' ,
+            'word_begin' ,
+            'word_end' ,
+            'length' ,
+            'count' ,
+            'text'  ,
+            'html'  ,
+            'merge'  ,
+            'sim_sents' ,
+            'sim_sents_count'  ,
+            'created_at' ,
+            'updated_at' 
+        ]
+    ],
+
+
+    'pali_sentences' =>[
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>false,
+        'fields'=>[
+            'id' ,
+            'book' ,
+            'paragraph' ,
+            'word_begin' ,
+            'word_end' ,
+            'length' ,
+            'count' ,
+            'text'  ,
+            'html'  ,
+            'sim_sents' ,
+            'sim_sents_count'  ,
+            'created_at' ,
+            'updated_at' 
+        ]
+    ],
+
+    'pali_texts' =>[
+        'key'=>'uid',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>false,
+        'fields'=>[
+            'id',
+            'book' ,
+            'paragraph' ,
+            'level' ,
+            'class' ,
+            'toc'  ,
+            'text'  ,
+            'html'  ,
+            'lenght' ,
+            'album_index' ,
+            'chapter_len' ,
+            'next_chapter' ,
+            'prev_chapter' ,
+            'parent' ,
+            'chapter_strlen' ,
+            'created_at' ,
+            'updated_at' ,
+            'path' ,
+            'uid'  ,
+            'title_en' ,
+            'title' ,
+            'pcd_book_id'         
+        ]
+    ],
+
+    'progress' =>[
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>false,
+        'fields'=>[
+            'id',
+            'book' ,
+            'para' ,
+            'lang' ,
+            'all_strlen' ,
+            'public_strlen' ,
+            'created_at' ,
+            'updated_at' ,
+            'channel_id'         
+        ]
+    ],
+
+    'progress_chapters' =>[
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>false,
+        'fields'=>[
+            'id',
+            'book' ,
+            'para' ,
+            'lang' ,
+            'all_trans'  ,
+            'public'  ,
+            'created_at' ,
+            'updated_at' ,
+            'channel_id' ,
+            'progress'  ,
+            'title' ,
+            'uid'  ,
+            'summary'         
+        ]
+    ],
+
+
+
+    'recents' =>[
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>true,
+        'fields'=>[
+            'id' ,
+            'type' ,
+            'article_id' ,
+            'param' ,
+            'user_uid' ,
+            'created_at' ,
+            'updated_at'
+        ]
+    ],
+
+
+    'related_paragraphs' =>[
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>false,
+        'fields'=>[
+            'id',
+            'book' ,
+            'para' ,
+            'book_id' ,
+            'cs_para' ,
+            'book_name'  ,
+            'created_at' ,
+            'updated_at'         
+        ]
+    ],
+
+
+
+    'relations'=>[
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>true,
+        'fields'=>[
+            'id'  ,
+            'name'  ,
+            "case" ,
+            "to" ,
+            'editor_id' ,
+            'created_at' ,
+            'updated_at' ,
+            "from" ,
+            'category' ,
+            'match'         
+        ]
+    ],
+
+    'res_indices' =>[
+        'key'=>'id',
+        'time1'=>'update_time',
+        'time2'=>'updated_at',
+        'user'=>false,
+        'fields'=>[
+            'id',
+            'book' ,
+            'paragraph' ,
+            'title'  ,
+            'title_en' ,
+            'level' ,
+            'type' ,
+            'language' ,
+            'author' ,
+            'editor' ,
+            'share'  ,
+            'edition'  ,
+            'hit' ,
+            'album' ,
+            'tag' ,
+            'summary' ,
+            'create_time',
+            'update_time',
+            'created_at' ,
+            'updated_at'         
+        ]
+    ],
+
+
+    'sent_blocks' =>[
+        'key'=>'id',
+        'time1'=>'modify_time',
+        'time2'=>'updated_at',
+        'user'=>true,
+        'fields'=>[
+            'id',
+            'uid' ,
+            'parent_uid' ,
+            'book_id' ,
+            'paragraph' ,
+            'owner_uid' ,
+            'editor_uid' ,
+            'lang' ,
+            'author'  ,
+            'status' ,
+            'create_time',
+            'modify_time',
+            'created_at' ,
+            'updated_at' ,
+            'deleted_at'         
+        ]
+    ],
+
+
+    'sent_histories' =>[
+        'key'=>'id',
+        'time1'=>'create_time',
+        'time2'=>'created_at',
+        'user'=>true,
+        'fields'=>[
+            'id',
+            'sent_uid' ,
+            'user_uid' ,
+            'content'  ,
+            'landmark' ,
+            'create_time',
+            'created_at'         
+        ]
+    ],
+
+    'sent_prs' => [
+        'key'=>'id',
+        'time1'=>'modify_time',
+        'time2'=>'updated_at',
+        'user'=>true,
+        'fields'=>[
+            'id',
+            'old_id' ,
+            'book_id' ,
+            'paragraph' ,
+            'word_start' ,
+            'word_end' ,
+            'channel_uid' ,
+            'author' ,
+            'editor_uid' ,
+            'content' ,
+            'language' ,
+            'status' ,
+            'strlen' ,
+            'create_time',
+            'modify_time',
+            'created_at' ,
+            'updated_at' ,
+            'deleted_at'
+        ]
+    ],
+
+
+    'sent_sim_indices' =>[
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>false,
+        'fields'=>[
+            'id',
+            'sent_id' ,
+            'count' ,
+            'created_at' ,
+            'updated_at'         
+        ]
+    ],
+
+    'sent_sims' =>[
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>false,
+        'fields'=>[
+            'id',
+            'sent1' ,
+            'sent2' ,
+            'sim'  ,
+            'created_at' ,
+            'updated_at'         
+        ]
+    ],
+
+    'sentences' =>[
+        'key'=>'id',
+        'time1'=>'modify_time',
+        'time2'=>'updated_at',
+        'user'=>true,
+        'fields'=>[
+            'id',
+            'uid' ,
+            'parent_uid' ,
+            'block_uid' ,
+            'channel_uid' ,
+            'book_id' ,
+            'paragraph' ,
+            'word_start' ,
+            'word_end' ,
+            'author' ,
+            'editor_uid' ,
+            'content' ,
+            'content_type' ,
+            'language' ,
+            'version'  ,
+            'strlen' ,
+            'status' ,
+            'create_time',
+            'modify_time',
+            'created_at' ,
+            'updated_at' ,
+            'deleted_at' ,
+            'pr_edit_at' ,
+            'acceptor_uid' ,
+            'pr_id' 
+        ]
+    ],
+
+
+    'shares' =>[
+        'key'=>'id',
+        'time1'=>'modify_time',
+        'time2'=>'updated_at',
+        'user'=>true,
+        'fields'=>[
+            'id',
+            'res_id' ,
+            'res_type' ,
+            'cooperator_id' ,
+            'cooperator_type' ,
+            'power' ,
+            'create_time',
+            'modify_time',
+            'accepted_at' ,
+            'acceptor' ,
+            'created_at' ,
+            'updated_at'         
+        ]
+    ],
+
+
+    'tag_maps' =>[
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>false,
+        'fields'=>[
+            'id' ,
+            'table_name' ,
+            'anchor_id' ,
+            'tag_id' ,
+            'created_at' ,
+            'updated_at'         
+        ]
+    ],
+
+
+    'tags'=>[
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>false,
+        'fields'=>[
+            'id'  ,
+            'name'  ,
+            'description' ,
+            'color'  ,
+            'owner_id' ,
+            'created_at' ,
+            'updated_at'         
+        ]
+    ],
+
+
+    'transfers'=>[
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>true,
+        'fields'=>[
+            'id'  ,
+            'origin_owner' ,
+            'res_type'  ,
+            'res_id' ,
+            'transferor_id' ,
+            'new_owner' ,
+            'editor_id' ,
+            'status'  ,
+            'created_at' ,
+            'updated_at'
+        ]
+    ],
+
+
+    'user_dicts' =>[
+        'key'=>'id',
+        'time1'=>'create_time',
+        'time2'=>'created_at',
+        'user'=>true,
+        'fields'=>[
+            'id',
+            'word'  ,
+            'type' ,
+            'grammar' ,
+            'parent' ,
+            'mean' ,
+            'note' ,
+            'factors' ,
+            'factormean' ,
+            'status' ,
+            'source' ,
+            'language'   ,
+            'confidence'  ,
+            'exp' ,
+            'creator_id',
+            'ref_counter'   ,
+            'create_time',
+            'created_at' ,
+            'updated_at' ,
+            'deleted_at' ,
+            'dict_id' ,
+            'flag' 
+        ]
+    ],
+
+    'user_experiences' =>[
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>true,
+        'fields'=>[
+            'id'  ,
+            'user_id' ,
+            'date' ,
+            'user_exp' ,
+            'user_level'  ,
+            'edit_exp' ,
+            'wbw_count' ,
+            'wbw_edit' ,
+            'trans_character' ,
+            'created_at' ,
+            'updated_at'         
+        ]
+    ],
+
+    'user_infos' =>[
+        'key'=>'id',
+        'time1'=>'modify_time',
+        'time2'=>'updated_at',
+        'user'=>true,
+        'fields'=>[
+            'id',
+            'userid' ,
+            'path' ,
+            'username'  ,
+            'password'  ,
+            'nickname'  ,
+            'email'  ,
+            'create_time' ,
+            'modify_time' ,
+            'receive_time' ,
+            'setting' ,
+            'reset_password_token' ,
+            'reset_password_sent_at' ,
+            'confirmation_token' ,
+            'confirmed_at' ,
+            'confirmation_sent_at' ,
+            'unconfirmed_email' ,
+            'created_at' ,
+            'updated_at'         
+        ]
+    ],
+
+
+
+    'user_operation_dailies' => [
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>true,
+        'fields'=>[
+            'id',
+            'user_id',
+            'date_int',
+            'duration',
+            'hit'  ,
+            'created_at' ,
+            'updated_at'
+        ]
+    ],
+
+
+    'user_operation_frames' =>[
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>true,
+        'fields'=>[
+            'id',
+            'user_id',
+            'duration',
+            'hit'  ,
+            'timezone'   ,
+            'op_start',
+            'op_end',
+            'created_at' ,
+            'updated_at'         
+        ]
+    ],
+
+    'user_operation_logs' =>[
+        'key'=>'id',
+        'time1'=>'create_time',
+        'time2'=>'created_at',
+        'user'=>true,
+        'fields'=>[
+            'id',
+            'user_id',
+            'op_type_id',
+            'op_type'  ,
+            'content' ,
+            'timezone' ,
+            'create_time',
+            'created_at' ,        
+        ]
+    ],
+
+    'views'=>[
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>false,
+        'fields'=>[
+            'id'  ,
+            'target_id' ,
+            'target_type'  ,
+            'user_id' ,
+            'user_ip' ,
+            'created_at' ,
+            'updated_at' ,
+            'title' ,
+            'org_title' ,
+            'count' ,
+            'meta'         
+        ]
+    ],
+
+
+    'vocabularies'=>[
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>false,
+        'fields'=>[
+            'id',
+            'word'  ,
+            'word_en'  ,
+            'count' ,
+            'flag' ,
+            'created_at' ,
+            'updated_at' ,
+            'strlen'         
+        ]
+    ],
+
+    'wbw_analyses' =>[
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>false,
+        'fields'=>[
+            'id',
+            'wbw_id',
+            'wbw_word'  ,
+            'book_id' ,
+            'paragraph' ,
+            'wid' ,
+            'type' ,
+            'data'  ,
+            'confidence' ,
+            'lang' ,
+            'd1' ,
+            'd2' ,
+            'd3' ,
+            'editor_id',
+            'created_at' ,
+            'updated_at'         
+        ]
+    ],
+
+    'wbw_blocks' =>[
+        'key'=>'uid',
+        'time1'=>'modify_time',
+        'time2'=>'updated_at',
+        'user'=>true,
+        'fields'=>[
+            'id',
+            'uid' ,
+            'parent_id' ,
+            'block_uid' ,
+            'block_id' ,
+            'channel_id' ,
+            'channel_uid' ,
+            'parent_channel_uid' ,
+            'creator_uid' ,
+            'editor_id',
+            'book_id' ,
+            'paragraph' ,
+            'style' ,
+            'lang' ,
+            'status' ,
+            'create_time',
+            'modify_time',
+            'created_at' ,
+            'updated_at' ,
+            'deleted_at' 
+        ]
+    ],
+
+    'wbw_templates' =>[
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>false,
+        'fields'=>[
+            'id',
+            'book' ,
+            'paragraph' ,
+            'wid' ,
+            'word'  ,
+            "real"  ,
+            'type'  ,
+            'gramma'  ,
+            'part'  ,
+            'style'  ,
+            'created_at' ,
+            'updated_at' ,
+            'pcd_book_id'         
+        ]
+    ],
+
+    'wbws' =>[
+        'key'=>'uid',
+        'time1'=>'modify_time',
+        'time2'=>'updated_at',
+        'user'=>true,
+        'fields'=>[
+            'id',
+            'uid' ,
+            'block_uid' ,
+            'block_id' ,
+            'channel_id' ,
+            'book_id' ,
+            'paragraph' ,
+            'wid',
+            'word'  ,
+            'data' ,
+            'status' ,
+            'creator_uid' ,
+            'editor_id',
+            'create_time',
+            'modify_time',
+            'created_at' ,
+            'updated_at' ,
+            'deleted_at' 
+        ]
+    ],
+
+
+    'web_hooks' =>[
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>true,
+        'fields'=>[
+            'id' ,
+            'res_type'  ,
+            'res_id' ,
+            'url' ,
+            'receiver'  ,
+            'event' ,
+            'fail' ,
+            'success' ,
+            'status' ,
+            'editor_uid' ,
+            'created_at' ,
+            'updated_at' 
+        ]
+    ],
+
+    'word_indices'=>[
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>false,
+        'fields'=>[
+            'id' ,
+            'word'  ,
+            'word_en'  ,
+            'count' ,
+            'normal' ,
+            'bold' ,
+            'is_base' ,
+            'len' ,
+            'final' ,
+            'created_at' ,
+            'updated_at'         
+        ]
+    ],
+
+    'word_lists' =>[
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>false,
+        'fields'=>[
+            'id',
+            'sn' ,
+            'book' ,
+            'paragraph' ,
+            'wordindex' ,
+            'bold' ,
+            'weight'  ,
+            'created_at' ,
+            'updated_at'         
+        ]
+    ],
+
+    'word_parts' =>[
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>false,
+        'fields'=>[
+            'id',
+            'word'  ,
+            'weight',
+            'created_at' ,
+            'updated_at'         
+        ]
+    ],
+
+    'word_statistics' =>[
+        'key'=>'id',
+        'time1'=>'',
+        'time2'=>'updated_at',
+        'user'=>false,
+        'fields'=>[
+            'id',
+            'bookid' ,
+            'word'  ,
+            'count' ,
+            'base'  ,
+            'end1'  ,
+            'end2'  ,
+            'type' ,
+            'length' ,
+            'created_at' ,
+            'updated_at'         
+        ]
+    ],
+];

+ 61 - 0
patch/db_fix/test.php

@@ -0,0 +1,61 @@
+<?php
+
+require_once "tables.php";
+require_once "config.php";
+require_once "function.php";
+
+if(php_sapi_name() !== "cli") {
+    echo 'no cli';
+    return;
+}
+
+$src_db = 'db_b';
+$dest_db = 'db_c';
+
+#打开源数据库
+$PDO_SRC = openDb($config[$src_db]);
+$PDO_SRC->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+
+#打开目标数据库
+$PDO_DEST = openDb($config[$dest_db]);
+$PDO_DEST->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+
+foreach ($tables as $tableName => $table) {
+    fwrite(STDOUT,$tableName.PHP_EOL);
+    $query = "SELECT count(*)  FROM {$tableName}";
+    $stmtSrc = $PDO_SRC->prepare($query);
+    $stmtSrc->execute();
+    $srcData = $stmtSrc->fetch(PDO::FETCH_ASSOC);
+    fwrite(STDOUT,'table 1 count='.$srcData['count'].PHP_EOL);
+
+    $stmtDest = $PDO_DEST->prepare($query);
+    $stmtDest->execute();
+    $destData = $stmtDest->fetch(PDO::FETCH_ASSOC);
+    fwrite(STDOUT,'table 2 count='.$destData['count'].PHP_EOL);
+
+    fwrite(STDOUT,'field='.count($table['fields']).PHP_EOL);
+    $fields = '"' . implode('","',$table['fields']) . '"' ;
+    $query = "SELECT {$fields}  FROM {$tableName} limit 1 ";
+    $stmtSrc = $PDO_SRC->prepare($query);
+    $stmtSrc->execute();
+
+    $keys = array();
+    if(is_array($table['key'])){
+        $keys = $table['key'];
+    }else{
+        $keys[] = $table['key'];
+    }
+    if(!empty($table['time1'])){
+        $keys[] = $table['time1'];
+    }
+    if(!empty($table['time1'])){
+        $keys[] = $table['time2'];
+    }
+    foreach ($keys as $key) {
+        if(!in_array($key,$table['fields'])){
+            fwrite(STDERR,$tableName.' no field '.$key.PHP_EOL);
+        }
+    }
+
+    fwrite(STDOUT,PHP_EOL);
+}

+ 20 - 33
public/app/studio/index.php

@@ -79,7 +79,7 @@ else{
 
 	<script type="text/javascript">
 	<?php require_once '../public/load_lang_js.php';//加载js语言包?>
-		
+
 		var g_device = "computer";
 		var strSertch = location.search;
 		if(strSertch.length>0){
@@ -105,7 +105,7 @@ else{
 			setCookie('language',g_language,365);
 			window.location.assign("index.php?language="+g_language);
 		}
-	
+
 	var gCurrPage="index";
 	</script>
 	<style>
@@ -120,7 +120,7 @@ else{
 	<script>
 		document.getElementById("id_language").value="<?php echo($currLanguage); ?>";
 	</script>
-		
+
         <style>
             #login_body{
                 display: flex;
@@ -173,7 +173,7 @@ else{
             .login_form input[type="submit"]{
                 margin-top:2em;
                 padding:0.1em 0.5em;
-            } 
+            }
 
             .form_error{
                 color:var(--error-text);
@@ -307,11 +307,6 @@ else{
 		<ul style="list-style-type: none;">
 		<li ><a href="<?php echo URL_HELP.'/zh-Hans' ?>" target="_blank"><?php echo $_local->gui->help; ?></a></li>
 			<li><?php echo $_local->gui->function_introduce;?>&nbsp;&nbsp;&nbsp;
-				<a href="https://www.youtube.com/playlist?list=PL_1iJBQvNPFFNLOaZh2u3VwDYUyJuK_xa" target="_block">
-					<svg class="icon">
-						<use xlink:href="../studio/svg/icon.svg#youtube_logo"></use>
-					</svg>
-				</a>&nbsp;
 				<a href="" target="_block" style="display: none;">
 					<svg class="icon">
 						<use xlink:href="../studio/svg/icon.svg#youku_logo"></use>
@@ -324,11 +319,7 @@ else{
 				</a>
 			</li>
 			<li><?php echo $_local->gui->project_introduce;?>&nbsp;&nbsp;&nbsp;
-				<a href="https://www.youtube.com/playlist?list=PL_1iJBQvNPFHT6UisME_cOSts5fFecK14" target="_block">
-					<svg class="icon">
-						<use xlink:href="../studio/svg/icon.svg#youtube_logo"></use>
-					</svg>
-				</a>&nbsp;
+
 				<a href="" target="_block" style="display: none;">
 					<svg class="icon">
 						<use xlink:href="../studio/svg/icon.svg#youku_logo"></use>
@@ -340,12 +331,8 @@ else{
 					</svg>
 				</a>
 			</li>
-			<li><?php echo $_local->gui->project_introduce_inbrief;?>&nbsp;&nbsp;&nbsp;
-				<a href="https://www.youtube.com/playlist?list=PLgavmc8e-GuWR-FKOr-7RfnUSWX82ED0q" target="_block">
-					<svg class="icon">
-						<use xlink:href="../studio/svg/icon.svg#youtube_logo"></use>
-					</svg>
-				</a>&nbsp;
+			<li><?php echo $_local->gui->project_introduce_inbrief;?>
+                &nbsp;&nbsp;&nbsp;
 				<a href="" target="_block" style="display: none;">
 					<svg class="icon">
 						<use xlink:href="../studio/svg/icon.svg#youku_logo"></use>
@@ -390,7 +377,7 @@ else{
 			</li>
 		</ul>
 	</div>
-</div>	
+</div>
 
 <div id="login_right">
 
@@ -416,7 +403,7 @@ else{
 				</div>
 			</div>
 		</div>
-	</div>       
+	</div>
 
 	<script src="../public/js/highcharts/highcharts.js"></script>
 	<script src="../public/js/highcharts/modules/exporting.js"></script>
@@ -462,9 +449,9 @@ else{
 
 		</div>
 	</div>
-		
 
-</div>	
+
+</div>
 </div>
 
 <script>
@@ -472,10 +459,10 @@ else{
                 let username=getCookie("username");
                 if(username==""){
                     $("#file_list").html("登陆后显示文件列表");
-                    return; 
+                    return;
                 }
                 var d=new Date();
-               
+
                 $.get("../studio/getfilelist.php",
                 {
                     t:d.getTime(),
@@ -491,7 +478,7 @@ else{
                     try{
                         let file_list = JSON.parse(data);
                         let html="";
-                        
+
                         if(file_list.length<count){
                             count=file_list.length;
                         }
@@ -511,16 +498,16 @@ else{
                         $("#file_list").html(strFilelist);
                     }
                     catch(e){
-                        
+
                     }
-                    
-                    
-                    
+
+
+
                 });
             }
 file_list();
 </script>
-		
+
 <script>
 /*
 		$.get('../uwbw/update_analytics.php', function (csvStr) {
@@ -634,7 +621,7 @@ file_list();
 		*/
         </script>
 	</div>
-	
+
 	<div class="foot_div">
 		<?php echo $_local->gui->poweredby;?>
 	</div>