.gitignore | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
966120App | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
ruoyi-admin/src/main/java/com/ruoyi/web/controller/evaluation/EvaluationController.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
ruoyi-admin/src/main/resources/application.yml | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
ruoyi-common/src/main/java/com/ruoyi/common/utils/WechatUtils.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
ruoyi-ui/src/api/evaluation.js | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
ruoyi-ui/src/views/evaluation/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
微信授权功能使用说明.md | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
微信授权调试指南.md | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
.gitignore
@@ -45,4 +45,5 @@ !*/build/*.java !*/build/*.html !*/build/*.xml node_modules/ node_modules/ 966120App/ 966120App
New file @@ -0,0 +1 @@ D:/project/æ¥æè½¬è¿/code/966120 ruoyi-admin/src/main/java/com/ruoyi/web/controller/evaluation/EvaluationController.java
@@ -1,5 +1,6 @@ package com.ruoyi.web.controller.evaluation; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; @@ -156,6 +157,38 @@ private String wechatAppSecret; /** * çæå¾®ä¿¡ææURL */ @Anonymous @GetMapping("/wechat/authurl") public AjaxResult getWechatAuthUrl(String redirectUri, String state) { try { if (StringUtils.isEmpty(redirectUri)) { return error("åè°å°åä¸è½ä¸ºç©º"); } logger.info("çæå¾®ä¿¡ææURL - åå§redirectUri: {}", redirectUri); // çæå¾®ä¿¡ææURLï¼ä½¿ç¨snsapi_userinfoè·åç¨æ·ä¿¡æ¯ String authUrl = WechatUtils.generateAuthUrl(wechatAppId, redirectUri, "snsapi_userinfo", state); if (authUrl == null) { return error("çæå¾®ä¿¡ææURL失败"); } logger.info("çæå¾®ä¿¡ææURLæå: {}", authUrl); Map<String, String> result = new HashMap<>(); result.put("authUrl", authUrl); result.put("originalRedirectUri", redirectUri); result.put("appId", wechatAppId); return success(result); } catch (Exception e) { logger.error("çæå¾®ä¿¡ææURL失败", e); return error("çæå¾®ä¿¡ææURL失败: " + e.getMessage()); } } /** * è·åå¾®ä¿¡ç¨æ·ä¿¡æ¯ */ @Anonymous @@ -163,7 +196,7 @@ public AjaxResult getWechatUserInfo(String code, HttpServletRequest request) { try { if (StringUtils.isEmpty(code)) { if (code.isEmpty()) { return error("ææç ä¸è½ä¸ºç©º"); } @@ -182,7 +215,22 @@ return error("è·åå¾®ä¿¡ç¨æ·ä¿¡æ¯å¤±è´¥"); } return success(userInfo); // å¤çç¨æ·ä¿¡æ¯ï¼ç¡®ä¿å段åç§°ä¸è´ Map<String, Object> result = new HashMap<>(); result.put("openid", userInfo.getString("openid")); result.put("nickname", userInfo.getString("nickname")); result.put("headimgurl", userInfo.getString("headimgurl")); result.put("sex", userInfo.getInteger("sex")); result.put("province", userInfo.getString("province")); result.put("city", userInfo.getString("city")); result.put("country", userInfo.getString("country")); result.put("unionid", userInfo.getString("unionid")); // 注æï¼å¾®ä¿¡ç½é¡µæææ æ³ç´æ¥è·åææºå·ï¼éè¦éè¿å ¶ä»æ¹å¼ // å¦éè·åææºå·ï¼éè¦ä½¿ç¨å¾®ä¿¡å°ç¨åºçgetPhoneNumberæ¥å£æå¾®ä¿¡å¼æ¾å¹³å°çææºå·å¿«ééªè¯ç»ä»¶ result.put("phone", ""); result.put("phoneNote", "微信ç½é¡µæææ æ³ç´æ¥è·åææºå·ï¼è¯·æå¨è¾å ¥"); return success(result); } catch (Exception e) { logger.error("è·åå¾®ä¿¡ç¨æ·ä¿¡æ¯å¤±è´¥", e); return error("è·åå¾®ä¿¡ç¨æ·ä¿¡æ¯å¤±è´¥"); ruoyi-admin/src/main/resources/application.yml
@@ -147,6 +147,11 @@ # 微信é ç½® wechat: appId: your_wechat_appid appSecret: your_wechat_appsecret redirectUri: http://yourdomain.com/evaluation appId: wx70f6a7346ee842c0 appSecret: 2d6c59de85e876b7eadebeba62e5417a redirectUri: http://yourdomain.com/evaluation # å¼åç¯å¢é ç½® dev: enabled: true # æ¯å¦å¯ç¨å¼åæ¨¡å¼ mockUserInfo: true # æ¯å¦æ¨¡æç¨æ·ä¿¡æ¯ ngrokUrl: http://your-ngrok-url.ngrok.io # å ç½ç©¿éå°å ruoyi-common/src/main/java/com/ruoyi/common/utils/WechatUtils.java
@@ -4,7 +4,7 @@ import com.alibaba.fastjson2.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.client.RestTemplate; import com.ruoyi.common.utils.http.HttpUtils; import java.util.HashMap; import java.util.Map; @@ -18,7 +18,8 @@ private static final Logger log = LoggerFactory.getLogger(WechatUtils.class); private static final String WECHAT_API_BASE_URL = "https://api.weixin.qq.com"; private static final String WECHAT_API_BASE_URL = "https://open.weixin.qq.com"; private static final String WECHAT_API_BASE_URL_SERVER = "https://api.weixin.qq.com"; /** * è·å微信Access Token @@ -29,10 +30,10 @@ */ public static String getAccessToken(String appId, String appSecret) { try { String url = WECHAT_API_BASE_URL + "/cgi-bin/token?grant_type=client_credential&appid=" + appId + "&secret=" + appSecret; String url = WECHAT_API_BASE_URL_SERVER + "/cgi-bin/token"; String param = "grant_type=client_credential&appid=" + appId + "&secret=" + appSecret; RestTemplate restTemplate = new RestTemplate(); String response = restTemplate.getForObject(url, String.class); String response = HttpUtils.sendGet(url, param); JSONObject jsonObject = JSON.parseObject(response); if (jsonObject.containsKey("access_token")) { @@ -56,10 +57,10 @@ */ public static JSONObject getWechatUserInfo(String accessToken, String openid) { try { String url = WECHAT_API_BASE_URL + "/cgi-bin/user/info?access_token=" + accessToken + "&openid=" + openid + "&lang=zh_CN"; String url = WECHAT_API_BASE_URL_SERVER + "/cgi-bin/user/info"; String param = "access_token=" + accessToken + "&openid=" + openid + "&lang=zh_CN"; RestTemplate restTemplate = new RestTemplate(); String response = restTemplate.getForObject(url, String.class); String response = HttpUtils.sendGet(url, param); JSONObject jsonObject = JSON.parseObject(response); if (jsonObject.containsKey("openid")) { @@ -84,10 +85,10 @@ */ public static JSONObject getWebAccessToken(String appId, String appSecret, String code) { try { String url = WECHAT_API_BASE_URL + "/sns/oauth2/access_token?appid=" + appId + "&secret=" + appSecret + "&code=" + code + "&grant_type=authorization_code"; String url = WECHAT_API_BASE_URL_SERVER + "/sns/oauth2/access_token"; String param = "appid=" + appId + "&secret=" + appSecret + "&code=" + code + "&grant_type=authorization_code"; RestTemplate restTemplate = new RestTemplate(); String response = restTemplate.getForObject(url, String.class); String response = HttpUtils.sendGet(url, param); JSONObject jsonObject = JSON.parseObject(response); if (jsonObject.containsKey("access_token")) { @@ -111,10 +112,10 @@ */ public static JSONObject getWebUserInfo(String accessToken, String openid) { try { String url = WECHAT_API_BASE_URL + "/sns/userinfo?access_token=" + accessToken + "&openid=" + openid + "&lang=zh_CN"; String url = WECHAT_API_BASE_URL_SERVER + "/sns/userinfo"; String param = "access_token=" + accessToken + "&openid=" + openid + "&lang=zh_CN"; RestTemplate restTemplate = new RestTemplate(); String response = restTemplate.getForObject(url, String.class); String response = HttpUtils.sendGet(url, param); JSONObject jsonObject = JSON.parseObject(response); if (jsonObject.containsKey("openid")) { @@ -140,8 +141,16 @@ */ public static String generateAuthUrl(String appId, String redirectUri, String scope, String state) { try { String encodedRedirectUri = java.net.URLEncoder.encode(redirectUri, "UTF-8"); return WECHAT_API_BASE_URL + "/connect/oauth2/authorize?appid=" + appId + "&redirect_uri=" + encodedRedirectUri + "&response_type=code&scope=" + scope + "&state=" + state + "#wechat_redirect"; // æ¸ çåéªè¯redirectUri String cleanRedirectUri = cleanRedirectUri(redirectUri); // URLç¼ç String encodedRedirectUri = java.net.URLEncoder.encode(cleanRedirectUri, "UTF-8"); return WECHAT_API_BASE_URL + "/connect/oauth2/authorize?appid=" + appId + "&redirect_uri=" + encodedRedirectUri + "&response_type=code&scope=" + scope + "&state=" + state + "#wechat_redirect"; } catch (Exception e) { log.error("çæå¾®ä¿¡ææURLå¼å¸¸: {}", e.getMessage()); return null; @@ -149,6 +158,37 @@ } /** * æ¸ çåéªè¯redirectUri * * @param redirectUri åå§åè°å°å * @return æ¸ çåçåè°å°å */ private static String cleanRedirectUri(String redirectUri) { if (StringUtils.isEmpty(redirectUri)) { return redirectUri; } try { // ç§»é¤ç«¯å£å·ï¼å¾®ä¿¡ææä¸æ¯æç«¯å£å·ï¼ if (redirectUri.contains(":81") || redirectUri.contains(":8080") || redirectUri.contains(":3000")) { redirectUri = redirectUri.replaceAll(":(81|8080|3000|8081|8082|8083|8084|8085|8086|8087|8088|8089|8090)", ""); log.warn("æ£æµå°ç«¯å£å·ï¼å·²èªå¨ç§»é¤: {}", redirectUri); } // ç¡®ä¿ä½¿ç¨HTTPSï¼ç产ç¯å¢ï¼ if (redirectUri.startsWith("http://") && !redirectUri.contains("localhost") && !redirectUri.contains("127.0.0.1")) { redirectUri = redirectUri.replace("http://", "https://"); log.warn("ç产ç¯å¢å»ºè®®ä½¿ç¨HTTPSï¼å·²èªå¨è½¬æ¢: {}", redirectUri); } return redirectUri; } catch (Exception e) { log.error("æ¸ çredirectUriå¼å¸¸: {}", e.getMessage()); return redirectUri; } } /** * 夿æ¯å¦ä¸ºå¾®ä¿¡æµè§å¨ * * @param userAgent ç¨æ·ä»£çå符串 ruoyi-ui/src/api/evaluation.js
@@ -17,6 +17,15 @@ }) } // çæå¾®ä¿¡ææURL export function getWechatAuthUrl(redirectUri, state) { return request({ url: '/evaluation/wechat/authurl', method: 'get', params: { redirectUri, state } }) } // è·åå¾®ä¿¡ç¨æ·ä¿¡æ¯ export function getWechatUserInfo(code) { return request({ ruoyi-ui/src/views/evaluation/index.vue
@@ -20,11 +20,41 @@ <!-- 客æ·ä¿¡æ¯ --> <div class="form-section"> <h3>客æ·ä¿¡æ¯</h3> <!-- 微信ææä¿¡æ¯æ¾ç¤º --> <div v-if="evaluationForm.wechatOpenid" class="wechat-info"> <div class="wechat-user"> <img v-if="evaluationForm.wechatAvatar" :src="evaluationForm.wechatAvatar" class="wechat-avatar" /> <div class="wechat-details"> <div class="wechat-nickname">{{ evaluationForm.wechatNickname }}</div> <div class="wechat-openid">å·²ææå¾®ä¿¡ç»å½</div> </div> </div> </div> <!-- å¾®ä¿¡æææé® --> <div v-if="isWechatBrowser() && !evaluationForm.wechatOpenid" class="wechat-auth-section"> <el-button type="primary" size="small" @click="redirectToWechatAuth" icon="el-icon-user" :loading="wechatAuthLoading" > {{ wechatAuthLoading ? 'æ£å¨è·³è½¬...' : '微信ä¸é®ç»å½' }} </el-button> <div class="wechat-tip">ç¹å»å¯èªå¨è·åæ¨ç微信信æ¯</div> </div> <el-form-item label="å§å" prop="customerName"> <el-input v-model="evaluationForm.customerName" placeholder="请è¾å ¥æ¨çå§å" /> </el-form-item> <el-form-item label="ææºå·" prop="customerPhone"> <el-input v-model="evaluationForm.customerPhone" placeholder="请è¾å ¥æ¨çææºå·" /> <div v-if="evaluationForm.wechatOpenid" class="phone-tip"> <i class="el-icon-info"></i> å¾®ä¿¡æææ æ³ç´æ¥è·åææºå·ï¼è¯·æå¨è¾å ¥ </div> </el-form-item> </div> @@ -118,7 +148,7 @@ </template> <script> import { getEvaluationDimensions, submitEvaluation, getWechatUserInfo } from "@/api/evaluation"; import { getEvaluationDimensions, submitEvaluation, getWechatUserInfo, getWechatAuthUrl } from "@/api/evaluation"; export default { name: "Evaluation", @@ -147,7 +177,8 @@ }, submitting: false, showResult: false, resultMessage: '' resultMessage: '', wechatAuthLoading: false }; }, created() { @@ -178,7 +209,10 @@ // å¤çå¾®ä¿¡ææ async handleWechatAuth() { const code = this.$route.query.code; const state = this.$route.query.state; if (code) { // æææç ï¼è·åç¨æ·ä¿¡æ¯ try { const response = await getWechatUserInfo(code); if (response.code === 200) { @@ -187,10 +221,59 @@ this.evaluationForm.wechatNickname = userInfo.nickname; this.evaluationForm.wechatAvatar = userInfo.headimgurl; this.evaluationForm.wechatPhone = userInfo.phone || ''; // 妿è·åå°äºå¾®ä¿¡æµç§°ï¼èªå¨å¡«å å°å§ååæ®µ if (userInfo.nickname && !this.evaluationForm.customerName) { this.evaluationForm.customerName = userInfo.nickname; } this.$message.success('å¾®ä¿¡æææåï¼å·²èªå¨è·åæ¨çä¿¡æ¯'); } else { this.$message.error(response.msg || 'è·åå¾®ä¿¡ç¨æ·ä¿¡æ¯å¤±è´¥'); } } catch (error) { console.error('è·åå¾®ä¿¡ç¨æ·ä¿¡æ¯å¤±è´¥:', error); this.$message.error('è·åå¾®ä¿¡ç¨æ·ä¿¡æ¯å¤±è´¥ï¼è¯·éè¯'); } } else { // æ²¡æææç ï¼æ£æ¥æ¯å¦éè¦è·³è½¬å°å¾®ä¿¡ææ const hasWechatInfo = this.evaluationForm.wechatOpenid; if (!hasWechatInfo) { await this.redirectToWechatAuth(); } } }, // 跳转å°å¾®ä¿¡ææ async redirectToWechatAuth() { this.wechatAuthLoading = true; try { // æå»ºå½å页é¢ç宿´URLä½ä¸ºåè°å°å const currentUrl = window.location.origin + window.location.pathname; const params = new URLSearchParams(window.location.search); params.delete('code'); // ç§»é¤å¯è½åå¨çcodeåæ° params.delete('state'); // ç§»é¤å¯è½åå¨çstateåæ° const redirectUri = currentUrl + (params.toString() ? '?' + params.toString() : ''); console.log('åå¤çæå¾®ä¿¡ææURLï¼åè°å°å:', redirectUri); const response = await getWechatAuthUrl(redirectUri, 'evaluation'); if (response.code === 200) { console.log('微信ææURLçææå:', response.data); console.log('åå§åè°å°å:', response.data.originalRedirectUri); console.log('微信AppID:', response.data.appId); // 跳转å°å¾®ä¿¡ææé¡µé¢ window.location.href = response.data.authUrl; } else { console.error('çæå¾®ä¿¡ææURL失败:', response.msg); this.$message.error('å¾®ä¿¡æææå¡ææ¶ä¸å¯ç¨ï¼è¯·æå¨å¡«åä¿¡æ¯'); } } catch (error) { console.error('跳转微信ææå¤±è´¥:', error); this.$message.error('微信ææå¤±è´¥ï¼è¯·æå¨å¡«åä¿¡æ¯'); } finally { this.wechatAuthLoading = false; } }, @@ -465,6 +548,72 @@ min-height: 60px !important; } /* 微信ææç¸å ³æ ·å¼ */ .wechat-info { background: #f0f9ff; border: 1px solid #b3d8ff; border-radius: 6px; padding: 12px; margin-bottom: 16px; } .wechat-user { display: flex; align-items: center; } .wechat-avatar { width: 40px; height: 40px; border-radius: 50%; margin-right: 12px; border: 2px solid #409EFF; } .wechat-details { flex: 1; } .wechat-nickname { font-weight: bold; color: #333; font-size: 14px; margin-bottom: 4px; } .wechat-openid { color: #666; font-size: 12px; } .wechat-auth-section { text-align: center; padding: 16px; background: #f8f9fa; border-radius: 6px; margin-bottom: 16px; border: 1px dashed #ddd; } .wechat-tip { color: #666; font-size: 12px; margin-top: 8px; } .phone-tip { color: #909399; font-size: 12px; margin-top: 4px; display: flex; align-items: center; } .phone-tip i { margin-right: 4px; color: #409EFF; } /* ä¼å表å项é´è· */ .el-form-item { margin-bottom: 12px; @@ -628,6 +777,41 @@ padding: 8px 24px; font-size: 13px; } /* ç§»å¨ç«¯å¾®ä¿¡æææ ·å¼ */ .wechat-info { padding: 10px; margin-bottom: 12px; } .wechat-avatar { width: 36px; height: 36px; margin-right: 10px; } .wechat-nickname { font-size: 13px; } .wechat-openid { font-size: 11px; } .wechat-auth-section { padding: 12px; margin-bottom: 12px; } .wechat-tip { font-size: 11px; margin-top: 6px; } .phone-tip { font-size: 11px; margin-top: 3px; } } /* è¶ å°å±å¹éé */ ΢ÐÅÊÚȨ¹¦ÄÜʹÓÃ˵Ã÷.md
New file @@ -0,0 +1,124 @@ # 微信ææåè½ä½¿ç¨è¯´æ ## åè½æ¦è¿° æ¬åè½å®ç°äºå¨è¯ä»·é¡µé¢ä¸è·åå¾®ä¿¡ç¨æ·çOpenIdã头åãæµç§°çä¿¡æ¯ï¼æåç¨æ·ä½éªã ## å®ç°çåè½ ### 1. 微信ææURLçæ - å端æ¥å£ï¼`GET /evaluation/wechat/authurl` - åè½ï¼çæå¾®ä¿¡ç½é¡µææURLï¼ä½¿ç¨`snsapi_userinfo`æéè·åç¨æ·åºæ¬ä¿¡æ¯ ### 2. å¾®ä¿¡ç¨æ·ä¿¡æ¯è·å - å端æ¥å£ï¼`GET /evaluation/wechat/userinfo` - åè½ï¼éè¿ææç è·åå¾®ä¿¡ç¨æ·ä¿¡æ¯ï¼å æ¬ï¼ - OpenIdï¼ç¨æ·å¯ä¸æ è¯ - æµç§°ï¼ç¨æ·å¾®ä¿¡æµç§° - 头åï¼ç¨æ·å¾®ä¿¡å¤´åURL - æ§å«ãç份ãåå¸ãå½å®¶çåºæ¬ä¿¡æ¯ ### 3. å端èªå¨æææµç¨ - æ£æµå¾®ä¿¡æµè§å¨ç¯å¢ - èªå¨è·³è½¬å°å¾®ä¿¡ææé¡µé¢ - æææååèªå¨è·åç¨æ·ä¿¡æ¯ - èªå¨å¡«å ç¨æ·æµç§°å°å§ååæ®µ ## é ç½®è¦æ± ### 1. å¾®ä¿¡å ¬ä¼å¹³å°é ç½® å¨`application.yml`ä¸é 置微信AppIDåAppSecretï¼ ```yaml wechat: appId: your_wechat_appid appSecret: your_wechat_appsecret ``` ### 2. å¾®ä¿¡å ¬ä¼å¹³å°ç½é¡µææååé ç½® éè¦å¨å¾®ä¿¡å ¬ä¼å¹³å°åå°é ç½®ææåè°ååï¼ä¾å¦ï¼ - `yourdomain.com` ## ä½¿ç¨æµç¨ ### 1. ç¨æ·è®¿é®è¯ä»·é¡µé¢ - å¨å¾®ä¿¡æµè§å¨ä¸æå¼è¯ä»·é¡µé¢ - ç³»ç»èªå¨æ£æµå¾®ä¿¡ç¯å¢ ### 2. å¾®ä¿¡ææ - å¦æç¨æ·æªææï¼æ¾ç¤º"微信ä¸é®ç»å½"æé® - ç¹å»æé®è·³è½¬å°å¾®ä¿¡ææé¡µé¢ - ç¨æ·ç¡®è®¤ææåè¿åè¯ä»·é¡µé¢ ### 3. ä¿¡æ¯èªå¨å¡«å - æææååèªå¨è·åç¨æ·ä¿¡æ¯ - èªå¨å¡«å æµç§°å°å§ååæ®µ - æ¾ç¤ºç¨æ·å¤´ååææç¶æ ### 4. æå¨è¾å ¥ææºå· - ç±äºå¾®ä¿¡ç½é¡µæææ æ³ç´æ¥è·åææºå· - ç¨æ·éè¦æå¨è¾å ¥ææºå· - ç³»ç»ä¼æ¾ç¤ºæç¤ºä¿¡æ¯è¯´æåå ## ææ¯å®ç° ### å端å®ç° 1. **WechatUtilså·¥å ·ç±»**ï¼å°è£ 微信APIè°ç¨ 2. **EvaluationController**ï¼æä¾å¾®ä¿¡ææç¸å ³æ¥å£ 3. **ç½é¡µæææµç¨**ï¼ä½¿ç¨OAuth2.0æ åæµç¨ ### å端å®ç° 1. **èªå¨æ£æµå¾®ä¿¡ç¯å¢**ï¼éè¿User-Agent夿 2. **ææURLçæ**ï¼å¨ææå»ºåè°å°å 3. **ç¨æ·ä¿¡æ¯å±ç¤º**ï¼ç¾è§çUIå±ç¤ºææç¶æ 4. **é误å¤ç**ï¼å®åçé误æç¤ºåéçº§æ¹æ¡ ## 注æäºé¡¹ ### 1. ææºå·è·åéå¶ - 微信ç½é¡µæææ æ³ç´æ¥è·åç¨æ·ææºå· - å¦éè·åææºå·ï¼éè¦ï¼ - 使ç¨å¾®ä¿¡å°ç¨åºç`getPhoneNumber`æ¥å£ - æä½¿ç¨å¾®ä¿¡å¼æ¾å¹³å°çææºå·å¿«ééªè¯ç»ä»¶ ### 2. ææååé ç½® - å¿ é¡»å¨å¾®ä¿¡å ¬ä¼å¹³å°é ç½®æ£ç¡®çææåè°åå - ååå¿ é¡»ä¸å®é 访é®ååä¸è´ ### 3. HTTPSè¦æ± - ç产ç¯å¢å¿ 须使ç¨HTTPSåè®® - 微信ææè¦æ±å®å ¨è¿æ¥ ### 4. ç¨æ·ä½éªä¼å - æä¾æå¨è¾å ¥é项ä½ä¸ºéçº§æ¹æ¡ - æ¸ æ°çæç¤ºä¿¡æ¯è¯´æææç¶æ - ååºå¼è®¾è®¡éé ç§»å¨ç«¯ ## æ©å±åè½ ### 1. 微信å°ç¨åºéæ å¦éå¨å°ç¨åºä¸ä½¿ç¨ï¼å¯ä»¥ï¼ - 使ç¨`wx.getUserProfile`è·åç¨æ·ä¿¡æ¯ - 使ç¨`wx.getPhoneNumber`è·åææºå· ### 2. å¾®ä¿¡å¼æ¾å¹³å°éæ å¦éè·åæ´å¤ä¿¡æ¯ï¼å¯ä»¥ï¼ - ç³è¯·å¾®ä¿¡å¼æ¾å¹³å°è´¦å· - ä½¿ç¨ææºå·å¿«ééªè¯ç»ä»¶ - è·åUnionIDè¿è¡å¤å¹³å°ç¨æ·ç»ä¸ ## æµè¯å»ºè®® 1. **åè½æµè¯** - å¨å¾®ä¿¡æµè§å¨ä¸æµè¯å®æ´æææµç¨ - æµè¯æææååå¤±è´¥çæ åµ - æµè¯é微信æµè§å¨çéçº§æ¹æ¡ 2. **å ¼å®¹æ§æµè¯** - æµè¯ä¸åçæ¬ç微信æµè§å¨ - æµè¯ä¸å设å¤çæ¾ç¤ºææ - æµè¯ç½ç»å¼å¸¸æ åµ 3. **å®å ¨æ§æµè¯** - éªè¯ææç çæææ§ - æµè¯æ¶æè¯·æ±çå¤ç - éªè¯ç¨æ·ä¿¡æ¯ç宿´æ§ ΢ÐÅÊÚȨµ÷ÊÔÖ¸ÄÏ.md
New file @@ -0,0 +1,209 @@ # 微信ææè°è¯æå ## é®é¢åæ æ¨éå°ç `redirect_uri åæ°é误` é®é¢ï¼ä¸»è¦åå å¦ä¸ï¼ ### 1. ååé ç½®é®é¢ 仿¨çURLå¯ä»¥çåºï¼ ``` redirect_uri=http%3A%2F%2Fwx.966120.com.cn%3A81%2Fevaluation%3Fvehicle%3D%25E7%25B2%25AC12345 ``` **é®é¢ï¼** - åå `wx.966120.com.cn:81` å¯è½æªå¨å¾®ä¿¡å ¬ä¼å¹³å°é ç½® - 端å£å· `:81` å¨å¾®ä¿¡ææä¸ä¸è¢«æ¯æ - éè¦é ç½®æ£ç¡®çææåè°åå ### 2. å¾®ä¿¡å ¬ä¼å¹³å°é ç½®è¦æ± #### ç½é¡µææååé ç½® 1. ç»å½å¾®ä¿¡å ¬ä¼å¹³å° (https://mp.weixin.qq.com) 2. è¿å ¥"设置ä¸å¼å" -> "å ¬ä¼å·è®¾ç½®" -> "åè½è®¾ç½®" 3. å¨"ç½é¡µææåå"䏿·»å ï¼`wx.966120.com.cn` 4. **注æï¼ä¸è¦å å«ç«¯å£å·ï¼ä¸è¦å å«åè®®** ## è§£å³æ¹æ¡ ### æ¹æ¡ä¸ï¼ä¿®å¤ååé ç½®ï¼æ¨èï¼ #### 1. å¾®ä¿¡å ¬ä¼å¹³å°é ç½® ``` ç½é¡µææååï¼wx.966120.com.cn ``` #### 2. ä¿®æ¹åºç¨é ç½® å¨ `application.yml` ä¸é ç½®æ£ç¡®çååï¼ ```yaml wechat: appId: wx70f6a7346ee842c0 appSecret: 2d6c59de85e876b7eadebeba62e5417a redirectUri: https://wx.966120.com.cn/evaluation # 使ç¨HTTPSï¼æ 端å£å· ``` #### 3. æå¡å¨é ç½® - ç¡®ä¿ `wx.966120.com.cn` ååæåæ¨çæå¡å¨ - é ç½®HTTPSè¯ä¹¦ï¼å¾®ä¿¡ææè¦æ±HTTPSï¼ - é ç½®åå代çï¼å°80/443端å£è½¬åå°æ¨çåºç¨ç«¯å£ ### æ¹æ¡äºï¼å¼åç¯å¢è°è¯ #### 1. 使ç¨å ç½ç©¿éå·¥å · **æ¨èå·¥å ·ï¼** - **ngrok** (å è´¹ç) - **natapp** (å½å è®¿é®æ´ç¨³å®) - **frp** (èªå»º) **ngrokä½¿ç¨æ¥éª¤ï¼** ```bash # 1. ä¸è½½ngrok # 2. 注åè´¦å·è·åauthtoken ngrok authtoken YOUR_AUTHTOKEN # 3. å¯å¨å ç½ç©¿é ngrok http 8080 # 4. è·å¾å ¬ç½å°åï¼å¦ï¼https://abc123.ngrok.io ``` #### 2. é ç½®å¼åç¯å¢ ```yaml wechat: appId: wx70f6a7346ee842c0 appSecret: 2d6c59de85e876b7eadebeba62e5417a redirectUri: https://your-ngrok-url.ngrok.io/evaluation ``` #### 3. å¾®ä¿¡å ¬ä¼å¹³å°é ç½® å¨ç½é¡µææåå䏿·»å ï¼`your-ngrok-url.ngrok.io` ### æ¹æ¡ä¸ï¼æ¬å°å¼å模æ #### 1. æ·»å å¼åç¯å¢é ç½® ```yaml wechat: appId: wx70f6a7346ee842c0 appSecret: 2d6c59de85e876b7eadebeba62e5417a redirectUri: https://wx.966120.com.cn/evaluation # å¼åç¯å¢é ç½® dev: enabled: true mockUserInfo: true ngrokUrl: https://your-ngrok-url.ngrok.io ``` #### 2. ä¿®æ¹åç«¯ä»£ç æ¯æå¼åæ¨¡å¼ ```java @Value("${wechat.dev.enabled:false}") private boolean devModeEnabled; @Value("${wechat.dev.mockUserInfo:false}") private boolean mockUserInfo; // å¨çæææURLæ¶æ·»å å¼å模å¼å¤æ if (devModeEnabled && mockUserInfo) { // è¿å模æçææURLï¼ç´æ¥è·³è½¬å°åè°å°å String mockCode = "dev_mock_code_" + System.currentTimeMillis(); String mockRedirectUri = redirectUri + "?code=" + mockCode + "&state=" + state; return success(Map.of("authUrl", mockRedirectUri)); } ``` ## è°è¯æ¥éª¤ ### 1. æ£æ¥å½åé ç½® ```bash # æ£æ¥åºç¨é ç½® curl -X GET "http://localhost:8080/evaluation/wechat/authurl?redirectUri=http://wx.966120.com.cn/evaluation&state=test" ``` ### 2. éªè¯å¾®ä¿¡ææURL çæçURLåºè¯¥æ¯è¿æ ·çæ ¼å¼ï¼ ``` https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx70f6a7346ee842c0&redirect_uri=https%3A%2F%2Fwx.966120.com.cn%2Fevaluation&response_type=code&scope=snsapi_userinfo&state=test#wechat_redirect ``` ### 3. æ£æ¥ååè§£æ ```bash # æ£æ¥å忝å¦è§£ææ£ç¡® nslookup wx.966120.com.cn ping wx.966120.com.cn ``` ### 4. æ£æ¥HTTPSè¯ä¹¦ ```bash # æ£æ¥HTTPSæ¯å¦æ£å¸¸ curl -I https://wx.966120.com.cn ``` ## 常è§é误åè§£å³æ¹æ¡ ### 1. redirect_uri åæ°é误 **åå ï¼** ååæªå¨å¾®ä¿¡å ¬ä¼å¹³å°é ç½®æé ç½®é误 **è§£å³ï¼** å¨å¾®ä¿¡å ¬ä¼å¹³å°æ£ç¡®é ç½®ç½é¡µææåå ### 2. ååä¸å¹é **åå ï¼** åè°å°åä¸é ç½®çææååä¸ä¸è´ **è§£å³ï¼** ç¡®ä¿åè°å°åçååä¸é ç½®çææååå®å ¨ä¸è´ ### 3. 端å£å·é®é¢ **åå ï¼** 微信ææä¸æ¯æç«¯å£å· **è§£å³ï¼** 使ç¨åå代çï¼å°80/443端å£è½¬åå°åºç¨ç«¯å£ ### 4. HTTPSè¦æ± **åå ï¼** ç产ç¯å¢å¿ 须使ç¨HTTPS **è§£å³ï¼** é ç½®SSLè¯ä¹¦ï¼ä½¿ç¨HTTPSåè®® ## æµè¯éªè¯ ### 1. æ¬å°æµè¯ ```bash # å¯å¨åºç¨ mvn spring-boot:run # æµè¯ææURLçæ curl "http://localhost:8080/evaluation/wechat/authurl?redirectUri=https://wx.966120.com.cn/evaluation&state=test" ``` ### 2. 微信ç¯å¢æµè¯ 1. å¨å¾®ä¿¡ä¸æå¼çæçææURL 2. ç¡®è®¤è½æ£å¸¸è·³è½¬å°å¾®ä¿¡ææé¡µé¢ 3. ææåè½æ£å¸¸åè°å°æ¨çåºç¨ ### 3. ç¨æ·ä¿¡æ¯è·åæµè¯ ```bash # ä½¿ç¨ææç è·åç¨æ·ä¿¡æ¯ curl "http://localhost:8080/evaluation/wechat/userinfo?code=YOUR_AUTH_CODE" ``` ## 注æäºé¡¹ 1. **ååé ç½®**ï¼å¿ é¡»å¨å¾®ä¿¡å ¬ä¼å¹³å°é ç½®æ£ç¡®çææåå 2. **HTTPSè¦æ±**ï¼ç产ç¯å¢å¿ 须使ç¨HTTPS 3. **端å£å·éå¶**ï¼å¾®ä¿¡ææä¸æ¯æç«¯å£å· 4. **ç¼ç é®é¢**ï¼ç¡®ä¿URLç¼ç æ£ç¡® 5. **æµè¯ç¯å¢**ï¼å¼åæ¶å¯ä»¥ä½¿ç¨å ç½ç©¿éå·¥å · ## æ¨èé ç½® ### ç产ç¯å¢ ```yaml wechat: appId: wx70f6a7346ee842c0 appSecret: 2d6c59de85e876b7eadebeba62e5417a redirectUri: https://wx.966120.com.cn/evaluation ``` ### å¼åç¯å¢ ```yaml wechat: appId: wx70f6a7346ee842c0 appSecret: 2d6c59de85e876b7eadebeba62e5417a redirectUri: https://your-ngrok-url.ngrok.io/evaluation dev: enabled: true mockUserInfo: true ``` æç §ä»¥ä¸æ¥éª¤é ç½®åï¼æ¨ç微信ææåè½åºè¯¥è½æ£å¸¸å·¥ä½ã