diff --git a/code/app/home/BaseController.php b/code/app/home/BaseController.php index 5c28d30..7bc3838 100644 --- a/code/app/home/BaseController.php +++ b/code/app/home/BaseController.php @@ -62,8 +62,6 @@ abstract class BaseController */ protected function initialize() { - // TODO 因为视图无法渲染 request 暂时使用这个 - View::assign("DomainModel", $this->request->DomainModel); $this->initSiteTKD(); } diff --git a/code/app/home/controller/Novel.php b/code/app/home/controller/Novel.php index a9b7f17..a7645d4 100644 --- a/code/app/home/controller/Novel.php +++ b/code/app/home/controller/Novel.php @@ -357,14 +357,13 @@ class Novel extends BaseController */ public function getKanNovelInfo(Request $Request) { - // TODO 这个页面数据没有处理 - // FIXED 不要这样打标记好不好,沟通成本太高了,你直接标注缺少什么数据,什么数据有问题,什么数据需要转换 - // TODO 这个是 kan 小说页面的控制器,是直接复制 小说详情的控制器, - // 1.从这个页面上看,这个应该展示 kan 小说的名字才对,目前 title 还是 小说的名字 , + // $arrNovel['og_novel_book_name'] = kan 小说 的 名字 $intNId = $Request->param('n_id'); + $intForgeId = $Request->param('n_forge_id'); + $boolIsMobile = $Request->param('boolIsMobile'); $NovelModel = new NovelModel; @@ -372,6 +371,13 @@ class Novel extends BaseController // 小说详情 $arrNovel = $NovelModel->getNovelByNId($intNId); + foreach ($arrNovel['n_forge_novel'] as $arrForgeNovel) { + if ($arrForgeNovel['n_forge_id'] == $intForgeId) { + $arrNovel['og_novel_book_name'] = $arrForgeNovel['n_forge_title']; + break; + } + } + // 分品拼音 $arrNovel['category_pinyin'] = CategoryModel::getPinYinByZh($arrNovel['og_novel_category']); @@ -826,9 +832,7 @@ class Novel extends BaseController ConverterMovel::setVal([ 'intPage' => $intPage, 'strSearchKeywords' => $strSearchKeywords, - // FIXED 如果不知道怎么做,就不要用这个模板,你现在太过依赖这个模板,这个模板如果是一个刚毕业的大学生的垃圾论文项目,你也被他的四维锁死? - // 这个和模板没有关系, 昨天 grok 使用超限制了,使用you.com 提供的 tkd 设计,它设计的 tkd模板 需要的 字段比较多,那几个 性别 和 分类 放在搜索是不太合理 - // 现在改了,使用grok 设计的 ,ai 记录在 app/home/view/kuangyu/推广SEO过程设计记录/TKD/搜索页Tkd.md + ]); $strTitleTemplate = $Request->DomainModel->getFomartSubject(getFomartSubjectKey(__METHOD__) . '@TITLE'); @@ -850,7 +854,7 @@ class Novel extends BaseController 'strKeywords' => $strKeywords, 'strDescription' => $strDescription, ]); - + $strView = $boolIsMobile ? '' : 'pc/getSearchNovel'; return $this->rending($strView); diff --git a/code/app/home/view/kuangyu/basePc.html b/code/app/home/view/kuangyu/basePc.html index 691e313..9785f8c 100644 --- a/code/app/home/view/kuangyu/basePc.html +++ b/code/app/home/view/kuangyu/basePc.html @@ -91,14 +91,8 @@ {$DomainModel->d_statis|raw} - {:request()->DomainModel->d_statis} - - - - - - - {:request()->DomainModel->d_domian} + + \ No newline at end of file diff --git a/code/app/model/CategoryModel.php b/code/app/model/CategoryModel.php index 664ed00..1164f1f 100644 --- a/code/app/model/CategoryModel.php +++ b/code/app/model/CategoryModel.php @@ -124,4 +124,15 @@ class CategoryModel extends MongoModel { return in_array($strName, self::$arrMan) ? 'man' : 'women'; } + + /** + * determine the gender of the novel category + * + * @param string $strName + * @return string + */ + static public function checkSexZh(string $strName): string + { + return in_array($strName, self::$arrMan) ? '男生' : '女生'; + } } diff --git a/code/app/model/ChapterModel.php b/code/app/model/ChapterModel.php index 5741e5f..1dd6e96 100644 --- a/code/app/model/ChapterModel.php +++ b/code/app/model/ChapterModel.php @@ -123,7 +123,7 @@ class ChapterModel extends MongoModel * @param integer $intNId * @return null|array */ - public function getLatestChapter(int $intNId): null|array + public function getLatestChapter(int $intNId, bool $boolReadContent = true): null|array { $arrFilter = ['n_id' => $intNId, 'c_page' => 0]; @@ -135,8 +135,10 @@ class ChapterModel extends MongoModel if (!empty($arrChapter)) { applyToKeys($arrChapter, ['created_at', 'updated_at'], 'formatMongoDate'); - - $arrChapter['content'] = StorageCore::getInstance()->read($arrChapter['c_content_path']); + $arrChapter['content'] = ''; + if ($boolReadContent) { + $arrChapter['content'] = StorageCore::getInstance()->read($arrChapter['c_content_path']); + } } return $arrChapter ?? []; diff --git a/code/app/model/NovelModel.php b/code/app/model/NovelModel.php index 14df81b..cd5eacb 100644 --- a/code/app/model/NovelModel.php +++ b/code/app/model/NovelModel.php @@ -88,6 +88,7 @@ class NovelModel extends MongoModel $arrOptions['limit'] = $intCount; + $arrFilter['n_have_chapter'] = 1; $Cursor = $this->getCol()->find($arrFilter, $arrOptions); @@ -110,14 +111,11 @@ class NovelModel extends MongoModel */ public function getNovelByNId(int $intNId): null|array { - $arrFilter = ['n_id' => $intNId]; + $arrFilter = ['n_id' => $intNId, 'n_have_chapter' => 1]; $arrOptions = self::$arrNovelOptions; $arrNovel = $this->getOne($arrFilter, $arrOptions); applyToKeys($arrNovel, ['created_at', 'updated_at'], 'formatMongoDate'); - if (!empty($arrNovel['og_novel_category'])) { - $arrNovel['category_pinyin'] = CategoryModel::getPinYinByZh($arrNovel['og_novel_category']); - $arrNovel['category_sex'] = CategoryModel::checkSex($arrNovel['og_novel_category']); - } + return $arrNovel; } @@ -141,6 +139,8 @@ class NovelModel extends MongoModel return $arrResult; } + $arrFilter['n_have_chapter'] = 1; + $pipeline = [ ['$match' => $arrFilter], ['$sample' => ['size' => $intCount]] @@ -180,7 +180,7 @@ class NovelModel extends MongoModel $strStatus = NULL, array $arrSort = ['n_level' => -1] ): array { - $arrFilter = []; + $arrFilter = ['n_have_chapter' => 1,]; switch ($intType) { case 1: @@ -219,6 +219,7 @@ class NovelModel extends MongoModel int $intLifeTime = 24 * 3600, ): array { try { + $arrFilter['n_have_chapter'] = 1; $arrResult = $this->checkCacheByKey($strKey); if ($arrResult !== NULL) { @@ -295,7 +296,7 @@ class NovelModel extends MongoModel return $arrResult; } - $arrFilter = []; + $arrFilter = ['n_have_chapter' => 1,]; switch ($intType) { case 1: @@ -362,12 +363,12 @@ class NovelModel extends MongoModel $intLifeTime = 24 * 3600; $arrResult = $this->checkCacheByKey($strKey); - + if ($arrResult !== NULL) { return $arrResult; } - $arrFilter = []; + $arrFilter = ['n_have_chapter' => 1,]; switch ($intType) { case 1: $arrCategory = array_values(CategoryModel::getManCategory()); @@ -398,10 +399,10 @@ class NovelModel extends MongoModel ], ]; - + $Cursor = $this->getCol()->find($arrFilter, $arrOptions); $arrResult = iterator_to_array($Cursor); - + shuffle($arrResult); $arrResult = array_slice($arrResult, 0, $intCount); @@ -435,6 +436,7 @@ class NovelModel extends MongoModel } $arrFilter = [ 'og_novel_status' => "已完结", + 'n_have_chapter' => 1, ]; $arrOptions = [ @@ -487,19 +489,16 @@ class NovelModel extends MongoModel if ($arrResult !== NULL) { return $arrResult; } + + $arrFilter = ['n_have_chapter' => 1,]; + if ($intType == 1) { $arrCategory = array_values(CategoryModel::getManCategory()); - $arrFilter = [ - 'og_novel_category' => ['$in' => $arrCategory] - ]; + $arrFilter['og_novel_category'] = ['$in' => $arrCategory]; } elseif ($intType == 2) { $arrCategory = array_values(CategoryModel::getWomenCategory()); - $arrFilter = [ - 'og_novel_category' => ['$in' => $arrCategory] - ]; - } else { - $arrFilter = []; - } + $arrFilter['og_novel_category'] = ['$in' => $arrCategory]; + } $arrOptions = [ 'sort' => ['og_novel_update_time' => -1], diff --git a/code/app/task/crawler/zw630/Scheduler.php b/code/app/task/crawler/zw630/Scheduler.php index a10c558..d8ece01 100644 --- a/code/app/task/crawler/zw630/Scheduler.php +++ b/code/app/task/crawler/zw630/Scheduler.php @@ -36,8 +36,8 @@ class Scheduler $arrFilter = []; // for ($intNId = 1; $intNId <= 531208; $intNId++) { - // for ($intNId = 1; $intNId <= 10000; $intNId++) { - for ($intNId = 1; $intNId <= 20000; $intNId++) { + // for ($intNId = 1; $intNId <= 10000; $intNId++) { + for ($intNId = 1; $intNId <= 6; $intNId++) { $arrFilter[] = [ 'n_source_id' => $intNId, 'page' => 0, diff --git a/code/app/task/crawler/zw630/page/ChapterPage.php b/code/app/task/crawler/zw630/page/ChapterPage.php index f91a330..72478dd 100644 --- a/code/app/task/crawler/zw630/page/ChapterPage.php +++ b/code/app/task/crawler/zw630/page/ChapterPage.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace app\task\crawler\zw630\page; +use app\model\ChapterModel; use storage\StorageCore; class ChapterPage extends BasePage @@ -70,7 +71,7 @@ class ChapterPage extends BasePage $arrExisting = (array) $arrExisting; $this->arrChapterInfo = array_merge($arrExisting, $this->getChapterInfo()); - $this->arrChapterInfo['updated_at'] = new \MongoDB\BSON\UTCDateTime(); // 添加更新时间 + $this->arrChapterInfo['updated_at'] = new \MongoDB\BSON\UTCDateTime(); $Col->updateOne( ['c_site_uuid' => $strUUId], ['$set' => $this->arrChapterInfo], @@ -94,6 +95,8 @@ class ChapterPage extends BasePage $this->getMongoBase()->insertOne('chapter', $this->arrChapterInfo); + $this->updateNovelHaveChapter($intNId); + if (!StorageCore::getInstance()->has($this->arrChapterInfo['c_content_path'])) { StorageCore::getInstance()->set($this->arrChapterInfo['c_content_path'], $this->strChapterContent); } @@ -106,6 +109,29 @@ class ChapterPage extends BasePage } } + + /** + * Undocumented function + * + * @param integer $intNId + * @return void + */ + protected function updateNovelHaveChapter(int $intNId) + { + $arrFilter = [ + 'n_id' => $intNId, + ]; + + $ChapterModel = new ChapterModel; + $arrLasteChapter = $ChapterModel->getLatestChapter($intNId, false); + + if (!empty($arrLasteChapter)) { + $arrUpdate['$set']['n_have_chapter'] = 1; + $arrUpdate['$set']['og_novel_latest_chapter_name'] = $arrLasteChapter['name']; + $this->getMongoBase()->updateOne('novel', $arrFilter, $arrUpdate); + } + } + protected $arrNextPage; /** diff --git a/code/app/task/crawler/zw630/page/NovelPage.php b/code/app/task/crawler/zw630/page/NovelPage.php index d98d3ab..882ffa1 100644 --- a/code/app/task/crawler/zw630/page/NovelPage.php +++ b/code/app/task/crawler/zw630/page/NovelPage.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace app\task\crawler\zw630\page; +use app\model\CategoryModel; use ddl\DDLManage; use QL\QueryList; @@ -51,6 +52,7 @@ class NovelPage extends BasePage $arrNovelInfo['n_site_uuid'] = $strUUId; $arrNovelInfo['n_source_id'] = $intNSourceId; $arrNovelInfo['n_level'] = 1; + $arrNovelInfo['n_have_chapter'] = 0; $arrNovelInfo['n_id'] = $this->getMongoBase()->getNextSequence('novel'); $this->getMongoBase()->insertOne('novel', $arrNovelInfo); } @@ -176,7 +178,17 @@ class NovelPage extends BasePage $this->arrNovelInfo['n_source_id'] = $this->arrData['n_source_id']; $this->arrNovelInfo['n_id'] = $this->getMongoBase()->getNextSequence('novel'); $this->arrNovelInfo['n_level'] = 1; + $this->arrNovelInfo['n_have_chapter'] = 0; $this->arrNovelInfo['n_cover_path'] = '/img/' . date('Y/m/d', time() - 24 * 3600 * 14) . '/' . $this->arrNovelInfo['n_id'] . '.jpg'; + + + if (!empty($this->arrNovelInfo['og_novel_category'])) { + $this->arrNovelInfo['category_pinyin'] = CategoryModel::getPinYinByZh($this->arrNovelInfo['og_novel_category']); + $this->arrNovelInfo['category_sex'] = CategoryModel::checkSex($this->arrNovelInfo['og_novel_category']); + $this->arrNovelInfo['category_sex_zh'] = CategoryModel::checkSexZh($this->arrNovelInfo['og_novel_category']); + } + + $this->getMongoBase()->insertOne('novel', $this->arrNovelInfo); $this->boolIsFirstSave = true; diff --git a/code/app/task/crawler/zw630/page/Site.php b/code/app/task/crawler/zw630/page/Site.php index 86adbb7..d36bc15 100644 --- a/code/app/task/crawler/zw630/page/Site.php +++ b/code/app/task/crawler/zw630/page/Site.php @@ -25,7 +25,7 @@ class Site * * @var string */ - protected $strDomain = 'https://www.630zw.org'; + protected $strDomain = 'https://www.630zw.com'; /** * Undocumented function diff --git a/code/config/console.php b/code/config/console.php index 98da0ec..10089f1 100644 --- a/code/config/console.php +++ b/code/config/console.php @@ -11,5 +11,7 @@ return [ 'commands' => [ 'task' => 'microserver\ServerConsole', 'db:manage' => 'db\DatabaseManage', + 'mongo:manage' => 'db\MongoMigrate', + 'novel:manage' => 'db\NovelManage', ], ]; diff --git a/code/database/mongo/migrations/category.php b/code/database/mongo/migrations/category.php new file mode 100644 index 0000000..ed64e88 --- /dev/null +++ b/code/database/mongo/migrations/category.php @@ -0,0 +1,10 @@ + 'category', + 'indexes' => [ + [ + 'key' => ['ca_name' => 1], + 'options' => ['unique' => true] + ], + ] +]; diff --git a/code/database/mongo/migrations/chapter.php b/code/database/mongo/migrations/chapter.php new file mode 100644 index 0000000..fe81801 --- /dev/null +++ b/code/database/mongo/migrations/chapter.php @@ -0,0 +1,23 @@ + 'chapter', + 'indexes' => [ + [ + 'key' => ['c_site_uuid' => 1], + 'options' => ['unique' => true] + ], + + [ + 'key' => ['n_id' => 1], + 'options' => ['unique' => false] + ], + [ + 'key' => ['c_sort_num' => 1], + 'options' => ['unique' => false] + ], + [ + 'key' => ['c_page' => 1], + 'options' => ['unique' => false] + ], + ] +]; diff --git a/code/database/mongo/migrations/novel.php b/code/database/mongo/migrations/novel.php new file mode 100644 index 0000000..4baab22 --- /dev/null +++ b/code/database/mongo/migrations/novel.php @@ -0,0 +1,34 @@ + 'novel', + 'indexes' => [ + [ + 'key' => ['n_id' => 1], + 'options' => ['unique' => false] + ], + [ + 'key' => ['og_title' => 1], + 'options' => ['unique' => false] + ], + [ + 'key' => ['og_novel_status' => 1], + 'options' => ['unique' => false] + ], + [ + 'key' => ['n_site_uuid' => 1], + 'options' => ['unique' => true] + ], + [ + 'key' => ['n_related_novel.n_id' => 1], + 'options' => ['unique' => false] + ], + [ + 'key' => ['n_forge_novel.n_forge_id' => 1], + 'options' => ['unique' => false] + ], + [ + 'key' => ['n_have_chapter' => 1], + 'options' => ['unique' => false] + ], + ] +]; diff --git a/code/database/mongo/migrations/novel_clicks.php b/code/database/mongo/migrations/novel_clicks.php new file mode 100644 index 0000000..66665a7 --- /dev/null +++ b/code/database/mongo/migrations/novel_clicks.php @@ -0,0 +1,10 @@ + 'novel_clicks', + 'indexes' => [ + [ + 'key' => ['n_id' => 1, 'nc_date' => 1, 'nc_type' => 1], + 'options' => ['unique' => true] + ], + ] +]; diff --git a/code/extend/db/DatabaseManage.php b/code/extend/db/DatabaseManage.php index 89dc728..b1432a2 100644 --- a/code/extend/db/DatabaseManage.php +++ b/code/extend/db/DatabaseManage.php @@ -98,7 +98,7 @@ class DatabaseManage extends Command $Output->writeln("创建临时导入文件失败."); return; } - + $strCmd = "mysql -h{$strHost} -P{$strPort} -u{$strUserName} -p{$strPwd} {$strDb} < {$strTempFile}"; system($strCmd, $intResultCode); diff --git a/code/extend/db/MongoMigrate.php b/code/extend/db/MongoMigrate.php new file mode 100644 index 0000000..2d84b68 --- /dev/null +++ b/code/extend/db/MongoMigrate.php @@ -0,0 +1,176 @@ +setName('mongo:manage') + ->addArgument('action', \think\console\input\Argument::OPTIONAL, 'The action to perform: migrate') + ->setDescription('Manage the mongo, migarte index or drop all index'); + } + + /** + * Undocumented function + * + * @param Input $input + * @param Output $Output + * @return void + */ + protected function execute(Input $input, Output $Output) + { + $strAction = $input->getArgument('action'); + + if ($strAction === 'migrate') { + $this->migrate($Output); + } else if ($strAction === 'drop') { + $this->dropAllIndexes($Output); + } else { + $Output->writeln("Invalid action. Use 'migrate' or 'drop'"); + } + } + + /** + * Undocumented function + * + * @param Output $Output + * @return void + */ + protected function dropAllIndexes(Output $Output) + { + try { + DDLManage::load('MongoBase'); + $Db = MongoBase::getInstance()->getDb(); + + $ColsInfo = $Db->listCollections(); + + foreach ($ColsInfo as $ColInfo) { + $strColName = $ColInfo->getName(); + $Col = $Db->selectCollection($strColName); + + + $arrIndexes = iterator_to_array($Col->listIndexes()); + $arrIndexesToDrop = []; + + + foreach ($arrIndexes as $Index) { + $strIndexName = $Index->getName(); + if ($strIndexName !== '_id_') { + $arrIndexesToDrop[] = $strIndexName; + } + } + + + if (!empty($arrIndexesToDrop)) { + foreach ($arrIndexesToDrop as $strIndexName) { + $Col->dropIndex($strIndexName); + $Output->writeln(" Dropped index: {$strIndexName} from collection: {$strColName}"); + } + } else { + $Output->writeln(" No non-_id indexes found in collection: {$strColName}"); + } + } + } catch (\Exception $e) { + $Output->writeln("Failed to drop indexes: {$e->getMessage()}"); + } + } + + /** + * Undocumented function + * + * @param Output $Output + * @return void + */ + protected function migrate(Output $Output) + { + try { + DDLManage::load('MongoBase'); + $strMigrationDir = root_path() . 'database/mongo/migrations'; + + + if (!is_dir($strMigrationDir)) { + mkdir($strMigrationDir, 0755, true); + $Output->writeln("Created migration directory: {$strMigrationDir}"); + } + + + $arrFiles = glob($strMigrationDir . '/*.php'); + if (empty($arrFiles)) { + $Output->writeln('No migration files found.'); + return 0; + } + + sort($arrFiles); + + $Db = MongoBase::getInstance()->getDb(); + + foreach ($arrFiles as $strFile) { + $Output->writeln("Processing migration: " . basename($strFile)); + + + $arrMigration = require $strFile; + if (!is_array($arrMigration) || !isset($arrMigration['collection']) || !isset($arrMigration['indexes'])) { + $Output->writeln("Invalid migration format in {$strFile}"); + continue; + } + + $strCollectionName = $arrMigration['collection']; + $arrIndexes = $arrMigration['indexes']; + + + $Col = $Db->selectCollection($strCollectionName); + + + $arrExistingIndexes = iterator_to_array($Col->listIndexes()); + + $arrExistingIndexKeys = []; + foreach ($arrExistingIndexes as $Index) { + $strKey = json_encode($Index->getKey()); + $arrExistingIndexKeys[$strKey] = $Index->getName(); + } + + + foreach ($arrIndexes as $arrIndex) { + $arrKey = $arrIndex['key']; + $arrOptions = $arrIndex['options'] ?? []; + + + $strKey = json_encode($arrKey); + + + if (array_key_exists($strKey, $arrExistingIndexKeys)) { + $Output->writeln(" Index for " . json_encode($arrKey) . " already exists as {$arrExistingIndexKeys[$strKey]}, skipping..."); + continue; + } + + + $arrIndexNameParts = []; + foreach ($arrKey as $field => $direction) { + $arrIndexNameParts[] = "{$field}_{$direction}"; + } + $strIndexName = implode('_', $arrIndexNameParts); + + + $arrOptions['name'] = $strIndexName; + $Col->createIndex($arrKey, $arrOptions); + + $Output->writeln(" Created index: {$strIndexName} for " . json_encode($arrKey)); + } + } + + $Output->writeln('Migration completed successfully!'); + return 0; + } catch (\Exception $e) { + $Output->writeln("Error: {$e->getMessage()}"); + return 1; + } + } +} diff --git a/code/extend/db/NovelManage.php b/code/extend/db/NovelManage.php new file mode 100644 index 0000000..0120566 --- /dev/null +++ b/code/extend/db/NovelManage.php @@ -0,0 +1,114 @@ +setName('novel:manage') + ->addArgument('action', \think\console\input\Argument::OPTIONAL, 'The action to perform: reset') + ->setDescription('Manage the novel data: reset or ...'); + } + + protected function execute(Input $input, Output $Output) + { + $strAction = $input->getArgument('action'); + + if ($strAction === 'reset') { + $this->resetData($Output); + } else { + $Output->writeln("Invalid action. Use 'reset' "); + } + } + + private function resetData(Output $Output) + { + DDLManage::load('MongoBase'); + + $Db = MongoBase::getInstance()->getDb(); + + $NovelCol = $Db->selectCollection('novel'); + + $ChapterCol = $Db->selectCollection('chapter'); + + $ChapterModel = new ChapterModel; + + $intBatchSize = 1000; + + $LastId = null; + + while (true) { + + $arrFilter = []; + if (!is_null($LastId)) { + $arrFilter['_id'] = ['$gt' => $LastId]; + } + $arrOptions = [ + 'sort' => ['_id' => 1], + 'limit' => $intBatchSize, + 'noCursorTimeout' => true, + ]; + + $Cursor = $NovelCol->find($arrFilter, $arrOptions); + $arrDocs = iterator_to_array($Cursor); + + if (empty($arrDocs)) { + echo "全部处理完毕。\n"; + break; + } + + $arrBulkOps = []; + + foreach ($arrDocs as $arrDoc) { + $arrUpdateData = []; + + if (!empty($arrDoc['og_novel_category'])) { + $arrUpdateData['category_pinyin'] = CategoryModel::getPinYinByZh($arrDoc['og_novel_category']); + $arrUpdateData['category_sex'] = CategoryModel::checkSex($arrDoc['og_novel_category']); + $arrUpdateData['category_sex_zh'] = CategoryModel::checkSexZh($arrDoc['og_novel_category']); + } + + $arrLasteChapter = $ChapterModel->getLatestChapter($arrDoc['n_id'], false); + + if (!empty($arrLasteChapter)) { + $arrUpdateData['n_have_chapter'] = 1; + $arrUpdateData['og_novel_latest_chapter_name'] = $arrLasteChapter['name']; + } + + if (!empty($arrUpdateData)) { + $arrBulkOps[] = [ + 'updateOne' => [ + ['_id' => $arrDoc['_id']], + ['$set' => $arrUpdateData], + ], + ]; + } + + $LastId = $arrDoc['_id']; + } + + if (!empty($arrBulkOps)) { + $Result = $NovelCol->bulkWrite($arrBulkOps); + printf("本批匹配 %d 条记录, 修改 %d 条记录\n", $Result->getMatchedCount(), $Result->getModifiedCount()); + } else { + echo "本批无需要更新的记录。\n"; + } + + echo "已处理一批,最后 _id: " . (string)$LastId . "\n"; + } + } +}