Forráskód Böngészése

添加用户标签管理

Administrator 7 éve
szülő
commit
bad4aba1d8

+ 11 - 0
doc/wxmp.sql

@@ -293,6 +293,17 @@ CREATE TABLE `wxcms_tpl_msg_text` (
   PRIMARY KEY (`id`)
 ) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8;
 
+-- ----------------------------
+-- Table structure for wxcms_user_tag
+-- ----------------------------
+DROP TABLE IF EXISTS `wxcms_user_tag`;
+CREATE TABLE `wxcms_user_tag` (
+  `id` int(11) NOT NULL COMMENT '主键',
+  `name` varchar(10) DEFAULT NULL COMMENT '标签名称',
+  `count` int(11) DEFAULT '0' COMMENT '该标签的粉丝数量',
+  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建日期',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 /*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
 /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
 /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;

+ 50 - 14
src/main/java/com/wxmp/core/page/PageInterceptor.java

@@ -18,12 +18,31 @@
  */
 package com.wxmp.core.page;
 
-import lombok.extern.slf4j.Slf4j;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.xml.bind.PropertyException;
+
 import org.apache.ibatis.executor.parameter.ParameterHandler;
+import org.apache.ibatis.executor.statement.RoutingStatementHandler;
 import org.apache.ibatis.executor.statement.StatementHandler;
 import org.apache.ibatis.mapping.BoundSql;
 import org.apache.ibatis.mapping.MappedStatement;
-import org.apache.ibatis.plugin.*;
+import org.apache.ibatis.plugin.Interceptor;
+import org.apache.ibatis.plugin.Intercepts;
+import org.apache.ibatis.plugin.Invocation;
+import org.apache.ibatis.plugin.Plugin;
+import org.apache.ibatis.plugin.Signature;
 import org.apache.ibatis.reflection.MetaObject;
 import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
 import org.apache.ibatis.reflection.factory.ObjectFactory;
@@ -31,15 +50,10 @@ import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
 import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
 import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
 import org.apache.ibatis.session.RowBounds;
+import org.apache.ibatis.session.defaults.DefaultSqlSession;
+import org.apache.ibatis.session.defaults.DefaultSqlSession.StrictMap;
 
-import javax.xml.bind.PropertyException;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.Properties;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
+import lombok.extern.slf4j.Slf4j;
 
 /**
  * 通过拦截StatementHandler的prepare方法,重写sql语句实现物理分页
@@ -121,10 +135,32 @@ public class PageInterceptor implements Interceptor {
      * @param obj
      * @return
      */
-    private Page getParameterPage(MetaObject obj) {
-        Page page = new Page();
-        page.setPage(PageUtil.obj2Int(obj.getValue("delegate.boundSql.parameterObject.page")));
-        page.setPageSize(PageUtil.obj2Int(obj.getValue("delegate.boundSql.parameterObject.pageSize")));
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+	private Page getParameterPage(MetaObject obj) {
+    	Page page = new Page();
+        //解决异常:  实体没有page属性:,在进行分页查询条件是List<String>集合时
+        RoutingStatementHandler originalObj =  (RoutingStatementHandler) obj.getOriginalObject();
+        BoundSql boundSql = originalObj.getBoundSql();
+        if(boundSql.getParameterObject().getClass() == DefaultSqlSession.StrictMap.class) {
+        	DefaultSqlSession.StrictMap parameterObject = (StrictMap<?>) boundSql.getParameterObject();
+            Set<Map.Entry> entrySet = parameterObject.entrySet();
+            Iterator<Entry> iterator = entrySet.iterator();
+            Page p = null;
+            while(iterator.hasNext()) {
+            	Entry entry = iterator.next();
+            	if("list".equals(entry.getKey().toString())) {
+            		List objList =(List) entry.getValue();
+            		p = (Page) objList.get(0);
+            		break;
+            	}
+            }
+            page.setPage(p.getPage());
+            page.setPageSize(p.getPageSize());
+        }else {
+        	page.setPage(PageUtil.obj2Int(obj.getValue("delegate.boundSql.parameterObject.page")));
+            page.setPageSize(PageUtil.obj2Int(obj.getValue("delegate.boundSql.parameterObject.pageSize")));
+        }
+        
         // page.setTotal(PageUtil.obj2Int(obj.getValue("delegate.boundSql.parameterObject.total")));
         // page.setTotalPage(PageUtil.obj2Int(obj.getValue("delegate.boundSql.parameterObject.totalPage")));
         // page.setPaging(obj.getValue("delegate.boundSql.parameterObject.paging").toString().equals("true") ? true : false);

+ 33 - 1
src/main/java/com/wxmp/wxapi/process/WxApi.java

@@ -137,6 +137,19 @@ public class WxApi {
     // 修改永久图文url
     public static final String UPDATE_NEWS_MATERIAL = "https://api.weixin.qq.com/cgi-bin/material/update_news?access_token=%s";
 
+    //获取用户标签列表
+  	private static final String GET_USER_TAG = "https://api.weixin.qq.com/cgi-bin/tags/get?access_token=%s";
+  	
+  	//创建用户标签
+  	private static final String CREATE_USER_TAG = "https://api.weixin.qq.com/cgi-bin/tags/create?access_token=%s";
+  	
+  	//获取标签下粉丝列表
+  	private static final String GET_USER_LIST_BY_TAG="https://api.weixin.qq.com/cgi-bin/user/tag/get?access_token=%s";
+  	
+  	//删除用户标签
+  	private static final String DELETE_USER_TAG = "https://api.weixin.qq.com/cgi-bin/tags/delete?access_token=%s";
+  		
+    
     //素材文件后缀
     public static Map<String,String> type_fix= new HashMap<>();
     public static Map<String,String> media_fix= new HashMap<>();
@@ -205,7 +218,25 @@ public class WxApi {
     public static String getUploadNewsUrl(String token) {
         return String.format(UPLOAD_NEWS, token);
     }
-
+    //获取用户标签列表接口
+  	public static String getUserTagList(String token) {
+  		return String.format(GET_USER_TAG, token);
+  	}
+  	
+  	//获取创建用户标签接口
+  	public static String getCreateUserTag(String token) {
+  		return String.format(CREATE_USER_TAG, token);
+  	}
+  	
+  	//获取标签下粉丝列表
+  	public static String getUserListByTag(String token) {
+  		return String.format(GET_USER_LIST_BY_TAG, token);
+  	}
+  	
+    //获取删除用户标签接口
+  	public static String getDeleteUserTag(String token) {
+  		return String.format(DELETE_USER_TAG, token);
+  	}
     // 群发接口
     public static String getMassSendUrl(String token) {
         return String.format(MASS_SEND, token);
@@ -324,6 +355,7 @@ public class WxApi {
         AccessToken token = null;
         String tockenUrl = WxApi.getTokenUrl(appId, appSecret);
         JSONObject jsonObject = httpsRequest(tockenUrl, HttpMethod.GET, null);
+        
         if (null != jsonObject && !jsonObject.containsKey("errcode")) {
             token = new AccessToken();
             token.setAccessToken(jsonObject.getString("access_token"));

+ 21 - 1
src/main/java/com/wxmp/wxapi/process/WxApiClient.java

@@ -126,7 +126,27 @@ public class WxApiClient {
         }
         return null;
     }
-    
+	//创建用户标签
+	public static JSONObject createUserTag(String userTags,MpAccount mpAccount ) throws WxErrorException {
+		String accessToken = getAccessToken(mpAccount);
+		String url = WxApi.getCreateUserTag(accessToken);
+		return WxApi.httpsRequest(url, HttpMethod.POST, userTags);
+	}
+	
+	//获取标签下粉丝列表
+	public static JSONObject getUserListByTag(String tagId,MpAccount mpAccount) throws WxErrorException {
+		String accessToken = getAccessToken(mpAccount);
+		String url = WxApi.getUserListByTag(accessToken);
+		return WxApi.httpsRequest(url, HttpMethod.POST, tagId);
+	}
+	
+	//删除用户标签
+	public static JSONObject deleteUserTag(String tagId,MpAccount mpAccount) throws WxErrorException {
+		String accessToken = getAccessToken(mpAccount);
+		String url = WxApi.getDeleteUserTag(accessToken);
+		return WxApi.httpsRequest(url, HttpMethod.POST, tagId);
+	}
+	
     // 发布菜单
     public static JSONObject publishMenus(String menus, MpAccount mpAccount)
         throws WxErrorException {

+ 227 - 0
src/main/java/com/wxmp/wxcms/ctrl/UserTagCtrl.java

@@ -0,0 +1,227 @@
+/**
+ * Copyright &copy; 2017-2018 <a href="http://www.webcsn.com">webcsn</a> All rights reserved.
+ *
+ * @author hermit
+ * @date 2018-04-17 10:54:58
+ */
+package com.wxmp.wxcms.ctrl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.wxmp.core.common.BaseCtrl;
+import com.wxmp.core.common.Constants;
+import com.wxmp.core.exception.WxErrorException;
+import com.wxmp.core.util.AjaxResult;
+import com.wxmp.wxapi.process.MpAccount;
+import com.wxmp.wxapi.process.WxApiClient;
+import com.wxmp.wxapi.process.WxMemoryCacheClient;
+import com.wxmp.wxcms.domain.AccountFans;
+import com.wxmp.wxcms.domain.UserTag;
+import com.wxmp.wxcms.service.AccountFansService;
+import com.wxmp.wxcms.service.UserTagService;
+
+
+
+/**
+ *
+ * @author fuziKong
+ * @version 2.0
+ * @date 2018-05-30 10:54:58
+ */
+@Controller
+@RequestMapping("/userTag")
+public class UserTagCtrl extends BaseCtrl {
+
+	@Autowired
+	private UserTagService entityService;
+	@Autowired
+	private AccountFansService accountFansService;
+	
+	/**
+	 * 根据用户标签获取粉丝列表
+	 * @return
+	 * @throws WxErrorException 
+	 */
+	@SuppressWarnings("unchecked")
+	@RequestMapping(value = "/getUserListByTag")
+	@ResponseBody
+	public AjaxResult getUserListByTag(Integer id) throws WxErrorException {
+		MpAccount mpAccount = WxMemoryCacheClient.getMpAccount();//获取缓存中的唯一账号
+		JSONObject  tagId = new JSONObject();
+		tagId.put("tagid", id);
+		tagId.put("next_openid", "");
+		JSONObject jsonObjUserList = WxApiClient.getUserListByTag(tagId.toString(), mpAccount);
+		if(jsonObjUserList !=null && jsonObjUserList.containsKey("data") ) {
+			JSONObject  data =  (JSONObject) jsonObjUserList.get("data");
+			JSONArray openidArray = data.getJSONArray("openid");
+			String js=JSONObject.toJSONString(openidArray);
+			List<String> list = JSONObject.parseArray(js, String.class);
+			List<AccountFans> fansList = new ArrayList<AccountFans>();
+			for (String  openId : list) {
+				AccountFans fans = new AccountFans();
+				fans.setOpenId(openId);
+				fansList.add(fans);
+			}
+			 fansList = accountFansService.getFansByOpenIdListByPage(fansList);
+			return AjaxResult.success(fansList);
+		}
+		AjaxResult result = new AjaxResult();
+		result.setMsg("没有数据");
+		return result;
+	}
+	
+	
+	/**
+	 * 同步用户标签列表
+	 * @return
+	 */
+	@RequestMapping(value = "/syncUserTagList")
+	@ResponseBody
+	public AjaxResult syncUserTagList(){
+		MpAccount mpAccount = WxMemoryCacheClient.getMpAccount();//获取缓存中的唯一账号
+		if(mpAccount != null){
+			boolean flag = entityService.syncUserTagList(mpAccount);
+			if(flag){
+				return AjaxResult.success();
+			}
+		}
+		return AjaxResult.failure();
+	}
+	
+	/**
+	 * 根据Id查询用户标签
+	 * @param id
+	 * @return
+	 */
+	@RequestMapping(value = "/getById")
+	@ResponseBody
+	public AjaxResult getById(Integer id){
+		UserTag  userTag = entityService.getById(id);
+		return AjaxResult.success(userTag);
+	}
+
+	/**
+	 * 分页查询
+	 * @param searchEntity
+	 * @return
+	 */
+	@RequestMapping(value = "/listForPage")
+	@ResponseBody
+	public AjaxResult listForPage(UserTag searchEntity) {
+		List<UserTag> list = entityService.listForPage(searchEntity);
+		if (CollectionUtils.isEmpty(list)) {
+			return AjaxResult.success();
+		}
+		return getResult(searchEntity,list);
+	}
+
+	/**
+	 * 修改/添加
+	 * @param entity
+	 * @return
+	 * @throws WxErrorException 
+	 */
+	@RequestMapping(value = "/update")
+	@ResponseBody
+	public AjaxResult update(UserTag entity) throws WxErrorException{
+		if (entity.getId() != null) {
+			entityService.update(entity);
+			//更新成功
+			return AjaxResult.updateSuccess();
+		} else {
+			//添加分两步
+			//1. 调用微信API添加 
+			MpAccount mpAccount = WxMemoryCacheClient.getMpAccount();//获取缓存中的唯一账号
+			JSONObject  tagName = new JSONObject();
+			tagName.put("name", entity.getName());
+			JSONObject  tagjson = new JSONObject();
+			tagjson.put("tag", tagName);
+			String userTags = tagjson.toString();
+			JSONObject userTag = WxApiClient.createUserTag(userTags, mpAccount);
+			//2.添加到本地库
+			if(userTag != null) {
+				JSONObject returnUserTag = (JSONObject) userTag.get("tag");
+				entity = JSONObject.parseObject(returnUserTag.toJSONString(), UserTag.class);
+				entityService.add(entity);
+				return AjaxResult.saveSuccess();
+			}
+			return AjaxResult.failure(Constants.MSG_ERROR);
+		}
+	}
+
+	/**
+	 * 删除
+	 * @param entity
+	 * @return
+	 * @throws WxErrorException 
+	 */
+	@RequestMapping(value = "/deleteById")
+	@ResponseBody
+	public AjaxResult deleteById(UserTag entity) {
+		//1.删除微信服务器的用户标签
+		if(deleteUserTag(entity.getId())){
+			//2.删除本地数据库的用户标签
+			entityService.delete(entity);
+			return AjaxResult.deleteSuccess();
+		}
+		return AjaxResult.failure("用户标签删除失败!");
+	}
+	
+	/**
+	 * 批量删除
+	 * @param String[] ids
+	 * @return
+	 * @throws WxErrorException 
+	 */
+	@RequestMapping(value = "/deleteBatchIds")
+	@ResponseBody
+	public AjaxResult deleteBatchIds(String [] ids)  {
+		if(null != ids && ids.length>0) {
+			int nums = 0;
+			for (String id : ids) {
+				if(deleteUserTag(Integer.parseInt(id))) {
+					nums++;
+				}
+			}
+			if(nums == ids.length) {
+				entityService.deleteBatchIds(ids);				
+			}
+			return AjaxResult.deleteSuccess();
+		}else {
+			return AjaxResult.failure("用户标签批量删除失败");
+		}
+	}
+	/**
+	 * 删除微信服务器的用户标签
+	 * @param id
+	 * @return boolean
+	 */
+	private boolean deleteUserTag(Integer id) {
+		MpAccount mpAccount = WxMemoryCacheClient.getMpAccount();//获取缓存中的唯一账号
+		JSONObject  tag = new JSONObject();
+		JSONObject  tagId = new JSONObject();
+		tagId.put("id", id);
+		tag.put("tag", tagId);
+		JSONObject result=null ;
+		try {
+			result = WxApiClient.deleteUserTag(tag.toJSONString(), mpAccount);
+		} catch (WxErrorException e) {
+			e.printStackTrace();
+		}
+		if(result!=null && result.containsKey("errmsg")) {
+			if("ok".equals(result.get("errmsg").toString())){
+				return true;
+			}
+		}
+		return false;
+	}
+}

+ 37 - 0
src/main/java/com/wxmp/wxcms/domain/UserTag.java

@@ -0,0 +1,37 @@
+/**
+ * Copyright &copy; 2017-2018 <a href="http://www.webcsn.com">webcsn</a> All rights reserved.
+ *
+ * @author hermit
+ * @date 2018-04-17 10:54:58
+ */
+package com.wxmp.wxcms.domain;
+import java.io.Serializable;
+import java.util.Date;
+
+import org.springframework.format.annotation.DateTimeFormat;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.wxmp.core.page.Page;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+/**
+ * @author fuzi Kong
+ * @version 2.0
+ * @date 2018-05-30 10:54:58
+ */
+@Data
+@EqualsAndHashCode(callSuper=false)
+@ToString
+public class UserTag extends Page implements Serializable {
+
+	private static final long serialVersionUID = 4576996489117070188L;
+	private Integer id;//主键ID
+	private String name;//标签名称
+	private Integer count = 0;//该标签的粉丝数量
+	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+	@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss",iso= DateTimeFormat.ISO.DATE_TIME)
+	private Date createTime = new Date();//创建时间
+}

+ 6 - 0
src/main/java/com/wxmp/wxcms/mapper/AccountFansDao.java

@@ -58,4 +58,10 @@ public interface AccountFansDao {
 	 * @return
 	 */
 	public List<AccountFans> getAccountFansList(AccountFans searchEntity);
+	/**
+	 * 根据多个openId查看粉丝列表
+	 * @param openIds
+	 * @return List
+	 */
+	public List<AccountFans> getFansByOpenIdListByPage(List<AccountFans> openIds);
 }

+ 44 - 0
src/main/java/com/wxmp/wxcms/mapper/UserTagDao.java

@@ -0,0 +1,44 @@
+/**
+ * Copyright &copy; 2017-2018 <a href="http://www.webcsn.com">webcsn</a> All rights reserved.
+ *
+ * @author hermit
+ * @date 2018-04-17 10:54:58
+ */
+package com.wxmp.wxcms.mapper;
+
+import java.util.List;
+import com.wxmp.wxcms.domain.UserTag;
+
+/**
+ *
+ * @author fuzi Kong
+ * @version 2.0
+ * @date 2018-04-17 10:54:58
+ */
+public interface UserTagDao {
+
+	public UserTag getById(Integer id);
+
+	public List<UserTag> getUserTagListByPage(UserTag searchEntity);
+
+	public void add(UserTag entity);
+	
+	public void addList(List<UserTag> list);
+
+	public void update(UserTag entity);
+
+	public void delete(UserTag entity);
+	/**
+     * <p>
+     * 删除(根据ID 批量删除)
+     * </p>
+     * @param ids 主键ID列表 
+     * @return int
+     */
+    Integer deleteBatchIds(String [] ids);
+    /**
+     * 获取最大的ID和本地库存的比较决定是否同步
+     * @return
+     */
+    public Integer getMaxId();
+}

+ 2 - 0
src/main/java/com/wxmp/wxcms/service/AccountFansService.java

@@ -38,6 +38,8 @@ public interface AccountFansService {
 
 	public List<AccountFans> getFansListByPage(AccountFans searchEntity);
 
+	public List<AccountFans> getFansByOpenIdListByPage(List<AccountFans> openIds);
+	
 	public AccountFans getLastOpenId();
 
 	public void sync(AccountFans searchEntity);

+ 37 - 0
src/main/java/com/wxmp/wxcms/service/UserTagService.java

@@ -0,0 +1,37 @@
+/**
+ * Copyright &copy; 2017-2018 <a href="http://www.webcsn.com">webcsn</a> All rights reserved.
+ *
+ * @author hermit
+ * @date 2018-04-17 10:54:58
+ */
+package com.wxmp.wxcms.service;
+
+import java.util.List;
+
+import com.wxmp.wxapi.process.MpAccount;
+import com.wxmp.wxcms.domain.UserTag;
+
+/**
+ *
+ * @author fuzi Kong
+ * @version 2.0
+ * @date 2018-05-30 10:54:58
+ */
+public interface UserTagService {
+
+	public UserTag getById(Integer id);
+
+	public List<UserTag> listForPage(UserTag searchEntity);
+
+	public void add(UserTag entity);
+
+	public void update(UserTag entity);
+
+	public void delete(UserTag entity);
+	//同步服务器的用户标签
+	public boolean syncUserTagList(MpAccount mpAccount);
+	
+	public Integer deleteBatchIds(String [] ids);
+	//获取数据库中用户标签的最大值,判断是否同步
+	public Integer getMaxId();
+}

+ 4 - 1
src/main/java/com/wxmp/wxcms/service/impl/AccountFansServiceImpl.java

@@ -81,5 +81,8 @@ public class AccountFansServiceImpl implements AccountFansService{
 	public void deleteByOpenId(String openId){
 		entityDao.deleteByOpenId(openId);
 	}
-
+	@Override
+	public List<AccountFans> getFansByOpenIdListByPage(List<AccountFans> openIds) {
+		return entityDao.getFansByOpenIdListByPage(openIds);
+	}
 }

+ 102 - 0
src/main/java/com/wxmp/wxcms/service/impl/UserTagServiceImpl.java

@@ -0,0 +1,102 @@
+package com.wxmp.wxcms.service.impl;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javax.annotation.Resource;
+
+import org.apache.log4j.Logger;
+import org.springframework.stereotype.Service;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.wxmp.core.exception.WxErrorException;
+import com.wxmp.wxapi.process.HttpMethod;
+import com.wxmp.wxapi.process.MpAccount;
+import com.wxmp.wxapi.process.WxApi;
+import com.wxmp.wxapi.process.WxApiClient;
+import com.wxmp.wxcms.domain.UserTag;
+import com.wxmp.wxcms.mapper.UserTagDao;
+import com.wxmp.wxcms.service.UserTagService;
+
+@Service
+public class UserTagServiceImpl implements UserTagService {
+
+	@Resource
+	private UserTagDao userTagDao;
+	
+	private Logger logger=Logger.getLogger(UserTagServiceImpl.class);
+	
+ 	
+	//同步粉丝列表
+	@SuppressWarnings("unchecked")
+	public boolean syncUserTagList(MpAccount mpAccount){
+	 		String url=null;
+			try {
+				url = WxApi.getUserTagList(WxApiClient.getAccessToken(mpAccount));
+			} catch (WxErrorException e) {
+				e.printStackTrace();
+			}
+			logger.info("同步用户标签参消息如下:"+url);
+			JSONObject jsonObject = WxApi.httpsRequest(url, HttpMethod.GET, null);
+			logger.info("同步用户标签消息如下:"+jsonObject.toString());
+			if(jsonObject.containsKey("errcode")){
+				return false;
+			}
+	    	JSONArray arr = jsonObject.getJSONArray("tags");//获取jsonArray对象
+	    	String js=JSONObject.toJSONString(arr);//将array数组转换成字符串
+	    	List<UserTag> userTagList=JSONObject.parseArray(js, UserTag.class);//把字符串转换成集合
+	    	//判断是否已经同步
+	    	UserTag userTag = userTagList.stream().max(  (u,u2) -> ( u.getId() - u2.getId() )  ).get();
+	    	Integer maxIdInDb = getMaxId() == null ?  0 : getMaxId();//第一次同步,数据库没有数据返回null
+	    	if(userTag.getId() == maxIdInDb) {
+	    		//说明已经同步
+	    		return true;
+	    	}else if( userTag.getId() > maxIdInDb ){
+	    		//如果微信服务器新增用户标签,同步新增标签,新增标签的ID比本地库的ID大
+	    		userTagList = userTagList.stream().filter( u -> u.getId() > maxIdInDb ).collect(Collectors.toList());
+	    		userTagDao.addList(userTagList);	    		
+	    		return true;
+	    	}
+	    	return true;
+		}
+	
+	@Override
+	public UserTag getById(Integer id) {
+		return userTagDao.getById(id);
+	}
+
+	@Override
+	public List<UserTag> listForPage(UserTag searchEntity) {
+		return userTagDao.getUserTagListByPage(searchEntity);
+	}
+
+	@Override
+	public void add(UserTag entity) {
+		userTagDao.add(entity);
+	}
+
+	@Override
+	public void update(UserTag entity) {
+		userTagDao.update(entity);
+
+	}
+
+	@Override
+	public void delete(UserTag entity) {
+		userTagDao.delete(entity);
+	}
+
+
+	@Override
+	public Integer deleteBatchIds(String[] ids) {
+		return userTagDao.deleteBatchIds(ids);
+	}
+
+
+	@Override
+	public Integer getMaxId() {
+		return userTagDao.getMaxId();
+	}
+
+}

+ 7 - 1
src/main/resources/mapper/AccountFansMapper.xml

@@ -23,7 +23,13 @@
 		</where>
 		ORDER BY create_time desc
 	</select>
-	
+	<select id="getFansByOpenIdListByPage" resultType="AccountFans" parameterType="list">
+		SELECT * FROM wxcms_account_fans WHERE OPEN_ID IN
+		<foreach collection="list"  item="item" open="(" separator="," close=")">
+			#{item.openId}
+		</foreach>
+		ORDER BY  CREATE_TIME DESC
+	</select>
 	<select id="getLastOpenId" resultType="com.wxmp.wxcms.domain.AccountFans">
 		SELECT * FROM wxcms_account_fans
 		ORDER BY ID DESC

+ 52 - 0
src/main/resources/mapper/UserTagMapper.xml

@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+<mapper namespace="com.wxmp.wxcms.mapper.UserTagDao">
+
+	<select id="getUserTagListByPage" parameterType="com.wxmp.wxcms.domain.UserTag" resultType="com.wxmp.wxcms.domain.UserTag">
+		SELECT * FROM wxcms_user_tag
+		<trim prefix="where" prefixOverrides="and|or">
+	        1=1 
+			<if test="name != null and name != '' ">
+			    AND  NAME = #{name}
+			</if>
+		</trim>	 
+		ORDER BY ID
+	</select>
+
+	<select id="getById" parameterType="java.lang.Integer" resultType="com.wxmp.wxcms.domain.UserTag">
+		SELECT * FROM wxcms_user_tag WHERE ID = #{id}
+	</select>
+	
+	<select id="getMaxId" resultType="int">
+		SELECT MAX(ID) FROM wxcms_user_tag
+	</select>
+	
+	<insert id="add" parameterType="com.wxmp.wxcms.domain.UserTag" flushCache="true" useGeneratedKeys="true" keyProperty="id">
+		INSERT INTO wxcms_user_tag ( ID,NAME,COUNT )  VALUES ( #{id},#{name},#{count})
+	</insert>
+
+	<update id="update" parameterType="com.wxmp.wxcms.domain.UserTag" flushCache="true">
+		UPDATE wxcms_user_tag SET NAME = #{name},COUNT = #{count},CREATE_TIME = #{createTime} WHERE ID = #{id}
+	</update>
+	
+	<delete id="delete" parameterType="com.wxmp.wxcms.domain.UserTag" >
+		DELETE FROM wxcms_user_tag WHERE ID = #{id}
+	</delete>
+	
+	<delete id="deleteBatchIds" parameterType="list">
+		DELETE FROM wxcms_user_tag WHERE ID IN 
+		<foreach collection="array"  item="item" open="(" separator="," close=")">
+			#{item}
+		</foreach>
+	</delete>
+	<!-- 批量添加微信服务器的用户标签到本地库 -->
+	<insert id="addList" parameterType="list">  
+        INSERT INTO wxcms_user_tag ( ID,NAME,COUNT )
+        VALUES
+        <foreach  collection="list" item="item" index="index" separator="," >  
+        	<![CDATA[
+             ( #{item.id},#{item.name},#{item.count} )
+        	 ]]>
+        </foreach> 
+    </insert>
+</mapper>

+ 1 - 1
src/main/resources/property/jdbc.properties

@@ -1,6 +1,6 @@
 #mysql database setting
 jdbc.driverClassName=com.mysql.jdbc.Driver
-jdbc.url=jdbc:mysql://127.0.0.1:3306/wxmp?useUnicode=true&characterEncoding=utf-8
+jdbc.url=jdbc:mysql://192.168.2.164:3306/wxmp?useUnicode=true&characterEncoding=utf-8
 jdbc.username=root
 jdbc.password=root
 #初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时

+ 1 - 1
src/main/resources/spring/mybatis-config.xml

@@ -17,7 +17,7 @@
 
 	<!-- 实体起别名 -->
 	<typeAliases>
-		<!--<typeAlias alias="SysNotice" type="com.fsh.entity.SysNotice"/>-->
+		<package name="com.wxmp.wxcms.domain"/>
 	</typeAliases>
 
 	<!-- 配置分页插件 -->

+ 4 - 0
src/main/webapp/views/index.html

@@ -73,6 +73,10 @@
                             <a href="javascript:;" data-url="/system/fans/fans">
                                 <i class="iconfont icon-yonghutouxiang"></i>粉丝管理</a>
                         </dd>
+                        <dd>
+                            <a href="javascript:;" data-url="/system/tags/tags">
+                                <i class="iconfont icon-discount"></i>用户标签管理</a>
+                        </dd>
                     </dl>
                 </li>
                 <li class="layui-nav-item layui-nav-itemed">

+ 138 - 0
src/main/webapp/views/system/tags/tags.html

@@ -0,0 +1,138 @@
+<style>
+    img.fans-portrait{
+        width:50px;
+        height:50px;
+    }
+    .fsh-rightPanel .layui-table-body .layui-table-cell {
+        height: 50px;
+        line-height: 50px;
+    }
+</style>
+<div class="fsh-rightPanel">
+    <div class="layui-anim layui-anim-upbit">
+        <div class="layui-form-item wx-search" id="list_form">
+            <div class="layui-inline">
+                <input type="text" name="name" placeholder="请输入标签名" autocomplete="off" class="layui-input">
+            </div>
+            <button class="layui-btn btn-primary" id="search">搜索</button>
+            <div class="layui-inline right">
+                <button class="layui-btn btn-primary" id="tags_add">添加</button>
+                <button class="layui-btn btn-danger" id="tags_sync">批量同步</button>
+                <button class="layui-btn btn-danger" id="tags_del">批量删除</button>
+            </div>
+        </div>
+        <table id="list_table" class="layui-hide" lay-filter="mainList"></table>
+    </div>
+</div>
+
+<script>
+    layui.use(['layer', 'table'], function () {
+        var layer = layui.layer;
+        var table = layui.table;
+
+        var tableObj = table.render({
+            id: 'list_table',
+            elem: '#list_table',
+            url: '/userTag/listForPage',
+            align: "center",
+            cols: [[ //表头
+                {type: 'checkbox'},
+                {type: 'numbers', title: '编号',width:50},
+                {field:'id', title: '标签编号',width:150,align: 'center'},
+                {field: 'name', title: '标签名称', width: 150, align: 'center'},
+                {field: 'count', title: '粉丝数量', width: 150, align: 'center'},
+                {field: 'createTime', title: '创建时间', width: 180, align: 'center'},
+                {
+                    field: 'lock', title: '操作', width: 300, align: 'center', templet: function (d) {
+                        return '<a href="javascript:;" class="font-primary" lay-event="showUser">查看粉丝</a>' ;
+                    }, unresize: true, align: 'center'
+                }
+            ]],
+        });
+
+        // 检索
+        $("#search").click(function () {
+            reloadTable(tableObj);
+        });
+     // 添加
+        $("#tags_add").click(function () {
+            showDialog({
+                title: '添加'
+                , template: 'add'
+                , saveUrl: '/userTag/update'
+                , tableObj: tableObj
+            })
+        });
+        // 批量同步用户标签
+        $("#tags_sync").click(function () {
+            layer.confirm('确认用户标签?', {
+                icon: 7,
+                title: "提示",
+                btn: ['确认', '取消'] //按钮
+            }, function () {
+                $.ajax({
+                    url: '/userTag/syncUserTagList',
+                    success: function (result) {
+                        if (result.success) {
+                            layer.msg("同步成功");
+                            reloadTable(tableObj);
+                        }
+                    },
+                    error: function () {
+                        layer.msg("同步异常");
+                    }
+                })
+            }, function () {
+                layer.closeAll();
+            });
+        });
+
+        // 批量删除
+        $("#tags_del").click(function () {
+            var data = table.checkStatus('list_table').data;//已选中数据
+            if (data.length == 0) {
+                layer.msg("至少选择一条");
+                return;
+            }
+            var arr = [];     //选中数组
+            for (var i = 0; i < data.length; i++) {
+                arr.push(data[i].id)
+            }
+            showConfirm("确认删除?", function () {
+                $.ajax({
+                    url: '/userTag/deleteBatchIds',
+                    data: {"ids": arr.join(",")},
+                    success: function (result) {
+                        if (result.success) {
+                            layer.msg("删除成功");
+                            reloadTable(tableObj);
+                        } else {
+                            layer.msg("删除失败");
+                        }
+                    }
+                })
+            });
+        });
+        
+        //表格内部操作按钮监听
+        table.on('tool(mainList)', function (obj) { //注:tool是工具条事件名,mainList是table原始容器的属性 lay-filter="对应的值"
+            var data = obj.data; //获得当前行数据
+            var layEvent = obj.event; //获得 lay-event 对应的值
+			//显示粉丝
+            if (layEvent === 'showUser') {            
+                showDialog({
+                    title: '粉丝信息', 
+                    template: 'fans',
+                    height: 600,
+                    htmlData: data,
+                    btn: ['关闭'],
+                    yes: function (index, layero) {
+                        layer.close(index);
+                    }
+                });
+            }
+           
+        });
+
+    });
+</script>

+ 10 - 0
src/main/webapp/views/system/tags/template/add.html

@@ -0,0 +1,10 @@
+<div class="fsh-pop">
+    <div id="add_form" class="layui-form">
+        <div class="layui-form-item layui-form-text">
+            <label class="layui-form-label"><i>*</i>标签名称</label>
+            <div class="layui-input-block">
+                <input name="name" maxlength="20" lay-verify="required" placeholder="请输入内容" class="layui-input">
+            </div>
+        </div>
+    </div>
+</div>

+ 37 - 0
src/main/webapp/views/system/tags/template/fans.html

@@ -0,0 +1,37 @@
+<div class="fsh-pop">
+    <div id="add_form" class="layui-form">
+        <table id="fans" class="layui-hide" lay-filter="fans"></table>
+        <input type="hidden" name="fans">
+    </div>
+</div>
+<script>
+
+
+    layui.use(['layer', 'table'], function () {
+        var layer = layui.layer;
+        var table = layui.table;
+
+        var fansArr=[];
+        var tableObj = table.render({
+            id: 'fans',
+            elem: '#fans',
+            url: '/userTag/getUserListByTag',
+            where:{id:  <%=id%> },
+            align: "center",
+            cols: [[ //表头
+                {type: 'checkbox'},
+                {type: 'numbers', title: '序号',width:50},
+                {field: 'nicknameStr', title: '昵称', width: 150, align: 'center',},
+                {field: 'headimgurl', title: '头像', width: 80, align: 'center',templet:function (d) {
+                    return "<img class='fans-portrait' style='width:50px;height:50px;' src=" + d.headimgurl + " />"
+                }},
+                {field: 'gender', title: '性别', align: 'center',templet:function (d) {
+                    return d.gender == 1 ? '男' : '女';
+                }},
+                {field: 'city', title: '省/市', width: 150,align: 'center',templet:function (d) {
+                    return (d.province + '-' + d.city);
+            }}
+            ]],
+        });
+    });
+</script>