diff --git a/api-mapper/src/main/java/com/heyu/api/data/dto/AccessTokenDTO.java b/api-mapper/src/main/java/com/heyu/api/data/dto/AccessTokenDTO.java new file mode 100644 index 0000000..1efa88d --- /dev/null +++ b/api-mapper/src/main/java/com/heyu/api/data/dto/AccessTokenDTO.java @@ -0,0 +1,17 @@ +package com.heyu.api.data.dto; + + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.NoArgsConstructor; + +@NoArgsConstructor +@Data +public class AccessTokenDTO { + + + @JsonProperty("access_token") + private String accessToken; + @JsonProperty("expires_in") + private Integer expiresIn; +} diff --git a/api-mapper/src/main/java/com/heyu/api/data/utils/HttpUtils.java b/api-mapper/src/main/java/com/heyu/api/data/utils/HttpUtils.java index 7c816f6..87994b8 100644 --- a/api-mapper/src/main/java/com/heyu/api/data/utils/HttpUtils.java +++ b/api-mapper/src/main/java/com/heyu/api/data/utils/HttpUtils.java @@ -24,9 +24,10 @@ import org.apache.http.util.EntityUtils; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; -import java.io.IOException; -import java.io.UnsupportedEncodingException; +import java.io.*; +import java.net.HttpURLConnection; import java.net.URI; +import java.net.URL; import java.net.URLEncoder; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; @@ -65,6 +66,46 @@ public class HttpUtils { return httpClient.execute(request); } + + + + + // 一、原生HttpURLConnection方式 + public static byte[] getWechatQrcodeByHttpURL(String url, Map body) { + HttpURLConnection httpURLConnection = null; + try { + httpURLConnection = (HttpURLConnection) new URL(url).openConnection(); + httpURLConnection.setRequestMethod("POST"); + // 发送POST请求必须设置如下两行 + httpURLConnection.setDoOutput(true); + httpURLConnection.setDoInput(true); + // 获取URLConnection对象对应的输出流 + PrintWriter printWriter = new PrintWriter(httpURLConnection.getOutputStream()); + // 发送请求参数 + printWriter.write(JSONObject.toJSONString(body)); + // flush输出流的缓冲 + printWriter.flush(); + //开始获取数据 + try (InputStream inputStream = httpURLConnection.getInputStream(); + ByteArrayOutputStream out = new ByteArrayOutputStream()) { + byte[] buffer = new byte[1024]; + int len = -1; + while ((len = inputStream.read(buffer)) != -1) { + out.write(buffer, 0, len); + } + return out.toByteArray(); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (httpURLConnection != null) { + httpURLConnection.disconnect(); + } + } + return null; + } + + /** * 发送POST方式请求 * @@ -169,6 +210,13 @@ public class HttpUtils { .setSocketTimeout(TIMEOUT_MSEC).build(); } + + + public static String doGet(String url) { + + return doGet(url,null); + } + /** * 发送GET方式请求 * @@ -219,6 +267,7 @@ public class HttpUtils { } + /** * post form diff --git a/api-mapper/src/main/java/com/heyu/api/data/utils/RedisUtils.java b/api-mapper/src/main/java/com/heyu/api/data/utils/RedisUtils.java index 52a54a1..440bedf 100644 --- a/api-mapper/src/main/java/com/heyu/api/data/utils/RedisUtils.java +++ b/api-mapper/src/main/java/com/heyu/api/data/utils/RedisUtils.java @@ -61,6 +61,13 @@ public class RedisUtils { public final static long thirty_minute = 30 * 60; + /** + * 默认过期时长,单位:秒 + */ + public final static long one_hours = 1* 60 * 60; + + + /** * 不设置过期时长 */ diff --git a/api-third/src/main/java/com/heyu/api/oss/OssFileUploadService.java b/api-third/src/main/java/com/heyu/api/oss/OssFileUploadService.java index c31e2b9..3c96305 100644 --- a/api-third/src/main/java/com/heyu/api/oss/OssFileUploadService.java +++ b/api-third/src/main/java/com/heyu/api/oss/OssFileUploadService.java @@ -94,4 +94,8 @@ public interface OssFileUploadService { public List> uploadImages(MultipartFile[] files); OssUploadResult uploadImageToOss(InputStream inputStream,String oss_buccket, String fileName, int fileSize); + + + + OssUploadResult uploadImageByBytes(byte[] bytes); } diff --git a/api-third/src/main/java/com/heyu/api/oss/OssFileUploadServiceImpl.java b/api-third/src/main/java/com/heyu/api/oss/OssFileUploadServiceImpl.java index 5e2122c..33dda39 100644 --- a/api-third/src/main/java/com/heyu/api/oss/OssFileUploadServiceImpl.java +++ b/api-third/src/main/java/com/heyu/api/oss/OssFileUploadServiceImpl.java @@ -48,7 +48,7 @@ public class OssFileUploadServiceImpl implements OssFileUploadService { public OssUploadResult uploadToOss(MultipartFile imageFile) { String fileName = imageFile.getOriginalFilename(); String contextType = this.getImageFileContentType(fileName); - String path = env + "/"; + String path = env + "/" ; String fileNameSuffix = DigestUtil.MD5_16(UUID.randomUUID().toString()); if (StringUtils.isNotBlank(contextType)) { fileNameSuffix = fileNameSuffix + fileName.substring(fileName.lastIndexOf(".")); @@ -96,6 +96,51 @@ public class OssFileUploadServiceImpl implements OssFileUploadService { return this.uploadFileToOss(imageFile, contextType, path, fileNameSuffix); } + + + + @Override + public OssUploadResult uploadImageByBytes(byte[] bytes) { + InputStream inputStream = null; + OssUploadResult result = new OssUploadResult(); + try { + + String suffix = ".jpeg"; + // 步骤 2: 使用字节数组创建 InputStream + inputStream = new ByteArrayInputStream(bytes); + // 生成文件名称 + String path = env + "/"; + + String fileName = DigestUtil.MD5_16(UUID.randomUUID().toString()) + suffix; + + //3 获取文件信息,为了上传 + // meta设置请求头 + ObjectMetadata meta = new ObjectMetadata(); + meta.setContentType(MimeTypeEnums.getContentType(suffix)); + //4 设置知道文件夹 + PutObjectResult pubResult = ossClient.putObject(oss_buccket, path + fileName, inputStream, meta); + + result.setSuccess(true); + result.setMsg("upload to oss succeed"); + result.setFileMd5(pubResult.getETag()); + result.setUrl(oss_url + path + fileName); + return result; + } catch (Exception e) { + log.error("upload to oss error", e); + } finally { + if(inputStream !=null){ + try { + inputStream.close(); + } catch (IOException e) { + log.error("inputStream to close error", e); + } + } + } + return null; + } + + + @Override public OssUploadResult uploadImageToOss(InputStream inputStream, String fileName, int fileSize) { OssUploadResult result = new OssUploadResult(); diff --git a/api-web/api-interface/src/main/java/com/heyu/api/controller/vv/AppQrCodeController.java b/api-web/api-interface/src/main/java/com/heyu/api/controller/vv/AppQrCodeController.java new file mode 100644 index 0000000..a4d7e34 --- /dev/null +++ b/api-web/api-interface/src/main/java/com/heyu/api/controller/vv/AppQrCodeController.java @@ -0,0 +1,113 @@ +package com.heyu.api.controller.vv; + + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.heyu.api.alibaba.request.vv.AppPromoterRequest; +import com.heyu.api.common.annotation.Describe; +import com.heyu.api.data.dto.AccessTokenDTO; +import com.heyu.api.data.utils.HttpUtils; +import com.heyu.api.data.utils.R; +import com.heyu.api.data.utils.RedisUtils; +import com.heyu.api.data.utils.StringUtils; +import com.heyu.api.oss.OssFileUploadService; +import com.heyu.api.oss.OssUploadResult; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.HashMap; +import java.util.Map; + +@Slf4j +@RestController +@RequestMapping("/app/qrcode") +public class AppQrCodeController { + + + public static final String wechatQrcodeUrl = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token="; + public static final String wechatAccessTokenUrl = "https://api.weixin.qq.com/cgi-bin/token?appid=%s&secret=%s&grant_type=client_credential"; + + @Value("${eb.config.weixin.pay.appid}") + private String appid; + + @Value("${eb.config.weixin.pay.appSecret}") + private String appSecret; + + + // 自行配置:正式版为 "release",体验版为 "trial",开发版为 "develop"。默认是正式版。 + private String envVersion = "trial"; + + @Autowired + private RedisUtils redisUtils; + + public static final String jsapi_acessToken = "jsapi_acessToken"; + + @Autowired + private OssFileUploadService ossFileUploadService; + + + /*** + * 列表 + * http://localhost:8888/app/qrcode/create + * + * https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/qrcode-link/qr-code/getUnlimitedQRCode.html + * + */ + @Describe("生成二维码") + @RequestMapping("/create") + public R create(@RequestBody AppPromoterRequest request) { + // 当前登录账号 + String accountId = "123456"; + AccessTokenDTO accessTokenDTO = null; + String acessTokenJson = redisUtils.get(jsapi_acessToken); + if (StringUtils.isNotEmpty(acessTokenJson)) { + accessTokenDTO = JSONObject.parseObject(acessTokenJson, AccessTokenDTO.class); + } else { + accessTokenDTO = getAccessToken(appid, appSecret); + redisUtils.set(jsapi_acessToken, JSON.toJSONString(accessTokenDTO), RedisUtils.one_hours); + } + String accessToken = accessTokenDTO.getAccessToken(); + + String url = wechatQrcodeUrl + accessToken; + + Map body = new HashMap<>(); + + // 场景码,请根据业务场景与【前端】约定 + // 最大32个可见字符,只支持数字,大小写英文以及部分特殊字符:!#$&'()*+,/:;=?@-._~,其它字符请自行编码为合法字符(因不支持%,中文无法使用 urlencode 处理,请使用其他编码方式) + body.put("scene", String.format("a=%s", accountId)); + + body.put("env_version", envVersion); + // body.put("page", envVersion); 默认是主页,页面 page,例如 pages/index/index,根路径前不要填加 /,不能携带参数(参数请放在scene字段里),如果不填写这个字段,默认跳主页面。scancode_time为系统保留参数,不允许配置 + + // 透明 + body.put("is_hyaline", true); // 默认是false,是否需要透明底色,为 true 时,生成透明底色的小程序 + + byte[] bytes = HttpUtils.getWechatQrcodeByHttpURL(url, body); + + if (bytes == null) { + return R.error("bytes is null"); + } + String error = new String(bytes); + if (error.contains("errcode")) { + return R.error(error); + } + + OssUploadResult ossUploadResult = ossFileUploadService.uploadImageByBytes(bytes); + return R.ok().setData(ossUploadResult); + } + + // 获取access_token + public AccessTokenDTO getAccessToken(String appid, String appSecret) { + String url = String.format(wechatAccessTokenUrl, appid, appSecret); + String responseBody = HttpUtils.doGet(url); + log.info("getAccessToken responseBody:{}", responseBody); + AccessTokenDTO accessTokenDTO = JSONObject.parseObject(responseBody, AccessTokenDTO.class); + return accessTokenDTO; + } + + +} diff --git a/api-web/api-interface/src/main/java/com/heyu/api/controller/vv/AppUserLoginController.java b/api-web/api-interface/src/main/java/com/heyu/api/controller/vv/AppUserLoginController.java index bf69341..ff29565 100644 --- a/api-web/api-interface/src/main/java/com/heyu/api/controller/vv/AppUserLoginController.java +++ b/api-web/api-interface/src/main/java/com/heyu/api/controller/vv/AppUserLoginController.java @@ -2,6 +2,7 @@ package com.heyu.api.controller.vv; import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; import com.heyu.api.alibaba.request.mm.VvAnonymousUserLoginRequest; import com.heyu.api.alibaba.request.mm.VvAppLoginRequest; import com.heyu.api.alibaba.request.vv.AppUserLoginRequest; @@ -41,6 +42,7 @@ public class AppUserLoginController { if (weiChatLoginDTO == null) { return R.error("code 失效"); } + VvBuyerEntity target = vvBuyerDao.selectVvBuyerOpenId(weiChatLoginDTO.getOpenid()); if (target == null) { target = new VvBuyerEntity(); @@ -48,6 +50,7 @@ public class AppUserLoginController { target.setIsTest(0); target.setCreateTimestamp(System.currentTimeMillis()); } + target.setSessionKey(weiChatLoginDTO.getSessionKey()); vvBuyerDao.insertOrUpdateVvBuyer(target); BuyerDTO buyerDTO = new BuyerDTO(); @@ -57,7 +60,7 @@ public class AppUserLoginController { String token = TokenUtils.generateToken(target.getId()); buyerDTO.setToken(token); // 默认24 小时 - redisUtils.set(token, JSON.toJSON(buyerDTO), RedisUtils.DEFAULT_EXPIRE); + redisUtils.set(token, JSON.toJSONString(buyerDTO), RedisUtils.DEFAULT_EXPIRE); return R.ok().put("buyer", buyerDTO); } @@ -77,7 +80,7 @@ public class AppUserLoginController { Random random = new Random(1000000L); String token = TokenUtils.generateToken(random.nextLong()); buyerDTO.setToken(token); - redisUtils.set(token, JSON.toJSON(buyerDTO), RedisUtils.thirty_minute); + redisUtils.set(token, JSON.toJSONString(buyerDTO), RedisUtils.thirty_minute); return R.ok().put("buyer", buyerDTO); } @@ -101,8 +104,28 @@ public class AppUserLoginController { String token = TokenUtils.generateToken(target.getId()); buyerDTO.setToken(token); // 默认24 小时 - redisUtils.set(token, JSON.toJSON(buyerDTO), RedisUtils.DEFAULT_EXPIRE); + redisUtils.set(token, JSON.toJSONString(buyerDTO), RedisUtils.DEFAULT_EXPIRE); return R.ok().put("buyer", buyerDTO); } + + + @RequestMapping("/getPhoneNumber") + public Object getPhoneNumber(@RequestBody VvAppLoginRequest request) { + + // String sessionKey = userRepository.findByOpenId(request.getOpenId()).getSessionKey(); // 从数据库中获取 sessionKey + + + // JSONObject phoneInfo = JSONObject.parseObject(WxMaCryptUtils.decrypt(sessionKey, request.getEncryptedData(), request.getIv())); // 解密获取手机号信息 + // String phoneNumber = phoneInfo.getString("phoneNumber"); + // 更新用户信息并存储到数据库 + + // 返回给前端的手机号信息 + JSONObject result = new JSONObject(); + result.put("phoneNumber", null); + return result; + } + + + } diff --git a/api-web/api-interface/src/main/resources/bootstrap.yml b/api-web/api-interface/src/main/resources/bootstrap.yml index 809863e..044446d 100644 --- a/api-web/api-interface/src/main/resources/bootstrap.yml +++ b/api-web/api-interface/src/main/resources/bootstrap.yml @@ -66,12 +66,13 @@ eb: weixin: pay: + appid: wx75fa59c097bd3dfd + appSecret: 1837c382de696013a2dfc64591e6b854 mchid: 1731491745 certificateSerialNo: 793D48CCEB62C6B227E0A4F46AD90279B149A7BE privateKeyFilePath: MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCzn74aYlkoD1lOrcbpqW9aIE/IiH/lAea+nFmmmao5ggxrXhnvkGDQ3e+59Cf59jTikKxmMc+aj36maxrd/OYDM27j0q5b7t8fWPD4qqyMR2uqiImc622BpQIxaPUeLeDHwsHq4C6g0UzZ6DXJppHpb6xn0gEFHS87Na0hhElCbEpjEnbyhRmOC1yDPDmnDTGMX2LP0Cxx6U1pX4JmYToQ0/iOFeD8F9sLrQElGelJnDS6QY+L+qKe7SecNv0axvbwyGXuSUef2+GWgBCnBeWTZhhHNPNAFo6fQR9+zaQmZEksg9HxmAKLxG1w6YSMXQWk8psSiCSoiQfu39m3pN13AgMBAAECggEAbhvoPug22xW9mztvieDxf3/7KGR0cf+eYQ4a7sOX07TixBZlM7N/hcnmoEkJEHNaq+Afrm2uY4K/EmjpiVbz8NZgjYuiknx1jhPJc8W8DCnj2B/mq6it8iEinQNH7v4Uop/Cm6ZdLvvebl2oSzquiziHqQTU1zuyrksTHE6pUetj6ci2AgyKOpyZ3nJ8ivgUSFuvQjlEQ1aJFrFg6nm52MTdJqB9JaQEcBYxw21JCfl1zNZ7EO4eFBqzNO0ttu3EJckYDKClhcesNrJJGUHvPFGo2hfOd2ZzfA4UnaL7yaraAJxQbqwnXOoaV14ka9HYNfakmz59348AxrIbPtHj0QKBgQDskdn7Cut/1DV2EhW26rNj9zaXt55COujHf5+0htj9JrZ6u9s4sCHuzPo8rLiAw7/Pvksfa7Oq+BHE65rMds88KluLyW84b1Wd4annyI+anjtDyz3Chmx7NGQyaHg7lfOqJ09S/0T6WwSViZJz3Fp6p39+1ROyhWZaFRQIwrA6UwKBgQDCYIrAYFnqBaXMk5wFoDg2kc7H6/SL9wgxTo5EwV/ccqFirbScjuM9IkylcIx02gKUH9y+3aDccNPr7PqIreLip9sX+DdFqDa5yEtY4XNR9zAf0aSgo+qSCAtgMqrWwyV+fKUjbzEbEjD7STPXLy+r1YC0g0Z7WeszLG0f4MYTzQKBgQCkCFW+7klwrzIKlmucE40jqYyfEmCXx8UUX3fbcw0OK0OoQo209tvwewyf+ZtNHW3onCf2t3Uy/SNFCaCiWVdEfpJPkPKfjQMuoARxhO2d5k1tqoU+VnrtytwW371Og6EawHsOL5Yiie3ZyjRURdwu4+lRhmlMBZd8qtTjZitPpQKBgQCVrMOLKWZzRxAJvOxahKpkkthYp//yOzHqzePNW95WIUrWco3uNDUVITFF/6mYXTu5FePkcULqHFODi0LMNqHMCJc0GOVu1P33Bx3F/izPw/kht0v+itoYwusHk1xr7W8UvCRpabi1cMeY6CBsJaCev9PQrHl8iJwNFruc3XeJTQKBgFt31iujP7W2FHaT6uyO1gKSlqCcZILD2F15Q52OYQoV6I5Tq152ITyMyE4EiYPOqnqVWUc+ej1m+AuvYljRqlvVCmp91QtYwLGguuHFYNVcE2V3WftKKbIIPT2qEFaS5eUb6hB+kl02E1EmjCtApwssCVJj81yu/DsBP0580zBL wechatPayPublicKeyId: PUB_KEY_ID_0117314917452025110400382304001401 wechatPayPublicKeyFilePath: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx/QqdSnkS8wd7xFH0LHVpZTFVI+eWL383eAP7RaxHfRwXNDfZYrdi/Bt8Enko+1Wkr2FGY2kS4Hol5NCvuZ7UY28/L4r95dBEgXtflkrxeVbiNbWHtiIdNXZSgkrpUkZEFoh7ks5Ou3pAlpG+94/Nnc//D/yckM7AuD85G1foZMSO5niyVeFAed6z7CeBMAhRVdnOIDUqsI/NaHruT4fvWNzPnn5SQKoKum00vRHckxftUqMkZ1D/YxHiAycEc9H0hCgLW7ZM6UV88Loa6SPvhM0fmXIuk6p277ldlxFl6Bcxd5jOcCTjdpWaB3CeSXnSIBl6KsZNcV3vPC3xey1WwIDAQAB - appid: wx75fa59c097bd3dfd notifyUrl: https://api.1024api.com/api-interface/app/weixin/payNotify apiv3key: E938F17ABF6843743F49688269F73B7D refundNotifyUrl: https://api.1024api.com/api-interface/app/weixin/refundNotify