diff --git a/nflg-mobilebroken-auth/pom.xml b/nflg-mobilebroken-auth/pom.xml index 411caf5d..a7e416de 100644 --- a/nflg-mobilebroken-auth/pom.xml +++ b/nflg-mobilebroken-auth/pom.xml @@ -16,10 +16,6 @@ com.nflg nflg-mobilebroken-repository - - - - com.alibaba.cloud spring-cloud-starter-alibaba-nacos-config @@ -62,6 +58,11 @@ org.springframework.boot spring-boot-starter-validation + + com.github.binarywang + weixin-java-mp + 4.7.0 + diff --git a/nflg-mobilebroken-auth/src/main/java/com/nflg/mobilebroken/auth/config/WxMpConfig.java b/nflg-mobilebroken-auth/src/main/java/com/nflg/mobilebroken/auth/config/WxMpConfig.java new file mode 100644 index 00000000..bd91e8e0 --- /dev/null +++ b/nflg-mobilebroken-auth/src/main/java/com/nflg/mobilebroken/auth/config/WxMpConfig.java @@ -0,0 +1,24 @@ +package com.nflg.mobilebroken.auth.config; + +import com.nflg.mobilebroken.auth.weixin.CustomWxMpConfigStorage; +import com.nflg.mobilebroken.auth.weixin.ExternalTokenService; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.Resource; + +@Configuration +public class WxMpConfig { + + @Resource + private ExternalTokenService externalTokenService; + + @Bean + public WxMpService wxMpService() { + WxMpService service = new WxMpServiceImpl(); + service.setWxMpConfigStorage(new CustomWxMpConfigStorage(externalTokenService)); + return service; + } +} diff --git a/nflg-mobilebroken-auth/src/main/java/com/nflg/mobilebroken/auth/controller/WeiXinController.java b/nflg-mobilebroken-auth/src/main/java/com/nflg/mobilebroken/auth/controller/WeiXinController.java new file mode 100644 index 00000000..98a4b1e4 --- /dev/null +++ b/nflg-mobilebroken-auth/src/main/java/com/nflg/mobilebroken/auth/controller/WeiXinController.java @@ -0,0 +1,174 @@ +package com.nflg.mobilebroken.auth.controller; + +import cn.dev33.satoken.stp.SaLoginConfig; +import cn.dev33.satoken.stp.SaTokenInfo; +import cn.hutool.core.util.StrUtil; +import com.nflg.mobilebroken.common.constant.Constant; +import com.nflg.mobilebroken.common.constant.STATE; +import com.nflg.mobilebroken.common.exception.NflgException; +import com.nflg.mobilebroken.common.pojo.ApiResult; +import com.nflg.mobilebroken.common.pojo.request.UserBindWXRequest; +import com.nflg.mobilebroken.common.pojo.vo.AppLoginVO; +import com.nflg.mobilebroken.common.pojo.vo.RoleVO; +import com.nflg.mobilebroken.common.util.IdUtil; +import com.nflg.mobilebroken.common.util.SaTokenAdminUtil; +import com.nflg.mobilebroken.common.util.SaTokenAppUtil; +import com.nflg.mobilebroken.common.util.VUtils; +import com.nflg.mobilebroken.repository.entity.AdminUser; +import com.nflg.mobilebroken.repository.entity.AppUser; +import com.nflg.mobilebroken.repository.service.IAdminUserRoleMapService; +import com.nflg.mobilebroken.repository.service.IAdminUserService; +import com.nflg.mobilebroken.repository.service.IAppUserService; +import me.chanjar.weixin.common.api.WxConsts; +import me.chanjar.weixin.common.bean.oauth2.WxOAuth2AccessToken; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.api.WxMpService; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.validation.Valid; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * 微信 + */ +@RestController +@RequestMapping("/weixin") +public class WeiXinController extends ControllerBase { + + @Resource + private WxMpService wxMpService; + + @Resource + private IAppUserService appUserService; + + @Resource + private IAdminUserService adminUserService; + + @Resource + private IAdminUserRoleMapService adminUserRoleMapService; + + /** + * 获取微信授权地址 + * @param redirectUri 回调地址 + */ + @GetMapping("getAuthUrl") + public ApiResult getAuthUrl(@Valid @RequestParam @NotBlank String redirectUri) { + String url = wxMpService.getOAuth2Service().buildAuthorizationUrl( + redirectUri, + WxConsts.OAuth2Scope.SNSAPI_BASE, + IdUtil.getIdStr() + ); + return ApiResult.success(url); + } + + /** + * 获取登录token + * @param code 微信授权码 + * @param state state + */ + @GetMapping("getToken") + public ApiResult getToken(@Valid @RequestParam @NotBlank String code + , @Valid @RequestParam @NotBlank String state) throws WxErrorException { + WxOAuth2AccessToken token = wxMpService.getOAuth2Service().getAccessToken(code); + String openId = token.getOpenId(); + AppLoginVO vo = getAppUserToken(openId); + vo = Objects.nonNull(vo) ? vo : getAdminUserToken(openId); + if (Objects.isNull(vo)) { + return ApiResult.success(new AppLoginVO().setWxOpenId(openId)); + } else { + return ApiResult.success(vo); + } + } + + private AppLoginVO getAppUserToken(String openId) { + AppUser user = appUserService.getByWxOpenId(openId); + if (Objects.nonNull(user)) { + return buildAppLoginVO(user); + } + return null; + } + + private AppLoginVO buildAppLoginVO(AppUser user) { + SaTokenAppUtil.login(user.getId(), SaLoginConfig + .setExtra("from", Constant.FROM_APP) + .setExtra("name", user.getName()) + .setExtra("email", user.getEmail()) + .setExtra("companyIds", StrUtil.split(user.getCompanyId(), ",").stream().map(Integer::valueOf).collect(Collectors.toList())) + .setExtra("isPrimary", user.getIsPrimary())); + user.setLastLoginTime(LocalDateTime.now()); + appUserService.updateById(user); + SaTokenInfo tokenInfo = SaTokenAppUtil.getTokenInfo(); + return new AppLoginVO() + .setUserId(user.getId()) + .setToken(tokenInfo.getTokenValue()) + .setExpire(tokenInfo.getTokenTimeout()) + .setLanguageCode(user.getLanguageCode()) + .setPlatform(Constant.FROM_APP); + } + + private AppLoginVO getAdminUserToken(String openId) { + AdminUser adminUser = adminUserService.getByWxOpenId(openId); + if (Objects.nonNull(adminUser)) { + return buildAdminLoginVO(adminUser); + } + return null; + } + + private AppLoginVO buildAdminLoginVO(AdminUser adminUser) { + List roleCodes = adminUserRoleMapService.getRoleList(adminUser.getId()); + SaTokenAdminUtil.login(adminUser.getId(), SaLoginConfig + .setExtra("from", Constant.FROM_ADMIN) + .setExtra("name", adminUser.getUserName()) + .setExtra("code", adminUser.getUserCode()) + .setExtra("email", adminUser.getEmail()) + .setExtra("roles", roleCodes.stream().map(RoleVO::getCode).collect(Collectors.toList()))); + String adminToken = SaTokenAdminUtil.getTokenInfo().getTokenValue(); + SaTokenAppUtil.login(adminUser.getId(), SaLoginConfig + .setExtra("from", Constant.FROM_ADMIN) + .setExtra("name", adminUser.getUserName()) + .setExtra("email", adminUser.getEmail())); + SaTokenInfo tokenInfo = SaTokenAppUtil.getTokenInfo(); + return new AppLoginVO() + .setUserId(adminUser.getId()) + .setToken(tokenInfo.getTokenValue()) + .setAdminToken(adminToken) + .setExpire(tokenInfo.getTokenTimeout()) + .setLanguageCode(Constant.DEFAULT_LANGUAGE_CODE) + .setPlatform(Constant.FROM_ADMIN); + } + + /** + * 绑定用户 + * @param request 请求参数 + */ + @PostMapping("bindUser") + public ApiResult bindUser(@Valid @RequestBody @NotNull UserBindWXRequest request) { + AppUser appUser = appUserService.getByWxOpenId(request.getOpenId()); + AdminUser adminUser = adminUserService.getByWxOpenId(request.getOpenId()); + VUtils.trueThrowBusinessError(Objects.nonNull(appUser) || Objects.nonNull(adminUser)) + .throwMessage("请勿重复绑定"); + appUser = appUserService.getUser(request.getLoginName(), request.getPwd()); + if (Objects.nonNull(appUser)) { + VUtils.trueThrowBusinessError(StrUtil.isNotBlank(appUser.getWxOpenId())).throwMessage("该账号已绑定了微信"); + appUser.setWxOpenId(request.getOpenId()); + appUser.setUpdateTime(LocalDateTime.now()); + return ApiResult.success(buildAppLoginVO(appUser)); + } else { + adminUser = adminUserService.getUser(request.getLoginName(), request.getPwd()); + if (Objects.nonNull(adminUser)) { + VUtils.trueThrowBusinessError(StrUtil.isNotBlank(adminUser.getWxOpenId())).throwMessage("该账号已绑定了微信"); + adminUser.setWxOpenId(request.getOpenId()); + adminUser.setUpdateTime(LocalDateTime.now()); + adminUserService.updateById(adminUser); + return ApiResult.success(buildAdminLoginVO(adminUser)); + } + throw new NflgException(STATE.PassportErr, "用户名或密码错误"); + } + } +} diff --git a/nflg-mobilebroken-auth/src/main/java/com/nflg/mobilebroken/auth/weixin/CustomWxMpConfigStorage.java b/nflg-mobilebroken-auth/src/main/java/com/nflg/mobilebroken/auth/weixin/CustomWxMpConfigStorage.java new file mode 100644 index 00000000..1bccae6e --- /dev/null +++ b/nflg-mobilebroken-auth/src/main/java/com/nflg/mobilebroken/auth/weixin/CustomWxMpConfigStorage.java @@ -0,0 +1,27 @@ +package com.nflg.mobilebroken.auth.weixin; + +import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl; + +public class CustomWxMpConfigStorage extends WxMpDefaultConfigImpl { + + private final ExternalTokenService externalTokenService; + + public CustomWxMpConfigStorage(ExternalTokenService externalTokenService) { + this.externalTokenService = externalTokenService; + } + + @Override + public String getAccessToken() { + return externalTokenService.fetchGlobalAccessToken(); + } + + @Override + public String getAppId() { + return externalTokenService.getAppId(); + } + + @Override + public String getSecret() { + return externalTokenService.getAppSecret(); + } +} diff --git a/nflg-mobilebroken-auth/src/main/java/com/nflg/mobilebroken/auth/weixin/ExternalTokenService.java b/nflg-mobilebroken-auth/src/main/java/com/nflg/mobilebroken/auth/weixin/ExternalTokenService.java new file mode 100644 index 00000000..3d8395f7 --- /dev/null +++ b/nflg-mobilebroken-auth/src/main/java/com/nflg/mobilebroken/auth/weixin/ExternalTokenService.java @@ -0,0 +1,10 @@ +package com.nflg.mobilebroken.auth.weixin; + +public interface ExternalTokenService { + + String fetchGlobalAccessToken(); + + String getAppId(); + + String getAppSecret(); +} diff --git a/nflg-mobilebroken-auth/src/main/java/com/nflg/mobilebroken/auth/weixin/TokenCache.java b/nflg-mobilebroken-auth/src/main/java/com/nflg/mobilebroken/auth/weixin/TokenCache.java new file mode 100644 index 00000000..cef3e5aa --- /dev/null +++ b/nflg-mobilebroken-auth/src/main/java/com/nflg/mobilebroken/auth/weixin/TokenCache.java @@ -0,0 +1,18 @@ +package com.nflg.mobilebroken.auth.weixin; + +import cn.hutool.core.annotation.Alias; +import lombok.Data; +import lombok.experimental.Accessors; + +@Data +@Accessors(chain = true) +public class TokenCache { + + private String appId; + + @Alias("appSecre") + private String appSecret; + + @Alias("token") + private String accessToken; +} diff --git a/nflg-mobilebroken-auth/src/main/java/com/nflg/mobilebroken/auth/weixin/impl/ExternalTokenServiceImpl.java b/nflg-mobilebroken-auth/src/main/java/com/nflg/mobilebroken/auth/weixin/impl/ExternalTokenServiceImpl.java new file mode 100644 index 00000000..be75c1fc --- /dev/null +++ b/nflg-mobilebroken-auth/src/main/java/com/nflg/mobilebroken/auth/weixin/impl/ExternalTokenServiceImpl.java @@ -0,0 +1,56 @@ +package com.nflg.mobilebroken.auth.weixin.impl; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.nflg.mobilebroken.auth.weixin.ExternalTokenService; +import com.nflg.mobilebroken.auth.weixin.TokenCache; +import com.nflg.mobilebroken.common.constant.STATE; +import com.nflg.mobilebroken.common.exception.NflgException; +import com.nflg.mobilebroken.common.util.VUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +public class ExternalTokenServiceImpl implements ExternalTokenService { + + private TokenCache token; + + @Value("${wx.mp.accessTokenUrl}") + private String weixinAccessTokenUrl; + + @Override + public String fetchGlobalAccessToken() { + refreshToken(); + return token.getAccessToken(); + } + + @Override + public String getAppId() { + refreshToken(); + return token.getAppId(); + } + + @Override + public String getAppSecret() { + refreshToken(); + return token.getAppSecret(); + } + + private void refreshToken() { + try { + String content = HttpUtil.get(weixinAccessTokenUrl, 10000); + log.info("获取微信AccessToken结果:{}", content); + JSONObject oj = JSONUtil.parseObj(content); + VUtils.trueThrowBusinessError(!StrUtil.equals(oj.getStr("status"), "success")) + .throwMessage(oj.getStr("message")); + token = JSONUtil.toBean(oj.getStr("message"), TokenCache.class); + log.debug("Token数据:{}", JSONUtil.toJsonStr(token)); + } catch (Exception e) { + throw new NflgException(STATE.ServiceConnectRefused, "获取微信AccessToken失败:" + e.getMessage()); + } + } +} diff --git a/nflg-mobilebroken-cfs-app/src/main/java/com/nflg/mobilebroken/cfs/controller/TicketController.java b/nflg-mobilebroken-cfs-app/src/main/java/com/nflg/mobilebroken/cfs/controller/TicketController.java index 27d4c1d3..cb76052d 100644 --- a/nflg-mobilebroken-cfs-app/src/main/java/com/nflg/mobilebroken/cfs/controller/TicketController.java +++ b/nflg-mobilebroken-cfs-app/src/main/java/com/nflg/mobilebroken/cfs/controller/TicketController.java @@ -148,7 +148,7 @@ public class TicketController extends ControllerBase { * @param request 工单信息 **/ @PostMapping("/addTiket") - public ApiResult addTiket(@Valid @RequestBody TicketAddRequest request) { + public ApiResult addTicket(@Valid @RequestBody TicketAddRequest request) { Ticket ticket = ticketService.add(request, AppUserUtil.getUserId()); ticketChatService.add(new TicketChatDTO() .setTicketId(ticket.getId()) diff --git a/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/request/TicketAddRequest.java b/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/request/TicketAddRequest.java index b6506c68..b1b084b4 100644 --- a/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/request/TicketAddRequest.java +++ b/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/request/TicketAddRequest.java @@ -39,4 +39,9 @@ public class TicketAddRequest { //设备地址 @Size(max = 300, message = "设备地址长度不能超过300") private String deviceAddress; + + /** + * 来源 + */ + private String source; } \ No newline at end of file diff --git a/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/request/UserBindWXRequest.java b/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/request/UserBindWXRequest.java new file mode 100644 index 00000000..7b0f69df --- /dev/null +++ b/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/request/UserBindWXRequest.java @@ -0,0 +1,27 @@ +package com.nflg.mobilebroken.common.pojo.request; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +@Data +public class UserBindWXRequest { + + /** + * 微信openId + */ + @NotBlank + private String openId; + + /** + * 登录名 + */ + @NotBlank + private String loginName; + + /** + * 密码 + */ + @NotBlank + private String pwd; +} diff --git a/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/vo/AppLoginVO.java b/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/vo/AppLoginVO.java index 62b110a6..b2a63501 100644 --- a/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/vo/AppLoginVO.java +++ b/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/vo/AppLoginVO.java @@ -23,4 +23,9 @@ public class AppLoginVO { //平台,app或者admin private String platform; + + /** + * 微信openId + */ + private String wxOpenId; } diff --git a/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/entity/AdminUser.java b/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/entity/AdminUser.java index 606e2561..685453e4 100644 --- a/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/entity/AdminUser.java +++ b/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/entity/AdminUser.java @@ -108,4 +108,9 @@ public class AdminUser implements Serializable { * 删除状态,0:未删除,1:已删除 */ private Boolean isDel; + + /** + * 微信openid + */ + private String wxOpenId; } diff --git a/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/entity/AppUser.java b/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/entity/AppUser.java index 899408bc..7f1df5da 100644 --- a/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/entity/AppUser.java +++ b/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/entity/AppUser.java @@ -134,4 +134,9 @@ public class AppUser implements Serializable { * 删除状态,0:未删除,1:已删除 */ private Boolean isDel; + + /** + * 微信openid + */ + private String wxOpenId; } diff --git a/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/entity/Ticket.java b/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/entity/Ticket.java index eb47663f..9222e196 100644 --- a/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/entity/Ticket.java +++ b/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/entity/Ticket.java @@ -146,4 +146,9 @@ public class Ticket implements Serializable { * 最后更新时间 */ private LocalDateTime updateTime; + + /** + * 来源 + */ + private String source; } diff --git a/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/service/IAdminUserService.java b/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/service/IAdminUserService.java index c73930c2..9d66e9fa 100644 --- a/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/service/IAdminUserService.java +++ b/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/service/IAdminUserService.java @@ -54,4 +54,6 @@ public interface IAdminUserService extends IService { List getTickerMangagers(); List getCQMIds(); + + AdminUser getByWxOpenId(String openId); } diff --git a/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/service/IAppUserService.java b/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/service/IAppUserService.java index a29d9a01..13e63806 100644 --- a/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/service/IAppUserService.java +++ b/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/service/IAppUserService.java @@ -68,4 +68,6 @@ public interface IAppUserService extends IService { void authorizeRole(@Valid AuthorizeRoleRequest request); Integer getPrimaryByChild(Integer userId); + + AppUser getByWxOpenId(String openId); } diff --git a/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/service/impl/AdminUserServiceImpl.java b/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/service/impl/AdminUserServiceImpl.java index 805693db..49bd4263 100644 --- a/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/service/impl/AdminUserServiceImpl.java +++ b/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/service/impl/AdminUserServiceImpl.java @@ -358,6 +358,11 @@ public class AdminUserServiceImpl extends ServiceImpl impl return primaryUser.getId(); } + @Override + public AppUser getByWxOpenId(String openId) { + return lambdaQuery().eq(AppUser::getWxOpenId, openId).one(); + } + private void bindChildren1(AreaSimpleVO vo) { List datas = appAreaService.lambdaQuery().eq(AppArea::getParentId, vo.getId()).eq(AppArea::getEnable, true).list(); List vos = datas.stream().map(d -> new AreaSimpleVO().setId(d.getId()).setName(d.getName())).collect(Collectors.toList()); diff --git a/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/service/impl/TicketServiceImpl.java b/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/service/impl/TicketServiceImpl.java index ec3294e0..38d2e86a 100644 --- a/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/service/impl/TicketServiceImpl.java +++ b/nflg-mobilebroken-repository/src/main/java/com/nflg/mobilebroken/repository/service/impl/TicketServiceImpl.java @@ -81,6 +81,7 @@ public class TicketServiceImpl extends ServiceImpl impleme .setTitle(request.getTitle()) .setDescription(request.getDescription()) .setState(TicketState.PendingProcessing.getState()) + .setSource(request.getSource()) .setUserId(userId) .setUserPlatform(AppUserUtil.getFrom()) .setCreateTime(LocalDateTime.now());