| | |
| | | @Autowired |
| | | TBillService tBillService; |
| | | |
| | | |
| | | |
| | | |
| | | @PreAuthorize("@ss.hasPermi('system:bill:list')") |
| | | @PostMapping("list") |
| | | @ApiOperation("分页查询账单列表") |
| | |
| | | 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.*; |
| | | |
| | | /** |
| | |
| | | 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')") |
| | |
| | | @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>() |
| | |
| | | } |
| | | @Log(title = "合同管理-批量删除合同", businessType = BusinessType.DELETE) |
| | | @ApiOperation(value = "批量删除合同") |
| | | @PreAuthorize("@ss.hasPermi('system:contract:delete')") |
| | | |
| | | @DeleteMapping(value = "/deleteContractByIds") |
| | | public R<Boolean> deleteContractByIds |
| | | (@RequestParam String ids) { |
| | |
| | | |
| | | @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); |
| | |
| | | } |
| | | @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); |
| | |
| | | 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"); |
| | |
| | | } |
| | | @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)); |
| | | } |
| | |
| | | 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<>(); |
| | |
| | | * 导出 |
| | | */ |
| | | @ApiOperation(value = "导出") |
| | | @PreAuthorize("@ss.hasPermi('system:contract:export')") |
| | | @Log(title = "导出", businessType = BusinessType.EXPORT) |
| | | @PostMapping("/export") |
| | | public void exportOpticalInspection(@RequestBody TContractQuery query) |
| | |
| | | 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(); |
New file |
| | |
| | | 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); |
| | | } |
| | | } |
| | |
| | | * 令牌前缀 |
| | | */ |
| | | public static final String LOGIN_USER_KEY = "login_user_key"; |
| | | /** |
| | | * 小程序 |
| | | */ |
| | | public static final String LOGIN_USER_APPLET_KEY = "login_user_applet_key"; |
| | | |
| | | /** |
| | | * 用户ID |
New file |
| | |
| | | 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; |
| | | } |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
| | | } |
New file |
| | |
| | | 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" }; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | 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); |
| | | } |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
| | | } |
New file |
| | |
| | | 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); |
| | | } |
| | | } |
| | |
| | | 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; |
| | |
| | | return getLoginUser(ServletUtils.getRequest()); |
| | | } |
| | | /** |
| | | * 小程序获取用户身份信息 |
| | | * |
| | | * @return 用户信息 |
| | | */ |
| | | public LoginUser getLoginUserApplet() |
| | | { |
| | | return getLoginUser(ServletUtils.getRequest()); |
| | | } |
| | | /** |
| | | * 获取用户身份信息 |
| | | * |
| | | * @return 用户信息 |
| | |
| | | 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; |
| | |
| | | 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分钟,自动刷新缓存 |
| | |
| | | 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); |
| | | } |
| | | |
| | | /** |
| | | * 设置用户代理信息 |
| | |
| | | * @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(); |
| | |
| | | .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; |
| | | } |
| | | |
| | | /** |
| | | * 从令牌中获取数据声明 |
| | |
| | | <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> |
| | |
| | | @ApiModelProperty(value = "登录密码") |
| | | @TableField("password") |
| | | private String password; |
| | | @ApiModelProperty(value = "微信openid") |
| | | @TableField("open_id") |
| | | private String openId; |
| | | |
| | | } |
| | |
| | | |
| | | 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> |
| | |
| | | * @return |
| | | */ |
| | | PageInfo<TenantVO> pageList(TTenantQuery query); |
| | | |
| | | Map<String, Object> wxLogin(AppletUserDecodeData appletUserDecodeData); |
| | | |
| | | } |
| | |
| | | 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> |
| | |
| | | @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()); |
| | |
| | | pageInfo.setRecords(list); |
| | | return pageInfo; |
| | | } |
| | | |
| | | @Override |
| | | public Map<String, Object> wxLogin(AppletUserDecodeData appletUserDecodeData) { |
| | | |
| | | return null; |
| | | } |
| | | } |
New file |
| | |
| | | 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; |
| | | |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | package com.ruoyi.system.utils.wx.pojo; |
| | | |
| | | import lombok.Data; |
| | | |
| | | @Data |
| | | public class Watermark { |
| | | private String appid; |
| | | private String timestamp; |
| | | } |
New file |
| | |
| | | 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); |
| | | } |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | 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(); |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | 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(); |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | 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 + |
| | | '}'; |
| | | } |
| | | } |
New file |
| | |
| | | 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); |
| | | } |
New file |
| | |
| | | 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); |
| | | } |
| | | } |
New file |
| | |
| | | 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("反序列化字符串成为对象失败"); |
| | | } |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | 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); |
| | | } |
| | | } |
| | | } |