sitemap.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. /**
  2. * 每天运行一次,生成站点地图
  3. */
  4. const fs = require('fs');
  5. const datefns = require('date-fns');
  6. const models = require('../../models');
  7. const categories = require('../../config/category');
  8. const { tags } = require('../../config/tag');
  9. const date = datefns.format(Date.now(), 'yyyy-MM-dd');
  10. const sitemapPath = __dirname + '/../../dist/sitemap.xml';
  11. const sitemapTempPath = __dirname + '/../../dist/sitemap.xml.temp';
  12. // 生成站点地图
  13. async function generateSitemap() {
  14. let commonXml = [
  15. '<?xml version="1.0" encoding="UTF-8"?>',
  16. // '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
  17. '<urlset',
  18. ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"',
  19. ' xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd http://www.w3.org/1999/xhtml http://www.w3.org/2002/08/xhtml/xhtml1-strict.xsd"',
  20. ' xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"',
  21. ' xmlns:xhtml="http://www.w3.org/1999/xhtml"',
  22. '>',
  23. ' <url>',
  24. ' <loc>https://art.pcoloring.com/en</loc>',
  25. ` <lastmod>${date}</lastmod>`,
  26. ' <changefreq>daily</changefreq>',
  27. ' <priority>1.0</priority>',
  28. ' </url>',
  29. ' <url>',
  30. ' <loc>https://art.pcoloring.com/en/videos</loc>',
  31. ` <lastmod>${date}</lastmod>`,
  32. ' <changefreq>weekly</changefreq>',
  33. ' <priority>0.8</priority>',
  34. ' </url>',
  35. ' <url>',
  36. ' <loc>https://art.pcoloring.com/en/category</loc>',
  37. ` <lastmod>${date}</lastmod>`,
  38. ' <changefreq>daily</changefreq>',
  39. ' <priority>0.8</priority>',
  40. ' </url>',
  41. ' <url>',
  42. ' <loc>https://art.pcoloring.com/en/tag</loc>',
  43. ` <lastmod>${date}</lastmod>`,
  44. ' <changefreq>daily</changefreq>',
  45. ' <priority>0.8</priority>',
  46. ' </url>',
  47. ' <url>',
  48. ' <loc>https://art.pcoloring.com/en/albums</loc>',
  49. ` <lastmod>${date}</lastmod>`,
  50. ' <changefreq>weekly</changefreq>',
  51. ' <priority>0.8</priority>',
  52. ' </url>',
  53. ' <url>',
  54. ' <loc>https://art.pcoloring.com/en/special</loc>',
  55. ` <lastmod>${date}</lastmod>`,
  56. ' <changefreq>daily</changefreq>',
  57. ' <priority>0.8</priority>',
  58. ' </url>',
  59. ' <url>',
  60. ' <loc>https://art.pcoloring.com/en/artists</loc>',
  61. ` <lastmod>${date}</lastmod>`,
  62. ' <changefreq>monthly</changefreq>',
  63. ' <priority>0.8</priority>',
  64. ' </url>',
  65. ' <url>',
  66. ' <loc>https://art.pcoloring.com/en/info</loc>',
  67. ` <lastmod>${date}</lastmod>`,
  68. ' </url>',
  69. ];
  70. // categories
  71. let categoriesXml = [];
  72. categories.forEach(e => {
  73. categoriesXml = categoriesXml.concat([
  74. ' <url>',
  75. ` <loc>https://art.pcoloring.com/en/category/${e.id}</loc>`,
  76. ` <lastmod>${date}</lastmod>`,
  77. ' </url>',
  78. ]
  79. );
  80. });
  81. console.log(categoriesXml);
  82. // tags
  83. let tagsXml = [];
  84. tags.forEach(e => {
  85. tagsXml = tagsXml.concat([
  86. ' <url>',
  87. ` <loc>https://art.pcoloring.com/en/tag/${e.tag}</loc>`,
  88. ` <lastmod>${date}</lastmod>`,
  89. ' </url>',
  90. ]);
  91. });
  92. console.log(tagsXml);
  93. // albums
  94. let albums = await models.ArtAlbum
  95. .find({ pid: 'art', enabled: true })
  96. .sort({ order: 'asc' })
  97. .select('_id')
  98. .lean()
  99. .exec();
  100. let albumsXml = [];
  101. albums.forEach(e => {
  102. albumsXml = albumsXml.concat([
  103. ' <url>',
  104. ` <loc>https://art.pcoloring.com/en/coloring-page-album/${e._id}</loc>`,
  105. ` <lastmod>${date}</lastmod>`,
  106. ' </url>',
  107. ])
  108. });
  109. console.log(albumsXml);
  110. // designers
  111. let designers = await models.Art.aggregate([
  112. // 首先,过滤出 status = 9000 的文档
  113. { $match: { open: true, status: 9000 } },
  114. // 首先,根据 user 字段进行分组,并计算每个 user 出现的次数
  115. { $group: { _id: '$user', count: { $sum: 1 } } },
  116. // 然后,按照 count 字段进行降序排列
  117. { $sort: { count: -1 } },
  118. // 接着,与 users 集合进行连接,以获取用户的详细信息
  119. {
  120. $lookup: {
  121. from: 'users', // 要连接的集合名称
  122. localField: '_id', // 本地字段(即上一步分组后的 _id 字段)
  123. foreignField: '_id', // 要连接的集合中的字段
  124. as: 'userDetails' // 连接后结果存储在新字段 userDetails 中
  125. }
  126. },
  127. // 展开 userDetails 数组,以便将用户信息提升到顶层
  128. { $unwind: '$userDetails' },
  129. // 调整输出格式,只保留需要的字段
  130. { $project: { _id: 1, user: '$_id', count: 1, username: '$userDetails.username', name: '$userDetails.name' } },
  131. // 限制返回的记录数量
  132. { $limit: 40 }
  133. ]);
  134. let designersXml = [];
  135. designers.forEach(e => {
  136. designersXml = designersXml.concat([
  137. ' <url>',
  138. ` <loc>https://art.pcoloring.com/en/artist/${e._id}</loc>`,
  139. ` <lastmod>${date}</lastmod>`,
  140. ' </url>',
  141. ])
  142. });
  143. console.log(designersXml);
  144. // details
  145. let detailsXml = [];
  146. const cursor = await models.Art
  147. .find({ open: true, status: 9000 })
  148. .select('name title')
  149. .sort({ publishTime: -1 }).cursor(); // 使用cursor,避免一次查询太多记录内存受不了
  150. // 遍历游标中的每个文档
  151. for await (const doc of cursor) {
  152. let uriTitle = doc.name;
  153. if (doc.title) {
  154. try {
  155. let titleJson = JSON.parse(doc.title);
  156. if (titleJson.en) uriTitle = titleJson.en; // URL 里还是尽量用英文title
  157. } catch (e) {
  158. console.error(e.message);
  159. }
  160. }
  161. let utf8name = encodeURIComponent(uriTitle.replace(/[\s_]+/g, '-')).toLowerCase();
  162. detailsXml = detailsXml.concat([
  163. ' <url>',
  164. ` <loc>https://art.pcoloring.com/en/coloring-page/${utf8name}-${doc._id}</loc>`,
  165. ` <lastmod>${date}</lastmod>`,
  166. ' </url>',
  167. ]);
  168. console.log(`process detail ${doc._id} url done`);
  169. }
  170. let endXml = ['</urlset>'];
  171. let sitemapXml = [...commonXml, ...categoriesXml, ...tagsXml, ...albumsXml, ...designersXml, ...detailsXml, ...endXml];
  172. let sitemapXmlStr = sitemapXml.join('\n');
  173. fs.writeFileSync(sitemapTempPath, sitemapXmlStr); // 先写入临时文件
  174. fs.renameSync(sitemapTempPath, sitemapPath); // 再重命名
  175. console.log('生成sitemap成功');
  176. }
  177. function run() {
  178. generateSitemap();
  179. }
  180. module.exports = { run }
  181. if (require.main == module) {
  182. run();
  183. }