Github address: github.com/pc859107393…
Real-time synchronous is the address of the domestic project code cloud: git.oschina.net/859107393/m…
I Jane books home page is: www.jianshu.com/users/86b79…
Last issue was: [Hand-in-hand tutorial][season 2] Java backend blogging system article system – No10
tool
- An IDE for idea2017.1.5
- The JDK environment is 1.8
- Gradle build, version: 2.14.1
- The Mysql version is 5.5.27
- Tomcat version 7.0.52
- Flowchart Drawing (XMIND)
- Modeling analysis software PowerDesigner16.5
- Database tool MySQLWorkBench, version 6.3.7build
The current target
Complete wechat public account access
Resources to introduce
Since we want to develop wechat-related functions, we need wechat-related resources. The first step is to open the official wechat developer documentation. Then we should build the code related to wechat. ?
In fact, this is not the case. We can find some tools related to wechat in the Java project of open Source China. In this paper, I used Fastweixin to develop it quickly.
compile 'com. Making. Sd4324530: fastweixin: 1.3.15'Copy the code
Refer to fastweixin instructions for development
Controller to realize wechat communication
Why implement this?
- Configure wechat related Settings
- Connect to the wechat server according to the generated Settings
- Interact with wechat server and bind wechat account
- Get the token of the data exchanged with wechat
So, we have a lot of things to do, but at this moment the Fastweixin we use has done a big step, we write wechat Controller according to his instructions.
@RestController
@RequestMapping("/weixin")
public class WeixinController extends WeixinControllerSupport {
private static final Logger log = LoggerFactory.getLogger(WeixinController.class);
private static final String TOKEN = "weixin"; // The default Token is weixin
@Autowired
private WeichatServiceImpl weichatService;
@Autowired
private PostService postService;
@Override
public void bindServer(HttpServletRequest request, HttpServletResponse response) {
String signature = request.getParameter("signature");
String timestamp = request.getParameter("timestamp");
String nonce = request.getParameter("nonce");
LogPrintUtil.getInstance(WeixinController.class).logOutLittle("bindWeiXin:\fsignature = "
+ signature + "\ntimestamp"
+ timestamp + "\nnonce" + nonce);
super.bindServer(request, response);
}
// Set TOKEN to bind wechat server
@Override
protected String getToken(a) {
return weichatService.getWeiConfig().getToken();
}
// Set to APPID when using safe mode
// It is no longer mandatory to override this method
@Override
protected String getAppId(a) {
return weichatService.getWeiConfig().getAppid();
}
// Set when using secure mode: key
// It is no longer mandatory to override this method
@Override
protected String getAESKey(a) {
return null;
}
// Override the parent class method to process the corresponding wechat message
@Override
protected BaseMsg handleTextMsg(TextReqMsg msg) {
String content = msg.getContent();
LogPrintUtil.getInstance(WeixinController.class).logOutLittle(String.format("What the user sends to the server :{%s}", content));
List<Article> articles = new ArrayList<>();
List<PostCustom> byKeyword = null;
try {
byKeyword = postService.findByKeyword(content, null.null);
if (null! = byKeyword && byKeyword.size() >0) {
int count = 0;
for (PostCustom postCustom : byKeyword) {
if (count >= 5) break;
Article article = new Article();
article.setTitle(postCustom.getPostTitle());
article.setDescription(HtmlUtil.getTextFromHtml(postCustom.getPostContent()));
article.setUrl("http://acheng1314.cn/front/post/" + postCustom.getId());
articles.add(article);
count++;
}
return newNewsMsg(articles); }}catch (NotFoundException e) {
e.printStackTrace();
}
return new TextMsg("Not found yet!");
}
/* New in version 1.1, rewrite the parent class method, add custom wechat message processor * is not necessary, the above method is unified processing all text messages, if the business is complex, the above will appear messy * this mechanism is to deal with this situation, each MessageHandle is a business, Only the specified part of the message */ is processed
@Override
protected List<MessageHandle> initMessageHandles(a) {
List<MessageHandle> handles = new ArrayList<MessageHandle>();
// handles.add(new MyMessageHandle());
return handles;
}
// Version 1.1 added, rewrite the parent class method, add custom wechat event handler, ditto above
@Override
protected List<EventHandle> initEventHandles(a) {
List<EventHandle> handles = new ArrayList<EventHandle>();
// handles.add(new MyEventHandle());
return handles;
}
/** * handle image messages, subclass overwrite ** if necessary@paramMSG Request message object *@returnResponse message object */
@Override
protected BaseMsg handleImageMsg(ImageReqMsg msg) {
return super.handleImageMsg(msg);
}
/** * handles voice messages, subclasses override ** if necessary@paramMSG Request message object *@returnResponse message object */
@Override
protected BaseMsg handleVoiceMsg(VoiceReqMsg msg) {
return super.handleVoiceMsg(msg);
}
/** * handles video messages, subclasses override ** if necessary@paramMSG Request message object *@returnResponse message object */
@Override
protected BaseMsg handleVideoMsg(VideoReqMsg msg) {
return super.handleVideoMsg(msg);
}
/** * handles small video messages, subclasses override ** if necessary@paramMSG Request message object *@returnResponse message object */
@Override
protected BaseMsg hadnleShortVideoMsg(VideoReqMsg msg) {
return super.hadnleShortVideoMsg(msg);
}
/** * handles geolocation messages, subclasses override ** if necessary@paramMSG Request message object *@returnResponse message object */
@Override
protected BaseMsg handleLocationMsg(LocationReqMsg msg) {
return super.handleLocationMsg(msg);
}
/** * handles link messages, subclasses override ** if necessary@paramMSG Request message object *@returnResponse message object */
@Override
protected BaseMsg handleLinkMsg(LinkReqMsg msg) {
return super.handleLinkMsg(msg);
}
/** * handle qr code scanning events, subclass overwrite ** if necessary@paramEvent Indicates the event object (*) that scans a QR code@returnResponse message object */
@Override
protected BaseMsg handleQrCodeEvent(QrCodeEvent event) {
return super.handleQrCodeEvent(event);
}
/** * handles location events, subclasses override ** if necessary@paramEvent Location event object *@returnResponse message object */
@Override
protected BaseMsg handleLocationEvent(LocationEvent event) {
return super.handleLocationEvent(event);
}
/** * handles menu click events, subclasses override ** if necessary@paramFrom the Event menu, click the event object *@returnResponse message object */
@Override
protected BaseMsg handleMenuClickEvent(MenuEvent event) {
LogPrintUtil.getInstance(this.getClass()).logOutLittle("Click" + event.toString());
MyWeChatMenu myWeChatMenu = weichatService.findOneById(StringUtils.toInt(event.getEventKey()));
try {
List<Article> articles = new ArrayList<>();
List<PostCustom> keyword = postService.findByKeyword(myWeChatMenu.getKeyword(), null.null);
if (null! = keyword && keyword.size() >0) {
int i = 0;
for (PostCustom postCustom : keyword) {
if (i >= 5) break;
Article article = new Article();
article.setTitle(postCustom.getPostTitle());
article.setDescription(HtmlUtil.getTextFromHtml(postCustom.getPostContent()));
article.setUrl("http://acheng1314.cn/front/post/" + postCustom.getId());
articles.add(article);
i++;
}
return newNewsMsg(articles); }}catch (NotFoundException e) {
e.printStackTrace();
}
return new TextMsg("Not found yet!");
}
/** * handles menu jump events, subclasses override ** if necessary@paramThe event menu jumps to the event object *@returnResponse message object */
@Override
protected BaseMsg handleMenuViewEvent(MenuEvent event) {
LogPrintUtil.getInstance(this.getClass()).logOutLittle("Click to jump" + event.toString());
return super.handleMenuViewEvent(event);
}
/** * handles menu scan push events, subclass overrides ** if necessary@paramThe Event menu scans the push event object *@returnThe response message object */
@Override
protected BaseMsg handleScanCodeEvent(ScanCodeEvent event) {
return super.handleScanCodeEvent(event);
}
/** * handle menu pop up album event, subclass override ** if necessary@paramThe event menu brings up the album event *@returnThe response message object */
@Override
protected BaseMsg handlePSendPicsInfoEvent(SendPicsInfoEvent event) {
return super.handlePSendPicsInfoEvent(event);
}
/** * handle template message sending events, subclasses override ** if necessary@paramThe event menu brings up the album event *@returnThe response message object */
@Override
protected BaseMsg handleTemplateMsgEvent(TemplateMsgEvent event) {
return super.handleTemplateMsgEvent(event);
}
/** * handles add attention events, subclasses override ** if necessary@paramEvent Adds the focus event object *@returnResponse message object */
@Override
protected BaseMsg handleSubscribe(BaseEvent event) {
return super.handleSubscribe(event);
}
/** * The callback method for receiving group messages **@paramEvent Group callback method *@returnResponse message object */
@Override
protected BaseMsg callBackAllMessage(SendMessageEvent event) {
return super.callBackAllMessage(event);
}
/** * handles unfocused events, subclasses override ** if necessary@paramEvent Unconcerns the event object *@returnResponse message object */
@Override
protected BaseMsg handleUnsubscribe(BaseEvent event) {
return super.handleUnsubscribe(event); }}Copy the code
As you can see, many of the above methods have Javadoc installed, but for now we’ll focus on the following three methods:
// Set TOKEN to bind wechat server
@Override
protected String getToken(a) {
return weichatService.getWeiConfig().getToken();
}
// Set to APPID when using safe mode
// It is no longer mandatory to override this method
@Override
protected String getAppId(a) {
return weichatService.getWeiConfig().getAppid();
}
// Set when using secure mode: key
// It is no longer mandatory to override this method
@Override
protected String getAESKey(a) {
return null;
}Copy the code
At the same time, there are corresponding Settings on the wechat developer Settings page to control, and the test account is as follows:
According to the figure above, we can directly obtain appId and APPSecret. Of course, Token needs to be set by ourselves, but URL is the address that we can accept the message sent by wechat server. To test whether the server can be bound, we can write the appId and Token to the method above. After these two Settings are completed, we can successfully bind the wechat public account to our server.
According to the Controller above, the URL can be set, that is, the domain name of our server +/weixin.
Of course, that’s not the point! But in accordance with our development habits, some Settings related to wechat can be persisted to the server that is the best. So let’s write it to the database. I actually wrote to properties in the beginning, but due to the nature of properties, the data is not refreshed. I’ll just store it in the database.
/* Create database table cc_site_option to store basic site information */
SET NAMES utf8;
-- ----------------------------
-- Table structure for `cc_site_option`
-- ----------------------------
DROP TABLE IF EXISTS `cc_site_option`;
CREATE TABLE `cc_site_option` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'primary key ID'.`option_key` varchar(128) DEFAULT NULL COMMENT 'configuration KEY'.`option_value` text COMMENT 'Configuration content',
PRIMARY KEY (`id`))ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8 COMMENT='Configuration information table, used to save all the configuration information of the site. ';Copy the code
In fact, if you are careful in the above table, you can see that I use a storage structure similar to Map. That is to say, our data is in the form of key-value pairs in general terms, so when reading data, we store it with List>. A brief Dao looks like this:
@Repository("siteConfigDao")
public interface SiteConfigDao extends Dao {
@Deprecated
@Override
public int add(Serializable serializable);
@Deprecated
@Override
public int del(Serializable serializable);
@Deprecated
@Override
public int update(Serializable serializable);
@Deprecated
@Override
public Serializable findOneById(Serializable Id);
@Override
List<HashMap<String, String>> findAll();
Serializable findOneByKey(@Param("mKey") Serializable key);
void updateOneByKey(@Param("mKey") Serializable key, @Param("mValue") Serializable value);
// @Insert("INSERT INTO `cc_site_option` (`option_key`,`option_value`) VALUES (#{mKey},#{mValue});" )
void insertOne(@Param("mKey") Serializable key, @Param("mValue") Serializable value);
}Copy the code
The only detail is getting the desired data from the corresponding Service. Meanwhile, our wechat menu also needs to be stored as follows:
CREATE TABLE `cc_wechat_menu` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` text NOT NULL COMMENT 'Name of wechat Menu'.`parent_id` int(11) DEFAULT '0' COMMENT 'Parent menu ID, outermost menu parent_id is 0'.`type` varchar(255) DEFAULT NULL COMMENT 'Wechat menu type, deleted means deleted, Click = click push event,view= jump URL,scancode_push= sweep code push event,scancode_waitmsg= sweep code push event and "message receiving" prompt box pop up,pic_sysphoto= pop up system take photos and send pictures,pic_ Photo_or_album = pop up photo or album post,pic_weixin= pop up wechat photo album post,location_select= pop up location selector,'.`keyword` text COMMENT 'Fill in the keyword will trigger the "automatic reply" matching content, please access the web page URL address. '.`position` int(11) DEFAULT '0' COMMENT The sorted number determines where the menu is located. ',
PRIMARY KEY (`id`),
UNIQUE KEY `cc_wechat_menu_id_uindex` (`id`))ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COMMENT='wechat Menu List';Copy the code
Of course, what we need is the Dao of wechat (this time, annotations are inserted into SQL in Dao, which can be lazy to create mapper files). .
@Repository("weChatDao")
public interface WeChatDao extends Dao<MyWeChatMenu> {
@Override
int add(MyWeChatMenu weChatMenu);
@Update("UPDATE `cc_wechat_menu` SET type='deleted' WHERE id=#{id}")
@Override
int del(MyWeChatMenu weChatMenu);
@Update("UPDATE `cc_wechat_menu` SET name=#{name},parent_id=#{parentId},type=#{type},keyword=#{keyword},position=#{position} WHERE id=#{id}")
@Override
int update(MyWeChatMenu weChatMenu);
@Select("SELECT * FROM `cc_wechat_menu` WHERE id=#{id}")
@Override
MyWeChatMenu findOneById(Serializable Id);
@Select("SELECT * FROM `cc_wechat_menu` WHERE type! ='deleted'")
@Override
List<MyWeChatMenu> findAll(a);
@Select("SELECT * FROM `cc_wechat_menu` WHERE type! ='deleted' AND parent_id=0")
List<MyWeChatMenu> getParentWeiMenu(a);
}Copy the code
In a nutshell, the above annotation inserts SQL statements to be executed like this. Note the use of these SQL statements. The rest is wechat Service, as follows:
@Service("weichatService")
public class WeichatServiceImpl {
@Autowired
private SiteConfigDao siteConfigDao;
@Autowired
private WeChatDao weChatDao;
public static String updateMenuUrl = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=";
/** ** Sync wechat menu to wechat official account **@return* /
public String synWeichatMenu(a) {
try {
WeiChatMenuBean menuBean = creatWeMenuList();
if (null == menuBean) return GsonUtils.toJsonObjStr(null, ResponseCode.FAILED, "Menu contents cannot be empty!");
String menuJson = GsonUtils.toJson(menuBean);
LogPrintUtil.getInstance(this.getClass()).logOutLittle(menuJson);
WeiChatResPM pm = null; // Wechat response response
String responseStr = HttpClientUtil.doJsonPost(String.format("%s%s", updateMenuUrl, getAccessToken()), menuJson);
LogPrintUtil.getInstance(this.getClass()).logOutLittle(responseStr);
pm = GsonUtils.fromJson(responseStr, WeiChatResPM.class);
if (pm.getErrcode() == 0) return GsonUtils.toJsonObjStr(null, ResponseCode.OK, "Wechat menu synchronization succeeded!");
else throw new Exception(pm.getErrmsg());
} catch (Exception e) {
e.printStackTrace();
return GsonUtils.toJsonObjStr(null, ResponseCode.FAILED, "Synchronization failed! Reason:"+ e.getMessage()); }}/** * get AccessToken */
public String getAccessToken(a) throws Exception {
MyWeiConfig weiConfig = getWeiConfig();
return WeiChatUtils.getSingleton(weiConfig.getAppid(), weiConfig.getAppsecret()).getWeAccessToken();
}
/** * Locally assemble wechat menu data and generate menu objects <br/> * the number of wechat outer menus must be less than or equal to 3, and the corresponding internal menus cannot exceed 5 *@return* /
private WeiChatMenuBean creatWeMenuList(a) throws Exception {··· Specific code omitted ···}/** * get wechat Settings, wrapped wechat appID, secret and token **@return* /
public MyWeiConfig getWeiConfig(a) {
String weiChatAppid = "", weichatAppsecret = "", token = "";
MyWeiConfig apiConfig;
try {
List<HashMap<String, String>> siteInfo = getAllSiteInfo();
LogPrintUtil.getInstance(this.getClass()).logOutLittle(siteInfo.toString());
for (HashMap<String, String> map : siteInfo) {
Set<Map.Entry<String, String>> sets = map.entrySet(); // Get the HashMap key-value pairs
for (Map.Entry<String, String> set : sets) { // Iterate over the HashMap key-value pairs
String mKey = set.getValue();
if (mKey.contains(MySiteMap.WECHAT_APPID)) {
weiChatAppid = map.get("option_value");
} else if (mKey.contains(MySiteMap.WECHAT_APPSECRET))
weichatAppsecret = map.get("option_value");
else if (mKey.contains(MySiteMap.WECHAT_TOKEN))
token = map.get("option_value");
}
}
apiConfig = new MyWeiConfig(weiChatAppid, weichatAppsecret, token);
return apiConfig;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public String saveOrUpdateMenu(MyWeChatMenu weChatMenu) {
if (null == weChatMenu || StringUtils.isEmpty(weChatMenu.getName()
, weChatMenu.getType()
, weChatMenu.getParentId() + ""))
return GsonUtils.toJsonObjStr(null, ResponseCode.FAILED, "Wechat menu information can not be empty!");
try {
if (weChatMenu.getId() == null || weChatMenu.getId() < 1) {
weChatDao.add(weChatMenu);
return GsonUtils.toJsonObjStr(weChatMenu, ResponseCode.OK, "Saved wechat menu information successfully!");
} else if (null! = weChatMenu.getId() && weChatMenu.getId() >0) {
weChatDao.update(weChatMenu);
return GsonUtils.toJsonObjStr(weChatMenu, ResponseCode.OK, "Update wechat menu information successfully!"); }}catch (Exception e) {
e.printStackTrace();
}
return GsonUtils.toJsonObjStr(null, ResponseCode.FAILED, "Failed to save or update wechat menu");
}
public List<HashMap<String, String>> getAllSiteInfo() {
List<HashMap<String, String>> allSiteInfo = siteConfigDao.findAll();
if (null! = allSiteInfo && ! allSiteInfo.isEmpty())return allSiteInfo;
return null; }}Copy the code
In the above code, some methods I directly return the JSON statement, while obtaining the wechat setting code can be briefly looked at, or very simple. But we can see the code for AccessToken, which I would say is fairly simple, but is that really the case? Take a look at the code for WeiChatUtils.
/** * singleton, get wechat AccessToken */
public class WeiChatUtils {
private static volatile WeiChatUtils singleton = null;
private static ApiConfig apiConfig;
private WeiChatUtils(a) {}public static WeiChatUtils getSingleton(String appId, String appSecret) {
if (singleton == null) {
synchronized (WeiChatUtils.class) {
if (singleton == null) {
singleton = new WeiChatUtils();
apiConfig = newApiConfig(appId, appSecret); }}}return singleton;
}
public String getWeAccessToken(a) {
returnapiConfig.getAccessToken(); }}Copy the code
At this point, we can see that the singleton is required to ensure the uniqueness of the AccessToken used when the above data is synchronized to the wechat server. As for why to use this guarantee unique, can look at the source of ApiConfig, here is not repeated.
Of course, this article is almost over. In fact, wechat-related access is relatively simple. After all, Fastweixin has helped us integrate most of the functional stuff. All we need to worry about is the composition of the business and data assembly, which is what programmers are all about.
At this point, this season’s articles basically come to an end.
These two days I put the server on IPv6 and HTTPS at home, of course, inevitably stepped on a lot of pits, these are the next story.
Next season forecast
In the next season, we will use the new Spring-boot as our development scaffolding, of course, the front page scaffolding is still looking for. Meanwhile, the next season will focus more on quick development techniques. Of course, in the next quarter of development, we will use OKHTTP as our new backend web request framework.
Next season, we will be replanning both the front and back ends to ensure high cohesion and low coupling of our project, as well as exploring microservices.
A brief summary
By the end of these two seasons, I’m sure you’ll be able to make simple websites, after all we already have:
- Web Front-end Tips
- The use of ajax
- Js
- Js dom operations on HTML
- Introduction and use of front-end frameworks
- JSTL loads web page data
- Back-end development tips
- Program business Process Analysis (Flow chart)
- Back-end development process implementation (Three-tier development)
- Complex SQL writing
- Use of common annotations (three-layer annotations, cache annotations, SQL annotations)
- ApiDocs document integration (spring – fox | swagger)
- Build the Spring framework (Spring +springMvc+ Mybatis +Druid, resource scanning allocation)
- Transaction handling (exceptions and rollback)
- File upload Processing
- Ueditor access
- Access to level 2 Cache (Ehcache)
- User Rights Authentication (Shiro)
- Back-end wechat development (using fastweixin framework)
- Use of HttpClient and easy encapsulation (support SSL links)
- Gson fast serialization
- Encryption strategies
- RestFul style API writing
- Server tips
- Setting up the Linux Environment
- Linux Software Configuration
- Common Linux Commands
- The MAC and WIN systems are connected to the Linux server
- Build gradle projects quickly
Of course, these are not all enumerated. There are a lot of common but less obvious techniques, after all, there are things that become a habit that you can’t remember for a while. That’s where we’re going to be, when we’re developing, we’re going to be ready.
If you recognize what I have done and think it is of some help to you, I hope you can also give me a cup of coffee, thank you.