From ace21e6ff6b8771bc2eaab3f496dc1106291c895 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BE=8D=E6=99=A8?= Date: Wed, 28 Sep 2022 17:49:57 +0800 Subject: [PATCH 01/13] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=94=AF=E4=BB=98?= =?UTF-8?q?=E5=AE=9D=E8=AF=81=E4=B9=A6sig=5Falg=5Foid=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/alipay/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/alipay/mod.rs b/src/alipay/mod.rs index 7fd553f..97534c7 100644 --- a/src/alipay/mod.rs +++ b/src/alipay/mod.rs @@ -275,7 +275,7 @@ impl AlipayClient { Ok(pem) => { let cert = pem.parse_x509()?; let algorithm = cert.signature_algorithm.oid().to_string(); - if algorithm.ne("1.2.840.113549.1.1.11") && algorithm.ne("1.2.840.113549.1.1.5") { + if algorithm.starts_with("1.2.840.113549.1.1") { continue; } let issuer = iter2string(cert.issuer())?; From 8f808533ba7c096bce0704d4326105d561c286c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BE=8D=E6=99=A8?= Date: Thu, 29 Sep 2022 17:28:32 +0800 Subject: [PATCH 02/13] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=BE=AE=E4=BF=A1?= =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E5=9B=BE=E7=89=87=E8=AF=B7=E6=B1=82=E5=A4=B4?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/request.rs | 27 +-------------------------- src/wechat/cp/api/media.rs | 2 +- 2 files changed, 2 insertions(+), 27 deletions(-) diff --git a/src/request.rs b/src/request.rs index c6fe395..35468a3 100644 --- a/src/request.rs +++ b/src/request.rs @@ -325,10 +325,7 @@ impl LabraRequest where T: Serialize { client = client.add_root_certificate(cert.reqwest_cert()?); } let client = client.build()?; - let mut request = client.request(self.method.clone().into(), http_url.to_owned()).header( - reqwest::header::CONTENT_TYPE, - self.req_type.get_content_type(), - ); + let mut request = client.request(self.method.clone().into(), http_url.to_owned()); let mut data = &self.body.to_string(); match self.body { RequestBody::Json(v) => { @@ -351,28 +348,6 @@ impl LabraRequest where T: Serialize { } RequestBody::Null => {} } - // if let Some(data) = &self.data { - // match self.req_type { - // RequestType::Json => { - // request = request.json(data); - // } - // RequestType::Form => { - // let value = serde_json::to_value(data.clone()).unwrap_or(Value::Null); - // if value.is_string() { - // let v = value.to_string(); - // request = request.body(v.replace("\"","")); - // } { - // request = request.form(data); - // } - // } - // RequestType::Multipart => { - // - // } - // _ => { - // request = request.body(serde_json::to_string(data).unwrap_or_default()) - // } - // } - // } if let Some(headers) = &self.headers { for (k, v) in headers.into_iter() { request = request.header(k, HeaderValue::from_str(v)?); diff --git a/src/wechat/cp/api/media.rs b/src/wechat/cp/api/media.rs index eefd583..e83ac5b 100644 --- a/src/wechat/cp/api/media.rs +++ b/src/wechat/cp/api/media.rs @@ -188,7 +188,7 @@ pub struct WechatCpMediaResponse { #[serde(rename="type")] pub r#type: Option, pub thumb_media_id: Option, - pub created_at: Option, + pub created_at: Option, } From eda99cbc6d4aa5df69f0d5e7e7a7fb2111b6f9bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BE=8D=E6=99=A8?= Date: Fri, 30 Sep 2022 17:17:12 +0800 Subject: [PATCH 03/13] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=A0=87=E7=AD=BE?= =?UTF-8?q?=E5=88=97=E8=A1=A8=E6=8E=A5=E5=8F=A3=E8=BF=94=E5=9B=9E=E7=BB=93?= =?UTF-8?q?=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/wechat/cp/api/tag.rs | 5 +++-- src/wechat/cp/tp/tag.rs | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/wechat/cp/api/tag.rs b/src/wechat/cp/api/tag.rs index e45fc92..0c153dd 100644 --- a/src/wechat/cp/api/tag.rs +++ b/src/wechat/cp/api/tag.rs @@ -1,7 +1,7 @@ use serde::{Serialize, Deserialize}; use serde_json::{json, Value}; -use crate::{session::SessionStore, request::{RequestType}, WechatCommonResponse, LabradorResult, WechatCpClient}; +use crate::{session::SessionStore, request::{RequestType}, WechatCommonResponse, LabradorResult, WechatCpClient, LabraError}; use crate::wechat::cp::method::{CpTagMethod, WechatCpMethod}; /// 标签相关 @@ -81,7 +81,8 @@ impl<'a, T: SessionStore> WechatCpTag<'a, T> { /// 获得标签列表. pub async fn list_all(&self) -> LabradorResult> { let v = self.client.get(WechatCpMethod::Tag(CpTagMethod::List), vec![], RequestType::Json).await?.json::()?; - WechatCommonResponse::parse::>(v) + let v = WechatCommonResponse::parse::(v)?; + serde_json::from_value::>(v["taglist"].to_owned()).map_err(LabraError::from) } } diff --git a/src/wechat/cp/tp/tag.rs b/src/wechat/cp/tp/tag.rs index d001d80..7a4c6c9 100644 --- a/src/wechat/cp/tp/tag.rs +++ b/src/wechat/cp/tp/tag.rs @@ -1,6 +1,6 @@ use serde_json::{json, Value}; -use crate::{session::SessionStore, request::{RequestType}, WechatCommonResponse, LabradorResult, WechatCpTpClient, WechatCpTagAddOrRemoveUsersResponse, WechatCpTagGetResponse, WechatCpTagInfo}; +use crate::{session::SessionStore, request::{RequestType}, WechatCommonResponse, LabradorResult, WechatCpTpClient, WechatCpTagAddOrRemoveUsersResponse, WechatCpTagGetResponse, WechatCpTagInfo, LabraError}; use crate::wechat::cp::method::{CpTagMethod, WechatCpMethod}; /// 企业微信第三方开发-标签相关 @@ -80,7 +80,8 @@ impl<'a, T: SessionStore> WechatCpTpTag<'a, T> { /// 获得标签列表. pub async fn list_all(&self) -> LabradorResult> { let v = self.client.get(WechatCpMethod::Tag(CpTagMethod::List), vec![], RequestType::Json).await?.json::()?; - WechatCommonResponse::parse::>(v) + let v = WechatCommonResponse::parse::(v)?; + serde_json::from_value::>(v["taglist"].to_owned()).map_err(LabraError::from) } } From 1c425b10bd36a8223cae72646a818129fdf23472 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BE=8D=E6=99=A8?= Date: Fri, 30 Sep 2022 17:30:16 +0800 Subject: [PATCH 04/13] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=A0=87=E7=AD=BE?= =?UTF-8?q?=E5=88=97=E8=A1=A8=E6=8E=A5=E5=8F=A3=E8=BF=94=E5=9B=9E=E7=BB=93?= =?UTF-8?q?=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/wechat/cp/api/tag.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wechat/cp/api/tag.rs b/src/wechat/cp/api/tag.rs index 0c153dd..38c2ba3 100644 --- a/src/wechat/cp/api/tag.rs +++ b/src/wechat/cp/api/tag.rs @@ -107,7 +107,7 @@ pub struct WechatCpTagAddOrRemoveUsersResponse { #[derive(Debug, Clone,Serialize, Deserialize)] pub struct WechatCpTagInfo { - pub tagid: Option, + pub tagid: Option, pub tagname: Option>, } From 82223625b5db4343a137e44f3ac0f5ad47337208 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BE=8D=E6=99=A8?= Date: Fri, 30 Sep 2022 17:34:10 +0800 Subject: [PATCH 05/13] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=A0=87=E7=AD=BE?= =?UTF-8?q?=E5=88=97=E8=A1=A8=E6=8E=A5=E5=8F=A3=E8=BF=94=E5=9B=9E=E7=BB=93?= =?UTF-8?q?=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/wechat/cp/api/tag.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wechat/cp/api/tag.rs b/src/wechat/cp/api/tag.rs index 38c2ba3..459b091 100644 --- a/src/wechat/cp/api/tag.rs +++ b/src/wechat/cp/api/tag.rs @@ -108,7 +108,7 @@ pub struct WechatCpTagAddOrRemoveUsersResponse { #[derive(Debug, Clone,Serialize, Deserialize)] pub struct WechatCpTagInfo { pub tagid: Option, - pub tagname: Option>, + pub tagname: Option, } /// 微信用户信息 From e2bf36a5691c2c75ac41594d8ef643addc62f18a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BE=8D=E6=99=A8?= Date: Fri, 30 Sep 2022 17:37:37 +0800 Subject: [PATCH 06/13] =?UTF-8?q?=E8=B0=83=E6=95=B4=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E6=A0=87=E7=AD=BE=E7=94=A8=E6=88=B7=E8=BF=94=E5=9B=9E=E7=BB=93?= =?UTF-8?q?=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/wechat/cp/api/tag.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wechat/cp/api/tag.rs b/src/wechat/cp/api/tag.rs index 459b091..df675c0 100644 --- a/src/wechat/cp/api/tag.rs +++ b/src/wechat/cp/api/tag.rs @@ -91,7 +91,7 @@ impl<'a, T: SessionStore> WechatCpTag<'a, T> { #[derive(Debug, Clone,Serialize, Deserialize)] pub struct WechatCpTagGetResponse { /// 用户列表 - pub userid: Vec, + pub userlist: Vec, /// 部门列表 pub partylist: Vec, pub tagname: Option, From 5b2292bb33452ae8a17e2646685784d97501eb0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BE=8D=E6=99=A8?= Date: Fri, 30 Sep 2022 18:14:34 +0800 Subject: [PATCH 07/13] =?UTF-8?q?=E8=B0=83=E6=95=B4=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E6=A0=87=E7=AD=BE=E7=94=A8=E6=88=B7=E8=BF=94=E5=9B=9E=E7=BB=93?= =?UTF-8?q?=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/wechat/cp/api/external_contact.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wechat/cp/api/external_contact.rs b/src/wechat/cp/api/external_contact.rs index 7681083..19ba3e5 100644 --- a/src/wechat/cp/api/external_contact.rs +++ b/src/wechat/cp/api/external_contact.rs @@ -1266,7 +1266,7 @@ pub struct WechatCpWelcomeMsg { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct WechatCpUserExternalTagGroupInfo { - pub tag_group: Option, + pub tag_group: Option>, } #[derive(Debug, Clone, Serialize, Deserialize)] From 7ed765a9f72ab0bf0433dc76689066488153707c Mon Sep 17 00:00:00 2001 From: ynp <15104511733@163.com> Date: Sat, 8 Oct 2022 14:24:57 +0800 Subject: [PATCH 08/13] =?UTF-8?q?=E6=9B=B4=E6=94=B9=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=AE=A2=E6=88=B7=E6=A0=87=E7=AD=BE=E7=9A=84=E7=BB=93=E6=9E=84?= =?UTF-8?q?=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/wechat/cp/api/external_contact.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/wechat/cp/api/external_contact.rs b/src/wechat/cp/api/external_contact.rs index 19ba3e5..6bb03cc 100644 --- a/src/wechat/cp/api/external_contact.rs +++ b/src/wechat/cp/api/external_contact.rs @@ -503,9 +503,8 @@ impl<'a, T: SessionStore> WechatCpExternalContact<'a, T> { /// 企业可通过此接口向客户标签库中添加新的标签组和标签,每个企业最多可配置3000个企业标签。 /// 暂不支持第三方调用。 /// - pub async fn add_corp_tag(&self, req: WechatCpUserExternalTagGroupInfo) -> LabradorResult { - let v = self.client.post(WechatCpMethod::ExternalContact(CpExternalContactMethod::AddCorpTag), vec![], req, RequestType::Json).await?.json::()?; - WechatCommonResponse::parse::(v) + pub async fn add_corp_tag(&self, req: TagGroup) -> LabradorResult { + self.client.post(WechatCpMethod::ExternalContact(CpExternalContactMethod::AddCorpTag), vec![], req, RequestType::Json).await?.json::() } ///

From 189f269486c68f47c9abd854f0db3766798ce17d Mon Sep 17 00:00:00 2001
From: ynp <15104511733@163.com>
Date: Sat, 8 Oct 2022 18:19:28 +0800
Subject: [PATCH 09/13] =?UTF-8?q?=E6=B7=BB=E5=8A=A0api:=E7=AC=AC=E4=B8=89?=
 =?UTF-8?q?=E6=96=B9=E5=BA=94=E7=94=A8=E8=8E=B7=E5=8F=96=E7=94=A8=E6=88=B7?=
 =?UTF-8?q?=E4=BF=A1=E6=81=AF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/wechat/cp/api/oauth2.rs | 32 +++++++++++++++++++++++++-------
 src/wechat/cp/method.rs     |  2 ++
 2 files changed, 27 insertions(+), 7 deletions(-)

diff --git a/src/wechat/cp/api/oauth2.rs b/src/wechat/cp/api/oauth2.rs
index a190c37..bc6dfa0 100644
--- a/src/wechat/cp/api/oauth2.rs
+++ b/src/wechat/cp/api/oauth2.rs
@@ -13,7 +13,6 @@ pub struct WechatCpOauth2<'a, T: SessionStore> {
 
 #[allow(unused)]
 impl<'a, T: SessionStore> WechatCpOauth2<'a, T> {
-
     #[inline]
     pub fn new(client: &WechatCpClient) -> WechatCpOauth2 {
         WechatCpOauth2 {
@@ -76,9 +75,18 @@ impl<'a, T: SessionStore> WechatCpOauth2<'a, T> {
     /// 该接口用于根据code获取成员信息,适用于自建应用与代开发应用
     ///
     /// 注意: 这个方法里的agentId,需要开发人员自己给出
-    pub async fn get_user_info_new(&self, code: &str) -> LabradorResult {
+    pub async fn get_user_info_new(&self, code: &str) -> LabradorResult {
         let v = self.client.get(WechatCpMethod::Oauth2(CpOauth2Method::GetAuthUserInfo), vec![(CODE.to_string(), code.to_string())], RequestType::Json).await?.json::()?;
-        WechatCommonResponse::parse::(v)
+        WechatCommonResponse::parse::(v)
+    }
+
+    /// 
+    /// 获取访问用户身份
+    /// 获取访问用户身份
+    /// 该接口用于根据code获取成员信息,适用于自建应用与代开发应用
+    pub async fn get_user_info_auth_3rd(&self, code: &str) -> LabradorResult {
+        let v = self.client.get(WechatCpMethod::Oauth2(CpOauth2Method::GetAuthUserInfo3rd), vec![(CODE.to_string(), code.to_string())], RequestType::Json).await?.json::()?;
+        WechatCommonResponse::parse::(v)
     }
 
     /// 
@@ -116,23 +124,33 @@ impl<'a, T: SessionStore> WechatCpOauth2<'a, T> {
         let v = self.client.post(WechatCpMethod::Oauth2(CpOauth2Method::GetAuthUserDetail), vec![], json!({USER_TICKET: user_ticket}), RequestType::Json).await?.json::()?;
         WechatCommonResponse::parse::(v)
     }
-
 }
 
 //----------------------------------------------------------------------------------------------------------------------------
 
 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
 pub struct WechatCpOauth2UserInfo {
-    #[serde(alias="OpenId", alias="openid")]
+    #[serde(alias = "OpenId", alias = "openid")]
     pub openid: Option,
     pub external_userid: Option,
-    #[serde(alias="UserId", alias="userid")]
+    #[serde(alias = "UserId", alias = "userid")]
     pub user_id: Option,
     pub user_ticket: Option,
     pub expires_in: Option,
-    #[serde(rename="DeviceId")]
+    #[serde(rename = "DeviceId")]
     pub device_id: Option,
 }
+
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub struct WechatCpOauth2UserInfo3rd {
+    pub corpid: Option,
+    #[serde(alias = "UserId", alias = "userid")]
+    pub user_id: Option,
+    pub user_ticket: Option,
+    pub expires_in: Option,
+    pub open_userid: Option,
+}
+
 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
 pub struct WechatCpUserDetail {
     /// 成员UserID
diff --git a/src/wechat/cp/method.rs b/src/wechat/cp/method.rs
index 2bd239f..5d62f5a 100644
--- a/src/wechat/cp/method.rs
+++ b/src/wechat/cp/method.rs
@@ -330,6 +330,7 @@ pub enum CpOauth2Method {
     GetAuthUserDetail,
     GetUserInfo,
     GetAuthUserInfo,
+    GetAuthUserInfo3rd,
 }
 
 #[allow(unused)]
@@ -340,6 +341,7 @@ impl CpOauth2Method {
             CpOauth2Method::GetUserDetail => String::from("/cgi-bin/user/getuserdetail"),
             CpOauth2Method::GetUserInfo => String::from("/cgi-bin/user/getuserinfo"),
             CpOauth2Method::GetAuthUserInfo => String::from("/cgi-bin/auth/getuserinfo"),
+            CpOauth2Method::GetAuthUserInfo3rd => String::from("/cgi-bin/service/auth/getuserinfo3rd"),
             CpOauth2Method::GetAuthUserDetail => String::from("/cgi-bin/auth/getuserdetail"),
         }
     }

From 4eb9259012cdcf71f6deab69ac5909f1ecc6c509 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=BE=8D=E6=99=A8?= 
Date: Sun, 9 Oct 2022 09:23:25 +0800
Subject: [PATCH 10/13] =?UTF-8?q?=E5=A2=9E=E5=8A=A0redis=E7=9B=B8=E5=85=B3?=
 =?UTF-8?q?=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/lib.rs     |   3 +-
 src/session.rs | 162 ++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 163 insertions(+), 2 deletions(-)

diff --git a/src/lib.rs b/src/lib.rs
index bc3e5e0..b1cbf39 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -203,4 +203,5 @@ pub use reqwest::multipart::{Form, Part};
 pub use bytes;
 pub use serde_urlencoded;
 pub use urlencoding;
-pub use dashmap;
\ No newline at end of file
+pub use dashmap;
+pub use redis;
\ No newline at end of file
diff --git a/src/session.rs b/src/session.rs
index 2293e47..d4d71c2 100644
--- a/src/session.rs
+++ b/src/session.rs
@@ -402,7 +402,7 @@ pub mod redis_store {
 
     pub type RedisPool = Pool;
     use r2d2::{Pool};
-    use redis::{self, ToRedisArgs, ConnectionLike, Commands, FromRedisValue};
+    use redis::{self, ToRedisArgs, ConnectionLike, Commands, FromRedisValue, streams};
     use crate::{LabradorResult, LabraError};
 
     use super::{SessionStore, ToStore, FromStore, Store};
@@ -483,6 +483,166 @@ pub mod redis_store {
             }
             client.xadd_map(key.as_ref(), "*", items).map_err(LabraError::from)
         }
+
+        pub fn xread<'a, K: ToRedisArgs,  ID: ToRedisArgs, RV: FromRedisValue>(&self, keys: &'a [K], ids: &'a [ID]) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.xread(keys, ids).map_err(LabraError::from)
+        }
+
+        pub fn xinfo_consumers<'a, K: ToRedisArgs,  G: ToRedisArgs, RV: FromRedisValue>(&self, key: K, group: G) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.xinfo_consumers(key, group).map_err(LabraError::from)
+        }
+
+        pub fn xinfo_groups<'a, K: ToRedisArgs, RV: FromRedisValue>(&self, key: K) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.xinfo_groups(key).map_err(LabraError::from)
+        }
+
+        pub fn xinfo_stream<'a, K: ToRedisArgs, RV: FromRedisValue>(&self, key: K) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.xinfo_stream(key).map_err(LabraError::from)
+        }
+
+        pub fn xread_options<'a, K: ToRedisArgs,  ID: ToRedisArgs, RV: FromRedisValue>(&self, keys: &'a [K], ids: &'a [ID], options: &'a streams::StreamReadOptions) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.xread_options(keys, ids, options).map_err(LabraError::from)
+        }
+
+        pub fn xgroup_create<'a, K: ToRedisArgs, G: ToRedisArgs,  ID: ToRedisArgs, RV: FromRedisValue>(&self, key: K, group: G, id: ID) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.xgroup_create(key, group, id).map_err(LabraError::from)
+        }
+
+        pub fn xgroup_delconsumer<'a, K: ToRedisArgs, G: ToRedisArgs,  C: ToRedisArgs, RV: FromRedisValue>(&self, key: K, group: G, consumer: C) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.xgroup_delconsumer(key, group, consumer).map_err(LabraError::from)
+        }
+
+        pub fn xack<'a, K: ToRedisArgs, G: ToRedisArgs,  I: ToRedisArgs, RV: FromRedisValue>(&self, key: K, group: G, ids: &'a [I]) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.xack(key, group, ids).map_err(LabraError::from)
+        }
+
+        pub fn xgroup_create_mkstream<'a, K: ToRedisArgs, G: ToRedisArgs, ID: ToRedisArgs, RV: FromRedisValue>(&self, key: K, group: G, id: ID) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.xgroup_create_mkstream(key, group, id).map_err(LabraError::from)
+        }
+
+        pub fn xgroup_destroy<'a, K: ToRedisArgs, G: ToRedisArgs, RV: FromRedisValue>(&self, key: K, group: G) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.xgroup_destroy(key, group).map_err(LabraError::from)
+        }
+
+        pub fn xdel<'a, K: ToRedisArgs, ID: ToRedisArgs, RV: FromRedisValue>(&self, key: K, ids: &'a [ID]) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.xdel(key, ids).map_err(LabraError::from)
+        }
+
+        pub fn xpending<'a, K: ToRedisArgs, G: ToRedisArgs, RV: FromRedisValue>(&self, key: K, group: G) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.xpending(key, group).map_err(LabraError::from)
+        }
+
+        pub fn xpending_count<'a, K: ToRedisArgs, G: ToRedisArgs, S: ToRedisArgs, E: ToRedisArgs, C: ToRedisArgs, RV: FromRedisValue>(&self, key: K, group: G, start: S, end: E, count: C) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.xpending_count(key, group, start, end, count).map_err(LabraError::from)
+        }
+
+        pub fn xpending_consumer_count<'a, K: ToRedisArgs, G: ToRedisArgs, S: ToRedisArgs, E: ToRedisArgs, C: ToRedisArgs, CN: ToRedisArgs, RV: FromRedisValue>(&self, key: K, group: G, start: S, end: E, count: C, consumer: CN) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.xpending_consumer_count(key, group, start, end, count, consumer).map_err(LabraError::from)
+        }
+
+        pub fn xrevrange<'a, K: ToRedisArgs, E: ToRedisArgs, S: ToRedisArgs, RV: FromRedisValue>(&self, key: K, start: S, end: E) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.xrevrange(key, end, start).map_err(LabraError::from)
+        }
+
+        pub fn xrevrange_all<'a, K: ToRedisArgs, RV: FromRedisValue>(&self, key: K) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.xrevrange_all(key).map_err(LabraError::from)
+        }
+
+        pub fn xrevrange_count<'a, K: ToRedisArgs, E: ToRedisArgs, S: ToRedisArgs, C: ToRedisArgs,RV: FromRedisValue>(&self, key: K, start: S, end: E, count: C) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.xrevrange_count(key, end, start, count).map_err(LabraError::from)
+        }
+
+        pub fn exists<'a, K: ToRedisArgs,RV: FromRedisValue>(&self, key: K) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.exists(key).map_err(LabraError::from)
+        }
+
+        pub fn expire<'a, K: ToRedisArgs,RV: FromRedisValue>(&self, key: K, seconds: usize) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.expire(key, seconds).map_err(LabraError::from)
+        }
+
+        pub fn expire_at<'a, K: ToRedisArgs,RV: FromRedisValue>(&self, key: K, ts: usize) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.expire_at(key, ts).map_err(LabraError::from)
+        }
     }
 
 

From 4f703fdeaa767a7aa7dabfd13856a531f9cc8cba Mon Sep 17 00:00:00 2001
From: ynp <15104511733@163.com>
Date: Sun, 9 Oct 2022 13:48:27 +0800
Subject: [PATCH 11/13] =?UTF-8?q?=E6=B7=BB=E5=8A=A0api:=E7=AC=AC=E4=B8=89?=
 =?UTF-8?q?=E6=96=B9=E5=BA=94=E7=94=A8=E8=8E=B7=E5=8F=96=E7=94=A8=E6=88=B7?=
 =?UTF-8?q?=E4=BF=A1=E6=81=AF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/wechat/cp/api/oauth2.rs | 23 ++------------------
 src/wechat/cp/method.rs     |  4 ++--
 src/wechat/cp/tp/auth.rs    | 42 +++++++++++++++++++++++++++++++++++++
 src/wechat/cp/tp/mod.rs     | 31 ++++++++++++++-------------
 4 files changed, 63 insertions(+), 37 deletions(-)
 create mode 100644 src/wechat/cp/tp/auth.rs

diff --git a/src/wechat/cp/api/oauth2.rs b/src/wechat/cp/api/oauth2.rs
index bc6dfa0..88cf9eb 100644
--- a/src/wechat/cp/api/oauth2.rs
+++ b/src/wechat/cp/api/oauth2.rs
@@ -75,18 +75,9 @@ impl<'a, T: SessionStore> WechatCpOauth2<'a, T> {
     /// 该接口用于根据code获取成员信息,适用于自建应用与代开发应用
     ///
     /// 注意: 这个方法里的agentId,需要开发人员自己给出
-    pub async fn get_user_info_new(&self, code: &str) -> LabradorResult {
+    pub async fn get_user_info_new(&self, code: &str) -> LabradorResult {
         let v = self.client.get(WechatCpMethod::Oauth2(CpOauth2Method::GetAuthUserInfo), vec![(CODE.to_string(), code.to_string())], RequestType::Json).await?.json::()?;
-        WechatCommonResponse::parse::(v)
-    }
-
-    /// 
-    /// 获取访问用户身份
-    /// 获取访问用户身份
-    /// 该接口用于根据code获取成员信息,适用于自建应用与代开发应用
-    pub async fn get_user_info_auth_3rd(&self, code: &str) -> LabradorResult {
-        let v = self.client.get(WechatCpMethod::Oauth2(CpOauth2Method::GetAuthUserInfo3rd), vec![(CODE.to_string(), code.to_string())], RequestType::Json).await?.json::()?;
-        WechatCommonResponse::parse::(v)
+        WechatCommonResponse::parse::(v)
     }
 
     /// 
@@ -141,16 +132,6 @@ pub struct WechatCpOauth2UserInfo {
     pub device_id: Option,
 }
 
-#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-pub struct WechatCpOauth2UserInfo3rd {
-    pub corpid: Option,
-    #[serde(alias = "UserId", alias = "userid")]
-    pub user_id: Option,
-    pub user_ticket: Option,
-    pub expires_in: Option,
-    pub open_userid: Option,
-}
-
 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
 pub struct WechatCpUserDetail {
     /// 成员UserID
diff --git a/src/wechat/cp/method.rs b/src/wechat/cp/method.rs
index 5d62f5a..957c70e 100644
--- a/src/wechat/cp/method.rs
+++ b/src/wechat/cp/method.rs
@@ -22,6 +22,7 @@ pub enum WechatCpMethod {
     GetOrder,
     GetOrderList,
     GetCustomizedAuthUrl,
+    GetAuthUserInfo3rd,
     Media(CpMediaMethod),
     Tag(CpTagMethod),
     Agent(CpAgentMethod),
@@ -58,6 +59,7 @@ impl RequestMethod for WechatCpMethod {
             WechatCpMethod::GetCallbackIp => String::from("/cgi-bin/getcallbackip"),
             WechatCpMethod::GetAgentConfigTicket => String::from("/cgi-bin/ticket/get?&type=agent_config"),
             WechatCpMethod::GetCustomizedAuthUrl => String::from("/cgi-bin/service/get_customized_auth_url"),
+            WechatCpMethod::GetAuthUserInfo3rd => String::from("/cgi-bin/service/auth/getuserinfo3rd"),
             WechatCpMethod::Media(v) => v.get_method(),
             WechatCpMethod::ExternalContact(v) => v.get_method(),
             WechatCpMethod::Oauth2(v) => v.get_method(),
@@ -330,7 +332,6 @@ pub enum CpOauth2Method {
     GetAuthUserDetail,
     GetUserInfo,
     GetAuthUserInfo,
-    GetAuthUserInfo3rd,
 }
 
 #[allow(unused)]
@@ -341,7 +342,6 @@ impl CpOauth2Method {
             CpOauth2Method::GetUserDetail => String::from("/cgi-bin/user/getuserdetail"),
             CpOauth2Method::GetUserInfo => String::from("/cgi-bin/user/getuserinfo"),
             CpOauth2Method::GetAuthUserInfo => String::from("/cgi-bin/auth/getuserinfo"),
-            CpOauth2Method::GetAuthUserInfo3rd => String::from("/cgi-bin/service/auth/getuserinfo3rd"),
             CpOauth2Method::GetAuthUserDetail => String::from("/cgi-bin/auth/getuserdetail"),
         }
     }
diff --git a/src/wechat/cp/tp/auth.rs b/src/wechat/cp/tp/auth.rs
new file mode 100644
index 0000000..bf92467
--- /dev/null
+++ b/src/wechat/cp/tp/auth.rs
@@ -0,0 +1,42 @@
+use serde::{Deserialize, Serialize};
+use serde_json::{json, Value};
+
+use crate::{LabradorResult, request::RequestType, session::SessionStore, WechatCommonResponse, WechatCpTpClient};
+use crate::wechat::cp::constants::{CODE, PROVIDER_ACCESS_TOKEN};
+use crate::wechat::cp::method::WechatCpMethod;
+
+#[derive(Debug, Clone)]
+pub struct WechatCpTpAuth<'a, T: SessionStore> {
+    client: &'a WechatCpTpClient,
+}
+
+#[allow(unused)]
+impl<'a, T: SessionStore> WechatCpTpAuth<'a, T> {
+    #[inline]
+    pub fn new(client: &WechatCpTpClient) -> WechatCpTpAuth {
+        WechatCpTpAuth {
+            client,
+        }
+    }
+
+    /// 
+    /// 获取访问用户身份
+    /// 获取访问用户身份
+    /// 该接口用于根据code获取成员信息,适用于自建应用与代开发应用
+    pub async fn get_user_info_auth_3rd(&self, code: &str) -> LabradorResult {
+        let v = self.client.get(WechatCpMethod::GetAuthUserInfo3rd, vec![(CODE.to_string(), code.to_string())], RequestType::Json).await?.json::()?;
+        WechatCommonResponse::parse::(v)
+    }
+}
+
+//----------------------------------------------------------------------------------------------------------------------------
+
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub struct WechatCpOauth2UserInfo3rd {
+    pub corpid: Option,
+    #[serde(alias = "UserId", alias = "userid")]
+    pub user_id: Option,
+    pub user_ticket: Option,
+    pub expires_in: Option,
+    pub open_userid: Option,
+}
\ No newline at end of file
diff --git a/src/wechat/cp/tp/mod.rs b/src/wechat/cp/tp/mod.rs
index 2143030..079e4f1 100644
--- a/src/wechat/cp/tp/mod.rs
+++ b/src/wechat/cp/tp/mod.rs
@@ -14,6 +14,7 @@ mod department;
 mod user;
 mod order;
 mod agent;
+mod auth;
 
 pub use tag::*;
 pub use license::*;
@@ -22,6 +23,7 @@ pub use department::*;
 pub use user::*;
 pub use order::*;
 pub use agent::*;
+use crate::wechat::cp::tp::auth::WechatCpTpAuth;
 
 
 /// 企业微信第三方应用API
@@ -44,7 +46,6 @@ pub struct WechatCpTpClient {
 
 #[allow(unused)]
 impl WechatCpTpClient {
-
     fn from_client(client: APIClient) -> WechatCpTpClient {
         WechatCpTpClient {
             corp_id: client.app_key.to_owned(),
@@ -54,7 +55,7 @@ impl WechatCpTpClient {
             suite_id: None,
             suite_secret: None,
             client,
-            provider_secret: None
+            provider_secret: None,
         }
     }
 
@@ -111,7 +112,7 @@ impl WechatCpTpClient {
     /// 授权企业的access token相关
     fn get_access_token(&self, auth_corp_id: &str) -> String {
         let session = self.client.session();
-        session.get::<_,String>(self.key_with_prefix(auth_corp_id) + ACCESS_TOKEN_KEY, None).unwrap_or(None).unwrap_or_default()
+        session.get::<_, String>(self.key_with_prefix(auth_corp_id) + ACCESS_TOKEN_KEY, None).unwrap_or(None).unwrap_or_default()
     }
 
     /// 
@@ -140,7 +141,7 @@ impl WechatCpTpClient {
         let timestamp = current_timestamp();
         let expires_at: i64 = session.get(&expires_key, Some(timestamp))?.unwrap_or_default();
         if expires_at <= timestamp {
-            return Err(LabraError::ApiError("invaild suite ticket".to_string()))
+            return Err(LabraError::ApiError("invaild suite ticket".to_string()));
         }
         Ok(token)
     }
@@ -199,7 +200,8 @@ impl WechatCpTpClient {
                 "suite_secret": self.suite_secret,
                 "suite_ticket": suite_ticket
             });
-            let result = self.client.post(WechatCpMethod::GetSuiteToken, vec![], req, RequestType::Json).await?.json::()?;
+            let result = self.client.post(WechatCpMethod::GetSuiteToken, vec![], req, RequestType::Json).await?.json::()?;
+            let result = WechatCommonResponse::parse::(result)?;
             let token = result.suite_access_token;
             let expires_in = result.expires_in;
             // 预留200秒的时间
@@ -280,12 +282,11 @@ impl WechatCpTpClient {
     }
 
 
-
     /// 
     /// 获取企业凭证
     /// 
pub async fn get_corp_token(&self, auth_corpid: &str, permanent_code: &str) -> LabradorResult { - self.get_corp_token_force(auth_corpid, permanent_code,false).await + self.get_corp_token_force(auth_corpid, permanent_code, false).await } ///
@@ -314,7 +315,7 @@ impl WechatCpTpClient {
             session.set(&expires_key, expires_at, Some(expires_in as usize));
             Ok(result)
         } else {
-            Ok(AccessTokenResponse{ access_token: token.to_string(), expires_in: expires_at })
+            Ok(AccessTokenResponse { access_token: token.to_string(), expires_in: expires_at })
         }
     }
 
@@ -485,8 +486,8 @@ impl WechatCpTpClient {
         let noncestr = get_nonce_str();
         let signature = WechatCrypto::get_sha1_sign(&vec!["jsapi_ticket=".to_string() + &jsapi_ticket,
                                                           "noncestr=".to_string() + &noncestr,
-                                                          "timestamp=".to_string() + ×tamp.to_string(),"url=".to_string() + &url].join("&"));
-        JsapiSignature{
+                                                          "timestamp=".to_string() + ×tamp.to_string(), "url=".to_string() + &url].join("&"));
+        JsapiSignature {
             app_id: auth_corp_id.to_string(),
             nonce_str: noncestr,
             url: url.to_string(),
@@ -567,6 +568,11 @@ impl WechatCpTpClient {
     pub fn agent(&self) -> WechatCpTpAgent {
         WechatCpTpAgent::new(self)
     }
+
+    /// 身份
+    pub fn auth(&self) -> WechatCpTpAuth {
+        WechatCpTpAuth::new(self)
+    }
 }
 
 //----------------------------------------------------------------------------------------------------------------------------
@@ -592,7 +598,7 @@ pub struct WechatCpThirdPermanentCodeInfo {
     pub edition_info: Option,
     pub expires_in: Option,
     /// 安装应用时,扫码或者授权链接中带的state值。详见state说明
-    pub state: Option
+    pub state: Option,
 }
 
 
@@ -731,7 +737,6 @@ pub struct WechatCpThirdPreauthCode {
 }
 
 
-
 /// 服务商模式获取授权信息
 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
 pub struct WechatCpThirdAuthInfo {
@@ -746,8 +751,6 @@ pub struct WechatCpThirdAuthInfo {
 }
 
 
-
-
 /// 服务商模式获取授权信息
 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
 pub struct DealerCorpInfo {

From 3934de5753b403d207dd0b3195ac61a710fb60ee Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=BE=8D=E6=99=A8?= 
Date: Mon, 10 Oct 2022 17:17:18 +0800
Subject: [PATCH 12/13] =?UTF-8?q?=E5=A2=9E=E5=8A=A0redis=E7=9B=B8=E5=85=B3?=
 =?UTF-8?q?=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/session.rs | 199 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 197 insertions(+), 2 deletions(-)

diff --git a/src/session.rs b/src/session.rs
index d4d71c2..ec2ede2 100644
--- a/src/session.rs
+++ b/src/session.rs
@@ -452,7 +452,7 @@ pub mod redis_store {
             Ok(())
         }
 
-        pub fn zlcount, T: ToRedisArgs>(&self, key: K, min: T, max: T) -> LabradorResult> {
+        pub fn zlcount, M: ToRedisArgs, MM: ToRedisArgs, RV: FromRedisValue>(&self, key: K, min: M, max: MM) -> LabradorResult {
             let mut client = self.client_pool.get()?;
             if !client.check_connection() {
                 return Err(LabraError::ApiError("error to get redis connection".to_string()))
@@ -460,7 +460,8 @@ pub mod redis_store {
             client.zcount(key.as_ref(), min, max).map_err(LabraError::from)
         }
 
-        pub fn zadd, T: ToRedisArgs>(&self, key: K, member: T, score: T) -> LabradorResult> {
+        /// 在zsetname集合中增加序号为n的value
+        pub fn zadd, S: ToRedisArgs, M: ToRedisArgs, RV: FromRedisValue>(&self, key: K, member: M, score: S) -> LabradorResult {
             let mut client = self.client_pool.get()?;
             if !client.check_connection() {
                 return Err(LabraError::ApiError("error to get redis connection".to_string()))
@@ -468,6 +469,112 @@ pub mod redis_store {
             client.zadd(key.as_ref(), member, score).map_err(LabraError::from)
         }
 
+        /// 排序指定的rank(排名)范围内的元素并输出
+        pub fn zrange, RV: FromRedisValue>(&self, key: K, start: isize, stop: isize) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.zrange(key.as_ref(), start, stop).map_err(LabraError::from)
+        }
+
+        pub fn zadd_multiple, S: ToRedisArgs, M: ToRedisArgs, RV: FromRedisValue>(&self, key: K, items: &[(S, M)]) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.zadd_multiple(key.as_ref(), items).map_err(LabraError::from)
+        }
+
+        /// 反向排序
+        pub fn zrevrange, RV: FromRedisValue>(&self, key: K, start: isize, stop: isize) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.zrevrange(key.as_ref(), start, stop).map_err(LabraError::from)
+        }
+
+        /// 获取指定的score范围内的元素
+        pub fn zrangebyscore, M: ToRedisArgs, MM: ToRedisArgs, RV: FromRedisValue>(&self, key: K, min: M, max: MM) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.zrangebyscore(key.as_ref(), min, max).map_err(LabraError::from)
+        }
+
+        /// 获取a<=x<=b范围的数据,如果分页加上limit
+        pub fn zrangebylex, M: ToRedisArgs, MM: ToRedisArgs, RV: FromRedisValue>(&self, key: K, min: M, max: MM) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.zrangebylex(key.as_ref(), min, max).map_err(LabraError::from)
+        }
+
+        /// 为score累加n,新元素score基数为0
+        pub fn zincr, M: ToRedisArgs, D: ToRedisArgs, RV: FromRedisValue>(&self, key: K, member: M, delta: D) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.zincr(key.as_ref(), member, delta).map_err(LabraError::from)
+        }
+
+        /// 删除zsetname集合中指定的元素
+        pub fn zrem, M: ToRedisArgs, RV: FromRedisValue>(&self, key: K, members: M) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.zrem(key.as_ref(), members).map_err(LabraError::from)
+        }
+
+        /// 获取zsetname集合的元素个数
+        pub fn zcard, RV: FromRedisValue>(&self, key: K) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.zcard(key.as_ref()).map_err(LabraError::from)
+        }
+
+        /// 删除下标在start end 范围内的元素
+        pub fn zremrangebyrank, RV: FromRedisValue>(&self, key: K, start: isize, stop: isize) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.zremrangebyrank(key.as_ref(), start, stop).map_err(LabraError::from)
+        }
+
+        /// 删除score在[min [max 范围内的元素
+        pub fn zrembyscore, M: ToRedisArgs, MM: ToRedisArgs, RV: FromRedisValue>(&self, key: K, min: M, max: MM) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.zrembyscore(key.as_ref(), min, max).map_err(LabraError::from)
+        }
+
+        pub fn zrembylex, M: ToRedisArgs, MM: ToRedisArgs, RV: FromRedisValue>(&self, key: K, min: M, max: MM) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.zrembylex(key.as_ref(), min, max).map_err(LabraError::from)
+        }
+
+        /// 查询指定value的排名,注意不是score
+        pub fn zrank, M: ToRedisArgs, RV: FromRedisValue>(&self, key: K, member: M) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.zrank(key.as_ref(), member).map_err(LabraError::from)
+        }
+
         pub fn xadd,  F: ToRedisArgs, V: ToRedisArgs, RV: FromRedisValue>(&self, key: K, items: &[(F, V)]) -> LabradorResult {
             let mut client = self.client_pool.get()?;
             if !client.check_connection() {
@@ -643,6 +750,94 @@ pub mod redis_store {
             }
             client.expire_at(key, ts).map_err(LabraError::from)
         }
+
+        pub fn lpush<'a, K: ToRedisArgs, V: ToRedisArgs, RV: FromRedisValue>(&self, key: K, value: V) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.lpush(key, value).map_err(LabraError::from)
+        }
+
+        pub fn lpush_exists<'a, K: ToRedisArgs, V: ToRedisArgs, RV: FromRedisValue>(&self, key: K, value: V) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.lpush_exists(key, value).map_err(LabraError::from)
+        }
+
+        /// 移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
+        pub fn blpop<'a, K: ToRedisArgs, RV: FromRedisValue>(&self, key: K, timeout: usize) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.blpop(key, timeout).map_err(LabraError::from)
+        }
+
+        /// 移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
+        pub fn brpop<'a, K: ToRedisArgs, RV: FromRedisValue>(&self, key: K, timeout: usize) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.brpop(key, timeout).map_err(LabraError::from)
+        }
+
+        /// 移出并获取列表的第一个元素
+        pub fn lpop<'a, K: ToRedisArgs, RV: FromRedisValue>(&self, key: K, count: Option) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.lpop(key, count).map_err(LabraError::from)
+        }
+
+        /// 通过索引获取列表中的元素
+        pub fn lindex<'a, K: ToRedisArgs, RV: FromRedisValue>(&self, key: K, index: isize) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.lindex(key, index).map_err(LabraError::from)
+        }
+
+        /// 获取列表长度
+        pub fn llen<'a, K: ToRedisArgs, RV: FromRedisValue>(&self, key: K) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.llen(key).map_err(LabraError::from)
+        }
+
+        /// 获取列表指定范围内的元素
+        pub fn lrange<'a, K: ToRedisArgs, RV: FromRedisValue>(&self, key: K, start: isize, stop: isize) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.lrange(key, start, stop).map_err(LabraError::from)
+        }
+
+        /// 在列表中添加一个或多个值
+        pub fn rpush<'a, K: ToRedisArgs, V: ToRedisArgs, RV: FromRedisValue>(&self, key: K, value: V) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.rpush(key, value).map_err(LabraError::from)
+        }
+
+        /// 在列表中添加一个或多个值
+        pub fn rpush_exists<'a, K: ToRedisArgs, V: ToRedisArgs, RV: FromRedisValue>(&self, key: K, value: V) -> LabradorResult {
+            let mut client = self.client_pool.get()?;
+            if !client.check_connection() {
+                return Err(LabraError::ApiError("error to get redis connection".to_string()))
+            }
+            client.rpush_exists(key, value).map_err(LabraError::from)
+        }
     }
 
 

From deb3561029c8e7916335f8d8ff91464259392758 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=BE=8D=E6=99=A8?= 
Date: Fri, 14 Oct 2022 12:27:53 +0800
Subject: [PATCH 13/13] =?UTF-8?q?=E5=8F=91=E5=B8=83=E7=89=88=E6=9C=AC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 Cargo.toml | 2 +-
 README.md  | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml
index 3775ca4..2eb2a2d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "labrador"
-version = "0.2.0"
+version = "0.2.1"
 authors = ["mrpan <1049058427@qq.com>"]
 edition = "2018"
 description = "Labrador - Mini thirdpart client for rust."
diff --git a/README.md b/README.md
index c84c84b..8fe8b56 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 # Labrador   [![Docs][docs-image]][docs-url] [![Build Status]][actions] [![Latest Version]][crates.io] [![labrador: rustc 1.13+]][Rust 1.13]
 
-[Build Status]: https://img.shields.io/docsrs/labrador/0.1.0?style=plastic
+[Build Status]: https://img.shields.io/docsrs/labrador/0.2.0?style=plastic
 [actions]: https://github.com/wslongchen/labrador/actions?query=branch%3Amaster
 [Latest Version]: https://img.shields.io/crates/v/labrador?style=plastic
 [crates.io]: https://crates.io/crates/labrador