微信公眾號: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 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 & 接收微信公眾號驗簽與行為信息通知
@RestController
@RequestMapping ( "/wx/portal/{appid}" )
public class WxPortalController { private Logger logger
= LoggerFactory
. getLogger ( WxPortalController
. class ) ; @Autowired private WxValidateService wxValidateService
; @Autowired private WxReceiveService wxReceiveService
; @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 ( ) ) * 1000 L
) ) ; 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,通過即可解鎖
@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 ( ) / 1000 L
) ) ; 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. 工程打包
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) ; } }
修改配置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. Drivermybatis: mapper- locations: classpath: / mybatis/ mapper
2. 服務上線
申請云服務器,目前提供方比較多,一般可以和域名選擇一個服務商 準備好Linux服務器jdk1.8、tomcat8 看個人域名訪問情況,酌情申請Https,配置RSA 本地安裝XShell、Xftp,方便部署和文件傳輸 準備好個人微信公眾號,用于配置服務 啟動服務 [ root@instance - 39394 m67 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- 39394 m67 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 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博客粉丝转移到公众号,我干了! 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。