ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/model/Agreement.java
New file @@ -0,0 +1,38 @@ package com.ruoyi.other.api.model; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import java.time.LocalDateTime; /** 协议 * @author zhibing.pu * @Date 2024/11/21 10:44 */ @Data @TableName("t_agreement") public class Agreement { /** * 主键 */ @TableId(value = "id", type = IdType.AUTO) private Integer id; /** * 类型(1=用户协议,2=隐私协议,3=技师上门免责声明,4=注销协议,5门店提现免责声明) */ @TableField("type") private Integer type; /** * 协议内容 */ @TableField("content") private String content; /** * 添加时间 */ @TableField("create_time") private LocalDateTime createTime; } ruoyi-api/ruoyi-api-other/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -1,11 +0,0 @@ com.ruoyi.other.api.factory.OtherFallbackFactory com.ruoyi.other.api.factory.UserSiteFallbackFactory com.ruoyi.other.api.factory.RoleSiteFallbackFactory com.ruoyi.other.api.factory.TEvaluationTagFallbackFactory com.ruoyi.other.api.factory.VipFallbackFactory com.ruoyi.other.api.factory.IntegralRuleFallbackFactory com.ruoyi.other.api.factory.GoodsFallbackFactory com.ruoyi.other.api.factory.CouponFallbackFactory com.ruoyi.other.api.factory.InvoiceTypeFallbackFactory com.ruoyi.other.api.factory.WebSocketFallbackFactory com.ruoyi.other.api.factory.UserTagFallbackFactory ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/weChat/AES.java
New file @@ -0,0 +1,72 @@ package com.ruoyi.account.util.weChat; import org.bouncycastle.jce.provider.BouncyCastleProvider; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.security.*; /** * AES加密 * @author pzb * @Date 2021/12/3 15:43 */ public class AES { public static boolean initialized = false; /** * AES解密 * * @param content * 密文 * @return * @throws InvalidAlgorithmParameterException * @throws NoSuchProviderException */ public byte[] decrypt(byte[] content, byte[] keyByte, byte[] ivByte) throws InvalidAlgorithmParameterException { initialize(); try { Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); Key sKeySpec = new SecretKeySpec(keyByte, "AES"); cipher.init(Cipher.DECRYPT_MODE, sKeySpec, generateIV(ivByte));// 初始化 byte[] result = cipher.doFinal(content); return result; } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } catch (NoSuchProviderException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } public static void initialize() { if (initialized) return; Security.addProvider(new BouncyCastleProvider()); initialized = true; } // 生成iv public static AlgorithmParameters generateIV(byte[] iv) throws Exception { AlgorithmParameters params = AlgorithmParameters.getInstance("AES"); params.init(new IvParameterSpec(iv)); return params; } } ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/weChat/WXCore.java
New file @@ -0,0 +1,48 @@ package com.ruoyi.account.util.weChat; import org.apache.commons.codec.binary.Base64; import org.springframework.beans.factory.annotation.Value; public class WXCore { private static final String WATERMARK = "watermark"; @Value("${wx.appletsAppid}") private static String appid ; /** * 解密数据 * @return * @throws Exception */ public static String decrypt(String encryptedData, String sessionKey, String iv){ String result = ""; try { AES aes = new AES(); byte[] resultByte = aes.decrypt(Base64.decodeBase64(encryptedData), Base64.decodeBase64(sessionKey), Base64.decodeBase64(iv)); if(null != resultByte && resultByte.length > 0){ result = new String(WxPKCS7Encoder.decode(resultByte), "UTF-8"); // JSONObject jsonObject = JSON.parseObject(result); // String decryptAppid = jsonObject.getJSONObject(WATERMARK).getString("appid"); // if(!appid.equals(decryptAppid)){ // result = ""; // } } } catch (Exception e) { result = ""; e.printStackTrace(); } return result; } public static void main(String[] args) throws Exception{ String appId = "wx4f4bc4dec97d474b"; String encryptedData = "CiyLU1Aw2KjvrjMdj8YKliAjtP4gsMZMQmRzooG2xrDcvSnxIMXFufNstNGTyaGS9uT5geRa0W4oTOb1WT7fJlAC+oNPdbB+3hVbJSRgv+4lGOETKUQz6OYStslQ142dNCuabNPGBzlooOmB231qMM85d2/fV6ChevvXvQP8Hkue1poOFtnEtpyxVLW1zAo6/1Xx1COxFvrc2d7UL/lmHInNlxuacJXwu0fjpXfz/YqYzBIBzD6WUfTIF9GRHpOn/Hz7saL8xz+W//FRAUid1OksQaQx4CMs8LOddcQhULW4ucetDf96JcR3g0gfRK4PC7E/r7Z6xNrXd2UIeorGj5Ef7b1pJAYB6Y5anaHqZ9J6nKEBvB4DnNLIVWSgARns/8wR2SiRS7MNACwTyrGvt9ts8p12PKFdlqYTopNHR1Vf7XjfhQlVsAJdNiKdYmYVoKlaRv85IfVunYzO0IKXsyl7JCUjCpoG20f0a04COwfneQAGGwd5oa+T8yO5hzuyDb/XcxxmK01EpqOyuxINew=="; String sessionKey = "tiihtNczf5v6AKRyjwEUhQ=="; String iv = "r7BXXKkLb8qrSNn05n0qiA=="; System.out.println(decrypt(encryptedData, sessionKey, iv)); } } ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/weChat/WeChatUtil.java
New file @@ -0,0 +1,203 @@ package com.ruoyi.account.util.weChat; import cn.hutool.http.HttpRequest; import cn.hutool.http.HttpResponse; import cn.hutool.http.HttpUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.util.HashMap; import java.util.Map; /** * 微信工具类 */ @Slf4j @Component public class WeChatUtil { @Value("${wx.appletsAppid}") private String wxAppletsAppid; @Value("${wx.appletsAppSecret}") private String wxAppletsAppSecret; @Value("{wx.officialAccountAppSecret}") private String officialAccountAppSecret; @Value("${wx.appid}") private String webAppId; @Value("${wx.appSecret}") private String webAppSecret; /** * 小程序使用jscode获取openid * @param jscode * @return */ public Map<String, Object> code2Session(String jscode) throws Exception{ String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + wxAppletsAppid + "&secret=" + wxAppletsAppSecret + "&js_code=" + jscode + "&grant_type=authorization_code"; HttpRequest get = HttpUtil.createGet(url); HttpResponse response = get.execute(); int status = response.getStatus(); if(200 != status){ throw new RuntimeException(response.body()); } JSONObject jsonObject = JSON.parseObject(response.body()); int errcode = jsonObject.getIntValue("errcode"); Map<String, Object> map = new HashMap<>(); map.put("errcode", errcode); if(errcode == 0){//成功 map.put("openid", jsonObject.getString("openid")); map.put("sessionKey", jsonObject.getString("session_key")); map.put("unionid", jsonObject.getString("unionid")); return map; } if(errcode == -1){//系统繁忙,此时请开发者稍候再试 map.put("msg", jsonObject.getString("errmsg")); return map; } if(errcode == 40029){//code 无效 map.put("msg", jsonObject.getString("errmsg")); return map; } if(errcode == 45011){//频率限制,每个用户每分钟100次 map.put("msg", jsonObject.getString("errmsg")); return map; } return null; } /** * 获取微信小程序token * @return */ public String getWxAppletsAccessToken() throws Exception{ String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + wxAppletsAppid + "&secret=" + wxAppletsAppSecret; HttpRequest get = HttpUtil.createGet(url); HttpResponse response = get.execute(); if(response.getStatus() != 200){ return ""; } JSONObject jsonObject = JSON.parseObject(response.body()); return jsonObject.getString("access_token"); } /** * 网站应用登录 * @param code * @return */ public Map<String, String> webAccessToken(String code) throws Exception{ String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + webAppId + "&secret=" + webAppSecret + "&code=" + code + "&grant_type=authorization_code"; HttpRequest get = HttpUtil.createGet(url); HttpResponse response = get.execute(); if(response.getStatus() != 200){ return null; } JSONObject jsonObject = JSON.parseObject(response.body()); int errcode = jsonObject.getIntValue("errcode"); Map<String, String> map = new HashMap<>(); if(errcode == 0){//成功 map.put("access_token", jsonObject.getString("access_token")); map.put("openid", jsonObject.getString("openid")); map.put("refresh_token", jsonObject.getString("refresh_token")); map.put("unionid", jsonObject.getString("unionid")); return map; } if(errcode == -1){//系统繁忙,此时请开发者稍候再试 map.put("msg", jsonObject.getString("errmsg")); return map; } if(errcode == 40029){//code 无效 map.put("msg", jsonObject.getString("errmsg")); return map; } if(errcode == 45011){//频率限制,每个用户每分钟100次 map.put("msg", jsonObject.getString("errmsg")); return map; } return map; } /** * 获取微信个人信息 * @param access_token * @param openid * @return */ public Map<String, Object> getUserInfo(String access_token, String openid) throws Exception{ String url = "https://api.weixin.qq.com/sns/userinfo?access_token=" + access_token + "&openid=" + openid; HttpRequest get = HttpUtil.createGet(url); HttpResponse response = get.execute(); if(response.getStatus() != 200){ return null; } JSONObject jsonObject = JSON.parseObject(response.body()); int errcode = jsonObject.getIntValue("errcode"); Map<String, Object> map = new HashMap<>(); if(errcode == 0){//成功 map.put("nickname", jsonObject.getString("nickname")); map.put("openid", jsonObject.getString("openid")); map.put("sex", jsonObject.getString("sex")); map.put("headimgurl", jsonObject.getString("headimgurl")); return map; } if(errcode == -1){//系统繁忙,此时请开发者稍候再试 map.put("msg", jsonObject.getString("errmsg")); return map; } if(errcode == 40029){//code 无效 map.put("msg", jsonObject.getString("errmsg")); return map; } if(errcode == 45011){//频率限制,每个用户每分钟100次 map.put("msg", jsonObject.getString("errmsg")); return map; } return map; } // /** // * 获取小程序二维码 // * @param page 跳转页 例如 pages/index/index // * @param scene 参数 a=1&b=2 // */ // public InputStream getwxacodeunlimit(String page, String scene){ // try { // String token = getWxAppletsAccessToken(); // if(StringUtils.isEmpty(token)){ // System.err.println("获取接口调用凭证失败"); // } // String url = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + token; // Map<String, Object> param = new HashMap<>(); // param.put("scene", scene); // param.put("page", page); // HttpHeaders httpHeaders = new HttpHeaders(); // MediaType type=MediaType.parseMediaType("application/json;charset=UTF-8"); // httpHeaders.setContentType(type); // HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(param, httpHeaders); // ResponseEntity<String> exchange = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class, new Object[0]); // String body1 = exchange.getBody(); //// System.err.println(body1); // ResponseEntity<byte[]> entity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, byte[].class, new Object[0]); // byte[] body = entity.getBody(); //// System.err.println(Base64.encodeBase64String(body)); // return new ByteArrayInputStream(body); // }catch (Exception e){ // e.printStackTrace(); // } // return null; // } } ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/weChat/WxPKCS7Encoder.java
New file @@ -0,0 +1,63 @@ package com.ruoyi.account.util.weChat; import java.nio.charset.Charset; import java.util.Arrays; /** * 微信小程序加解密 * @author pzb * @Date 2021/12/3 15:43 */ public class WxPKCS7Encoder { private static final Charset CHARSET = Charset.forName("utf-8"); private static final int BLOCK_SIZE = 32; /** * 获得对明文进行补位填充的字节. * * @param count * 需要进行填充补位操作的明文字节个数 * @return 补齐用的字节数组 */ public static byte[] encode(int count) { // 计算需要填充的位数 int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE); if (amountToPad == 0) { amountToPad = BLOCK_SIZE; } // 获得补位所用的字符 char padChr = chr(amountToPad); String tmp = new String(); for (int index = 0; index < amountToPad; index++) { tmp += padChr; } return tmp.getBytes(CHARSET); } /** * 删除解密后明文的补位字符 * * @param decrypted * 解密后的明文 * @return 删除补位字符后的明文 */ public static byte[] decode(byte[] decrypted) { int pad = decrypted[decrypted.length - 1]; if (pad < 1 || pad > 32) { pad = 0; } return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad); } /** * 将数字转化成ASCII码对应的字符,用于对明文进行补码 * * @param a * 需要转化的数字 * @return 转化得到的字符 */ public static char chr(int a) { byte target = (byte) (a & 0xFF); return (char) target; } } ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/vo/AppletLogin.java
New file @@ -0,0 +1,20 @@ package com.ruoyi.account.vo; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; /** * @author zhibing.pu * @Date 2024/11/21 11:40 */ @Data @ApiModel public class AppletLogin { @ApiModelProperty(value = "微信jscode", required = true) private String jscode; @ApiModelProperty(value = "手机号加密数据", required = true) private String encryptedData_phone; @ApiModelProperty(value = "加密算法的初始向量", required = true) private String iv_phone; } ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/AgreementController.java
@@ -1,20 +1,41 @@ package com.ruoyi.other.controller; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.ruoyi.common.core.web.domain.AjaxResult; import com.ruoyi.other.api.model.Agreement; import com.ruoyi.other.service.IAgreementService; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiOperation; import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; /** * <p> * 前端控制器 * </p> * * @author luodangjia * @since 2024-11-20 * @author zhibing.pu * @Date 2024/11/21 10:48 */ @RestController @RequestMapping("/agreement") public class AgreementController { @Resource private IAgreementService agreementService; /** * 获取协议详情 * @param type * @return */ @ResponseBody @GetMapping("/getAgreement/{type}") @ApiOperation(value = "获取协议", tags = {"小程序-登录注册"}) @ApiImplicitParams({ @ApiImplicitParam(value = "类型(1=用户协议,2=隐私协议,3=技师上门免责声明,4=注销协议,5门店提现免责声明)", name = "type", required = true, dataType = "int"), }) public AjaxResult<String> getAgreement(@PathVariable("type") Integer type){ Agreement one = agreementService.getOne(new LambdaQueryWrapper<Agreement>().eq(Agreement::getType, type)); return AjaxResult.success(null == one ? "" : one.getContent()); } } ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/mapper/AgreementMapper.java
@@ -1,16 +1,11 @@ package com.ruoyi.other.mapper; import com.ruoyi.other.api.domain.Agreement; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.ruoyi.other.api.model.Agreement; /** * <p> * Mapper 接口 * </p> * * @author luodangjia * @since 2024-11-20 * @author zhibing.pu * @Date 2024/11/21 10:46 */ public interface AgreementMapper extends BaseMapper<Agreement> { } ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/IAgreementService.java
New file @@ -0,0 +1,11 @@ package com.ruoyi.other.service; import com.baomidou.mybatisplus.extension.service.IService; import com.ruoyi.other.api.model.Agreement; /** * @author zhibing.pu * @Date 2024/11/21 10:46 */ public interface IAgreementService extends IService<Agreement> { } ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/impl/AgreementServiceImpl.java
@@ -1,20 +1,15 @@ package com.ruoyi.other.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.other.api.model.Agreement; import com.ruoyi.other.mapper.AgreementMapper; import com.ruoyi.other.api.domain.Agreement; import com.ruoyi.other.service.AgreementService; import com.ruoyi.other.service.IAgreementService; import org.springframework.stereotype.Service; /** * <p> * 服务实现类 * </p> * * @author luodangjia * @since 2024-11-20 * @author zhibing.pu * @Date 2024/11/21 10:48 */ @Service public class AgreementServiceImpl extends ServiceImpl<AgreementMapper, Agreement> implements AgreementService { public class AgreementServiceImpl extends ServiceImpl<AgreementMapper, Agreement> implements IAgreementService { } ruoyi-service/ruoyi-other/src/main/resources/bootstrap.yml
@@ -43,69 +43,69 @@ shared-configs: - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} --- seata: enabled: true application-id: ${spring.application.name} tx-service-group: seata_tx_group #此处配置自定义的seata事务分组名称 enable-auto-data-source-proxy: false #关闭数据库代理 service: vgroup-mapping: seata_tx_group: default data-source-proxy-mode: AT config: type: nacos nacos: # 开发环境 server-addr: 127.0.0.1:8848 namespace: cdf47c5f-2bf9-4dec-a616-a8dc653aceb9 group: DEFAULT_GROUP data-id: seata-server.properties username: nacos password: nacos # 生产环境 # server-addr: 192.168.0.137:8848,192.168.0.123:8848 # namespace: c6cf40b5-44e8-43f9-be61-8d10fa830e2a #seata: # enabled: true # application-id: ${spring.application.name} # tx-service-group: seata_tx_group #此处配置自定义的seata事务分组名称 # enable-auto-data-source-proxy: false #关闭数据库代理 # service: # vgroup-mapping: # seata_tx_group: default # data-source-proxy-mode: AT # config: # type: nacos # nacos: # # 开发环境 # server-addr: 127.0.0.1:8848 # namespace: cdf47c5f-2bf9-4dec-a616-a8dc653aceb9 # group: DEFAULT_GROUP # data-id: seata-server.properties # username: nacos # password: nacos registry: type: nacos nacos: application: seata-server # 开发环境 server-addr: 127.0.0.1:8848 namespace: cdf47c5f-2bf9-4dec-a616-a8dc653aceb9 group: DEFAULT_GROUP username: nacos password: nacos # 生产环境 # server-addr: 192.168.0.137:8848,192.168.0.123:8848 # namespace: c6cf40b5-44e8-43f9-be61-8d10fa830e2a # # 生产环境 ## server-addr: 192.168.0.137:8848,192.168.0.123:8848 ## namespace: c6cf40b5-44e8-43f9-be61-8d10fa830e2a ## group: DEFAULT_GROUP ## data-id: seata-server.properties ## username: nacos ## password: nacos # registry: # type: nacos # nacos: # application: seata-server # # 开发环境 # server-addr: 127.0.0.1:8848 # namespace: cdf47c5f-2bf9-4dec-a616-a8dc653aceb9 # group: DEFAULT_GROUP # username: nacos # password: nacos cloud: nacos: discovery: # 开发环境 server-addr: 127.0.0.1:8848 # nacos注册中心地址 namespace: cdf47c5f-2bf9-4dec-a616-a8dc653aceb9 # 命名空间 group: DEFAULT_GROUP application: seata-server #Nacos 中 Seata 名称 username: nacos password: nacos # 生产环境 # server-addr: 192.168.0.137:8848,192.168.0.123:8848 # namespace: c6cf40b5-44e8-43f9-be61-8d10fa830e2a # # 生产环境 ## server-addr: 192.168.0.137:8848,192.168.0.123:8848 ## namespace: c6cf40b5-44e8-43f9-be61-8d10fa830e2a ## group: DEFAULT_GROUP ## username: nacos ## password: nacos # cloud: # nacos: # discovery: # # 开发环境 # server-addr: 127.0.0.1:8848 # nacos注册中心地址 # namespace: cdf47c5f-2bf9-4dec-a616-a8dc653aceb9 # 命名空间 # group: DEFAULT_GROUP # application: seata-server #Nacos 中 Seata 名称 # username: nacos # password: nacos sentinel: transport: dashboard: 127.0.0.1:8080 # Sentinel控制台地址 eager: true # # 生产环境 ## server-addr: 192.168.0.137:8848,192.168.0.123:8848 ## namespace: c6cf40b5-44e8-43f9-be61-8d10fa830e2a ## group: DEFAULT_GROUP ## application: seata-server #Nacos 中 Seata 名称 ## username: nacos ## password: nacos # sentinel: # transport: # dashboard: 127.0.0.1:8080 # Sentinel控制台地址 # eager: true --- spring: config: ruoyi-service/ruoyi-other/src/main/resources/mapper/other/AgreementMapper.xml
New file @@ -0,0 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.ruoyi.other.mapper.AgreementMapper"> </mapper>