并不想吹牛皮,但!为了把Github博客粉丝转移到公众号,我干了!
微信公众号:bugstack虫洞栈
沉淀、分享、成长,专注于原创专题案例,以最易学习编程的方式分享知识,让自己和他人都能有所收获。目前已完成的专题有;Netty4.x实战专题案例、用Java实现JVM、基于JavaAgent的全链路监控、手写RPC框架、架构设计专题案例[Ing]等。
1. 前言介绍
这个!截至到19年11月我已经工作6年了,从业Java但也折腾过C#、搞PHP也弄过中继器、IO板卡,似乎我就是一个很喜欢在技术上折腾的人!与此同时,我也搞了6年的个人小网站,它们的呈现形式多种多样;有用PHP自己捣鼓的技术站用于分享资料、书籍、软件等、有用PHPWIND和DISCUZ的搭建的个人论坛、有用emlog和wordpress搭建的个人博客、也有借用于github+hexo/jekyll的能力组装出的技术博客。但无一例外它们都战死于征战的路上了,亡于;org域名不能备案、PHP服务器瘫痪被清空、http连接被注入恶意内容、定位不准确经常换模式、缺少核心优质内容等等。但!老衲的心依然如春(cun),因为喜欢干一件事,往往来自于干一了件喜欢的事!
所以!从19年开始我又继续写博客了,注册了新的域名bugstack.cn,备了案、买了服务器、还喊了新的口号;沉淀、分享、成长,让自己和他人都能有所收获!并且将尘封已久的微信公众号找回;结构上调整、内容上布局、粉丝上求关注。在这期间遇到了更大的牛;小灰、王二哥、纯洁的微笑还有松哥等一群伙伴,从他们那学到很多知识,真的非常感谢!
那么!这次的产品功能总结一句话就是;将基于Github+Jekyll搭建的静态博客与我并未开发过的微信公众号功能打通,通过在文章短口令码加锁引导用户到公众号内回复密码可解锁内容,以此来获得粉丝关注,当然如果取消关注了则文章继续锁定。
在多说一句,我理解的产品;其实是使用研发技术力搭建出可以用于承载接收用户在各种设备上所生产的行为数据的一种产品化服务。所以有些产品在做减法,同时也有为丰富的功能做加法,但究其一点我们其实都是在为接收有价值数据服务的。兴衰存亡,皆在核心数据沉淀与运作上!
2. 流程设计
为了使博客粉丝主动关注微信公众号,我们在用户初次浏览文章时增加权限验证,给每一个用户都生成一个唯一码引导用户在公众号内回复解锁文章,以此来与微信openid对应。当用户取消关注时则进行删除openid或标记状态,使得用户无法继续浏览文章。其实为了更好的体验,我参照了大牛的方式内容60%的区域可见,其余内容渐进遮挡,蒙胧胧的感觉还挺美。整体流程图如下;
3. 功能实现
为了实现本产品功能,我准备了;
- 微信公众号;bugstack虫洞栈
- 博客;本文使用的是github+jekyll;地址:https://bugstack.cn
- 域名;域名申请比较简单,但可能针对不同的服务商还必须备案后才可以使用
- 友盟;主要为了获取全局的唯一id值,也可以使用其他具备此功能的产品或者自己实现
- 部署环境;Java云服务器、Mysql云数据库、https证书、Jdk1.8、tomcat1.8,云市场比较多按需购买,如果是本地调试可以使用https://natapp.cn,做免费网络穿透映射
3.1 前端
前端主要负责针对发布时设置了look: need的文章,在用户浏览文章检查是否有权限查看全部内容,当用户没有权限时隐藏文章60%内容,并通过页面结尾提醒用户在公众号内回复口令解锁文章。
- 找到加锁文章容器ID,文章容器选择器:article.post.container.need,拿到选择器ID针对这类文章进行缩小隐藏处理。
<article class="post container need lock" itemscope="" itemtype="http://schema.org/BlogPosting" style="height: 4400px;"><div class="row"><div class="col-md-9 markdown-body"><h2 id="前言介绍">前言介绍</h2>
<p>为什么会有路由层?因为在微服务架构设计中,往往并不会直接将服务暴漏给调用端,而是通过调用路由层进行业务隔离,以达到不同的业务调用对应的服务模块。</p><p><strong>Spring Cloud Zuul</strong></p>
- 使用Jquery把文章所在容器高度缩小,这样会把内容进行压缩
// 文章所在容器的选择器
var articleSelector = 'article.post.container.need';
// 找到文章所在的容器
var $article = $(articleSelector);// 文章的实际高度
var article = $article[0], height = article.clientHeight;
// 文章隐藏后的高度
var halfHeight = height * 0.4;
// 隐藏缩小
$article.css('height', halfHeight + 'px');
$article.addClass('lock');
- 给文章加一点朦胧感,渐变隐藏
.asb-post-01 {position: absolute;left: 0;bottom: 0;width: 100%;display: none;z-index: 10000;
margin-bottom: 0;
}.asb-post-01 .mask {height: 240px;width: 100%;background: -webkit-gradient(linear, 0 top, 0 bottom, from(rgba(255, 255, 255, 0)), to(#fff));
}
- 从UM全局唯一ID中获取token
友盟作为网站数据采集服务,会生成一个针对用户的全局唯一值UM_distinctid,而我们需要就使用这个值部分截取后作为唯一token加锁钥匙
UM_distinctid = 16e9cd64925334-0882eb883c9554-7711b3e-144000-16e9cd6492631c
function getCookie(name) {var value = "; " + document.cookie;var parts = value.split("; " + name + "=");if (parts.length == 2)return parts.pop().split(";").shift();
}function getToken() {let value = getCookie('UM_distinctid');if (!value) {return getUUID().toUpperCase();}return value.substring(value.length - 6).toUpperCase();
}
- 定时轮旋检查是否关注公众号并解锁,可以优化写入全局Jquery值或者记录在cookie,减少轮询检查次数
// 查询后端的结果
var _detect = function() {console.info(token);$.ajax({url : 'https://bugstack.cn/xx/xx/check',type: "GET",dataType: "text",data : {token : token},success : function(data) {console.log(data);if (data == 'refuse') {_lock();} else {_unlock();}},error : function(data) {_unlock();}})
}// 定时任务
_detect();
setInterval(function() {_detect();
}, 5000);
- 引导用户关注公众号
现在将token值回显到页面,提醒用户关注公众号回复口令解锁全部文章,以此来得到粉丝的关注。效果如下;
3.2 后端
- 本地环境开发需要安装(免费)http://natapp.cn/,做网络穿透便于我们进行测试
- 服务度三个主要接口;1同名称的get、post请求,分别用于公众号验验、接收用户行为与回复信息。2给博客提供验证是否解锁接口
- 在微信公众号;开发->基础设置(服务器配置),修改配置内容并开启服务,此时服务端会收到验证请求信息
- Java服务度采用领域驱动设计微服务方式开发,DDD的开发模式会更加清晰以及易于后续功能的拓展
- (提醒)个人微信号,部分接口权限是没有的,同时如果开启开发者功能配置后,自定义菜单和自定义回复都会失效。而自定义菜单又不开放给个人微信号,所以需要再次配置开启,但是不能修改
开发环境
- jdk 1.8、tomcat8、idea2018、Maven3
- Spring Boot 2.1.2.RELEASE
- mysql 5.6
- natapp 网络穿透
工程代码
itstack-ark-wx & 领域驱动设计方式设计
itstack-ark-wx
└── src├── main│ ├── java│ │ └── org.itstack.demo│ │ ├── application│ │ │ ├── UserLockAuthService.java│ │ │ ├── WxReceiveService.java │ │ │ └── WxValidateService.java │ │ ├── domain│ │ │ ├── lockauth│ │ │ │ ├── repository│ │ │ │ │ └── IUserAuthPatrolRepository.java │ │ │ │ └── service│ │ │ │ └── UserLockAuthServiceImpl.java │ │ │ ├── receive│ │ │ │ ├── model│ │ │ │ │ ├── BehaviorMatter.java│ │ │ │ │ └── MessageTextEntity.java│ │ │ │ ├── repository│ │ │ │ │ └── IUserAuthGrantRepository.java │ │ │ │ └── service│ │ │ │ ├── engine│ │ │ │ │ ├── impl │ │ │ │ │ │ └── MsgEngineHandle.java│ │ │ │ │ ├── Engine.java │ │ │ │ │ ├── EngineBase.java │ │ │ │ │ └── EngineConfig.java │ │ │ │ ├── logic│ │ │ │ │ ├── impl │ │ │ │ │ │ ├── AnswerFilter.java│ │ │ │ │ │ ├── SubscribeFilter.java│ │ │ │ │ │ ├── UnlockFilter.java│ │ │ │ │ │ └── UnsubscribeFilter.java │ │ │ │ │ └── LogicFilter.java │ │ │ │ └── WxReceiveServiceImpl.java │ │ │ └── validate│ │ │ └── service│ │ │ └── WxValidateServiceImpl.java │ │ ├── infrastructure│ │ │ ├── common│ │ │ │ └── Constants.java│ │ │ ├── dao │ │ │ │ └── UserAuthDao.java │ │ │ ├── po│ │ │ │ └── UserAuth.java │ │ │ ├── repository│ │ │ │ ├── UserAuthGrantRepository.java │ │ │ │ └── UserAuthPatrolRepository.java │ │ │ └── util│ │ │ ├── sdk│ │ │ │ └── SignatureUtil.java │ │ │ └── XmlUtil.java│ │ ├── interfaces│ │ │ ├── BlogController.java│ │ │ └── WxPortalController.java│ │ └── WxApplication.java│ └── resources │ ├── mybatis│ └── application.yml└── test└── java└── org.itstack.ark.wx.test└── ApiTest.java
itstack-ark-wx & 建表语句
CREATE TABLEuser_auth(id bigint NOT NULL AUTO_INCREMENT,openId VARCHAR(64),token VARCHAR(16) NOT NULL,uuid VARCHAR(128),createTime DATETIME,updateTime DATETIME,PRIMARY KEY (id, token),CONSTRAINT idx_uuid UNIQUE (uuid))ENGINE=InnoDB DEFAULT CHARSET=utf8
讲解部分重点代码块,完整代码下载关注公众号;bugstack虫洞栈 & 回复:itstack-ark-wx
interfaces接口层
WxPortalController.java & 接收微信公众号验签与行为信息通知
微信公众号都是通过服务提供方的一个接口的get/post请求来执行操作的{这样设计真sao但真香}
get接口主要是验证签名
post接口会收到;关注、取消关注、用户的回复信息
/*** 微信公众号:bugstack虫洞栈* 纯洁版博客:https://bugstack.cn* 沉淀、分享、成长,让自己和他人都能有所收获!* Create by 付政委 on @2019*/
@RestController
@RequestMapping("/wx/portal/{appid}")
public class WxPortalController {private Logger logger = LoggerFactory.getLogger(WxPortalController.class);@Autowiredprivate WxValidateService wxValidateService;@Autowiredprivate WxReceiveService wxReceiveService;/*** 处理微信服务器发来的get请求,进行签名的验证* <p>* appid 微信端AppID* signature 微信端发来的签名* timestamp 微信端发来的时间戳* nonce 微信端发来的随机字符串* echostr 微信端发来的验证字符串*/@GetMapping(produces = "text/plain;charset=utf-8")public String validate(@PathVariable String appid,@RequestParam(value = "signature", required = false) String signature,@RequestParam(value = "timestamp", required = false) String timestamp,@RequestParam(value = "nonce", required = false) String nonce,@RequestParam(value = "echostr", required = false) String echostr) {try {logger.info("微信公众号验签信息{}开始 [{}, {}, {}, {}]", appid, signature, timestamp, nonce, echostr);if (StringUtils.isAnyBlank(signature, timestamp, nonce, echostr)) {throw new IllegalArgumentException("请求参数非法,请核实!");}boolean check = wxValidateService.checkSign(signature, timestamp, nonce);logger.info("微信公众号验签信息{}完成 check:{}", appid, check);if (!check) return null;return echostr;} catch (Exception e) {logger.error("微信公众号验签信息{}失败 [{}, {}, {}, {}]", appid, signature, timestamp, nonce, echostr, e);return null;}}/*** 此处是处理微信服务器的消息转发的*/@PostMapping(produces = "application/xml; charset=UTF-8")public String post(@PathVariable String appid,@RequestBody String requestBody,@RequestParam("signature") String signature,@RequestParam("timestamp") String timestamp,@RequestParam("nonce") String nonce,@RequestParam("openid") String openid,@RequestParam(name = "encrypt_type", required = false) String encType,@RequestParam(name = "msg_signature", required = false) String msgSignature) {try {logger.info("接收微信公众号信息请求{}开始 {}", openid, requestBody);MessageTextEntity message = XmlUtil.xmlToBean(requestBody, MessageTextEntity.class);BehaviorMatter behaviorMatter = new BehaviorMatter();behaviorMatter.setOpenId(openid);behaviorMatter.setFromUserName(message.getFromUserName());behaviorMatter.setMsgType(message.getMsgType());behaviorMatter.setContent(message.getContent());behaviorMatter.setEvent(message.getEvent());behaviorMatter.setCreateTime(new Date(Long.parseLong(message.getCreateTime()) * 1000L));// 处理消息String result = wxReceiveService.doReceive(behaviorMatter);logger.info("接收微信公众号信息请求{}完成 {}", openid, result);return result;} catch (Exception e) {logger.error("接收微信公众号信息请求{}失败 {}", openid, requestBody, e);return "";}}}
BlogController.java & 博客验权接口,用于判断文章是否解锁
- 因为我们这个服务部署在二级域名下,因此需要设置跨域访问
- 由于我们网站是https加密,那么非https是不能被访问,需要下载安装证书到tomcat服务器
- 本接口只返回success和refuse,通过即可解锁
/*** 微信公众号:bugstack虫洞栈* 纯洁版博客:https://bugstack.cn* 沉淀、分享、成长,让自己和他人都能有所收获!* Create by 付政委 on @2019*/
@CrossOrigin("https://bugstack.cn")
@RestController
@RequestMapping("/api")
public class BlogController {private Logger logger = LoggerFactory.getLogger(BlogController.class);@Autowiredprivate UserLockAuthService userLockAuthService;@GetMapping(value = "check", produces = "application/json;charset=utf-8")public String check(@RequestParam String token) {try {logger.info("校验博客浏览用户授权状态{}开始", token);boolean status = userLockAuthService.checkAuth(token);logger.info("校验博客浏览用户授权状态{}完成", token, status);return status ? "success" : "refuse";} catch (Exception e) {logger.error("校验博客浏览用户授权状态{}失败", token, e);return "refuse";}}}
application应用层
- 本层主要定义逻辑分层,属于非常薄的一层,在一些复杂设计中会有一些服务编排
- UserLockAuthService 用户权限验证
- WxReceiveService 接收用户行为消息
- WxValidateService 微信公众号验签
UserLockAuthService.java & 定义后由领域层实现
public interface UserLockAuthService {boolean checkAuth(String token);}
domain领域层
- 领域层完成了;权限验证、行为消息处理、公众号验签
- 这里最重要的各种行为消息处理,我们设计为决策树工厂模型,定义了逻辑功能和引擎服务,后续只需要按需扩展即可
logic/LogicFilter.java & 定义逻辑模型,impl中有对应的一组的实现类
public interface LogicFilter {String filter(BehaviorMatter request);}
logic/impl/SubscribeFilter.java & 其中一个实现,关注时行为处理
@Service("subscribe")
public class SubscribeFilter implements LogicFilter {private final String content = "您好!\n" +"\n" +"非常感谢您关注,微信公众号:bugstack虫洞栈 | 也期待您分享给更多小伙伴!\n" +"\n" +"bugstack虫洞栈,专注于原创技术专题案例,以最易学习编程开发的方式分享技术知识,让萌新、小白、大牛都能有所收获。目前已经完成的专题有;《Netty4.x从入门到实战》、《手写RPC框架》、《用Java实现JVM》、《基于JavaAgent的全链路监控》、《DDD专题案例》,其他更多专题还在排兵布阵中。\n" +"\n" +"获取专题案例源码回复;netty案例、rpc案例、用Java实现jvm源码、基于JavaAgent的全链路监控案例、DDD落地。\n" +"\n" +"联系作者:付政委 | monkeycode";@Overridepublic String filter(BehaviorMatter request) {return content;}}
engine/impl/MsgEngineHandle.java & 引擎路由调用
@Service("msgEngineHandle")
public class MsgEngineHandle extends EngineBase {@Value("${wx.config.originalid:你的Err默认值}")private String originalId;@Overridepublic String process(BehaviorMatter request) throws Exception {LogicFilter router = super.router(request);if (null == router) return null;String resultStr = router.filter(request);if (StringUtils.isBlank(resultStr)) return "";//反馈信息[文本],暂时只有文本后续按需拓展MessageTextEntity res = new MessageTextEntity();res.setToUserName(request.getOpenId());res.setFromUserName(originalId);res.setCreateTime(String.valueOf(System.currentTimeMillis() / 1000L));res.setMsgType("text");res.setContent(resultStr);return XmlUtil.beanToXml(res);}}
infrastructure基础层
- 本层主要提供基础服务能力,包括数据库操作、缓存操作、工具包等
- 如果你的服务中由redis可以在本层来提供
- 在领域驱动设计中数据库不提倡共用,是单独分离,因为此设计中只有用户表还体现的不明显
repository/UserAuthGrantRepository.java & 数据库实现
@Repository("userAuthGrantRepository")
public class UserAuthGrantRepository implements IUserAuthGrantRepository {@Autowiredprivate UserAuthDao userAuthDao;@Overridepublic void grantAuth(String openId, String token) {UserAuth userAuthReq = new UserAuth();userAuthReq.setOpenId(openId);userAuthReq.setToken(token);userAuthReq.setUuid(openId + "_" + token);userAuthDao.insert(userAuthReq);}@Overridepublic void revokeAuth(String openId) {userAuthDao.delete(openId);}}
3.3 部署
1. 工程打包
springboot配置打包部署成可运行war包
pom.xml
<packaging>war</packaging>
WxApplication.java
@SpringBootApplication public class WxApplication extends SpringBootServletInitializer {@Overrideprotected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {return builder.sources(WxApplication.class);}public static void main(String[] args) {SpringApplication.run(WxApplication.class, args);}}
修改配置application.yml,数据库配置、微信配置
server:port: 80spring: # datasource: # username: root # password: 123456 # url: jdbc:mysql://localhost:3306/itstack-ark-wx?useUnicode=true&characterEncoding=utf8 # driver-class-name: com.mysql.jdbc.Drivermybatis:mapper-locations: classpath:/mybatis/mapper/*.xmlconfig-location: classpath:/mybatis/config/mybatis-config.xml# 微信公众号配置信息 # originalid:原始ID # appid:个人AppID # token:开通接口服务自定义设置 wx:config:originalid: xxxxxxxappid: xxxxxxxtoken: xxxxxxx
2. 服务上线
- 申请云服务器,目前提供方比较多,一般可以和域名选择一个服务商
- 准备好Linux服务器jdk1.8、tomcat8
- 看个人域名访问情况,酌情申请Https,配置RSA
- 本地安装XShell、Xftp,方便部署和文件传输
- 准备好个人微信公众号,用于配置服务
- 启动服务
[root@instance-39394m67 bin]# ./startup.sh. ____ _ __ _ _/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \\\/ ___)| |_)| | | | | || (_| | ) ) ) )' |____| .__|_| |_|_| |_\__, | / / / /=========|_|==============|___/=/_/_/_/:: Spring Boot :: (v2.1.2.RELEASE)2019-11-23 18:10:57.131 INFO 22052 --- [ost-startStop-1] org.itstack.ark.wx.WxApplication : Starting WxApplication v1.0.0-SNAPSHOT on instance-39394m67 with PID 22052 (/usr/local/java/apache-tomcat-8.5.37/webapps/itstack-ark-wx/WEB-INF/classes started by root in /usr/local/java/apache-tomcat-8.5.37/bin)2019-11-23 18:10:57.158 INFO 22052 --- [ost-startStop-1] org.itstack.ark.wx.WxApplication : No active profile set, falling back to default profiles: default2019-11-23 18:10:59.137 INFO 22052 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1890 ms2019-11-23 18:11:01.050 INFO 22052 --- [ost-startStop-1] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'2019-11-23 18:11:01.336 WARN 22052 --- [ost-startStop-1] ion$DefaultTemplateResolverConfiguration : Cannot find template location: classpath:/templates/ (please add some templates or check your Thymeleaf configuration)2019-11-23 18:11:01.677 INFO 22052 --- [ost-startStop-1] org.itstack.ark.wx.WxApplication : Started WxApplication in 5.873 seconds (JVM running for 10.311)23-Nov-2019 18:11:01.718 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployWAR Deployment of web application archive [/usr/local/java/apache-tomcat-8.5.37/webapps/itstack-ark-wx.war] has finished in [9,226] ms23-Nov-2019 18:11:01.720 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/usr/local/java/apache-tomcat-8.5.37/webapps/ROOT]23-Nov-2019 18:11:01.746 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/usr/local/java/apache-tomcat-8.5.37/webapps/ROOT] has finished in [26] ms23-Nov-2019 18:11:01.753 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["http-nio-80"]23-Nov-2019 18:11:01.764 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["ajp-nio-8009"]23-Nov-2019 18:11:01.766 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in 9326 ms2019-11-23 18:11:13.039 INFO 22052 --- [p-nio-80-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'2019-11-23 18:11:13.059 INFO 22052 --- [p-nio-80-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 20 ms2019-11-23 18:11:13.172 INFO 22052 --- [p-nio-80-exec-1] o.i.ark.wx.interfaces.BlogController : 校验博客浏览用户授权状态UDHIUS开始
3. 功能验证
验证连接;https://bugstack.cn/itstack-demo-ddd/2019/10/16/DDD%E4%B8%93%E9%A2%98%E6%A1%88%E4%BE%8B%E4%BA%8C-%E9%A2%86%E5%9F%9F%E5%B1%82%E5%86%B3%E7%AD%96%E8%A7%84%E5%88%99%E6%A0%91%E6%9C%8D%E5%8A%A1%E8%AE%BE%E8%AE%A1.html
引导提示;
文章解锁;
4. 综上总结
- 还是很感激那些前途的探路人,为原创研发贡献力量
- 我博客;https://bugstack.cn,可以尝试验证或者克隆对应的代码仓库
- 利用了11月24日周六给自己加个通宵班完成所有代码的开发和服务申请到部署,从最开始拿到想法到最终实现还是经历了很多坎坎坷坷
- 这里面还有很多可以优化的点以及一些拓展的功能,后续会陆续完善,如果由小伙伴有好的点子欢迎随时交流
- 源码贡献给所有研发,微信公众号内回复;itstack-ark-wx,即可获得一份基于领域驱动设计开发的微信公众号与博客打通工程(Star&Frok)
- 夜深人静咣咣敲代码我也会怀疑,我是谁,我在哪,我在干什么!哎,这就是;喜欢干一份工作,往往来自于干了一件喜欢的工作
并不想吹牛皮,但!为了把Github博客粉丝转移到公众号,我干了!相关推荐
- 牛掰!我是这么把博客粉丝转到公众号的
? "沉默王二" ,你值得星标的公众号 01.前言 纯洁的微笑推荐了一篇文章,题目没有任何特色,叫做<我是怎么把博客粉丝转到公众号的>,但读完后,我震惊了--原来还有这 ...
- 发布新的个人博客地址和微信公众号
Github地址 https://github.com/chenhaifeng2016 个人博客新地址 https://chenhaifeng2016.github.io/ 微信公众号
- 如何给CSDN博客添加个人微信公众号二维码或自定义栏目
公众号查看文章更清晰 在使用CSDN的过程中,可以看到有些博主的主页有个人微信二维码,微信公众号二维码等一些个人栏目信息.这对作者而言,可以让其他浏览博客的游客和作者进行更有效的沟通,也可以在这里对自 ...
- 博客导流到微信公众号
前言 你好呀,我是小邹. 最近在个人博客网站上加了个新功能,在大家第一次阅读本站文章的时候,会隐藏文章的部分内容,跳出个关注微信公众号的界面,通过简单发送一串数字来获取验证码,输入验证码后,网站就恢复 ...
- CSDN博客如何添加微信公众号二维码
有人问我,我的csdn上的二维码是怎么添加的,其实很简单. 步骤 1:点击管理博客 2:选择栏目管理 3:添加自定义栏目 4:栏目内容 <ul class="panel_head&qu ...
- 我的博客园主页、公众号
https://www.cnblogs.com/CodeWorkerLiMing/ 如今我看到csdn的链接和博客园的链接,优先点击博客园的,没办法,csdn的体验广告太多.. 近期开通了公众号,也一 ...
- GitHub 博客-- Jekyll--代码高亮,Liquid 转义字符
此文首发于我的个人博客:GitHub 博客-- Jekyll–代码高亮,Liquid 转义字符 - zhang0peter的个人博客 转载请注明 在使用Jekyll搭建了自己的 GitHub博客后,想 ...
- 实现在CentOS7环境下搭建个人github博客
实现在CentOS7环境下搭建个人github博客 主机要求:必须是centos环境版本可以不一样,假如你用的是其他版本的linux系统,只不过是命令不太一样. Tips:这里提前说下,对于Ubunt ...
- github博客Hexo引流到微信
相信有不少小伙伴都在github上创建了属于自己的博客,其中用Hexo的Next主题应该不少,那么,我们究竟该如何将博客的流量引流到微信呢?今天就来带你看一看. 如何引流 现在网上有一种套路,当你在看 ...
最新文章
- USB无法识别原因分析及解决方案
- c语言 得到结构体成员偏移
- 窗外传来嬉闹声,我默默关上窗
- RESET MASTER 和RESET SLAVE 命令的使用方法 注意事项
- Mac升级到Yosemite后默认的php版本不支持imagetfftext函数问题解决
- 台达服务器型号,台达网络服务器机柜 42u 600宽 1200 2000mm SR1160标准机柜
- 【多视图几何】TUM 课程 第6章 多视图重建
- 达内TTS6.0课件basic_day05
- Linux入门之磁盘管理(3)文件系统挂载
- 面试题目_数据分析之hive sql面试题目
- Apache和Tomcat的区别与联系
- NDNAV主题:简约大气昼夜wordpress免费导航主题
- python简易语音助手
- word如何去掉标题前面的黑点
- 【渝粤教育】电大中专跨境电子商务理论与实务 (17)作业 题库
- 4.2 长训练序列的生成
- python中copy()和deepcopy()详解
- html图片折叠,CSS 实现 图片鼠标悬停折叠效果
- Python全栈工程师(4:函数)
- python画名字七十周年快乐用英语怎么说_一周年快乐的英语怎么说?