diff --git a/coolstore-partner-common/src/main/java/com/cool/store/constants/CommonConstants.java b/coolstore-partner-common/src/main/java/com/cool/store/constants/CommonConstants.java index e98b61ff8..bc8072e4a 100644 --- a/coolstore-partner-common/src/main/java/com/cool/store/constants/CommonConstants.java +++ b/coolstore-partner-common/src/main/java/com/cool/store/constants/CommonConstants.java @@ -22,11 +22,17 @@ public class CommonConstants { public static final int REFRESH_TOKEN_EXPIRE = 60*60*24*30; + public static final int THREE_DAY_SECONDS = 60*60*24*3; + /** * 系统用户id */ public static final String SYSTEM_USER_ID = "system"; public static final String COMMA = ","; + public static final String MOSAICS = "#"; + + public static final String WX_APP_SECRET_KEY = "wx_app_secret_key:{0}"; + public static final String MINI_PROGRAM_SESSION_KEY = "mini_program_session_key:{0}:{1}"; public static final int ZERO = 0; diff --git a/coolstore-partner-common/src/main/java/com/cool/store/enums/ErrorCodeEnum.java b/coolstore-partner-common/src/main/java/com/cool/store/enums/ErrorCodeEnum.java index 3d6e47184..822c423a5 100644 --- a/coolstore-partner-common/src/main/java/com/cool/store/enums/ErrorCodeEnum.java +++ b/coolstore-partner-common/src/main/java/com/cool/store/enums/ErrorCodeEnum.java @@ -35,6 +35,10 @@ public enum ErrorCodeEnum { ENTERPRISE_NOT_EXIST(1021020,"企业不存在",null), USER_NOT_EXIST(1021021,"用户不存在",null), USER_WAIT_AUDIT(1021018,"账号审核中,请联系企业管理员",null), + OPERATION_OVER_TIME(1021019, "您的操作过于频繁,休息一下~", null), + GET_APP_SECRET_ERROR(1021019, "获取secret异常", null), + WX_SERVICE_ERROR(1021020, "调用微信服务异常", null), + SESSION_KEY_ERROR(1021021, "sessionKey过期", null), ; diff --git a/coolstore-partner-common/src/main/java/com/cool/store/utils/AesUtil.java b/coolstore-partner-common/src/main/java/com/cool/store/utils/AesUtil.java new file mode 100644 index 000000000..5aef25b12 --- /dev/null +++ b/coolstore-partner-common/src/main/java/com/cool/store/utils/AesUtil.java @@ -0,0 +1,77 @@ +package com.cool.store.utils; + +import com.cool.store.exception.ServiceException; +import org.springframework.util.Assert; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.security.AlgorithmParameters; +import java.util.Arrays; +import java.util.Base64; + +/** + * @author zhangchenbiao + * @FileName: AesUtil + * @Description: + * @date 2023-05-29 14:28 + */ + +public class AesUtil { + + private static final Charset utf8 = StandardCharsets.UTF_8; + + + public static String genAesKey() { + return StringUtil.random(32); + } + + public static String encrypt(String content, String aesTextKey) { + return Base64.getEncoder().encodeToString(encrypt(content.getBytes(utf8), aesTextKey.getBytes(utf8))); + } + + public static String decrypt(String content, String aesTextKey) { + byte[] buffer = Base64.getDecoder().decode(content); + return new String(decrypt(buffer, aesTextKey.getBytes(utf8)), utf8); + } + + public static byte[] encrypt(byte[] content, byte[] aesKey) { + Assert.isTrue(aesKey.length == 32, "IllegalAesKey, aesKey's length must be 32"); + try { + Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); + SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES"); + IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16); + cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv); + return cipher.doFinal(Pkcs7Encoder.encode(content)); + } catch (Exception e) { + throw new ServiceException(e.getMessage()); + } + } + + public static byte[] decrypt(byte[] encrypted, byte[] aesKey) { + Assert.isTrue(aesKey.length == 32, "IllegalAesKey, aesKey's length must be 32"); + try { + Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); + SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES"); + IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16)); + cipher.init(Cipher.DECRYPT_MODE, keySpec, iv); + return Pkcs7Encoder.decode(cipher.doFinal(encrypted)); + } catch (Exception e) { + throw new ServiceException(e.getMessage()); + } + } + + public static String decryptWechat(String sessionKey, String encryptedData, String ivStr) { + try { + AlgorithmParameters params = AlgorithmParameters.getInstance("AES"); + params.init(new IvParameterSpec(Base64.getDecoder().decode(ivStr))); + Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); + cipher.init(2, new SecretKeySpec(Base64.getDecoder().decode(sessionKey), "AES"), params); + return new String(Pkcs7Encoder.decode(cipher.doFinal(Base64.getDecoder().decode(encryptedData))), StandardCharsets.UTF_8); + } catch (Exception var5) { + throw new ServiceException("AES解密失败"); + } + } +} diff --git a/coolstore-partner-common/src/main/java/com/cool/store/utils/Pkcs7Encoder.java b/coolstore-partner-common/src/main/java/com/cool/store/utils/Pkcs7Encoder.java new file mode 100644 index 000000000..074db0c70 --- /dev/null +++ b/coolstore-partner-common/src/main/java/com/cool/store/utils/Pkcs7Encoder.java @@ -0,0 +1,71 @@ +package com.cool.store.utils; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; + +/** + * @author zhangchenbiao + * @FileName: Pkcs7Encoder + * @Description: + * @date 2023-05-29 14:29 + */ +public class Pkcs7Encoder { + + private static int BLOCK_SIZE = 32; + private static final Charset CHARSET = StandardCharsets.UTF_8; + + /** + * 获得对明文进行补位填充的字节. + * + * @param count 需要进行填充补位操作的明文字节个数 + * @return 补齐用的字节数组 + */ + public static byte[] encode(int count) { + // 计算需要填充的位数 + int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE); + // 获得补位所用的字符 + char padChr = chr(amountToPad); + StringBuilder tmp = new StringBuilder(); + for (int index = 0; index < amountToPad; index++) { + tmp.append(padChr); + } + return tmp.toString().getBytes(CHARSET); + } + + public static byte[] encode(byte[] src) { + int count = src.length; + // 计算需要填充的位数 + int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE); + if (amountToPad == 0) { + amountToPad = BLOCK_SIZE; + } + // 获得补位所用的字符 + byte pad = (byte) (amountToPad & 0xFF); + byte[] pads = new byte[amountToPad]; + for (int index = 0; index < amountToPad; index++) { + pads[index] = pad; + } + int length = count + amountToPad; + byte[] dest = new byte[length]; + System.arraycopy(src, 0, dest, 0, count); + System.arraycopy(pads, 0, dest, count, amountToPad); + return dest; + } + + public static byte[] decode(byte[] decrypted) { + int pad = decrypted[decrypted.length - 1]; + if (pad < 1 || pad > BLOCK_SIZE) { + pad = 0; + } + if (pad > 0) { + return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad); + } + return decrypted; + } + + private static char chr(int a) { + byte target = (byte) (a & 0xFF); + return (char) target; + } +} \ No newline at end of file diff --git a/coolstore-partner-common/src/main/java/com/cool/store/utils/RedisUtilPool.java b/coolstore-partner-common/src/main/java/com/cool/store/utils/RedisUtilPool.java index 4326f6ce3..3cd7927e7 100644 --- a/coolstore-partner-common/src/main/java/com/cool/store/utils/RedisUtilPool.java +++ b/coolstore-partner-common/src/main/java/com/cool/store/utils/RedisUtilPool.java @@ -1372,7 +1372,16 @@ public class RedisUtilPool { return setnx.equals(1L) ; } }.getResult(); + } + public Boolean lock(String key){ + return new Executor(shardedJedisPool) { + @Override + Boolean execute() { + Long setnx = jedis.setnx(key, System.currentTimeMillis() + ""); + return setnx.equals(1L) ; + } + }.getResult(); } /** diff --git a/coolstore-partner-common/src/main/java/com/cool/store/utils/StringUtil.java b/coolstore-partner-common/src/main/java/com/cool/store/utils/StringUtil.java new file mode 100644 index 000000000..017beab97 --- /dev/null +++ b/coolstore-partner-common/src/main/java/com/cool/store/utils/StringUtil.java @@ -0,0 +1,223 @@ +package com.cool.store.utils; + +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; +import org.springframework.web.util.HtmlUtils; + +import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.concurrent.ThreadLocalRandom; +import java.util.regex.Pattern; + +/** + * @author zhangchenbiao + * @FileName: AesUtil + * @Description:字符串相关操作 + * @date 2023-05-29 14:28 + */ + +public class StringUtil extends org.apache.commons.lang3.StringUtils { + + private static final char UPPER_A = 'A'; + private static final char LOWER_A = 'a'; + private static final char UPPER_Z = 'Z'; + private static final char LOWER_Z = 'z'; + + private static final byte[] DIGITS = { + '0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', 'a', 'b', + 'c', 'd', 'e', 'f', 'g', 'h', + 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', + 'A', 'B', 'C', 'D', 'E', 'F', + 'G', 'H', 'I', 'J', 'K', 'L', + 'M', 'N', 'O', 'P', 'Q', 'R', + 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z' + }; + /** + * 特殊字符正则,sql特殊字符和空白符 + */ + private static final Pattern SPECIAL_CHARS_REGEX = Pattern.compile("[`'\"|/,;()-+*%#·•� \\s]"); + /** + * 随机字符串因子 + */ + private static final String INT_STR = "0123456789"; + private static final String STR_STR = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + private static final String ALL_STR = INT_STR + STR_STR; + + /** + * 首字母变小写 + * + * @param str 字符串 + * @return {String} + */ + public static String firstCharToLower(String str) { + char firstChar = str.charAt(0); + if (firstChar >= UPPER_A && firstChar <= UPPER_Z) { + char[] arr = str.toCharArray(); + arr[0] += (LOWER_A - UPPER_A); + return new String(arr); + } + return str; + } + + /** + * 首字母变大写 + * + * @param str 字符串 + * @return {String} + */ + public static String firstCharToUpper(String str) { + char firstChar = str.charAt(0); + if (firstChar >= LOWER_A && firstChar <= LOWER_Z) { + char[] arr = str.toCharArray(); + arr[0] -= (LOWER_A - UPPER_A); + return new String(arr); + } + return str; + } + + public static String appendParams(String url, Map params) { + if (StringUtil.isBlank(url)) { + return ""; + } else if (CollectionUtils.isEmpty(params)) { + return url.trim(); + } else { + StringBuilder sb = new StringBuilder(200); + params.forEach((k, v) -> sb.append(k).append("=").append(v).append("&")); + sb.deleteCharAt(sb.length() - 1); + url = url.trim(); + int length = url.length(); + int index = url.indexOf("?"); + if (index > -1) { + if ((length - 1) == index) { + url += sb.toString(); + } else { + url += "&" + sb.toString(); + } + } else { + url += "?" + sb.toString(); + } + return url; + } + } + + /** + * 生成uuid,采用 jdk 9 的形式,优化性能 + * + * @return UUID + */ + public static String getUUID() { + ThreadLocalRandom random = ThreadLocalRandom.current(); + long lsb = random.nextLong(); + long msb = random.nextLong(); + byte[] buf = new byte[32]; + formatUnsignedLong(lsb, buf, 20, 12); + formatUnsignedLong(lsb >>> 48, buf, 16, 4); + formatUnsignedLong(msb, buf, 12, 4); + formatUnsignedLong(msb >>> 16, buf, 8, 4); + formatUnsignedLong(msb >>> 32, buf, 0, 8); + return new String(buf, StandardCharsets.UTF_8); + } + + private static void formatUnsignedLong(long val, byte[] buf, int offset, int len) { + int charPos = offset + len; + int radix = 1 << 4; + int mask = radix - 1; + do { + buf[--charPos] = DIGITS[((int) val) & mask]; + val >>>= 4; + } while (charPos > offset); + } + + /** + * 转义HTML用于安全过滤 + * + * @param html html + * @return {String} + */ + public static String escapeHtml(String html) { + return HtmlUtils.htmlEscape(html); + } + + /** + * 清理字符串,清理出某些不可见字符和一些sql特殊字符 + * + * @param txt 文本 + * @return {String} + */ + @Nullable + public static String cleanText(@Nullable String txt) { + if (txt == null) { + return null; + } + return SPECIAL_CHARS_REGEX.matcher(txt).replaceAll(StringUtil.EMPTY); + } + + /** + * 获取标识符,用于参数清理 + * + * @param param 参数 + * @return 清理后的标识符 + */ + @Nullable + public static String cleanIdentifier(@Nullable String param) { + if (param == null) { + return null; + } + StringBuilder paramBuilder = new StringBuilder(); + for (int i = 0; i < param.length(); i++) { + char c = param.charAt(i); + if (Character.isJavaIdentifierPart(c)) { + paramBuilder.append(c); + } + } + return paramBuilder.toString(); + } + + /** + * 随机数生成 + * + * @param count 字符长度 + * @return 随机数 + */ + public static String random(int count) { + return random(count, RandomType.ALL); + } + + /** + * 随机数生成 + * + * @param count 字符长度 + * @param randomType 随机数类别 + * @return 随机数 + */ + public static String random(int count, RandomType randomType) { + if (count == 0) { + return ""; + } + Assert.isTrue(count > 0, "Requested random string length " + count + " is less than 0."); + final ThreadLocalRandom random = ThreadLocalRandom.current(); + char[] buffer = new char[count]; + for (int i = 0; i < count; i++) { + if (RandomType.INT == randomType) { + buffer[i] = INT_STR.charAt(random.nextInt(INT_STR.length())); + } else if (RandomType.STRING == randomType) { + buffer[i] = STR_STR.charAt(random.nextInt(STR_STR.length())); + } else { + buffer[i] = ALL_STR.charAt(random.nextInt(ALL_STR.length())); + } + } + return new String(buffer); + } + + public enum RandomType { + /** + * INT STRING ALL + */ + INT, STRING, ALL; + } +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/dto/wx/CodeSessionDTO.java b/coolstore-partner-model/src/main/java/com/cool/store/dto/wx/CodeSessionDTO.java new file mode 100644 index 000000000..333a8a6ba --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/dto/wx/CodeSessionDTO.java @@ -0,0 +1,23 @@ +package com.cool.store.dto.wx; + +import com.alibaba.fastjson.annotation.JSONField; +import lombok.Data; + +/** + * @author zhangchenbiao + * @FileName: CodeSessionDTO + * @Description: + * @date 2023-05-29 14:28 + */ +@Data +public class CodeSessionDTO extends WXBaseResultDTO{ + + @JSONField(name = "session_key") + private String sessionKey; + + @JSONField(name = "openid") + private String openid; + + @JSONField(name = "unionid") + private String unionId; +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/dto/wx/MiniProgramLoginDTO.java b/coolstore-partner-model/src/main/java/com/cool/store/dto/wx/MiniProgramLoginDTO.java new file mode 100644 index 000000000..704744652 --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/dto/wx/MiniProgramLoginDTO.java @@ -0,0 +1,27 @@ +package com.cool.store.dto.wx; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +/** + * @author zhangchenbiao + * @FileName: MiniProgramLoginDTO + * @Description: + * @date 2023-05-29 14:28 + */ +@Data +public class MiniProgramLoginDTO { + + @NotBlank(message = "appid不能为空") + private String appid; + + @NotBlank(message = "jsCode不能为空") + private String jsCode; + + @NotBlank(message = "用户encryptedData不能为空") + private String encryptedData; + + @NotBlank(message = "ivStr不能为空") + private String ivStr; +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/dto/wx/MiniProgramMsgDTO.java b/coolstore-partner-model/src/main/java/com/cool/store/dto/wx/MiniProgramMsgDTO.java new file mode 100644 index 000000000..6fa13281c --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/dto/wx/MiniProgramMsgDTO.java @@ -0,0 +1,27 @@ +package com.cool.store.dto.wx; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +/** + * @author zhangchenbiao + * @FileName: MiniProgramMsgDTO + * @Description: + * @date 2023-05-29 14:28 + */ +@Data +public class MiniProgramMsgDTO { + + @NotBlank(message = "appid不能为空") + private String appid; + + @NotBlank(message = "encryptedData不能为空") + private String encryptedData; + + @NotBlank(message = "ivStr不能为空") + private String ivStr; + + @NotBlank(message = "openid不能为空") + private String openid; +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/dto/wx/WXBaseResultDTO.java b/coolstore-partner-model/src/main/java/com/cool/store/dto/wx/WXBaseResultDTO.java new file mode 100644 index 000000000..b608a32dc --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/dto/wx/WXBaseResultDTO.java @@ -0,0 +1,27 @@ +package com.cool.store.dto.wx; + +import com.alibaba.fastjson.annotation.JSONField; +import lombok.Data; + +/** + * @author zhangchenbiao + * @FileName: WXBaseResultDTO + * @Description: + * @date 2023-05-29 14:52 + */ +@Data +public class WXBaseResultDTO { + + private static final String SUCCESS_CODE = "0"; + + @JSONField(name = "errcode") + private String errCode; + + @JSONField(name = "errmsg") + private String errMsg; + + public boolean isSuccess() { + return this.errCode == null || this.errCode.isEmpty() || this.errCode.equals("0"); + } + +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/vo/wx/CodeSessionVO.java b/coolstore-partner-model/src/main/java/com/cool/store/vo/wx/CodeSessionVO.java new file mode 100644 index 000000000..f182dad81 --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/vo/wx/CodeSessionVO.java @@ -0,0 +1,18 @@ +package com.cool.store.vo.wx; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + + +@Builder +@Data +@AllArgsConstructor +@NoArgsConstructor +public class CodeSessionVO { + + private String openid; + + private String unionId; +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/vo/wx/MiniProgramUserVO.java b/coolstore-partner-model/src/main/java/com/cool/store/vo/wx/MiniProgramUserVO.java new file mode 100644 index 000000000..01e9c882f --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/vo/wx/MiniProgramUserVO.java @@ -0,0 +1,18 @@ +package com.cool.store.vo.wx; + +import lombok.Data; + + +@Data +public class MiniProgramUserVO { + private String openId; + private String nickName; + private String gender; + private String language; + private String city; + private String province; + private String country; + private String avatarUrl; + private String unionId; + private String wxUnionId; +} diff --git a/coolstore-partner-service/src/main/java/com/cool/store/config/rest/RestTemplateConfig.java b/coolstore-partner-service/src/main/java/com/cool/store/config/rest/RestTemplateConfig.java index c1e0cc5e9..b3a6d584e 100644 --- a/coolstore-partner-service/src/main/java/com/cool/store/config/rest/RestTemplateConfig.java +++ b/coolstore-partner-service/src/main/java/com/cool/store/config/rest/RestTemplateConfig.java @@ -5,7 +5,10 @@ import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; /** - * @author dong_gui on 2020/5/20. + * @author zhangchenbiao + * @FileName: RestTemplateConfig + * @Description: + * @date 2023-05-29 14:29 */ @Configuration public class RestTemplateConfig { diff --git a/coolstore-partner-service/src/main/java/com/cool/store/http/WechatRest.java b/coolstore-partner-service/src/main/java/com/cool/store/http/WechatRest.java new file mode 100644 index 000000000..99eb8ba81 --- /dev/null +++ b/coolstore-partner-service/src/main/java/com/cool/store/http/WechatRest.java @@ -0,0 +1,42 @@ +package com.cool.store.http; + +import com.alibaba.fastjson.JSONObject; +import com.cool.store.dto.enterprise.EnterpriseUserDTO; +import com.cool.store.dto.wx.CodeSessionDTO; +import com.cool.store.enums.ErrorCodeEnum; +import com.cool.store.exception.ServiceException; +import com.cool.store.utils.RestTemplateUtil; +import com.coolstore.base.dto.ResultDTO; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; + +import java.util.Objects; + +/** + * @author zhangchenbiao + * @FileName: WechatRest + * @Description:微信api + * @date 2023-05-29 14:49 + */ +@Slf4j +@Service +public class WechatRest { + + public CodeSessionDTO miniProgramJsCodeSession(String appId, String secret, String jsCode){ + String url = "https://api.weixin.qq.com/sns/jscode2session"; + ResponseEntity responseEntity = null; + try { + responseEntity = RestTemplateUtil.loadGet(url, CodeSessionDTO.class); + log.info("url:{}, response:{}", url, JSONObject.toJSONString(responseEntity)); + if(Objects.nonNull(responseEntity.getBody()) && responseEntity.getBody().isSuccess()){ + return responseEntity.getBody(); + } + } catch (Exception e) { + log.info("调用微信服务异常{}", e); + throw new ServiceException(ErrorCodeEnum.WX_SERVICE_ERROR); + } + return null; + } + +} diff --git a/coolstore-partner-service/src/main/java/com/cool/store/service/WechatMiniAppService.java b/coolstore-partner-service/src/main/java/com/cool/store/service/WechatMiniAppService.java new file mode 100644 index 000000000..b96ab41fa --- /dev/null +++ b/coolstore-partner-service/src/main/java/com/cool/store/service/WechatMiniAppService.java @@ -0,0 +1,21 @@ +package com.cool.store.service; + +import com.cool.store.dto.wx.MiniProgramLoginDTO; +import com.cool.store.dto.wx.MiniProgramMsgDTO; +import com.cool.store.vo.wx.CodeSessionVO; +import com.cool.store.vo.wx.MiniProgramUserVO; + +/** + * @author zhangchenbiao + * @FileName: WechatMiniAppService + * @Description: + * @date 2023-05-29 14:28 + */ +public interface WechatMiniAppService { + + + CodeSessionVO miniProgramLogin(MiniProgramLoginDTO param); + + + MiniProgramUserVO queryMiniProgramUser(MiniProgramMsgDTO param); +} diff --git a/coolstore-partner-service/src/main/java/com/cool/store/service/impl/WechatMiniAppServiceImpl.java b/coolstore-partner-service/src/main/java/com/cool/store/service/impl/WechatMiniAppServiceImpl.java new file mode 100644 index 000000000..73215a72c --- /dev/null +++ b/coolstore-partner-service/src/main/java/com/cool/store/service/impl/WechatMiniAppServiceImpl.java @@ -0,0 +1,79 @@ +package com.cool.store.service.impl; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.aliyun.openservices.shade.org.apache.commons.lang3.StringUtils; +import com.cool.store.constants.CommonConstants; +import com.cool.store.dto.wx.CodeSessionDTO; +import com.cool.store.dto.wx.MiniProgramLoginDTO; +import com.cool.store.dto.wx.MiniProgramMsgDTO; +import com.cool.store.enums.ErrorCodeEnum; +import com.cool.store.exception.ServiceException; +import com.cool.store.http.WechatRest; +import com.cool.store.service.WechatMiniAppService; +import com.cool.store.utils.AesUtil; +import com.cool.store.utils.RedisUtilPool; +import com.cool.store.vo.wx.CodeSessionVO; +import com.cool.store.vo.wx.MiniProgramUserVO; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.text.MessageFormat; +import java.util.Objects; + +/** + * @author zhangchenbiao + * @FileName: WechatMiniAppServiceImpl + * @Description: + * @date 2023-05-29 14:29 + */ +@Slf4j +@Service +public class WechatMiniAppServiceImpl implements WechatMiniAppService { + + @Resource + private RedisUtilPool redisUtilPool; + @Resource + private WechatRest wechatRest; + + @Override + public CodeSessionVO miniProgramLogin(MiniProgramLoginDTO param) { + String jsCode = param.getJsCode(); + String lockKey = "codeSession:" + param.getAppid() + CommonConstants.MOSAICS + jsCode; + boolean lock = redisUtilPool.lock(lockKey); + if (!lock) { + throw new ServiceException(ErrorCodeEnum.OPERATION_OVER_TIME); + } + String appid = param.getAppid(); + String secret = redisUtilPool.getString(MessageFormat.format(CommonConstants.WX_APP_SECRET_KEY, appid)); + if(StringUtils.isBlank(secret)){ + throw new ServiceException(ErrorCodeEnum.GET_APP_SECRET_ERROR); + } + CodeSessionDTO codeSession = wechatRest.miniProgramJsCodeSession(appid, secret, jsCode); + String openid = codeSession.getOpenid(); + String sessionCacheKey = MessageFormat.format(CommonConstants.MINI_PROGRAM_SESSION_KEY, appid, openid); + redisUtilPool.setString(sessionCacheKey, codeSession.getSessionKey(), CommonConstants.THREE_DAY_SECONDS); + String unionId = codeSession.getUnionId(); + log.info("小程序登录:{}", unionId); + //todo 保存授权信息 判断是否第一次授权 + return CodeSessionVO.builder().openid(openid).unionId(unionId).build(); + } + + @Override + public MiniProgramUserVO queryMiniProgramUser(MiniProgramMsgDTO param) { + String sessionCacheKey = MessageFormat.format(CommonConstants.MINI_PROGRAM_SESSION_KEY, param.getAppid(), param.getOpenid()); + String sessionKey = redisUtilPool.getString(sessionCacheKey); + if (StringUtils.isBlank(sessionKey)) { + throw new ServiceException(ErrorCodeEnum.SESSION_KEY_ERROR); + } + log.info("sessionKey {}", sessionKey); + String decryptUser = AesUtil.decryptWechat(sessionKey, param.getEncryptedData(), param.getIvStr()); + log.info("解密用户信息:{}", decryptUser); + MiniProgramUserVO miniProgramUser = JSON.parseObject(decryptUser, MiniProgramUserVO.class); + if (Objects.isNull(miniProgramUser)) { + throw new ServiceException("获取小程序用户信息失败"); + } + return miniProgramUser; + } +} diff --git a/coolstore-partner-webc/src/main/java/com/cool/store/controller/MiniProgramAppController.java b/coolstore-partner-webc/src/main/java/com/cool/store/controller/MiniProgramAppController.java new file mode 100644 index 000000000..c66c6d7c4 --- /dev/null +++ b/coolstore-partner-webc/src/main/java/com/cool/store/controller/MiniProgramAppController.java @@ -0,0 +1,45 @@ +package com.cool.store.controller; + + +import com.cool.store.dto.wx.MiniProgramLoginDTO; +import com.cool.store.dto.wx.MiniProgramMsgDTO; +import com.cool.store.response.ResponseResult; +import com.cool.store.service.WechatMiniAppService; +import com.cool.store.vo.wx.CodeSessionVO; +import com.cool.store.vo.wx.MiniProgramUserVO; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.validation.Valid; + +/** + * @author zhangchenbiao + * @FileName: MiniProgramAppController + * @Description: + * @date 2023-05-29 14:28 + */ + +@Api(tags = "微信小程序app接口") +@RestController +@RequestMapping("/appApi/mini-program") +public class MiniProgramAppController { + + @Resource + private WechatMiniAppService wechatMiniAppService; + + @ApiOperation("小程序登录") + @PostMapping("/code/login") + public ResponseResult login(@RequestBody @Valid MiniProgramLoginDTO param) { + CodeSessionVO codeSessionVO = wechatMiniAppService.miniProgramLogin(param); + return ResponseResult.success(codeSessionVO); + } + + @ApiOperation("获取小程序用户信息") + @PostMapping("/user") + public ResponseResult queryMiniProgramUser(@RequestBody @Valid MiniProgramMsgDTO param) { + MiniProgramUserVO miniProgramUserVO = wechatMiniAppService.queryMiniProgramUser(param); + return ResponseResult.success(miniProgramUserVO); + } +}