公众号订阅 用户
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
package com.czg.controller;
|
||||
|
||||
import com.czg.account.service.UserInfoService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 公众号 通知
|
||||
* @author ww
|
||||
* @description
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/notify")
|
||||
public class NotifyController {
|
||||
|
||||
private static final String SUCCESS = "SUCCESS";
|
||||
|
||||
@Resource
|
||||
private UserInfoService userInfoService;
|
||||
|
||||
@RequestMapping
|
||||
public String notify(@RequestBody String str) {
|
||||
log.info("公众号 通知:{}", str);
|
||||
return SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,10 @@ wx:
|
||||
secrete: 8492a7e8d55bbb1b57f5c8276ea1add0
|
||||
operationMsgTmpId: wFdoUG-dUT7bDRHq8bMJD9CF5TjyH9x_uJQgQByZqHg
|
||||
warnMsgTmpId: C08OUr80x6wGmUN1zpFhSQ3Sv7VF5vksdZigiEx2pD0
|
||||
|
||||
# 公众号
|
||||
ac:
|
||||
appId: wx1fb600d0f5ea6279
|
||||
secrete: b4c0534c9b5e6c84a7fe5c2078dff876
|
||||
|
||||
logging:
|
||||
config: classpath:logback.xml
|
||||
|
||||
@@ -11,5 +11,6 @@ import lombok.Data;
|
||||
@Data
|
||||
public class LoginTokenDTO {
|
||||
private String token;
|
||||
private String followIndex;
|
||||
private UserInfo userInfo;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import java.time.LocalDateTime;
|
||||
* @since 2025-02-11
|
||||
*/
|
||||
@Data
|
||||
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Table("tb_user_info")
|
||||
@@ -63,6 +62,10 @@ public class UserInfo implements Serializable {
|
||||
* 微信 openid
|
||||
*/
|
||||
private String wechatOpenId;
|
||||
/**
|
||||
* 微信公众号 openid
|
||||
*/
|
||||
private String wechatAcOpenId;
|
||||
|
||||
/**
|
||||
* 支付宝 openid
|
||||
@@ -83,6 +86,10 @@ public class UserInfo implements Serializable {
|
||||
* 登录密码
|
||||
*/
|
||||
private String password;
|
||||
// 微信公众号二维码有效时间
|
||||
private LocalDateTime acQrcodeValidTime;
|
||||
// 微信公众号二维码
|
||||
private String wechatAcQrcode;
|
||||
|
||||
/**
|
||||
* 支付密码
|
||||
|
||||
@@ -9,17 +9,57 @@ import lombok.Getter;
|
||||
@Getter
|
||||
public enum SysParamCodeEnum {
|
||||
|
||||
|
||||
// 微信公众号(用户订阅相关)
|
||||
WECHAT_AC_SECRETE("wechat_ac_secrete", "用户订阅公众号secret"),
|
||||
WECHAT_AC_APPID("wechat_ac_appid", "用户订阅公众号appid"),
|
||||
|
||||
// 公众号关注位置
|
||||
FOLLOW_INDEX("follow_index", "公众号关注位置 mine-我的 order-订单 eat-就餐"),
|
||||
|
||||
// 商家配置
|
||||
AC_DAY_COUNT("ac_day_count", "商家每日可创建次数"),
|
||||
|
||||
// 短信相关
|
||||
SMS_FEE("sms_fee", "短信费用"),
|
||||
ALI_SMS_KEY("ali_sms_key", "阿里云短信key"),
|
||||
ALI_SMS_TEMPLATE_CODE("ali_sms_template_code", "阿里云短信模板"),
|
||||
ALI_SMS_SECRET("ali_sms_secret", "阿里云短信secret"),
|
||||
|
||||
// 微信相关(公众号/小程序)
|
||||
WX_ACCOUNT_APP_ID("wx_account_app_id", "微信公众号appId"),
|
||||
WX_ACCOUNT_SECRETE("wx_account_secrete", "微信公众号密钥"),
|
||||
WX_MINI_APP_ID("wx_mini_app_id", "微信小程序appId"),
|
||||
WX_MINI_SECRETE("wx_mini_secrete", "微信小程序密钥"),
|
||||
|
||||
// 页面地址相关
|
||||
WX_MINI_VIP_URL("wx_mini_vip_url", "小程序会员页面地址"),
|
||||
TABLE_CODE_URL("table_code_url", "桌码生成路径"),
|
||||
CALL_PAGE_URL("call_page_url", "叫号页面地址"),
|
||||
SHOP_ORDER_PAY_BASE_URL("shop_order_pay_base_url", "店铺订单支付BaseUrl"),
|
||||
|
||||
// 支付相关(超掌柜)
|
||||
PAY_CZG_DOMAIN("pay_czg_domain", "超掌柜支付域名"),
|
||||
PAY_CZG_NOTIFY_URL("pay_czg_notify_url", "超掌柜支付回调地址"),
|
||||
PAY_CZG_REFUND_NOTIFY_URL("pay_czg_refund_notify_url", "超掌柜退款回调地址"),
|
||||
|
||||
// 阿里云OSS相关
|
||||
ALI_OSS_ACCESS_KEY("ali_oss_access_key", "阿里云oss_ACCESS_KEY"),
|
||||
ALI_OSS_ACCESS_SECRET("ali_oss_access_secret", "阿里云oss_secret"),
|
||||
ALI_OSS_ENDPOINT("ali_oss_endpoint", "阿里云endpoint"),
|
||||
ALI_OSS_ROLE_ARN("ali_oss_role_arn", "阿里云roleArn"),
|
||||
WX_MINI_VIP_URL("wx_mini_vip_url", "小程序会员页面地址"),
|
||||
TABLE_CODE_URL("table_code_url", "桌码生成路径"),
|
||||
|
||||
// 支付宝相关(小程序/网页)
|
||||
ALI_MINI_PUBLIC_KEY("ali_mini_public_key", "支付宝小程序公钥"),
|
||||
ALI_MINI_PRIVATE_KEY("ali_mini_private_key", "支付宝小程序支付私钥"),
|
||||
ALI_MINI_APP_ID("ali_mini_app_id", "支付宝小程序id"),
|
||||
ALI_GATEWAY("ali_gateway", "支付宝网关"),
|
||||
ALI_ENCRYPT_KEY("ali_encrypt_key", "阿里AES加密串"),
|
||||
ALI_ACCOUNT_PUBLIC_KEY("ali_account_public_key", "支付宝网页公钥"),
|
||||
ALI_ACCOUNT_PRIVATE_KEY("ali_account_private_key", "支付宝网页私钥"),
|
||||
ALI_ACCOUNT_APP_ID("ali_account_app_id", "支付宝网页appid"),
|
||||
// 平台信息
|
||||
PLATE_NAME("plate_name", "平台名称")
|
||||
|
||||
;
|
||||
|
||||
|
||||
@@ -20,12 +20,17 @@ import com.czg.exception.ApiNotPrintException;
|
||||
import com.czg.exception.CzgException;
|
||||
import com.czg.sa.MyStpLogic;
|
||||
import com.czg.sa.StpKit;
|
||||
import com.czg.service.account.util.AcAccountUtil;
|
||||
import com.czg.service.account.util.AlipayUtil;
|
||||
import com.czg.service.account.util.WechatAuthUtil;
|
||||
import com.czg.system.enums.SysParamCodeEnum;
|
||||
import com.czg.system.service.SysParamsService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.dubbo.config.annotation.DubboReference;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* @author Administrator
|
||||
@@ -36,9 +41,13 @@ public class UserAuthorizationServiceImpl implements UserAuthorizationService {
|
||||
@Resource
|
||||
private WechatAuthUtil wechatAuthUtil;
|
||||
@Resource
|
||||
private AcAccountUtil acAccountUtil;
|
||||
@Resource
|
||||
private AlipayUtil alipayUtil;
|
||||
@Resource
|
||||
private UserInfoService userInfoService;
|
||||
@DubboReference
|
||||
private SysParamsService paramsService;
|
||||
|
||||
@Override
|
||||
public String getOpenId(String code, String source) {
|
||||
@@ -117,8 +126,22 @@ public class UserAuthorizationServiceImpl implements UserAuthorizationService {
|
||||
|
||||
userInfo.setLastLoginTime(DateUtil.date().toLocalDateTime());
|
||||
userInfoService.saveOrUpdate(userInfo);
|
||||
initAc(userInfo);
|
||||
// StpKit.USER.login(userInfo.getId());
|
||||
StpKit.USER.login(userInfo.getId(), openId, null, null, null, MyStpLogic.LoginType.USER, false, "userMini", false);
|
||||
return new LoginTokenDTO(StpKit.USER.getTokenValue(), userInfo);
|
||||
String followIndex = paramsService.getSysParamValue(SysParamCodeEnum.FOLLOW_INDEX.getCode());
|
||||
return new LoginTokenDTO(StpKit.USER.getTokenValue(), followIndex, userInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化用户微信公众号二维码\
|
||||
*/
|
||||
private void initAc(UserInfo userInfo) {
|
||||
if (StrUtil.isBlank(userInfo.getWechatAcOpenId()) &&
|
||||
(userInfo.getAcQrcodeValidTime() == null || userInfo.getAcQrcodeValidTime().isBefore(LocalDateTime.now()))) {
|
||||
userInfo.setWechatAcQrcode(acAccountUtil.createQrCode(userInfo.getId()));
|
||||
userInfo.setAcQrcodeValidTime(LocalDateTime.now().plusDays(29));
|
||||
userInfoService.updateById(userInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
package com.czg.service.account.util;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.czg.service.RedisService;
|
||||
import com.czg.system.service.SysParamsService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.dubbo.config.annotation.DubboReference;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
/**
|
||||
* 微信公众号
|
||||
*
|
||||
* @author ww
|
||||
*/
|
||||
@Data
|
||||
@Slf4j
|
||||
@Component
|
||||
public class AcAccountUtil {
|
||||
@Resource
|
||||
@Lazy
|
||||
private RedisService redisService;
|
||||
@DubboReference
|
||||
private SysParamsService paramsService;
|
||||
|
||||
static LinkedHashMap<String, String> linkedHashMap = new LinkedHashMap<>();
|
||||
|
||||
static {
|
||||
linkedHashMap.put("40001", "获取 access_token 时 AppSecret 错误,或者 access_token 无效。请开发者认真比对 AppSecret 的正确性,或查看是否正在为恰当的公众号调用接口");
|
||||
linkedHashMap.put("40003", "不合法的 OpenID ,请开发者确认 OpenID (该用户)是否已关注公众号,或是否是其他公众号的 OpenID");
|
||||
linkedHashMap.put("40014", "不合法的 access_token ,请开发者认真比对 access_token 的有效性(如是否过期),或查看是否正在为恰当的公众号调用接口");
|
||||
linkedHashMap.put("40037", "不合法的 template_id");
|
||||
linkedHashMap.put("43101", "用户未订阅消息");
|
||||
linkedHashMap.put("43107", "订阅消息能力封禁");
|
||||
linkedHashMap.put("43108", "并发下发消息给同一个粉丝");
|
||||
linkedHashMap.put("45168", "命中敏感词");
|
||||
linkedHashMap.put("47003", "参数错误");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取临时二维码用于关注公众号 30天(即2592000秒)后过期
|
||||
*/
|
||||
public String createQrCode(Long userId) {
|
||||
String accessToken = Convert.toStr(redisService.get("accessToken"));
|
||||
if (StrUtil.isNotEmpty(accessToken)) {
|
||||
return accessToken;
|
||||
}
|
||||
JSONObject bodyJson = new JSONObject();
|
||||
//二维码有效时间(秒),最大2592000,仅临时二维码需要
|
||||
bodyJson.put("expire_seconds", "2592000");
|
||||
//二维码类型:QR_SCENE(临时整型)/QR_STR_SCENE(临时字符串)/QR_LIMIT_SCENE(永久整型)/QR_LIMIT_STR_SCENE(永久字符串)
|
||||
bodyJson.put("action_name", "QR_SCENE");
|
||||
JSONObject actionInfo = new JSONObject();
|
||||
JSONObject scene = new JSONObject();
|
||||
scene.put("scene_id", userId);
|
||||
actionInfo.put("scene", scene);
|
||||
bodyJson.put("action_info", actionInfo);
|
||||
|
||||
String resp = HttpUtil.post(StrUtil.format("https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token={}", getAccessToken()), bodyJson);
|
||||
/**
|
||||
* {
|
||||
* "ticket": "gQH47joAAAAAAAAAASxodHRwOi8vd2VpeGlu...",
|
||||
* "expire_seconds": 60,
|
||||
* "url": "http://weixin.qq.com/q/kZgfwMTm72WWPkovabbI"
|
||||
* }
|
||||
*/
|
||||
JSONObject respInfo = JSONObject.parseObject(resp);
|
||||
return respInfo.get("url").toString();
|
||||
}
|
||||
|
||||
|
||||
public String getAccessToken() {
|
||||
String accessToken = Convert.toStr(redisService.get("wx:ac:AccessToken"));
|
||||
if (StrUtil.isNotEmpty(accessToken)) {
|
||||
return accessToken;
|
||||
}
|
||||
String acAppId = paramsService.getSysParamValue("wechat_ac_appid");
|
||||
String acSecrete = paramsService.getSysParamValue("wechat_ac_secrete");
|
||||
String resp = HttpUtil.get(StrUtil.format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={}&secret={}", acAppId, acSecrete));
|
||||
JSONObject respInfo = JSONObject.parseObject(resp);
|
||||
if (!respInfo.containsKey("access_token")) {
|
||||
log.warn("公众号获取token失败, 响应内容: {}", resp);
|
||||
throw new RuntimeException(resp);
|
||||
}
|
||||
accessToken = respInfo.getString("access_token");
|
||||
int expiresIn = respInfo.getInteger("expires_in");
|
||||
redisService.set("wx:ac:AccessToken", accessToken, expiresIn - 10);
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
String resp = HttpUtil.get(StrUtil.format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={}&secret={}",
|
||||
"wx1fb600d0f5ea6279", "b4c0534c9b5e6c84a7fe5c2078dff876"));
|
||||
JSONObject respInfo = JSONObject.parseObject(resp);
|
||||
if (!respInfo.containsKey("access_token")) {
|
||||
log.warn("公众号获取token失败, 响应内容: {}", resp);
|
||||
throw new RuntimeException(resp);
|
||||
}
|
||||
String accessToken = respInfo.getString("access_token");
|
||||
JSONObject bodyJson = new JSONObject();
|
||||
//二维码有效时间(秒),最大2592000,仅临时二维码需要
|
||||
bodyJson.put("expire_seconds", "2592000");
|
||||
//二维码类型:QR_SCENE(临时整型)/QR_STR_SCENE(临时字符串)/QR_LIMIT_SCENE(永久整型)/QR_LIMIT_STR_SCENE(永久字符串)
|
||||
bodyJson.put("action_name", "QR_SCENE");
|
||||
JSONObject actionInfo = new JSONObject();
|
||||
JSONObject scene = new JSONObject();
|
||||
scene.put("scene_id", 3);
|
||||
actionInfo.put("scene", scene);
|
||||
bodyJson.put("action_info", actionInfo);
|
||||
System.out.println(bodyJson);
|
||||
|
||||
String resps = HttpUtil.post(StrUtil.format("https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token={}", accessToken), JSONObject.toJSONString(bodyJson));
|
||||
JSONObject respInfos = JSONObject.parseObject(resps);
|
||||
System.out.println(respInfos);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user