mirror of
https://github.com/zhwei820/learn.lianglianglee.com.git
synced 2025-09-25 20:56:42 +08:00
753 lines
22 KiB
HTML
753 lines
22 KiB
HTML
<!DOCTYPE html>
|
||
|
||
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
|
||
|
||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||
|
||
<head>
|
||
|
||
<head>
|
||
|
||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||
|
||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
|
||
|
||
<link rel="icon" href="/static/favicon.png">
|
||
|
||
<title>01 以真实“商场停车”业务切入——需求分析.md.html</title>
|
||
|
||
<!-- Spectre.css framework -->
|
||
|
||
<link rel="stylesheet" href="/static/index.css">
|
||
|
||
<!-- theme css & js -->
|
||
|
||
<meta name="generator" content="Hexo 4.2.0">
|
||
|
||
</head>
|
||
|
||
|
||
|
||
<body>
|
||
|
||
|
||
|
||
<div class="book-container">
|
||
|
||
<div class="book-sidebar">
|
||
|
||
<div class="book-brand">
|
||
|
||
<a href="/">
|
||
|
||
<img src="/static/favicon.png">
|
||
|
||
<span>技术文章摘抄</span>
|
||
|
||
</a>
|
||
|
||
</div>
|
||
|
||
<div class="book-menu uncollapsible">
|
||
|
||
<ul class="uncollapsible">
|
||
|
||
<li><a href="/" class="current-tab">首页</a></li>
|
||
|
||
</ul>
|
||
|
||
|
||
|
||
<ul class="uncollapsible">
|
||
|
||
<li><a href="../">上一级</a></li>
|
||
|
||
</ul>
|
||
|
||
|
||
|
||
<ul class="uncollapsible">
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/SpringCloud微服务实战(完)/00 开篇导读.md.html">00 开篇导读.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
<a class="current-tab" href="/专栏/SpringCloud微服务实战(完)/01 以真实“商场停车”业务切入——需求分析.md.html">01 以真实“商场停车”业务切入——需求分析.md.html</a>
|
||
|
||
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/SpringCloud微服务实战(完)/02 具象业务需求再抽象分解——系统设计.md.html">02 具象业务需求再抽象分解——系统设计.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/SpringCloud微服务实战(完)/03 第一个 Spring Boot 子服务——会员服务.md.html">03 第一个 Spring Boot 子服务——会员服务.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/SpringCloud微服务实战(完)/04 如何维护接口文档供外部调用——在线接口文档管理.md.html">04 如何维护接口文档供外部调用——在线接口文档管理.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/SpringCloud微服务实战(完)/05 认识 Spring Cloud 与 Spring Cloud Alibaba 项目.md.html">05 认识 Spring Cloud 与 Spring Cloud Alibaba 项目.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/SpringCloud微服务实战(完)/06 服务多不易管理如何破——服务注册与发现.md.html">06 服务多不易管理如何破——服务注册与发现.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/SpringCloud微服务实战(完)/07 如何调用本业务模块外的服务——服务调用.md.html">07 如何调用本业务模块外的服务——服务调用.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/SpringCloud微服务实战(完)/08 服务响应慢或服务不可用怎么办——快速失败与服务降级.md.html">08 服务响应慢或服务不可用怎么办——快速失败与服务降级.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/SpringCloud微服务实战(完)/09 热更新一样更新服务的参数配置——分布式配置中心.md.html">09 热更新一样更新服务的参数配置——分布式配置中心.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/SpringCloud微服务实战(完)/10 如何高效读取计费规则等热数据——分布式缓存.md.html">10 如何高效读取计费规则等热数据——分布式缓存.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/SpringCloud微服务实战(完)/11 多实例下的定时任务如何避免重复执行——分布式定时任务.md.html">11 多实例下的定时任务如何避免重复执行——分布式定时任务.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/SpringCloud微服务实战(完)/12 同一套服务如何应对不同终端的需求——服务适配.md.html">12 同一套服务如何应对不同终端的需求——服务适配.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/SpringCloud微服务实战(完)/13 采用消息驱动方式处理扣费通知——集成消息中间件.md.html">13 采用消息驱动方式处理扣费通知——集成消息中间件.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/SpringCloud微服务实战(完)/14 Spring Cloud 与 Dubbo 冲突吗——强强联合.md.html">14 Spring Cloud 与 Dubbo 冲突吗——强强联合.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/SpringCloud微服务实战(完)/15 破解服务中共性问题的繁琐处理方式——接入 API 网关.md.html">15 破解服务中共性问题的繁琐处理方式——接入 API 网关.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/SpringCloud微服务实战(完)/16 服务压力大系统响应慢如何破——网关流量控制.md.html">16 服务压力大系统响应慢如何破——网关流量控制.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/SpringCloud微服务实战(完)/17 集成网关后怎么做安全验证——统一鉴权.md.html">17 集成网关后怎么做安全验证——统一鉴权.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/SpringCloud微服务实战(完)/18 多模块下的接口 API 如何统一管理——聚合 API.md.html">18 多模块下的接口 API 如何统一管理——聚合 API.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/SpringCloud微服务实战(完)/19 数据分库后如何确保数据完整性——分布式事务.md.html">19 数据分库后如何确保数据完整性——分布式事务.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/SpringCloud微服务实战(完)/20 优惠券如何避免超兑——引入分布式锁.md.html">20 优惠券如何避免超兑——引入分布式锁.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/SpringCloud微服务实战(完)/21 如何查看各服务的健康状况——系统应用监控.md.html">21 如何查看各服务的健康状况——系统应用监控.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/SpringCloud微服务实战(完)/22 如何确定一次完整的请求过程——服务链路跟踪.md.html">22 如何确定一次完整的请求过程——服务链路跟踪.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/SpringCloud微服务实战(完)/23 结束语.md.html">23 结束语.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
|
||
|
||
<div class="sidebar-toggle-inner"></div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
<script>
|
||
|
||
function add_inner() {
|
||
|
||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||
|
||
inner.classList.add('show')
|
||
|
||
}
|
||
|
||
|
||
|
||
function remove_inner() {
|
||
|
||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||
|
||
inner.classList.remove('show')
|
||
|
||
}
|
||
|
||
|
||
|
||
function sidebar_toggle() {
|
||
|
||
let sidebar_toggle = document.querySelector('.sidebar-toggle')
|
||
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
|
||
let content = document.querySelector('.off-canvas-content')
|
||
|
||
if (sidebar_toggle.classList.contains('extend')) { // show
|
||
|
||
sidebar_toggle.classList.remove('extend')
|
||
|
||
sidebar.classList.remove('hide')
|
||
|
||
content.classList.remove('extend')
|
||
|
||
} else { // hide
|
||
|
||
sidebar_toggle.classList.add('extend')
|
||
|
||
sidebar.classList.add('hide')
|
||
|
||
content.classList.add('extend')
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
function open_sidebar() {
|
||
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
|
||
let overlay = document.querySelector('.off-canvas-overlay')
|
||
|
||
sidebar.classList.add('show')
|
||
|
||
overlay.classList.add('show')
|
||
|
||
}
|
||
|
||
function hide_canvas() {
|
||
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
|
||
let overlay = document.querySelector('.off-canvas-overlay')
|
||
|
||
sidebar.classList.remove('show')
|
||
|
||
overlay.classList.remove('show')
|
||
|
||
}
|
||
|
||
|
||
|
||
</script>
|
||
|
||
|
||
|
||
<div class="off-canvas-content">
|
||
|
||
<div class="columns">
|
||
|
||
<div class="column col-12 col-lg-12">
|
||
|
||
<div class="book-navbar">
|
||
|
||
<!-- For Responsive Layout -->
|
||
|
||
<header class="navbar">
|
||
|
||
<section class="navbar-section">
|
||
|
||
<a onclick="open_sidebar()">
|
||
|
||
<i class="icon icon-menu"></i>
|
||
|
||
</a>
|
||
|
||
</section>
|
||
|
||
</header>
|
||
|
||
</div>
|
||
|
||
<div class="book-content" style="max-width: 960px; margin: 0 auto;
|
||
|
||
overflow-x: auto;
|
||
|
||
overflow-y: hidden;">
|
||
|
||
<div class="book-post">
|
||
|
||
<p id="tip" align="center"></p>
|
||
|
||
<div><h1>01 以真实“商场停车”业务切入——需求分析</h1>
|
||
|
||
<p>不管是传统开发模式还是时下推崇的敏捷开发模式,都要从源头——业务需求开始着手,缺少了需求获取分析环节或本环节做的不够好,由此造成的错误会随着软件迭代的步伐,逐步被放大,近而造成巨大的损失。有时金钱成本尚可接受,但由此造成的时间成本、机会成本则是无法估量的。</p>
|
||
|
||
<p>为什么选取"商场停车业务"作为需求原型呢,有两个考量:一是场景熟知度比较高,业务理解上不存在很大障碍;二是能将我们所要表达的微服务特性融入进去,业务复杂度较低,便于上手开发,门槛太高反而不利于技术实践。</p>
|
||
|
||
<p>下面随着课程的步伐,一步一步深入进去吧。</p>
|
||
|
||
<h3>停车收费需求</h3>
|
||
|
||
<p>相信大家经常去商场购物,不管你有没有停过车,商场的停车场不会是个陌生的存在,本次实操就是以商场停车收费为业务原型开展。业务需求相对简单(实际业务要比案例中功能更复杂),主要是要能通过简单的业务功能掌握到我们关心的微服务技术栈即可。</p>
|
||
|
||
<p>主要干系人就是商场物业管理部,提出的原始需求如下:</p>
|
||
|
||
<ol>
|
||
|
||
<li>用户手机号绑定商场的系统,在里面添加车辆,每天到商场附近时可以签到,领取积分,积分可以用于兑换限量的商场优惠券,也可以兑换洗车券,在停车场指定位置洗车。</li>
|
||
|
||
<li>用户可以通过系统开通 VIP 停车月卡,出入停车场时,无须额外付费。可以充值为月卡延期,充值时,为用户赠送一定数量的积分,可以做为正常积分使用。充值后,用户可以收到充值通知。</li>
|
||
|
||
<li>若用户未开通月卡,出场时,必须在系统里支付停车费用后,才能出场。费用多少按停车场约定的计费规则来计算。缴费后,用户能收到缴费通知。</li>
|
||
|
||
<li>用户进停车场前,能实时看到可用停车位数量。</li>
|
||
|
||
<li>在系统后台,可以看到用户的支付流水、充值流水,车位使用情况等</li>
|
||
|
||
</ol>
|
||
|
||
<p>业务场景很清晰,也比较常见(实际场景中,本系统应当与其它系统打通,数据共享,便于数据分析、统计挖掘等,实际功能同样比这个更复杂些),本次就以如上的简单需求,结合微服务技术栈,来一次从 0 到 1 的完整性开发实践。</p>
|
||
|
||
<h3>需求整理思路</h3>
|
||
|
||
<p>Scrum 敏捷开发实践,在国内应用的时间也不算短,不少企业已经成功应用于产品迭代开发中,本次实战会融入敏捷思想,来指导这次的微服务开发实战。本文的需求分析工作多数情况下是由 PO(产品经理)完成。</p>
|
||
|
||
<p>按传统的瀑布研发模式来走,流程大致时需求分析,输出需求分析文档,而后进行概要设计、详细设计(数据库设计),并输出对应的文档,后续再进行编码工作。</p>
|
||
|
||
<p>敏捷中提倡可工作的文档高于详尽的文档,所以无须严格按照传统的模块输出一堆文档,能表述清楚、能与团队成员正确传达即可。</p>
|
||
|
||
<p><img src="assets/2020-05-05-021642.jpg" alt="img" /></p>
|
||
|
||
<p>(图片来源于网站<a href="http://agilemanifesto.org/">http://agilemanifesto.org/</a>)</p>
|
||
|
||
<p>基于原始需求,由 PO 输出用户故事列表形成需求池 Product Backlog,每个迭代中从中按优先级顺序,放入不同的迭代中形成 Sprint Backlog,同时分解故事为任务(故事的粒度要比任务粗,是一组功能的组合),由组员一起将任务工作量估算并认领。任务工作量汇总后,就形成了当前迭代的总工作量,基于燃尽图,随着时间的延长以及工作进度的推进,可以清晰的从燃尽图中看到任务的完成情况。可以借助于看板,将故事任务以不同色块的 3M 便利贴贴在白板上,画出待办列表、进行中列表、已完成列表,任务完成后由组员调整位置。</p>
|
||
|
||
<p><img src="assets/2020-05-05-021643.jpg" alt="img" /> (工具绘制)</p>
|
||
|
||
<p><img src="assets/2020-05-05-021645.png" alt="img" /></p>
|
||
|
||
<p>(图片来源于敏捷中文网)</p>
|
||
|
||
<p>为了提高工作效率,一般会借助对应的敏捷开发工具完成,比如 Teambition、JIRA、禅道等等,里面针对敏捷的各个环节都有很好的支持,相信能帮助大家节省不少成本。</p>
|
||
|
||
<h3>需求分析</h3>
|
||
|
||
<p>依据原始停车需求,整合分析后可归集为六个业务模块:</p>
|
||
|
||
<ol>
|
||
|
||
<li>会员,会员信息(主要是姓名、手机号、生日),车辆信息(主要车牌信息),月卡信息(充值一定金额后,指定区间内免支付出入场,针对商场的工作人员或办公区的白领),签到信息</li>
|
||
|
||
<li>停车场资源,车位(车辆停靠车位后,系统自动打标,可用车位扣减,停车场入口可用车实时更新)、道闸(基础数据,真实场景中涉及到岗亭值班人员安排,设备维护等)</li>
|
||
|
||
<li>积分,会员到达商场后签到领取积分(如签到一次可免费停车 2 小时,超出部分,以计费规则为准)。积分可兑换商场优惠券、洗车券。</li>
|
||
|
||
<li>计费,计费规则,车辆进场(记录车辆入场时间,开始计费)、出场扣费(出场时识别车牌,依计费规则,支付停车费,分临时车与月卡车两种,临时车现场收费,月卡车做有效期判定是符合条件后再出场)</li>
|
||
|
||
<li>财务,车辆出入的支付流水,会员月卡充值费用,收入统计</li>
|
||
|
||
<li>消息,车辆出场支付消息,会员充值消息</li>
|
||
|
||
<li>兑换洗车券后,停车场内洗车消费</li>
|
||
|
||
</ol>
|
||
|
||
<h3>用户故事、任务整理</h3>
|
||
|
||
<p>以上述需求归集情况,罗列几个代表性的用户故事,取代传统的需求文档。约定:商场停车系统以下简称为系统</p>
|
||
|
||
<ol>
|
||
|
||
<li>作为一个普通用户,我希望可以使用系统绑定手机号,并录入车辆信息,以便于可以离场时支付费用,接受支付结果通知。</li>
|
||
|
||
<li>作为一个系统用户,我希望可以开通停车场月卡,同时可额外获得积分,以便于在离场时无须额外支付,快速通过。</li>
|
||
|
||
<li>作为一个系统用户,我希望可以通过系统日常签到,得到积分,以便于可以兑换商场优惠券或停车场洗车券。</li>
|
||
|
||
<li>作为一个普遍用户/系统,我希望可以在进入停车场前,识别可用车位数,以便我做出相应的决策。</li>
|
||
|
||
<li>作为一个系统月卡用户,为月卡充值时收到消费通知,同时得到额外的积分。</li>
|
||
|
||
<li>作为一个系统用户,离场时需要按计费规则支付对应的停车费,并接收支付通知。</li>
|
||
|
||
<li>作为一个系统管理用户,我希望可以看到所有场内车位的使用情况、用户的支付流水、会员月卡的充值流水。</li>
|
||
|
||
</ol>
|
||
|
||
<p>基于以上故事,将故事拆解成较细的任务,拿第一个 User Story 举例。</p>
|
||
|
||
<p>1.1 商场系统中绑定手机号,通过手机验收码,确定是本人手机。</p>
|
||
|
||
<p>1.2 完善个人信息,可以保存个人姓名、生日等信息</p>
|
||
|
||
<p>1.3 录入车辆车牌信息,后期出场车牌识别后,自动计算车辆停车费</p>
|
||
|
||
<p>罗列出故事的具体任务后,开发人员针对任务情况,进行工作量评估,敏捷估算工作量常用用部分斐波那契数列(小时数):0 ,1 ,2 ,3 ,5 ,8, 13, 21 等,建议不超过 8 时,超过 8 意味着需要进一步分拆,争取在一天内可以完成一个任务。估算需要选取一个基准复杂度,比如一次 DB 交互为 1,在此基础上,分析每个故事的复杂度,结合前面的工作量估算方法就可以给某个任务确定预估工时数。</p>
|
||
|
||
<p>有朋友会比较担心,万一预估的时间少了完不成怎么办,其实这只是初步预估,在实际执行时,以实际执行为准,没有一个敏捷团队的迭代燃尽图会像理想状态一般燃烧。</p>
|
||
|
||
<p>实操过程中不少小伙伴存在一个误区:关注花费过的时间,而不是关注剩余工作时间,燃尽图中显示是剩余工时数量。</p>
|
||
|
||
<h3>关键业务流程梳理</h3>
|
||
|
||
<p>基于需求情况,找出核心的业务流程,指导后期的核心业务开发。</p>
|
||
|
||
<p><img src="assets/2020-05-05-021646.png" alt="img" /></p>
|
||
|
||
<p>小结:</p>
|
||
|
||
<p>本篇带你结合 Scrum 敏捷思想进行产品需求的分析,由 PO 人员整理用户故事、核心业务流程,由开发人员认领故事,分解故事为具体任务,并预估工作量,录入敏捷辅助工具中,为期开发人员介入作好准备工作。</p>
|
||
|
||
<h3>留 2 个节后实践题目</h3>
|
||
|
||
<ol>
|
||
|
||
<li>初中级的开发者普遍不重视需求,更追求技能的提升,如果要走的快走的远,一定要坚持把技术、业务一起抓。尝试将所有 User Story 罗列出来吧,并分解为 Task 录入到一个特定的敏捷开发辅助工具中去,并结合敏捷开发全流程,跟随项目的实战全程体验一番。</li>
|
||
|
||
<li>敏捷真的不需要留存文档吗?</li>
|
||
|
||
</ol>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div>
|
||
|
||
<div style="float: left">
|
||
|
||
<a href="/专栏/SpringCloud微服务实战(完)/00 开篇导读.md.html">上一页</a>
|
||
|
||
</div>
|
||
|
||
<div style="float: right">
|
||
|
||
<a href="/专栏/SpringCloud微服务实战(完)/02 具象业务需求再抽象分解——系统设计.md.html">下一页</a>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
|
||
|
||
</div>
|
||
|
||
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"70997586ee293d60","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
|
||
|
||
</body>
|
||
|
||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||
|
||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
|
||
|
||
<script>
|
||
|
||
window.dataLayer = window.dataLayer || [];
|
||
|
||
|
||
|
||
function gtag() {
|
||
|
||
dataLayer.push(arguments);
|
||
|
||
}
|
||
|
||
|
||
|
||
gtag('js', new Date());
|
||
|
||
gtag('config', 'G-NPSEEVD756');
|
||
|
||
var path = window.location.pathname
|
||
|
||
var cookie = getCookie("lastPath");
|
||
|
||
console.log(path)
|
||
|
||
if (path.replace("/", "") === "") {
|
||
|
||
if (cookie.replace("/", "") !== "") {
|
||
|
||
console.log(cookie)
|
||
|
||
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
setCookie("lastPath", path)
|
||
|
||
}
|
||
|
||
|
||
|
||
function setCookie(cname, cvalue) {
|
||
|
||
var d = new Date();
|
||
|
||
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
|
||
|
||
var expires = "expires=" + d.toGMTString();
|
||
|
||
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
|
||
|
||
}
|
||
|
||
|
||
|
||
function getCookie(cname) {
|
||
|
||
var name = cname + "=";
|
||
|
||
var ca = document.cookie.split(';');
|
||
|
||
for (var i = 0; i < ca.length; i++) {
|
||
|
||
var c = ca[i].trim();
|
||
|
||
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
|
||
|
||
}
|
||
|
||
return "";
|
||
|
||
}
|
||
|
||
|
||
|
||
</script>
|
||
|
||
|
||
|
||
</html>
|
||
|