[领域驱动设计实践]并不想吹牛皮,但!为了把Github博客粉丝转移到公众号,我干了!

微信公众号:bugstack虫洞栈 沉淀、分享、成长,专一于原创专题案例,以最易学习编程的方式分享知识,让本身和他人都能有所收获。目前已完成的专题有;Netty4.x实战专题案例、用Java实现JVM、基于JavaAgent的全链路监控、手写RPC框架、架构设计专题案例[Ing]等。css

1. 前言介绍

这个!截至到19年11月我已经工做6年了,从业Java但也折腾过C#、搞PHP也弄过中继器、IO板卡,彷佛我就是一个很喜欢在技术上折腾的人!与此同时,我也搞了6年的我的小网站,它们的呈现形式多种多样;有用PHP本身捣鼓的技术站用于分享资料、书籍、软件等、有用PHPWIND和DISCUZ的搭建的我的论坛、有用emlog和wordpress搭建的我的博客、也有借用于github+hexo/jekyll的能力组装出的技术博客。但无一例外它们都战死于征战的路上了,亡于;org域名不能备案、PHP服务器瘫痪被清空、http链接被注入恶意内容、定位不许确常常换模式、缺乏核心优质内容等等。但!老衲的心依然如春(cun),由于喜欢干一件事,每每来自于干一了件喜欢的事!html

因此!从19年开始我又继续写博客了,注册了新的域名bugstack.cn,备了案、买了服务器、还喊了新的口号;沉淀、分享、成长,让本身和他人都能有所收获!而且将尘封已久的微信公众号找回;结构上调整、内容上布局、粉丝上求关注。在这期间遇到了更大的牛;小灰、王二哥、纯洁的微笑还有松哥等一群伙伴,从他们那学到不少知识,真的很是感谢!前端

那么!此次的产品功能总结一句话就是;将基于Github+Jekyll搭建的静态博客与我并未开发过的微信公众号功能打通,经过在文章短口令码加锁引导用户到公众号内回复密码可解锁内容,以此来得到粉丝关注,固然若是取消关注了则文章继续锁定。java

在多说一句,我理解的产品;实际上是使用研发技术力搭建出能够用于承载接收用户在各类设备上所生产的行为数据的一种产品化服务。因此有些产品在作减法,同时也有为丰富的功能作加法,但究其一点咱们其实都是在为接收有价值数据服务的。兴衰存亡,皆在核心数据沉淀与运做上!mysql

2. 流程设计

为了使博客粉丝主动关注微信公众号,咱们在用户初次浏览文章时增长权限验证,给每个用户都生成一个惟一码引导用户在公众号内回复解锁文章,以此来与微信openid对应。当用户取消关注时则进行删除openid或标记状态,使得用户没法继续浏览文章。其实为了更好的体验,我参照了大牛的方式内容60%的区域可见,其他内容渐进遮挡,蒙胧胧的感受还挺美。总体流程图以下; git

微信公众号:bugstack虫洞栈 & 流程图设计

3. 功能实现

为了实现本产品功能,我准备了;github

  1. 微信公众号;bugstack虫洞栈
  2. 博客;本文使用的是github+jekyll;地址:bugstack.cn
  3. 域名;域名申请比较简单,但可能针对不一样的服务商还必须备案后才可使用
  4. 友盟;主要为了获取全局的惟一id值,也可使用其余具有此功能的产品或者本身实现
  5. 部署环境;Java云服务器、Mysql云数据库、https证书、Jdk1.八、tomcat1.8,云市场比较多按需购买,若是是本地调试可使用https://natapp.cn,作免费网络穿透映射

3.1 前端

前端主要负责针对发布时设置了look: need的文章,在用户浏览文章检查是否有权限查看所有内容,当用户没有权限时隐藏文章60%内容,并经过页面结尾提醒用户在公众号内回复口令解锁文章。web

  1. 找到加锁文章容器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>
复制代码
  1. 使用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');
复制代码
  1. 给文章加一点朦胧感,渐变隐藏
.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));
}
复制代码
  1. 从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();
}
复制代码
  1. 定时轮旋检查是否关注公众号并解锁,能够优化写入全局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);
复制代码
  1. 引导用户关注公众号 如今将token值回显到页面,提醒用户关注公众号回复口令解锁所有文章,以此来获得粉丝的关注。效果以下;

微信公众号:bugstack虫洞栈 & 文章加锁效果

3.2 后端

  1. 本地环境开发须要安装(免费)natapp.cn/,作网络穿透便于咱们进…
  2. 服务度三个主要接口;1同名称的get、post请求,分别用于公众号验验、接收用户行为与回复信息。2给博客提供验证是否解锁接口
  3. 在微信公众号;开发->基础设置(服务器配置),修改配置内容并开启服务,此时服务端会收到验证请求信息
  4. Java服务度采用领域驱动设计微服务方式开发,DDD的开发模式会更加清晰以及易于后续功能的拓展
  5. (提醒)我的微信号,部分接口权限是没有的,同时若是开启开发者功能配置后,自定义菜单和自定义回复都会失效。而自定义菜单又不开放给我的微信号,因此须要再次配置开启,可是不能修改

开发环境

  1. jdk 1.八、tomcat八、idea201八、Maven3
  2. Spring Boot 2.1.2.RELEASE
  3. mysql 5.6
  4. natapp 网络穿透

工程代码

itstack-ark-wx & 领域驱动设计方式设计ajax

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 & 建表语句redis

CREATE TABLE
    user_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);

    @Autowired
    private WxValidateService wxValidateService;
    @Autowired
    private 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);

    @Autowired
    private 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";

    @Override
    public 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;

    @Override
    public 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 {

    @Autowired
    private UserAuthDao userAuthDao;

    @Override
    public void grantAuth(String openId, String token) {
        UserAuth userAuthReq = new UserAuth();
        userAuthReq.setOpenId(openId);
        userAuthReq.setToken(token);
        userAuthReq.setUuid(openId + "_" + token);
        userAuthDao.insert(userAuthReq);
    }

    @Override
    public void revokeAuth(String openId) {
        userAuthDao.delete(openId);
    }

}
复制代码

3.3 部署

1. 工程打包

  1. springboot配置打包部署成可运行war包

    pom.xml

    <packaging>war</packaging>
    复制代码

    WxApplication.java

    @SpringBootApplication
    public class WxApplication extends SpringBootServletInitializer {
    
    	@Override
    	protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
    		return builder.sources(WxApplication.class);
    	}
    
    	public static void main(String[] args) {
    		SpringApplication.run(WxApplication.class, args);
    	}
    
    }
    复制代码
  2. 修改配置application.yml,数据库配置、微信配置

    server:
      port: 80
    
    spring:
    #  datasource:
    #    username: root
    #    password: 123456
    #    url: jdbc:mysql://localhost:3306/itstack-ark-wx?useUnicode=true&characterEncoding=utf8
    #    driver-class-name: com.mysql.jdbc.Driver
    
    
    mybatis:
      mapper-locations: classpath:/mybatis/mapper/*.xml
      config-location:  classpath:/mybatis/config/mybatis-config.xml
    
    # 微信公众号配置信息
    # originalid:原始ID
    # appid:我的AppID
    # token:开通接口服务自定义设置
    wx:
      config:
    	originalid: xxxxxxx
    	appid: xxxxxxx
    	token: xxxxxxx
    复制代码

2. 服务上线

  1. 申请云服务器,目前提供方比较多,通常能够和域名选择一个服务商
  2. 准备好Linux服务器jdk1.八、tomcat8
  3. 看我的域名访问状况,酌情申请Https,配置RSA
  4. 本地安装XShell、Xftp,方便部署和文件传输
  5. 准备好我的微信公众号,用于配置服务
  6. 启动服务
    [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: default
     2019-11-23 18:10:59.137  INFO 22052 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1890 ms
     2019-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] ms
     23-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] ms
     23-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 ms
     2019-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 ms
     2019-11-23 18:11:13.172  INFO 22052 --- [p-nio-80-exec-1] o.i.ark.wx.interfaces.BlogController     : 校验博客浏览用户受权状态UDHIUS开始
    复制代码

3. 功能验证

  1. 验证链接;bugstack.cn/itstack-dem…

  2. 引导提示;

  3. 文章解锁;

4. 综上总结

  1. 仍是很感激那些前途的探路人,为原创研发贡献力量
  2. 我博客;bugstack.cn,能够尝试验证或者克隆对应的代码仓库
  3. 利用了11月24日周六给本身加个通宵班完成全部代码的开发和服务申请到部署,从最开始拿到想法到最终实现仍是经历了不少坎坎坷坷
  4. 这里面还有不少能够优化的点以及一些拓展的功能,后续会陆续完善,若是由小伙伴有好的点子欢迎随时交流
  5. 源码贡献给全部研发,微信公众号内回复;itstack-ark-wx,便可得到一份基于领域驱动设计开发的微信公众号与博客打通工程(Star&Frok)
  6. 夜深人静咣咣敲代码我也会怀疑,我是谁,我在哪,我在干什么!哎,这就是;喜欢干一份工做,每每来自于干了一件喜欢的工做

微信公众号:bugstack虫洞栈,欢迎关注&获取源码
相关文章
相关标签/搜索