From 9e73f6c52bde7456fd0d40d4b8f9a7abd8bba333 Mon Sep 17 00:00:00 2001 From: liujiangtao Date: Sat, 25 Jul 2020 12:39:47 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=8E=AF=E5=A2=83=E9=85=8D?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/info.txt | 25 + doc/xxzzu.conf | 30 + muster-admin/pom.xml | 11 + .../controller/common/CommonController.java | 8 +- .../controller/common/WeChatController.java | 165 +++++ .../controller/muster/WeiXinController.java | 66 ++ .../controller/muster/weixin/JsApiTicket.java | 34 + .../muster/weixin/MyX509TrustManager.java | 31 + .../controller/muster/weixin/QrCodeUtils.java | 46 ++ .../controller/muster/weixin/SignUtil.java | 79 +++ .../web/controller/muster/weixin/Token.java | 34 + .../web/controller/muster/weixin/URIUtil.java | 58 ++ .../muster/weixin/WeChatService.java | 19 + .../muster/weixin/WeChatServiceImpl.java | 75 ++ .../muster/weixin/WechatQRCode.java | 44 ++ .../muster/weixin/WeiXinCommonUtil.java | 641 ++++++++++++++++++ .../muster/weixin/WeiXinOauth2Token.java | 96 +++ .../muster/weixin/WeiXinUserInfo.java | 166 +++++ .../muster/weixin/WxJsapiSignature.java | 86 +++ .../system/SysProfileController.java | 4 +- .../muster/web/core/config/SwaggerConfig.java | 4 +- .../src/main/resources/application-dev.yml | 11 + .../src/main/resources/application-prod.yml | 12 + .../src/main/resources/application.yml | 22 +- muster-common/pom.xml | 6 + .../{RuoYiConfig.java => MusterConfig.java} | 46 +- .../com/muster/common/constant/WxConsts.java | 270 ++++++++ .../common/utils/file/FileUploadUtils.java | 6 +- .../muster/common/utils/ip/AddressUtils.java | 4 +- .../muster/common/utils/poi/ExcelUtil.java | 4 +- .../framework/config/ResourcesConfig.java | 4 +- .../web/service/SysLoginService.java | 30 +- .../com/muster/generator/util/GenUtils.java | 2 +- muster-logic/pom.xml | 23 + .../java/com/muster/logic/BO/DaoContext.java | 150 ++++ .../com/muster/logic/BO/package-info.java | 9 + .../com/muster/logic/DaoContextUtils.java | 39 ++ .../java/com/muster/logic/DbLogicService.java | 23 + .../com/muster/logic/DbLogicServiceImpl.java | 163 +++++ .../muster/logic/model/ProcedureResult.java | 64 ++ .../com/muster/logic/model/package-info.java | 9 + .../java/com/muster/logic/package-info.java | 9 + muster-ui/package.json | 4 +- .../assets/styles/{ruoyi.scss => muster.scss} | 0 muster-ui/src/settings.js | 62 +- muster-ui/src/utils/{ruoyi.js => muster.js} | 0 muster.sh | 86 +++ pom.xml | 25 + sql/ry_20200723.sql | 24 +- 49 files changed, 2746 insertions(+), 83 deletions(-) create mode 100644 doc/info.txt create mode 100644 doc/xxzzu.conf create mode 100755 muster-admin/src/main/java/com/muster/web/controller/common/WeChatController.java create mode 100755 muster-admin/src/main/java/com/muster/web/controller/muster/WeiXinController.java create mode 100755 muster-admin/src/main/java/com/muster/web/controller/muster/weixin/JsApiTicket.java create mode 100755 muster-admin/src/main/java/com/muster/web/controller/muster/weixin/MyX509TrustManager.java create mode 100755 muster-admin/src/main/java/com/muster/web/controller/muster/weixin/QrCodeUtils.java create mode 100755 muster-admin/src/main/java/com/muster/web/controller/muster/weixin/SignUtil.java create mode 100755 muster-admin/src/main/java/com/muster/web/controller/muster/weixin/Token.java create mode 100755 muster-admin/src/main/java/com/muster/web/controller/muster/weixin/URIUtil.java create mode 100644 muster-admin/src/main/java/com/muster/web/controller/muster/weixin/WeChatService.java create mode 100644 muster-admin/src/main/java/com/muster/web/controller/muster/weixin/WeChatServiceImpl.java create mode 100755 muster-admin/src/main/java/com/muster/web/controller/muster/weixin/WechatQRCode.java create mode 100755 muster-admin/src/main/java/com/muster/web/controller/muster/weixin/WeiXinCommonUtil.java create mode 100755 muster-admin/src/main/java/com/muster/web/controller/muster/weixin/WeiXinOauth2Token.java create mode 100755 muster-admin/src/main/java/com/muster/web/controller/muster/weixin/WeiXinUserInfo.java create mode 100755 muster-admin/src/main/java/com/muster/web/controller/muster/weixin/WxJsapiSignature.java create mode 100644 muster-admin/src/main/resources/application-dev.yml create mode 100644 muster-admin/src/main/resources/application-prod.yml rename muster-common/src/main/java/com/muster/common/config/{RuoYiConfig.java => MusterConfig.java} (68%) create mode 100755 muster-common/src/main/java/com/muster/common/constant/WxConsts.java create mode 100644 muster-logic/pom.xml create mode 100644 muster-logic/src/main/java/com/muster/logic/BO/DaoContext.java create mode 100644 muster-logic/src/main/java/com/muster/logic/BO/package-info.java create mode 100644 muster-logic/src/main/java/com/muster/logic/DaoContextUtils.java create mode 100644 muster-logic/src/main/java/com/muster/logic/DbLogicService.java create mode 100644 muster-logic/src/main/java/com/muster/logic/DbLogicServiceImpl.java create mode 100644 muster-logic/src/main/java/com/muster/logic/model/ProcedureResult.java create mode 100644 muster-logic/src/main/java/com/muster/logic/model/package-info.java create mode 100644 muster-logic/src/main/java/com/muster/logic/package-info.java rename muster-ui/src/assets/styles/{ruoyi.scss => muster.scss} (100%) rename muster-ui/src/utils/{ruoyi.js => muster.js} (100%) create mode 100755 muster.sh diff --git a/doc/info.txt b/doc/info.txt new file mode 100644 index 000000000..362b4d9d6 --- /dev/null +++ b/doc/info.txt @@ -0,0 +1,25 @@ + +我发一下服务器的信息: +grow.xxzzu.com +47.92.6.215 +mysql 和 linux 的密码都是 +gywj@1qaz@WSX + +数据库使用 +sys_manager +测试存储过程可以使用 : +proc_DS01E_enterprise_info + +当前,ms_muster 数据库 未更新,等开发模式确定后,一起逐一更新数据结构 + +小象智租 微信 开发者ID(AppID) +AppID:wx08196c4219236ab5 +AppSecret:9a131c53016eb64e1f55161326307bbf + +appId:wx08196c4219236ab5 +授权回调域:grow.xxzzu.com + + +微信开放平台:(open.weixin.qq.com) +注册邮箱:jcjytbkt@126.com +密码:xx-621588+- diff --git a/doc/xxzzu.conf b/doc/xxzzu.conf new file mode 100644 index 000000000..105a5eb10 --- /dev/null +++ b/doc/xxzzu.conf @@ -0,0 +1,30 @@ +server { + listen 80; + server_name grow.xxzzu.com; + + location ^~ /system { + proxy_pass http://127.0.0.1:8081; + } + + location ~ .*\.(js|css)?$ { + expires 12h; + access_log off; + } + + location / { + index index.html index index.htm; + alias /www/muster-ui; + } + + location ^~ /common/ { + proxy_pass http://127.0.0.1:8081; + } + + #文件资源映射路径 + location /profile { + alias /www/muster/upload; + access_log off; + log_not_found off; + } + +} diff --git a/muster-admin/pom.xml b/muster-admin/pom.xml index 17c12b148..4eb5b3b68 100644 --- a/muster-admin/pom.xml +++ b/muster-admin/pom.xml @@ -73,6 +73,17 @@ muster-generator + + + com.muster + muster-logic + + + + com.google.code.gson + gson + + diff --git a/muster-admin/src/main/java/com/muster/web/controller/common/CommonController.java b/muster-admin/src/main/java/com/muster/web/controller/common/CommonController.java index a2bfa89e5..e727e8b5c 100644 --- a/muster-admin/src/main/java/com/muster/web/controller/common/CommonController.java +++ b/muster-admin/src/main/java/com/muster/web/controller/common/CommonController.java @@ -9,7 +9,7 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; -import com.muster.common.config.RuoYiConfig; +import com.muster.common.config.MusterConfig; import com.muster.common.constant.Constants; import com.muster.common.core.domain.AjaxResult; import com.muster.common.utils.StringUtils; @@ -46,7 +46,7 @@ public class CommonController throw new Exception(StringUtils.format("文件名称({})非法,不允许下载。 ", fileName)); } String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1); - String filePath = RuoYiConfig.getDownloadPath() + fileName; + String filePath = MusterConfig.getDownloadPath() + fileName; response.setCharacterEncoding("utf-8"); response.setContentType("multipart/form-data"); @@ -73,7 +73,7 @@ public class CommonController try { // 上传文件路径 - String filePath = RuoYiConfig.getUploadPath(); + String filePath = MusterConfig.getUploadPath(); // 上传并返回新文件名称 String fileName = FileUploadUtils.upload(filePath, file); String url = serverConfig.getUrl() + fileName; @@ -95,7 +95,7 @@ public class CommonController public void resourceDownload(String name, HttpServletRequest request, HttpServletResponse response) throws Exception { // 本地资源路径 - String localPath = RuoYiConfig.getProfile(); + String localPath = MusterConfig.getProfile(); // 数据库资源地址 String downloadPath = localPath + StringUtils.substringAfter(name, Constants.RESOURCE_PREFIX); // 下载名称 diff --git a/muster-admin/src/main/java/com/muster/web/controller/common/WeChatController.java b/muster-admin/src/main/java/com/muster/web/controller/common/WeChatController.java new file mode 100755 index 000000000..8e8be2a96 --- /dev/null +++ b/muster-admin/src/main/java/com/muster/web/controller/common/WeChatController.java @@ -0,0 +1,165 @@ +package com.muster.web.controller.common; + + +import com.muster.common.constant.WxConsts; +import com.muster.common.core.controller.BaseController; +import com.muster.common.core.domain.AjaxResult; +import com.muster.common.utils.StringUtils; +import com.muster.common.utils.file.MimeTypeUtils; +import com.muster.common.utils.uuid.IdUtils; +import com.muster.web.controller.muster.weixin.QrCodeUtils; +import com.muster.web.controller.muster.weixin.WeChatService; +import com.muster.web.controller.muster.weixin.WeiXinCommonUtil; +import com.muster.web.controller.muster.weixin.WeiXinOauth2Token; +import com.muster.web.controller.muster.weixin.WeiXinUserInfo; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * @ClassName WeiXinController + * @Description TOOD + * @Author guoconglin + * @DATE 2020/4/10 14:29 + * @Version 1.0 + **/ + +@RestController +@Api(value = "微信相关操作管理", description = "微信相关操作管理", tags = {"微信相关操作管理"}) +@RequestMapping("/system/wechat") +public class WeChatController extends BaseController { + + @Value("${wx.open.appid}") + private String wxMapAppid; + + @Value("${wx.open.appsecret}") + private String wxMapAppsecret; + + @Value("${wx.open.callback_url}") + private String wxMapCallbackUrl; + + @Autowired + private WeChatService weChatService; + + @GetMapping("/callback") + @ApiOperation("微信回调接口") + @ApiImplicitParams(value = { + @ApiImplicitParam(name = "redirectUri", value = "跳转url", dataType = "String"), + @ApiImplicitParam(name = "code", value = "微信授权的Code", dataType = "String") + }) + public void callback(HttpServletResponse response, HttpServletRequest request, + @RequestParam("redirectUri") String redirectUri, + @RequestParam(value = "code", required = false) String code, + @RequestParam("state") String state) throws IOException { + String redirectUrl = String.format("%s?code=%s&state=%s", redirectUri , code, state); + response.sendRedirect(redirectUrl); + } + + /** + * <1>用户访问微信网页版,此时微信服务器会为其生成一个全局唯一的UUID。然后这个UUID就 回调路径wxMapCallbackUrl的后面, + * 此时该操作并没有和用户有交互,所以该ID仅仅是个唯一字符串而已,系统并不知道该ID会和哪个用户相绑定。 +  如果此时你不断地刷新,你会发现每次的ID都会发生过变化。感兴趣的可以自己手动来抓包,这里就不做示范了。 + 注意:此时服务器和你网页还会建立一个长连接(websocket),为了节约系统资源,如果一段时间不扫描,便会超时。返回状态为408。 + + <2>用户扫描PC端的二维码,然后用户和服务创建一个长链接(websocket)判断当前状态,并且。这个步骤的目的是为了获取起生成的全局唯一UUID,进行前端状态变化。 + + <3>用户如果此时授权,则会像系统发送一条请求,并且将UUID和code一块发送过去,然后根据code获取微信用户信息,看系统是否存在该用户,不存在,则将微信用户信息保存,然后让用户取绑定账号 + <4> 如果用户输入的账号也不存在,则需要用户开通账号(开通账号方式分为自己注册和要求管理员开通), + 4.1 如果自己注册,则开始走注册流程,走注册流程的话,审核流程通过之后,然后重新扫码绑定。 + 4.2 如果需要管理员开通账号,开通账号之后,首次扫码登录或者微信授权登录都需要重新绑定一次 + + <5>系统受到这一步的目的是将UUIIDI和用户账号(或token)绑定在一起,因为二者都是唯一,便可以确定唯一的对应关系。 + 处理完该关系后,系统会向PC端反馈消息,这个UUID对应的用户是A,然后网页便可请求加载A的微信信息和资料 + + */ + + @GetMapping("/wxQrMapCode") + @ApiOperation("获取微信公众号二维码") + @ApiImplicitParams(value = { + @ApiImplicitParam(name = "width",value = "图片宽度",dataType = "Integer"), + @ApiImplicitParam(name = "height",value = "图片高度",dataType = "Integer") + }) + public AjaxResult wxQrMapCode(@RequestParam(value = "width", required = false, defaultValue = "100") int width, + @RequestParam(value = "height", required = false, defaultValue = "100") int height) { + AjaxResult ajax = AjaxResult.success(); + // 唯一标识 + String uuid = IdUtils.simpleUUID(); +// //将uuid带到回调地址上,判断当前用户是否使用了 + String authorizeUrl = WeiXinCommonUtil.getAuthorizeUrl(wxMapAppid, String.format(wxMapCallbackUrl,uuid), WxConsts.QrConnectScope.SNSAPI_LOGIN, null); + String qrCodeBasePicture = QrCodeUtils.getQrCodeBasePicture(authorizeUrl, width, height, MimeTypeUtils.getExtension(MimeTypeUtils.IMAGE_PNG)); + ajax.put("img", qrCodeBasePicture); + ajax.put("uuid",uuid); + return ajax; + } + + @PostMapping("/user-info") + @ApiOperation("通过authorizeCode获取微信用户信息,并进行判断,让前端根据返回的参数去判断") + @ApiImplicitParam(name = "authorizeCode",value = "微信授权authorizeCode",dataType = "String") + public AjaxResult getWeChatCode(@RequestParam("authorizeCode") String authorizeCode, + @RequestParam("state") String state, + @RequestParam("appId") String appId, + @RequestParam("bizId") String bizId){ + if(StringUtils.isNotNull(authorizeCode)){ + WeiXinOauth2Token oauth2AccessToken = WeiXinCommonUtil.getOauth2AccessToken(authorizeCode, wxMapAppid, wxMapAppsecret); + if(StringUtils.isNotNull(oauth2AccessToken)){ + if(oauth2AccessToken.getErrcode()==0){ +// String refreshToken = oauth2AccessToken.getRefreshToken(); + String accessToken = oauth2AccessToken.getAccessToken(); + String openId = oauth2AccessToken.getOpenId(); +// //校验授权token是否有效,false则取刷新token +// boolean b = WeiXinCommonUtil.validateToken(refreshToken, wxMapAppid); +// if(!b){ +// refreshToken = WeiXinCommonUtil.getRefreshToken(wxMapAppid, refreshToken); +// } + + WeiXinUserInfo userInfo = WeiXinCommonUtil.getUserInfo(accessToken, openId); + boolean loginFlag = weChatService.login(userInfo, appId, bizId); + if (loginFlag) { + + return AjaxResult.success(userInfo); + } + + + //先拿openId去库里查找微信信息标 +// BWxOpenUser bWxOpenUser = ibWxOpenUserService.selectByOpenId(openId); +// // 如果没有则获取微信用户信息,插入数据库 +// if(StringUtils.isNull(bWxOpenUser)){ +// //获取微信用户信息 +// WeiXinUserInfo userInfo = WeiXinCommonUtil.getUserInfo(refreshToken, openId); +// if(StringUtils.isNotNull(userInfo) && userInfo.getErrcode()==0){ +// ibWxOpenUserService.saveWxOpenUser(userInfo); +// } +// }else { +// return AjaxResult.success(openId); +// } + //如果有则判断userId有没有,如果userId为0或者null,则返回让用户绑定账号。如果有,则使用userId查询sys_user标,将user_name和password查找到,使用loginService登录判断 + + } + + } + return AjaxResult.error("登录异常,请重试"); + + }else { + return AjaxResult.error("参数不合法,请传入授权码"); + } + } + + @GetMapping("/pushToWeb/{uuid}") + public AjaxResult pushToWeb(@PathVariable String uuid){ + //TODO 写逻辑 + return AjaxResult.success(); + } +} diff --git a/muster-admin/src/main/java/com/muster/web/controller/muster/WeiXinController.java b/muster-admin/src/main/java/com/muster/web/controller/muster/WeiXinController.java new file mode 100755 index 000000000..097cd8ee2 --- /dev/null +++ b/muster-admin/src/main/java/com/muster/web/controller/muster/WeiXinController.java @@ -0,0 +1,66 @@ +package com.muster.web.controller.muster; + + +import com.muster.common.constant.Constants; +import com.muster.common.constant.WxConsts; +import com.muster.common.core.domain.AjaxResult; +import com.muster.framework.web.service.SysLoginService; +import com.muster.web.controller.muster.weixin.WeChatService; +import com.muster.web.controller.muster.weixin.WeiXinUserInfo; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +/** + * @ClassName WeiXinController + * @Description TOOD + * @Author guoconglin + * @DATE 2020/4/10 14:29 + * @Version 1.0 + **/ +@Controller +@Api(value = "微信相关操作管理", description = "微信相关操作管理", tags = {"微信相关操作管理"}) +@RequestMapping("/system/weixin") +public class WeiXinController { + + @Autowired + private WeChatService weChatService; + + @Autowired + private SysLoginService loginService; + + private String prefix = "weixin"; + + @GetMapping("/redirect") + public String user() + { + return prefix + "/redirect"; + } + + @PostMapping("/login") + @ResponseBody + @ApiOperation("子系统通过微信登录") + @ApiImplicitParam(name = "子系统通过微信登录",value = "子系统通过微信登录", dataTypeClass = WeiXinUserInfo.class) + public AjaxResult login(@RequestBody WeiXinUserInfo weiXinUserInfo) { + + boolean loginFlag = weChatService.login(weiXinUserInfo); + + if (loginFlag) { + AjaxResult ajax = AjaxResult.success(); + String token = loginService.login(WxConsts.Account.SHRIO_USERNAME, WxConsts.Account.SHRIO_PASSWORD); + + ajax.put(Constants.TOKEN, token); + return ajax; + + } + return AjaxResult.error("微信扫码登录失败,请重试"); + } + +} diff --git a/muster-admin/src/main/java/com/muster/web/controller/muster/weixin/JsApiTicket.java b/muster-admin/src/main/java/com/muster/web/controller/muster/weixin/JsApiTicket.java new file mode 100755 index 000000000..ce9c04fb9 --- /dev/null +++ b/muster-admin/src/main/java/com/muster/web/controller/muster/weixin/JsApiTicket.java @@ -0,0 +1,34 @@ +package com.muster.web.controller.muster.weixin; + + +/** + * @ClassName JsApiTicket + * @Description TOOD 获取授权页ticket 获取的ticket和有效期 + * @Author guoconglin + * @DATE 2020/4/10 10:39 + * @Version 1.0 + **/ + +public class JsApiTicket { + + // 接口访问凭证 + private String ticket; + // 凭证有效期,单位:秒 + private int expiresIn; + + public String getTicket() { + return ticket; + } + + public void setTicket(String ticket) { + this.ticket = ticket; + } + + public int getExpiresIn() { + return expiresIn; + } + + public void setExpiresIn(int expiresIn) { + this.expiresIn = expiresIn; + } +} diff --git a/muster-admin/src/main/java/com/muster/web/controller/muster/weixin/MyX509TrustManager.java b/muster-admin/src/main/java/com/muster/web/controller/muster/weixin/MyX509TrustManager.java new file mode 100755 index 000000000..a30d0c8ba --- /dev/null +++ b/muster-admin/src/main/java/com/muster/web/controller/muster/weixin/MyX509TrustManager.java @@ -0,0 +1,31 @@ +package com.muster.web.controller.muster.weixin; + +import javax.net.ssl.X509TrustManager; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +/** + * @ClassName MyX509TrustManager + * @Description TOOD + * @Author guoconglin + * @DATE 2020/4/10 10:47 + * @Version 1.0 + **/ + + +public class MyX509TrustManager implements X509TrustManager { + @Override + public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { + + } + + @Override + public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { + + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } +} diff --git a/muster-admin/src/main/java/com/muster/web/controller/muster/weixin/QrCodeUtils.java b/muster-admin/src/main/java/com/muster/web/controller/muster/weixin/QrCodeUtils.java new file mode 100755 index 000000000..42094c15c --- /dev/null +++ b/muster-admin/src/main/java/com/muster/web/controller/muster/weixin/QrCodeUtils.java @@ -0,0 +1,46 @@ +package com.muster.web.controller.muster.weixin; + + +import cn.hutool.extra.qrcode.QrCodeUtil; +import com.muster.common.utils.sign.Base64; + +import javax.servlet.http.HttpServletResponse; +import java.io.ByteArrayOutputStream; + +/** + * @ClassName QrCodeUtils + * @Description TOOD 生成二维码图片 + * @Author guoconglin + * @DATE 2020/4/10 13:57 + * @Version 1.0 + **/ + + +public class QrCodeUtils { + + /** + * 生成二维码方法返回到流 + * @author guoconglin + * @date 2020/4/10 13:44 + * @param: [url 访问链接, width 图片宽, height 图片高, mimeType 类型比图png\jpg, resp] + * @return: void + */ + public static void getQrcode(String url,int width,int height,String mimeType, HttpServletResponse resp) throws Exception { + QrCodeUtil.generate(url,width,height, mimeType,resp.getOutputStream()); + } + + /** + * 生成base64图片 + * @author guoconglin + * @date 2020/4/10 13:49 + * @param: [url 访问链接, width 图片宽, height 图片高, mimeType 类型比图png\jpg] + * @return: java.lang.String + */ + public static String getQrCodeBasePicture(String url,int width,int height,String mimeType){ + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + QrCodeUtil.generate(url,width,height,mimeType,stream); + String base64EnCode = Base64.encode(stream.toByteArray()); + return base64EnCode; + } + +} diff --git a/muster-admin/src/main/java/com/muster/web/controller/muster/weixin/SignUtil.java b/muster-admin/src/main/java/com/muster/web/controller/muster/weixin/SignUtil.java new file mode 100755 index 000000000..c3cc89782 --- /dev/null +++ b/muster-admin/src/main/java/com/muster/web/controller/muster/weixin/SignUtil.java @@ -0,0 +1,79 @@ +package com.muster.web.controller.muster.weixin; + +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Formatter; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * @ClassName SignUtil + * @Description TOOD 微信签名 + * @Author guoconglin + * @DATE 2020/4/10 13:17 + * @Version 1.0 + **/ + + +public class SignUtil { + + public static Map sign(String jsapi_ticket, String url) { + Map ret = new HashMap(); + String nonce_str = create_nonce_str(); + String timestamp = create_timestamp(); + String string1; + String signature = ""; + + //注意这里参数名必须全部小写,且必须有序 + string1 = "jsapi_ticket=" + jsapi_ticket + + "&noncestr=" + nonce_str + + "×tamp=" + timestamp + + "&url=" + url; + System.out.println(string1); + + try + { + MessageDigest crypt = MessageDigest.getInstance("SHA-1"); + crypt.reset(); + crypt.update(string1.getBytes("UTF-8")); + signature = byteToHex(crypt.digest()); + } + catch (NoSuchAlgorithmException e) + { + e.printStackTrace(); + } + catch (UnsupportedEncodingException e) + { + e.printStackTrace(); + } + + ret.put("url", url); + ret.put("jsapi_ticket", jsapi_ticket); + ret.put("nonceStr", nonce_str); + ret.put("timestamp", timestamp); + ret.put("signature", signature); + + return ret; + } + + private static String byteToHex(final byte[] hash) { + Formatter formatter = new Formatter(); + for (byte b : hash) + { + formatter.format("%02x", b); + } + String result = formatter.toString(); + formatter.close(); + return result; + } + + private static String create_nonce_str() { + return UUID.randomUUID().toString(); + } + + private static String create_timestamp() { + return Long.toString(System.currentTimeMillis() / 1000); + } +} diff --git a/muster-admin/src/main/java/com/muster/web/controller/muster/weixin/Token.java b/muster-admin/src/main/java/com/muster/web/controller/muster/weixin/Token.java new file mode 100755 index 000000000..3289ee472 --- /dev/null +++ b/muster-admin/src/main/java/com/muster/web/controller/muster/weixin/Token.java @@ -0,0 +1,34 @@ +package com.muster.web.controller.muster.weixin; + + +/** + * @ClassName Token + * @Description TOOD 获取的token 和有效时间 + * @Author guoconglin + * @DATE 2020/4/10 10:40 + * @Version 1.0 + **/ + +public class Token { + + // 接口访问凭证 + private String accessToken; + // 凭证有效期,单位:秒 + private int expiresIn; + + public String getAccessToken() { + return accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + + public int getExpiresIn() { + return expiresIn; + } + + public void setExpiresIn(int expiresIn) { + this.expiresIn = expiresIn; + } +} diff --git a/muster-admin/src/main/java/com/muster/web/controller/muster/weixin/URIUtil.java b/muster-admin/src/main/java/com/muster/web/controller/muster/weixin/URIUtil.java new file mode 100755 index 000000000..c4681d795 --- /dev/null +++ b/muster-admin/src/main/java/com/muster/web/controller/muster/weixin/URIUtil.java @@ -0,0 +1,58 @@ +package com.muster.web.controller.muster.weixin; + +import com.muster.common.utils.StringUtils; + +import java.io.UnsupportedEncodingException; + +/** + * @ClassName URIUtil + * @Description TOOD 网站转码 + * @Author guoconglin + * @DATE 2020/4/10 12:50 + * @Version 1.0 + **/ + + +public class URIUtil { + + private static final String ALLOWED_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.!~*'()"; + + public static String encodeURIComponent(String input) { + if (StringUtils.isEmpty(input)) { + return input; + } + + int l = input.length(); + StringBuilder o = new StringBuilder(l * 3); + try { + for (int i = 0; i < l; i++) { + String e = input.substring(i, i + 1); + if (ALLOWED_CHARS.indexOf(e) == -1) { + byte[] b = e.getBytes("utf-8"); + o.append(getHex(b)); + continue; + } + o.append(e); + } + return o.toString(); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + return input; + } + + private static String getHex(byte buf[]) { + StringBuilder o = new StringBuilder(buf.length * 3); + for (int i = 0; i < buf.length; i++) { + int n = buf[i] & 0xff; + o.append("%"); + if (n < 0x10) { + o.append("0"); + } + o.append(Long.toString(n, 16).toUpperCase()); + } + return o.toString(); + } +} + + diff --git a/muster-admin/src/main/java/com/muster/web/controller/muster/weixin/WeChatService.java b/muster-admin/src/main/java/com/muster/web/controller/muster/weixin/WeChatService.java new file mode 100644 index 000000000..b575ed594 --- /dev/null +++ b/muster-admin/src/main/java/com/muster/web/controller/muster/weixin/WeChatService.java @@ -0,0 +1,19 @@ +package com.muster.web.controller.muster.weixin; + +/** + * Description + *

+ *

+ * DATE 2020-07-08. + * + * @author 刘江涛. + */ +public interface WeChatService { + + int query(final String openId); + + boolean login(final WeiXinUserInfo userInfo); + + boolean login(final WeiXinUserInfo userInfo, String appId, String bizId); + +} diff --git a/muster-admin/src/main/java/com/muster/web/controller/muster/weixin/WeChatServiceImpl.java b/muster-admin/src/main/java/com/muster/web/controller/muster/weixin/WeChatServiceImpl.java new file mode 100644 index 000000000..94f07e1ec --- /dev/null +++ b/muster-admin/src/main/java/com/muster/web/controller/muster/weixin/WeChatServiceImpl.java @@ -0,0 +1,75 @@ +package com.muster.web.controller.muster.weixin; + +import com.muster.common.config.MusterConfig; +import com.muster.common.constant.WxConsts; +import com.muster.logic.DbLogicService; +import com.muster.logic.model.ProcedureResult; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; + +/** + * Description + *

+ *

+ * DATE 2020-07-11. + * + * @author 刘江涛. + */ +@Component +public class WeChatServiceImpl implements WeChatService{ + + private DbLogicService storedProcedure; + + /** 微信用户 */ + public final static String PROC_DU02A_USER_LOGIN = "proc_DU02A_user_login"; + public final static String PROC_DU02C_USER_CREATE = "proc_DU02C_user_create"; + @Autowired + WeChatServiceImpl (DbLogicService storedProcedure) { + this.storedProcedure = storedProcedure; + } + + @Override + public int query(final String openId) { + return 1; + } + + + @Override + public boolean login(final WeiXinUserInfo userInfo) { + return this.login(userInfo, MusterConfig.getAppId(), MusterConfig.getBizId()); + } + + @Override + public boolean login(final WeiXinUserInfo userInfo, String appId, String bizId) { + // 创建账号 + Map params = new HashMap<>(); + params.put("01_acc_type", WxConsts.Account.TYPE); + params.put("02_username", userInfo.getWxOpenid()); + params.put("03_password", WxConsts.Account.PASSWORD); + params.put("04_userip", "0.0.0.0"); + params.put("appId", appId); + params.put("bizId", bizId); + + int createRes = this.create(params); + if (createRes > 0) { + int loginRes = this.login(params); + return loginRes > 0; + } + return false; + } + + private int create( final Map params) { + Map p = new HashMap<>(params); + p.put("05_json", "{}"); + ProcedureResult createRes = storedProcedure.exec(PROC_DU02C_USER_CREATE, p); + return createRes.getRes(); + } + + private int login(final Map params) { + ProcedureResult loginRes = storedProcedure.exec(PROC_DU02A_USER_LOGIN, params); + return loginRes.getRes(); + } +} diff --git a/muster-admin/src/main/java/com/muster/web/controller/muster/weixin/WechatQRCode.java b/muster-admin/src/main/java/com/muster/web/controller/muster/weixin/WechatQRCode.java new file mode 100755 index 000000000..28e8cdbb9 --- /dev/null +++ b/muster-admin/src/main/java/com/muster/web/controller/muster/weixin/WechatQRCode.java @@ -0,0 +1,44 @@ +package com.muster.web.controller.muster.weixin; + + +/** + * @ClassName WechatQRCode + * @Description TOOD 二维码参数 + * @Author guoconglin + * @DATE 2020/4/10 13:29 + * @Version 1.0 + **/ + +public class WechatQRCode { + + // 获取的二维码 + private String ticket; + // 二维码的有效时间,单位为秒,最大不超过2592000(即30天) + private int expire_seconds; + // 二维码图片解析后的地址 + private String url; + + public String getTicket() { + return ticket; + } + + public void setTicket(String ticket) { + this.ticket = ticket; + } + + public int getExpire_seconds() { + return expire_seconds; + } + + public void setExpire_seconds(int expire_seconds) { + this.expire_seconds = expire_seconds; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } +} diff --git a/muster-admin/src/main/java/com/muster/web/controller/muster/weixin/WeiXinCommonUtil.java b/muster-admin/src/main/java/com/muster/web/controller/muster/weixin/WeiXinCommonUtil.java new file mode 100755 index 000000000..03ec32c4a --- /dev/null +++ b/muster-admin/src/main/java/com/muster/web/controller/muster/weixin/WeiXinCommonUtil.java @@ -0,0 +1,641 @@ +package com.muster.web.controller.muster.weixin; + +import com.alibaba.fastjson.JSONException; +import com.alibaba.fastjson.JSONObject; +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; +import com.muster.common.utils.StringUtils; +import com.muster.common.utils.http.HttpUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.ConnectException; +import java.net.URL; +import java.text.ParseException; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * @ClassName WeiXinCommonUtil + * @Description TOOD 微信通用工具类 + * @Author guoconglin + * @DATE 2020/4/10 10:38 + * @Version 1.0 + **/ + + +public class WeiXinCommonUtil { + + private static final Logger LOG = LoggerFactory.getLogger(WeiXinCommonUtil.class); + + + private static Map weixinCache = new HashMap(); + + private static Map tokenCache = new HashMap(); + + // 临时二维码 + private static final String QR_SCENE = "QR_SCENE"; + // 永久二维码 + private static final String QR_LIMIT_SCENE = "QR_LIMIT_SCENE"; + // 永久二维码(字符串) + private static final String QR_LIMIT_STR_SCENE = "QR_LIMIT_STR_SCENE"; + // 创建二维码 + private static final String create_ticket_path = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=%s"; + // 通过ticket换取二维码 + private static final String showqrcode_path = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=%s"; + + // 凭证获取(GET) + public static final String TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s"; + /** + * 长链接转短链接接口 + */ + public static final String SHORTURL_API_URL = "https://api.weixin.qq.com/cgi-bin/shorturl?access_token=%s"; + /** + * 获取access_token + */ + public static final String GET_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s"; + + /** + * 获得jsapi_ticket + */ + public static final String GET_JSAPI_TICKET_URL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi"; + + /** + * 通过access token获得jsapi_ticket + */ + public static final String TOKEN_GET_JSAPI_TICKET_URL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=%s&type=jsapi"; + + /** + * 用code换取oauth2的access token + */ + public static final String OAUTH2_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code"; + /** + * 刷新oauth2的access token + */ + public static final String OAUTH2_REFRESH_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=%s&grant_type=refresh_token&refresh_token=%s"; + /** + * 用oauth2获取用户信息 + */ + public static final String OAUTH2_USERINFO_URL = "https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=%s"; + /** + * 验证oauth2的access token是否有效 + */ + public static final String OAUTH2_VALIDATE_TOKEN_URL = "https://api.weixin.qq.com/sns/auth?access_token=%s&openid=%s"; + /** + * 获取微信服务器IP地址 + */ + public static final String GET_CALLBACK_IP_URL = "https://api.weixin.qq.com/cgi-bin/getcallbackip"; + /** + * 第三方使用网站应用授权登录的url + */ + public static final String QRCONNECT_URL = "https://open.weixin.qq.com/connect/qrconnect?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=%s#wechat_redirect"; + /** + * oauth2授权的url连接 + */ + public static final String CONNECT_OAUTH2_AUTHORIZE_URL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=%s#wechat_redirect"; + + /** + * 获取公众号的自动回复规则 + */ + public static final String GET_CURRENT_AUTOREPLY_INFO_URL = "https://api.weixin.qq.com/cgi-bin/get_current_autoreply_info"; + + /** + * 公众号调用或第三方平台帮公众号调用对公众号的所有api调用(包括第三方帮其调用)次数进行清零 + */ + public static final String CLEAR_QUOTA_URL = "https://api.weixin.qq.com/cgi-bin/clear_quota"; + + /** + * 通过openId 获取公众号用户详细信息 + */ + public static final String OAUTH2_USERINFO_URLS = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=%s&openid=%s&lang=%s"; + + /** + * 公众号发送模板消息链接 + */ + public static final String SEND_MP_URL = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=%s"; + + + + + /** + * 获取接口访问凭证 + * @author guoconglin + * @date 2020/4/10 10:44 + * @param: [appId 微信平台开账号的appId, appSecret 微信开放平台开账号后的appSecret] + * @return: com.sanqi.common.utils.weixin.Token + */ + public static Token getToken(String appId,String appSecret) { + Token token = null; + String requestUrl = String.format(TOKEN_URL,appId,appSecret); + // 发起GET请求获取凭证 + JSONObject jsonObject = httpsRequest(requestUrl, "GET", null); + LOG.info("获取token jsonObject jsonObject:{}", jsonObject); + if (null != jsonObject) { + try { + token = new Token(); + token.setAccessToken(jsonObject.getString("access_token")); + token.setExpiresIn(jsonObject.getIntValue("expires_in")); + } catch (JSONException e) { + token = null; + // 获取token失败 + LOG.error("获取token失败 errcode:{} errmsg:{}", jsonObject.getIntValue("errcode"), jsonObject.getString("errmsg")); + } + } + return token; + } + + /** + * 刷新token + * @param appId 微信平台开的唯一标识 + * @param accessToken token + * @return + */ + public static String getRefreshToken(String appId,String accessToken){ + String refreshToken = String.format(OAUTH2_REFRESH_TOKEN_URL,appId,accessToken); + return refreshToken; + } + + /** + * 验证token是否有效 + * @param refreshToken 刷新获取到的新token + * @param appId 微信平台开的唯一标识 + * @return + */ + public static boolean validateToken(String refreshToken,String appId){ + String refreshTokenUrl = String.format(OAUTH2_VALIDATE_TOKEN_URL,refreshToken,appId); + JSONObject jsonObject = httpsRequest(refreshTokenUrl, "GET", null); + if(jsonObject != null){ + //TODO需要判断 + int errcode = jsonObject.getIntValue("errcode"); + if(errcode == 0){ //则证明有效 + return true; + }else { + return false; + } + } + return false; + } + + /** + * 获取微信授权页面链接 + * @param appId 微信平台开的唯一标识 + * @param redirectURI 回调地址 + * @param scope + * @param state + * @return + */ + public static String getAuthorizeUrl(String appId,String redirectURI, String scope, String state){ + String authorizeUrl = String.format(QRCONNECT_URL,appId,URIUtil.encodeURIComponent(redirectURI),scope, StringUtils.trimToEmpty(state)); + return authorizeUrl; + } + + /** + * 用code换取oauth2的access token + * @author guoconglin + * @date 2020/4/10 12:55 + * @param: [code, appId 微信平台唯一识别appId, appSecret 微信平台唯一识别appSecret] + * @return: com.sanqi.common.utils.weixin.WeiXinOauth2Token + */ + public static WeiXinOauth2Token getOauth2AccessToken(String code,String appId,String appSecret){ + WeiXinOauth2Token weixinOauth2Token = new WeiXinOauth2Token(); + String getTokenUrl = String.format(OAUTH2_ACCESS_TOKEN_URL, appId, appSecret, code); + JSONObject jsonObject = httpsRequest(getTokenUrl, "GET", null); + LOG.info("获取网页授权凭证 jsonObject:{}",jsonObject); + if (null != jsonObject) { + if(jsonObject.getIntValue("errcode") == 0){ + weixinOauth2Token.setAccessToken(jsonObject.getString("access_token")); + weixinOauth2Token.setExpiresIn(jsonObject.getIntValue("expires_in")); + weixinOauth2Token.setRefreshToken(jsonObject.getString("refresh_token")); + weixinOauth2Token.setOpenId(jsonObject.getString("openid")); + weixinOauth2Token.setScope(jsonObject.getString("scope")); + weixinOauth2Token.setErrcode(jsonObject.getIntValue("errcode")); + weixinOauth2Token.setErrmsg(jsonObject.getString("errmsg")); + weixinOauth2Token.setUnionId(jsonObject.containsKey("unionid")?jsonObject.getString("unionid"):null); + LOG.info("获取网页授权凭证成功 weixinOauth2Token:{}",weixinOauth2Token); + }else { + int errorCode = jsonObject.getIntValue("errcode"); + String errorMsg = jsonObject.getString("errmsg"); + weixinOauth2Token.setErrcode(errorCode); + weixinOauth2Token.setErrmsg(errorMsg); + LOG.error("获取网页授权凭证失败 errcode:{} errmsg:{}", errorCode, errorMsg); + } + } + return weixinOauth2Token; + } + + + /** + * 通过网页授权获取用户信息 + * @author guoconglin + * @date 2020/4/10 13:00 + * @param: [accessToken 获取的token, openId 用户的openId] + * @return: com.sanqi.common.utils.weixin.WeiXinUserInfo + */ + public static WeiXinUserInfo getUserInfo(String accessToken, String openId){ + WeiXinUserInfo weixinUserInfo = new WeiXinUserInfo(); + String userInfoUrl = String.format(OAUTH2_USERINFO_URL, accessToken, openId, "zh_CN"); + JSONObject jsonObject = httpsRequest(userInfoUrl, "GET", null); + LOG.info("jsonObject:{}",jsonObject); + LOG.info("jsonObject:{}",jsonObject.getIntValue("subscribe")); + if (null != jsonObject) { + try { + if(jsonObject.getIntValue("errcode") == 0){ + // 用户的标识 + if(jsonObject.containsKey("openid")){ + weixinUserInfo.setWxOpenid(jsonObject.getString("openid")); + } + if(jsonObject.containsKey("subscribe")){ + // 关注状态(1是关注,0是未关注),未关注时获取不到其余信息 + int subscribe = jsonObject.getIntValue("subscribe"); + weixinUserInfo.setSubscribe(subscribe); + } + // 用户关注时间 + if(jsonObject.containsKey("subscribe_time")){ + weixinUserInfo.setSubscribeTime(jsonObject.getString("subscribe_time")); + } + // 昵称 + if(jsonObject.containsKey("nickname")){ + weixinUserInfo.setNickName(jsonObject.getString("nickname")); + } + // 用户的性别(1是男性,2是女性,0是未知) + if(jsonObject.containsKey("sex")){ + weixinUserInfo.setSex(jsonObject.getIntValue("sex")); + } + // 用户所在国家 + if(jsonObject.containsKey("country")){ + weixinUserInfo.setCountry(jsonObject.getString("country")); + } + // 用户所在省份 + if(jsonObject.containsKey("province")){ + weixinUserInfo.setProvince(jsonObject.getString("province")); + } + // 用户所在城市 + if(jsonObject.containsKey("city")){ + weixinUserInfo.setCity(jsonObject.getString("city")); + } + // 用户的语言,简体中文为zh_CN + if(jsonObject.containsKey("language")){ + weixinUserInfo.setLanguage(jsonObject.getString("language")); + } + // 用户头像 + if(jsonObject.containsKey("headimgurl")){ + weixinUserInfo.setHeadImgUrl(jsonObject.getString("headimgurl")); + } + //unionid + if(jsonObject.containsKey("unionid")){ + weixinUserInfo.setUnionId(jsonObject.getString("unionid")); + } + int errorCode = jsonObject.getIntValue("errcode"); + String errorMsg = jsonObject.getString("errmsg"); + weixinUserInfo.setErrcode(errorCode); + weixinUserInfo.setErrmsg(errorMsg); + LOG.info("获取用户信息成功 weixinUserInfo:{}", weixinUserInfo); + } + + } catch (Exception e) { + int errorCode = jsonObject.getIntValue("errcode"); + String errorMsg = jsonObject.getString("errmsg"); + weixinUserInfo.setErrcode(errorCode); + weixinUserInfo.setErrmsg(errorMsg); + LOG.error("获取用户信息失败 errcode:{} errmsg:{}", errorCode, errorMsg); + } + } + return weixinUserInfo; + } + + /** + * 创建调用jsapi时所需要的签名 + * @author guoconglin + * @date 2020/4/10 13:19 + * @param: [url, mpAppId, mpSecret] + * @return: com.sanqi.common.utils.weixin.WxJsapiSignature + */ + public static WxJsapiSignature createJsapiSignature(String url,String mpAppId,String mpSecret) throws ParseException { + LOG.info("创建调用jsapi时所需要的签名 url:{} ",url); + String token_str = null; + Token token = tokenCache.get("accessToken"); + if(StringUtils.isNotNull(token)){ + int expiresIn = token.getExpiresIn(); + Calendar ever = Calendar.getInstance(); + Date date = new Date(expiresIn*1000L); + ever.setTime(date); + Calendar current = Calendar.getInstance(); + if(ever.after(current)){ + token_str = token.getAccessToken(); + LOG.info("获取的accessToken:{}",token_str); + }else { + token_str = getWxJsapiTokenStr(mpAppId, mpSecret); + } + }else{ + token_str = getWxJsapiTokenStr(mpAppId, mpSecret); + } + WxJsapiSignature wxJsapiSignature = null; + String ticket_str = null; + LOG.info("weixinCache:{}",weixinCache); + JsApiTicket jsApiTicket = weixinCache.get("ticket"); + LOG.info("获取jsApiTicket成功 jsApiTicket:{}", jsApiTicket); + if(jsApiTicket != null){ + int expiresIn = jsApiTicket.getExpiresIn(); + Calendar ever = Calendar.getInstance(); + Date date = new Date(expiresIn*1000L); + ever.setTime(date); + Calendar current = Calendar.getInstance(); + if(ever.after(current)) { + ticket_str = jsApiTicket.getTicket(); + LOG.info("获取jsApiTicket成功 ticket_str:{}", ticket_str); + }else{ + String ticketUrl = String.format(TOKEN_GET_JSAPI_TICKET_URL,token_str); + JSONObject jsonObject = httpsRequest(ticketUrl, "GET", null); + if(jsonObject != null){ + try { + ticket_str = jsonObject.getString("ticket"); + JsApiTicket jsApiTicket1 = new JsApiTicket(); + weixinCache.remove("ticket"); + jsApiTicket1.setTicket(jsonObject.getString("ticket")); + LOG.info("获取jticket的值为: ticket:{}",jsonObject.getString("ticket")); + jsApiTicket1.setExpiresIn(jsonObject.getIntValue("expires_in")); + weixinCache.put("ticket", jsApiTicket1); + } catch (JSONException e) { + token = null; + // 获取jsApiTicket失败 + LOG.error("获取jsApiTicket失败 errcode:{} errmsg:{}", jsonObject.getIntValue("errcode"), jsonObject.getString("errmsg")); + } + } + } + Map sign = SignUtil.sign(ticket_str, url); + wxJsapiSignature = new WxJsapiSignature(); + wxJsapiSignature.setAppId(mpAppId); + wxJsapiSignature.setNonceStr(sign.get("nonceStr")); + wxJsapiSignature.setTimestamp(Long.parseLong(sign.get("timestamp"))); + wxJsapiSignature.setSignature(sign.get("signature")); + LOG.info("获取jsApiTicket成功 wxJsapiSignature:{}", wxJsapiSignature); + }else{ + String ticketUrl = String.format(TOKEN_GET_JSAPI_TICKET_URL,token_str); + JSONObject jsonObject = httpsRequest(ticketUrl, "GET", null); + LOG.info("jsonObject 请求jsonObject:{}",jsonObject); + if(jsonObject != null){ + try { + JsApiTicket jsApiTicket1 = new JsApiTicket(); + weixinCache.remove("ticket"); + jsApiTicket1.setTicket(jsonObject.getString("ticket")); + jsApiTicket1.setExpiresIn(jsonObject.getIntValue("expires_in")); + weixinCache.put("ticket", jsApiTicket1); + LOG.info("获取ticket成功 ========= ticket:{}", jsApiTicket1); + Map sign = SignUtil.sign(jsApiTicket1.getTicket(), url); + wxJsapiSignature = new WxJsapiSignature(); + wxJsapiSignature.setAppId(mpAppId); + wxJsapiSignature.setNonceStr(sign.get("nonceStr")); + wxJsapiSignature.setTimestamp(Long.parseLong(sign.get("timestamp"))); + wxJsapiSignature.setSignature(sign.get("signature")); + LOG.info("获取jsApiTicket成功 ========= wxJsapiSignature:{}", wxJsapiSignature); + } catch (JSONException e) { + token = null; + // 获取jsApiTicket失败 + LOG.error("获取jsApiTicket失败 errcode:{} errmsg:{}", jsonObject.getIntValue("errcode"), jsonObject.getString("errmsg")); + } + } + } + return wxJsapiSignature; + } + + private static String getWxJsapiTokenStr(String mpAppId,String mpSecret){ + String token_str = null; + Token token = getToken(mpAppId, mpSecret); + LOG.info("获取的token1:{}",token); + if(StringUtils.isNotNull(token)){ + token_str = token.getAccessToken(); + tokenCache.remove("accessToken"); + tokenCache.put("accessToken",token); + }else{ + LOG.info("失败{} {}"); + } + LOG.info("获取的第二次accessToken:{}",token_str); + return token_str; + } + + /** + * 创建临时带参数二维码 + * @param accessToken + * @expireSeconds 该二维码有效时间,以秒为单位。 最大不超过2592000(即30天),此字段如果不填,则默认有效期为30秒。 + * @param sceneId 场景Id + * @return + */ + public static String createTempTicket(String accessToken, String expireSeconds, int sceneId) { + WechatQRCode wxQRCode = null; + Map intMap = new HashMap(); + intMap.put("scene_id",sceneId); + Map> mapMap = new HashMap>(); + mapMap.put("scene", intMap); + // + Map paramsMap = new HashMap(); + paramsMap.put("expire_seconds", expireSeconds); + paramsMap.put("action_name", QR_SCENE); + paramsMap.put("action_info", mapMap); + String data = new Gson().toJson(paramsMap); + System.out.println("创建临时带参数二维码参数"+data); + String requestUrl = String.format(create_ticket_path,accessToken); + data = HttpUtils.sendPost( requestUrl, data); + System.out.println("创建临时带参数二维码返回结果"+data); + try { + wxQRCode = new Gson().fromJson(data, WechatQRCode.class); + } catch (JsonSyntaxException e) { + wxQRCode = null; + e.printStackTrace(); + } + System.out.println("创建临时带参数二维码返回转换对象"+wxQRCode); + return wxQRCode==null?null:wxQRCode.getUrl(); + + } + + /** + * 创建永久二维码(数字) + * @param accessToken + * @param sceneId 场景Id + * @return + */ + public static String createForeverTicket(String accessToken, int sceneId) { + + //output data + Map intMap = new HashMap(); + intMap.put("scene_id",sceneId); + Map> mapMap = new HashMap>(); + mapMap.put("scene", intMap); + // + Map paramsMap = new HashMap(); + paramsMap.put("action_name", QR_LIMIT_SCENE); + paramsMap.put("action_info", mapMap); + String data = new Gson().toJson(paramsMap); + System.out.println("创建临时带参数二维码参数"+data); + String requestUrl = String.format(create_ticket_path,accessToken); + data = HttpUtils.sendPost( requestUrl, data); + System.out.println("创建临时带参数二维码返回结果"+data); + WechatQRCode wxQRCode = null; + try { + wxQRCode = new Gson().fromJson(data, WechatQRCode.class); + } catch (JsonSyntaxException e) { + wxQRCode = null; + e.printStackTrace(); + } + return wxQRCode==null?null:wxQRCode.getTicket(); + } + + /** + * 创建永久二维码(字符串) + * + * @param accessToken + * @param sceneStr 场景str + * @return + */ + public static String createForeverStrTicket(String accessToken, String sceneStr){ + //output data + Map intMap = new HashMap(); + intMap.put("scene_str",sceneStr); + Map> mapMap = new HashMap>(); + mapMap.put("scene", intMap); + + Map paramsMap = new HashMap(); + paramsMap.put("action_name", QR_LIMIT_STR_SCENE); + paramsMap.put("action_info", mapMap); + String data = new Gson().toJson(paramsMap); + System.out.println("创建临时带参数二维码参数"+data); + String requestUrl = String.format(create_ticket_path,accessToken); + data = HttpUtils.sendPost( requestUrl, data); + System.out.println("创建临时带参数二维码返回结果"+data); + WechatQRCode wxQRCode = null; + try { + wxQRCode = new Gson().fromJson(data, WechatQRCode.class); + } catch (JsonSyntaxException e) { + wxQRCode = null; + } + return wxQRCode==null?null:wxQRCode.getTicket(); + } + + + /** + * 获取二维码ticket后,通过ticket换取二维码图片展示 + * @param ticket + * @return + */ + public static String showQrcode(String ticket){ + System.out.println("通过ticket换取二维码图片展示参数==="+ticket); + String s = null; + try { + s = HttpUtils.sendGet(String.format(showqrcode_path,urlEncodeUTF8(ticket)),""); + System.out.println("通过ticket换取二维码图片展示参数==="+s); + } catch (Exception e) { + e.printStackTrace(); + } + return s; + } + + /** + * URL编码(utf-8) + * + * @param source + * @return + */ + public static String urlEncodeUTF8(String source) { + String result = source; + try { + result = java.net.URLEncoder.encode(source, "utf-8"); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + return result; + } + + /** + * 长链接生成短链接 + * @param longUrl 长链接地址 + * @param accessToken token令牌 + * @return + */ + public static String Long2Short(String longUrl,String accessToken){ + Map paramsMap = new HashMap(); + paramsMap.put("action", "long2short"); + paramsMap.put("long_url", longUrl); + String data = new Gson().toJson(paramsMap); + String requestUrl = String.format(SHORTURL_API_URL,accessToken); + data = HttpUtils.sendPost(requestUrl,data); + JSONObject jsonObject = JSONObject.parseObject(data); + return jsonObject.getString("short_url"); + } + + + + + + + + /** + * 发送https请求 + * + * @param requestUrl 请求地址 + * @param requestMethod 请求方式(GET、POST) + * @param outputStr 提交的数据 + * @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值) + */ + public static JSONObject httpsRequest(String requestUrl, String requestMethod, String outputStr) { + JSONObject jsonObject = null; + try { + // 创建SSLContext对象,并使用我们指定的信任管理器初始化 + TrustManager[] tm = { new MyX509TrustManager() }; + SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE"); + sslContext.init(null, tm, new java.security.SecureRandom()); + // 从上述SSLContext对象中得到SSLSocketFactory对象 + SSLSocketFactory ssf = sslContext.getSocketFactory(); + + URL url = new URL(requestUrl); + HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); + conn.setSSLSocketFactory(ssf); + + conn.setDoOutput(true); + conn.setDoInput(true); + conn.setUseCaches(false); + // 设置请求方式(GET/POST) + conn.setRequestMethod(requestMethod); + + // 当outputStr不为null时向输出流写数据 + if (null != outputStr) { + OutputStream outputStream = conn.getOutputStream(); + // 注意编码格式 + outputStream.write(outputStr.getBytes("UTF-8")); + outputStream.close(); + } + + // 从输入流读取返回内容 + InputStream inputStream = conn.getInputStream(); + InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8"); + BufferedReader bufferedReader = new BufferedReader(inputStreamReader); + String str = null; + StringBuffer buffer = new StringBuffer(); + while ((str = bufferedReader.readLine()) != null) { + buffer.append(str); + } + + // 释放资源 + bufferedReader.close(); + inputStreamReader.close(); + inputStream.close(); + conn.disconnect(); + jsonObject = JSONObject.parseObject(buffer.toString()); + } catch (ConnectException ce) { + LOG.error("连接超时:{}", ce); + } catch (Exception e) { + LOG.error("https请求异常:{}", e); + } + return jsonObject; + } + +} diff --git a/muster-admin/src/main/java/com/muster/web/controller/muster/weixin/WeiXinOauth2Token.java b/muster-admin/src/main/java/com/muster/web/controller/muster/weixin/WeiXinOauth2Token.java new file mode 100755 index 000000000..5427123da --- /dev/null +++ b/muster-admin/src/main/java/com/muster/web/controller/muster/weixin/WeiXinOauth2Token.java @@ -0,0 +1,96 @@ +package com.muster.web.controller.muster.weixin; + + +/** + * @ClassName WeiXinOauth2Token + * @Description TOOD 微信授权 + * @Author guoconglin + * @DATE 2020/4/10 10:13 + * @Version 1.0 + **/ + +public class WeiXinOauth2Token { + + // 网页授权接口调用凭证 + private String accessToken; + // 凭证有效时长 + private int expiresIn; + // 用于刷新凭证 + private String refreshToken; + // 用户标识 + private String openId; + // 用户授权作用域 + private String scope; + + //unionId + private String unionId; + + //错误码 + private int errcode; + //错误信息 + private String errmsg; + + public String getAccessToken() { + return accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + + public int getExpiresIn() { + return expiresIn; + } + + public void setExpiresIn(int expiresIn) { + this.expiresIn = expiresIn; + } + + public String getRefreshToken() { + return refreshToken; + } + + public void setRefreshToken(String refreshToken) { + this.refreshToken = refreshToken; + } + + public String getOpenId() { + return openId; + } + + public void setOpenId(String openId) { + this.openId = openId; + } + + public String getScope() { + return scope; + } + + public void setScope(String scope) { + this.scope = scope; + } + + public String getUnionId() { + return unionId; + } + + public void setUnionId(String unionId) { + this.unionId = unionId; + } + + public int getErrcode() { + return errcode; + } + + public void setErrcode(int errcode) { + this.errcode = errcode; + } + + public String getErrmsg() { + return errmsg; + } + + public void setErrmsg(String errmsg) { + this.errmsg = errmsg; + } +} diff --git a/muster-admin/src/main/java/com/muster/web/controller/muster/weixin/WeiXinUserInfo.java b/muster-admin/src/main/java/com/muster/web/controller/muster/weixin/WeiXinUserInfo.java new file mode 100755 index 000000000..33b081007 --- /dev/null +++ b/muster-admin/src/main/java/com/muster/web/controller/muster/weixin/WeiXinUserInfo.java @@ -0,0 +1,166 @@ +package com.muster.web.controller.muster.weixin; + + +/** + * @ClassName WeiXinUserInfo + * @Description TOOD + * @Author guoconglin + * @DATE 2020/4/10 12:59 + * @Version 1.0 + **/ + +public class WeiXinUserInfo { + + // 用户的标识 + private String wxOpenid; + // 关注状态(1是关注,0是未关注),未关注时获取不到其余信息 + private int subscribe; + // 用户关注时间,为时间戳。如果用户曾多次关注,则取最后关注时间 + private String subscribeTime; + // 昵称 + private String nickName; + // 用户的性别(1是男性,2是女性,0是未知) + private int sex; + // 用户所在国家 + private String country; + // 用户所在省份 + private String province; + // 用户所在城市 + private String city; + // 用户的语言,简体中文为zh_CN + private String language; + // 用户头像 + private String headImgUrl; + + //用户统一标识。针对一个微信开放平台帐号下的应用,同一用户的unionid是唯一的 + private String unionId; + + //错误码 + private int errcode; + //错误信息 + private String errmsg; + + public String getWxOpenid() { + return wxOpenid; + } + + public void setWxOpenid(String wxOpenid) { + this.wxOpenid = wxOpenid; + } + + public int getSubscribe() { + return subscribe; + } + + public void setSubscribe(int subscribe) { + this.subscribe = subscribe; + } + + public String getSubscribeTime() { + return subscribeTime; + } + + public void setSubscribeTime(String subscribeTime) { + this.subscribeTime = subscribeTime; + } + + public String getNickName() { + return nickName; + } + + public void setNickName(String nickName) { + this.nickName = nickName; + } + + public int getSex() { + return sex; + } + + public void setSex(int sex) { + this.sex = sex; + } + + public String getCountry() { + return country; + } + + public void setCountry(String country) { + this.country = country; + } + + public String getProvince() { + return province; + } + + public void setProvince(String province) { + this.province = province; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getLanguage() { + return language; + } + + public void setLanguage(String language) { + this.language = language; + } + + public String getHeadImgUrl() { + return headImgUrl; + } + + public void setHeadImgUrl(String headImgUrl) { + this.headImgUrl = headImgUrl; + } + + public String getUnionId() { + return unionId; + } + + public void setUnionId(String unionId) { + this.unionId = unionId; + } + + public int getErrcode() { + return errcode; + } + + public void setErrcode(int errcode) { + this.errcode = errcode; + } + + public String getErrmsg() { + return errmsg; + } + + public void setErrmsg(String errmsg) { + this.errmsg = errmsg; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("WeiXinUserInfo{"); + sb.append("wxOpenid='").append(wxOpenid).append('\''); + sb.append(", subscribe=").append(subscribe); + sb.append(", subscribeTime='").append(subscribeTime).append('\''); + sb.append(", nickName='").append(nickName).append('\''); + sb.append(", sex=").append(sex); + sb.append(", country='").append(country).append('\''); + sb.append(", province='").append(province).append('\''); + sb.append(", city='").append(city).append('\''); + sb.append(", language='").append(language).append('\''); + sb.append(", headImgUrl='").append(headImgUrl).append('\''); + sb.append(", unionId='").append(unionId).append('\''); + sb.append(", errcode=").append(errcode); + sb.append(", errmsg='").append(errmsg).append('\''); + sb.append('}'); + return sb.toString(); + } +} diff --git a/muster-admin/src/main/java/com/muster/web/controller/muster/weixin/WxJsapiSignature.java b/muster-admin/src/main/java/com/muster/web/controller/muster/weixin/WxJsapiSignature.java new file mode 100755 index 000000000..0937f77b8 --- /dev/null +++ b/muster-admin/src/main/java/com/muster/web/controller/muster/weixin/WxJsapiSignature.java @@ -0,0 +1,86 @@ +package com.muster.web.controller.muster.weixin; + + +import java.io.Serializable; + +/** + * @ClassName WxJsapiSignature + * @Description TOOD + * @Author guoconglin + * @DATE 2020/4/10 13:14 + * @Version 1.0 + **/ + +public class WxJsapiSignature implements Serializable { + + private static final long serialVersionUID = -1116808193154384804L; + + /** + * 公众号的唯一标识 + */ + private String appId; + + /** + * 生成签名的随机串 + */ + private String nonceStr; + + /** + * 生成签名的时间戳 + */ + private long timestamp; + + /** + * 地址 + */ + private String url; + + /** + * 签名 + */ + private String signature; + + public static long getSerialVersionUID() { + return serialVersionUID; + } + + public String getAppId() { + return appId; + } + + public void setAppId(String appId) { + this.appId = appId; + } + + public String getNonceStr() { + return nonceStr; + } + + public void setNonceStr(String nonceStr) { + this.nonceStr = nonceStr; + } + + public long getTimestamp() { + return timestamp; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getSignature() { + return signature; + } + + public void setSignature(String signature) { + this.signature = signature; + } +} diff --git a/muster-admin/src/main/java/com/muster/web/controller/system/SysProfileController.java b/muster-admin/src/main/java/com/muster/web/controller/system/SysProfileController.java index d6e96a5b0..bef794a6a 100644 --- a/muster-admin/src/main/java/com/muster/web/controller/system/SysProfileController.java +++ b/muster-admin/src/main/java/com/muster/web/controller/system/SysProfileController.java @@ -11,7 +11,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import com.muster.common.annotation.Log; -import com.muster.common.config.RuoYiConfig; +import com.muster.common.config.MusterConfig; import com.muster.common.core.controller.BaseController; import com.muster.common.core.domain.AjaxResult; import com.muster.common.core.domain.entity.SysUser; @@ -111,7 +111,7 @@ public class SysProfileController extends BaseController if (!file.isEmpty()) { LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); - String avatar = FileUploadUtils.upload(RuoYiConfig.getAvatarPath(), file); + String avatar = FileUploadUtils.upload(MusterConfig.getAvatarPath(), file); if (userService.updateUserAvatar(loginUser.getUsername(), avatar)) { AjaxResult ajax = AjaxResult.success(); diff --git a/muster-admin/src/main/java/com/muster/web/core/config/SwaggerConfig.java b/muster-admin/src/main/java/com/muster/web/core/config/SwaggerConfig.java index 0ab3599f1..d5a734196 100644 --- a/muster-admin/src/main/java/com/muster/web/core/config/SwaggerConfig.java +++ b/muster-admin/src/main/java/com/muster/web/core/config/SwaggerConfig.java @@ -6,7 +6,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import com.muster.common.config.RuoYiConfig; +import com.muster.common.config.MusterConfig; import io.swagger.annotations.ApiOperation; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; @@ -32,7 +32,7 @@ public class SwaggerConfig { /** 系统基础配置 */ @Autowired - private RuoYiConfig ruoyiConfig; + private MusterConfig ruoyiConfig; /** 是否开启swagger */ @Value("${swagger.enabled}") diff --git a/muster-admin/src/main/resources/application-dev.yml b/muster-admin/src/main/resources/application-dev.yml new file mode 100644 index 000000000..195a6a1a3 --- /dev/null +++ b/muster-admin/src/main/resources/application-dev.yml @@ -0,0 +1,11 @@ +muster: + # 文件路径 示例( Windows配置D:/muster/uploadPath,Linux配置 /home/muster/uploadPath) + profile: /Users/liujiangtao/project/mishang/muster-vue + +# 开发环境配置 +server: + # 服务器的HTTP端口,默认为8080 + port: 8080 + servlet: + # 应用的访问路径 + context-path: / \ No newline at end of file diff --git a/muster-admin/src/main/resources/application-prod.yml b/muster-admin/src/main/resources/application-prod.yml new file mode 100644 index 000000000..92bdd86e1 --- /dev/null +++ b/muster-admin/src/main/resources/application-prod.yml @@ -0,0 +1,12 @@ +muster: + # 文件路径 示例( Windows配置D:/muster/uploadPath,Linux配置 /home/muster/uploadPath) + profile: /www/muster/upload + + +# 开发环境配置 +server: + # 服务器的HTTP端口,默认为8080 + port: 8081 + servlet: + # 应用的访问路径 + context-path: /prod-api diff --git a/muster-admin/src/main/resources/application.yml b/muster-admin/src/main/resources/application.yml index 92a6157a1..ddf8cfcea 100644 --- a/muster-admin/src/main/resources/application.yml +++ b/muster-admin/src/main/resources/application.yml @@ -5,11 +5,9 @@ muster: # 版本 version: 3.0.0 # 版权年份 - copyrightYear: 2019 + copyrightYear: 2020 # 实例演示开关 demoEnabled: true - # 文件路径 示例( Windows配置D:/muster/uploadPath,Linux配置 /home/muster/uploadPath) - profile: /Users/liujiangtao/project/mishang/muster-vue # 获取ip地址开关 addressEnabled: false # 验证码类型 math 数组计算 char 字符验证 @@ -17,11 +15,6 @@ muster: # 开发环境配置 server: - # 服务器的HTTP端口,默认为8080 - port: 8080 - servlet: - # 应用的访问路径 - context-path: / tomcat: # tomcat的URI编码 uri-encoding: UTF-8 @@ -107,7 +100,7 @@ swagger: # 是否开启swagger enabled: true # 请求前缀 - pathMapping: /dev-api + pathMapping: /prod-api # 防止XSS攻击 xss: @@ -117,3 +110,14 @@ xss: excludes: /system/notice/* # 匹配链接 urlPatterns: /system/*,/monitor/*,/tool/* + +#微信 +wx: + #微信开放平台 + open: + #开放平台appid + appid: wx08196c4219236ab5 + #开放平台appsecret + appsecret: 9a131c53016eb64e1f55161326307bbf + #回调地址 + callback_url: http://grow.xxzzu.com/system/wechat/callback diff --git a/muster-common/pom.xml b/muster-common/pom.xml index e933c59f1..1a420fcc5 100644 --- a/muster-common/pom.xml +++ b/muster-common/pom.xml @@ -119,6 +119,12 @@ javax.servlet-api + + + cn.hutool + hutool-all + + \ No newline at end of file diff --git a/muster-common/src/main/java/com/muster/common/config/RuoYiConfig.java b/muster-common/src/main/java/com/muster/common/config/MusterConfig.java similarity index 68% rename from muster-common/src/main/java/com/muster/common/config/RuoYiConfig.java rename to muster-common/src/main/java/com/muster/common/config/MusterConfig.java index 4e6107747..28ab6d442 100644 --- a/muster-common/src/main/java/com/muster/common/config/RuoYiConfig.java +++ b/muster-common/src/main/java/com/muster/common/config/MusterConfig.java @@ -10,7 +10,7 @@ import org.springframework.stereotype.Component; */ @Component @ConfigurationProperties(prefix = "muster") -public class RuoYiConfig +public class MusterConfig { /** 项目名称 */ private String name; @@ -30,6 +30,10 @@ public class RuoYiConfig /** 获取地址开关 */ private static boolean addressEnabled; + private static String appId; + + private static String bizId; + public String getName() { return name; @@ -77,7 +81,7 @@ public class RuoYiConfig public void setProfile(String profile) { - RuoYiConfig.profile = profile; + MusterConfig.profile = profile; } public static boolean isAddressEnabled() @@ -87,7 +91,7 @@ public class RuoYiConfig public void setAddressEnabled(boolean addressEnabled) { - RuoYiConfig.addressEnabled = addressEnabled; + MusterConfig.addressEnabled = addressEnabled; } /** @@ -113,4 +117,40 @@ public class RuoYiConfig { return getProfile() + "/upload"; } + + /** + * Gets appId. + * + * @return the appId + */ + public static String getAppId() { + return appId; + } + + /** + * Sets appId. + * + * @param appId the appId + */ + public void setAppId(final String appId) { + MusterConfig.appId = appId; + } + + /** + * Gets bizId. + * + * @return the bizId + */ + public static String getBizId() { + return bizId; + } + + /** + * Sets bizId. + * + * @param bizId the bizId + */ + public void setBizId(final String bizId) { + MusterConfig.bizId = bizId; + } } diff --git a/muster-common/src/main/java/com/muster/common/constant/WxConsts.java b/muster-common/src/main/java/com/muster/common/constant/WxConsts.java new file mode 100755 index 000000000..dbf2af2c4 --- /dev/null +++ b/muster-common/src/main/java/com/muster/common/constant/WxConsts.java @@ -0,0 +1,270 @@ +package com.muster.common.constant; + +import java.util.HashMap; +import java.util.Map; + +/** + * @ClassName WxConsts + * @Description TOOD 微信开发所使用到的常量类. + * @Author guoconglin + * @DATE 2020/4/10 14:01 + * @Version 1.0 + **/ + + +public class WxConsts { + + /** + * 微信推送过来的消息的类型,和发送给微信xml格式消息的消息类型. + */ + public static class XmlMsgType { + public static final String TEXT = "text"; + public static final String IMAGE = "image"; + public static final String VOICE = "voice"; + public static final String SHORTVIDEO = "shortvideo"; + public static final String VIDEO = "video"; + public static final String NEWS = "news"; + public static final String MUSIC = "music"; + public static final String LOCATION = "location"; + public static final String LINK = "link"; + public static final String EVENT = "event"; + public static final String DEVICE_TEXT = "device_text"; + public static final String DEVICE_EVENT = "device_event"; + public static final String DEVICE_STATUS = "device_status"; + public static final String HARDWARE = "hardware"; + public static final String TRANSFER_CUSTOMER_SERVICE = "transfer_customer_service"; + } + + /** + * 主动发送消息(即客服消息)的消息类型. + */ + public static class KefuMsgType { + /** + * 文本消息. + */ + public static final String TEXT = "text"; + /** + * 图片消息. + */ + public static final String IMAGE = "image"; + /** + * 语音消息. + */ + public static final String VOICE = "voice"; + + /** + * 图文链接. + */ + public static final String LINK = "link"; + /** + /** + * 视频消息. + */ + public static final String VIDEO = "video"; + /** + * 音乐消息. + */ + public static final String MUSIC = "music"; + /** + * 图文消息(点击跳转到外链). + */ + public static final String NEWS = "news"; + /** + * 图文消息(点击跳转到图文消息页面). + */ + public static final String MPNEWS = "mpnews"; + /** + * 发送文件(CP专用). + */ + public static final String FILE = "file"; + /** + * 文本卡片消息(CP专用). + */ + public static final String TEXTCARD = "textcard"; + /** + * 卡券消息. + */ + public static final String WXCARD = "wxcard"; + /** + * 转发到客服的消息. + */ + public static final String TRANSFER_CUSTOMER_SERVICE = "transfer_customer_service"; + } + + /** + * 表示是否是保密消息,0表示否,1表示是,默认0. + */ + public static class KefuMsgSafe { + public static final String NO = "0"; + public static final String YES = "1"; + } + + public static class Account { + public static final int TYPE = 2; + public static final String PASSWORD = "123456"; + + public static final String SHRIO_USERNAME = "admin"; + public static final String SHRIO_PASSWORD = "admin123"; + + } + + /** + * 群发消息的消息类型. + */ + public static class MassMsgType { + public static final String MPNEWS = "mpnews"; + public static final String TEXT = "text"; + public static final String VOICE = "voice"; + public static final String IMAGE = "image"; + public static final String MPVIDEO = "mpvideo"; + } + + /** + * 群发消息后微信端推送给服务器的反馈消息. + */ + public static class MassMsgStatus { + public static final String SEND_SUCCESS = "send success"; + public static final String SEND_FAIL = "send fail"; + public static final String ERR_10001 = "err(10001)"; + public static final String ERR_20001 = "err(20001)"; + public static final String ERR_20004 = "err(20004)"; + public static final String ERR_20002 = "err(20002)"; + public static final String ERR_20006 = "err(20006)"; + public static final String ERR_20008 = "err(20008)"; + public static final String ERR_20013 = "err(20013)"; + public static final String ERR_22000 = "err(22000)"; + public static final String ERR_21000 = "err(21000)"; + + /** + * 群发反馈消息代码所对应的文字描述. + */ + public static final Map STATUS_DESC = new HashMap<>(); + + static { + STATUS_DESC.put(SEND_SUCCESS, "发送成功"); + STATUS_DESC.put(SEND_FAIL, "发送失败"); + STATUS_DESC.put(ERR_10001, "涉嫌广告"); + STATUS_DESC.put(ERR_20001, "涉嫌政治"); + STATUS_DESC.put(ERR_20004, "涉嫌社会"); + STATUS_DESC.put(ERR_20002, "涉嫌色情"); + STATUS_DESC.put(ERR_20006, "涉嫌违法犯罪"); + STATUS_DESC.put(ERR_20008, "涉嫌欺诈"); + STATUS_DESC.put(ERR_20013, "涉嫌版权"); + STATUS_DESC.put(ERR_22000, "涉嫌互推_互相宣传"); + STATUS_DESC.put(ERR_21000, "涉嫌其他"); + } + } + + /** + * 微信端推送过来的事件类型. + */ + public static class EventType { + public static final String SUBSCRIBE = "subscribe"; + public static final String UNSUBSCRIBE = "unsubscribe"; + public static final String SCAN = "SCAN"; + public static final String LOCATION = "LOCATION"; + public static final String CLICK = "CLICK"; + public static final String VIEW = "VIEW"; + public static final String MASS_SEND_JOB_FINISH = "MASSSENDJOBFINISH"; + public static final String SCANCODE_PUSH = "scancode_push"; + public static final String SCANCODE_WAITMSG = "scancode_waitmsg"; + public static final String PIC_SYSPHOTO = "pic_sysphoto"; + public static final String PIC_PHOTO_OR_ALBUM = "pic_photo_or_album"; + public static final String PIC_WEIXIN = "pic_weixin"; + public static final String LOCATION_SELECT = "location_select"; + public static final String TEMPLATE_SEND_JOB_FINISH = "TEMPLATESENDJOBFINISH"; + public static final String ENTER_AGENT = "enter_agent"; + } + + /** + * 上传多媒体(临时素材)文件的类型. + */ + public static class MediaFileType { + public static final String IMAGE = "image"; + public static final String VOICE = "voice"; + public static final String VIDEO = "video"; + public static final String THUMB = "thumb"; + public static final String FILE = "file"; + } + + /** + * 自定义菜单的按钮类型. + */ + public static class MenuButtonType { + /** + * 点击推事件. + */ + public static final String CLICK = "click"; + /** + * 跳转URL. + */ + public static final String VIEW = "view"; + /** + * 跳转到小程序. + */ + public static final String MINIPROGRAM = "miniprogram"; + /** + * 扫码推事件. + */ + public static final String SCANCODE_PUSH = "scancode_push"; + /** + * 扫码推事件且弹出“消息接收中”提示框. + */ + public static final String SCANCODE_WAITMSG = "scancode_waitmsg"; + /** + * 弹出系统拍照发图. + */ + public static final String PIC_SYSPHOTO = "pic_sysphoto"; + /** + * 弹出拍照或者相册发图. + */ + public static final String PIC_PHOTO_OR_ALBUM = "pic_photo_or_album"; + /** + * 弹出微信相册发图器. + */ + public static final String PIC_WEIXIN = "pic_weixin"; + /** + * 弹出地理位置选择器. + */ + public static final String LOCATION_SELECT = "location_select"; + /** + * 下发消息(除文本消息). + */ + public static final String MEDIA_ID = "media_id"; + /** + * 跳转图文消息URL. + */ + public static final String VIEW_LIMITED = "view_limited"; + } + + /** + * oauth2网页授权的scope. + */ + public static class OAuth2Scope { + /** + * 不弹出授权页面,直接跳转,只能获取用户openid. + */ + public static final String SNSAPI_BASE = "snsapi_base"; + /** + * 弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息. + */ + public static final String SNSAPI_USERINFO = "snsapi_userinfo"; + } + + /** + * 网页应用登录授权作用域. + */ + public static class QrConnectScope { + public static final String SNSAPI_LOGIN = "snsapi_login"; + } + + /** + * 永久素材类型. + */ + public static class MaterialType { + public static final String NEWS = "news"; + public static final String VOICE = "voice"; + public static final String IMAGE = "image"; + public static final String VIDEO = "video"; + } +} diff --git a/muster-common/src/main/java/com/muster/common/utils/file/FileUploadUtils.java b/muster-common/src/main/java/com/muster/common/utils/file/FileUploadUtils.java index 107de7b3e..5841a1436 100644 --- a/muster-common/src/main/java/com/muster/common/utils/file/FileUploadUtils.java +++ b/muster-common/src/main/java/com/muster/common/utils/file/FileUploadUtils.java @@ -4,7 +4,7 @@ import java.io.File; import java.io.IOException; import org.apache.commons.io.FilenameUtils; import org.springframework.web.multipart.MultipartFile; -import com.muster.common.config.RuoYiConfig; +import com.muster.common.config.MusterConfig; import com.muster.common.constant.Constants; import com.muster.common.exception.file.FileNameLengthLimitExceededException; import com.muster.common.exception.file.FileSizeLimitExceededException; @@ -33,7 +33,7 @@ public class FileUploadUtils /** * 默认上传的地址 */ - private static String defaultBaseDir = RuoYiConfig.getProfile(); + private static String defaultBaseDir = MusterConfig.getProfile(); public static void setDefaultBaseDir(String defaultBaseDir) { @@ -144,7 +144,7 @@ public class FileUploadUtils private static final String getPathFileName(String uploadDir, String fileName) throws IOException { - int dirLastIndex = RuoYiConfig.getProfile().length() + 1; + int dirLastIndex = MusterConfig.getProfile().length() + 1; String currentDir = StringUtils.substring(uploadDir, dirLastIndex); String pathFileName = Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName; return pathFileName; diff --git a/muster-common/src/main/java/com/muster/common/utils/ip/AddressUtils.java b/muster-common/src/main/java/com/muster/common/utils/ip/AddressUtils.java index 436942b38..a00fff458 100644 --- a/muster-common/src/main/java/com/muster/common/utils/ip/AddressUtils.java +++ b/muster-common/src/main/java/com/muster/common/utils/ip/AddressUtils.java @@ -3,7 +3,7 @@ package com.muster.common.utils.ip; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.alibaba.fastjson.JSONObject; -import com.muster.common.config.RuoYiConfig; +import com.muster.common.config.MusterConfig; import com.muster.common.constant.Constants; import com.muster.common.utils.StringUtils; import com.muster.common.utils.http.HttpUtils; @@ -31,7 +31,7 @@ public class AddressUtils { return "内网IP"; } - if (RuoYiConfig.isAddressEnabled()) + if (MusterConfig.isAddressEnabled()) { try { diff --git a/muster-common/src/main/java/com/muster/common/utils/poi/ExcelUtil.java b/muster-common/src/main/java/com/muster/common/utils/poi/ExcelUtil.java index beb2d6815..aff7b6244 100644 --- a/muster-common/src/main/java/com/muster/common/utils/poi/ExcelUtil.java +++ b/muster-common/src/main/java/com/muster/common/utils/poi/ExcelUtil.java @@ -46,7 +46,7 @@ import com.muster.common.annotation.Excel; import com.muster.common.annotation.Excel.ColumnType; import com.muster.common.annotation.Excel.Type; import com.muster.common.annotation.Excels; -import com.muster.common.config.RuoYiConfig; +import com.muster.common.config.MusterConfig; import com.muster.common.core.domain.AjaxResult; import com.muster.common.core.text.Convert; import com.muster.common.exception.CustomException; @@ -739,7 +739,7 @@ public class ExcelUtil */ public String getAbsoluteFile(String filename) { - String downloadPath = RuoYiConfig.getDownloadPath() + filename; + String downloadPath = MusterConfig.getDownloadPath() + filename; File desc = new File(downloadPath); if (!desc.getParentFile().exists()) { diff --git a/muster-framework/src/main/java/com/muster/framework/config/ResourcesConfig.java b/muster-framework/src/main/java/com/muster/framework/config/ResourcesConfig.java index 5242a42e0..677a92fd5 100644 --- a/muster-framework/src/main/java/com/muster/framework/config/ResourcesConfig.java +++ b/muster-framework/src/main/java/com/muster/framework/config/ResourcesConfig.java @@ -9,7 +9,7 @@ import org.springframework.web.filter.CorsFilter; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -import com.muster.common.config.RuoYiConfig; +import com.muster.common.config.MusterConfig; import com.muster.common.constant.Constants; import com.muster.framework.interceptor.RepeatSubmitInterceptor; @@ -28,7 +28,7 @@ public class ResourcesConfig implements WebMvcConfigurer public void addResourceHandlers(ResourceHandlerRegistry registry) { /** 本地文件上传路径 */ - registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**").addResourceLocations("file:" + RuoYiConfig.getProfile() + "/"); + registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**").addResourceLocations("file:" + MusterConfig.getProfile() + "/"); /** swagger配置 */ registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/"); diff --git a/muster-framework/src/main/java/com/muster/framework/web/service/SysLoginService.java b/muster-framework/src/main/java/com/muster/framework/web/service/SysLoginService.java index f4045282e..9533bf2ed 100644 --- a/muster-framework/src/main/java/com/muster/framework/web/service/SysLoginService.java +++ b/muster-framework/src/main/java/com/muster/framework/web/service/SysLoginService.java @@ -1,12 +1,5 @@ package com.muster.framework.web.service; -import javax.annotation.Resource; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.BadCredentialsException; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.stereotype.Component; import com.muster.common.constant.Constants; import com.muster.common.core.domain.model.LoginUser; import com.muster.common.core.redis.RedisCache; @@ -17,6 +10,14 @@ import com.muster.common.exception.user.UserPasswordNotMatchException; import com.muster.common.utils.MessageUtils; import com.muster.framework.manager.AsyncManager; import com.muster.framework.manager.factory.AsyncFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; /** * 登录校验方法 @@ -59,13 +60,26 @@ public class SysLoginService AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"))); throw new CaptchaException(); } + return this.login(username, password); + } + + /** + * 内部登录验证 + * + * @param username 用户名 + * @param password 密码 + * @return 结果 + */ + public String login(String username, String password) + { + // 用户验证 Authentication authentication = null; try { // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername authentication = authenticationManager - .authenticate(new UsernamePasswordAuthenticationToken(username, password)); + .authenticate(new UsernamePasswordAuthenticationToken(username, password)); } catch (Exception e) { diff --git a/muster-generator/src/main/java/com/muster/generator/util/GenUtils.java b/muster-generator/src/main/java/com/muster/generator/util/GenUtils.java index 92ff0ed56..55d94a283 100644 --- a/muster-generator/src/main/java/com/muster/generator/util/GenUtils.java +++ b/muster-generator/src/main/java/com/muster/generator/util/GenUtils.java @@ -200,7 +200,7 @@ public class GenUtils */ public static String replaceText(String text) { - return RegExUtils.replaceAll(text, "(?:表|若依)", ""); + return RegExUtils.replaceAll(text, "(?:表|muster)", ""); } /** diff --git a/muster-logic/pom.xml b/muster-logic/pom.xml new file mode 100644 index 000000000..22063bb9f --- /dev/null +++ b/muster-logic/pom.xml @@ -0,0 +1,23 @@ + + + + muster + com.muster + 3.0.0 + + 4.0.0 + + muster-logic + 3.0.0 + + + + + com.muster + muster-framework + + + + \ No newline at end of file diff --git a/muster-logic/src/main/java/com/muster/logic/BO/DaoContext.java b/muster-logic/src/main/java/com/muster/logic/BO/DaoContext.java new file mode 100644 index 000000000..f0cd679a4 --- /dev/null +++ b/muster-logic/src/main/java/com/muster/logic/BO/DaoContext.java @@ -0,0 +1,150 @@ +package com.muster.logic.BO; + +/** + * Description + *

+ *

+ * DATE 2020-07-04. + * + * @author 刘江涛. + */ +public class DaoContext { + + public DaoContext() { + } + + public DaoContext(final String appId, final String bizId, final String uhId, final int userId, final int token) { + this.appId = appId; + this.bizId = bizId; + this.uhId = uhId; + this.userId = userId; + this.token = token; + } + + /** + * 系统级设定appid + */ + private String appId = "10001"; + + /** + * 系统级设定bizid 原则上 fx_bizid>0 时,将无权注册企业 + */ + private String bizId = "0"; + + /** + * 用户操作的终端硬件参数 + */ + private String uhId = "test"; + + /** + * 用户id + */ + private int userId = 1; + + /** + * 用户token + */ + private int token = 1; + + /** + * Gets appId. + * + * @return the appId + */ + public String getAppId() { + return appId; + } + + /** + * Sets appId. + * + * @param appId the appId + */ + public void setAppId(final String appId) { + this.appId = appId; + } + + /** + * Gets bizId. + * + * @return the bizId + */ + public String getBizId() { + return bizId; + } + + /** + * Sets bizId. + * + * @param bizId the bizId + */ + public void setBizId(final String bizId) { + this.bizId = bizId; + } + + /** + * Gets uhId. + * + * @return the uhId + */ + public String getUhId() { + return uhId; + } + + /** + * Sets uhId. + * + * @param uhId the uhId + */ + public void setUhId(final String uhId) { + this.uhId = uhId; + } + + /** + * Gets userId. + * + * @return the userId + */ + public int getUserId() { + return userId; + } + + /** + * Sets userId. + * + * @param userId the userId + */ + public void setUserId(final int userId) { + this.userId = userId; + } + + /** + * Gets token. + * + * @return the token + */ + public int getToken() { + return token; + } + + /** + * Sets token. + * + * @param token the token + */ + public void setToken(final int token) { + this.token = token; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("DaoContext{"); + sb.append("appId='").append(appId).append('\''); + sb.append(", bizId='").append(bizId).append('\''); + sb.append(", uhId='").append(uhId).append('\''); + sb.append(", userId=").append(userId); + sb.append(", token=").append(token); + sb.append('}'); + return sb.toString(); + } +} diff --git a/muster-logic/src/main/java/com/muster/logic/BO/package-info.java b/muster-logic/src/main/java/com/muster/logic/BO/package-info.java new file mode 100644 index 000000000..2f0aecfcc --- /dev/null +++ b/muster-logic/src/main/java/com/muster/logic/BO/package-info.java @@ -0,0 +1,9 @@ +/** + * Description + *

+ *

+ * DATE 2020-07-04. + * + * @author 刘江涛. + */ +package com.muster.logic.BO; diff --git a/muster-logic/src/main/java/com/muster/logic/DaoContextUtils.java b/muster-logic/src/main/java/com/muster/logic/DaoContextUtils.java new file mode 100644 index 000000000..1833f6153 --- /dev/null +++ b/muster-logic/src/main/java/com/muster/logic/DaoContextUtils.java @@ -0,0 +1,39 @@ +package com.muster.logic; + + +import com.muster.common.config.MusterConfig; +import com.muster.logic.BO.DaoContext; +import org.slf4j.MDC; + +import java.util.Map; + +/** + * Description + *

+ *

+ * DATE 2020-07-05. + * + * @author 刘江涛. + */ +public class DaoContextUtils { + + public static DaoContext createContext() { + String hid = MDC.get("hid"); + int userId = Integer.valueOf(MDC.get("userId")); + int token = Integer.valueOf(MDC.get("token")); + String appId = MusterConfig.getAppId(); + String bizId = MusterConfig.getBizId(); + return new DaoContext(appId, bizId, hid, userId, token); + } + + public static DaoContext createContext(final Map params) { + String hid = MDC.get("hid"); + int userId = Integer.valueOf(MDC.get("userId")); + int token = Integer.valueOf(MDC.get("token")); + String appId = String.valueOf(params.getOrDefault("appId", MusterConfig.getAppId())); + String bizId = String.valueOf(params.getOrDefault("bizId", MusterConfig.getBizId())); + params.remove("appId"); + params.remove("bizId"); + return new DaoContext(appId, bizId, hid, userId, token); + } +} diff --git a/muster-logic/src/main/java/com/muster/logic/DbLogicService.java b/muster-logic/src/main/java/com/muster/logic/DbLogicService.java new file mode 100644 index 000000000..a571f4cd8 --- /dev/null +++ b/muster-logic/src/main/java/com/muster/logic/DbLogicService.java @@ -0,0 +1,23 @@ +package com.muster.logic; + +import com.muster.logic.model.ProcedureResult; + +import java.util.Map; + +/** + * Description + *

+ *

+ * DATE 2020-07-04. + * + * @author 刘江涛. + */ +public interface DbLogicService { + + /** + * @param procedureName 存储过程名称,从 ProcedureNameConstants 获取 + * @param params 存储过程参数 + * @return 返回结果集 + */ + ProcedureResult exec(final String procedureName, final Map params); +} diff --git a/muster-logic/src/main/java/com/muster/logic/DbLogicServiceImpl.java b/muster-logic/src/main/java/com/muster/logic/DbLogicServiceImpl.java new file mode 100644 index 000000000..4c08138f1 --- /dev/null +++ b/muster-logic/src/main/java/com/muster/logic/DbLogicServiceImpl.java @@ -0,0 +1,163 @@ +package com.muster.logic; + +import com.muster.logic.BO.DaoContext; +import com.muster.logic.model.ProcedureResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Service; + +import javax.sql.DataSource; +import java.sql.CallableStatement; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Types; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * Description + *

+ *

+ * DATE 2020-07-04. + * + * @author 刘江涛. + */ +@Service +public class DbLogicServiceImpl implements DbLogicService { + + Logger log = LoggerFactory.getLogger(DbLogicServiceImpl.class); + + private DataSource dataSource; + + private final static int OUPUT_INDEX = 6; + + @Autowired + DbLogicServiceImpl(@Qualifier("masterDataSource") DataSource dataSource) { + this.dataSource = dataSource; + } + + @Override + public ProcedureResult exec(final String procedureName, final Map params) { + ProcedureResult result = new ProcedureResult(); + DaoContext context = DaoContextUtils.createContext(params); + String sql = this.createProcedure(procedureName, params.size()); + + try (Connection conn = dataSource.getConnection(); + CallableStatement cs = conn.prepareCall(sql)) { + + // 设置上下文参数 + this.setContext(cs, context); + // 设置业务参数 + this.setParams(cs, params); + + cs.registerOutParameter(6, Types.INTEGER); + cs.execute(); + + int res = cs.getInt(OUPUT_INDEX); + result.setRes(res); + if (log.isDebugEnabled()) { + log.debug("{}, {}, {}, {}", procedureName, context.toString(), params, res); + } + List> list = this.getResult(cs); + result.setResult(list); + + } catch (SQLException e) { + log.error("{}, {}, {}", procedureName, context.toString(), params, e); + } + return result; + } + + private List> getResult(final CallableStatement cs) { + List> list = new ArrayList<>(); + try (ResultSet rs = cs.getResultSet();) { + if (Objects.isNull(rs)) { + return list; + } + ResultSetMetaData rsmd = rs.getMetaData(); + int columnCount = rsmd.getColumnCount(); + while (rs.next()) { + Map map = new HashMap<>(); + for (int i = 1; i <= columnCount; i++) { + String name = rsmd.getColumnName(i); + String type = rsmd.getColumnClassName(i); + Object value = null; + if ("java.lang.String".equals(type)) { + value = rs.getString(i); + } else if ("java.lang.Integer".equals(type)) { + value = rs.getInt(i); + } else if ("java.sql.Date".equals(type)) { + value = rs.getDate(i); + } else if ("java.math.BigDecimal".equals(type)) { + value = rs.getBigDecimal(i); + } else if ("java.lang.Boolean".equals(type)) { + value = rs.getBoolean(i); + } else if ("java.lang.Long".equals(type)) { + value = rs.getLong(i); + } else if ("java.sql.Timestamp".equals(type)) { + value = rs.getTimestamp(i); + } + map.put(name, value); + } + + list.add(map); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + private void setParams(final CallableStatement cs, final Map params) { + int index = 7; + List keys = params.keySet().stream().sorted().collect(Collectors.toList()); + for (int i = 0; i < keys.size(); i++) { + setParam(cs, params, keys.get(i), index); + index++; + } + } + + private void setParam(final CallableStatement cs, final Map params, final String key, + final int index) { + Object v = params.get(key); + + try { + if (v instanceof String) { + cs.setString(index, v.toString()); + } else if (v instanceof Integer) { + cs.setInt(index, Integer.valueOf(v.toString())); + } else if (v instanceof Double) { + cs.setDouble(index, Double.valueOf(v.toString())); + } + } catch (SQLException e) { + + } + + } + + private void setContext(final CallableStatement cs, final DaoContext context) throws SQLException { + cs.setString(1, context.getAppId()); + cs.setString(2, context.getBizId()); + cs.setString(3, context.getUhId()); + cs.setInt(4, context.getUserId()); + cs.setInt(5, context.getToken()); + } + + private String createProcedure(final String name, final int size) { + StringBuilder str = new StringBuilder(); + for (int i = 0; i < size; i++) { + str.append(" ,?"); + } + + return String.format(procedureTemplate, name, str.toString()); + } + + private final static String procedureTemplate = "call %s(?, ?, ?, ?, ?, ?%s)"; +} diff --git a/muster-logic/src/main/java/com/muster/logic/model/ProcedureResult.java b/muster-logic/src/main/java/com/muster/logic/model/ProcedureResult.java new file mode 100644 index 000000000..f20089559 --- /dev/null +++ b/muster-logic/src/main/java/com/muster/logic/model/ProcedureResult.java @@ -0,0 +1,64 @@ +package com.muster.logic.model; + +import java.util.List; +import java.util.Map; + +/** + * Description + *

+ *

+ * DATE 2020-07-04. + * + * @author 刘江涛. + */ +public class ProcedureResult { + + private int res; + + private List> result; + + /** + * Gets res. + * + * @return the res + */ + public int getRes() { + return res; + } + + /** + * Sets res. + * + * @param res the res + */ + public void setRes(final int res) { + this.res = res; + } + + /** + * Gets result. + * + * @return the result + */ + public List> getResult() { + return result; + } + + /** + * Sets result. + * + * @param result the result + */ + public void setResult(final List> result) { + this.result = result; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("ProcedureResult{"); + sb.append("res=").append(res); + sb.append(", result=").append(result); + sb.append('}'); + return sb.toString(); + } +} diff --git a/muster-logic/src/main/java/com/muster/logic/model/package-info.java b/muster-logic/src/main/java/com/muster/logic/model/package-info.java new file mode 100644 index 000000000..7e36f1bdb --- /dev/null +++ b/muster-logic/src/main/java/com/muster/logic/model/package-info.java @@ -0,0 +1,9 @@ +/** + * Description + *

+ *

+ * DATE 2020-07-04. + * + * @author 刘江涛. + */ +package com.muster.logic.model; diff --git a/muster-logic/src/main/java/com/muster/logic/package-info.java b/muster-logic/src/main/java/com/muster/logic/package-info.java new file mode 100644 index 000000000..8a42e682c --- /dev/null +++ b/muster-logic/src/main/java/com/muster/logic/package-info.java @@ -0,0 +1,9 @@ +/** + * Description + *

+ *

+ * DATE 2020-07-04. + * + * @author 刘江涛. + */ +package com.muster.logic; diff --git a/muster-ui/package.json b/muster-ui/package.json index fcff52e5e..036b25b77 100644 --- a/muster-ui/package.json +++ b/muster-ui/package.json @@ -1,8 +1,8 @@ { "name": "muster", "version": "3.0.0", - "description": "若依管理系统", - "author": "若依", + "description": "Muster管理系统", + "author": "MUster", "license": "MIT", "scripts": { "dev": "vue-cli-service serve --open", diff --git a/muster-ui/src/assets/styles/ruoyi.scss b/muster-ui/src/assets/styles/muster.scss similarity index 100% rename from muster-ui/src/assets/styles/ruoyi.scss rename to muster-ui/src/assets/styles/muster.scss diff --git a/muster-ui/src/settings.js b/muster-ui/src/settings.js index ca6d00c9e..658107b14 100644 --- a/muster-ui/src/settings.js +++ b/muster-ui/src/settings.js @@ -1,31 +1,31 @@ -module.exports = { - title: '若依管理系统', - - /** - * 是否系统布局配置 - */ - showSettings: false, - - /** - * 是否显示 tagsView - */ - tagsView: true, - - /** - * 是否固定头部 - */ - fixedHeader: false, - - /** - * 是否显示logo - */ - sidebarLogo: true, - - /** - * @type {string | array} 'production' | ['production', 'development'] - * @description Need show err logs component. - * The default is only used in the production env - * If you want to also use it in dev, you can pass ['production', 'development'] - */ - errorLog: 'production' -} +module.exports = { + title: 'Muster管理系统', + + /** + * 是否系统布局配置 + */ + showSettings: false, + + /** + * 是否显示 tagsView + */ + tagsView: true, + + /** + * 是否固定头部 + */ + fixedHeader: false, + + /** + * 是否显示logo + */ + sidebarLogo: true, + + /** + * @type {string | array} 'production' | ['production', 'development'] + * @description Need show err logs component. + * The default is only used in the production env + * If you want to also use it in dev, you can pass ['production', 'development'] + */ + errorLog: 'production' +} diff --git a/muster-ui/src/utils/ruoyi.js b/muster-ui/src/utils/muster.js similarity index 100% rename from muster-ui/src/utils/ruoyi.js rename to muster-ui/src/utils/muster.js diff --git a/muster.sh b/muster.sh new file mode 100755 index 000000000..01ffd06a9 --- /dev/null +++ b/muster.sh @@ -0,0 +1,86 @@ +#!/bin/bash + +AppName=muster-admin.jar + +#JVM参数 +JVM_OPTS="-Dname=$AppName -Duser.timezone=Asia/Shanghai -Xms512M -Xmx512M -XX:PermSize=256M -XX:MaxPermSize=512M -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:NewRatio=1 -XX:SurvivorRatio=30 -XX:+UseParallelGC -XX:+UseParallelOldGC" +APP_HOME=`pwd` +LOG_PATH=$APP_HOME/logs/$AppName.log + +if [ "$1" = "" ]; +then + echo -e "\033[0;31m 未输入操作名 \033[0m \033[0;34m {start|stop|restart|status} \033[0m" + exit 1 +fi + +if [ "$AppName" = "" ]; +then + echo -e "\033[0;31m 未输入应用名 \033[0m" + exit 1 +fi + +function start() +{ + PID=`ps -ef |grep java|grep $AppName|grep -v grep|awk '{print $2}'` + + if [ x"$PID" != x"" ]; then + echo "$AppName is running..." + else + nohup java -jar $JVM_OPTS target/$AppName > /dev/null 2>&1 & + echo "Start $AppName success..." + fi +} + +function stop() +{ + echo "Stop $AppName" + + PID="" + query(){ + PID=`ps -ef |grep java|grep $AppName|grep -v grep|awk '{print $2}'` + } + + query + if [ x"$PID" != x"" ]; then + kill -TERM $PID + echo "$AppName (pid:$PID) exiting..." + while [ x"$PID" != x"" ] + do + sleep 1 + query + done + echo "$AppName exited." + else + echo "$AppName already stopped." + fi +} + +function restart() +{ + stop + sleep 2 + start +} + +function status() +{ + PID=`ps -ef |grep java|grep $AppName|grep -v grep|wc -l` + if [ $PID != 0 ];then + echo "$AppName is running..." + else + echo "$AppName is not running..." + fi +} + +case $1 in + start) + start;; + stop) + stop;; + restart) + restart;; + status) + status;; + *) + +esac diff --git a/pom.xml b/pom.xml index 4777da02a..269412801 100644 --- a/pom.xml +++ b/pom.xml @@ -30,6 +30,8 @@ 3.17 1.7 0.9.0 + 5.2.1 + 2.8.5 @@ -181,6 +183,28 @@ ${muster.version} + + + + com.muster + muster-logic + ${muster.version} + + + + + + cn.hutool + hutool-all + ${hutool.version} + + + + com.google.code.gson + gson + ${gson.version} + + @@ -191,6 +215,7 @@ muster-quartz muster-generator muster-common + muster-logic pom diff --git a/sql/ry_20200723.sql b/sql/ry_20200723.sql index 42fdd72eb..c183779a6 100644 --- a/sql/ry_20200723.sql +++ b/sql/ry_20200723.sql @@ -23,16 +23,16 @@ create table sys_dept ( -- ---------------------------- -- 初始化-部门表数据 -- ---------------------------- -insert into sys_dept values(100, 0, '0', '若依科技', 0, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00'); -insert into sys_dept values(101, 100, '0,100', '深圳总公司', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00'); -insert into sys_dept values(102, 100, '0,100', '长沙分公司', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00'); -insert into sys_dept values(103, 101, '0,100,101', '研发部门', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00'); -insert into sys_dept values(104, 101, '0,100,101', '市场部门', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00'); -insert into sys_dept values(105, 101, '0,100,101', '测试部门', 3, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00'); -insert into sys_dept values(106, 101, '0,100,101', '财务部门', 4, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00'); -insert into sys_dept values(107, 101, '0,100,101', '运维部门', 5, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00'); -insert into sys_dept values(108, 102, '0,100,102', '市场部门', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00'); -insert into sys_dept values(109, 102, '0,100,102', '财务部门', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00'); +insert into sys_dept values(100, 0, '0', '若依科技', 0, 'muster', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00'); +insert into sys_dept values(101, 100, '0,100', '深圳总公司', 1, 'muster', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00'); +insert into sys_dept values(102, 100, '0,100', '长沙分公司', 2, 'muster', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00'); +insert into sys_dept values(103, 101, '0,100,101', '研发部门', 1, 'muster', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00'); +insert into sys_dept values(104, 101, '0,100,101', '市场部门', 2, 'muster', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00'); +insert into sys_dept values(105, 101, '0,100,101', '测试部门', 3, 'muster', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00'); +insert into sys_dept values(106, 101, '0,100,101', '财务部门', 4, 'muster', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00'); +insert into sys_dept values(107, 101, '0,100,101', '运维部门', 5, 'muster', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00'); +insert into sys_dept values(108, 102, '0,100,102', '市场部门', 1, 'muster', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00'); +insert into sys_dept values(109, 102, '0,100,102', '财务部门', 2, 'muster', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00'); -- ---------------------------- @@ -65,8 +65,8 @@ create table sys_user ( -- ---------------------------- -- 初始化-用户信息表数据 -- ---------------------------- -insert into sys_user values(1, 103, 'admin', '若依', '00', 'ry@163.com', '15888888888', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', '2018-03-16 11-33-00', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '管理员'); -insert into sys_user values(2, 105, 'ry', '若依', '00', 'ry@qq.com', '15666666666', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', '2018-03-16 11-33-00', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '测试员'); +insert into sys_user values(1, 103, 'admin', 'muster', '00', 'ry@163.com', '15888888888', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', '2018-03-16 11-33-00', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '管理员'); +insert into sys_user values(2, 105, 'ry', 'muster', '00', 'ry@qq.com', '15666666666', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', '2018-03-16 11-33-00', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '测试员'); -- ----------------------------