From 9c2b29176050996de04c2e3fa67ff77295934202 Mon Sep 17 00:00:00 2001 From: 无关风月 <443237572@qq.com> Date: 星期四, 13 二月 2025 13:57:11 +0800 Subject: [PATCH] 小程序登录 --- ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/pojo/AppletPhoneEncrypteData.java | 19 ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxCache.java | 117 ++ ruoyi-system/src/main/java/com/ruoyi/system/service/TTenantService.java | 6 ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/model/WeixinProperties.java | 79 + ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/pojo/AppletUserDecodeData.java | 52 + ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxException.java | 55 + ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TBillController.java | 3 ruoyi-common/src/main/java/com/ruoyi/common/redis/configure/FastJson2JsonRedisSerializer.java | 50 + ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/TaskUtil.java | 15 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TTenantServiceImpl.java | 15 ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java | 4 ruoyi-common/src/main/java/com/ruoyi/common/core/utils/HttpUtils.java | 311 ++++++ ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUserApplet.java | 267 +++++ ruoyi-common/src/main/java/com/ruoyi/common/redis/configure/RedisConfig.java | 43 ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxAppletTools.java | 123 ++ ruoyi-common/src/main/java/com/ruoyi/common/core/utils/Constants.java | 143 +++ ruoyi-system/pom.xml | 4 ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/WxLoginController.java | 247 +++++ ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxUtils.java | 175 +++ ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/pojo/AppletUserEncrypteData.java | 20 ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/body/resp/RespBody.java | 19 ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/pojo/Watermark.java | 9 ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxCacheTemplate.java | 34 ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/body/resp/AccessTokenRespBody.java | 28 ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TContractController.java | 128 ++ ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/SHA1.java | 36 ruoyi-system/src/main/java/com/ruoyi/system/model/TTenant.java | 3 ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/body/resq/Code2SessionResqBody.java | 21 ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WebUtils.java | 48 + ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxJsonUtils.java | 109 ++ ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java | 95 ++ ruoyi-common/src/main/java/com/ruoyi/common/core/exception/ServiceException.java | 74 + ruoyi-common/src/main/java/com/ruoyi/common/redis/service/RedisService.java | 273 ++++++ ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/body/resp/Code2SessionRespBody.java | 29 34 files changed, 2,644 insertions(+), 10 deletions(-) diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TBillController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TBillController.java index 4ce9a6b..be95875 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TBillController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TBillController.java @@ -34,6 +34,9 @@ @Autowired TBillService tBillService; + + + @PreAuthorize("@ss.hasPermi('system:bill:list')") @PostMapping("list") @ApiOperation("分页查询账单列表") diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TContractController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TContractController.java index 3402f97..3512e5e 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TContractController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TContractController.java @@ -47,7 +47,9 @@ import java.io.IOException; import java.math.BigDecimal; import java.net.URLEncoder; +import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; import java.util.*; /** @@ -72,6 +74,115 @@ private TBillService billService; @Autowired private TCheckAcceptRecordService checkAcceptRecordService; + @ApiOperation(value = "测试生成账单") + @PostMapping(value = "/testBill") + + public R testBill(String id) { + TContract contract = contractService.getById(id); + // 查询所有已签订的合同并且未生成第一笔账单的 + + List<TBill> bills = new ArrayList<>(); + List<TContractRentType> contractRentTypes = contractRentTypeService.list(); + contract.setFirstPayTime(contract.getStartTime().plusDays(10)); + // 第一次应缴费日期 + LocalDateTime firstPayTime = contract.getStartTime().plusDays(10).withHour(0).withMinute(0).withSecond(0); + LocalDate localDate = contract.getStartTime().plusDays(10).toLocalDate(); + LocalDate now = LocalDate.now(); + TBill rentBill = new TBill(); + rentBill.setContractId(contract.getId()); + rentBill.setContractNumber(contract.getContractNumber()); + LocalDateTime startPayTime = contract.getStartPayTime(); + LocalDateTime endTime1 = contract.getEndTime(); +// // 计算两个时间相差多少天 +// // 如果时间小于30天 需要计算每日租金 +// if (days<30){ +// rentBill.setPayableFeesMoney(contract.getMonthRent().divide(new BigDecimal("30"),2,BigDecimal.ROUND_DOWN).multiply(new BigDecimal(days))); +// }else{ +// rentBill.setPayableFeesMoney(contract.getPayType().equals("1")?contract.getMonthRent(): +// contract.getPayType().equals("2")?contract.getMonthRent().multiply(new BigDecimal("3")):contract.getMonthRent().multiply(new BigDecimal("12")).setScale(2,BigDecimal.ROUND_DOWN)); +// } + rentBill.setPayableFeesTime(firstPayTime); + rentBill.setPayFeesStatus("1"); + rentBill.setBillType("1"); + rentBill.setStartTime(contract.getStartPayTime()); + TContractRentType tContractRentType = contractRentTypes.stream().filter(e -> e.getContractId().equals(contract.getId())).findFirst().orElse(null); + + if (tContractRentType!=null && contract.getStartPayTime().plusMonths(contract.getPayType().equals("1")? 1:contract.getPayType().equals("2")? 3:12).isAfter(tContractRentType.getChangeTime())){ + // 计算租金变动的天数 + long moneyDays = ChronoUnit.DAYS.between(tContractRentType.getChangeTime(), contract.getStartPayTime().plusMonths(contract.getPayType().equals("1")? 1:contract.getPayType().equals("2")? 3:12))+1L; + contract.setChangeTime(LocalDateTime.now()); + // 递增递减的租金 + BigDecimal contractRentTypeMoney = new BigDecimal("0"); + // 不递增递减的租金 + BigDecimal originalMoney = new BigDecimal("0"); + // 原租金 + switch (tContractRentType.getIncreasingDecreasingType()){ + case 1: + switch (tContractRentType.getIncreasingDecreasing()){ + case 1: + contractRentTypeMoney =contractRentTypeMoney.add(contract.getChangeRent().multiply(new BigDecimal(100).add(tContractRentType.getNumericalValue()).divide(new BigDecimal(100),2,BigDecimal.ROUND_DOWN)).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays))); + contract.setChangeRent(contractRentTypeMoney.add(contract.getChangeRent().multiply(new BigDecimal(100).add(tContractRentType.getNumericalValue()).divide(new BigDecimal(100),2,BigDecimal.ROUND_DOWN)))); + break; + case 2: + contractRentTypeMoney = contractRentTypeMoney.add(contract.getChangeRent().multiply(new BigDecimal(100).subtract(tContractRentType.getNumericalValue()).divide(new BigDecimal(100),2,BigDecimal.ROUND_DOWN)).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays))); + contract.setChangeRent(contractRentTypeMoney.add(contract.getChangeRent().multiply(new BigDecimal(100).subtract(tContractRentType.getNumericalValue()).divide(new BigDecimal(100),2,BigDecimal.ROUND_DOWN)))); + break; + } + break; + case 2: + switch (tContractRentType.getIncreasingDecreasing()){ + case 1: + contractRentTypeMoney =contractRentTypeMoney.add(contract.getChangeRent().add(tContractRentType.getNumericalValue())).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays)); + contract.setChangeRent(contractRentTypeMoney.add(contract.getChangeRent().add(tContractRentType.getNumericalValue()))); + break; + case 2: + contractRentTypeMoney = contractRentTypeMoney.add(contract.getChangeRent().subtract(tContractRentType.getNumericalValue())).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays)); + contract.setChangeRent(contractRentTypeMoney.add(contract.getChangeRent().subtract(tContractRentType.getNumericalValue()))); + + break; + } + break; + } + // 不需要涨租金的时间段 + long originalDays = ChronoUnit.DAYS.between(contract.getFirstPayTime(), tContractRentType.getChangeTime()); + originalMoney=originalMoney.add(contract.getMonthRent().divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN)) + .multiply(new BigDecimal(originalDays)); + rentBill.setPayableFeesMoney(contractRentTypeMoney.add(originalMoney)); + rentBill.setOutstandingMoney(rentBill.getPayableFeesMoney()); + if (contract.getFirstPayTime().plusMonths(contract.getPayType().equals("1")? 1:contract.getPayType().equals("2")? 3:12).isAfter(contract.getEndTime())){ + rentBill.setEndTime(contract.getFirstPayTime().plusMonths(contract.getPayType().equals("1")? 1:contract.getPayType().equals("2")? 3:12)); + }else{ + rentBill.setEndTime(contract.getEndTime()); + } + }else{ + if (contract.getFirstPayTime().plusMonths(contract.getPayType().equals("1")? 1:contract.getPayType().equals("2")? 3:12).isAfter(contract.getEndTime())){ + rentBill.setEndTime(contract.getFirstPayTime().plusMonths(contract.getPayType().equals("1")? 1:contract.getPayType().equals("2")? 3:12)); + }else{ + rentBill.setEndTime(contract.getEndTime()); + } + // 不走递增递减 + long allDays = ChronoUnit.DAYS.between(contract.getFirstPayTime(), rentBill.getEndTime()); + rentBill.setPayableFeesMoney(contract.getMonthRent().divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(allDays))); + rentBill.setOutstandingMoney(rentBill.getPayableFeesMoney()); + + } + // 租金账单 + bills.add(rentBill); + // 押金账单 + TBill depositBill = new TBill(); + depositBill.setContractId(contract.getId()); + depositBill.setContractNumber(contract.getContractNumber()); + depositBill.setPayableFeesMoney(contract.getDeposit()); + depositBill.setOutstandingMoney(depositBill.getPayableFeesMoney()); + + depositBill.setPayableFeesTime(firstPayTime); + depositBill.setPayFeesStatus("1"); + depositBill.setBillType("2"); + contractService.updateById(contract); + billService.save(rentBill); + billService.save(depositBill); + return R.ok(); + } @ApiOperation(value = "获取合同分页列表") @PostMapping(value = "/contractList") @PreAuthorize("@ss.hasPermi('system:contract:list')") @@ -100,6 +211,8 @@ @Log(title = "合同管理-编辑合同", businessType = BusinessType.UPDATE) @ApiOperation(value = "编辑合同") @PostMapping(value = "/updateContract") + @PreAuthorize("@ss.hasPermi('system:contract:update')") + public R<Boolean> updateContract(@Validated @RequestBody TContractDTO dto) { contractService.updateById(dto); contractRentTypeService.remove(new LambdaQueryWrapper<TContractRentType>() @@ -118,6 +231,8 @@ } @Log(title = "合同管理-批量删除合同", businessType = BusinessType.DELETE) @ApiOperation(value = "批量删除合同") + @PreAuthorize("@ss.hasPermi('system:contract:delete')") + @DeleteMapping(value = "/deleteContractByIds") public R<Boolean> deleteContractByIds (@RequestParam String ids) { @@ -129,6 +244,8 @@ @ApiOperation(value = "查询合同信息信息") @GetMapping(value = "/getContractById") + @PreAuthorize("@ss.hasPermi('system:contract:detail')") + public R<TContractVO> getContractById(@RequestParam String id) { TContractVO res = new TContractVO(); TContract contract = contractService.getById(id); @@ -165,6 +282,8 @@ } @Log(title = "合同管理-撤销审批", businessType = BusinessType.UPDATE) @ApiOperation(value = "撤销审批") + @PreAuthorize("@ss.hasPermi('system:contract:revoke')") + @GetMapping(value = "/updateContractStatus") public R<Boolean> updateContractStatus(String id) { TContract contract = contractService.getById(id); @@ -172,9 +291,12 @@ contractService.updateById(contract); return R.ok(); } + @PreAuthorize("@ss.hasPermi('system:contract:confirm')") + @Log(title = "合同管理-确认结算", businessType = BusinessType.UPDATE) @ApiOperation(value = "确认结算") @PostMapping(value = "/confirmSettlement") + public R<Boolean> confirmSettlement(String id) { TContract contract = contractService.getById(id); contract.setStatus("8"); @@ -183,17 +305,20 @@ } @ApiOperation(value = "终止合同剩余未缴费账单列表") @PostMapping(value = "/contractBillList") + @PreAuthorize("@ss.hasPermi('system:contract:billList')") public R<PageInfo<BillVO>> contractBillList(@RequestBody TContractBillQuery query) { return R.ok(contractService.contractBillList(query)); } @ApiOperation(value = "终止合同") @PostMapping(value = "/terminateContract") + @PreAuthorize("@ss.hasPermi('system:contract:terminate')") public R terminateContract(@RequestBody TerminateContractDTO dto) { contractService.terminateContract(dto); return R.ok(); } @ApiOperation(value = "根据合同id查看验收记录") @GetMapping(value = "/getCheckByContractId") + @PreAuthorize("@ss.hasPermi('system:contract:checkDetail')") public R<CheckAcceptRecordVO> getCheckByContractId(String id) { return R.ok(contractService.getCheckByContractId(id)); } @@ -201,6 +326,8 @@ private WordUtil wordUtil; @ApiOperation(value = "生成合同附件") @PostMapping("/set") + @Log(title = "生成合同附件", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:contract:set')") public R<List<String>> set(@RequestBody SetContractDto dto,HttpServletResponse response){ List<TContract> list = contractService.lambdaQuery().in(TContract::getId, dto.getIds()).list(); List<String> res = new ArrayList<>(); @@ -238,6 +365,7 @@ * 导出 */ @ApiOperation(value = "导出") + @PreAuthorize("@ss.hasPermi('system:contract:export')") @Log(title = "导出", businessType = BusinessType.EXPORT) @PostMapping("/export") public void exportOpticalInspection(@RequestBody TContractQuery query) diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/TaskUtil.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/TaskUtil.java index e68b651..023d435 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/TaskUtil.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/TaskUtil.java @@ -51,19 +51,16 @@ long hours = ChronoUnit.HOURS.between(payableFeesTime, now); long l = hours / 72; if (l>0){ - // 计算每天租金 - long days = ChronoUnit.DAYS.between(tBill.getStartTime(),tBill.getEndTime()); - BigDecimal everyDayMoney = tBill.getPayableFeesMoney().divide(new BigDecimal(days), 2, BigDecimal.ROUND_DOWN); // 违约金比例 BigDecimal proportion = contract.getProportion(); - // 预期x天后的违约金 - BigDecimal money = everyDayMoney.multiply(proportion).multiply(new BigDecimal(l)); - tBill.setPayableFeesPenalty(money); - tBill.setOutstandingMoney(money); - + // 应缴违约金 + BigDecimal money = tBill.getOutstandingMoney().multiply(proportion); + TBill changeBill = new TBill(); + changeBill.setId(tBill.getId()); + changeBill.setPayableFeesPenalty(money); + billService.lockAndUpdateInfo(changeBill,2); } } - billService.updateBatchById(list); } catch (Exception e) { e.printStackTrace(); diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/WxLoginController.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/WxLoginController.java new file mode 100644 index 0000000..7f39fab --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/WxLoginController.java @@ -0,0 +1,247 @@ +package com.ruoyi.web.controller.api; + +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; + +import com.ruoyi.common.core.domain.entity.SysMenu; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.model.LoginBody; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.domain.model.LoginUserApplet; +import com.ruoyi.common.core.utils.HttpUtils; +import com.ruoyi.common.redis.service.RedisService; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.framework.web.service.SysLoginService; +import com.ruoyi.framework.web.service.TokenService; +import com.ruoyi.system.model.TTenant; +import com.ruoyi.system.service.TTenantService; +import com.ruoyi.system.utils.wx.body.resp.Code2SessionRespBody; +import com.ruoyi.system.utils.wx.body.resq.Code2SessionResqBody; +import com.ruoyi.system.utils.wx.model.WeixinProperties; +import com.ruoyi.system.utils.wx.pojo.AppletUserDecodeData; +import com.ruoyi.system.utils.wx.pojo.AppletUserEncrypteData; +import com.ruoyi.system.utils.wx.tools.WxAppletTools; +import com.ruoyi.system.utils.wx.tools.WxUtils; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.client.RestTemplate; + +import javax.annotation.Resource; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * <p> + * 微信小程序登录 前端控制器 + * </p> + * + * @author xiaochen + * @since 2024-08-06 + */ +@Slf4j +@RestController +@RequestMapping("/wxLogin") +public class WxLoginController { + + @Autowired + private WeixinProperties wxConfig; + @Autowired + private RestTemplate wxRestTemplate; + @Resource + private RedisService redisService; + @Resource + private TTenantService tTenantService; + + /** + * 上传文件存储在本地的根路径 + */ +// @Value("${file.upload.location}") +// private String localFilePath; + + @Resource + private TokenService tokenService; + @Autowired + private SysLoginService loginService; + /** + * 账号密码登录 + * + * @param loginBody 登录信息 + * @return 结果 + */ + @ApiOperation(value = "账号密码登录",notes = "管理员账号密码登录") + @PostMapping("/login") + public AjaxResult login(@RequestBody LoginBody loginBody) + { + AjaxResult ajax = AjaxResult.success(); + // 生成令牌 + LoginUser loginUser = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(), + loginBody.getUuid()); + ajax.put(Constants.TOKEN, tokenService.createToken(loginUser)); + List<SysRole> roles = loginUser.getUser().getRoles(); + if(CollectionUtils.isEmpty(roles)){ + return AjaxResult.error("请关联角色!"); + } + if(roles.get(0).getStatus() == 1){ + return AjaxResult.error("该账号角色已被禁用!"); + } + return ajax; + } + + @ApiOperation(value = "通过code获得openid,获取用户信息",tags = {"微信小程序登录"}) + @PostMapping("/openIdByJsCode") + public R<Map<String, Object>> openIdByJsCode(@RequestBody AppletUserEncrypteData data) { + log.info("<<<<<<<<换取openid开始<<<<<<<<:{}", data.getCode()); + WxAppletTools appletTools = new WxAppletTools(wxRestTemplate, wxConfig, redisService); + Code2SessionRespBody body = appletTools.getOpenIdByJscode2session(new Code2SessionResqBody().build(data.getCode())); + String openid = body.getOpenid(); + String sessionKey = body.getSessionKey(); + // 用户信息解密 数据验签 +// if (StringUtils.isNotBlank(data.getSignature())) { +// WxUtils.verifySignature(data.getRawData(), sessionKey, data.getSignature()); +// } + if(StringUtils.isEmpty(data.getEncryptedData()) || StringUtils.isEmpty(data.getIv())){ + return R.fail("已拒绝授权"); + } + AppletUserDecodeData appletUserDecodeData = WxUtils.encryptedData(data.getEncryptedData(), sessionKey, data.getIv()); + appletUserDecodeData.setOpenId(openid); + // 先使用openId和当前手机号进行查询 + TTenant tenant = tTenantService.getOne(Wrappers.lambdaQuery(TTenant.class) + .eq(TTenant::getOpenId, appletUserDecodeData.getOpenId()) + .eq(TTenant::getPhone, appletUserDecodeData.getPhoneNumber())); + if (tenant==null){ +// appUser.setTenantAttributes(); +// appUser.setTenantType(); + tenant.setPhone(appletUserDecodeData.getPhoneNumber()); + tenant.setAccount(appletUserDecodeData.getPhoneNumber()); + tenant.setPassword(SecurityUtils.encryptPassword(appletUserDecodeData.getPhoneNumber().substring(5))); + tenant.setOpenId(appletUserDecodeData.getOpenId()); + tTenantService.save(tenant); + } + LoginUserApplet loginUserApplet = new LoginUserApplet(); + loginUserApplet.setUserId(Long.valueOf(tenant.getId())); + Map<String, Object> tokenInfos = new HashMap<>(); + tokenInfos.put("token",tokenService.createTokenApplet(loginUserApplet)); + tokenInfos.put("info",loginUserApplet); + return R.ok(tokenInfos); + } + + + + + +// @ApiOperation(value = "获取微信小程序二维码",tags = {"获取微信小程序二维码"}) +// @PostMapping("/getQRCode") +// public AjaxResult getQRCode() { +// InputStream inputStream = null; +// OutputStream outputStream = null; +// WxAppletTools appletTools = new WxAppletTools(wxRestTemplate, wxConfig, redisService); +// String accessToken = appletTools.getAccessToken(""); +// try { +// String url = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + accessToken; +// Map<String, Object> param = new HashMap<>(); +//// param.put("page", "pageA/houseDetail"); +// param.put("check_path", false); +// param.put("env_version", "trial"); +// param.put("width", 200); //二维码尺寸 +// param.put("is_hyaline", true); // 是否需要透明底色, is_hyaline 为true时,生成透明底色的小程序码 参数仅对小程序码生效 +// param.put("auto_color", true); // 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调 参数仅对小程序码生效 +// Map<String, Object> line_color = new HashMap<>(); +// line_color.put("r", 0); +// line_color.put("g", 0); +// line_color.put("b", 0); +// param.put("line_color", line_color); +// System.err.println("调用生成微信URL接口传参:" + param); +// MultiValueMap<String, String> headers = new LinkedMultiValueMap<>(); +// HttpEntity requestEntity = new HttpEntity(param, headers); +// ResponseEntity<byte[]> entity = wxRestTemplate.exchange(url, HttpMethod.POST, requestEntity, byte[].class, new Object[0]); +// System.err.println("调用小程序生成微信永久小程序码URL接口返回结果:" + entity.getBody()); +// byte[] result = entity.getBody(); +// System.err.println(Base64.encodeBase64String(result)); +// inputStream = new ByteArrayInputStream(result); +// String finalFileName = System.currentTimeMillis() + "" + new SecureRandom().nextInt(0x0400) + ".jpeg"; +//// MultipartFile multipartFile = convertInputStreamToMultipartFile(inputStream, finalFileName, "image/jpeg"); +//// String name = FileUploadUtils.upload(localFilePath, multipartFile); +//// System.err.println(name); +// return AjaxResult.success(null); +// } catch (Exception e) { +// System.err.println("调用小程序生成微信永久小程序码URL接口异常" + e); +// } finally { +// if (inputStream != null) { +// try { +// inputStream.close(); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// } +// if (outputStream != null) { +// try { +// outputStream.close(); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// } +// } +// return AjaxResult.success(); +// } + + + /** + * 获取微信token + * @return + */ + @PostMapping("/getWXToken") + public R<String> getWXToken(){ + WxAppletTools appletTools = new WxAppletTools(wxRestTemplate, wxConfig, redisService); + String accessToken = appletTools.getAccessToken(""); + return R.ok(accessToken); + } + + + /** + * 敏感词检测 + * @param content + * @param openid + * @return + */ + @PostMapping("/sensitiveWordDetection") + public R<Boolean> sensitiveWordDetection (@RequestParam("content") String content, @RequestParam("openid") String openid){ + WxAppletTools appletTools = new WxAppletTools(wxRestTemplate, wxConfig, redisService); + String accessToken = appletTools.getAccessToken(""); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("content", content); + jsonObject.put("version", 2); + jsonObject.put("scene", 2); + jsonObject.put("openid", openid); + String post = HttpUtils.post("https://api.weixin.qq.com/wxa/msg_sec_check?access_token=" + accessToken, jsonObject.toString()); + JSONObject object = JSONObject.parseObject(post); + Integer errcode = object.getInteger("errcode"); + if(0 != errcode){ + throw new RuntimeException(object.getString("errmsg")); + } + JSONArray detail = object.getJSONArray("detail"); + for (int i = 0; i < detail.size(); i++) { + JSONObject jsonObject1 = detail.getJSONObject(i); + Integer errcode1 = jsonObject1.getInteger("errcode"); + if(0 == errcode1){ + String suggest = jsonObject1.getString("suggest"); + Integer label = jsonObject1.getInteger("label"); + String keyword = jsonObject1.getString("keyword"); + Integer prob = jsonObject1.getInteger("prob"); + if(("risky".equals(suggest) || "review".equals(suggest)) && 100 != label && com.ruoyi.common.utils.StringUtils.isNotEmpty(keyword) && 80 <= prob){ + return R.ok(true); + } + } + } + return R.ok(false); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java index 942e7a3..82b912c 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java @@ -83,6 +83,10 @@ * 令牌前缀 */ public static final String LOGIN_USER_KEY = "login_user_key"; + /** + * 小程序 + */ + public static final String LOGIN_USER_APPLET_KEY = "login_user_applet_key"; /** * 用户ID diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUserApplet.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUserApplet.java new file mode 100644 index 0000000..963aa01 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUserApplet.java @@ -0,0 +1,267 @@ +package com.ruoyi.common.core.domain.model; + +import com.alibaba.fastjson2.annotation.JSONField; +import com.ruoyi.common.core.domain.entity.SysUser; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.Collection; +import java.util.Set; + +/** + * 登录用户身份权限 + * + * @author ruoyi + */ +public class LoginUserApplet implements UserDetails +{ + private static final long serialVersionUID = 1L; + + /** + * 用户ID + */ + private Long userId; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 用户唯一标识 + */ + private String token; + + /** + * 登录时间 + */ + private Long loginTime; + + /** + * 过期时间 + */ + private Long expireTime; + + /** + * 登录IP地址 + */ + private String ipaddr; + + /** + * 登录地点 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * 权限列表 + */ + private Set<String> permissions; + + /** + * 用户信息 + */ + private SysUser user; + + public LoginUserApplet() + { + } + + public LoginUserApplet(SysUser user, Set<String> permissions) + { + this.user = user; + this.permissions = permissions; + } + + public LoginUserApplet(Long userId, Long deptId, SysUser user, Set<String> permissions) + { + this.userId = userId; + this.deptId = deptId; + this.user = user; + this.permissions = permissions; + } + + public Long getUserId() + { + return userId; + } + + public void setUserId(Long userId) + { + this.userId = userId; + } + + public Long getDeptId() + { + return deptId; + } + + public void setDeptId(Long deptId) + { + this.deptId = deptId; + } + + public String getToken() + { + return token; + } + + public void setToken(String token) + { + this.token = token; + } + + @JSONField(serialize = false) + @Override + public String getPassword() + { + return user.getPassword(); + } + + @Override + public String getUsername() + { + return user.getUserName(); + } + + /** + * 账户是否未过期,过期无法验证 + */ + @JSONField(serialize = false) + @Override + public boolean isAccountNonExpired() + { + return true; + } + + /** + * 指定用户是否解锁,锁定的用户无法进行身份验证 + * + * @return + */ + @JSONField(serialize = false) + @Override + public boolean isAccountNonLocked() + { + return true; + } + + /** + * 指示是否已过期的用户的凭据(密码),过期的凭据防止认证 + * + * @return + */ + @JSONField(serialize = false) + @Override + public boolean isCredentialsNonExpired() + { + return true; + } + + /** + * 是否可用 ,禁用的用户不能身份验证 + * + * @return + */ + @JSONField(serialize = false) + @Override + public boolean isEnabled() + { + return true; + } + + public Long getLoginTime() + { + return loginTime; + } + + public void setLoginTime(Long loginTime) + { + this.loginTime = loginTime; + } + + public String getIpaddr() + { + return ipaddr; + } + + public void setIpaddr(String ipaddr) + { + this.ipaddr = ipaddr; + } + + public String getLoginLocation() + { + return loginLocation; + } + + public void setLoginLocation(String loginLocation) + { + this.loginLocation = loginLocation; + } + + public String getBrowser() + { + return browser; + } + + public void setBrowser(String browser) + { + this.browser = browser; + } + + public String getOs() + { + return os; + } + + public void setOs(String os) + { + this.os = os; + } + + public Long getExpireTime() + { + return expireTime; + } + + public void setExpireTime(Long expireTime) + { + this.expireTime = expireTime; + } + + public Set<String> getPermissions() + { + return permissions; + } + + public void setPermissions(Set<String> permissions) + { + this.permissions = permissions; + } + + public SysUser getUser() + { + return user; + } + + public void setUser(SysUser user) + { + this.user = user; + } + + @Override + public Collection<? extends GrantedAuthority> getAuthorities() + { + return null; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/exception/ServiceException.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/exception/ServiceException.java new file mode 100644 index 0000000..4983866 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/exception/ServiceException.java @@ -0,0 +1,74 @@ +package com.ruoyi.common.core.exception; + +/** + * 业务异常 + * + * @author ruoyi + */ +public final class ServiceException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + /** + * 错误码 + */ + private Integer code; + + /** + * 错误提示 + */ + private String message; + + /** + * 错误明细,内部调试错误 + * + * 和 {@link CommonResult#getDetailMessage()} 一致的设计 + */ + private String detailMessage; + + /** + * 空构造方法,避免反序列化问题 + */ + public ServiceException() + { + } + + public ServiceException(String message) + { + this.message = message; + } + + public ServiceException(String message, Integer code) + { + this.message = message; + this.code = code; + } + + public String getDetailMessage() + { + return detailMessage; + } + + @Override + public String getMessage() + { + return message; + } + + public Integer getCode() + { + return code; + } + + public ServiceException setMessage(String message) + { + this.message = message; + return this; + } + + public ServiceException setDetailMessage(String detailMessage) + { + this.detailMessage = detailMessage; + return this; + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/utils/Constants.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/utils/Constants.java new file mode 100644 index 0000000..8144c3a --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/utils/Constants.java @@ -0,0 +1,143 @@ +package com.ruoyi.common.core.utils; + +/** + * 通用常量信息 + * + * @author ruoyi + */ +public class Constants +{ + /** + * UTF-8 字符集 + */ + public static final String UTF8 = "UTF-8"; + + /** + * GBK 字符集 + */ + public static final String GBK = "GBK"; + + /** + * www主域 + */ + public static final String WWW = "www."; + + /** + * RMI 远程方法调用 + */ + public static final String LOOKUP_RMI = "rmi:"; + + /** + * LDAP 远程方法调用 + */ + public static final String LOOKUP_LDAP = "ldap:"; + + /** + * LDAPS 远程方法调用 + */ + public static final String LOOKUP_LDAPS = "ldaps:"; + + /** + * http请求 + */ + public static final String HTTP = "http://"; + + /** + * https请求 + */ + public static final String HTTPS = "https://"; + + /** + * 成功标记 + */ + public static final Integer SUCCESS = 200; + + /** + * 失败标记 + */ + public static final Integer FAIL = 500; + + /** + * 登录成功状态 + */ + public static final String LOGIN_SUCCESS_STATUS = "1"; + + /** + * 登录失败状态 + */ + public static final String LOGIN_FAIL_STATUS = "2"; + + /** + * 登录成功 + */ + public static final String LOGIN_SUCCESS = "Success"; + + /** + * 注销 + */ + public static final String LOGOUT = "Logout"; + + /** + * 注册 + */ + public static final String REGISTER = "Register"; + + /** + * 登录失败 + */ + public static final String LOGIN_FAIL = "Error"; + + /** + * 当前记录起始索引 + */ + public static final String PAGE_NUM = "pageNum"; + + /** + * 每页显示记录数 + */ + public static final String PAGE_SIZE = "pageSize"; + + /** + * 排序列 + */ + public static final String ORDER_BY_COLUMN = "orderByColumn"; + + /** + * 排序的方向 "desc" 或者 "asc". + */ + public static final String IS_ASC = "isAsc"; + + /** + * 验证码有效期(分钟) + */ + public static final long CAPTCHA_EXPIRATION = 2; + + /** + * 资源映射路径 前缀 + */ + public static final String RESOURCE_PREFIX = "/profile"; + + /** + * 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加) + */ + public static final String[] JOB_WHITELIST_STR = { "com.ruoyi" }; + + /** + * 时间格式化 + */ + public static final String DATE_FORMATTER_TIME = "yyyy-MM-dd HH:mm:ss"; + public static final String DATE_FORMATTER_DATE = "yyyy-MM-dd"; + /** + * 修改手机号后缀 + */ + public static final String UPDATE_PHONE = "_updatePhone"; + /** + * 申请建桩后缀 + */ + public static final String APPLY_CHARGING = "_applyCharging"; + /** + * 定时任务违规的字符 + */ + public static final String[] JOB_ERROR_STR = { "java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml", + "org.springframework", "org.apache", "com.ruoyi.common.core.utils.file" }; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/utils/HttpUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/utils/HttpUtils.java new file mode 100644 index 0000000..7d25f7a --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/utils/HttpUtils.java @@ -0,0 +1,311 @@ +package com.ruoyi.common.core.utils; + +import com.ruoyi.common.core.exception.ServiceException; +import com.ruoyi.common.utils.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.net.ssl.*; +import java.io.*; +import java.net.*; +import java.nio.charset.StandardCharsets; +import java.security.cert.X509Certificate; + +/** + * 通用http发送方法 + * + * @author ruoyi + */ +public class HttpUtils +{ + private static final Logger log = LoggerFactory.getLogger(HttpUtils.class); + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url) + { + return sendGet(url, StringUtils.EMPTY); + } + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url, String param) + { + return sendGet(url, param, Constants.UTF8); + } + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @param contentType 编码类型 + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url, String param, String contentType) + { + StringBuilder result = new StringBuilder(); + BufferedReader in = null; + try + { + String urlNameString = StringUtils.isNotBlank(param) ? url + "?" + param : url; + log.info("sendGet - {}", urlNameString); + URL realUrl = new URL(urlNameString); + URLConnection connection = realUrl.openConnection(); + connection.setRequestProperty("accept", "*/*"); + connection.setRequestProperty("connection", "Keep-Alive"); + connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + connection.connect(); + in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType)); + String line; + while ((line = in.readLine()) != null) + { + result.append(line); + } + log.info("recv - {}", result); + } + catch (ConnectException e) + { + log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e); + } + catch (SocketTimeoutException e) + { + log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e); + } + catch (IOException e) + { + log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e); + } + catch (Exception e) + { + log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e); + } + finally + { + try + { + if (in != null) + { + in.close(); + } + } + catch (Exception ex) + { + log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); + } + } + return result.toString(); + } + + /** + * 向指定 URL 发送POST方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @return 所代表远程资源的响应结果 + */ + public static String sendPost(String url, String param) + { + PrintWriter out = null; + BufferedReader in = null; + StringBuilder result = new StringBuilder(); + try + { + log.info("sendPost - {}", url); + URL realUrl = new URL(url); + URLConnection conn = realUrl.openConnection(); + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("connection", "Keep-Alive"); + conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + conn.setRequestProperty("Accept-Charset", "utf-8"); + conn.setRequestProperty("contentType", "utf-8"); + conn.setDoOutput(true); + conn.setDoInput(true); + out = new PrintWriter(conn.getOutputStream()); + out.print(param); + out.flush(); + in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8)); + String line; + while ((line = in.readLine()) != null) + { + result.append(line); + } + log.info("recv - {}", result); + } + catch (ConnectException e) + { + log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e); + } + catch (SocketTimeoutException e) + { + log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e); + } + catch (IOException e) + { + log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e); + } + catch (Exception e) + { + log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e); + } + finally + { + try + { + if (out != null) + { + out.close(); + } + if (in != null) + { + in.close(); + } + } + catch (IOException ex) + { + log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); + } + } + return result.toString(); + } + + public static String sendSSLPost(String url, String param) + { + StringBuilder result = new StringBuilder(); + String urlNameString = url + "?" + param; + try + { + log.info("sendSSLPost - {}", urlNameString); + SSLContext sc = SSLContext.getInstance("SSL"); + sc.init(null, new TrustManager[] { new TrustAnyTrustManager() }, new java.security.SecureRandom()); + URL console = new URL(urlNameString); + HttpsURLConnection conn = (HttpsURLConnection) console.openConnection(); + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("connection", "Keep-Alive"); + conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + conn.setRequestProperty("Accept-Charset", "utf-8"); + conn.setRequestProperty("contentType", "utf-8"); + conn.setDoOutput(true); + conn.setDoInput(true); + + conn.setSSLSocketFactory(sc.getSocketFactory()); + conn.setHostnameVerifier(new TrustAnyHostnameVerifier()); + conn.connect(); + InputStream is = conn.getInputStream(); + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + String ret = ""; + while ((ret = br.readLine()) != null) + { + if (ret != null && !"".equals(ret.trim())) + { + result.append(new String(ret.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8)); + } + } + log.info("recv - {}", result); + conn.disconnect(); + br.close(); + } + catch (ConnectException e) + { + log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e); + } + catch (SocketTimeoutException e) + { + log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e); + } + catch (IOException e) + { + log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e); + } + catch (Exception e) + { + log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e); + } + return result.toString(); + } + + public static String post(String strURL, String params) { + String result = ""; + BufferedReader reader = null; + try { + URL url = new URL(strURL);// 创建连接 + HttpURLConnection connection = (HttpURLConnection) url + .openConnection(); + connection.setDoOutput(true); + connection.setDoInput(true); + connection.setUseCaches(false); + connection.setInstanceFollowRedirects(true); + connection.setRequestMethod("POST"); // 设置请求方式 + connection.setRequestProperty("Accept", "application/json"); // 设置接收数据的格式 + connection.setRequestProperty("Content-Type", "application/json"); // 设置发送数据的格式 + connection.connect(); + if (params != null && !StringUtils.isEmpty(params)) { + byte[] writebytes = params.getBytes(); + // 设置文件长度 + // connection.setRequestProperty("Content-Length", String.valueOf(writebytes.length)); + OutputStream outwritestream = connection.getOutputStream(); + outwritestream.write(params.getBytes()); + outwritestream.flush(); + outwritestream.close(); + // Log.d("hlhupload", "doJsonPost: conn"+connection.getResponseCode()); + } + if (connection.getResponseCode() == 200) { + log.info("<<<<<<<<<<<<<请求响应:{}", connection.getResponseMessage()); + reader = new BufferedReader( + new InputStreamReader(connection.getInputStream())); + result = reader.readLine(); + log.info("<<<<<<<<<<<<<请求响应:{}", result); + } else { + throw new ServiceException(connection.getResponseMessage()); + } + } catch (Exception e) { + throw new ServiceException("http的post请求异常!" + e.getMessage()); + } finally { + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return result; + } + + private static class TrustAnyTrustManager implements X509TrustManager + { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) + { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) + { + } + + @Override + public X509Certificate[] getAcceptedIssuers() + { + return new X509Certificate[] {}; + } + } + + private static class TrustAnyHostnameVerifier implements HostnameVerifier + { + @Override + public boolean verify(String hostname, SSLSession session) + { + return true; + } + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/redis/configure/FastJson2JsonRedisSerializer.java b/ruoyi-common/src/main/java/com/ruoyi/common/redis/configure/FastJson2JsonRedisSerializer.java new file mode 100644 index 0000000..c9f3b66 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/redis/configure/FastJson2JsonRedisSerializer.java @@ -0,0 +1,50 @@ +package com.ruoyi.common.redis.configure; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONReader; +import com.alibaba.fastjson2.JSONWriter; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.data.redis.serializer.SerializationException; + +import java.nio.charset.Charset; + +/** + * Redis使用FastJson序列化 + * + * @author ruoyi + */ +public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T> +{ + public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); + + private Class<T> clazz; + + + public FastJson2JsonRedisSerializer(Class<T> clazz) + { + super(); + this.clazz = clazz; + } + + @Override + public byte[] serialize(T t) throws SerializationException + { + if (t == null) + { + return new byte[0]; + } + return JSON.toJSONString(t, JSONWriter.Feature.WriteClassName).getBytes(DEFAULT_CHARSET); + } + + @Override + public T deserialize(byte[] bytes) throws SerializationException + { + if (bytes == null || bytes.length <= 0) + { + return null; + } + String str = new String(bytes, DEFAULT_CHARSET); + + return JSON.parseObject(str, clazz, JSONReader.Feature.SupportAutoType); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/redis/configure/RedisConfig.java b/ruoyi-common/src/main/java/com/ruoyi/common/redis/configure/RedisConfig.java new file mode 100644 index 0000000..6e1ec8a --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/redis/configure/RedisConfig.java @@ -0,0 +1,43 @@ +package com.ruoyi.common.redis.configure; + +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; +import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +/** + * redis配置 + * + * @author ruoyi + */ +@Configuration +@EnableCaching +@AutoConfigureBefore(RedisAutoConfiguration.class) +public class RedisConfig extends CachingConfigurerSupport +{ + @Bean + @SuppressWarnings(value = { "unchecked", "rawtypes" }) + public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) + { + RedisTemplate<Object, Object> template = new RedisTemplate<>(); + template.setConnectionFactory(connectionFactory); + + FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class); + + // 使用StringRedisSerializer来序列化和反序列化redis的key值 + template.setKeySerializer(new StringRedisSerializer()); + template.setValueSerializer(serializer); + + // Hash的key也采用StringRedisSerializer的序列化方式 + template.setHashKeySerializer(new StringRedisSerializer()); + template.setHashValueSerializer(serializer); + + template.afterPropertiesSet(); + return template; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/redis/service/RedisService.java b/ruoyi-common/src/main/java/com/ruoyi/common/redis/service/RedisService.java new file mode 100644 index 0000000..1ae72cd --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/redis/service/RedisService.java @@ -0,0 +1,273 @@ +package com.ruoyi.common.redis.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.BoundSetOperations; +import org.springframework.data.redis.core.HashOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * spring redis 工具类 + * + * @author ruoyi + **/ +@SuppressWarnings(value = { "unchecked", "rawtypes" }) +@Component +public class RedisService +{ + @Autowired + public RedisTemplate redisTemplate; + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + */ + public <T> void setCacheObject(final String key, final T value) + { + redisTemplate.opsForValue().set(key, value); + } + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + * @param timeout 时间 + * @param timeUnit 时间颗粒度 + */ + public <T> void setCacheObject(final String key, final T value, final Long timeout, final TimeUnit timeUnit) + { + redisTemplate.opsForValue().set(key, value, timeout, timeUnit); + } + + /** + * 设置有效时间 + * + * @param key Redis键 + * @param timeout 超时时间 + * @return true=设置成功;false=设置失败 + */ + public boolean expire(final String key, final long timeout) + { + return expire(key, timeout, TimeUnit.SECONDS); + } + + /** + * 设置有效时间 + * + * @param key Redis键 + * @param timeout 超时时间 + * @param unit 时间单位 + * @return true=设置成功;false=设置失败 + */ + public boolean expire(final String key, final long timeout, final TimeUnit unit) + { + return redisTemplate.expire(key, timeout, unit); + } + + /** + * 获取有效时间 + * + * @param key Redis键 + * @return 有效时间 + */ + public long getExpire(final String key) + { + return redisTemplate.getExpire(key); + } + + /** + * 判断 key是否存在 + * + * @param key 键 + * @return true 存在 false不存在 + */ + public Boolean hasKey(String key) + { + return redisTemplate.hasKey(key); + } + + /** + * 获得缓存的基本对象。 + * + * @param key 缓存键值 + * @return 缓存键值对应的数据 + */ + public <T> T getCacheObject(final String key) + { + ValueOperations<String, T> operation = redisTemplate.opsForValue(); + return operation.get(key); + } + + /** + * 删除单个对象 + * + * @param key + */ + public boolean deleteObject(final String key) + { + return redisTemplate.delete(key); + } + + /** + * 删除集合对象 + * + * @param collection 多个对象 + * @return + */ + public boolean deleteObject(final Collection collection) + { + return redisTemplate.delete(collection) > 0; + } + + /** + * 缓存List数据 + * + * @param key 缓存的键值 + * @param dataList 待缓存的List数据 + * @return 缓存的对象 + */ + public <T> long setCacheList(final String key, final List<T> dataList) + { + Long count = redisTemplate.opsForList().rightPushAll(key, dataList); + return count == null ? 0 : count; + } + + /** + * 获得缓存的list对象 + * + * @param key 缓存的键值 + * @return 缓存键值对应的数据 + */ + public <T> List<T> getCacheList(final String key) + { + return redisTemplate.opsForList().range(key, 0, -1); + } + + /** + * 缓存Set + * + * @param key 缓存键值 + * @param dataSet 缓存的数据 + * @return 缓存数据的对象 + */ + public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet) + { + BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key); + Iterator<T> it = dataSet.iterator(); + while (it.hasNext()) + { + setOperation.add(it.next()); + } + return setOperation; + } + + /** + * 获得缓存的set + * + * @param key + * @return + */ + public <T> Set<T> getCacheSet(final String key) + { + return redisTemplate.opsForSet().members(key); + } + + /** + * 缓存Map + * + * @param key + * @param dataMap + */ + public <T> void setCacheMap(final String key, final Map<String, T> dataMap) + { + if (dataMap != null) { + redisTemplate.opsForHash().putAll(key, dataMap); + } + } + + public <T> void setCacheMap(final String key, final Map<String, T> dataMap, long timeout) + { + if (dataMap != null) { + redisTemplate.opsForHash().putAll(key, dataMap); + redisTemplate.expire(key, timeout, TimeUnit.SECONDS); + } + } + + /** + * 获得缓存的Map + * + * @param key + * @return + */ + public <T> Map<String, T> getCacheMap(final String key) + { + return redisTemplate.opsForHash().entries(key); + } + + /** + * 往Hash中存入数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @param value 值 + */ + public <T> void setCacheMapValue(final String key, final String hKey, final T value) + { + redisTemplate.opsForHash().put(key, hKey, value); + } + + /** + * 获取Hash中的数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @return Hash中的对象 + */ + public <T> T getCacheMapValue(final String key, final String hKey) + { + HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash(); + return opsForHash.get(key, hKey); + } + + /** + * 获取多个Hash中的数据 + * + * @param key Redis键 + * @param hKeys Hash键集合 + * @return Hash对象集合 + */ + public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys) + { + return redisTemplate.opsForHash().multiGet(key, hKeys); + } + + /** + * 删除Hash中的某条数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @return 是否成功 + */ + public boolean deleteCacheMapValue(final String key, final String hKey) + { + return redisTemplate.opsForHash().delete(key, hKey) > 0; + } + + /** + * 获得缓存的基本对象列表 + * + * @param pattern 字符串前缀 + * @return 对象列表 + */ + public Collection<String> keys(final String pattern) + { + return redisTemplate.keys(pattern); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java index d4e744e..9bed984 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java @@ -4,6 +4,8 @@ import java.util.Map; import java.util.concurrent.TimeUnit; import javax.servlet.http.HttpServletRequest; + +import com.ruoyi.common.core.domain.model.LoginUserApplet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -66,6 +68,15 @@ return getLoginUser(ServletUtils.getRequest()); } /** + * 小程序获取用户身份信息 + * + * @return 用户信息 + */ + public LoginUser getLoginUserApplet() + { + return getLoginUser(ServletUtils.getRequest()); + } + /** * 获取用户身份信息 * * @return 用户信息 @@ -81,6 +92,33 @@ Claims claims = parseToken(token); // 解析对应的权限以及用户信息 String uuid = (String) claims.get(Constants.LOGIN_USER_KEY); + String userKey = getTokenKey(uuid); + LoginUser user = redisCache.getCacheObject(userKey); + return user; + } + catch (Exception e) + { + log.error("获取用户信息异常'{}'", e.getMessage()); + } + } + return null; + } + /** + * 小程序获取用户身份信息 + * + * @return 用户信息 + */ + public LoginUser getLoginUserApplet(HttpServletRequest request) + { + // 获取请求携带的令牌 + String token = getToken(request); + if (StringUtils.isNotEmpty(token)) + { + try + { + Claims claims = parseToken(token); + // 解析对应的权限以及用户信息 + String uuid = (String) claims.get(Constants.LOGIN_USER_APPLET_KEY); String userKey = getTokenKey(uuid); LoginUser user = redisCache.getCacheObject(userKey); return user; @@ -133,6 +171,23 @@ claims.put(Constants.LOGIN_USER_KEY, token); return createToken(claims); } + /** + * 创建用户小程序令牌 + * + * @param loginUser 用户信息 + * @return 令牌 + */ + public String createTokenApplet(LoginUserApplet loginUser) + { + String token = IdUtils.fastUUID(); + loginUser.setToken(token); + setUserAgentApplet(loginUser); + refreshTokenApplet(loginUser); + + Map<String, Object> claims = new HashMap<>(); + claims.put(Constants.LOGIN_USER_APPLET_KEY, token); + return createTokenApplet(claims); + } /** * 验证令牌有效期,相差不足20分钟,自动刷新缓存 @@ -163,6 +218,19 @@ String userKey = getTokenKey(loginUser.getToken()); redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES); } + /** + * 刷新令牌有效期 + * + * @param loginUser 登录信息 + */ + public void refreshTokenApplet(LoginUserApplet loginUser) + { + loginUser.setLoginTime(System.currentTimeMillis()); + loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE); + // 根据uuid将loginUser缓存 + String userKey = getTokenKey(loginUser.getToken()); + redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES); + } /** * 设置用户代理信息 @@ -170,6 +238,20 @@ * @param loginUser 登录信息 */ public void setUserAgent(LoginUser loginUser) + { + UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent")); + String ip = IpUtils.getIpAddr(); + loginUser.setIpaddr(ip); + loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip)); + loginUser.setBrowser(userAgent.getBrowser().getName()); + loginUser.setOs(userAgent.getOperatingSystem().getName()); + } + /** + * 设置用户代理信息 + * + * @param loginUser 登录信息 + */ + public void setUserAgentApplet(LoginUserApplet loginUser) { UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent")); String ip = IpUtils.getIpAddr(); @@ -192,6 +274,19 @@ .signWith(SignatureAlgorithm.HS512, secret).compact(); return token; } + /** + * 小程序从数据声明生成令牌 + * + * @param claims 数据声明 + * @return 令牌 + */ + private String createTokenApplet(Map<String, Object> claims) + { + String token = Jwts.builder() + .setClaims(claims) + .signWith(SignatureAlgorithm.HS512, secret).compact(); + return token; + } /** * 从令牌中获取数据声明 diff --git a/ruoyi-system/pom.xml b/ruoyi-system/pom.xml index 0747834..80a2359 100644 --- a/ruoyi-system/pom.xml +++ b/ruoyi-system/pom.xml @@ -79,6 +79,10 @@ <artifactId>bankapi</artifactId> <version>1.0.0-SNAPSHOT</version> </dependency> + <dependency> + <groupId>com.fasterxml.jackson.datatype</groupId> + <artifactId>jackson-datatype-jsr310</artifactId> + </dependency> </dependencies> </project> \ No newline at end of file diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/model/TTenant.java b/ruoyi-system/src/main/java/com/ruoyi/system/model/TTenant.java index 92a1d88..cca42c8 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/model/TTenant.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/model/TTenant.java @@ -88,5 +88,8 @@ @ApiModelProperty(value = "登录密码") @TableField("password") private String password; + @ApiModelProperty(value = "微信openid") + @TableField("open_id") + private String openId; } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/TTenantService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/TTenantService.java index 0357962..8bf0ed0 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/TTenantService.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/TTenantService.java @@ -2,9 +2,12 @@ import com.baomidou.mybatisplus.extension.service.IService; import com.ruoyi.common.basic.PageInfo; +import com.ruoyi.system.utils.wx.pojo.AppletUserDecodeData; import com.ruoyi.system.model.TTenant; import com.ruoyi.system.query.TTenantQuery; import com.ruoyi.system.vo.TenantVO; + +import java.util.Map; /** * <p> @@ -22,4 +25,7 @@ * @return */ PageInfo<TenantVO> pageList(TTenantQuery query); + + Map<String, Object> wxLogin(AppletUserDecodeData appletUserDecodeData); + } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TTenantServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TTenantServiceImpl.java index c6d65f1..7d297fa 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TTenantServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TTenantServiceImpl.java @@ -1,18 +1,23 @@ package com.ruoyi.system.service.impl; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.ruoyi.common.basic.PageInfo; import com.ruoyi.common.constant.DictConstants; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.system.utils.wx.pojo.AppletUserDecodeData; import com.ruoyi.common.utils.DictUtils; import com.ruoyi.system.mapper.TTenantMapper; import com.ruoyi.system.model.TTenant; import com.ruoyi.system.query.TTenantQuery; import com.ruoyi.system.service.TTenantService; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.ruoyi.system.vo.SysUserVO; import com.ruoyi.system.vo.TenantVO; +import org.springframework.security.core.token.TokenService; import org.springframework.stereotype.Service; +import javax.annotation.Resource; import java.util.List; +import java.util.Map; /** * <p> @@ -25,6 +30,8 @@ @Service public class TTenantServiceImpl extends ServiceImpl<TTenantMapper, TTenant> implements TTenantService { + @Resource + private TokenService tokenService; @Override public PageInfo<TenantVO> pageList(TTenantQuery query) { PageInfo<TenantVO> pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize()); @@ -36,4 +43,10 @@ pageInfo.setRecords(list); return pageInfo; } + + @Override + public Map<String, Object> wxLogin(AppletUserDecodeData appletUserDecodeData) { + + return null; + } } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/body/resp/AccessTokenRespBody.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/body/resp/AccessTokenRespBody.java new file mode 100644 index 0000000..db2d1b4 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/body/resp/AccessTokenRespBody.java @@ -0,0 +1,28 @@ +package com.ruoyi.system.utils.wx.body.resp; + + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * AccessToken 全局唯一 + * + * @author xiaochen + */ +@Data +public class AccessTokenRespBody extends RespBody implements Serializable { + + /** + * 获取到的凭证 + */ + @JsonProperty("access_token") + private String accessToken; + /** + * 凭证有效时间,单位:秒 + */ + @JsonProperty("expires_in") + private int expiresIn; + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/body/resp/Code2SessionRespBody.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/body/resp/Code2SessionRespBody.java new file mode 100644 index 0000000..d741edb --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/body/resp/Code2SessionRespBody.java @@ -0,0 +1,29 @@ +package com.ruoyi.system.utils.wx.body.resp; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +/** + * @author xiaochen + * @ClassName Code2SessionRespBody + * @Description + * @date 2021-07-28 12:35 + */ +@Data +public class Code2SessionRespBody extends RespBody { + /** + * 用户唯一标识 + */ + @JsonProperty("openid") + private String openid; + /** + * 会话密钥 + */ + @JsonProperty("session_key") + private String sessionKey; + /** + * 用户在开放平台的唯一标识符,若当前小程序已绑定到微信开放平台帐号下会返回,详见 UnionID 机制说明。 + */ + @JsonProperty("unionid") + private String unionid; +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/body/resp/RespBody.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/body/resp/RespBody.java new file mode 100644 index 0000000..ee60ab3 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/body/resp/RespBody.java @@ -0,0 +1,19 @@ +package com.ruoyi.system.utils.wx.body.resp; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +/** + * @author xiaochen + * @ClassName RespBody + * @Description + * @date 2021-07-28 11:44 + */ +@Data +public class RespBody { + @JsonProperty("errcode") + private Integer errorCode; + + @JsonProperty("errmsg") + private String errorMsg; +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/body/resq/Code2SessionResqBody.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/body/resq/Code2SessionResqBody.java new file mode 100644 index 0000000..424e376 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/body/resq/Code2SessionResqBody.java @@ -0,0 +1,21 @@ +package com.ruoyi.system.utils.wx.body.resq; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +/** + * @author xiaochen + * @ClassName Code2SessionResqBody + * @Description + * @date 2021-07-28 11:47 + */ +@Data +public class Code2SessionResqBody { + @JsonProperty("js_code") + private String jsCode; + + public Code2SessionResqBody build(String jsCode) { + this.jsCode = jsCode; + return this; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/model/WeixinProperties.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/model/WeixinProperties.java new file mode 100644 index 0000000..fdeb5ab --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/model/WeixinProperties.java @@ -0,0 +1,79 @@ +package com.ruoyi.system.utils.wx.model; + +import lombok.ToString; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * @author xiaochen + * @ClassName WeixinProperties + * @Description + * @date 2024-08-14 13:55 + */ +@ToString +@Component +@ConfigurationProperties(prefix = "wx.conf") +public class WeixinProperties { + /** + * 默认开启 + */ + private boolean enabled = true; + /** + * 获取 App ID + * + * @return App ID + */ + private String appId; + /** + * 获取 Mch ID + * + * @return Mch ID + */ + private String mchId; + + /** + * 获取 secret ID + * + * @return secret ID + */ + private String secretId; + + public String getSecretId() { + return secretId; + } + + public void setSecretId(String secretId) { + this.secretId = secretId; + } + + /** + * HTTP(S) 连接超时时间,单位毫秒 + * + */ + public int getHttpConnectTimeoutMs() { + return 6 * 1000; + } + + /** + * HTTP(S) 读数据超时时间,单位毫秒 + */ + public int getHttpReadTimeoutMs() { + return 8 * 1000; + } + + public String getAppId() { + return appId; + } + + public void setAppId(String appId) { + this.appId = appId; + } + + public String getMchId() { + return mchId; + } + + public void setMchId(String mchId) { + this.mchId = mchId; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/pojo/AppletPhoneEncrypteData.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/pojo/AppletPhoneEncrypteData.java new file mode 100644 index 0000000..c7f5257 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/pojo/AppletPhoneEncrypteData.java @@ -0,0 +1,19 @@ +package com.ruoyi.system.utils.wx.pojo; + +import lombok.Data; + +/** + * @author xiaochen + * @ClassName AppletUserDecodeData + * @Description + * @date 2021-08-13 17:46 + * 小程序加密数据体 + * + */ +@Data +public class AppletPhoneEncrypteData { + private String encryptedData; + private String openid; + private String unionid; + private String iv; +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/pojo/AppletUserDecodeData.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/pojo/AppletUserDecodeData.java new file mode 100644 index 0000000..a3573ad --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/pojo/AppletUserDecodeData.java @@ -0,0 +1,52 @@ +package com.ruoyi.system.utils.wx.pojo; + +import lombok.Data; + +/** + * @author xiaochen + * @ClassName AppletUserDecodeData + * @Description + * 用户主体信息部分 + * { + * "openId": "OPENID", + * "nickName": "NICKNAME", + * "gender": GENDER, + * "city": "CITY", + * "province": "PROVINCE", + * "country": "COUNTRY", + * "avatarUrl": "AVATARURL", + * "unionId": "UNIONID", + * "watermark": + * { + * "appid":"APPID", + * "timestamp":TIMESTAMP + * } + * } + * 电话部分 + * { + * "phoneNumber": "13580006666", + * "purePhoneNumber": "13580006666", + * "countryCode": "86", + * "watermark": + * { + * "appid":"APPID", + * "timestamp": TIMESTAMP + * } + * } + * + */ +@Data +public class AppletUserDecodeData { + private String openId; + private String unionId; + private String nickName; + private int gender; + private String city; + private String province; + private String country; + private String avatarUrl; + private Watermark watermark; + private String phoneNumber; + private String purePhoneNumber; + private String countryCode; +} \ No newline at end of file diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/pojo/AppletUserEncrypteData.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/pojo/AppletUserEncrypteData.java new file mode 100644 index 0000000..16d0057 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/pojo/AppletUserEncrypteData.java @@ -0,0 +1,20 @@ +package com.ruoyi.system.utils.wx.pojo; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * @author xiaochen + * @ClassName AppletUserDecodeData + * @Description + * 小程序加密数据体 + * + */ +@Data +public class AppletUserEncrypteData extends AppletPhoneEncrypteData { + private String rawData; + private String signature; + private String code; + @ApiModelProperty(value = "邀请用户id") + private Long inviteUserId; +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/pojo/Watermark.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/pojo/Watermark.java new file mode 100644 index 0000000..16f4f7a --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/pojo/Watermark.java @@ -0,0 +1,9 @@ +package com.ruoyi.system.utils.wx.pojo; + +import lombok.Data; + +@Data +public class Watermark { + private String appid; + private String timestamp; +} \ No newline at end of file diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/SHA1.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/SHA1.java new file mode 100644 index 0000000..2b74822 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/SHA1.java @@ -0,0 +1,36 @@ +package com.ruoyi.system.utils.wx.tools; + +import java.security.MessageDigest; + +public class SHA1 { + + + /** + * 用SHA1算法生成安全签名 + * + * @param str + * @return + * @throws WxException + */ + public static String getSHA1(String str) throws WxException { + try { + // SHA1签名生成 + MessageDigest md = MessageDigest.getInstance("SHA-1"); + md.update(str.getBytes()); + byte[] digest = md.digest(); + StringBuffer hexstr = new StringBuffer(); + String shaHex; + for (int i = 0; i < digest.length; i++) { + shaHex = Integer.toHexString(digest[i] & 0xFF); + if (shaHex.length() < 2) { + hexstr.append(0); + } + hexstr.append(shaHex); + } + return hexstr.toString(); + } catch (Exception e) { + throw new WxException(WxException.ComputeSignatureError); + } + } + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WebUtils.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WebUtils.java new file mode 100644 index 0000000..c2e15e1 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WebUtils.java @@ -0,0 +1,48 @@ +package com.ruoyi.system.utils.wx.tools; + +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +/** + * @Author xiaochen + * @Date 2019/08/26 10:28 AM + * @Description + */ +public final class WebUtils { + + private WebUtils() { + } + + /** + * 当前请求 + */ + public static HttpServletRequest request() { + return contextHolder() == null ? null : contextHolder().getRequest(); + } + + /** + * 当前响应 + */ + public static HttpServletResponse response() { + return contextHolder() == null ? null : contextHolder().getResponse(); + } + + /** + * 当前session + */ + public static HttpSession session() { + return request() == null ? null : request().getSession(); + } + + /** + * 当前ServletRequest + */ + public static ServletRequestAttributes contextHolder() { + return (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + } + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxAppletTools.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxAppletTools.java new file mode 100644 index 0000000..2298a44 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxAppletTools.java @@ -0,0 +1,123 @@ +package com.ruoyi.system.utils.wx.tools; + + +import com.ruoyi.common.redis.service.RedisService; +import com.ruoyi.system.utils.wx.body.resp.AccessTokenRespBody; +import com.ruoyi.system.utils.wx.body.resp.Code2SessionRespBody; +import com.ruoyi.system.utils.wx.body.resq.Code2SessionResqBody; +import com.ruoyi.system.utils.wx.model.WeixinProperties; +import lombok.extern.slf4j.Slf4j; +import org.springframework.util.StringUtils; +import org.springframework.web.client.RestTemplate; + +import java.text.MessageFormat; +import java.util.concurrent.TimeUnit; + +/** + * @author xiaochen + * @ClassName WxAppletTools + * @Description + * @date 2024-8-04 13:55 + */ +@Slf4j +public class WxAppletTools { + private final static String ACCESSTOKEN_CACHE_KEY = "accessToken"; + /** + * 请求参数 + * 属性 类型 默认值 必填 说明 + * appid string 是 小程序 appId + * secret string 是 小程序 appSecret + * js_code string 是 登录时获取的 code + * grant_type string 是 授权类型,此处只需填写 authorization_cod + * <p> + * 返回值: + * <p> + * 属性 类型 说明 + * openid string 用户唯一标识 + * session_key string 会话密钥 + * unionid string 用户在开放平台的唯一标识符,若当前小程序已绑定到微信开放平台帐号下会返回,详见 UnionID 机制说明。 + * errcode number 错误码 + * errmsg string 错误信息 + */ + private static final String JSCODE_2_SESSION_URL = "https://api.weixin.qq.com/sns/jscode2session?appid={0}&secret={1}&js_code={2}&grant_type=authorization_code"; + /** + * 请求参数 + * 属性 类型 默认值 必填 说明 + * grant_type string 是 填写 client_credential + * appid string 是 小程序唯一凭证,即 AppID,可在「微信公众平台 - 设置 - 开发设置」页中获得。(需要已经成为开发者,且帐号没有异常状态) + * secret string 是 小程序唯一凭证密钥,即 AppSecret,获取方式同 appid + * 返回值 + * Object + * 返回的 JSON 数据包 + * <p> + * 属性 类型 说明 + * access_token string 获取到的凭证 + * expires_in number 凭证有效时间,单位:秒。目前是7200秒之内的值。 + * errcode number 错误码 + * errmsg string 错误信息 + */ + public static String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}"; + private WeixinProperties wxConfig; + private RestTemplate wxRestTemplate; + private RedisService redisService; + + public WxAppletTools(RestTemplate wxRestTemplate, WeixinProperties wxConfig, RedisService redisService) { + this.wxRestTemplate = wxRestTemplate; + this.wxConfig = wxConfig; + this.redisService = redisService; + } + + /** + * 自定义部分数据 + * + * @param wxConfig + * @return + */ + public WxAppletTools build(WeixinProperties wxConfig) { + this.wxConfig = wxConfig; + return this; + } + + /** + * @param resqBody + * @return + */ + public Code2SessionRespBody getOpenIdByJscode2session(Code2SessionResqBody resqBody) { + long start = System.currentTimeMillis(); + String requestUrl = MessageFormat.format(JSCODE_2_SESSION_URL, wxConfig.getAppId(), wxConfig.getSecretId(), resqBody.getJsCode()); + long end = System.currentTimeMillis(); + log.info("code换取sessionKey时间:{}", (end - start)); + String respBody = wxRestTemplate.getForEntity(requestUrl, String.class).getBody(); + end = System.currentTimeMillis(); + log.info("code换取sessionKey时间:{}", (end - start)); + log.info("Jscode2session:{}", respBody); + Code2SessionRespBody code2SessionRespBody = WxJsonUtils.parseObject(respBody, Code2SessionRespBody.class); + // 判断有误异常 + if (StringUtils.hasLength(code2SessionRespBody.getErrorMsg())) { + // 抛出错误 + throw new WxException(code2SessionRespBody.getErrorCode() + ":" + code2SessionRespBody.getErrorMsg()); + } + return code2SessionRespBody; + } + + /** + * @return + */ + public String getAccessToken(String version) { + String accessToken = redisService.getCacheObject(ACCESSTOKEN_CACHE_KEY + version); + if (StringUtils.hasLength(accessToken)) { + return accessToken; + } + String requestUrl = MessageFormat.format(ACCESS_TOKEN_URL, wxConfig.getAppId(), wxConfig.getSecretId()); + String respBody = wxRestTemplate.getForEntity(requestUrl, String.class).getBody(); + AccessTokenRespBody accessTokenRespBody = WxJsonUtils.parseObject(respBody, AccessTokenRespBody.class); + // 判断有误异常 + if (StringUtils.hasLength(accessTokenRespBody.getErrorMsg())) { + // 抛出错误 + throw new WxException(accessTokenRespBody.getErrorCode() + ":" + accessTokenRespBody.getErrorMsg()); + } + redisService.setCacheObject(ACCESSTOKEN_CACHE_KEY + version, accessTokenRespBody.getAccessToken(), 7200L, TimeUnit.SECONDS); + return accessTokenRespBody.getAccessToken(); + } + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxCache.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxCache.java new file mode 100644 index 0000000..fa82920 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxCache.java @@ -0,0 +1,117 @@ +package com.ruoyi.system.utils.wx.tools; + +import java.util.concurrent.TimeUnit; + +/** + * 缓存 + * + * @author xiaochen + */ +class WxCache { + /** + * 缓存的初始化容量 + */ + private int initialCapacity = 50; + /** + * 缓存最大容量 + */ + private long maximumSize = 200L; + /** + * 缓存时长 + */ + private long duration = 7000L; + /** + * 时长单位,自动转换 + * 支持: + * 时 + * 分 + * 秒 + * 天 + */ + private TimeUnit timeunit = TimeUnit.SECONDS; + + public int getInitialCapacity() { + return initialCapacity; + } + + public void setInitialCapacity(int initialCapacity) { + this.initialCapacity = initialCapacity; + } + + public long getMaximumSize() { + return maximumSize; + } + + public void setMaximumSize(long maximumSize) { + this.maximumSize = maximumSize; + } + + + public long getDuration() { + return duration; + } + + public void setDuration(long duration) { + this.duration = duration; + } + + public TimeUnit getTimeunit() { + return timeunit; + } + + public void setTimeunit(TimeUnit timeunit) { + this.timeunit = timeunit; + } + + public static class Builder { + private int initialCapacity; + private long maximumSize; + private long duration; + private TimeUnit timeunit; + + public Builder setInitialCapacity(int initialCapacity) { + this.initialCapacity = initialCapacity; + return this; + } + + public Builder setMaximumSize(long maximumSize) { + this.maximumSize = maximumSize; + return this; + } + + public Builder setDuration(long duration) { + this.duration = duration; + return this; + } + + public Builder setTimeUnit(TimeUnit timeunit) { + this.timeunit = timeunit; + return this; + } + + public WxCache build() { + return new WxCache(this); + } + } + + public static Builder options() { + return new Builder(); + } + + private WxCache(Builder builder) { + this.initialCapacity = 0 == builder.initialCapacity ? this.initialCapacity : builder.initialCapacity; + this.maximumSize = 0L == builder.maximumSize ? this.maximumSize : builder.maximumSize; + this.duration = 0L == builder.duration ? this.duration : builder.duration; + this.timeunit = null == builder.timeunit ? this.timeunit : builder.timeunit; + } + + @Override + public String toString() { + return "WxCache{" + + "initialCapacity=" + initialCapacity + + ", maximumSize=" + maximumSize + + ", duration=" + duration + + ", timeunit=" + timeunit + + '}'; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxCacheTemplate.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxCacheTemplate.java new file mode 100644 index 0000000..8640177 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxCacheTemplate.java @@ -0,0 +1,34 @@ +package com.ruoyi.system.utils.wx.tools; + +/** + * @author xiaochen + * @ClassName WxCacheTemplate + * @Description + * @date 2021-01-11 11:27 + */ +public interface WxCacheTemplate<T> { + /** + * 保存key + * + * @param key + * @param value + * @return + */ + boolean setKey(String key, T value); + + /** + * 获取缓存 + * + * @param key + * @return + */ + T getKey(String key); + + /** + * 删除 + * + * @param key + * @return + */ + boolean delKey(String key); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxException.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxException.java new file mode 100644 index 0000000..09d46ae --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxException.java @@ -0,0 +1,55 @@ +package com.ruoyi.system.utils.wx.tools; + +/** + * @author lihen + */ +public class WxException extends RuntimeException { + + private final static int OK = 0; + private final static int ValidateSignatureError = -40001; + private final static int ParseXmlError = -40002; + public final static int ComputeSignatureError = -40003; + private final static int IllegalAesKey = -40004; + private final static int ValidateAppidError = -40005; + private final static int EncryptAESError = -40006; + private final static int DecryptAESError = -40007; + private final static int IllegalBuffer = -40008; + + private int code; + + private static String getMessage(int code) { + switch (code) { + case ValidateSignatureError: + return "签名验证错误"; + case ParseXmlError: + return "xml解析失败"; + case ComputeSignatureError: + return "sha加密生成签名失败"; + case IllegalAesKey: + return "SymmetricKey非法"; + case ValidateAppidError: + return "appid校验失败"; + case EncryptAESError: + return "aes加密失败"; + case DecryptAESError: + return "aes解密失败"; + case IllegalBuffer: + return "解密后得到的buffer非法"; + default: + return null; + } + } + + public int getCode() { + return code; + } + + WxException(int code) { + super(getMessage(code)); + this.code = code; + } + + public WxException(String message) { + super(message); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxJsonUtils.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxJsonUtils.java new file mode 100644 index 0000000..cc6113e --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxJsonUtils.java @@ -0,0 +1,109 @@ +package com.ruoyi.system.utils.wx.tools; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +/** + * Json转换工具类 + * 参考:https://blog.csdn.net/weixin_38413579/article/details/82562634 + * @author madman + */ +@Slf4j +public final class WxJsonUtils { + public static final String dateFormat = "yyyy-MM-dd"; + public static final String dateTimeFormat = "yyyy-MM-dd HH:mm:ss"; + private static final ObjectMapper OM = new ObjectMapper(); + private static final JavaTimeModule timeModule = new JavaTimeModule(); + + /** + * 转换LocalDateTime + */ + static class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> { + @Override + public void serialize(LocalDateTime localDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { + jsonGenerator.writeString(localDateTime.format(DateTimeFormatter.ofPattern(dateTimeFormat))); + } + } + + /** + * 转换LocalDate + */ + static class LocalDateSerializer extends JsonSerializer<LocalDate> { + @Override + public void serialize(LocalDate localDate, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { + jsonGenerator.writeString(localDate.format(DateTimeFormatter.ofPattern(dateFormat))); + } + } + + /** + * 设置 ObjectMapper + * + * @return + */ + private static ObjectMapper getObjectMapper() { + // 序列化 + timeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer()); + timeModule.addSerializer(LocalDate.class, new LocalDateSerializer()); + // 反序列化 + timeModule.addDeserializer(LocalDateTime.class, + new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(dateTimeFormat))); + timeModule.addDeserializer(LocalDate.class, + new LocalDateDeserializer(DateTimeFormatter.ofPattern(dateFormat))); + // 允许对象忽略json中不存在的属性 + OM.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + OM.registerModule(timeModule); + return OM; + } + + /** + * 将对象序列化 + */ + public static <T> String toJsonString(T obj) { + try { + ObjectMapper om = getObjectMapper(); + return om.writeValueAsString(obj); + } catch (JsonProcessingException e) { + log.error("转json字符串失败:{}", obj); + return null; + } + } + + /** + * 反序列化对象字符串 + */ + public static <T> T parseObject(String json, Class<T> clazz) { + try { + ObjectMapper om = getObjectMapper(); + return om.readValue(json, clazz); + } catch (JsonProcessingException e) { + throw new RuntimeException("反序列化对象字符串失败"); + } + } + + /** + * 反序列化字符串成为对象 + */ + public static <T> T parseObject(String json, TypeReference<T> valueTypeRef) { + try { + ObjectMapper om = getObjectMapper(); + return om.readValue(json, valueTypeRef); + } catch (JsonProcessingException e) { + throw new RuntimeException("反序列化字符串成为对象失败"); + } + } + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxUtils.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxUtils.java new file mode 100644 index 0000000..6287358 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxUtils.java @@ -0,0 +1,175 @@ +package com.ruoyi.system.utils.wx.tools; + +import com.ruoyi.system.utils.wx.pojo.AppletUserDecodeData; +import com.ruoyi.common.utils.sign.Base64; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.CharEncoding; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import javax.servlet.http.HttpServletRequest; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.security.AlgorithmParameters; +import java.security.Security; +import java.util.Arrays; + +/** + * @Description 获取用户信息工具类 + * @Author xiaochen + * @Date 2021/8/12 15:45 + */ +@Slf4j +public class WxUtils { + + /** + * 微信小程序API 用户数据的解密 + * + * @param encryptedData + * @param sessionKey + * @param iv + * @return + */ + public static AppletUserDecodeData encryptedData(String encryptedData, String sessionKey, String iv) { + // 被加密的数据 + byte[] dataByte = Base64.decode(encryptedData); + // 加密秘钥 + byte[] keyByte = Base64.decode(sessionKey); + // 偏移量 + byte[] ivByte = Base64.decode(iv); + try { + // 如果密钥不足16位,那么就补足. 这个if 中的内容很重要 + int base = 16; + if (keyByte.length % base != 0) { + int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0); + byte[] temp = new byte[groups * base]; + Arrays.fill(temp, (byte) 0); + System.arraycopy(keyByte, 0, temp, 0, keyByte.length); + keyByte = temp; + } + // 初始化 + Security.addProvider(new BouncyCastleProvider()); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC"); + SecretKeySpec spec = new SecretKeySpec(keyByte, "AES"); + AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES"); + parameters.init(new IvParameterSpec(ivByte)); + cipher.init(Cipher.DECRYPT_MODE, spec, parameters); + byte[] resultByte = cipher.doFinal(dataByte); + if (null != resultByte && resultByte.length > 0) { + String result = new String(resultByte, CharEncoding.UTF_8); + log.info("解密原串:{}", result); + return WxJsonUtils.parseObject(result, AppletUserDecodeData.class); + } + throw new RuntimeException("解密的数据为空"); + } catch (Exception e) { + log.error("解密失败. error = {}", e.getMessage(), e); + throw new RuntimeException(e.getMessage()); + } + } + + /** + * 微信小程序API 用户数据的签名验证 + * signature = sha1( rawData + session_key ) + * + * @param rawData 不包括敏感信息的原始数据字符串,用于计算签名。 + * @param sessionKey + */ + public static void verifySignature(String rawData, String sessionKey, String signature) { + String serverSignature = SHA1.getSHA1(rawData + sessionKey); + log.info(rawData + ">>>>>>:" + sessionKey + " === " + serverSignature + " ======" + signature); + if (!signature.equals(serverSignature)) { + throw new RuntimeException("数据验签不通过"); + } + } + + /** + * 根据流接收请求数据 + * + * @param request + * @return + */ + public static String streamBodyByReceive(HttpServletRequest request) throws IOException { + log.info("微信异步回调地址:{}", request.getRequestURL()); + StringBuffer buffer = new StringBuffer(); + InputStream inputStream = request.getInputStream(); + InputStreamReader reader = new InputStreamReader(inputStream); + BufferedReader bufferedReader = new BufferedReader(reader); + String body = null; + while ((body = bufferedReader.readLine()) != null) { + buffer.append(body); + } + String data = buffer.toString(); + reader.close(); + inputStream.close(); + log.info("微信异步回调数据:{}", data); + return data; + } + + /** + * 日志 + * + * @return + */ + public static Logger getLogger() { + Logger logger = LoggerFactory.getLogger("wxpay java sdk"); + return logger; + } + + /** + * debug + * + * @param msg + * @param args + */ + public static void debug(String msg, Object... args) { + Logger log = getLogger(); + if (log.isDebugEnabled()) { + log.debug(msg, args); + } + } + + /** + * info + * + * @param msg + * @param args + */ + public static void info(String msg, Object... args) { + Logger log = getLogger(); + if (log.isInfoEnabled()) { + log.info(msg, args); + } + } + + /** + * warn + * + * @param msg + * @param args + */ + public static void warn(String msg, Object... args) { + Logger log = getLogger(); + if (log.isWarnEnabled()) { + log.warn(msg, args); + } + } + + /** + * error + * + * @param msg + * @param args + */ + public static void error(String msg, Object... args) { + Logger log = getLogger(); + if (log.isErrorEnabled()) { + log.error(msg, args); + } + } +} -- Gitblit v1.7.1