{"id":1206,"date":"2025-06-26T22:43:16","date_gmt":"2025-06-26T14:43:16","guid":{"rendered":"https:\/\/eve2333.top\/?p=1206"},"modified":"2025-06-26T22:43:16","modified_gmt":"2025-06-26T14:43:16","slug":"saas%e7%9f%ad%e9%93%be%e6%8e%a5%e7%b3%bb%e7%bb%9f-%e6%96%b0%e6%89%8b%e4%bb%8e%e9%9b%b6%e5%ad%a6%e4%b9%a0-8-%e7%9f%ad%e9%93%be%e6%8e%a5%e7%9b%91%e6%8e%a7%e4%b8%8a","status":"publish","type":"post","link":"https:\/\/eve2333.top\/?p=1206","title":{"rendered":"SaaS\u77ed\u94fe\u63a5\u7cfb\u7edf-\u65b0\u624b\u4ece\u96f6\u5b66\u4e60 8. \u77ed\u94fe\u63a5\u76d1\u63a7(\u4e0a"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">- \u7b2c01\u8282\uff1a\u77ed\u94fe\u63a5\u7edf\u8ba1\u6a21\u5757\u529f\u80fd\u5206\u6790<\/h2>\n\n\n\n<p>&nbsp;\u5927\u5bb6\u4e00\u8d77\u6765\u804a\u4e00\u4e0b\u77ed\u94fe\u63a5\u76d1\u63a7\u8be5\u600e\u4e48\u505a\uff0c\u7136\u540e\u4f17\u6240\u5468\u77e5\uff0c\u77ed\u94fe\u63a5\u5982\u679c\u8bf4\u53ea\u662f\u7528\u4f5c\u8df3\u8f6c\uff0c\u5176\u5b9e\u6709\u4e9b\u5927\u6750\u5c0f\u7528\uff0c\u867d\u7136\u8bf4\u8fd9\u662f\u5b83\u7684\u8fd9\u79cd\u57fa\u672c\u529f\u80fd\uff0c\u4f46\u662f\u66f4\u91cd\u8981\u7684\u662f\u4f60\u5728\u8c03\u6574\u7684\u8fc7\u7a0b\u5f53\u4e2d\uff0c\u5b83\u5e26\u6765\u7684\u4e00\u4e9b\u6570\u636e\u5206\u6790\u7684\u4e00\u4e9b\u8fc7\u7a0b\uff0c\u6211\u5728\u7f51\u4e0a\u627e\u7684\u4e24\u4e2a\u505a\u8fd9\u79cd\u5206\u6790\u505a\u7684\u6bd4\u8f83\u597d\u7684\uff0c\u6211\u89c9\u5f97\u9996\u5148\u7b2c\u4e00\u4e2a\u7684\u8bdd\u5c0f\u7801\u94fe\u63a5\uff0c\u7136\u540e\u6211\u4eec\u53ef\u4ee5\u770b\u5230\u5b83\u662f\u5206\u51e0\u4e2a\u66f2\u7ebf\u7684\uff0c\u9996\u5148\u7b2c\u4e00\u5c31\u662f\u5b83\u7684\u4e00\u4e2a\u8bbf\u95ee\u66f2\u7ebf\uff0c\u4e5f\u5c31\u76f8\u5f53\u4e8e\u8bf4\u8bbf\u95ee\u6b21\u6570\uff0c\u4e5f\u5c31\u662f\u6211\u4eec\u5e38\u8bf4\u7684pv\uff0c\u5c31\u76f8\u5f53\u4e8e\u4e0d\u7ba1\u662f\u4efb\u4f55\u4eba\uff0c\u4e0d\u7ba1\u4e00\u4e2a\u4eba\u8bbf\u95ee\u4e86\u591a\u5c11\u6b21\uff0c\u4f46\u51e1\u8fd9\u4e2a\u77ed\u94fe\u63a5\u8df3\u8f6c\u4e00\u6b21\u957f\u94fe\u63a5\u5b83\u5c31\u662f\u4e00\u6b21\u8bbf\u95ee\u6b21\u6570\u3002\u3001<\/p>\n\n\n\n<p>\u8bbf\u95ee\u4eba\u6570\u7684\u8bdd\uff0c\u5047\u5982\u8bf4\u6709\u4e24\u4e2a\u4eba\u8bbf\u95ee\u4e8610\u6b21\u77ed\u94fe\u63a5\uff0c\u90a3\u4e48\u5b83\u7684\u8bbf\u95ee\u4eba\u6570\u5c31\u662f\u4e8c\uff0c\u5b83\u662f\u53ea\u6839\u636e\u76f8\u5f53\u4e8eUV\u5bf9\u5427\uff1f\u5b83\u6839\u636e\u7528\u6237\u6570\u53bb\u8bbf\u95ee\u6b21\u6570\u8fdb\u884c\u7edf\u8ba1\uff0c\u800c\u4e0d\u662f\u4e00\u6765\u8bf4\u8bbf\u95ee\u4e86\u591a\u5c11\u6b21\u3002\u7136\u540eIP\u7684\u8bdd\u8fd9\u4e2a\u4e5f\u5f88\u5bb9\u6613\u89e3\u91ca\u67d0\u4e9b\u5047\u5982\u6211\u4eec\u7684\u7528\u6237\u5206\u522b\u5c5e\u4e8eIP\uff0c\u4e0b\u9762\uff0c\u90a3\u4e48IP\u7edf\u8ba1\u4e8c\u76f8\u5f53\u4e8e\u4ed6\u53c8\u5728\u7528\u6237\u53c8\u9f3b\u5b50\u4e0a\u9762\u53c8\u518d\u4e00\u6b21\u8fdb\u884c\u4e86\u7cbe\u7ec6\u5316\u76841\u4e2a\u7edf\u8ba1\uff0c\u7136\u540e\u7528\u7684\u6570\u636e\u6a21\u578b\uff0c\u7136\u540e\u4ed6\u8ddf\u7684\u65e5\u671f\u4e0b\u9762\u662f\u4ed6\u67d0\u4e00\u5929\u7684 ppuv\u4ee5\u53caIP\u6570\uff0c\u7136\u540e\u63a5\u4e0b\u6765\u7684\u8bdd\u662f\u8bbf\u95ee\u5730\u533a\uff0c\u7136\u540e\u5730\u533a\u7684\u8bdd\u5b83\u662f\u5206\u4e3a\u4e2d\u56fd\u5730\u56fe\u548c\u4e16\u754c\u5730\u56fe\uff0c\u76f8\u5f53\u4e8e\u6211\u4eec\u53ef\u4ee5\u770b\uff0c\u5176\u5b9e\u8fd9\u51e0\u6b21\u8fd9\u4e9b\u94fe\u63a5\u90fd\u662f\u6211\u81ea\u5df1\u8bbf\u95ee\u7684\uff0c\u53ef\u4ee5\u770b\u5230\u5b83\u90fd\u662f\u5728\u5317\u4eac\uff0c\u7136\u540e\u5982\u679c\u5207\u6362\u4e16\u754c\u91cc\u7684\u8bdd\uff0c\u5b83\u5c31\u662f\u5728\u4e2d\u56fd\u3002<\/p>\n\n\n\n<p>\u7136\u540e\u8fd9\u8fb9\u662f24\u5c0f\u65f6\u5206\u5e03\uff0c\u76f8\u5f53\u4e8e\u4f60\u8fd9\u4e9b\u8bf7\u6c42\u5b83\u662f\u5728\u4ec0\u4e48\u65f6\u95f4\u8bbf\u95ee\u7684\uff0c\u5c31\u662f\u4ec0\u4e48\u5c0f\u65f6\u5bf9\u5427\uff1f\u5047\u5982\u8bf4\u6211\u662f\u5728\u665a\u4e0a6\u70b9\u8bbf\u95ee\u7684\uff0c\u5b83\u8fd9\u91cc\u9762\u5c31\u5c55\u793a\u7684\u662f6:00\u8bbf\u95ee6\u6b21\uff0c\u7136\u540e\u9ad8\u9891IP\u5c31\u76f8\u5f53\u4e8e\u662f\u6211\u4eec\u7684\u4ec0\u4e48\u5bf9\u5427\uff1f\u6211\u4eec\u7684\u4f60\u8bbf\u95ee\u77ed\u94fe\u63a5\u7684\u65f6\u5019\uff0c\u5047\u5982\u6211\u5728\u6211\u7535\u8111\u4e0a\u8bbf\u95ee\uff0c\u7136\u540e\u6211\u5f53\u524d\u7684IP\u662f\u591a\u5c11\uff0c\u7136\u540e\u4ed6\u90fd\u7ed9\u4f60\u8bb0\u4e0b\u6765\uff0c\u4ed6\u53ea\u4f1a\u53d6\u8bbf\u95ee\u6b21\u6570\u6bd4\u8f83\u9ad8\u7684\u8fd9\u4e9bIP\u4e2d\u5206\u5e03\u7684\u8bdd\uff0c\u5c31\u76f8\u5f53\u4e8e\u8fd9\u4e2a\u6bd4\u8f83\u5bb9\u6613\u7406\u89e3\uff0c\u6211\u5468\u5929\u8bbf\u95ee\u7684\u65f6\u5019\u4ed6\u5c31\u8bb0\u4f4f\u5929\u5468\u516d\u8bbf\u95ee\u5468\u516d\u5927\u6982\u662f\u8fd9\u4e2a\u6837\u5b50\uff0c\u64cd\u4f5c\u7cfb\u7edf\uff0c\u7136\u540e\u5c31\u662fMAC\u7136\u540e\u6211\u4eec\u8fd9\u8fb9\u53ef\u80fd\u6709windows ubuntu\u8fd8\u6709\u4e00\u4e9b\u5176\u4ed6\u7684\u7136\u540e\u9632\u6d4f\u89c8\u5668\uff0c\u6bd4\u5982\u8bf4chrome\uff0c\u7136\u540e\u8fd8\u6709\u4e00\u4e9b\u6bd4\u5982\u8bf4\u50cf\u8fd8\u6709\u5565\u6765\u7740\uff0c\u5bf9\u5f02\u5730\u4e4b\u4e00\uff0c\u7136\u540e\u5c31\u52a0\u8d77\u6765\uff0c\u7136\u540e\u53cd\u9988\u7c7b\u578b\u7684\u8bdd\uff0c\u6bd4\u5982\u8bf4\u6211\u6211\u53bb\u8bbf\u95ee\u4e86\u4e00\u6b21\u8fd9\u4e2a\u94fe\u63a5\uff0c\u4ed6\u7684\u65b0\u8bbf\u5ba2\u5176\u5b9e\u662f\u52a0\u4e00\u7684\uff0c\u5047\u5982\u8bf4\u6211\u7b2c\u4e8c\u5929\u6216\u8005\u5176\u4ed6\u65f6\u95f4\u4ee5\u540e\uff0c\u5c31\u76f8\u5f53\u4e8e\u662f\u8001\u8bbf\u5ba2\u6570\u91cf\u5bf9\u5427\uff1f\u76ee\u524d\u7684\u8bdd\u8fd9\u4e2a\u6570\u636e\u7684\u7edf\u8ba1\u6211\u8fd8\u4e0d\u662f\u7279\u522b\u6e05\u695a\uff0c\u540e\u9762\u9700\u8981\u597d\u597d\u601d\u8003\u4e00\u4e0b\u8be5\u600e\u4e48\u505a\uff0c\u4e0d\u8fc7\u5e94\u8be5\u95ee\u9898\u4e0d\u5927\u3002<\/p>\n\n\n\n<p>\u7136\u540e\u8bbf\u95ee\u7f51\u7edc\u6bd4\u8f83\u5bb9\u6613\u7406\u89e3WiFi\u5bf9\u5427\uff1f\u79fb\u52a8\u6570\u636e\uff0c\u7136\u540e\u8bbf\u95ee\u8bbe\u5907\u662f\u7535\u8111\u8fd8\u662f\u79fb\u52a8\u8bbe\u5907\u5bf9\u5427\uff1f\u662f\u624b\u673a\u8fd8\u662f\u7535\u8111\uff0c\u7136\u540e\u8bbf\u95ee\u6765\u6e90\u4e5f\u662f\u76f4\u63a5\u8bbf\u95ee\u7684\uff0c\u8fd8\u662f\u8bf4\u901a\u8fc7\u522b\u4eba\u7684\u7f51\u9875\u5c31\u662f\u70b9\u51fb\u8df3\u8f6c\u5bf9\u5427\u7b49\u7b49\uff0c\u7136\u540e\u6211\u4e5f\u770b\u4e86\u6211\u4eec\u53e6\u5916\u4e00\u4e2a\u53eb\u505a\u7231\u77ed\u94fe\u5927\u540c\u5c0f\u5f02\uff0c\u7136\u540e\u8fd9\u4e2a\u4e1c\u897f\u5927\u81f4\u8fd9\u4e9b\u4e1c\u897f\u53cd\u6b63\u6211\u4e2a\u4eba\u89c9\u5f97\u6709\u70b9\u8fc7\u4e8e\u590d\u6742\uff0c\u4f60\u50cf\u6211\u4e3e\u4e2a\u4f8b\u5b50\uff0c\u4f60\u50cf\u6211\u4eec\u505a\u8fd9\u4e2a\u94fe\u63a5\uff0c\u5176\u5b9e\u50cf\u8fd9\u79cd\u9632\u536b\u6765\u6e90\u5bf9\u5427\uff1f<\/p>\n\n\n\n<p>\u7136\u540e\u6211\u5bf9\u8fd9\u4e9b\u6570\u636e\u505a\u4e86\u4e00\u4e9b\u5206\u6790\uff0c\u7136\u540e\u6211\u89c9\u5f97\u6211\u4eec\u9700\u8981\u4e00\u4e9b\u4ec0\u4e48\u6837\u7684\u6570\u636e\uff0c\u9996\u5148\u7b2c\u4e00\u4e2a\u5c31\u662f\u673a\u623f\u7684\u6570\u636epvuv\u4ee5\u53ca\u6211\u4eec\u7684uip\uff0c\u7136\u540e\u5730\u533a\u7684\u8bdd\uff0c\u56e0\u4e3a\u6211\u5728\u7f51\u4e0a\u627e\u4e86\u4e00\u4e9b\u5173\u4e8e\u8bbf\u95ee\u901a\u8fc7IP\u6765\u8ba1\u7b97\u5f52\u5c5e\u5730\u7684\u8fd9\u4e9bAPI\u5927\u90e8\u5206\u8bf4\u5b9e\u8bdd\u6536\u8d39\u7684\u5982\u679c\u8bf4\uff0c\u4f60\u60f3\u4e0d\u6536\u8d39\u8fd8\u505a\u5f97\u6bd4\u8f83\u597d\u7684\uff0c\u6211\u5728\u9ad8\u5fb7\u4e0a\u627e\u4e86\u4e00\u4e0b\u4ed6\u4eec\u7684 IP\u505a\u7684\u6bd4\u8f83\u597d\uff0c\u4f46\u662f\u6709\u4e00\u4e2a\u9650\u5236\u4ed6\u53ea\u80fd\u56fd\u5185\u7528\uff0c\u6240\u4ee5\u8bf4\u6211\u4eec\u53ea\u505a\u4e2d\u56fd\u5730\u56fe\uff0c\u6211\u4eec\u4e0d\u505a\u4e16\u754c\u5730\u56fe\uff0c\u7136\u540e24\u5c0f\u65f6\u5206\u5e03\u8fd9\u4e2a\u6bd4\u8f83\u597d\u505a\uff0c\u7136\u540e\u9ad8\u9891\u9632IP\u548c\u4e00\u5468\u5206\u5e03\u4e5f\u6bd4\u8f83\u597d\u505a\uff0c\u7136\u540e\u64cd\u4f5c\u7cfb\u7edf\u9632\u6d4f\u89c8\u5668\u53cd\u9988\u7c7b\u578b\u4ee5\u53ca\u8bbf\u95ee\u8bbe\u5907<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">- \u7b2c02\u8282\uff1a\u77ed\u94fe\u63a5\u7edf\u8ba1\u6a21\u5757\u6570\u636e\u5e93\u8868\u8bbe\u8ba1<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE TABLE `link`.`t_link_access_stats`  (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID',\n  `gid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '\u5206\u7ec4\u6807\u8bc6',\n  `full_short_url` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '\u5b8c\u6574\u77ed\u94fe\u63a5',\n  `date` date NULL DEFAULT NULL COMMENT '\u65e5\u671f',\n  `pv` int NULL DEFAULT NULL COMMENT '\u8bbf\u95ee\u91cf',\n  `uv` int NULL DEFAULT NULL COMMENT '\u72ec\u7acb\u8bbf\u95ee\u6570',\n  `uip` int NULL DEFAULT NULL COMMENT '\u72ec\u7acbIP\u6570',\n  `hour` int NULL DEFAULT NULL COMMENT '\u5c0f\u65f6',\n  `weekday` int NULL DEFAULT NULL COMMENT '\u661f\u671f',\n  `create_time` datetime NULL DEFAULT NULL COMMENT '\u521b\u5efa\u65f6\u95f4',\n  `update_time` datetime NULL DEFAULT NULL COMMENT '\u4fee\u6539\u65f6\u95f4',\n  `del_flag` tinyint(1) NULL DEFAULT NULL COMMENT '\u5220\u9664\u6807\u8bc6\uff1a0 \u672a\u5220\u9664 1 \u5df2\u5220\u9664',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;<\/code><\/pre>\n\n\n\n<p><s>\u522b\u5fd8\u4e86\u518d\u6dfb\u52a0\u552f\u4e00\u7d22\u5f15<\/s>\uff1aALTER TABLE t_link_access_stats ADD UNIQUE INDEX idx_unique_access_stats (full_short_url, gid, weekday, hour);<br>&nbsp;<strong>\u4e0d\u5efa\u8bae\u4f7f\u7528\u5f53\u524d\u5b57\u6bb5\u7ec4\u5408 <\/strong>\uff08full_short_url, gid, weekday, hour\uff09\uff0c\u5efa\u8bae\u8c03\u6574\u4e3a\uff1aALTER TABLE t_link_access_stats&nbsp;<br>ADD UNIQUE INDEX idx_unique_access_stats (full_short_url, gid, `date`, hour);<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>&nbsp;\u8fd9\u91cc\u5982\u679c\u8981\u5206\u8868\u7684\u8bdd\uff0c\u5206\u7247\u952e\u7528full_short_url\u66f4\u597d\u5417\uff1f\u8fd8\u662f\u7528gid\u5462\uff1f\u53ef\u80fd\u7528 full_short_url \u66f4\u597d\u4e9b\uff0c\u5982\u679c\u53d8\u66f4\u4e86\u5206\u7ec4\u7684\u8bdd\uff0c\u4fee\u6539\u5206\u7ec4\u4fe1\u606f\u7edf\u8ba1\u76d1\u63a7\u8bb0\u5f55\u592a\u591a\u4e86\u3002\u5728\u6700\u540e\u7ae0\u8282\u3010\u6027\u80fd\u4f18\u5316&amp;\u95ee\u9898\u4fee\u590d\u3011\u91cc\u6211\u628a\u6240\u6709\u76d1\u63a7\u8868\u7684 gid \u5b57\u6bb5\u5220\u6389\u4e86\u3002<\/p>\n<\/blockquote>\n\n\n\n<h2 class=\"wp-block-heading\">- \u7b2c03\u8282\uff1a\u5982\u4f55\u7edf\u8ba1\u77ed\u94fe\u63a5PV\u8bbf\u95ee<\/h2>\n\n\n\n<p>&nbsp;\u6211\u6ca1\u6709\u6dfb\u52a0\u7d22\u5f15\uff0c\u56e0\u6b64\u65e0\u9700\u5220\u9664\uff0c\u76f4\u63a5\u6267\u884c\u4ee5\u4e0b\u4ee3\u7801\u5373\u53ef<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ALTER TABLE `link`.`t_link_access_stats`\nADD UNIQUE INDEX `idx_unique_access_stats` (`full_short_url`,`gid`,`date`,`hour`) USING BTREE;<\/code><\/pre>\n\n\n\n<p>\u5148\u505a\u4e00\u4e2a\u5c31\u662f\u6280\u672f\u8bbf\u95ee\u4ee5\u53ca24\u5c0f\u65f6\u8bbf\u95ee\u4ee5\u53ca\u4e00\u5468\u5206\u5e03\uff0c\u5176\u5b9e\u8fd9\u4e9b\u90fd\u662f\u548c\u8fd9\u4e9bppuv\u8fd9\u4e9b\u4e1c\u897f\u662f\u5728\u4e00\u8d77\u7684\uff0c\u6240\u4ee5\u8bf4\u4f1a\u628a\u5b83\u89c4\u5212\u5230\u4e00\u4e2a\u6a21\u5757\u91cc\u9762\uff1b\u7136\u540e\u6211\u4eec\u5728\u8bbf\u95ee ppuv\u4ee5\u53ca IP\u7684\u65f6\u5019\uff0c\u6211\u4eec\u7b2c\u4e00\u6b65\u5c31\u662f\u5148\u5b9e\u73b0\u4e00\u4e2a\u7b80\u6613\u7248\u7684\uff0c\u56e0\u4e3a\u8981\u8ddf\u524d\u6240\u4ee5\u8bf4\u6211\u4eec\u5e94\u8be5\u5148\u5b9e\u73b0\u4e00\u4e2a\u6bd4\u8f83\u5bb9\u6613\u5b9e\u73b0\u7684\u7248\u672c\uff0c\u7b49\u5230\u5168\u90e8\u76d1\u63a7\u8bb2\u5b8c\uff0c\u6211\u4eec\u4f1a\u5bf9\u76ee\u524d\u7684\u73b0\u6709\u4ea7\u54c1\u8fdb\u884c\u91cd\u6784\uff0c\u56e0\u4e3a\u4f60\u4f1a\u53d1\u73b0\u7b2c\u4e00\u7248\u5b9e\u73b0\u7684\u8bdd\u5b83\u4f1a\u975e\u5e38\u7684\u6f0f\u6d1e\uff0c\u867d\u7136\u80fd\u5b9e\u73b0\u529f\u80fd\uff0c\u4f46\u662f\u751f\u4ea7\u57fa\u672c\u4e0a\u4e0d\u53ef\u7528\uff0c\u800c\u4f60\u60f3\u5982\u679c\u8981\u63d0\u4f9b\u4e00\u4e2aSaaS\u7248\u7684\u6d77\u91cf\u8bbf\u95ee\u7684\u4e00\u4e2a\u77ed\u94fe\u63a5\u7684\uff0c\u4e00\u4e2a\u5c31\u662f\u8df3\u8f6c\u5b83\u7684\u76d1\u63a7\u5fc5\u7136\u4e5f\u662f\u6d77\u91cf\u7684\u6570\u636e\u4ee5\u53ca\u6d77\u91cf\u7684\u8bf7\u6c42\uff0c\u6240\u4ee5\u8bf4\u8bbe\u8ba1\u8fd8\u662f\u6709\u4e9b\u8003\u8651\u7684\u9996\u5148\u6211\u4eec\u5148\u770b\u4e00\u4e0b\u6211\u4eec\u7b2c\u4e00\u7248\u600e\u4e48\u8bbe\u8ba1\u9996\u5148\u7684\u8bdd\uff0c\u6211\u4eec\u5728\u6211\u4eec\u5728\u8fd9\u4e2a\u91cc\u9762\u521b\u5efa\u4e86\u8bb0\u5f55\uff0c\u4e0a\u8282\u8bfe\u4e5f\u8bb2\u4e86\uff0c\u5b83\u662f\u6309\u7167\u5c0f\u65f6\u548c\u4fe1\u606f\u7ef4\u5ea6\uff0c\u7136\u540e\u518d\u52a0\u4e0a\u77ed\u94fe\u63a5\u662f\u6709\u7ef4\u5ea6\u7684\uff0c\u4e5f\u5c31\u76f8\u5f53\u4e8e\u5047\u5982\u5468\u4e00\u5b83\u670924\u6761\u8bb0\u5f55\uff0c\u5468\u4e8c\u670924\u6761\u8bb0\u5f55\uff0c\u5927\u6982\u662f\u8fd9\u4e2a\u6837\u5b50\u3002\u7136\u540e\u4ed6\u600e\u4e48\u53bb\u8fdb\u884c\u6570\u636e\u7edf\u8ba1\u6bd4\u8f83\u597d\u7684\u65b9\u6cd5\uff0c\u6bd4\u5982\u8bf4\u81ea\u589e\u5bf9\u5427\uff1f\u50cfRedis\u91cc\u9762\u7684\u90a3\u79cd\u81ea\u52a8increment\u7684\u65b9\u5f0f\u53bb\u505a\u5176\u5b9e\u662f\u6bd4\u8f83\u597d\u7684\uff0c\u4f46\u662f\u56e0\u4e3a\u6211\u4eecmysql\u6ca1\u6709increment\u547d\u4ee4\uff0c\u4f46\u662f\u6211\u4eec\u53ef\u4ee5\u7528\u53e6\u5916\u4e00\u4e2a\u4e1c\u897f\u53bb\u505a\u5982\u679c\u8bf4\uff0c\u6211\u4eec\u67d0\u4e2a\u8bb0\u5f55\u5b83\u5b58\u5728\u4e86\uff0c\u6211\u4eec\u5c31\u7ed9\u5b83\u8fdb\u884c\u66f4\u65b0\uff0c\u5982\u679c\u4e0d\u5b58\u5728\u5c31\u65b0\u589e\uff0c\u5176\u5b9e\u8fd8\u662f\u86ee\u7b26\u5408\u6211\u4eec\u7684\u8981\u6c42\u7684\uff0c\u6bd4\u5982\u8bf4\u6211\u4eec\u7684\u8fd9\u6761\u77ed\u94fe\u63a5\u5df2\u7ecf\u5b58\u5728\u4e86\uff0c\u5bf9\u5427\uff1f\u6211\u4eec\u5c31\u628a\u521d\u59cb\u7684\u503c\u7ed9\u5b83\u52a0\u8fdb\u53bb\uff0c\u5982\u679c\u8bf4\u4e0d\u8bf4\u9519\u4e86\uff0c\u77ed\u4fe1\u8bb0\u5f55\u4e0d\u5b58\u5728\uff0c\u6211\u4eec\u5c31\u628a\u8fd9\u4e9b\u521d\u59cb\u7684\u503c\u8fd9\u4e9b\u96f6\u5b83\u7684\u5b9e\u9645\u8bbf\u95ee\u91cf\u7136\u540e\u628a\u5b83\u52a0\u8fdb\u53bb\uff0c\u5982\u679c\u5df2\u7ecf\u5b58\u5728\uff0c\u90a3\u4e48\u5c31\u5728\u5b83\u539f\u6709\u7684\u57fa\u7840\u4e0a\u7ed9\u5b83\u52a0\u5165\u6307\u5b9a\u7684\u6570\u503c\u5927\u6982\u6837\u5b50\uff0c\u5982\u679c\u8981\u5b9e\u73b0\u8fd9\u4e2a\u529f\u80fd\u7684\u8bdd\uff0c\u6211\u4eec\u662f\u9700\u8981\u5728\u8fd9\u91cc\u9762\u52a0\u4e00\u4e2a\u552f\u4e00\u7d22\u5f15\u7684\u662f\u8bed\u6cd5\u7684\u4e00\u4e2a\u9650\u5236\uff0c\u6211\u4eec\u5148\u52a0\u4e00\u4e0b\u7ed9\u5927\u5bb6\u6f14\u793a\u4e00\u4e0b\u6548\u679c\u3002<\/p>\n\n\n\n<p>\u7136\u540e\u5728\u8fd9\u91cc\uff0c\u6bd4\u5982\u8bf4\u6211\u4eec\u7684\u94fe\u63a5\u91cc\u9762\u662f\u6709\u8bb0\u5f55\u7684\uff0c\u4f46\u662f\u4e0d\u662f\u6211\u4eec\u94fe\u6761\uff0c\u8fd9\u4e2a\u65f6\u5019\u6211\u4eec\u628a\u8fd9\u6761\u8bb0\u5f55\u6267\u884c\u4e00\u4e0b\uff0c\u662f\u4ec0\u4e48\u60c5\u51b5\uff0c\u65b0\u7684\u903b\u8f91\u5c31\u662f\u4fee\u6539GPU\uff0cIP\u90fd\u52a0\u6570\u503c\uff0cpv\uff0cuv\u4ee5\u53cauip\u90fd\u5404\u81ea\u4ef7\u683c\u6570\u503c\uff0c\u73b0\u5728\u6211\u4eec\u770b\u5176\u5b9e\u8fd9\u91cc\u9762\u7684\u8bdd\u5b83\u7684\u77ed\u94fe\u63a5\u53ea\u6709\u8fd9\u6837\u4e00\u6761\u8bb0\u5f55\uff0c\u5176\u5b9e\u662f\u6ca1\u6709\u5bf9\u5b83\u8fdb\u884c\u65b0\u589e\u7684\uff0c\u6ca1\u6709\u8fdb\u884c\u4fee\u6539\u7684\u3002&nbsp;\u8fd9\u4e2a\u65f6\u5019\u6211\u4eec\u73b0\u5728\u5df2\u7ecf\u6709\u8bb0\u5f55\uff0c\u6211\u4eec\u6267\u884c\u6211\u4eec\u5237\u65b0\u4e00\u4e0b\u770b\u662f\u4e0d\u662fP\u4e00IP\u90fd\u5df2\u7ecf\u53d8\u4e86\u5bf9\u5427\uff1f\u57fa\u4e8e\u8fd9\u79cd\u573a\u666f\u6211\u4eec\u5e94\u8be5\u77e5\u9053\u600e\u4e48\u6837\u53bb\u505a\u4e86\u5bf9\u5427\uff1f Ok\u63a5\u4e0b\u6765\u6211\u4eec\u53bb\u5199\u4ee3\u7801\uff0c\u9996\u5148\u6211\u4eec\u5728\u6570\u636e\u5e93\u5c42\u9762\u7ed9\u4ed6\u53bb\u505a\u81ea\u589e\uff0c\u5df2\u7ecf\u660e\u767d\u8be5\u600e\u4e48\u53bb\u505a\u4e86\uff0c\u4f46\u662f\u63a5\u4e0b\u6765\u8fd8\u6709\u4e2a\u95ee\u9898\u5c31\u662f\u5b83\u7684pvuv\u4ee5\u53ca\u5bf9\u5e94\u7684IP\u8be5\u600e\u4e48\u53bb\u505a\u597d\uff1f\u6211\u4eec\u7b2c\u4e00\u8282\u8bfe\u5c31\u662f\u5148\u8bb2\u5c31\u662f\u8fd9\u4e9b\u6bd4\u8f83\u57fa\u7840\u7684\uff0c\u7136\u540e\u6bd4\u5982\u8bf4\u8bbf\u95ee\u6b21\u6570\uff0c\u8bbf\u95ee\u6b21\u6570\u5c31\u662fpv\u5bf9\u5427\uff1f\u7528\u6237\u8bbf\u95ee\u4e00\u6b21\u6211\u5c31\u5bf9\u8fd9\u4e2a\u77ed\u94fe\u63a5\u52a0\u4e00\u6b21\u6570\u503c\u5c31\u53ef\u4ee5\u4e86\uff0cok\u7136\u540e\u8fd9\u4e2a\u8981\u5728\u4ec0\u4e48\u65f6\u5019\u53bb\u7ed9\u5b83\u8fdb\u884c\u505a\u5c31\u662f\u5728\u77ed\u94fe\u63a5\u8bbf\u95ee\u7684\u65f6\u5019\uff0c\u5e94\u8be5\u662f\u5728\u5bf9\u4e00\u8df3\u8f6c\u8fd9\u4e00\u6b65\u3002<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>\u5982\u679c\u4f60\u6dfb\u52a0\u4e86\u7d22\u5f15\uff0c\u8bf7\u6309\u7167\u4ee5\u4e0b\u65b9\u5f0f<\/p>\n\n\n\n<p>\u975e\u5e38\u611f\u8c22\u5927\u5bb6\u7684\u6307\u6b63\uff0c\u7ecf\u6392\u67e5\u7d22\u5f15\u786e\u5b9e\u52a0\u7684\u6709\u95ee\u9898\uff0c\u5df2\u5728\u6570\u636e\u5e93\u521d\u59cb\u5316\u6587\u4ef6\u4e2d\u4fee\u590d\u3002\u53e6\u5916\uff0c\u9488\u5bf9\u5df2\u6709\u7684\u6570\u636e\u5e93\u8868\uff0c\u8bf7\u5927\u5bb6\u6267\u884c\u4ee5\u4e0b\u8bed\u53e5\uff1a ALTER TABLE `link`.`t_link_access_stats` DROP INDEX `idx_unique_access_stats`, ADD UNIQUE INDEX `idx_unique_access_stats` (`full_short_url`,`gid`,`date`,`hour`) USING BTREE;&nbsp;<\/p>\n<\/blockquote>\n\n\n\n<p>dao\u7684entity\u65b0\u5efaDO \u5982\u4e0b<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">package com.nageoffer.shortlink.project.dao.entiry;\n\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.nageoffer.shortlink.project.common.database.BaseDO;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.Date;\n\n\/**\n * \u77ed\u94fe\u63a5\u57fa\u7840\u8bbf\u95ee\u76d1\u63a7\u5b9e\u4f53\n *\/\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\n@TableName(\"t_link_access_stats\")\npublic class LinkAccessStatsDO extends BaseDO {\n    \/**\n     * id\n     *\/\n    private Long id;\n    \/**\n     * \u5b8c\u6574\u77ed\u94fe\u63a5\n     *\/\n    private String fullShortUrl;\n    \/**\n     * \u5206\u7ec4\u6807\u8bc6\n     *\/\n    private String gid;\n    \/**\n     * \u65e5\u671f\n     *\/\n    private Date date;\n    \/**\n     * \u72ec\u7acb\u8bbf\u5ba2\u6570\n     *\/\n    private Integer uv;\n    \/**\n     * \u8bbf\u95ee\u91cf\n     *\/\n    private Integer pv;\n    \/**\n     * \u72ec\u7acbip\u6570\n     *\/\n    private Integer uip;\n    \/**\n     * \u5c0f\u65f6\n     *\/\n    private Integer hour;\n    \/**\n     * \u661f\u671f\n     *\/\n    private Integer weekday;\n}\n<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"16e7de31\">1. <strong>PV\uff08Page View\uff09\u9875\u9762\u6d4f\u89c8\u91cf<\/strong><\/h3>\n\n\n\n<p id=\"u826e35fd\"><strong>\u5b9a\u4e49<\/strong>\uff1aPV \u4ee3\u8868\u9875\u9762\u6d4f\u89c8\u91cf\uff08Page View\uff09\uff0c\u662f\u6307\u7f51\u7ad9\u6216\u5e94\u7528\u4e2d\u67d0\u4e2a\u9875\u9762\u88ab\u7528\u6237\u8bbf\u95ee\u7684\u6b21\u6570\u3002\u6bcf\u6b21\u7528\u6237\u52a0\u8f7d\u9875\u9762\uff0c\u90fd\u4f1a\u589e\u52a0\u4e00\u4e2a PV\uff0c\u65e0\u8bba\u8fd9\u4e2a\u7528\u6237\u662f\u5426\u662f\u540c\u4e00\u4e2a\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"a8b3f843\">2. <strong>UV\uff08Unique Visitor\uff09\u72ec\u7acb\u8bbf\u5ba2\u6570<\/strong><\/h3>\n\n\n\n<p id=\"u42296c89\"><strong>\u5b9a\u4e49<\/strong>\uff1aUV \u4ee3\u8868\u72ec\u7acb\u8bbf\u5ba2\u6570\uff08Unique Visitor\uff09\uff0c\u662f\u6307\u5728\u4e00\u5b9a\u65f6\u95f4\u5185\uff0c\u8bbf\u95ee\u7f51\u7ad9\u6216\u5e94\u7528\u7684\u4e0d\u540c\u7528\u6237\u6570\u91cf\u3002\u6bcf\u4e2a\u7528\u6237\u5728\u8be5\u65f6\u95f4\u6bb5\u5185\u65e0\u8bba\u8bbf\u95ee\u591a\u5c11\u6b21\uff0c\u90fd\u4f1a\u88ab\u8ba1\u7b97\u4e3a\u4e00\u4e2a UV\u3002<\/p>\n\n\n\n<p>&nbsp;\u65b0\u5efamapper\u5982\u4e0bLinkAccessStatsMapper<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">import com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.nageoffer.shortlink.project.dao.entiry.LinkAccessStatsDO;\nimport org.apache.ibatis.annotations.Insert;\nimport org.apache.ibatis.annotations.Param;\n\n\/**\n * \u77ed\u94fe\u63a5\u57fa\u7840\u8bbf\u95ee\u76d1\u63a7\u6301\u4e45\u5c42\n *\/\npublic interface LinkAccessStatsMapper extends BaseMapper&lt;LinkAccessStatsDO&gt; {\n\n    \/**\n     * \u8bb0\u5f55\u57fa\u7840\u8bbf\u95ee\u76d1\u63a7\u6570\u636e\n     *\/\n    @Insert(\"INSERT INTO t_link_access_stats (full_short_url, gid, date, pv, uv, uip, hour, weekday, create_time, update_time, del_flag) \" +\n            \"VALUES( #{linkAccessStats.fullShortUrl}, #{linkAccessStats.gid}, #{linkAccessStats.date}, #{linkAccessStats.pv}, #{linkAccessStats.uv}, #{linkAccessStats.uip}, #{linkAccessStats.hour}, #{linkAccessStats.weekday}, NOW(), NOW(), 0) ON DUPLICATE KEY UPDATE pv = pv +  #{linkAccessStats.pv}, \" +\n            \"uv = uv + #{linkAccessStats.uv}, \" +\n            \" uip = uip + #{linkAccessStats.uip};\")\n    void shortLinkStats(@Param(\"linkAccessStats\") LinkAccessStatsDO linkAccessStatsDO);\n}<\/pre>\n\n\n\n<p>\u5f00\u5934\u653e\u5165&nbsp; &nbsp; private final LinkAccessStatsMapper linkAccessStatsMapper;<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">private void shortLinkStats(String fullShortUrl, String gid, ServletRequest request, ServletResponse response) {\n    try {\n        if (StrUtil.isBlank(gid)) {\n            LambdaQueryWrapper&lt;ShortLinkGotoDO&gt; queryWrapper = Wrappers.lambdaQuery(ShortLinkGotoDO.class)\n                    .eq(ShortLinkGotoDO::getFullShortUrl, fullShortUrl);\n            ShortLinkGotoDO shortLinkGotoDO = shortLinkGotoMapper.selectOne(queryWrapper);\n            gid = shortLinkGotoDO.getGid();\n        }\n        int hour = DateUtil.hour(new Date(), true);\n        Week week = DateUtil.dayOfWeekEnum(new Date());\n        int weekValue = week.getIso8601Value();\n        LinkAccessStatsDO linkAccessStatsDO = LinkAccessStatsDO.builder()\n                .pv(1)\n                .uv(1)\n                .uip(1)\n                .hour(hour)\n                .weekday(weekValue)\n                .fullShortUrl(fullShortUrl)\n                .gid(gid)\n                .date(new Date())\n                .build();\n        linkAccessStatsMapper.shortLinkStats(linkAccessStatsDO);\n    } catch (Throwable ex) {\n        log.error(\"\u77ed\u94fe\u63a5\u8bbf\u95ee\u91cf\u7edf\u8ba1\u5f02\u5e38\", ex);\n    }\n}<\/pre>\n\n\n\n<p><img loading=\"lazy\" decoding=\"async\" height=\"445\" width=\"680\" src=\"https:\/\/i-blog.csdnimg.cn\/direct\/eaf5175160f342cb8c1854a0a1f54c0a.png\" alt=\"\"><\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>&nbsp;\u8fd9\u91cc\u7684\u552f\u4e00\u7d22\u5f15\u662f\u4e0d\u662f\u8981\u6dfb\u52a0\u2018\u65e5\u671f\u2019\u8fdb\u5165\u590d\u5408\u7d22\u5f15\u4e2d\uff0c\u4e0d\u7136\u7684\u8bdd\u5bf9\u4e8e\u540c\u4e00\u4e2a\u94fe\u63a5\uff0c\u6211\u8fd9\u5468\u4e0012\u70b9\u8bbf\u95ee\u7684\u4e0d\u5c31\u52a0\u5230\u4e0a\u5468\u4e0012\u70b9\u4e0a\u9762\u53bb\u4e86\uff1b\u540e\u9762\u6211\u8bb0\u5f97\u52a0\u4e86\uff0c\u53ef\u4ee5\u770b\u770b<\/p>\n<\/blockquote>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>&nbsp;\u8fd9\u91cc\u6700\u597d\u4e0d\u8981new\u5f88\u591aDate() \u63d0\u524dnew\u4e00\u4e2a\u6bd4\u8f83\u597d\uff0c\u6709\u6781\u5c0f\u6982\u7387\uff0c\u521a\u597d\u8de80\u70b9\u521b\u5efa\uff0c\u4f1a\u6709\u9519\u8bef\u3002\u521b\u5efa\u65f6\u95f4\u5c31\u5728insert\u8bed\u53e5\u7684\u65f6\u5019\u7528now\u5c31\u53ef\u4ee5\uff0c\u66f4\u65b0\u65f6\u95f4\u53ef\u4ee5\u5728ON DUPLICATE KEY UPDATE\u540e\u9762\u518d\u52a0\u4e00\u4e2a\u201cupdate_time = NOW()\u201d<\/p>\n<\/blockquote>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>&nbsp;java8\u7684\u65f6\u95f4api\u81ea\u5e26\u7b2c\u51e0\u5c0f\u65f6\u7b2c\u51e0\u5929\uff0c\u5c31\u4e0d\u7528hutool\u7684\u4e86\uff0cint hourOfDay = LocalTime.now().getHour(); int dayOfWeek = LocalDate.now().getDayOfWeek().getValue(); \u800c\u4e14\u628alinkAccesStatsDO\u4e2d\u7684\u65f6\u95f4\u8bbe\u7f6e\u4e3aLocalDate\uff0c\u7136\u540e\u7ed9\u65e5\u671f\u7684\u65f6\u5019\u76f4\u63a5LocalDate.now()\u7ed9\u7684\u5c31\u662f\u5f53\u524d\u65e5\u671f<\/p>\n<\/blockquote>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>&nbsp;\u8003\u8651\u591a\u4e2a\u7528\u6237\u8bbf\u95ee\u540c\u4e00\u4e2a\u77ed\u94fe\u63a5\u7684\u8bdd\uff0c\u76d1\u63a7\u90e8\u5206\u4e0d\u7528\u52a0\u9501<\/p>\n<\/blockquote>\n\n\n\n<h2 class=\"wp-block-heading\">- \u7b2c04\u8282\uff1a\u5982\u4f55\u7edf\u8ba1\u77ed\u94fe\u63a5UV\u8bbf\u95ee<\/h2>\n\n\n\n<p>\u5355\u4e2a\u7528\u6237\u8bbf\u95ee\u591a\u6b21\u76f8\u540c\u77ed\u94fe\u63a5\uff0c\u8be5\u77ed\u94fe\u63a5\u8bb0\u5f55\u7528\u6237\u6570\u4e3a1\u6b21\u3002<img loading=\"lazy\" decoding=\"async\" height=\"181\" width=\"675\" src=\"https:\/\/i-blog.csdnimg.cn\/direct\/01a0e4b685b542948bcd5351ae055e17.png\" alt=\"\"><\/p>\n\n\n\n<p>\u4ec0\u4e48\u662fcookie\uff1f&nbsp;<a href=\"https:\/\/www.yuque.com\/shenshendenaohaili-cdskw\/few4bm\/oxo5po14iay3idnl?singleDoc#\" target=\"_blank\"  rel=\"nofollow\" >Cookie\u548cSession<\/a><\/p>\n\n\n\n<p>LinkAccessStatsDO.java&nbsp;<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import com.baomidou.mybatisplus.annotation.TableName;\nimport com.nageoffer.shortlink.project.common.database.BaseDO;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.Date;\n\n\/**\n * \u77ed\u94fe\u63a5\u57fa\u7840\u8bbf\u95ee\u76d1\u63a7\u5b9e\u4f53\n *\/\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\n@TableName(\"t_link_access_stats\")\npublic class LinkAccessStatsDO extends BaseDO {\n\n    \/**\n     * id\n     *\/\n    private Long id;\n\n    \/**\n     * \u5b8c\u6574\u77ed\u94fe\u63a5\n     *\/\n    private String fullShortUrl;\n\n    \/**\n     * \u5206\u7ec4\u6807\u8bc6\n     *\/\n    private String gid;\n\n    \/**\n     * \u65e5\u671f\n     *\/\n    private Date date;\n\n    \/**\n     * \u8bbf\u95ee\u91cf\n     *\/\n    private Integer pv;\n\n    \/**\n     * \u72ec\u7acb\u8bbf\u5ba2\u6570\n     *\/\n    private Integer uv;\n\n    \/**\n     * \u72ec\u7acbip\u6570\n     *\/\n    private Integer uip;\n\n    \/**\n     * \u5c0f\u65f6\n     *\/\n    private Integer hour;\n\n    \/**\n     * \u661f\u671f\n     *\/\n    private Integer weekday;\n}<\/code><\/pre>\n\n\n\n<p>&nbsp;\\projectdao\\mapper\u65b0\u5efaLinkAccessStatsMapper.java&nbsp;<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.nageoffer.shortlink.project.dao.entity.LinkAccessStatsDO;\nimport org.apache.ibatis.annotations.Insert;\nimport org.apache.ibatis.annotations.Param;\n\n\/**\n * \u77ed\u94fe\u63a5\u57fa\u7840\u8bbf\u95ee\u76d1\u63a7\u6301\u4e45\u5c42\n *\/\npublic interface LinkAccessStatsMapper extends BaseMapper&lt;LinkAccessStatsDO&gt; {\n\n    \/**\n     * \u8bb0\u5f55\u57fa\u7840\u8bbf\u95ee\u76d1\u63a7\u6570\u636e\n     *\/\n    @Insert(\"INSERT INTO t_link_access_stats (full_short_url, gid, date, pv, uv, uip, hour, weekday, create_time, update_time, del_flag) \" +\n            \"VALUES( #{linkAccessStats.fullShortUrl}, #{linkAccessStats.gid}, #{linkAccessStats.date}, #{linkAccessStats.pv}, #{linkAccessStats.uv}, #{linkAccessStats.uip}, #{linkAccessStats.hour}, #{linkAccessStats.weekday}, NOW(), NOW(), 0) ON DUPLICATE KEY UPDATE pv = pv +  #{linkAccessStats.pv}, \" +\n            \"uv = uv + #{linkAccessStats.uv}, \" +\n            \" uip = uip + #{linkAccessStats.uip};\")\n    void shortLinkStats(@Param(\"linkAccessStats\") LinkAccessStatsDO linkAccessStatsDO);\n}<\/code><\/pre>\n\n\n\n<p>\u524d\u9762\u52a0\u4e2a&nbsp; &nbsp; &nbsp;private final LinkAccessStatsMapper linkAccessStatsMapper;<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    @SneakyThrows\n    @Override\n    public void restoreUrl(String shortUri, ServletRequest request, ServletResponse response) {\n        String serverName = request.getServerName();\n        String fullShortUrl = serverName + \"\/\" + shortUri;\n        String originalLink = stringRedisTemplate.opsForValue().get(String.format(GOTO_SHORT_LINK_KEY, fullShortUrl));\n        if (StrUtil.isNotBlank(originalLink)) {\n            shortLinkStats(fullShortUrl, null, request, response);\n            ((HttpServletResponse) response).sendRedirect(originalLink);\n            return;\n        }\n        boolean contains = shortUriCreateCachePenetrationBloomFilter.contains(fullShortUrl);\n        if (!contains) {\n            ((HttpServletResponse) response).sendRedirect(\"\/page\/notfound\");\n            return;\n        }\n        String gotoIsNullShortLink = stringRedisTemplate.opsForValue().get(String.format(GOTO_IS_NULL_SHORT_LINK_KEY, fullShortUrl));\n        if (StrUtil.isNotBlank(gotoIsNullShortLink)) {\n            ((HttpServletResponse) response).sendRedirect(\"\/page\/notfound\");\n            return;\n        }\n        RLock lock = redissonClient.getLock(String.format(LOCK_GOTO_SHORT_LINK_KEY, fullShortUrl));\n        lock.lock();\n        try {\n            originalLink = stringRedisTemplate.opsForValue().get(String.format(GOTO_SHORT_LINK_KEY, fullShortUrl));\n            if (StrUtil.isNotBlank(originalLink)) {\n                shortLinkStats(fullShortUrl, null, request, response);\n                ((HttpServletResponse) response).sendRedirect(originalLink);\n                return;\n            }\n            LambdaQueryWrapper&lt;ShortLinkGotoDO&gt; linkGotoQueryWrapper = Wrappers.lambdaQuery(ShortLinkGotoDO.class)\n                    .eq(ShortLinkGotoDO::getFullShortUrl, fullShortUrl);\n            ShortLinkGotoDO shortLinkGotoDO = shortLinkGotoMapper.selectOne(linkGotoQueryWrapper);\n            if (shortLinkGotoDO == null) {\n                stringRedisTemplate.opsForValue().set(String.format(GOTO_IS_NULL_SHORT_LINK_KEY, fullShortUrl), \"-\", 30, TimeUnit.MINUTES);\n                ((HttpServletResponse) response).sendRedirect(\"\/page\/notfound\");\n                return;\n            }\n            LambdaQueryWrapper&lt;ShortLinkDO&gt; queryWrapper = Wrappers.lambdaQuery(ShortLinkDO.class)\n                    .eq(ShortLinkDO::getGid, shortLinkGotoDO.getGid())\n                    .eq(ShortLinkDO::getFullShortUrl, fullShortUrl)\n                    .eq(ShortLinkDO::getDelFlag, 0)\n                    .eq(ShortLinkDO::getEnableStatus, 0);\n            ShortLinkDO shortLinkDO = baseMapper.selectOne(queryWrapper);\n            \/\/stringRedisTemplate.opsForValue().set(String.format(GOTO_SHORT_LINK_KEY, fullShortUrl), shortLinkDO.getOriginUrl());\n            \/\/if (shortLinkDO != null) {\n            \/\/if (shortLinkDO.getValidDate() != null &amp;&amp; shortLinkDO.getValidDate().before(new Date())) {\n            if (shortLinkDO == null || (shortLinkDO.getValidDate() != null &amp;&amp; shortLinkDO.getValidDate().before(new Date()))) {\n                stringRedisTemplate.opsForValue().set(String.format(GOTO_IS_NULL_SHORT_LINK_KEY, fullShortUrl), \"-\", 30, TimeUnit.MINUTES);\n                ((HttpServletResponse) response).sendRedirect(\"\/page\/notfound\");\n                return;\n            }\n            stringRedisTemplate.opsForValue().set(\n                    String.format(GOTO_SHORT_LINK_KEY, fullShortUrl),\n                    shortLinkDO.getOriginUrl(),\n                    LinkUtil.getLinkCacheValidTime(shortLinkDO.getValidDate()), TimeUnit.MILLISECONDS\n            );\n            shortLinkStats(fullShortUrl, shortLinkDO.getGid(), request, response);\n            ((HttpServletResponse) response).sendRedirect(shortLinkDO.getOriginUrl());\n\n        } finally {\n            lock.unlock();\n        }\n    }\n\n\n    private void shortLinkStats(String fullShortUrl, String gid, ServletRequest request, ServletResponse response) {\n        try {\n            if (StrUtil.isBlank(gid)) {\n                LambdaQueryWrapper&lt;ShortLinkGotoDO&gt; queryWrapper = Wrappers.lambdaQuery(ShortLinkGotoDO.class)\n                        .eq(ShortLinkGotoDO::getFullShortUrl, fullShortUrl);\n                ShortLinkGotoDO shortLinkGotoDO = shortLinkGotoMapper.selectOne(queryWrapper);\n                gid = shortLinkGotoDO.getGid();\n            }\n            int hour = DateUtil.hour(new Date(), true);\n            Week week = DateUtil.dayOfWeekEnum(new Date());\n            int weekValue = week.getIso8601Value();\n            LinkAccessStatsDO linkAccessStatsDO = LinkAccessStatsDO.builder()\n                    .pv(1)\n                    .uv(1)\n                    .uip(1)\n                    .hour(hour)\n                    .weekday(weekValue)\n                    .fullShortUrl(fullShortUrl)\n                    .gid(gid)\n                    .date(new Date())\n                    .build();\n            linkAccessStatsMapper.shortLinkStats(linkAccessStatsDO);\n        } catch (Throwable ex) {\n            log.error(\"\u77ed\u94fe\u63a5\u8bbf\u95ee\u91cf\u7edf\u8ba1\u5f02\u5e38\", ex);\n        }\n    }<\/code><\/pre>\n\n\n\n<p>feature:\u5f00\u53d1\u77ed\u94fe\u63a5\u76d1\u63a7\u4e4b\u57fa\u7840\u8bbf\u95eePV\u6570\u636e<\/p>\n\n\n\n<p>\u5982\u679c\u8bf4\u4f60\u8981\u53bb\u7ed9\u4ed6\u4ed6\u8fd4\u56deUV\u7684\u8bdd\u6211\u4eec\u5e94\u8be5\u5e72\u4ec0\u4e48\uff1f\u6211\u4eec\u8981\u5b9a\u4e00\u4e2auuID\u3002\u7b2c\u4e8c\u5c31\u662fID\u5e94\u8be5\u662f\u53eb UV flag. \u7136\u540e\u63a5\u4e0b\u6765\u6211\u4eec\u8981\u521b\u5efa\u4e00\u4e2acookie\u7684\u6807\u8bc6\uff0cUV cookie\u7b49\u4e8enew\u4e00\u4e2acookie\uff0c\u7528\u4e00\u4e2acookie\u7684\u8bdd\u6211\u4eec\u5c31\u53ebUV\uff08cook\u4e5f\u8981\u7ed9\u5b83\u8bbe\u7f6e\u4e00\u4e2a\u6807\u8bc6\u65f6\u95f4\uff1b\u56e0\u4e3a\u8981\u6709\u4e00\u4e2a\u8fc7\u671f\u65f6\u95f4\u7684set\uff0cmax\uff0c\u7136\u540e\u8fd9\u4e2a\u662f\u79d2\uff0c\u7136\u540e\u518d\u4e58\u4e00\u4e2a\u5206\u949f\uff0c\u518d\u4e58\u4ee524\u5c31\u662f\u4e00\u5929\uff0c\u7136\u540e\u518d\u4e58\u4ee530\uff0c1\u4e2a\u6708\uff0c\u76f8\u5f53\u4e8e cookie\u5b58\u5728\u4e8e\u6211\u4eec\u6d4f\u89c8\u4e00\u4e2a\u6708\uff0c\u5982\u679c\u8bf4\u4e00\u4e2a\u6708\u4e4b\u5185\u4f60\u7528\u6237\u53bb\u8bbf\u95ee\u6211\u4eec\u7684\u77ed\u94fe\u63a5\uff0c\u4ed6\u5c31\u80fd\u6807\u8bc6\u540c\u4e00\u4e2a\u4eba\uff0c\u4e00\u4e2a\u6708\u4e4b\u540e\uff0c\u4e0d\u7ba1\u56e0\u4e3a\u4ece\u6211\u4eec\u77ed\u94fe\u63a5\u7684\u89d2\u5ea6\u4e0a\u5f88\u96be\u6709\u7528\u6237\u5bf9\u5427\uff1f\u4e00\u4e2a\u6708\u4e4b\u540e\u8fd8\u8bbf\u95ee\u5982\u679c\u8bf4\uff0c\u4f60\u60f3\u628a\u7528\u6237\u7684\u6807\u6ce8\u65f6\u95f4\u62c9\u957f\uff0c\u76f4\u63a5\u628a cookie\u7684\u65f6\u95f4\u8bbe\u7f6e\u957f\u4e00\u70b9\u5c31\u53ef\u4ee5\u4e86\u3002\uff09\u3002<\/p>\n\n\n\n<p>\u7136\u540e\u8fd9\u6837\u7684\u8bdd\u6211\u4eec\u8981\u53bb\u7ed9response\uff0c\u7136\u540e\u6211\u4eecUV\u6211\u60f3\u60f3\u3002\u7136\u540eUV cookie\u70b9set path\u3002 Pass\u4ec0\u4e48\u610f\u601d\uff1f\u56e0\u4e3a\u6211\u4eec\u90fd\u77e5\u9053 cookie\u8fd9\u4e9b\u4e1c\u897f\u662f\u8ddf\u57df\u540d\u90a3\u4e9b\u4e1c\u897f\u6302\u94a9\u7684\uff0c\u5982\u679c\u8bf4\u4f60\u4e0d\u8bbe\u7f6epath\u7684\u8bdd\uff0c\u5b83\u7ed9\u4f60\u6574\u4e2a\u57df\u540d\u5168\u90e8\u5c31\u662f\u5b83\u7684\u4f5c\u7528\uff0c\u5982\u679c\u662f\u4f60\u6574\u4e2a\u57df\u540d\u7684\u8bdd\uff0c\u6211\u4e3e\u4e2a\u4f8b\u5b50\uff0c\u5982\u679c\u8bf4\u4eba\u5bb6\u8bbf\u95ee\u7684\u662f\u4f60\u7684nageoffer\u4e0b\u9762\u7684\u4e1c\u897f\u5bf9\u5427\uff1f\u5b83\u5168\u90e8\u90fd\u4f1a\u8ba4\u4e3a\u5df2\u7ecf\u767b\u5f55\u7684\u5c31\u662f\u8fd9\u4e2a\u4e0d\u662fso URL\u4e0b\u9762\u5bf9\u5427\uff1f\u4f60\u8bbf\u95ee\u4e0d\u540c\u7684\u7aef\u94fe\u63a5\uff0c\u4eba\u5bb6\u90fd\u4f1a\u4ee5\u4e3a\u4f60\u5df2\u7ecf\u767b\u5f55\u4e86\uff0c\u6240\u4ee5\u8bf4\u6211\u4eec\u8981\u6839\u636e\u4ec0\u4e48\uff1f\u6211\u4eec\u8981\u6839\u636e\u5b83\u7684\u4e00\u4e2a path\u53bb\u505a\uff0c\u53bb\u505a\u4e00\u4e2a\u6807\u8bc6\uff0c\u5bf9\u3002\u56e0\u4e3a\u6211\u4eec\u8fd9\u8fb9pass\u7684\u8bdd\u662f\u8981\u6839\u636e\u6211\u4eec\u77ed\u57df\u540d\u7684\u540e\u9762\u7684\u6807\u8bc6\u53bb\u505a\u7684\uff0c\u8fd9\u6211\u4eec\u8fd9\u8fb9\u662f\u4e2afullshorturl\uff0c\u6240\u4ee5\u8bf4\u6211\u4eec\u8981\u7ed9\u5b83\u8fdb\u884c\u4e00\u4e2a\u5207\u5272\uff0c\u7136\u540e\u5207\u5272\u7684\u8bdd\u5c31\u662f\u4ee5\u77ed\u57df\u540d\u540e\u9762\u6760\u4e3a\u6807\u8bc6\u7684\uff0cfullShortUrl.indexOf(\"\/\"), fullShortUrl.length())\uff0c\u5bf9\u3002\u7136\u540e\u8fd9\u6837\u7684\u8bdd\u6211\u4eec\u7ed9response\u8bbe\u7f6e\u4e00\u4e2acookie\uff0c\u8fd8\u5f97\u7ed9\u4ed6\u53bb\u505a\u5f3a\u8f6cHTTP&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;((HttpServletResponse) response)<\/p>\n\n\n\n<p>\u6210\u529f\u4e4b\u540e\u8fd8\u8981\u5e72\u4ec0\u4e48\uff1f&nbsp;\u5ba2\u6237\u7aef\u91cc\u9762\u4f20\u7684cookie\u4e0d\u7b49\u4e8e\u7a7a\uff0c\u90a3\u4e48\u6211\u4eec\u5f00\u59cb\u7ed9\u5b83\u8fdb\u884c\u5224\u65ad\u6709\u6ca1\u6709\u6211\u4eec\u7684uVcookie\u3002 stringRedisTemplate.opsForSet().add(\"short-link:stats:uv:\" + fullShortUrl, uv);\u5b9e\u73b0\u90a3\u4e48\u5c31\u662f\u90a3\u4e2a\u5df2\u7ecf\u5df2\u7ecf\u8bbf\u95ee\u8fc7\u4e86\uff0cset\u7ed3\u6784\u6765+1.each\u3001\u662f\u90a3\u4e2a\u7ed3\u6784\u6807\u8bc6<\/p>\n\n\n\n<p>\u5df2\u7ecf\u5b58\u5728\u90a3\u4e48uv\u662f\u4e0d\u8fdb\u884c+1\u7684<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">private void shortLinkStats(String fullShortUrl, String gid, ServletRequest request, ServletResponse response) {\n    AtomicBoolean uvFirstFlag = new AtomicBoolean();\n    Cookie[] cookies = ((HttpServletRequest) request).getCookies();\n    try {\n        Runnable addResponseCookieTask = () -&gt; {\n            String uv = UUID.fastUUID().toString();\n            Cookie uvCookie = new Cookie(\"uv\", uv);\n            uvCookie.setMaxAge(60 * 60 * 24 * 30);\n            uvCookie.setPath(StrUtil.sub(fullShortUrl, fullShortUrl.indexOf(\"\/\"), fullShortUrl.length()));\n            ((HttpServletResponse) response).addCookie(uvCookie);\n            uvFirstFlag.set(Boolean.TRUE);\n            stringRedisTemplate.opsForSet().add(\"short-link:stats:uv:\" + fullShortUrl, uv);\n        };\n        if (ArrayUtil.isNotEmpty(cookies)) {\n            Arrays.stream(cookies)\n                    .filter(each -&gt; Objects.equals(each.getName(), \"uv\"))\n                    .findFirst()\n                    .map(Cookie::getValue)\n                    .ifPresentOrElse(each -&gt; {\n                        Long added = stringRedisTemplate.opsForSet().add(\"short-link:stats:uv:\" + fullShortUrl, each);\n                        uvFirstFlag.set(added != null &amp;&amp; added &gt; 0L);\n                    }, addResponseCookieTask);\n        } else {\n            addResponseCookieTask.run();\n        }\n        if (StrUtil.isBlank(gid)) {\n            LambdaQueryWrapper&lt;ShortLinkGotoDO&gt; queryWrapper = Wrappers.lambdaQuery(ShortLinkGotoDO.class)\n                    .eq(ShortLinkGotoDO::getFullShortUrl, fullShortUrl);\n            ShortLinkGotoDO shortLinkGotoDO = shortLinkGotoMapper.selectOne(queryWrapper);\n            gid = shortLinkGotoDO.getGid();\n        }\n        int hour = DateUtil.hour(new Date(), true);\n        Week week = DateUtil.dayOfWeekEnum(new Date());\n        int weekValue = week.getIso8601Value();\n        LinkAccessStatsDO linkAccessStatsDO = LinkAccessStatsDO.builder()\n                .pv(1)\n                \/\/uv\u4e0d\u518d\u8bbe\u7f6e\u62101\n                .uv(uvFirstFlag.get() ? 1 : 0)\n                .uip(1)\n                .hour(hour)\n                .weekday(weekValue)\n                .fullShortUrl(fullShortUrl)\n                .gid(gid)\n                .date(new Date())\n                .build();\n        linkAccessStatsMapper.shortLinkStats(linkAccessStatsDO);\n    } catch (Throwable ex) {\n        log.error(\"\u77ed\u94fe\u63a5\u8bbf\u95ee\u91cf\u7edf\u8ba1\u5f02\u5e38\", ex);\n    }\n}\n<\/pre>\n\n\n\n<p>feature\uff1a\u5f00\u53d1\u77ed\u94fe\u63a5\u76d1\u63a7\u4e4b\u57fa\u7840\u8bbf\u95eeuv\u6570\u636e<\/p>\n\n\n\n<p>\u6211\u4eec\u76f4\u63a5\u5148\u521b\u5efa\u4e00\u4e2a\u77ed\u94fe\u63a5\uff0c\u7ed3\u679c\u5728\u662fnurl.ink\/1fN5rc\u00a0\uff0c\u5728\u6d4f\u89c8\u5668\u8bbf\u95ee\u4e00\u4e0b<img loading=\"lazy\" decoding=\"async\" height=\"84\" width=\"1482\" src=\"https:\/\/i-blog.csdnimg.cn\/direct\/0c8f44b777c3479f902ca18b31d6dd5b.png\" alt=\"\"><\/p>\n\n\n\n<p>\u00a0\u518d\u8bbf\u95ee\u4e00\u4e0b<img loading=\"lazy\" decoding=\"async\" height=\"112\" width=\"1471\" src=\"https:\/\/i-blog.csdnimg.cn\/direct\/09a5fc0dc9944fdb9ff6990fc2f4e6e5.png\" alt=\"\"><\/p>\n\n\n\n<p>&nbsp;&nbsp;UV\u7684\u4e00\u4e2a\u5217\u8868\u5bf9\u5427\uff1f\u5b58\u5728\u91cc\u9762\u4f60\u8981\u5b58\u591a\u957f\u65f6\u95f4\uff1f\u5982\u679c\u8bf4\uff0c\u5343\u4e07\u4ebf\u7684\u4eba\u53bb\u8bbf\u95ee\u4f60\u7684\u8fd9\u4e9b\u5f88\u591a\u6d4f\u89c8\u5668\uff0c\u5305\u62ec\u5f88\u591a\u7684\u8fd9\u4e9b\u77ed\u94fe\u63a5\uff0c\u96be\u9053\u4f60\u6bcf\u4e2a\u94fe\u63a5\u90fd\u8981\u7ef4\u62a4\u8fd9\u6837\u7684\u4e00\u4e2a\u5f88\u5927\u96c6\u5408\u5417\uff1f\u80af\u5b9a\u662f\u4e0d\u5bf9\uff1b\u8fd9\u6837\u7684\u8bdd\u540e\u7eed\u4f1a\u5728\u76d1\u63a7\u91cd\u6784\u7684\u65f6\u5019fix<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>\u63d0\u793a&nbsp; &nbsp;\u7edf\u8ba1\u65b9\u6cd5\u8981\u5728\u91cd\u5b9a\u5411\u524d\u8c03\u7528\uff0c\u4e0d\u7136cookie\u52a0\u4e0d\u8fdb\u53bb<\/p>\n\n\n\n<p>\u6211\u89c9\u5f97\u903b\u8f91\u6709\u70b9\u95ee\u9898\uff0c\u5982\u679c\u540d\u4e3auv\u7684cookie\u5b58\u5728\uff0c\u4e0d\u5e94\u8be5\u7528set\u7684add\u547d\u4ee4\u6765\u5224\u65ad\u662f\u5426\u662f\u65b0\u7528\u6237\uff0c\u56e0\u4e3aadd\u547d\u4ee4\u65e0\u6cd5\u9632\u6b62\u7528\u6237\u6076\u610f\u4fee\u6539cookie\u503c\uff1bcookie\u5224\u65ad\u7684\u65b9\u5f0f\u4e0d\u592a\u597d\u5904\u7406\u8fd9\u4e2a\u95ee\u9898\uff0c\u53ea\u80fd\u7c97\u7565\u5730\u8ba4\u4e3a\u53ea\u8981cookie\u5b58\u5728\u5c31\u662f\u8001\u7528\u6237\u3002<\/p>\n\n\n\n<p>miccoui&nbsp;\u56de\u590d&nbsp;\u738b\u5c0f\u660e\uff1a\u5982\u679ccookie\u5b58\u5728\uff0c\u4f46\u662f\u96c6\u5408\u4e2d\u67e5\u4e0d\u5230\uff0c\u4e0d\u5c31\u662f\u8868\u793a\u662f\u4f2a\u9020\u7684\u5417\uff0c\u6b64\u65f6\u4e5f\u53ef\u4ee5\u8ba4\u4e3a\u662f\u65b0\u7528\u6237\u5427\uff1f<\/p>\n\n\n\n<p>\u738b\u5c0f\u660e&nbsp;\u56de\u590d&nbsp;miccoui\uff1a\u95ee\u4e86\u4e0b\u9a6c\u54e5\u8fd9\u4e2a\u95ee\u9898\u6ca1\u6cd5\u89e3\u51b3...\u65e0\u6cd5\u9632\u6b62\u7528\u6237\u6539cookie \u4e0d\u8fc7\u5e94\u8be5\u4e5f\u6ca1\u6709\u7528\u6237\u8fd9\u4e48\u65e0\u804a\u5c31\u662f\u4e86<\/p>\n\n\n\n<p>2024-01-20 12:16<\/p>\n\n\n\n<p>miccoui&nbsp;\u56de\u590d&nbsp;\u738b\u5c0f\u660e\uff1a\u4e0d\u8fc7\u8fd9\u6837\u597d\u50cf\u786e\u5b9e\u9632\u4e0d\u4f4f\u6076\u610f\u4fee\u6539\u3002<\/p>\n\n\n\n<p>\u738b\u5c0f\u660e&nbsp;\u56de\u590d&nbsp;miccoui\uff1a\u7684\u786e<\/p>\n\n\n\n<p>\u5168\u51ed\u81ea\u89c9\u4e86<\/p>\n\n\n\n<p>2024-01-20 12:37<\/p>\n\n\n\n<p>\u9a6c\u4e01&nbsp;\u56de\u590d&nbsp;\u738b\u5c0f\u660e\uff1a\u5f88\u96be\u9632\u6b62\u7be1\u6539\u7684\uff0c\u5305\u62ec\u50cf\u5c0f\u7801\u77ed\u94fe\u63a5\u6211\u4e5f\u6d4b\u8bd5\u8fc7\uff0c\u9632\u4e0d\u4f4f<\/p>\n\n\n\n<p>2024-04-15 13:23<\/p>\n\n\n\n<p>TheTurtle&nbsp;\u56de\u590d&nbsp;\u738b\u5c0f\u660e\uff1a\u8fd8\u662f\u90a3\u53e5\u8bdd \u6ca1\u6709\u94f6\u5f39<\/p>\n<\/blockquote>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>&nbsp;\u8fd9\u91cc\u518d\u9ad8\u5e76\u53d1\u7684\u60c5\u51b5\u4e0b\uff0c\u6570\u636e\u5e93\u80fd\u627f\u53d7\u4e48\uff1fset\u80fd\u5b8c\u6210\u4efb\u52a1\u4e48\uff1f<\/p>\n\n\n\n<p>\u540e\u9762\u7528\u4e86\u6d88\u606f\u961f\u5217\u8fdb\u884c\u524a\u5cf0\uff0c\u80af\u5b9a\u4e0d\u4f1a\u8ba9\u6570\u636e\u5e93\u53bb\u6297\u5e76\u53d1\u9ad8\u7684\u8bf7\u6c42\u3002Set \u5b8c\u6210\u529f\u80fd\u662f\u6ca1\u95ee\u9898\u7684\uff1b\u4f7f\u7528hyperloglog\u5462\uff1f\u6211\u89c9\u5f97\u6570\u636e\u91cf\u5927\u4e86\u80af\u5b9a\u5f97\u7528hyperloglog<\/p>\n<\/blockquote>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>&nbsp;cookie\u4e2d\u6709uv\u4e0d\u662f\u5df2\u7ecf\u8bf4\u660e\u7528\u6237\u8bbf\u95ee\u8fc7\u4e86\u5417\uff0c\u4e3a\u4ec0\u4e48\u8fd8\u8981\u5728redis\u91cc\u9762\u5224\u65ad<\/p>\n\n\n\n<p>cookie\u53ea\u662f\u6807\u8bc6\u4f60\u5728\u8fd9\u4e2a\u77ed\u94fe\u63a5\u4e0a\u9762\u662f\u4e00\u4e2a\u8001\u7528\u6237\uff0c\u4f46\u662f\u8001\u7528\u6237\u5728\u4e0d\u540c\u7684\u4e24\u5929\u8bbf\u95ee\u53ea\u52a0\u4e00\u6b21uv\u5417\uff1f\u663e\u7136\u4e0d\u662f\u5427\uff0c\u8fd9\u91ccredis\u7684set\u5c31\u9700\u8981\u8bbe\u7f6e\u8fc7\u671f\u65f6\u95f4\u5230\u7b2c\u4e8c\u5929\u51cc\u6668\u3002 \u90a3\u8fd9\u6837cookie\u548credis\u7684\u4f5c\u7528\u4e0d\u5c31\u6e05\u695a\u4e86\u3002\u8fd9\u4e2a\u6211\u60f3\u60f3\uff0c\u611f\u89c9\u53ef\u4ee5\u6539\u4e3a\u5982\u679c\u6ca1\u5e26uv\u5c31\u5e94\u8be5+1\uff0c\u5982\u679c\u643a\u5e26\u4e86\u5ffd\u7565<\/p>\n<\/blockquote>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>&nbsp;cookie.setPath(\"\/shorturi\")\u7684\u4f5c\u7528\u662f\u7528\u6237\u4e0b\u6b21\u8bbf\u95ee\u8be5\u77ed\u94fe\u63a5\u7684\u65f6\u5019\u4f1a\u643a\u5e26cookie\uff0c\u5982\u679c\u4e0d\u8bbe\u7f6e\u7684\u8bdd\u9ed8\u8ba4\u662f\u8bbf\u95ee\u5f53\u524d\u57df\u540d\u4e0b\u7684\u6240\u6709\u77ed\u94fe\u63a5\u90fd\u4f1a\u643a\u5e26cookie\u3002\u4e0d\u8fc7\u597d\u50cf\u5e76\u4e0d\u5f71\u54cd\uff0c\u8bbf\u95ee\u65b0\u94fe\u63a5\u7684\u8bdd\u56e0\u4e3aredis\u4e2d\u4e0d\u5b58\u5728\u8be5cookie\u4f9d\u65e7\u4f1a\u8ba4\u4e3a\u662f\u7b2c\u4e00\u6b21\u8bbf\u95ee\uff0c\u751a\u81f3\u8fd8\u80fd\u5c11\u751f\u6210\u4e00\u6b21uuid\u3002\u4e0d\u77e5\u9053\u5bf9\u4e8e\u8bbf\u95ee\u65b0\u94fe\u63a5\u90fd\u4f7f\u7528\u4e00\u4e2a\u65b0\u7684cookie\u662f\u5426\u5fc5\u8981\uff1f<\/p>\n<\/blockquote>\n\n\n\n<p>&nbsp;\u6dfb\u52a0cookie\u7684\u65f6\u5019\u4e3a\u4ec0\u4e48\u8981\u5efa\u4e00\u4e2arunnable\u7684\u7ebf\u7a0b\u5462\uff1f\u8fd9\u662f\u90a3\u4e2a\u51fd\u6570\u7684\u7528\u6cd5\uff0c\u8981\u6c42\u503c\u4e3anull\u65f6\u4f20\u9012\u4e00\u4e2arunnbale\u7c7b\u578b\u7684\u53c2\u6570<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>&nbsp;uvFirstFlag\u662f\u4e00\u4e2aAtomicBoolean\u7c7b\u578b\u7684\u53d8\u91cf\uff0c\u5b83\u662f\u7ebf\u7a0b\u5b89\u5168\u7684\u3002AtomicBoolean\u63d0\u4f9b\u4e86\u539f\u5b50\u64cd\u4f5c\uff0c\u53ef\u4ee5\u786e\u4fdd\u591a\u4e2a\u7ebf\u7a0b\u4e4b\u95f4\u7684\u53ef\u89c1\u6027\u548c\u4e00\u81f4\u6027\u3002\u5728addResponseCookieTask\u4efb\u52a1\u4e2d\uff0cuvFirstFlag\u88ab\u8bbe\u7f6e\u4e3aBoolean.TRUE\uff0c\u800c\u5728\u53e6\u4e00\u4e2a\u7ebf\u7a0b\u4e2d\u901a\u8fc7uvFirstFlag.get()\u6765\u8bfb\u53d6\u5b83\u7684\u503c\u3002\u7531\u4e8eAtomicBoolean\u7684\u7279\u6027\uff0c\u8fd9\u4e2a\u8bfb\u53d6\u64cd\u4f5c\u662f\u5b89\u5168\u7684\uff0c\u4e0d\u4f1a\u53d7\u5230\u5176\u4ed6\u7ebf\u7a0b\u7684\u5f71\u54cd\u3002\u56e0\u6b64\uff0c\u4e0d\u4f1a\u51fa\u73b0\u810f\u6570\u636e\u7684\u95ee\u9898\u3002uvFirstFlag\u7684\u503c\u5728\u591a\u4e2a\u7ebf\u7a0b\u4e4b\u95f4\u662f\u540c\u6b65\u548c\u53ef\u9760\u7684\u3002 \u5bf9\u4e8eAtomic\u6211\u603b\u7ed3\u4e86\u4e00\u7bc7\u6587\u7ae0\uff1a<a href=\"https:\/\/blog.fivk.cn\/archives\/6526.html\" target=\"_blank\"  rel=\"nofollow\" >Java\u5e76\u53d1\u7f16\u7a0b\uff1a\u6df1\u5165\u7406\u89e3java.util.concurrent.atomic\u5305 - Fivk\u535a\u5ba2<\/a><\/p>\n\n\n\n<p>\u8bf7\u95ee\u54ea\u91cc\u6709\u53e6\u4e00\u4e2a\u7ebf\u7a0b\u901a\u8fc7uvFirstFlag.get()\u6765\u8bfb\u53d6\u5b83\u7684\u503c\uff1f\u5e76\u4e0d\u662f\u8fd9\u6837\uff0c\u548c\u8fd9\u4e2a\u6ca1\u5173\u7cfb\uff0c\u5c40\u90e8\u7528boolean\u672c\u5c31\u662f\u7ebf\u7a0b\u5b89\u5168\u7684\uff0c\u8fd9\u91cc\u7528atomic\u662f\u56e0\u4e3alambda\u8868\u8fbe\u5f0f\u91cc\u9762\u53ea\u80fd\u4f7f\u7528\u4e0d\u80fd\u6539\u53d8\u7684\u53d8\u91cf\uff08\u56e0\u4e3a\u6700\u7ec8\u8fd9\u4e2a\u53d8\u91cf\u4f1a\u88ab\u7f16\u8bd1\u5230\u533f\u540d\u5185\u90e8\u7c7b\u7684\u5185\u90e8\uff09\uff0c\u7528atomic\u4ed6\u7684\u5f15\u7528\u662f\u4e0d\u4f1a\u53d8\u7684\uff0c\u53ea\u662f\u6539\u53d8\u91cc\u9762\u7684value\uff0c\u6240\u4ee5\u53ef\u884c<\/p>\n<\/blockquote>\n\n\n\n<h2 class=\"wp-block-heading\">- \u7b2c05\u8282\uff1a\u5982\u4f55\u7edf\u8ba1\u77ed\u94fe\u63a5IP\u8bbf\u95ee<\/h2>\n\n\n\n<p>&nbsp;\u901a\u8fc7httpservletRespose\u6765\u83b7\u5f97&nbsp;&nbsp;<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">if (ArrayUtil.isNotEmpty(cookies)) {\n    Arrays.stream(cookies)\n            .filter(each -&gt; Objects.equals(each.getName(), \"uv\"))\n            .findFirst()\n            .map(Cookie::getValue)\n            .ifPresentOrElse(each -&gt; {\n                Long uvAdded = stringRedisTemplate.opsForSet().add(\"short-link:stats:uv:\" + fullShortUrl, each);\n                uvFirstFlag.set(uvAdded != null &amp;&amp; uvAdded &gt; 0L);\n            }, addResponseCookieTask);\n} else {\n    addResponseCookieTask.run();\n}\nString remoteAddr = LinkUtil.getActualIp(((HttpServletRequest) request));\nLong uipAdded = stringRedisTemplate.opsForSet().add(\"short-link:stats:uip:\" + fullShortUrl, remoteAddr);\nboolean uipFirstFlag = uipAdded != null &amp;&amp; uipAdded &gt; 0L;\nif (StrUtil.isBlank(gid)) {\n    LambdaQueryWrapper&lt;ShortLinkGotoDO&gt; queryWrapper = Wrappers.lambdaQuery(ShortLinkGotoDO.class)\n            .eq(ShortLinkGotoDO::getFullShortUrl, fullShortUrl);\n    ShortLinkGotoDO shortLinkGotoDO = shortLinkGotoMapper.selectOne(queryWrapper);\n    gid = shortLinkGotoDO.getGid();\n}\nint hour = DateUtil.hour(new Date(), true);\nWeek week = DateUtil.dayOfWeekEnum(new Date());\nint weekValue = week.getIso8601Value();\nLinkAccessStatsDO linkAccessStatsDO = LinkAccessStatsDO.builder()\n        .pv(1)\n        \/\/uv\u4e0d\u518d\u8bbe\u7f6e\u62101\n        .uv(uvFirstFlag.get() ? 1 : 0)\n        .uip(uipFirstFlag ? 1 : 0)\n<\/pre>\n\n\n\n<p>LinkUtil.java\u91cc\u9762\u52a0\u4e2a&nbsp;<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">\/**\n * \u83b7\u53d6\u7528\u6237\u771f\u5b9eIP\n *\n * @param request \u8bf7\u6c42\n * @return \u7528\u6237\u771f\u5b9eIP\n *\/\npublic static String getActualIp(HttpServletRequest request) {\n    String ipAddress = request.getHeader(\"X-Forwarded-For\");\n    if (ipAddress == null || ipAddress.isEmpty() || \"unknown\".equalsIgnoreCase(ipAddress)) {\n        ipAddress = request.getHeader(\"Proxy-Client-IP\");\n    }\n    if (ipAddress == null || ipAddress.isEmpty() || \"unknown\".equalsIgnoreCase(ipAddress)) {\n        ipAddress = request.getHeader(\"WL-Proxy-Client-IP\");\n    }\n    if (ipAddress == null || ipAddress.isEmpty() || \"unknown\".equalsIgnoreCase(ipAddress)) {\n        ipAddress = request.getHeader(\"HTTP_CLIENT_IP\");\n    }\n    if (ipAddress == null || ipAddress.isEmpty() || \"unknown\".equalsIgnoreCase(ipAddress)) {\n        ipAddress = request.getHeader(\"HTTP_X_FORWARDED_FOR\");\n    }\n    if (ipAddress == null || ipAddress.isEmpty() || \"unknown\".equalsIgnoreCase(ipAddress)) {\n        ipAddress = request.getRemoteAddr();\n    }\n    return ipAddress;\n}<\/pre>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>tx\uff1auv\u548cip\u8fd9\u91cc\u6700\u597d\u628a\u8868\u7684\u7d22\u5f15\u91cc\u7684hour\u5220\u6389\uff0c\u6216\u8005\u7ed9redis\u8bbe\u7f6e\u8fc7\u671f\u65f6\u95f4,\u8981\u4e0d\u7136\u8df3\u5c0f\u65f6\u540e\u4f1a\u65b0\u5efa\u8bb0\u5f55\u5e76\u4e14\u7531\u4e8eredis\u5b58\u5728\u7f13\u5b58\u5bfc\u81f4uv\u548cip\u90fd\u4e3a0<\/p>\n\n\n\n<p>2024-02-25 15:15<\/p>\n\n\n\n<p>\u771f\u6ef4\u5f88\u5f3a&nbsp;\u56de\u590d&nbsp;tx\uff1a\u6211\u5199\u4e86\u4e2a\u83b7\u53d6\u5f53\u524d\u65f6\u95f4\u4e0e\u4e0b\u4e00\u4e2a\u6574\u70b9\u7684\u79d2\u6570\u5dee\u503c\u4f5c\u4e3aredis\u7f13\u5b58\u7684\u8fc7\u671f\u65f6\u95f4<\/p>\n\n\n\n<p>2024-04-23 11:54<\/p>\n\n\n\n<p>Jluuno&nbsp;\u56de\u590d&nbsp;tx\uff1a\/** * \u6839\u636e\u5f53\u524d\u65f6\u95f4\u5230\u4e0b\u4e00\u4e2a\u5c0f\u65f6\u7684\u5206\u949f\u5dee\u503c\u5e76\u8fd4\u56de * @return *\/ public static int minutesUntilNextHour() { \/\/ \u83b7\u53d6\u5f53\u524d\u65f6\u95f4 LocalDateTime now = LocalDateTime.now(); \/\/ \u83b7\u53d6\u4e0b\u4e00\u4e2a\u6574\u70b9\u65f6\u95f4 LocalDateTime nextHour = now.plusHours(1).truncatedTo(ChronoUnit.HOURS); \/\/ \u8ba1\u7b97\u5f53\u524d\u65f6\u95f4\u5230\u4e0b\u4e00\u4e2a\u6574\u70b9\u7684\u5206\u949f\u5dee\u503c long minutesUntilNextHour = ChronoUnit.MINUTES.between(now, nextHour); return (int) minutesUntilNextHour; }<\/p>\n\n\n\n<p>2024-07-01 17:57<\/p>\n\n\n\n<p>3&nbsp;\u56de\u590d&nbsp;tx\uff1a\u8bd5\u4e86\u4e0b\u786e\u5b9e\u6709\u8fd9\u4e2a\u95ee\u9898\uff0c\u8bbe\u7f6e\u4e2a\u8fc7\u671f\u65f6\u95f4\u5c31\u53ef\u4ee5 if (uipAdded != null &amp;&amp; uipAdded &gt; 0) { stringRedisTemplate.expire(\"short-link:stats:uv:\" + fullShortUrl, 60, TimeUnit.SECONDS); }<\/p>\n\n\n\n<p>2024-08-07 23:21<\/p>\n\n\n\n<p>TheTurtle&nbsp;\u56de\u590d&nbsp;3\uff1a\u90a3\u540e\u9762\u8ba1\u7b97\u67d0\u65f6\u95f4\u6bb5\u5185\u7684\u603buv\uff08\u603b\u8bbf\u95ee\u4eba\u6570\uff09\u5c31\u4f1a\u591a\u4e86\u554a \u56e0\u4e3a\u6bcf\u4e2a\u5c0f\u65f6\u76f8\u540c\u7528\u6237\u90fd\u91cd\u65b0\u8ba1\u4e86\u4e00\u6b21 \u611f\u89c9\u8bbe\u7f6e\u7684\u65f6\u95f4\u5e94\u8be5\u662f\u5f53\u524d\u65f6\u95f4\u5230\u4eca\u65e524:00:00\u7684\u5dee\u503c \u5927\u4f6c\u53ef\u4ee5\u770b\u770bUV\u7684\u5b9a\u4e49\uff1a<a href=\"https:\/\/blog.csdn.net\/CJY131\/article\/details\/108764541\" target=\"_blank\"  rel=\"nofollow\" >\u6e05\u695a\u6613\u61c2\u7684\u8bb2\u89e3\u201dUV\u548cPV\u201c\u7684\u542b\u4e49\uff0c\u4ee5\u53ca\u4e4b\u95f4\u7684\u533a\u522b\u3002_uv pv-CSDN\u535a\u5ba2<\/a><\/p>\n\n\n\n<p>2024-08-11 13:21<\/p>\n\n\n\n<p>TheTurtle&nbsp;\u56de\u590d&nbsp;\u771f\u6ef4\u5f88\u5f3a\uff1a\u611f\u89c9\u8fc7\u671f\u65f6\u95f4\u4e0d\u5e94\u8be5\u662f\u4e00\u4e2a\u5c0f\u65f6 \u4e0d\u7136\u540e\u9762\u8ba1\u7b97\u67d0\u65f6\u95f4\u6bb5\u5185\u7684\u603buv\uff08\u603b\u8bbf\u95ee\u4eba\u6570\uff09\u5c31\u4f1a\u591a\u4e86 \u56e0\u4e3a\u6bcf\u4e2a\u5c0f\u65f6\u90fd\u4f1a\u5bf9\u76f8\u540c\u7528\u6237\u90fd\u4f1a\u91cd\u8ba11\u6b21UV \u90a3\u4e48\u6c42\u548c\u7684\u65f6\u5019\u80af\u5b9a\u591a\u4e86 \u672c\u6765\u6bcf\u5929\u6bcf\u4e2a\u7528\u6237\u8ba1\u4e00\u6b21\u5c31\u591f\u4e86\u3002\u5927\u4f6c\u53ef\u4ee5\u770b\u770bUV\u7684\u5b9a\u4e49\uff1a<a href=\"https:\/\/blog.csdn.net\/CJY131\/article\/details\/10876...\" target=\"_blank\"  rel=\"nofollow\" >https:\/\/blog.csdn.net\/CJY131\/article\/details\/10876...<\/a><\/p>\n\n\n\n<p>2024-08-11 13:23<\/p>\n\n\n\n<p>3&nbsp;\u56de\u590d&nbsp;TheTurtle\uff1aOkok\u6211\u5f53\u65f6\u5c31\u662f\u60f3\u7740\u8dd1\u901a\u5c31\u53ef\u4ee5\uff0c\u6ca1\u6709\u7ec6\u60f3<\/p>\n\n\n\n<p>2024-08-15 14:05<\/p>\n\n\n\n<p>\u5c0f\u53ea\u5c0f\u667a&nbsp;\u56de\u590d&nbsp;\u771f\u6ef4\u5f88\u5f3a\uff1a\u8fd9\u4e2a\u65b9\u6cd5\u79d2\u554a<\/p>\n\n\n\n<p>2024-10-19 17:15<\/p>\n\n\n\n<p>Venom.&nbsp;\u56de\u590d&nbsp;3\uff1a\u4f46\u8fd9\u4e2a\u4ee3\u7801\u6bcf\u6b21\u63d2\u4e00\u4e2a\u65b0ip \u7f13\u5b58\u65f6\u95f4\u5c31\u91cd\u7f6e\u4e86 \u8ddf\u6211\u4eec\u60f3\u8981\u7684\u903b\u8f91\u4e0d\u4e00\u6837\u5427 \u6211\u4eec\u5e0c\u671b\u4e00\u5f00\u59cb\u8bbe\u7f6e\u4e00\u5c0f\u65f6\u4ee5\u540e\u5c31\u4e0d\u8981\u91cd\u7f6e\u4e86<\/p>\n\n\n\n<p>2024-10-28 16:19<\/p>\n\n\n\n<p>Binary_tre*&nbsp;\u56de\u590d&nbsp;tx\uff1a\u5e94\u8be5\u6ca1\u4e8b\u5427\uff0cuv\u548cpv\u53ea\u8bb0\u5f55\u4e00\u6b21\u5c31\u884c\u554a\uff0c\u53cd\u6b63\u4e00\u4e2a\u7528\u6237\u53ea\u8bb0\u5f55\u4e00\u6b21\uff0c\u4e00\u4e2aip\u4e5f\u53ea\u8bb0\u5f55\u4e00\u6b21\u3002\u5982\u679c\u5728\u4e0b\u4e00\u4e2a\u5c0f\u65f6\u6709\u4e0d\u540c\u7684ip\u548c\u7528\u6237\uff0credis\u4e2d\u4e5f\u67e5\u4e0d\u5230\u5bf9\u5e94\u7684Key-value,\u4f46\u662f\u7edf\u8ba1\u67d0\u4e2a\u77ed\u94fe\u63a5\u7684\u5168\u5c40ip\u548cuv\u6570\u91cf\u4e0d\u4f1a\u6709\u9519\u7684\u3002\u90a3\u4f60\u8bbe\u7f6e\u8fc7\u671f\u65f6\u95f4\u5c82\u4e0d\u662f\u6bcf\u4e2a\u5c0f\u65f6\u8fd9\u4e2a\u77ed\u94fe\u63a5\u5bf9\u5e94\u7684\u4e00\u4e2a\u7528\u6237\u90fd\u52a0\u4e86\u4e00\u6b21\uff1f<\/p>\n<\/blockquote>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>\u95ee\u9898\u6838\u5fc3<\/strong><\/h3>\n\n\n\n<p>\u539f\u4ee3\u7801\u4e2d\uff0cUV\uff08\u57fa\u4e8eCookie\uff09\u548cUip\uff08\u57fa\u4e8eIP\uff09\u7684Redis\u952e\u672a\u5305\u542b<strong>\u5c0f\u65f6\u7ef4\u5ea6 <\/strong>\uff08\u5982 <code>\"short-link:stats:uv:\" + fullShortUrl<\/code>\uff09\uff0c\u5bfc\u81f4\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u8de8\u5c0f\u65f6\u7edf\u8ba1\u9519\u8bef <\/strong>\uff1a\u540c\u4e00\u7528\u6237\u5728\u4e0d\u540c\u5c0f\u65f6\u8bbf\u95ee\u65f6\uff0c\u56e0\u65e7\u952e\u672a\u5931\u6548\uff0c\u65b0\u5c0f\u65f6\u7684UV\/Uip\u4f1a\u88ab\u9519\u8bef\u6807\u8bb0\u4e3a0\uff08\u672a\u65b0\u589e\uff09\u3002<\/li>\n\n\n\n<li><strong>\u6570\u636e\u5806\u79ef\u98ce\u9669 <\/strong>\uff1aRedis\u952e\u65e0\u8fc7\u671f\u65f6\u95f4\uff0c\u957f\u671f\u5b58\u50a8\u5bfc\u81f4\u5185\u5b58\u81a8\u80c0\u3002<\/li>\n<\/ol>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>\u89e3\u51b3\u65b9\u6848<\/strong><\/h3>\n\n\n\n<h4 class=\"wp-block-heading\">1. <strong>\u6309\u5c0f\u65f6\u5212\u5206Redis\u952e<\/strong><\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u4fee\u6539\u952e\u7ed3\u6784 <\/strong>\uff1a\u5c06\u5c0f\u65f6\u4fe1\u606f\u7eb3\u5165\u952e\u540d\uff0c\u4f8b\u5982\uff1a String uvKey = \"short-link:stats:uv:\" + fullShortUrl + \":\" + hour; String uipKey = \"short-link:stats:uip:\" + fullShortUrl + \":\" + hour;<\/li>\n\n\n\n<li><strong>\u4f5c\u7528 <\/strong>\uff1a\u786e\u4fdd\u6bcf\u5c0f\u65f6\u7684UV\/Uip\u7edf\u8ba1\u72ec\u7acb\uff0c\u907f\u514d\u8de8\u5c0f\u65f6\u5e72\u6270\u3002<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">2. <strong>\u8bbe\u7f6e\u5408\u7406\u8fc7\u671f\u65f6\u95f4<\/strong><\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u6309\u5929\u8fc7\u671f <\/strong>\uff1a\u82e5UV\u5b9a\u4e49\u4e3a\u201c\u6bcf\u65e5\u552f\u4e00\u8bbf\u95ee\u201d\uff0c\u5219\u952e\u8fc7\u671f\u65f6\u95f4\u8bbe\u4e3a\u5f53\u592924:00\u3002 LocalDateTime now = LocalDateTime.now(); LocalDateTime midnight = now.with(LocalTime.MIDNIGHT).plusDays(1); long expireSeconds = Duration.between(now, midnight).getSeconds(); stringRedisTemplate.expire(uvKey, expireSeconds, TimeUnit.SECONDS);<\/li>\n\n\n\n<li><strong>\u6309\u5c0f\u65f6\u8fc7\u671f <\/strong>\uff1a\u82e5UV\u5b9a\u4e49\u4e3a\u201c\u6bcf\u5c0f\u65f6\u552f\u4e00\u8bbf\u95ee\u201d\uff0c\u5219\u952e\u8fc7\u671f\u65f6\u95f4\u8bbe\u4e3a\u5f53\u524d\u5c0f\u65f6\u7ed3\u675f\u3002 \/\/ \u793a\u4f8b\uff1a\u8ba1\u7b97\u5f53\u524d\u65f6\u95f4\u5230\u4e0b\u4e00\u5c0f\u65f6\u7684\u79d2\u6570\u5dee LocalDateTime nextHour = now.plusHours(1).truncatedTo(ChronoUnit.HOURS); long expireSeconds = Duration.between(now, nextHour).getSeconds(); stringRedisTemplate.expire(uvKey, expireSeconds, TimeUnit.SECONDS);<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">3. <strong>\u7d22\u5f15\u4f18\u5316<\/strong><\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u82e5\u7edf\u8ba1\u8868\uff08\u5982 <code>LinkAccessStatsDO<\/code>\uff09\u6309\u5c0f\u65f6\u5206\u7ec4\u5b58\u50a8\uff0c\u9700\u786e\u4fdd\u8868\u7d22\u5f15\u5305\u542b <code>hour<\/code> \u5b57\u6bb5\uff0c\u907f\u514d\u5168\u8868\u626b\u63cf\u3002<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>\u9519\u8bef\u5efa\u8bae\u5206\u6790<\/strong><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u8bbe\u7f6e60\u79d2\u8fc7\u671f <\/strong>\uff08\u7528\u62373\uff09\uff1a<br>\u53ef\u80fd\u5bfc\u81f4\u540c\u5c0f\u65f6\u5185\u591a\u6b21\u8bbf\u95ee\u88ab\u91cd\u590d\u8ba1UV\uff0c\u8fdd\u80cc\u201c\u552f\u4e00\u6027\u201d\u539f\u5219\u3002<\/li>\n\n\n\n<li><strong>\u4ec5\u5230\u4e0b\u4e00\u6574\u70b9 <\/strong>\uff08\u771f\u6ef4\u5f88\u5f3a\uff09\uff1a<br>\u82e5UV\u9700\u8de8\u5c0f\u65f6\u7edf\u8ba1\uff08\u5982\u6bcf\u65e5\u552f\u4e00\uff09\uff0c\u4f1a\u5bfc\u81f4\u6570\u636e\u63d0\u524d\u8fc7\u671f\uff0c\u6f0f\u7edf\u8ba1\u3002<\/li>\n\n\n\n<li><strong>\u4e0d\u8bbe\u7f6e\u8fc7\u671f <\/strong>\uff08Binary_tre*\uff09\uff1a<br>\u6570\u636e\u5806\u79ef\u548c\u8de8\u5c0f\u65f6\u7edf\u8ba1\u9519\u8bef\u65e0\u6cd5\u907f\u514d\uff0c\u4e0d\u53ef\u53d6\u3002<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>\u7ed3\u8bba<\/strong><\/h3>\n\n\n\n<p>TV\u7684\u5efa\u8bae\u6b63\u786e\uff0c\u4f46\u9700\u6839\u636eUV\/Uip\u7684\u7edf\u8ba1\u7c92\u5ea6\uff08\u6309\u5c0f\u65f6\/\u6309\u5929\uff09\u8c03\u6574\u952e\u7ed3\u6784\u548c\u8fc7\u671f\u65f6\u95f4\u3002<strong>\u6b63\u786e\u7684\u505a\u6cd5\u662f\uff1a<\/strong><\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u6309\u7edf\u8ba1\u7c92\u5ea6\u5212\u5206Redis\u952e <\/strong>\uff08\u5982 <code>uv:\u77ed\u94fe:\u5c0f\u65f6<\/code> \u6216 <code>uv:\u77ed\u94fe:\u5929<\/code>\uff09\u3002<\/li>\n\n\n\n<li><strong>\u8bbe\u7f6e\u8fc7\u671f\u65f6\u95f4\u4e3a\u5f53\u524d\u65f6\u95f4\u5230\u7edf\u8ba1\u7c92\u5ea6\u7ed3\u675f\u70b9 <\/strong>\uff08\u5982\u5f53\u592924:00\u6216\u4e0b\u4e00\u5c0f\u65f6\uff09\u3002<\/li>\n\n\n\n<li><strong>\u540c\u6b65\u4f18\u5316\u6570\u636e\u5e93\u7d22\u5f15 <\/strong>\uff0c\u786e\u4fdd\u67e5\u8be2\u6548\u7387\u3002<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">- \u7b2c06\u8282\uff1a\u5982\u4f55\u7edf\u8ba1\u77ed\u94fe\u63a5\u5730\u533a\u8bbf\u95ee<\/h2>\n\n\n\n<p id=\"u359eb08c\">\u901a\u8fc7\u7f51\u7edcIP\u83b7\u53d6\u7528\u6237\u6240\u5728\u5730\u533a\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"tB5Bd\">\u83b7\u53d6\u5730\u533a API<\/h2>\n\n\n\n<p id=\"ud6a964a8\"><a href=\"https:\/\/ip-api.com\/\" target=\"_blank\"  rel=\"nofollow\" >IP-API.com - Geolocation API<\/a><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u652f\u6301\u56fd\u5185\u5916IP\u5730\u5740<\/li>\n\n\n\n<li>\u4f46\u662f\u4f7f\u7528API\u9650\u5236\u8f83\u591a<\/li>\n<\/ul>\n\n\n\n<p id=\"ud2951a25\"><a href=\"https:\/\/lbs.amap.com\/api\/webservice\/guide\/api\/ipconfig\" target=\"_blank\"  rel=\"nofollow\" >IP\u5b9a\u4f4d-API\u6587\u6863-\u5f00\u53d1\u6307\u5357-Web\u670d\u52a1 API | \u9ad8\u5fb7\u5730\u56feAPI<\/a><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u4ec5\u652f\u6301\u56fd\u5185IP\u5730\u5740<\/li>\n\n\n\n<li>\u4f7f\u7528API\u9650\u5236\u5bbd\u677e<\/li>\n<\/ul>\n\n\n\n<p id=\"u2a471f85\">\u6700\u7ec8\u51b3\u5b9a\u4f7f\u7528\u9ad8\u5fb7 IP API \u5b9a\u4f4d\u63a5\u53e3\u3002<\/p>\n\n\n\n<p>&nbsp;\u521b\u5efa\u9ad8\u7684\u670d\u52a1\u7684\u90a3\u4e2a\u8868<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE TABLE `t_link_locale_stats` (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID',\n  `full_short_url` varchar(128) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '\u5b8c\u6574\u77ed\u94fe\u63a5',\n  `gid` varchar(32) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '\u5206\u7ec4\u6807\u8bc6',\n  `date` date DEFAULT NULL COMMENT '\u65e5\u671f',\n  `cnt` int DEFAULT NULL COMMENT '\u8bbf\u95ee\u91cf',\n  `province` varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '\u7701\u4efd\u540d\u79f0',\n  `city` varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '\u5e02\u540d\u79f0',\n  `adcode` varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '\u57ce\u5e02\u7f16\u7801',\n  `country` varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '\u56fd\u5bb6\u6807\u8bc6',\n  `create_time` datetime DEFAULT NULL COMMENT '\u521b\u5efa\u65f6\u95f4',\n  `update_time` datetime NOT NULL COMMENT '\u4fee\u6539\u65f6\u95f4',\n  `del_flag` tinyint(1) DEFAULT NULL COMMENT '\u5220\u9664\u6807\u8bc6 0\u8868\u793a\u5220\u9664 1\u8868\u793a\u672a\u5220\u9664',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `idx_unique_locale_stats` (`full_short_url`,`gid`,`date`,`adcode`,`province`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;<\/code><\/pre>\n\n\n\n<p>&nbsp;\u5728entiry\u4e2d\u65b0\u5efaDo<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">package com.nageoffer.shortlink.project.dao.entiry;<br><br>import com.baomidou.mybatisplus.annotation.TableName;<br>import com.nageoffer.shortlink.project.common.database.BaseDO;<br>import lombok.AllArgsConstructor;<br>import lombok.Builder;<br>import lombok.Data;<br>import lombok.NoArgsConstructor;<br><br>import java.util.Date;<br><br>\/**<br> * \u5730\u533a\u7edf\u8ba1\u8bbf\u95ee\u5b9e\u4f53<br> *\/<br>@Data<br>@TableName(\"t_link_locale_stats\")<br>@Builder<br>@NoArgsConstructor<br>@AllArgsConstructor<br>public class LinkLocaleStatsDO extends BaseDO {<br><br>    \/**<br>     * id<br>     *\/<br>    private Long id;<br><br>    \/**<br>     * \u5b8c\u6574\u77ed\u94fe\u63a5<br>     *\/<br>    private String fullShortUrl;<br><br>    \/**<br>     * \u5206\u7ec4\u6807\u8bc6<br>     *\/<br>    private String gid;<br><br>    \/**<br>     * \u65e5\u671f<br>     *\/<br>    private Date date;<br><br>    \/**<br>     * \u8bbf\u95ee\u91cf<br>     *\/<br>    private Integer cnt;<br><br>    \/**<br>     * \u7701\u4efd\u540d\u79f0<br>     *\/<br>    private String province;<br><br>    \/**<br>     * \u5e02\u540d\u79f0<br>     *\/<br>    private String city;<br><br>    \/**<br>     * \u57ce\u5e02\u7f16\u7801<br>     *\/<br>    private String adcode;<br><br>    \/**<br>     * \u56fd\u5bb6\u6807\u8bc6<br>     *\/<br>    private String country;<br>}<br><br><\/pre>\n\n\n\n<p>\u521b\u5efaLinkLocaleStatsMapper&nbsp;<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">package com.nageoffer.shortlink.project.dao.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.nageoffer.shortlink.project.dao.entiry.LinkLocaleStatsDO;\nimport org.apache.ibatis.annotations.Insert;\nimport org.apache.ibatis.annotations.Param;\n\n\/**\n * \u5730\u533a\u7edf\u8ba1\u8bbf\u95ee\u6301\u4e45\u5c42\n *\/\npublic interface LinkLocaleStatsMapper extends BaseMapper&lt;LinkLocaleStatsDO&gt; {\n\n    \/**\n     * \u8bb0\u5f55\u5730\u533a\u8bbf\u95ee\u76d1\u63a7\u6570\u636e\n     *\/\n    @Insert(\"INSERT INTO t_link_locale_stats (full_short_url, gid, date, cnt, country, province, city, adcode, create_time, update_time, del_flag) \" +\n            \"VALUES( #{linkLocaleStats.fullShortUrl}, #{linkLocaleStats.gid}, #{linkLocaleStats.date}, #{linkLocaleStats.cnt}, #{linkLocaleStats.country}, #{linkLocaleStats.province}, #{linkLocaleStats.city}, #{linkLocaleStats.adcode}, NOW(), NOW(), 0) \" +\n            \"ON DUPLICATE KEY UPDATE cnt = cnt +  #{linkLocaleStats.cnt};\")\n    void shortLinkLocaleState(@Param(\"linkLocaleStats\") LinkLocaleStatsDO linkLocaleStatsDO);\n}<\/pre>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>&nbsp;\u5728Mapper\u91cc\u5199sql\u53ef\u4ee5\u4f7f\u7528JDK15\u65b0\u7279\u6027\u6587\u672c\u5757\uff0c\"\"\" \"\"\"\u7684\u8fd9\u4e2a\uff0c\u4f1a\u597d\u770b\u5f88\u591a\u5f88<\/p>\n<\/blockquote>\n\n\n\n<p>ShortLinkServicelmpl.java\u524d\u9762\u653e\u4e2a\u8fd9\u4e2amapper \uff0c\u8fd9\u91cc\u7684value\u5bfc\u5165\u5230\u662fimport org.springframework.beans.factory.annotation.Value;&nbsp; &nbsp;\u4e0d\u662f\u90a3\u4e2ahutool\u7684value&nbsp;<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    private final LinkLocaleStatsMapper linkLocaleStatsMapper;\n\n    @Value(\"${short-link.stats.locale.amap-key}\")\n    private String statsLocaleAmapKey;<\/code><\/pre>\n\n\n\n<p>\u540e\u65b9\u5199\u65b9\u6cd5\u5982\u4e0b<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">        LinkAccessStatsDO linkAccessStatsDO = LinkAccessStatsDO.builder()\n                .pv(1)\n                \/\/uv\u4e0d\u518d\u8bbe\u7f6e\u62101\n                .uv(uvFirstFlag.get() ? 1 : 0)\n                .uip(uipFirstFlag ? 1 : 0)\n                .hour(hour)\n                .weekday(weekValue)\n                .fullShortUrl(fullShortUrl)\n                .gid(gid)\n                .date(new Date())\n                .build();\n        linkAccessStatsMapper.shortLinkStats(linkAccessStatsDO);\n\n        Map&lt;String, Object&gt; localeParamMap = new HashMap&lt;&gt;();\n        localeParamMap.put(\"key\", statsLocaleAmapKey);\n        localeParamMap.put(\"ip\", remoteAddr);\n        String localeResultStr = HttpUtil.get(AMAP_REMOTE_URL, localeParamMap);\n        JSONObject localeResultObj = JSON.parseObject(localeResultStr);\n        String infoCode = localeResultObj.getString(\"infocode\");\n        if (StrUtil.isNotBlank(infoCode) &amp;&amp; StrUtil.equals(infoCode, \"10000\")) {\n            String province = localeResultObj.getString(\"province\");\n            boolean unknownFlag = StrUtil.equals(province, \"[]\");\n            LinkLocaleStatsDO linkLocaleStatsDO = LinkLocaleStatsDO.builder()\n                    .province(unknownFlag ? \"\u672a\u77e5\" : province)\n                    .city(unknownFlag ? \"\u672a\u77e5\" : localeResultObj.getString(\"city\"))\n                    .adcode(unknownFlag ? \"\u672a\u77e5\" : localeResultObj.getString(\"adcode\"))\n                    .cnt(1)\n                    .fullShortUrl(fullShortUrl)\n                    .country(\"\u4e2d\u56fd\")\n                    .gid(gid)\n                    .date(new Date())\n                    .build();\n            linkLocaleStatsMapper.shortLinkLocaleState(linkLocaleStatsDO);\n        }\n\n    } catch (Throwable ex) {\n        log.error(\"\u77ed\u94fe\u63a5\u8bbf\u95ee\u91cf\u7edf\u8ba1\u5f02\u5e38\", ex);\n    }\n}<\/pre>\n\n\n\n<p>&nbsp;\u4f60\u6253\u5f00<a href=\"https:\/\/console.amap.com\/dev\/key\/app\" target=\"_blank\"  rel=\"nofollow\" >\u6211\u7684\u5e94\u7528 | \u9ad8\u5fb7\u63a7\u5236\u53f0<\/a>\uff0c\u6ce8\u518c\u5b9e\u540d\u8d26\u53f7\uff0c\u7136\u540e\u9009\u62e9web\u670d\u52a1api\uff0c\u627e\u5230ip\u5b9a\u4f4d\u5730\u5740<\/p>\n\n\n\n<p>\u4e0b\u9762\u7684\u653e\u5728application.yaml&nbsp;<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">short-link:\n  stats:\n    locale:\n      amap-key: 824c511f0997586ea016f979fdb23087  #\u975e\u6211\u672c\u4eba<\/pre>\n\n\n\n<p>&nbsp;common\u91cc\u9762\u5bfc\u5165\u9ad8\u5fb7\u5730\u56fe\u7684\u514d\u8d39\u4f7f\u7528\uff1b&nbsp;\u8bf7\u81ea\u884c\u7533\u8bf7\u7684\u7f3a\u5fb7\u5730\u56fe\u7684\u5f00\u53d1\u8005api\u63a5\u53e3<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">package com.nageoffer.shortlink.project.common.constant;\n\n\/**\n * \u77ed\u94fe\u63a5\u5e38\u91cf\u7c7b\n *\/\npublic class ShortLinkConstant {\n\n    \/**\n     * \u6c38\u4e45\u77ed\u94fe\u63a5\u9ed8\u8ba4\u7f13\u5b58\u6709\u6548\u65f6\u95f4\n     *\/\n    public static final long DEFAULT_CACHE_VALID_TIME = 2626560000L;\n\n    \/**\n     * \u9ad8\u5fb7\u83b7\u53d6\u5730\u533a\u63a5\u53e3\u5730\u5740\n     *\/\n    public static final String AMAP_REMOTE_URL = \"https:\/\/restapi.amap.com\/v3\/ip\";\n}\n<\/pre>\n\n\n\n<p><img loading=\"lazy\" decoding=\"async\" height=\"150\" width=\"1467\" src=\"https:\/\/i-blog.csdnimg.cn\/direct\/dcc7981799434a49842c3f1dcf4d0582.png\" alt=\"\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">- \u7b2c07\u8282\uff1a\u5982\u4f55\u7edf\u8ba1\u77ed\u94fe\u63a5\u64cd\u4f5c\u7cfb\u7edf\u8bbf\u95ee<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE TABLE `t_link_os_stats`\n(\n    `id`             bigint   NOT NULL AUTO_INCREMENT COMMENT 'ID',\n    `full_short_url` varchar(128) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '\u5b8c\u6574\u77ed\u94fe\u63a5',\n    `gid`            varchar(32) COLLATE utf8mb4_general_ci  DEFAULT NULL COMMENT '\u5206\u7ec4\u6807\u8bc6',\n    `date`           date                                    DEFAULT NULL COMMENT '\u65e5\u671f',\n    `cnt`            int                                     DEFAULT NULL COMMENT '\u8bbf\u95ee\u91cf',\n    `os`             varchar(64) COLLATE utf8mb4_general_ci  DEFAULT NULL COMMENT '\u64cd\u4f5c\u7cfb\u7edf',\n    `create_time`    datetime                                DEFAULT NULL COMMENT '\u521b\u5efa\u65f6\u95f4',\n    `update_time`    datetime NOT NULL COMMENT '\u4fee\u6539\u65f6\u95f4',\n    `del_flag`       tinyint(1)                              DEFAULT NULL COMMENT '\u5220\u9664\u6807\u8bc6 0\u8868\u793a\u5220\u9664 1\u8868\u793a\u672a\u5220\u9664',\n    PRIMARY KEY (`id`),\n    UNIQUE KEY `idx_unique_locale_stats` (`full_short_url`, `gid`, `date`, `os`) USING BTREE\n) COMMENT = '\u77ed\u94fe\u63a5\u76d1\u63a7\u64cd\u4f5c\u7cfb\u7edf\u8bbf\u95ee\u72b6\u6001'\n    ENGINE = InnoDB\n    DEFAULT CHARSET = utf8mb4\n    COLLATE = utf8mb4_general_ci;<\/code><\/pre>\n\n\n\n<p><img loading=\"lazy\" decoding=\"async\" height=\"15\" src=\"blob:https:\/\/eve2333.top\/7f416369-419a-4965-969a-ddad0e047a9a\" width=\"15\"><\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">package com.nageoffer.shortlink.project.dao.entiry;\n\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.nageoffer.shortlink.project.common.database.BaseDO;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.Date;\n\n\/**\n * \u64cd\u4f5c\u7cfb\u7edf\u7edf\u8ba1\u8bbf\u95ee\u5b9e\u4f53\n *\/\n@Data\n@TableName(\"t_link_os_stats\")\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class LinkOsStatsDO extends BaseDO {\n\n    \/**\n     * id\n     *\/\n    private Long id;\n\n    \/**\n     * \u5b8c\u6574\u77ed\u94fe\u63a5\n     *\/\n    private String fullShortUrl;\n\n    \/**\n     * \u5206\u7ec4\u6807\u8bc6\n     *\/\n    private String gid;\n\n    \/**\n     * \u65e5\u671f\n     *\/\n    private Date date;\n\n    \/**\n     * \u8bbf\u95ee\u91cf\n     *\/\n    private Integer cnt;\n\n    \/**\n     * \u64cd\u4f5c\u7cfb\u7edf\n     *\/\n    private String os;\n}\n<\/pre>\n\n\n\n<pre class=\"wp-block-preformatted\">package com.nageoffer.shortlink.project.dao.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.nageoffer.shortlink.project.dao.entiry.LinkOsStatsDO;\nimport org.apache.ibatis.annotations.Insert;\nimport org.apache.ibatis.annotations.Param;\n\n\/**\n * \u64cd\u4f5c\u7cfb\u7edf\u7edf\u8ba1\u8bbf\u95ee\u6301\u4e45\u5c42\n *\/\npublic interface LinkOsStatsMapper extends BaseMapper&lt;LinkOsStatsDO&gt; {\n\n    \/**\n     * \u8bb0\u5f55\u5730\u533a\u8bbf\u95ee\u76d1\u63a7\u6570\u636e\n     *\/\n    @Insert(\"INSERT INTO t_link_os_stats (full_short_url, gid, date, cnt, os, create_time, update_time, del_flag) \" +\n            \"VALUES( #{linkOsStats.fullShortUrl}, #{linkOsStats.gid}, #{linkOsStats.date}, #{linkOsStats.cnt}, #{linkOsStats.os}, NOW(), NOW(), 0) \" +\n            \"ON DUPLICATE KEY UPDATE cnt = cnt +  #{linkOsStats.cnt};\")\n    void shortLinkOsState(@Param(\"linkOsStats\") LinkOsStatsDO linkOsStatsDO);\n}\n<\/pre>\n\n\n\n<p>&nbsp;LinkUtil\u91cc\u9762\u653e\u8fd9\u4e2a<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">\/**\n * \u83b7\u53d6\u7528\u6237\u8bbf\u95ee\u64cd\u4f5c\u7cfb\u7edf\n *\n * @param request \u8bf7\u6c42\n * @return \u8bbf\u95ee\u64cd\u4f5c\u7cfb\u7edf\n *\/\npublic static String getOs(HttpServletRequest request) {\n    String userAgent = request.getHeader(\"User-Agent\");\n    if (userAgent.toLowerCase().contains(\"windows\")) {\n        return \"Windows\";\n    } else if (userAgent.toLowerCase().contains(\"mac\")) {\n        return \"Mac OS\";\n    } else if (userAgent.toLowerCase().contains(\"linux\")) {\n        return \"Linux\";\n    } else if (userAgent.toLowerCase().contains(\"android\")) {\n        return \"Android\";\n    } else if (userAgent.toLowerCase().contains(\"iphone\") || userAgent.toLowerCase().contains(\"ipad\")) {\n        return \"iOS\";\n    } else {\n        return \"Unknown\";\n    }\n}<\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>                LinkOsStatsDO linkOsStatsDO = LinkOsStatsDO.builder()\n                        .os(LinkUtil.getOs(((HttpServletRequest) request)))\n                        .cnt(1)\n                        .gid(gid)\n                        .fullShortUrl(fullShortUrl)\n                        .date(new Date())\n                        .build();\n                linkOsStatsMapper.shortLinkOsState(linkOsStatsDO);\n            }\n\n        } catch (Throwable ex) {\n            log.error(\"\u77ed\u94fe\u63a5\u8bbf\u95ee\u91cf\u7edf\u8ba1\u5f02\u5e38\", ex);\n        }\n    }<\/code><\/pre>\n\n\n\n<p>switch case\u5e94\u8be5\u662f\u6027\u80fd\u66f4\u597d\uff0c\u66f4\u4f18\u96c5\u4e00\u70b9\u5427\uff0c\u4e0d\u8fc7\u521a\u95ee\u4e86gpt \u8bf4\u662f\uff1a \u867d\u7136\u5728\u7406\u8bba\u4e0a switch-case \u901a\u5e38\u4f1a\u6bd4 if-else \u6027\u80fd\u7a0d\u9ad8\uff0c\u4f46\u5728\u5b9e\u9645\u5e94\u7528\u4e2d\uff0c\u8fd9\u79cd\u5dee\u5f02\u901a\u5e38\u662f\u5fae\u4e4e\u5176\u5fae\u7684\u3002JVM \u7684\u4f18\u5316\u5728\u73b0\u4ee3\u7f16\u8bd1\u5668\u4e0a\u5df2\u7ecf\u975e\u5e38\u6709\u6548\uff0c\u56e0\u6b64\u5728\u5927\u591a\u6570\u573a\u666f\u4e2d\uff0c\u9009\u62e9 switch-case \u8fd8\u662f if-else \u66f4\u591a\u662f\u57fa\u4e8e\u4ee3\u7801\u7684\u53ef\u8bfb\u6027\u548c\u53ef\u7ef4\u62a4\u6027\uff0c\u800c\u4e0d\u662f\u7eaf\u7cb9\u7684\u6027\u80fd\u3002&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">- \u7b2c08\u8282\uff1a\u5982\u4f55\u7edf\u8ba1\u77ed\u94fe\u63a5\u6d4f\u89c8\u5668\u8bbf\u95ee<\/h2>\n\n\n\n<p><img loading=\"lazy\" decoding=\"async\" height=\"266\" width=\"543\" src=\"https:\/\/i-blog.csdnimg.cn\/direct\/c9aec827904e48078b053311a883e933.png\" alt=\"\"><\/p>\n\n\n\n<p>\u4e00\u4e9b\u6ca1\u7528\u7684\u5c0f\u6587\u4ef6&nbsp;<\/p>\n\n\n\n<p>windows\uff0c\u4f46\u662f\u6211\u7528edge\u6d4f\u89c8\u5668\u8bbf\u95ee\u91cc\u9762\u53ea\u6709chrome\u548csafari\uff0c\u6ca1\u6709edge<br>1.\u5386\u53f2\u539f\u56e0\uff1a\u57282008\u5e74\uff0c\u8c37\u6b4c\u57fa\u4e8e\u82f9\u679c\u7684Webkit\u5f15l\u64ce\u5f00\u53d1\u4e86Chrome\u6d4f\u89c8\u5668\uff0c\u4e3a\u4e86\u517c\u5bb9\u90a3\u4e9b\u4e13\u4e3a<br>Safari\u7f16\u5199\u7684\u7f51\u9875\uff0cChrome\u7684User-Agent\u7ee7\u627f\u4e86Safari\u7684\u6807\u8bc6<br>2.\u517c\u5bb9\u6027\uff1a\u5fae\u8f6f\u5728\u5f00\u53d1Edge\u6d4f\u89c8\u5668\u65f6\uff0c\u4e3a\u4e86\u786e\u4fdd\u7f51\u9875\u80fd\u5728\u65b0\u6d4f\u89c8\u5668\u4e0a\u6b63\u786e\u6e32\u67d3\uff0c\u4e5f\u91c7\u7528\u4e86\u7c7b\u4f3c\u7684<br>\u7b56\u7565\u3002Edge\u6d4f\u89c8\u5668\u5728\u57fa\u4e8eChromium\u5185\u6838\u5f00\u53d1\u540e\uff0c\u5176User-Agent\u5b57\u7b26\u4e32\u4e2d\u5305\u542b\u4e86Chrome\u7684\u6807<br>\u8bc6\uff0c\u8fd9\u6837\u505a\u53ef\u4ee5\u5e2e\u52a9Edge\u6d4f\u89c8\u5668\u901a\u8fc7\u4e00\u4e9b\u57fa\u4e8eUser-Agent\u8fdb\u884c\u6d4f\u89c8\u5668\u68c0\u6d4b\u7684\u7f51\u7ad9<\/p>\n\n\n\n<p>&nbsp;\u8fd8\u662f\u901a\u8fc7\u8fd9\u4e2arequest\u53bb\u62ff\u8fd9\u4e2auser-agent\u7684\uff0c\u8fd9\u51e0\u5f20\u903b\u8f91\u57fa\u672c\u4e0a\u4e00\u6837\u7684<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE TABLE `t_link_browser_stats` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',\n  `full_short_url` varchar(128) DEFAULT NULL COMMENT '\u5b8c\u6574\u77ed\u94fe\u63a5',\n  `gid` varchar(32) DEFAULT 'default' COMMENT '\u5206\u7ec4\u6807\u8bc6',\n  `date` date DEFAULT NULL COMMENT '\u65e5\u671f',\n  `cnt` int(11) DEFAULT NULL COMMENT '\u8bbf\u95ee\u91cf',\n  `browser` varchar(64) DEFAULT NULL COMMENT '\u6d4f\u89c8\u5668',\n  `create_time` datetime DEFAULT NULL COMMENT '\u521b\u5efa\u65f6\u95f4',\n  `update_time` datetime DEFAULT NULL COMMENT '\u4fee\u6539\u65f6\u95f4',\n  `del_flag` tinyint(1) DEFAULT NULL COMMENT '\u5220\u9664\u6807\u8bc6 0\uff1a\u672a\u5220\u9664 1\uff1a\u5df2\u5220\u9664',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `idx_unique_browser_stats` (`full_short_url`,`gid`,`date`,`browser`) USING BTREE\n) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4;;<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-preformatted\">package com.nageoffer.shortlink.project.dao.entiry;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.nageoffer.shortlink.project.common.database.BaseDO;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.Date;\n\n\/**\n * \u6d4f\u89c8\u5668\u7edf\u8ba1\u8bbf\u95ee\u5b9e\u4f53\n *\/\n@Data\n@TableName(\"t_link_browser_stats\")\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class LinkBrowserStatsDO extends BaseDO {\n\n    \/**\n     * id\n     *\/\n    private Long id;\n\n    \/**\n     * \u5b8c\u6574\u77ed\u94fe\u63a5\n     *\/\n    private String fullShortUrl;\n\n    \/**\n     * \u5206\u7ec4\u6807\u8bc6\n     *\/\n    private String gid;\n\n    \/**\n     * \u65e5\u671f\n     *\/\n    private Date date;\n\n    \/**\n     * \u8bbf\u95ee\u91cf\n     *\/\n    private Integer cnt;\n\n    \/**\n     * \u6d4f\u89c8\u5668\n     *\/\n    private String browser;\n}<\/pre>\n\n\n\n<pre class=\"wp-block-preformatted\">package com.nageoffer.shortlink.project.dao.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.nageoffer.shortlink.project.dao.entiry.LinkBrowserStatsDO;\nimport org.apache.ibatis.annotations.Insert;\nimport org.apache.ibatis.annotations.Param;\n\n\/**\n * \u6d4f\u89c8\u5668\u7edf\u8ba1\u8bbf\u95ee\u6301\u4e45\u5c42\n *\/\npublic interface LinkBrowserStatsMapper extends BaseMapper&lt;LinkBrowserStatsDO&gt; {\n\n    \/**\n     * \u8bb0\u5f55\u6d4f\u89c8\u5668\u8bbf\u95ee\u76d1\u63a7\u6570\u636e\n     *\/\n    @Insert(\"INSERT INTO t_link_browser_stats (full_short_url, gid, date, cnt, browser, create_time, update_time, del_flag) \" +\n            \"VALUES( #{linkBrowserStats.fullShortUrl}, #{linkBrowserStats.gid}, #{linkBrowserStats.date}, #{linkBrowserStats.cnt}, #{linkBrowserStats.browser}, NOW(), NOW(), 0) \" +\n            \"ON DUPLICATE KEY UPDATE cnt = cnt +  #{linkBrowserStats.cnt};\")\n    void shortLinkBrowserState(@Param(\"linkBrowserStats\") LinkBrowserStatsDO linkBrowserStatsDO);\n}<\/pre>\n\n\n\n<pre class=\"wp-block-preformatted\">\/**\n * \u83b7\u53d6\u7528\u6237\u8bbf\u95ee\u6d4f\u89c8\u5668\n *\n * @param request \u8bf7\u6c42\n * @return \u8bbf\u95ee\u6d4f\u89c8\u5668\n *\/\npublic static String getBrowser(HttpServletRequest request) {\n    String userAgent = request.getHeader(\"User-Agent\");\n    if (userAgent.toLowerCase().contains(\"edg\")) {\n        return \"Microsoft Edge\";\n    } else if (userAgent.toLowerCase().contains(\"chrome\")) {\n        return \"Google Chrome\";\n    } else if (userAgent.toLowerCase().contains(\"firefox\")) {\n        return \"Mozilla Firefox\";\n    } else if (userAgent.toLowerCase().contains(\"safari\")) {\n        return \"Apple Safari\";\n    } else if (userAgent.toLowerCase().contains(\"opera\")) {\n        return \"Opera\";\n    } else if (userAgent.toLowerCase().contains(\"msie\") || userAgent.toLowerCase().contains(\"trident\")) {\n        return \"Internet Explorer\";\n    } else {\n        return \"Unknown\";\n    }\n}<\/pre>\n\n\n\n<p>impl \u524d\u9762\u8bb0\u5f97\u52a0mapper<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>                LinkBrowserStatsDO linkBrowserStatsDO = LinkBrowserStatsDO.builder()\n                        .browser(LinkUtil.getBrowser(((HttpServletRequest) request)))\n                        .cnt(1)\n                        .gid(gid)\n                        .fullShortUrl(fullShortUrl)\n                        .date(new Date())\n                        .build();\n                linkBrowserStatsMapper.shortLinkBrowserState(linkBrowserStatsDO);<\/code><\/pre>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>public static UserAgent getUserAgent(HttpServletRequest request) { return UserAgentUtil.parse(request.getHeader(\"User-Agent\")); } Hutool\u5de5\u5177\u5305 UserAgentUtil \u53ef\u4ee5\u83b7\u53d6UserAgent ,\u91cc\u9762\u5305\u542b \u64cd\u4f5c\u7cfb\u7edf\u4e0e\u6d4f\u89c8\u5668\u7c7b\u578b.&nbsp;<\/p>\n<\/blockquote>\n\n\n\n<h2 class=\"wp-block-heading\">- \u7b2c09\u8282\uff1a\u5982\u4f55\u7edf\u8ba1\u77ed\u94fe\u63a5\u9ad8\u9891IP\u8bbf\u95ee<\/h2>\n\n\n\n<p>mysql \u505a\u8fd9\u4e2a\u4e8b\u60c5\uff0c\u5982\u679c\u8bf4\u4f60\u8981\u5728mysql \u91cc\u9762\u505a\u9ad8\u9891\u7684\u8bbf\u95eeIP\uff0c\u90a3\u4e48\u4e00\u5b9a\u8981\u8bb0\u5f55\u5b83\u7684\u64cd\u4f5c\uff1a\u8bbf\u95ee\u65e5\u5fd7\uff0c\u54b1\u4eec\u4e4b\u524d\u5176\u5b9e\u8bb0\u8fc7\u4e00\u4e2a\u5173\u4e8e\u8bbf\u95ee\u7684\u8fd9\u4e9b\u4fe1\u606f\uff0c\u6bd4\u5982\u8bf4\u6211\u4eec\u7684\u72ec\u7acbIP\u4ee5\u53ca\u8bbf\u95ee\u91cf\u4ee5\u53ca UV\u5bf9\u5427\uff1f\u6211\u4eec\u662f\u7edf\u8ba1\u7684\u662f\u6c47\u603b\u5bf9\u5427\uff1f\u5176\u5b9e\u662f\u6ca1\u6709\u5355\u4e2aIP\u7684\uff0c\u76f8\u5f53\u4e8e\u6211\u4eec\u73b0\u5728\u5c31\u4e0d\u641e\u8fd9\u4e2a\u8bbf\u95ee\u7edf\u8ba1\u4e86\uff0c\u6211\u4eec\u8981\u641e\u8bbf\u95ee\u65e5\u5fd7\u8868\uff0c\u65e5\u5fd7\u8868\u91cc\u9762\u90fd\u4f1a\u5305\u542b\u4ec0\u4e48\u5462\uff1f\u5b83\u4f1a\u5305\u542b\u4f60\u8fd9\u4e2a\u8bf7\u6c42\u7684\u7528\u6237\u5bf9\u5e94\u7684\u6d4f\u89c8\u5668IP\uff0c\u4ee5\u53ca\u4e00\u4e2a\u662f\u5426\u7535\u8111\u64cd\u4f5c\uff0c\u8fd8\u6709\u4ee5\u53ca\u4e00\u4e9b\u5176\u4ed6\u7684\uff0c\u64cd\u4f5c\u7cfb\u7edf\uff0c\u7136\u540e\u6d4f\u89c8\u5668\uff0c\u7136\u540e\u8fd9\u4e24\u4e2a\u6211\u4eec\u4e0d\u8981\uff0c\u7136\u540e\u628a\u8fd9\u51e0\u6761IP\u8bb0\u5f55\u4e0b\u6765\uff0c\u5176\u5b9e\u5c31\u80fd\u591f\u53bb\u505a\u6211\u4eec\u7684\u4e00\u4e2a\u9ad8\u9891\u8bbfIP\u7684\u6d41\u7a0b\uff0c\u628a\u8fd9\u4e9b\u8bb0\u5f55\u4e0b\u6765\u600e\u4e48\u8bbf\u95ee\u600e\u4e48\u53bb\u64cd\u4f5c\u9ad8\u9891\u8bbf\u95eeIP\uff0c\u6211\u4eec\u53ea\u9700\u8981\u7ed9\u5b83\u8fdb\u884c\u4e00\u4e2agroup\uff0c\u7136\u540e\u7ed9\u5b83\u8fdb\u884c\u4e00\u4e2acount\uff0c\u628a\u5b83\u6700\u7ec8\u7684\u6570\u636e\u5c31\u662f\u6392\u540d\u524d\u51e0\u7684\u6570\u636e\u53cd\u9988\u7ed9\u6211\u4eec\u5c31\u597d\u4e86\uff0c\u8fd9\u5c31\u662f\u9ad8\u9891\u7e41IP\u3002<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE TABLE `t_link_access_logs` (\n  `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID',\n  `full_short_url` varchar(128) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '\u5b8c\u6574\u77ed\u94fe\u63a5',\n  `gid` varchar(32) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '\u5206\u7ec4\u6807\u8bc6',\n  `user` varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '\u7528\u6237\u4fe1\u606f',\n  `browser` varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '\u6d4f\u89c8\u5668',\n  `os` varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '\u64cd\u4f5c\u7cfb\u7edf',\n  `ip` varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'IP',\n  `create_time` datetime DEFAULT NULL COMMENT '\u521b\u5efa\u65f6\u95f4',\n  `update_time` datetime DEFAULT NULL COMMENT '\u4fee\u6539\u65f6\u95f4',\n  `del_flag` tinyint(1) DEFAULT NULL COMMENT '\u5220\u9664\u6807\u8bc6 0\uff1a\u672a\u5220\u9664 1\uff1a\u5df2\u5220\u9664',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-preformatted\">package com.nageoffer.shortlink.project.dao.entiry;\n\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.nageoffer.shortlink.project.common.database.BaseDO;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n\/**\n * \u8bbf\u95ee\u65e5\u5fd7\u76d1\u63a7\u5b9e\u4f53\n *\/\n@Data\n@TableName(\"t_link_access_logs\")\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class LinkAccessLogsDO extends BaseDO {\n\n    \/**\n     * id\n     *\/\n    private Long id;\n\n    \/**\n     * \u5b8c\u6574\u77ed\u94fe\u63a5\n     *\/\n    private String fullShortUrl;\n\n    \/**\n     * \u5206\u7ec4\u6807\u8bc6\n     *\/\n    private String gid;\n\n    \/**\n     * \u7528\u6237\u4fe1\u606f\n     *\/\n    private String user;\n\n    \/**\n     * \u6d4f\u89c8\u5668\n     *\/\n    private String browser;\n\n    \/**\n     * \u64cd\u4f5c\u7cfb\u7edf\n     *\/\n    private String os;\n\n    \/**\n     * ip\n     *\/\n    private String ip;\n}\n<\/pre>\n\n\n\n<pre class=\"wp-block-preformatted\">package com.nageoffer.shortlink.project.dao.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.nageoffer.shortlink.project.dao.entiry.LinkAccessLogsDO;\n\n\/**\n * \u8bbf\u95ee\u65e5\u5fd7\u76d1\u63a7\u6301\u4e45\u5c42\n *\/\npublic interface LinkAccessLogsMapper extends BaseMapper&lt;LinkAccessLogsDO&gt; {\n}<\/pre>\n\n\n\n<p>\u63a5\u4e0b\u6765\u7684impl&nbsp;<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    private void shortLinkStats(String fullShortUrl, String gid, ServletRequest request, ServletResponse response) {\n        AtomicBoolean uvFirstFlag = new AtomicBoolean();\n        Cookie&#91;] cookies = ((HttpServletRequest) request).getCookies();\n        try {\n            AtomicReference&lt;String&gt; uv = new AtomicReference&lt;&gt;();\n            Runnable addResponseCookieTask = () -&gt; {\n                uv.set(UUID.fastUUID().toString());\n                Cookie uvCookie = new Cookie(\"uv\", uv.get());\n                uvCookie.setMaxAge(60 * 60 * 24 * 30);\n                uvCookie.setPath(StrUtil.sub(fullShortUrl, fullShortUrl.indexOf(\"\/\"), fullShortUrl.length()));\n                ((HttpServletResponse) response).addCookie(uvCookie);\n                uvFirstFlag.set(Boolean.TRUE);\n                stringRedisTemplate.opsForSet().add(\"short-link:stats:uv:\" + fullShortUrl, uv.get());\n            };\n            if (ArrayUtil.isNotEmpty(cookies)) {\n                Arrays.stream(cookies)\n                        .filter(each -&gt; Objects.equals(each.getName(), \"uv\"))\n                        .findFirst()\n                        .map(Cookie::getValue)\n                        .ifPresentOrElse(each -&gt; {\n                            uv.set(each);\n                            Long uvAdded = stringRedisTemplate.opsForSet().add(\"short-link:stats:uv:\" + fullShortUrl, each);\n                            uvFirstFlag.set(uvAdded != null &amp;&amp; uvAdded &gt; 0L);\n                        }, addResponseCookieTask);\n            } else {\n                addResponseCookieTask.run();\n            }\n            String remoteAddr = LinkUtil.getActualIp(((HttpServletRequest) request));\n            Long uipAdded = stringRedisTemplate.opsForSet().add(\"short-link:stats:uip:\" + fullShortUrl, remoteAddr);\n            boolean uipFirstFlag = uipAdded != null &amp;&amp; uipAdded &gt; 0L;\n            if (StrUtil.isBlank(gid)) {\n                LambdaQueryWrapper&lt;ShortLinkGotoDO&gt; queryWrapper = Wrappers.lambdaQuery(ShortLinkGotoDO.class)\n                        .eq(ShortLinkGotoDO::getFullShortUrl, fullShortUrl);\n                ShortLinkGotoDO shortLinkGotoDO = shortLinkGotoMapper.selectOne(queryWrapper);\n                gid = shortLinkGotoDO.getGid();\n            }\n            int hour = DateUtil.hour(new Date(), true);\n            Week week = DateUtil.dayOfWeekEnum(new Date());\n            int weekValue = week.getIso8601Value();\n            LinkAccessStatsDO linkAccessStatsDO = LinkAccessStatsDO.builder()\n                    .pv(1)\n                    \/\/uv\u4e0d\u518d\u8bbe\u7f6e\u62101\n                    .uv(uvFirstFlag.get() ? 1 : 0)\n                    .uip(uipFirstFlag ? 1 : 0)\n                    .hour(hour)\n                    .weekday(weekValue)\n                    .fullShortUrl(fullShortUrl)\n                    .gid(gid)\n                    .date(new Date())\n                    .build();\n            linkAccessStatsMapper.shortLinkStats(linkAccessStatsDO);\n            Map&lt;String, Object&gt; localeParamMap = new HashMap&lt;&gt;();\n            localeParamMap.put(\"key\", statsLocaleAmapKey);\n            localeParamMap.put(\"ip\", remoteAddr);\n            String localeResultStr = HttpUtil.get(AMAP_REMOTE_URL, localeParamMap);\n            JSONObject localeResultObj = JSON.parseObject(localeResultStr);\n            String infoCode = localeResultObj.getString(\"infocode\");\n            if (StrUtil.isNotBlank(infoCode) &amp;&amp; StrUtil.equals(infoCode, \"10000\")) {\n                String province = localeResultObj.getString(\"province\");\n                boolean unknownFlag = StrUtil.equals(province, \"&#91;]\");\n                LinkLocaleStatsDO linkLocaleStatsDO = LinkLocaleStatsDO.builder()\n                        .province(unknownFlag ? \"\u672a\u77e5\" : province)\n                        .city(unknownFlag ? \"\u672a\u77e5\" : localeResultObj.getString(\"city\"))\n                        .adcode(unknownFlag ? \"\u672a\u77e5\" : localeResultObj.getString(\"adcode\"))\n                        .cnt(1)\n                        .fullShortUrl(fullShortUrl)\n                        .country(\"\u4e2d\u56fd\")\n                        .gid(gid)\n                        .date(new Date())\n                        .build();\n                linkLocaleStatsMapper.shortLinkLocaleState(linkLocaleStatsDO);\n                String os = LinkUtil.getOs(((HttpServletRequest) request));\n\n                LinkOsStatsDO linkOsStatsDO = LinkOsStatsDO.builder()\n                        .os(os)\n                        .cnt(1)\n                        .gid(gid)\n                        .fullShortUrl(fullShortUrl)\n                        .date(new Date())\n                        .build();\n                linkOsStatsMapper.shortLinkOsState(linkOsStatsDO);\n\n                String browser = LinkUtil.getBrowser(((HttpServletRequest) request));\n\n                LinkBrowserStatsDO linkBrowserStatsDO = LinkBrowserStatsDO.builder()\n                        .browser(browser)\n                        .cnt(1)\n                        .gid(gid)\n                        .fullShortUrl(fullShortUrl)\n                        .date(new Date())\n                        .build();\n                LinkAccessLogsDO linkAccessLogsDO = LinkAccessLogsDO.builder()\n                        .user(uv.get())\n                        .ip(remoteAddr)\n                        .browser(browser)\n                        .os(os)\n                        .gid(gid)\n                        .fullShortUrl(fullShortUrl)\n                        .build();\n                linkAccessLogsMapper.insert(linkAccessLogsDO);\n                linkBrowserStatsMapper.shortLinkBrowserState(linkBrowserStatsDO);\n            }\n\n        } catch (Throwable ex) {\n            log.error(\"\u77ed\u94fe\u63a5\u8bbf\u95ee\u91cf\u7edf\u8ba1\u5f02\u5e38\", ex);\n        }\n    }<\/code><\/pre>\n\n\n\n<p>\u00a0<img loading=\"lazy\" decoding=\"async\" height=\"90\" width=\"1398\" src=\"https:\/\/i-blog.csdnimg.cn\/direct\/64edea430999484f805c7883dbaea111.png\" alt=\"\"><img loading=\"lazy\" decoding=\"async\" height=\"79\" width=\"1441\" src=\"https:\/\/i-blog.csdnimg.cn\/direct\/42bc76fb220e4e56acbd9fbf7af928ec.png\" alt=\"\"><img loading=\"lazy\" decoding=\"async\" height=\"80\" width=\"1656\" src=\"https:\/\/i-blog.csdnimg.cn\/direct\/b2b1260cf82746bc811443c7f98684c9.png\" alt=\"\"><\/p>\n\n\n\n<p>\u8fd9\u91cc\u6bcf\u6709\u4e00\u4e2a\u77ed\u94fe\u63a5\u88ab\u8bbf\u95ee\u5c31\u4f1a\u5f80\u6570\u636e\u5e93\u63d2\u4e00\u6761\u6570\u636e\u4e0d\u4f1a\u5f88\u5360\u7a7a\u95f4\u561b,\u4e3a\u4ec0\u4e48\u4e0d\u50cf\u4e4b\u524d\u7684\u8868\u4e00\u6837\u641e\u4e2acnt\u6765\u7edf\u8ba1,\u662f\u56e0\u4e3a\u8fd9\u5f20\u8868\u4f1a\u5b9a\u671f\u6e05\u7406\u8d85\u8fc7\u4e00\u5b9a\u65f6\u95f4\u7684\u6570\u636e\u561b,\u53ea\u6709\u8fd1\u671f\u7684\u6570\u636e\u6bd4\u8f83\u6709\u4ef7\u503c\u561b&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">- \u7b2c10\u8282\uff1a\u5982\u4f55\u7edf\u8ba1\u77ed\u94fe\u63a5\u8bbf\u5ba2\u7c7b\u578b\u8bbf\u95ee<\/h2>\n\n\n\n<p>&nbsp;\u5f53\u9009\u62e9\u4e00\u4e2a\u7279\u5b9a\u7684\u65f6\u95f4\u6bb5\u65f6\uff0c\u201c\u65b0\u8bbf\u5ba2\u201d\u6307\u7684\u662f\u5728\u8be5\u65f6\u95f4\u6bb5\u5185\u8bbf\u95ee\u8fc7\u77ed\u94fe\u63a5\u7684\u4eba\u7fa4\u4e2d\uff0c\u6709\u591a\u5c11\u4eba\u662f\u9996\u6b21\u8bbf\u95ee\u8005\u3002\u76f8\u5bf9\u5e94\u5730\uff0c\u201c\u8001\u8bbf\u5ba2\u201d\u6307\u7684\u662f\u5728\u8be5\u65f6\u95f4\u6bb5\u5185\u8bbf\u95ee\u8fc7\u77ed\u94fe\u63a5\u7684\u4eba\u7fa4\u4e2d\uff0c\u5df2\u7ecf\u5728\u4e4b\u524d\u8bbf\u95ee\u8fc7\u7684\u4eba\u6570\u3002<br>\u53ef\u4ee5\u9009\u4e2d\u5f53\u5929\u7684\u65e5\u671f\u7136\u540e\u67e5\u770b\u8001\u8bbf\u5ba2\u7684\u6570\u91cf\uff0c\u5982\u679c\u8001\u8bbf\u5ba2\u6570\u91cf\u8f83\u591a\uff0c\u5c31\u8bf4\u660e\u60a8\u6295\u653e\u7684\u77ed\u94fe\u63a5\u5f88\u53d7\u6b22\u8fce\uff0c\u60a8\u7684\u7528\u6237\u4f1a\u6301\u7eed\u6765\u8bbf\u95ee\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">- \u7b2c11\u8282\uff1a\u5982\u4f55\u7edf\u8ba1\u77ed\u94fe\u63a5\u8bbf\u95ee\u8bbe\u5907\u8bbf\u95ee<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE TABLE `t_link_device_stats` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',\n  `full_short_url` varchar(128) DEFAULT NULL COMMENT '\u5b8c\u6574\u77ed\u94fe\u63a5',\n  `gid` varchar(32) DEFAULT 'default' COMMENT '\u5206\u7ec4\u6807\u8bc6',\n  `date` date DEFAULT NULL COMMENT '\u65e5\u671f',\n  `cnt` int(11) DEFAULT NULL COMMENT '\u8bbf\u95ee\u91cf',\n  `device` varchar(64) DEFAULT NULL COMMENT '\u8bbf\u95ee\u8bbe\u5907',\n  `create_time` datetime DEFAULT NULL COMMENT '\u521b\u5efa\u65f6\u95f4',\n  `update_time` datetime DEFAULT NULL COMMENT '\u4fee\u6539\u65f6\u95f4',\n  `del_flag` tinyint(1) DEFAULT NULL COMMENT '\u5220\u9664\u6807\u8bc6 0\uff1a\u672a\u5220\u9664 1\uff1a\u5df2\u5220\u9664',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `idx_unique_browser_stats` (`full_short_url`,`gid`,`date`,`device`) USING BTREE\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;;<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-preformatted\">package com.nageoffer.shortlink.project.dao.entiry;\n\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.nageoffer.shortlink.project.common.database.BaseDO;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.Date;\n\n\/**\n * \u8bbf\u95ee\u8bbe\u5907\u7edf\u8ba1\u8bbf\u95ee\u5b9e\u4f53\n *\/\n@Data\n@TableName(\"t_link_device_stats\")\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class LinkDeviceStatsDO extends BaseDO {\n\n    \/**\n     * id\n     *\/\n    private Long id;\n\n    \/**\n     * \u5b8c\u6574\u77ed\u94fe\u63a5\n     *\/\n    private String fullShortUrl;\n\n    \/**\n     * \u5206\u7ec4\u6807\u8bc6\n     *\/\n    private String gid;\n\n    \/**\n     * \u65e5\u671f\n     *\/\n    private Date date;\n\n    \/**\n     * \u8bbf\u95ee\u91cf\n     *\/\n    private Integer cnt;\n\n    \/**\n     * \u6d4f\u89c8\u5668\n     *\/\n    private String device;\n}<\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>    \/**\n     * \u83b7\u53d6\u7528\u6237\u8bbf\u95ee\u8bbe\u5907\n     *\n     * @param request \u8bf7\u6c42\n     * @return \u8bbf\u95ee\u8bbe\u5907\n     *\/\n    public static String getDevice(HttpServletRequest request) {\n        String userAgent = request.getHeader(\"User-Agent\");\n        if (userAgent.toLowerCase().contains(\"mobile\")) {\n            return \"Mobile\";\n        }\n        return \"PC\";\n    }\n    \/\/\u5b9e\u9645\u4e0a\u662f\u60f3\u770b\u770b\u6709\u6ca1\u6709ipad\u5e73\u677f\u7c7b\u7684<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>                LinkDeviceStatsDO linkDeviceStatsDO = LinkDeviceStatsDO.builder()\n                        .device(LinkUtil.getDevice(((HttpServletRequest) request)))\n                        .cnt(1)\n                        .gid(gid)\n                        .fullShortUrl(fullShortUrl)\n                        .date(new Date())\n                        .build();\n                linkDeviceStatsMapper.shortLinkDeviceState(linkDeviceStatsDO);<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-preformatted\">package com.nageoffer.shortlink.project.dao.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.nageoffer.shortlink.project.dao.entiry.LinkDeviceStatsDO;\nimport org.apache.ibatis.annotations.Insert;\nimport org.apache.ibatis.annotations.Param;\n\n\/**\n * \u8bbf\u95ee\u8bbe\u5907\u76d1\u63a7\u6301\u4e45\u5c42\n *\/\npublic interface LinkDeviceStatsMapper extends BaseMapper&lt;LinkDeviceStatsDO&gt; {\n\n    \/**\n     * \u8bb0\u5f55\u8bbf\u95ee\u8bbe\u5907\u76d1\u63a7\u6570\u636e\n     *\/\n    @Insert(\"INSERT INTO t_link_device_stats (full_short_url, gid, date, cnt, device, create_time, update_time, del_flag) \" +\n            \"VALUES( #{linkDeviceStats.fullShortUrl}, #{linkDeviceStats.gid}, #{linkDeviceStats.date}, #{linkDeviceStats.cnt}, #{linkDeviceStats.device}, NOW(), NOW(), 0) \" +\n            \"ON DUPLICATE KEY UPDATE cnt = cnt +  #{linkDeviceStats.cnt};\")\n    void shortLinkDeviceState(@Param(\"linkDeviceStats\") LinkDeviceStatsDO linkDeviceStatsDO);\n}<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">- \u7b2c12\u8282\uff1a\u5982\u4f55\u7edf\u8ba1\u77ed\u94fe\u63a5\u8bbf\u95ee\u7f51\u7edc\u8bbf\u95ee<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE TABLE `t_link_network_stats` (\n  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',\n  `full_short_url` varchar(128) DEFAULT NULL COMMENT '\u5b8c\u6574\u77ed\u94fe\u63a5',\n  `gid` varchar(32) DEFAULT 'default' COMMENT '\u5206\u7ec4\u6807\u8bc6',\n  `date` date DEFAULT NULL COMMENT '\u65e5\u671f',\n  `cnt` int(11) DEFAULT NULL COMMENT '\u8bbf\u95ee\u91cf',\n  `network` varchar(64) DEFAULT NULL COMMENT '\u8bbf\u95ee\u7f51\u7edc',\n  `create_time` datetime DEFAULT NULL COMMENT '\u521b\u5efa\u65f6\u95f4',\n  `update_time` datetime DEFAULT NULL COMMENT '\u4fee\u6539\u65f6\u95f4',\n  `del_flag` tinyint(1) DEFAULT NULL COMMENT '\u5220\u9664\u6807\u8bc6 0\uff1a\u672a\u5220\u9664 1\uff1a\u5df2\u5220\u9664',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `idx_unique_browser_stats` (`full_short_url`,`gid`,`date`,`network`) USING BTREE\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;;<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-preformatted\">package com.nageoffer.shortlink.project.dao.entiry;\n\n\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.nageoffer.shortlink.project.common.database.BaseDO;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.Date;\n\n\/**\n * \u8bbf\u95ee\u7f51\u7edc\u7edf\u8ba1\u8bbf\u95ee\u5b9e\u4f53\n *\/\n@Data\n@TableName(\"t_link_network_stats\")\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class LinkNetworkStatsDO extends BaseDO {\n\n    \/**\n     * id\n     *\/\n    private Long id;\n\n    \/**\n     * \u5b8c\u6574\u77ed\u94fe\u63a5\n     *\/\n    private String fullShortUrl;\n\n    \/**\n     * \u5206\u7ec4\u6807\u8bc6\n     *\/\n    private String gid;\n\n    \/**\n     * \u65e5\u671f\n     *\/\n    private Date date;\n\n    \/**\n     * \u8bbf\u95ee\u91cf\n     *\/\n    private Integer cnt;\n\n    \/**\n     * \u8bbf\u95ee\u7f51\u7edc\n     *\/\n    private String network;\n}<\/pre>\n\n\n\n<pre class=\"wp-block-preformatted\">package com.nageoffer.shortlink.project.dao.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.nageoffer.shortlink.project.dao.entiry.LinkNetworkStatsDO;\nimport org.apache.ibatis.annotations.Insert;\nimport org.apache.ibatis.annotations.Param;\n\n\/**\n * \u8bbf\u95ee\u7f51\u7edc\u76d1\u63a7\u6301\u4e45\u5c42\n *\/\npublic interface LinkNetworkStatsMapper extends BaseMapper&lt;LinkNetworkStatsDO&gt; {\n\n    \/**\n     * \u8bb0\u5f55\u8bbf\u95ee\u8bbe\u5907\u76d1\u63a7\u6570\u636e\n     *\/\n    @Insert(\"INSERT INTO t_link_network_stats (full_short_url, gid, date, cnt, network, create_time, update_time, del_flag) \" +\n            \"VALUES( #{linkNetworkStats.fullShortUrl}, #{linkNetworkStats.gid}, #{linkNetworkStats.date}, #{linkNetworkStats.cnt}, #{linkNetworkStats.network}, NOW(), NOW(), 0) \" +\n            \"ON DUPLICATE KEY UPDATE cnt = cnt +  #{linkNetworkStats.cnt};\")\n    void shortLinkNetworkState(@Param(\"linkNetworkStats\") LinkNetworkStatsDO linkNetworkStatsDO);\n}\n<\/pre>\n\n\n\n<pre class=\"wp-block-preformatted\">\/**\n * \u83b7\u53d6\u7528\u6237\u8bbf\u95ee\u7f51\u7edc\n *\n * @param request \u8bf7\u6c42\n * @return \u8bbf\u95ee\u8bbe\u5907\n *\/\npublic static String getNetwork(HttpServletRequest request) {\n    String actualIp = getActualIp(request);\n    \/\/ \u8fd9\u91cc\u7b80\u5355\u5224\u65adIP\u5730\u5740\u8303\u56f4\uff0c\u60a8\u53ef\u80fd\u9700\u8981\u66f4\u590d\u6742\u7684\u903b\u8f91\n    \/\/ \u4f8b\u5982\uff0c\u901a\u8fc7\u8c03\u7528IP\u5730\u5740\u5e93\u6216\u8c03\u7528\u7b2c\u4e09\u65b9\u670d\u52a1\u6765\u5224\u65ad\u7f51\u7edc\u7c7b\u578b\n    return actualIp.startsWith(\"192.168.\") || actualIp.startsWith(\"10.\") ? \"WIFI\" : \"Mobile\";\n}<\/pre>\n\n\n\n<pre class=\"wp-block-preformatted\">LinkNetworkStatsDO linkNetworkStatsDO = LinkNetworkStatsDO.builder()\n        .network(LinkUtil.getNetwork(((HttpServletRequest) request)))\n        .cnt(1)\n        .gid(gid)\n        .fullShortUrl(fullShortUrl)\n        .date(new Date())\n        .build();\nlinkNetworkStatsMapper.shortLinkNetworkState(linkNetworkStatsDO);<\/pre>\n\n\n\n<p>\u672c\u5730localhost\u662f127.0.0.1\uff0c\u4e0d\u6ee1\u8db3getNetwork()\u7684\u8fd4\u56de\u6761\u4ef6\uff0c\u6240\u6709\u624d\u8fd4\u56de\u7684\"Mobile\" \u200b<\/p>\n","protected":false},"excerpt":{"rendered":"<p>&#8211; \u7b2c01\u8282\uff1a\u77ed\u94fe\u63a5\u7edf\u8ba1\u6a21\u5757\u529f\u80fd\u5206\u6790 &nbsp;\u5927\u5bb6\u4e00\u8d77\u6765\u804a\u4e00\u4e0b\u77ed\u94fe\u63a5\u76d1\u63a7\u8be5\u600e\u4e48\u505a\uff0c\u7136\u540e\u4f17\u6240\u5468\u77e5\uff0c\u77ed\u94fe\u63a5\u5982\u679c\u8bf4\u53ea\u662f\u7528\u4f5c\u8df3\u8f6c\uff0c\u5176\u5b9e &#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"emotion":"","emotion_color":"","title_style":"","license":"","footnotes":""},"categories":[3],"tags":[16,20],"class_list":["post-1206","post","type-post","status-publish","format-standard","hentry","category-3","tag-sql","tag-20"],"_links":{"self":[{"href":"https:\/\/eve2333.top\/index.php?rest_route=\/wp\/v2\/posts\/1206","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/eve2333.top\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/eve2333.top\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/eve2333.top\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/eve2333.top\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1206"}],"version-history":[{"count":1,"href":"https:\/\/eve2333.top\/index.php?rest_route=\/wp\/v2\/posts\/1206\/revisions"}],"predecessor-version":[{"id":1207,"href":"https:\/\/eve2333.top\/index.php?rest_route=\/wp\/v2\/posts\/1206\/revisions\/1207"}],"wp:attachment":[{"href":"https:\/\/eve2333.top\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1206"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/eve2333.top\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1206"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/eve2333.top\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1206"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}