Commit a0787e03 authored by shiyu's avatar shiyu

首页关注列表

parent 632570e2
......@@ -178,6 +178,12 @@
<version>2.2.6</version>
</dependency>
<!--IM-->
<dependency>
<groupId>com.github.tencentyun</groupId>
<artifactId>tls-sig-api-v2</artifactId>
<version>2.0</version>
</dependency>
</dependencies>
</project>
package com.wwdz.ch.core.consts;
/**
* 公众号枚举
*/
......@@ -39,6 +40,44 @@ public class OfficalAccountEnum {
public String getDes() {
return des;
}
}
/**
* 订阅消息的类型
*/
public enum SubscribeMsgTypeEnum {
COLLECT(1, "收藏"),
SHARE(2, "分享"),
FOLLOW(3, "关注"),
;
private int code;
private String des;
private String templetId;
SubscribeMsgTypeEnum(int code, String des) {
this.code = code;
this.des = des;
}
public static String getNameByCode(int code) {
for (OfficalAccountEnum.SubscribeMsgTypeEnum subscribeMsgTypeEnum : OfficalAccountEnum.SubscribeMsgTypeEnum.values()) {
if (code == subscribeMsgTypeEnum.getCode()) {
return subscribeMsgTypeEnum.getDes();
}
}
return null;
}
public int getCode() {
return code;
}
public String getDes() {
return des;
}
}
}
package com.wwdz.ch.core.entity;
import java.util.Map;
public interface AbstractSubscribeMsg {
Map<String, Object> getTempletParam();
String getTempletId();
}
package com.wwdz.ch.core.entity;
import com.xxdxxs.entity.Entity;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
/**
* 订阅号收藏通知模板
*/
@Data
public class OfficialAccountCollectSubscribeMsg implements Entity, AbstractSubscribeMsg {
private static final String templetId = "b5K4kTJ0wtOz953y60X_jm6Tjr6GFV2bfVaGr-dc0sM";
/**
* 收藏的用户
*/
private String collectUser;
/**
* 收藏时间
*/
private String collectTime;
/**
* 收藏帖子
*/
private String collectContent;
/**
* 温馨提示
*/
private String tip;
@Override
public Map<String, Object> getTempletParam(){
Map<String, Object> map = new HashMap<>();
map.put("thing8.DATA", new HashMap(){{put("value", collectUser);}});
map.put("time2.DATA", new HashMap(){{put("value", collectTime);}});
map.put("thing3.DATA", new HashMap(){{put("value", collectContent);}});
map.put("thing6.DATA", new HashMap(){{put("value", tip);}});
return map;
}
@Override
public String getTempletId() {
return templetId;
}
}
package com.wwdz.ch.core.entity;
import com.xxdxxs.entity.Entity;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
/**
* 订阅号关注通知模板
*/
@Data
public class OfficialAccountFollowSubscribeMsg implements Entity, AbstractSubscribeMsg {
private static final String templetId = "glJE6ftZq43kINzaxfKW4xTbyLgJYZfgvketYzAHkS4";
/**
* 关注的用户
*/
private String followUser;
/**
* 关注时间
*/
private String followTime;
/**
* 温馨提示
*/
private String tip;
@Override
public Map<String, Object> getTempletParam(){
Map<String, Object> map = new HashMap<>();
map.put("name1.DATA", new HashMap(){{put("value", followUser);}});
map.put("time2.DATA", new HashMap(){{put("value", followTime);}});
map.put("thing3.DATA", new HashMap(){{put("value", tip);}});
return map;
}
@Override
public String getTempletId() {
return templetId;
}
}
package com.wwdz.ch.core.entity;
import com.xxdxxs.entity.Entity;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
/**
* 订阅号分享通知模板
*/
@Data
public class OfficialAccountShareSubscribeMsg implements Entity, AbstractSubscribeMsg {
private static final String templetId = "0nWRthCjrsXOYAMfaP7FtuULdeNSA-mP7WXfMjEpBm0";
/**
* 分享的用户
*/
private String shareUser;
/**
* 分享时间
*/
private String shareTime;
/**
* 分享帖子标题
*/
private String shareTitle;
@Override
public Map<String, Object> getTempletParam(){
Map<String, Object> map = new HashMap<>();
map.put("thing1.DATA", new HashMap(){{put("value", shareUser);}});
map.put("data2.DATA", new HashMap(){{put("value", shareTime);}});
map.put("thing3.DATA", new HashMap(){{put("value", shareTitle);}});
return map;
}
@Override
public String getTempletId() {
return templetId;
}
}
......@@ -7,6 +7,11 @@ dts:
mch-id: 1538666666
mch-key: 1538xxxxxx-1538xxxxxx-1538xxxxxx
notify-url: https://test.dtsshop.com/demo/order/dtsNotify
get-access-token-url: https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s
#公众号消息点击后跳转到小程序页面的链接
msg-url: pages/im/index
# 商户证书文件路径
# 请参考“商户证书”一节 https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=4_3
key-path: xxxxx
......@@ -15,6 +20,7 @@ dts:
officialaccount-secret: daecb3de53841fe1a787bc9849ac3406
officialaccount-token: Uu6GHYOiP1xJ6GaBMyXfnITufAGJTe3Q
officialaccount-key: noeN1amYRvzrVmE2GnC5VnsIfgPEmv6bhX54wJOARlM
get-officialaccount-accesstoken-url: https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential
#通知相关配置
notify:
......
......@@ -6,6 +6,11 @@ dts:
mch-id: 1538666666
mch-key: 1538xxxxxx-1538xxxxxx-1538xxxxxx
notify-url: https://test.dtsshop.com/demo/order/dtsNotify
get-access-token-url: POST https://api.weixin.qq.com/cgi-bin/stable_token?grant_type=client_credential&appid=%s&secret=%s
#公众号消息点击后跳转到小程序页面的链接
msg-url: pages/im/index
# 商户证书文件路径
# 请参考“商户证书”一节 https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=4_3
key-path: xxxxx
......@@ -14,6 +19,7 @@ dts:
officialaccount-secret: daecb3de53841fe1a787bc9849ac3406
officialaccount-token: Uu6GHYOiP1xJ6GaBMyXfnITufAGJTe3Q
officialaccount-key: noeN1amYRvzrVmE2GnC5VnsIfgPEmv6bhX54wJOARlM
get-officialaccount-accesstoken-url: https://api.weixin.qq.com/cgi-bin/stable_token?grant_type=client_credential
#通知相关配置
notify:
......
......@@ -6,12 +6,11 @@ public interface OfficialAccountSubscribeRecordDao {
boolean insert(OfficialAccountSubscribeRecord officialAccountSubscribeRecord);
boolean update(OfficialAccountSubscribeRecord officialAccountSubscribeRecord);
boolean isExisted(String openid);
boolean isSubscribed(String openid);
OfficialAccountSubscribeRecord findOne(String openid);
OfficialAccountSubscribeRecord findOne(String unionid);
}
package com.wwdz.ch.db.domain;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
......@@ -10,7 +9,7 @@ import lombok.Data;
/**
* @author shiyu
* @date 2023/10/11
* @date 2023/10/16
*/
@Data
public class AiAssistant implements Entity {
......@@ -26,6 +25,11 @@ public class AiAssistant implements Entity {
*/
private String name;
/**
* 简介
*/
private String profile;
/**
* 类型
*/
......@@ -47,6 +51,7 @@ public class AiAssistant implements Entity {
sb.append(", id=").append(id);
sb.append(", avatar=").append(avatar);
sb.append(", name=").append(name);
sb.append(", profile=").append(profile);
sb.append(", type=").append(type);
sb.append(", createTime=").append(createTime);
sb.append(", serialVersionUID=").append(serialVersionUID);
......@@ -69,6 +74,7 @@ public class AiAssistant implements Entity {
return (this.getId() == null ? other.getId() == null : this.getId().equals(other.getId()))
&& (this.getAvatar() == null ? other.getAvatar() == null : this.getAvatar().equals(other.getAvatar()))
&& (this.getName() == null ? other.getName() == null : this.getName().equals(other.getName()))
&& (this.getProfile() == null ? other.getProfile() == null : this.getProfile().equals(other.getProfile()))
&& (this.getType() == null ? other.getType() == null : this.getType().equals(other.getType()))
&& (this.getCreateTime() == null ? other.getCreateTime() == null : this.getCreateTime().equals(other.getCreateTime()));
}
......@@ -80,6 +86,7 @@ public class AiAssistant implements Entity {
result = prime * result + ((getId() == null) ? 0 : getId().hashCode());
result = prime * result + ((getAvatar() == null) ? 0 : getAvatar().hashCode());
result = prime * result + ((getName() == null) ? 0 : getName().hashCode());
result = prime * result + ((getProfile() == null) ? 0 : getProfile().hashCode());
result = prime * result + ((getType() == null) ? 0 : getType().hashCode());
result = prime * result + ((getCreateTime() == null) ? 0 : getCreateTime().hashCode());
return result;
......@@ -96,6 +103,7 @@ public class AiAssistant implements Entity {
id("id", "id", "INTEGER", false),
avatar("avatar", "avatar", "VARCHAR", false),
name("name", "name", "VARCHAR", true),
profile("profile", "profile", "VARCHAR", false),
type("type", "type", "INTEGER", true),
createTime("create_time", "createTime", "TIMESTAMP", false);
......
......@@ -564,6 +564,148 @@ public class AiAssistantExample {
return (Criteria) this;
}
public Criteria andProfileIsNull() {
addCriterion("profile is null");
return (Criteria) this;
}
public Criteria andProfileIsNotNull() {
addCriterion("profile is not null");
return (Criteria) this;
}
public Criteria andProfileEqualTo(String value) {
addCriterion("profile =", value, "profile");
return (Criteria) this;
}
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table ai_assistant
*
* @mbg.generated
* @project https://github.com/itfsw/mybatis-generator-plugin
*/
public Criteria andProfileEqualToColumn(AiAssistant.Column column) {
addCriterion(new StringBuilder("profile = ").append(column.getEscapedColumnName()).toString());
return (Criteria) this;
}
public Criteria andProfileNotEqualTo(String value) {
addCriterion("profile <>", value, "profile");
return (Criteria) this;
}
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table ai_assistant
*
* @mbg.generated
* @project https://github.com/itfsw/mybatis-generator-plugin
*/
public Criteria andProfileNotEqualToColumn(AiAssistant.Column column) {
addCriterion(new StringBuilder("profile <> ").append(column.getEscapedColumnName()).toString());
return (Criteria) this;
}
public Criteria andProfileGreaterThan(String value) {
addCriterion("profile >", value, "profile");
return (Criteria) this;
}
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table ai_assistant
*
* @mbg.generated
* @project https://github.com/itfsw/mybatis-generator-plugin
*/
public Criteria andProfileGreaterThanColumn(AiAssistant.Column column) {
addCriterion(new StringBuilder("profile > ").append(column.getEscapedColumnName()).toString());
return (Criteria) this;
}
public Criteria andProfileGreaterThanOrEqualTo(String value) {
addCriterion("profile >=", value, "profile");
return (Criteria) this;
}
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table ai_assistant
*
* @mbg.generated
* @project https://github.com/itfsw/mybatis-generator-plugin
*/
public Criteria andProfileGreaterThanOrEqualToColumn(AiAssistant.Column column) {
addCriterion(new StringBuilder("profile >= ").append(column.getEscapedColumnName()).toString());
return (Criteria) this;
}
public Criteria andProfileLessThan(String value) {
addCriterion("profile <", value, "profile");
return (Criteria) this;
}
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table ai_assistant
*
* @mbg.generated
* @project https://github.com/itfsw/mybatis-generator-plugin
*/
public Criteria andProfileLessThanColumn(AiAssistant.Column column) {
addCriterion(new StringBuilder("profile < ").append(column.getEscapedColumnName()).toString());
return (Criteria) this;
}
public Criteria andProfileLessThanOrEqualTo(String value) {
addCriterion("profile <=", value, "profile");
return (Criteria) this;
}
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table ai_assistant
*
* @mbg.generated
* @project https://github.com/itfsw/mybatis-generator-plugin
*/
public Criteria andProfileLessThanOrEqualToColumn(AiAssistant.Column column) {
addCriterion(new StringBuilder("profile <= ").append(column.getEscapedColumnName()).toString());
return (Criteria) this;
}
public Criteria andProfileLike(String value) {
addCriterion("profile like", value, "profile");
return (Criteria) this;
}
public Criteria andProfileNotLike(String value) {
addCriterion("profile not like", value, "profile");
return (Criteria) this;
}
public Criteria andProfileIn(List<String> values) {
addCriterion("profile in", values, "profile");
return (Criteria) this;
}
public Criteria andProfileNotIn(List<String> values) {
addCriterion("profile not in", values, "profile");
return (Criteria) this;
}
public Criteria andProfileBetween(String value1, String value2) {
addCriterion("profile between", value1, value2, "profile");
return (Criteria) this;
}
public Criteria andProfileNotBetween(String value1, String value2) {
addCriterion("profile not between", value1, value2, "profile");
return (Criteria) this;
}
public Criteria andTypeIsNull() {
addCriterion("`type` is null");
return (Criteria) this;
......
......@@ -47,10 +47,10 @@ public class OfficialAccountSubscribeRecordDaoImpl implements OfficialAccountSub
}
@Override
public OfficialAccountSubscribeRecord findOne(String openid) {
public OfficialAccountSubscribeRecord findOne(String unionid) {
OfficialAccountSubscribeRecordExample example = new OfficialAccountSubscribeRecordExample();
OfficialAccountSubscribeRecordExample.Criteria criteria = example.createCriteria();
criteria.andOpenidEqualTo(openid);
criteria.andUnionidEqualTo(unionid);
return officialAccountSubscribeRecordMapper.selectOneByExample(example);
}
}
......@@ -29,6 +29,7 @@ public class UserItemsRelationDaoImpl implements UserItemsRelationDao {
return usersItemsRelationMapper.selectByExample(example);
}
@Override
public boolean isExisted(Long userId, Long itemId) {
UserItemsRelationExample example = new UserItemsRelationExample();
......
......@@ -5,6 +5,7 @@
<id column="id" jdbcType="INTEGER" property="id" />
<result column="avatar" jdbcType="VARCHAR" property="avatar" />
<result column="name" jdbcType="VARCHAR" property="name" />
<result column="profile" jdbcType="VARCHAR" property="profile" />
<result column="type" jdbcType="INTEGER" property="type" />
<result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
</resultMap>
......@@ -67,7 +68,7 @@
</where>
</sql>
<sql id="Base_Column_List">
id, avatar, `name`, `type`, create_time
id, avatar, `name`, profile, `type`, create_time
</sql>
<select id="selectByExample" parameterType="com.wwdz.ch.db.domain.AiAssistantExample" resultMap="BaseResultMap">
select
......@@ -96,13 +97,13 @@
distinct
</if>
<choose>
<when test="selective != null and selective.length > 0">
<when test="selective != null and selective.length &gt; 0">
<foreach collection="selective" item="column" separator=",">
${column.escapedColumnName}
</foreach>
</when>
<otherwise>
id, avatar, `name`, `type`, create_time
id, avatar, `name`, profile, `type`, create_time
</otherwise>
</choose>
from ai_assistant
......@@ -127,13 +128,13 @@
-->
select
<choose>
<when test="selective != null and selective.length > 0">
<when test="selective != null and selective.length &gt; 0">
<foreach collection="selective" item="column" separator=",">
${column.escapedColumnName}
</foreach>
</when>
<otherwise>
id, avatar, `name`, `type`, create_time
id, avatar, `name`, profile, `type`, create_time
</otherwise>
</choose>
from ai_assistant
......@@ -153,10 +154,10 @@
<selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
SELECT LAST_INSERT_ID()
</selectKey>
insert into ai_assistant (avatar, `name`, `type`,
create_time)
values (#{avatar,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR}, #{type,jdbcType=INTEGER},
#{createTime,jdbcType=TIMESTAMP})
insert into ai_assistant (avatar, `name`, profile,
`type`, create_time)
values (#{avatar,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR}, #{profile,jdbcType=VARCHAR},
#{type,jdbcType=INTEGER}, #{createTime,jdbcType=TIMESTAMP})
</insert>
<insert id="insertSelective" parameterType="com.wwdz.ch.db.domain.AiAssistant">
<selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
......@@ -170,6 +171,9 @@
<if test="name != null">
`name`,
</if>
<if test="profile != null">
profile,
</if>
<if test="type != null">
`type`,
</if>
......@@ -184,6 +188,9 @@
<if test="name != null">
#{name,jdbcType=VARCHAR},
</if>
<if test="profile != null">
#{profile,jdbcType=VARCHAR},
</if>
<if test="type != null">
#{type,jdbcType=INTEGER},
</if>
......@@ -210,6 +217,9 @@
<if test="record.name != null">
`name` = #{record.name,jdbcType=VARCHAR},
</if>
<if test="record.profile != null">
profile = #{record.profile,jdbcType=VARCHAR},
</if>
<if test="record.type != null">
`type` = #{record.type,jdbcType=INTEGER},
</if>
......@@ -226,6 +236,7 @@
set id = #{record.id,jdbcType=INTEGER},
avatar = #{record.avatar,jdbcType=VARCHAR},
`name` = #{record.name,jdbcType=VARCHAR},
profile = #{record.profile,jdbcType=VARCHAR},
`type` = #{record.type,jdbcType=INTEGER},
create_time = #{record.createTime,jdbcType=TIMESTAMP}
<if test="_parameter != null">
......@@ -241,6 +252,9 @@
<if test="name != null">
`name` = #{name,jdbcType=VARCHAR},
</if>
<if test="profile != null">
profile = #{profile,jdbcType=VARCHAR},
</if>
<if test="type != null">
`type` = #{type,jdbcType=INTEGER},
</if>
......@@ -254,6 +268,7 @@
update ai_assistant
set avatar = #{avatar,jdbcType=VARCHAR},
`name` = #{name,jdbcType=VARCHAR},
profile = #{profile,jdbcType=VARCHAR},
`type` = #{type,jdbcType=INTEGER},
create_time = #{createTime,jdbcType=TIMESTAMP}
where id = #{id,jdbcType=INTEGER}
......@@ -285,13 +300,13 @@
select
'true' as QUERYID,
<choose>
<when test="selective != null and selective.length > 0">
<when test="selective != null and selective.length &gt; 0">
<foreach collection="selective" item="column" separator=",">
${column.escapedColumnName}
</foreach>
</when>
<otherwise>
id, avatar, `name`, `type`, create_time
id, avatar, `name`, profile, `type`, create_time
</otherwise>
</choose>
from ai_assistant
......@@ -303,4 +318,5 @@
</if>
limit 1
</select>
</mapper>
\ No newline at end of file
package com.wwdz.ch.wx.api;
import com.wwdz.ch.core.entity.AbstractSubscribeMsg;
import com.wwdz.ch.core.util.OkHttpUtil;
import com.wwdz.ch.core.util.RedisUtils;
import com.wwdz.ch.wx.manager.WxLoginManager;
import com.xxdxxs.utils.JsonUtils;
import com.xxdxxs.utils.StringUtils;
import org.slf4j.Logger;
......@@ -10,6 +12,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
......@@ -20,22 +24,40 @@ public class OfficialAccountApi {
private static final Logger logger = LoggerFactory.getLogger(OfficialAccountApi.class);
private final static String GET_USER_BASE_INFO_URL = "https://api.weixin.qq.com/cgi-bin/user/info?lang=zh_CN";
private final static String SEND_MSG_URL = "https://api.weixin.qq.com/cgi-bin/message/template/send";
/**
* 测试环境和正式环境的url不同,相互隔离不会冲突
*/
@Value("${dts.wx.get-officialaccount-accesstoken-url}")
private String GET_OFFICIAL_ACCOUNT_ACCESSTOKEN_URL;
@Value("${dts.wx.officialaccount-appid}")
private String APPID;
private String OFFICIAL_ACCOUNT_APPID;
@Value("${dts.wx.officialaccount-secret}")
private String APPSECRET;
private final static String GET_ACCESSTOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential";
@Value("${dts.wx.app-id}")
private String APPLET_APPID;
@Value("${dts.wx.msg-url}")
private String MSG_URL;
private final static String GET_USER_BASE_INFO_URL = "https://api.weixin.qq.com/cgi-bin/user/info?lang=zh_CN";
@Autowired
RedisUtils redisUtils;
@Autowired
WxLoginManager wxLoginManager;
public String getAccessToken() {
//根据商户的appid从redis中获取商户的token
String token = redisUtils.hasKey(APPID)? redisUtils.get(APPID).toString() : null;
String token = redisUtils.hasKey(OFFICIAL_ACCOUNT_APPID)? redisUtils.get(OFFICIAL_ACCOUNT_APPID).toString() : null;
if (StringUtils.isEmpty(token)) {
token = refreshAccessToken();
}
......@@ -43,20 +65,20 @@ public class OfficialAccountApi {
}
public String refreshAccessToken() {
String url = GET_ACCESSTOKEN_URL + "&appid=" + APPID + "&secret=" + APPSECRET;
String url = GET_OFFICIAL_ACCOUNT_ACCESSTOKEN_URL + "&appid=" + OFFICIAL_ACCOUNT_APPID + "&secret=" + APPSECRET;
OkHttpUtil okHttpUtil = OkHttpUtil.builder().url(url);
okHttpUtil.get();
String responseStr = okHttpUtil.async();
logger.info("appid : {}, 获取token response :{}", APPID, responseStr);
logger.info("appid : {}, 获取token response :{}", OFFICIAL_ACCOUNT_APPID, responseStr);
if (!responseStr.contains("access_token")) {
String errorCode = JsonUtils.getValueByPath(responseStr, "errcode");
String errmsg = JsonUtils.getValueByPath(responseStr, "errmsg");
logger.error("appid : {}, 获取token出错, errorCode : {}, errmsg : {} ", APPID, errorCode, errmsg);
logger.error("appid : {}, 获取token出错, errorCode : {}, errmsg : {} ", OFFICIAL_ACCOUNT_APPID, errorCode, errmsg);
return null;
}
String token = JsonUtils.getValueByPath(responseStr, "access_token");
String expires_in = JsonUtils.getValueByPath(responseStr, "expires_in");
redisUtils.set(APPID, token, Long.valueOf(expires_in) - 10, TimeUnit.SECONDS);
redisUtils.set(OFFICIAL_ACCOUNT_APPID, token, Long.valueOf(expires_in) - 10, TimeUnit.SECONDS);
return token;
}
......@@ -88,5 +110,38 @@ public class OfficialAccountApi {
}
/**
* 发送公众号模板消息
* @param openId
* @return
*/
public boolean sendModelMsg(String openId, AbstractSubscribeMsg abstractSubscribeMsg) {
String token = getAccessToken();
String url = SEND_MSG_URL + "?access_token=" + token;
OkHttpUtil okHttpUtil = OkHttpUtil.builder().url(url);
Map<String, Object> map = new HashMap<>();
map.put("touser", openId);
map.put("template_id", abstractSubscribeMsg.getTempletId());
Map<String, Object> miniprogramMap = new HashMap<>();
String link = wxLoginManager.getLink(MSG_URL);
miniprogramMap.put("appid", APPLET_APPID);
miniprogramMap.put("pagepath", link);
map.put("miniprogram", miniprogramMap);
map.put("data", abstractSubscribeMsg.getTempletParam());
logger.info("============ 公众号模板消息内容 : {}", JsonUtils.fromMap(map));
okHttpUtil.addParams(map);
okHttpUtil.post(true);
String responseStr = okHttpUtil.async();
logger.info("openid : {}, 发送公众号订阅消息 response :{}", openId, responseStr);
String errorCode = JsonUtils.getValueByPath(responseStr, "errcode");
String errmsg = JsonUtils.getValueByPath(responseStr, "errmsg");
if (!"0".equals(errorCode)) {
logger.error("openid : {}, 发送公众号订阅消息, errorCode : {}, errmsg : {} ", openId, errorCode, errmsg);
return false;
}
logger.info("============= 发送公众号模板消息成功 ===========");
return true;
}
}
package com.wwdz.ch.wx.entity.vo;
import com.xxdxxs.entity.Entity;
import lombok.Data;
import java.util.Date;
@Data
public class AiAssistantVo implements Entity {
private Integer id;
/**
* 头像
*/
private String avatar;
/**
* 名称
*/
private String name;
/**
* 类型
*/
private Integer type;
/**
* 创建时间
*/
private Date createTime;
/**
* 是否已有会话
*/
private Boolean isHasChated;
}
package com.wwdz.ch.wx.im.ussrSig;
import java.util.Base64;
public class Base64URL {
public static byte[] base64EncodeUrl(byte[] input) {
byte[] base64 = Base64.getEncoder().encode(input);
for (int i = 0; i < base64.length; ++i)
switch (base64[i]) {
case '+':
base64[i] = '*';
break;
case '/':
base64[i] = '-';
break;
case '=':
base64[i] = '_';
break;
default:
break;
}
return base64;
}
}
package com.wwdz.ch.wx.im.ussrSig;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.security.*;
import java.util.Arrays;
import java.util.zip.Deflater;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.json.JSONObject;
public class TLSSigAPIv2 {
final private long sdkappid;
final private String key;
public TLSSigAPIv2(long sdkappid, String key) {
this.sdkappid = sdkappid;
this.key = key;
}
/**
* 【功能说明】用于签发 TRTC 和 IM 服务中必须要使用的 UserSig 鉴权票据
* <p>
* 【参数说明】
*
* @param userid - 用户id,限制长度为32字节,只允许包含大小写英文字母(a-zA-Z)、数字(0-9)及下划线和连词符。
* @param expire - UserSig 票据的过期时间,单位是秒,比如 86400 代表生成的 UserSig 票据在一天后就无法再使用了。
* @return usersig -生成的签名
*/
/**
* Function: Used to issue UserSig that is required by the TRTC and IM services.
* <p>
* Parameter description:
*
* @param userid - User ID. The value can be up to 32 bytes in length and contain letters (a-z and A-Z), digits (0-9), underscores (_), and hyphens (-).
* @param expire - UserSig expiration time, in seconds. For example, 86400 indicates that the generated UserSig will expire one day after being generated.
* @return usersig - Generated signature.
*/
public String genUserSig(String userid, long expire) {
return genUserSig(userid, expire, null);
}
/**
* 【功能说明】
* 用于签发 TRTC 进房参数中可选的 PrivateMapKey 权限票据。
* PrivateMapKey 需要跟 UserSig 一起使用,但 PrivateMapKey 比 UserSig 有更强的权限控制能力:
* - UserSig 只能控制某个 UserID 有无使用 TRTC 服务的权限,只要 UserSig 正确,其对应的 UserID 可以进出任意房间。
* - PrivateMapKey 则是将 UserID 的权限控制的更加严格,包括能不能进入某个房间,能不能在该房间里上行音视频等等。
* 如果要开启 PrivateMapKey 严格权限位校验,需要在【实时音视频控制台】/【应用管理】/【应用信息】中打开“启动权限密钥”开关。
* <p>
* 【参数说明】
*
* @param userid - 用户id,限制长度为32字节,只允许包含大小写英文字母(a-zA-Z)、数字(0-9)及下划线和连词符。
* @param expire - PrivateMapKey 票据的过期时间,单位是秒,比如 86400 生成的 PrivateMapKey 票据在一天后就无法再使用了。
* @param roomid - 房间号,用于指定该 userid 可以进入的房间号
* @param privilegeMap - 权限位,使用了一个字节中的 8 个比特位,分别代表八个具体的功能权限开关:
* - 第 1 位:0000 0001 = 1,创建房间的权限
* - 第 2 位:0000 0010 = 2,加入房间的权限
* - 第 3 位:0000 0100 = 4,发送语音的权限
* - 第 4 位:0000 1000 = 8,接收语音的权限
* - 第 5 位:0001 0000 = 16,发送视频的权限
* - 第 6 位:0010 0000 = 32,接收视频的权限
* - 第 7 位:0100 0000 = 64,发送辅路(也就是屏幕分享)视频的权限
* - 第 8 位:1000 0000 = 200,接收辅路(也就是屏幕分享)视频的权限
* - privilegeMap == 1111 1111 == 255 代表该 userid 在该 roomid 房间内的所有功能权限。
* - privilegeMap == 0010 1010 == 42 代表该 userid 拥有加入房间和接收音视频数据的权限,但不具备其他权限。
* @return usersig - 生成带userbuf的签名
*/
/**
* Function:
* Used to issue PrivateMapKey that is optional for room entry.
* PrivateMapKey must be used together with UserSig but with more powerful permission control capabilities.
* - UserSig can only control whether a UserID has permission to use the TRTC service. As long as the UserSig is correct, the user with the corresponding UserID can enter or leave any room.
* - PrivateMapKey specifies more stringent permissions for a UserID, including whether the UserID can be used to enter a specific room and perform audio/video upstreaming in the room.
* To enable stringent PrivateMapKey permission bit verification, you need to enable permission key in TRTC console > Application Management > Application Info.
*
* Parameter description:
*
* @param userid - User ID. The value can be up to 32 bytes in length and contain letters (a-z and A-Z), digits (0-9), underscores (_), and hyphens (-).
* @param roomid - ID of the room to which the specified UserID can enter.
* @param expire - PrivateMapKey expiration time, in seconds. For example, 86400 indicates that the generated PrivateMapKey will expire one day after being generated.
* @param privilegeMap - Permission bits. Eight bits in the same byte are used as the permission switches of eight specific features:
* - Bit 1: 0000 0001 = 1, permission for room creation
* - Bit 2: 0000 0010 = 2, permission for room entry
* - Bit 3: 0000 0100 = 4, permission for audio sending
* - Bit 4: 0000 1000 = 8, permission for audio receiving
* - Bit 5: 0001 0000 = 16, permission for video sending
* - Bit 6: 0010 0000 = 32, permission for video receiving
* - Bit 7: 0100 0000 = 64, permission for substream video sending (screen sharing)
* - Bit 8: 1000 0000 = 200, permission for substream video receiving (screen sharing)
* - privilegeMap == 1111 1111 == 255: Indicates that the UserID has all feature permissions of the room specified by roomid.
* - privilegeMap == 0010 1010 == 42: Indicates that the UserID has only the permissions to enter the room and receive audio/video data.
* @return usersig - Generate signature with userbuf
*/
public String genPrivateMapKey(String userid, long expire, long roomid, long privilegeMap) {
byte[] userbuf = genUserBuf(userid, roomid, expire, privilegeMap, 0, ""); //生成userbuf
return genUserSig(userid, expire, userbuf);
}
/**
* 【功能说明】
* 用于签发 TRTC 进房参数中可选的 PrivateMapKey 权限票据。
* PrivateMapKey 需要跟 UserSig 一起使用,但 PrivateMapKey 比 UserSig 有更强的权限控制能力:
* - UserSig 只能控制某个 UserID 有无使用 TRTC 服务的权限,只要 UserSig 正确,其对应的 UserID 可以进出任意房间。
* - PrivateMapKey 则是将 UserID 的权限控制的更加严格,包括能不能进入某个房间,能不能在该房间里上行音视频等等。
* 如果要开启 PrivateMapKey 严格权限位校验,需要在【实时音视频控制台】/【应用管理】/【应用信息】中打开“启动权限密钥”开关。
* <p>
* 【参数说明】
*
* @param userid - 用户id,限制长度为32字节,只允许包含大小写英文字母(a-zA-Z)、数字(0-9)及下划线和连词符。
* @param expire - PrivateMapKey 票据的过期时间,单位是秒,比如 86400 生成的 PrivateMapKey 票据在一天后就无法再使用了。
* @param roomstr - 字符串房间号,用于指定该 userid 可以进入的房间号
* @param privilegeMap - 权限位,使用了一个字节中的 8 个比特位,分别代表八个具体的功能权限开关:
* - 第 1 位:0000 0001 = 1,创建房间的权限
* - 第 2 位:0000 0010 = 2,加入房间的权限
* - 第 3 位:0000 0100 = 4,发送语音的权限
* - 第 4 位:0000 1000 = 8,接收语音的权限
* - 第 5 位:0001 0000 = 16,发送视频的权限
* - 第 6 位:0010 0000 = 32,接收视频的权限
* - 第 7 位:0100 0000 = 64,发送辅路(也就是屏幕分享)视频的权限
* - 第 8 位:1000 0000 = 200,接收辅路(也就是屏幕分享)视频的权限
* - privilegeMap == 1111 1111 == 255 代表该 userid 在该 roomid 房间内的所有功能权限。
* - privilegeMap == 0010 1010 == 42 代表该 userid 拥有加入房间和接收音视频数据的权限,但不具备其他权限。
* @return usersig - 生成带userbuf的签名
*/
/**
* Function:
* Used to issue PrivateMapKey that is optional for room entry.
* PrivateMapKey must be used together with UserSig but with more powerful permission control capabilities.
* - UserSig can only control whether a UserID has permission to use the TRTC service. As long as the UserSig is correct, the user with the corresponding UserID can enter or leave any room.
* - PrivateMapKey specifies more stringent permissions for a UserID, including whether the UserID can be used to enter a specific room and perform audio/video upstreaming in the room.
* To enable stringent PrivateMapKey permission bit verification, you need to enable permission key in TRTC console > Application Management > Application Info.
*
* Parameter description:
*
*
* @param userid - User ID. The value can be up to 32 bytes in length and contain letters (a-z and A-Z), digits (0-9), underscores (_), and hyphens (-).
* @param roomid - ID of the room to which the specified UserID can enter.
* @param expire - PrivateMapKey expiration time, in seconds. For example, 86400 indicates that the generated PrivateMapKey will expire one day after being generated.
* @param privilegeMap - Permission bits. Eight bits in the same byte are used as the permission switches of eight specific features:
* - Bit 1: 0000 0001 = 1, permission for room creation
* - Bit 2: 0000 0010 = 2, permission for room entry
* - Bit 3: 0000 0100 = 4, permission for audio sending
* - Bit 4: 0000 1000 = 8, permission for audio receiving
* - Bit 5: 0001 0000 = 16, permission for video sending
* - Bit 6: 0010 0000 = 32, permission for video receiving
* - Bit 7: 0100 0000 = 64, permission for substream video sending (screen sharing)
* - Bit 8: 1000 0000 = 200, permission for substream video receiving (screen sharing)
* - privilegeMap == 1111 1111 == 255: Indicates that the UserID has all feature permissions of the room specified by roomid.
* - privilegeMap == 0010 1010 == 42: Indicates that the UserID has only the permissions to enter the room and receive audio/video data.
* @return usersig - Generate signature with userbuf
*/
public String genPrivateMapKeyWithStringRoomID(String userid, long expire, String roomstr, long privilegeMap) {
byte[] userbuf = genUserBuf(userid, 0, expire, privilegeMap, 0, roomstr); //生成userbuf
return genUserSig(userid, expire, userbuf);
}
private String hmacsha256(String identifier, long currTime, long expire, String base64Userbuf) {
String contentToBeSigned = "TLS.identifier:" + identifier + "\n"
+ "TLS.sdkappid:" + sdkappid + "\n"
+ "TLS.time:" + currTime + "\n"
+ "TLS.expire:" + expire + "\n";
if (null != base64Userbuf) {
contentToBeSigned += "TLS.userbuf:" + base64Userbuf + "\n";
}
try {
byte[] byteKey = key.getBytes(StandardCharsets.UTF_8);
Mac hmac = Mac.getInstance("HmacSHA256");
SecretKeySpec keySpec = new SecretKeySpec(byteKey, "HmacSHA256");
hmac.init(keySpec);
byte[] byteSig = hmac.doFinal(contentToBeSigned.getBytes(StandardCharsets.UTF_8));
return (Base64.getEncoder().encodeToString(byteSig)).replaceAll("\\s*", "");
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
return "";
}
}
private String genUserSig(String userid, long expire, byte[] userbuf) {
long currTime = System.currentTimeMillis() / 1000;
JSONObject sigDoc = new JSONObject();
sigDoc.put("TLS.ver", "2.0");
sigDoc.put("TLS.identifier", userid);
sigDoc.put("TLS.sdkappid", sdkappid);
sigDoc.put("TLS.expire", expire);
sigDoc.put("TLS.time", currTime);
String base64UserBuf = null;
if (null != userbuf) {
base64UserBuf = Base64.getEncoder().encodeToString(userbuf).replaceAll("\\s*", "");
sigDoc.put("TLS.userbuf", base64UserBuf);
}
String sig = hmacsha256(userid, currTime, expire, base64UserBuf);
if (sig.length() == 0) {
return "";
}
sigDoc.put("TLS.sig", sig);
Deflater compressor = new Deflater();
compressor.setInput(sigDoc.toString().getBytes(StandardCharsets.UTF_8));
compressor.finish();
byte[] compressedBytes = new byte[2048];
int compressedBytesLength = compressor.deflate(compressedBytes);
compressor.end();
return (new String(Base64URL.base64EncodeUrl(Arrays.copyOfRange(compressedBytes,
0, compressedBytesLength)))).replaceAll("\\s*", "");
}
public byte[] genUserBuf(String account, long dwAuthID, long dwExpTime,
long dwPrivilegeMap, long dwAccountType, String RoomStr) {
//视频校验位需要用到的字段,按照网络字节序放入buf中
/*
cVer unsigned char/1 版本号,填0
wAccountLen unsigned short /2 第三方自己的帐号长度
account wAccountLen 第三方自己的帐号字符
dwSdkAppid unsigned int/4 sdkappid
dwAuthID unsigned int/4 群组号码
dwExpTime unsigned int/4 过期时间 ,直接使用填入的值
dwPrivilegeMap unsigned int/4 权限位,主播0xff,观众0xab
dwAccountType unsigned int/4 第三方帐号类型
*/
//The fields required for the video check digit are placed in buf according to the network byte order.
/*
cVer unsigned char/1 Version number, fill in 0
wAccountLen unsigned short /2 Third party's own account length
account wAccountLen Third party's own account characters
dwSdkAppid unsigned int/4 sdkappid
dwAuthID unsigned int/4 group number
dwExpTime unsigned int/4 Expiration time , use the filled value directly
dwPrivilegeMap unsigned int/4 Permission bits, host 0xff, audience 0xab
dwAccountType unsigned int/4 Third-party account type
*/
int accountLength = account.length();
int roomStrLength = RoomStr.length();
int offset = 0;
int bufLength = 1 + 2 + accountLength + 20 ;
if (roomStrLength > 0) {
bufLength = bufLength + 2 + roomStrLength;
}
byte[] userbuf = new byte[bufLength];
//cVer
if (roomStrLength > 0) {
userbuf[offset++] = 1;
} else {
userbuf[offset++] = 0;
}
//wAccountLen
userbuf[offset++] = (byte) ((accountLength & 0xFF00) >> 8);
userbuf[offset++] = (byte) (accountLength & 0x00FF);
//account
for (; offset < 3 + accountLength; ++offset) {
userbuf[offset] = (byte) account.charAt(offset - 3);
}
//dwSdkAppid
userbuf[offset++] = (byte) ((sdkappid & 0xFF000000) >> 24);
userbuf[offset++] = (byte) ((sdkappid & 0x00FF0000) >> 16);
userbuf[offset++] = (byte) ((sdkappid & 0x0000FF00) >> 8);
userbuf[offset++] = (byte) (sdkappid & 0x000000FF);
//dwAuthId,房间号
//dwAuthId, room number
userbuf[offset++] = (byte) ((dwAuthID & 0xFF000000) >> 24);
userbuf[offset++] = (byte) ((dwAuthID & 0x00FF0000) >> 16);
userbuf[offset++] = (byte) ((dwAuthID & 0x0000FF00) >> 8);
userbuf[offset++] = (byte) (dwAuthID & 0x000000FF);
//expire,过期时间,当前时间 + 有效期(单位:秒)
//expire,Expiration time, current time + validity period (unit: seconds)
long currTime = System.currentTimeMillis() / 1000;
long expire = currTime + dwExpTime;
userbuf[offset++] = (byte) ((expire & 0xFF000000) >> 24);
userbuf[offset++] = (byte) ((expire & 0x00FF0000) >> 16);
userbuf[offset++] = (byte) ((expire & 0x0000FF00) >> 8);
userbuf[offset++] = (byte) (expire & 0x000000FF);
//dwPrivilegeMap,权限位
//dwPrivilegeMap,Permission bits
userbuf[offset++] = (byte) ((dwPrivilegeMap & 0xFF000000) >> 24);
userbuf[offset++] = (byte) ((dwPrivilegeMap & 0x00FF0000) >> 16);
userbuf[offset++] = (byte) ((dwPrivilegeMap & 0x0000FF00) >> 8);
userbuf[offset++] = (byte) (dwPrivilegeMap & 0x000000FF);
//dwAccountType,账户类型
//dwAccountType,account type
userbuf[offset++] = (byte) ((dwAccountType & 0xFF000000) >> 24);
userbuf[offset++] = (byte) ((dwAccountType & 0x00FF0000) >> 16);
userbuf[offset++] = (byte) ((dwAccountType & 0x0000FF00) >> 8);
userbuf[offset++] = (byte) (dwAccountType & 0x000000FF);
if (roomStrLength > 0) {
//roomStrLen
userbuf[offset++] = (byte) ((roomStrLength & 0xFF00) >> 8);
userbuf[offset++] = (byte) (roomStrLength & 0x00FF);
//roomStr
for (; offset < bufLength; ++offset) {
userbuf[offset] = (byte) RoomStr.charAt(offset - (bufLength - roomStrLength));
}
}
return userbuf;
}
}
\ No newline at end of file
......@@ -2,20 +2,47 @@ package com.wwdz.ch.wx.impl;
import com.wwdz.ch.core.type.Result;
import com.wwdz.ch.db.dao.AiAssistantDao;
import com.wwdz.ch.db.domain.AiAssistant;
import com.wwdz.ch.db.dto.request.AiAssistantRequestDto;
import com.wwdz.ch.wx.entity.vo.AiAssistantVo;
import com.wwdz.ch.wx.service.AiAssistantService;
import com.wwdz.ch.wx.web.AiAssistantController;
import com.xxdxxs.utils.EntityMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* ai助手
*/
@Service
public class AiAssistantServiceImpl implements AiAssistantService {
private static final Logger logger = LoggerFactory.getLogger(AiAssistantServiceImpl.class);
@Autowired
AiAssistantDao aiAssistantDao;
@Override
public Result findList(AiAssistantRequestDto dto) {
try {
List<AiAssistantVo> aiAssistantVos = new ArrayList<>();
List<AiAssistant> aiAssistantList = aiAssistantDao.findList(dto);
aiAssistantList.forEach(aiAssistant -> {
AiAssistantVo aiAssistantVo = new AiAssistantVo();
EntityMapper.copyAttribute(aiAssistant, aiAssistantVo);
aiAssistantVo.setIsHasChated(false);
aiAssistantVos.add(aiAssistantVo);
});
return Result.success(aiAssistantVos);
} catch (Exception e) {
logger.error("查询ai助手列表error : {}", e);
}
return null;
}
}
package com.wwdz.ch.wx.impl;
import com.wwdz.ch.core.type.Result;
import com.wwdz.ch.db.dao.AiDefaultQuestionDao;
import com.wwdz.ch.db.domain.AiAssistant;
import com.wwdz.ch.db.domain.AiDefaultQuestion;
import com.wwdz.ch.db.dto.request.AiDefaultQuestionRequestDto;
import com.wwdz.ch.wx.service.AiDefaultQuestionService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* ai助手默认问题
*/
@Service
public class AiDefaultQuestionServiceImpl implements AiDefaultQuestionService {
private static final Logger logger = LoggerFactory.getLogger(AiDefaultQuestionServiceImpl.class);
@Autowired
AiDefaultQuestionDao aiDefaultQuestionDao;
@Override
public List<AiDefaultQuestion> findList(AiDefaultQuestionRequestDto dto) {
List<AiDefaultQuestion> aiDefaultQuestionList = aiDefaultQuestionDao.findList(dto);
return null;
public Result findList(AiDefaultQuestionRequestDto dto) {
try {
List<AiDefaultQuestion> aiDefaultQuestionList = aiDefaultQuestionDao.findList(dto);
return Result.success(aiDefaultQuestionList);
} catch (Exception e) {
logger.error("查询ai默认问题列表error : {}", e);
}
return Result.failed();
}
}
......@@ -65,9 +65,9 @@ public class OfficialAccountSubscribeRecordServiceImpl implements OfficialAccoun
@Override
public Result findOne(String openid) {
public Result findOne(String unionid) {
try {
OfficialAccountSubscribeRecord officialAccountSubscribeRecord = officialAccountSubscribeRecordDao.findOne(openid);
OfficialAccountSubscribeRecord officialAccountSubscribeRecord = officialAccountSubscribeRecordDao.findOne(unionid);
return Result.success(officialAccountSubscribeRecord);
} catch (Exception e) {
logger.error("findOne error :{}", e);
......
......@@ -5,17 +5,20 @@ import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import com.alibaba.fastjson.JSON;
import com.wwdz.ch.core.util.OkHttpUtil;
import com.wwdz.ch.core.util.RedisUtils;
import com.wwdz.ch.wx.entity.request.UserRequestDto;
import com.xxdxxs.utils.JsonUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* 微信小程序服务
......@@ -24,14 +27,19 @@ import java.util.Map;
public class WxLoginManager {
private static final Logger logger = LoggerFactory.getLogger(WxLoginManager.class);
// 微信小程序appid(需要替换)
private static final String appid = "wx5b4409448bf2c72b";
// private static final String appid = "wxb2bc9d1be2d6345c";
// 微信小程序secret(需要替换)
private static final String secret = "6fee0d7b2197845fed0a3fe8f5a9bd66";
// private static final String secret = "fade0a145eea51cfc89a9e8808f28f04";
// 微信小程序获取access_token地址
private static final String GET_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";
@Value("${dts.wx.app-id}")
private String APPLET_APPID;
@Value("${dts.wx.app-secret}")
private String APPLET_APP_SECRET;
/**
* 微信小程序获取access_token地址
* 测试环境和正式环境的url不同,相互隔离不会冲突
*/
@Value("${dts.wx.app-secret}")
private String GET_ACCESS_TOKEN_URL;
// 微信小程序获取用户手机号地址
private static final String GET_PHONE_URL = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=%s";
// 微信小程序获取小程序码地址
......@@ -42,6 +50,9 @@ public class WxLoginManager {
@Autowired
private WxMaService wxMaService;
@Autowired
RedisUtils redisUtils;
/**
* 获取微信openid
* 为了公众号发送消息,这里实际获取的是unionid,同一用户小程序和公众号unionid相同
......@@ -119,25 +130,41 @@ public class WxLoginManager {
return phone;
}
public String getAccessToken() {
//根据商户的appid从redis中获取商户的token
String token = redisUtils.hasKey(APPLET_APPID)? redisUtils.get(APPLET_APPID).toString() : null;
if (com.xxdxxs.utils.StringUtils.isEmpty(token)) {
token = refreshAccessToken();
}
return token;
}
/**
* 获取调用凭证access_token
*
* @return
*/
public String getAccessToken() {
logger.info("开始获取access_token----------------");
public String refreshAccessToken() {
logger.info("开始获取小程序access_token----------------");
try {
String accessTokenUrl = String.format(GET_ACCESS_TOKEN_URL, appid, secret);
String accessTokenUrl = String.format(GET_ACCESS_TOKEN_URL, APPLET_APPID, APPLET_APP_SECRET);
String res = OkHttpUtil.builder().url(accessTokenUrl)
.get().sync();
logger.info("获取access_token结果:" + res);
logger.info("获取小程序access_token结果:" + res);
if (!res.contains("access_token")) {
logger.error("获取accessToken出错,返回结果是:" + res);
String errorCode = JsonUtils.getValueByPath(res, "errcode");
String errmsg = JsonUtils.getValueByPath(res, "errmsg");
logger.error("appid : {}, 获取小程序access_token出错, errorCode : {}, errmsg : {} ", APPLET_APPID, errorCode, errmsg);
return null;
}
return JsonUtils.getValueByPath(res, "access_token");
String token = JsonUtils.getValueByPath(res, "access_token");
String expires_in = JsonUtils.getValueByPath(res, "expires_in");
redisUtils.set(APPLET_APPID, token, Long.valueOf(expires_in) - 10, TimeUnit.SECONDS);
return token;
} catch (Exception e) {
logger.error("获取access_token失败", e);
logger.error("获取小程序access_token失败", e);
return null;
}
}
......
package com.wwdz.ch.wx.service;
import com.wwdz.ch.core.type.Result;
import com.wwdz.ch.db.domain.AiDefaultQuestion;
import com.wwdz.ch.db.dto.request.AiDefaultQuestionRequestDto;
......@@ -12,6 +13,6 @@ public interface AiDefaultQuestionService {
* @param dto
* @return
*/
List<AiDefaultQuestion> findList (AiDefaultQuestionRequestDto dto);
Result findList (AiDefaultQuestionRequestDto dto);
}
......@@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSON;
import com.wwdz.ch.core.consts.ResultCode;
import com.wwdz.ch.core.type.Result;
import com.wwdz.ch.db.dto.request.AiAssistantRequestDto;
import com.wwdz.ch.db.dto.request.AiDefaultQuestionRequestDto;
import com.wwdz.ch.wx.service.AiAssistantService;
import com.wwdz.ch.wx.service.AiDefaultQuestionService;
import io.swagger.annotations.ApiOperation;
......@@ -42,12 +43,12 @@ public class AiAssistantController {
@ApiOperation(value = "查询ai默认问题")
@PostMapping("/findDefaultQuestions")
public Result findDefaultQuestions(@RequestBody AiAssistantRequestDto dto) {
logger.info("【请求开始】查询ai助手列表,请求参数:{}", JSON.toJSONString(dto));
public Result findDefaultQuestions(@RequestBody AiDefaultQuestionRequestDto dto) {
logger.info("【请求开始】查询ai默认问题,请求参数:{}", JSON.toJSONString(dto));
if (dto.getType() == null) {
return Result.failed(ResultCode.PARAM_ERROR);
}
return aiAssistantService.findList(dto);
return aiDefaultQuestionService.findList(dto);
}
......
......@@ -20,4 +20,7 @@ public class ChatApiTest {
String question = "刀币的由来";
chatApi.chatWithModel(question);
}
}
\ No newline at end of file
package com.wwdz.ch.wx.api;
import com.wwdz.ch.core.entity.OfficialAccountFollowSubscribeMsg;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -24,4 +25,14 @@ public class OfficialAccountApiTest {
String openid = "oNPTN6QaVliZfKB53OzZRPBZr_r8";
officialAccountApi.getUnionidByOpenid(openid);
}
@Test
public void sendSubscribeMsg() {
String openId = "oNPTN6QaVliZfKB53OzZRPBZr_r8";
OfficialAccountFollowSubscribeMsg officialAccountFollowSubscribeMsg = new OfficialAccountFollowSubscribeMsg();
officialAccountFollowSubscribeMsg.setFollowUser("测试用户");
officialAccountFollowSubscribeMsg.setTip("测试帖子");
officialAccountFollowSubscribeMsg.setFollowTime("2023-10-16 06:00:11");
officialAccountApi.sendModelMsg(openId, officialAccountFollowSubscribeMsg);
}
}
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment