1. 程式人生 > >APP訊息推送(APP Push)解決方案-服務端工作邏輯和實現

APP訊息推送(APP Push)解決方案-服務端工作邏輯和實現



一、APP 推送概述:

App推送訊息是我們常見的一種app訊息提醒方式。 我們的實現需要第三方的支援,實現方式是後臺通過介面將Push請求傳送至第三方,第三方實現在App所在裝置上的推送。

二、APP推送後臺處理邏輯:

在與推送平臺互動時,後臺需要向第三方傳送兩部分資訊,推送目標終端標示+推送內容

1、推送目標終端標示:

APP推送需要定位目標終端,也就是說要給那臺裝置進行推送, 簡單的情況下,單裝置推送,我們需要拿到一個終端ID的概念,用於定位目標裝置, 注:不同渠道中使用的單裝置ID方式也不盡相同,以下用TokenID來表示這個終端ID的概念。 而實際推送渠道中往往還有自定義的功能,比如通過打標籤的方式將TokenID進行劃分,達到批量差異化的效果。

2、推送內容:

即指通過API介面引數的定義終端上收到的Push訊息的內容和格式。 其中IOS的推送訊息在展示上區別於安卓的一點是沒有title,title的部分只能是預設的APP名稱,而安卓的部分雖然預設值也是APP名稱,但是也支援自定義title。

三、後端實現:

通過上述的處理邏輯可得知,後端首先需要登記客戶端的TokenId,然後保持TokenID的有效性更新,然後在需要傳送APP推送時拿到使用者的有效TokenID, 然後使用TokenID和已有的內容資訊通過API與三方Push服務互動,完成推送。 即後端的實現分為兩部分: 1、TokenID的登記 2、App Push API的呼叫
注:以下示例中有兩個元素為本專案的特殊情況: 其中product_id是因為當前專案中客戶端同時有多個版本,不同版本需要推送獨立處理,但在同一張表內統一記登記;
而login_id跟member_id同時存在是因為當前系統中存在共享賬戶的情況,一般賬號賬戶一對一的情況login_id和member_id是繫結的,不需要同時重複登記。

1、TokenID的登記:

以下是現專案使用的TokenID登記表結構:
/*==============================================================*/
/* Table: sys_app_push_token                                    */
/*==============================================================*/
create table sys_app_push_token
(
   record_id            int(11) not null auto_increment,
   login_id             int(11),
   member_id            int(11),
   push_token           varchar(200),
   visit_device         int(4) comment '3:Android;4:IOS',
   product_id           varchar(20) default '0' comment '',
   push_channel         int(4) default 1 comment '1:IOS信鴿,2:華為,3:小米,4:極光',
   nstatus              int(4) not null default 0 comment '狀態:0:申請中;1:生效;2:失效;3:刪除;4:歷史記錄',
   create_userid        int(11) not null default 0,
   create_time          varchar(20) character set utf8 not null default "",
   edit_userid          int(11) not null default 0,
   edit_time            varchar(20) character set utf8 not null default "",
   this_remark          text,
   description          text,
   create_ordernum      varchar(30) character set utf8 comment '記錄建立時的流水號',
   last_ordernum        varchar(30) character set utf8 comment '記錄最後一次編輯時的流水號',
   primary key (record_id)
)
ENGINE  = InnoDB
DEFAULT CHARACTER SET = utf8
COLLATE = utf8_general_ci
auto_increment = 10000
row_format = COMPACT;

alter table sys_app_push_token comment 'app推送token表';

/*==============================================================*/
/* Index: Index_1                                               */
/*==============================================================*/
create index Index_1 on sys_app_push_token
(
   record_id
);

以下是APP上傳TokenID介面上傳引數列表:



注:其中,推送渠道絕對在做Push時使用哪家API,引數的判定交由客戶端進行處理,後端直接登記判定結果。
以下是TokenID登記介面處理方法
	@Transactional(readOnly=false)
	public boolean uploadMemberPushTokenEnt(TrainVansContext trainVansContext) {
		try {
			//check already data
			trainVansContext.getTrainVansRequest().put("login_id", TrainVansUtils.getMV(trainVansContext.getTrainVansRequest(), "login_login_id"));
			// get All memberRelation
			trainVansContext.getTrainVansRequest().put("relation_type", TrainVansUtils.getMV(trainVansContext.getTrainVansRequest(), "visit_role"));
			List<Map<String,Object>> memberRelationList = SpringContextHandler.getBean(MemberService.class).getRelateMemberListByLoginId(trainVansContext);
			for(Map<String,Object> memberRelateMap : memberRelationList){
				//
				trainVansContext.getTrainVansRequest().put("member_id", TrainVansUtils.getMV(memberRelateMap, "member_id"));
				Map tokenMap = SpringContextHandler.getBean(AppPushService.class).getPushTokenMapByLoginMap(trainVansContext.getTrainVansRequest());
				//disable already data
				if(tokenMap != null){
					if(!TrainVansUtils.getMV(tokenMap, "push_token").equals(TrainVansUtils.getMV(trainVansContext.getTrainVansRequest(), "push_token"))){
						//
						trainVansContext.getTrainVansRequest().put("record_id", TrainVansUtils.getMV(tokenMap, "record_id"));
						if(!SpringContextHandler.getBean(AppPushService.class).updateDiabledThePushToken(trainVansContext)){
							throw new RuntimeException("TranVans_Operate_Exception");
						}
						//insert new data
						if(!SpringContextHandler.getBean(AppPushService.class).insertPushTokenRecord(trainVansContext)){
							throw new RuntimeException("TranVans_Operate_Exception");
						}
					} 
				}else{
					//insert new data
					if(!SpringContextHandler.getBean(AppPushService.class).insertPushTokenRecord(trainVansContext)){
						throw new RuntimeException("TranVans_Operate_Exception");
					}
				}
			}
			
 			return true;
		} catch (Exception e) {
			TrainVansUtils.setRetInfo(trainVansContext, "10005001","Register TokenID Error");
			e.printStackTrace();
			throw new RuntimeException("TranVans_Operate_Exception");
		}
	}

注:方法外部有一個關於對應本賬號的對賬戶列表的遍歷,遍歷中的處理部分為TokenID的登記處理操作。

2、APP Push API 的呼叫

推送渠道: APP推送不僅僅要求在APP開啟狀態時或者後臺執行時進行訊息推送,更多的場景是在移動終端關閉APP的場景下進行訊息推送, 渠道的優劣無非在於兩個維度,送達率和送達效率。 其中安卓推送的渠道較為雜亂,其中華為和小米提供的PUSH服務對於自平臺的移動終端支援的較為完善,而沒有廠商提供PUSH服務的終端只能通過 第三方服務來進行對接。 對於現有的這些渠道進行如下總結: 1、IOS:信鴿推送,這個推送在我門公司中經歷了三個專案,推送效果穩定。API接入也方便,是IOS端的不二選擇。 2、Android-華為:華為自平臺。 3、Android-小米:小米自平臺。 4、Android-其他:目前使用的是“極光推送”。在理想狀態下送達率和送達效率表現很好,但並不如以上三家渠道穩定。 在進行呼叫時可根據之前定義的push_channel分發給各自的渠道,各渠道的具體對接請各自檢視官網,API都很完善。