guoshilong
2023-04-25 d737a229856becf58cbc7b61b34426caf9056553
Merge remote-tracking branch 'origin/jtdev' into jtdev
18 files modified
3 files added
1 files deleted
548 ■■■■ changed files
skjcmanager/skjcmanager-auth/src/main/java/cn/gistack/auth/config/MyAuthenticationProvider.java 120 ●●●●● patch | view | raw | blame | history
skjcmanager/skjcmanager-auth/src/main/java/cn/gistack/auth/config/SecurityConfiguration.java 16 ●●●●● patch | view | raw | blame | history
skjcmanager/skjcmanager-auth/src/main/java/cn/gistack/auth/granter/BladeTokenGranter.java 2 ●●●●● patch | view | raw | blame | history
skjcmanager/skjcmanager-auth/src/main/java/cn/gistack/auth/granter/PhoneTokenGranter.java 72 ●●●●● patch | view | raw | blame | history
skjcmanager/skjcmanager-auth/src/main/java/cn/gistack/auth/service/BladeUserDetailsServiceImpl.java 13 ●●●● patch | view | raw | blame | history
skjcmanager/skjcmanager-auth/src/main/java/cn/gistack/auth/support/BladePasswordEncoder.java 2 ●●●●● patch | view | raw | blame | history
skjcmanager/skjcmanager-auth/src/main/java/cn/gistack/auth/utils/TokenUtil.java 2 ●●●●● patch | view | raw | blame | history
skjcmanager/skjcmanager-common/src/main/java/cn/gistack/common/cache/CacheNames.java 5 ●●●●● patch | view | raw | blame | history
skjcmanager/skjcmanager-service-api/skjcmanager-user-api/src/main/java/cn/gistack/system/user/feign/IUserClient.java 24 ●●●●● patch | view | raw | blame | history
skjcmanager/skjcmanager-service/skjcmanager-sm/pom.xml 5 ●●●●● patch | view | raw | blame | history
skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/intelligentCall/controller/CallController.java 21 ●●●● patch | view | raw | blame | history
skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/intelligentCall/entity/User.java 15 ●●●●● patch | view | raw | blame | history
skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/intelligentCall/service/CallService.java 13 ●●●●● patch | view | raw | blame | history
skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/intelligentCall/service/impl/CallServiceImpl.java 14 ●●●●● patch | view | raw | blame | history
skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/sms/controller/SmsController.java 109 ●●●● patch | view | raw | blame | history
skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/sms/entity/SmsRequest.java 21 ●●●●● patch | view | raw | blame | history
skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/sms/util/SmsUtils.java 21 ●●●●● patch | view | raw | blame | history
skjcmanager/skjcmanager-service/skjcmanager-user/src/main/java/cn/gistack/system/user/feign/UserClient.java 14 ●●●●● patch | view | raw | blame | history
skjcmanager/skjcmanager-service/skjcmanager-user/src/main/java/cn/gistack/system/user/mapper/UserMapper.java 8 ●●●●● patch | view | raw | blame | history
skjcmanager/skjcmanager-service/skjcmanager-user/src/main/java/cn/gistack/system/user/mapper/UserMapper.xml 7 ●●●●● patch | view | raw | blame | history
skjcmanager/skjcmanager-service/skjcmanager-user/src/main/java/cn/gistack/system/user/service/IUserService.java 16 ●●●●● patch | view | raw | blame | history
skjcmanager/skjcmanager-service/skjcmanager-user/src/main/java/cn/gistack/system/user/service/impl/UserServiceImpl.java 28 ●●●●● patch | view | raw | blame | history
skjcmanager/skjcmanager-auth/src/main/java/cn/gistack/auth/config/MyAuthenticationProvider.java
New file
@@ -0,0 +1,120 @@
package cn.gistack.auth.config;
import cn.gistack.auth.utils.TokenUtil;
import cn.gistack.common.cache.CacheNames;
import com.alibaba.nacos.common.utils.StringUtils;
import lombok.SneakyThrows;
import org.springblade.core.redis.cache.BladeRedis;
import org.springblade.core.tool.utils.DigestUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.WebUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.oauth2.common.exceptions.UserDeniedAuthorizationException;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.time.Duration;
/**
 * 自定义安全认证
 * @author zhongrj
 * @time 2022-9-2
 */
@Component
public class MyAuthenticationProvider extends DaoAuthenticationProvider {
    /**
     * 免密常量
     */
    public static final String CUSTOM_LOGIN_SMS = "CUSTOM_LOGIN_SMS";
    @Autowired
    private BladeRedis bladeRedis;
    /**
     * 设置 userDetailsService
     * @param userDetailsService
     */
    public MyAuthenticationProvider(UserDetailsService userDetailsService) {
        super();
        setUserDetailsService(userDetailsService);
    }
    /**
     * 自定义处理密码校验逻辑
     * @param userDetails 用户信息
     * @param authentication 认证对象信息
     * @throws AuthenticationException 认证异常对象信息
     */
    @SneakyThrows
    @Override
    protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        if (authentication.getCredentials() == null) {
            this.logger.debug("Authentication failed: no credentials provided");
            throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
        } else {
            String password = authentication.getCredentials().toString();
            // 手机号登录时不验证密码
            if(!CUSTOM_LOGIN_SMS.equals(password)){
                HttpServletRequest request = WebUtil.getRequest();
                String grantType = request.getParameter(TokenUtil.GRANT_TYPE_KEY);
                // 获取租户ID
                String headerTenant = request.getHeader(TokenUtil.TENANT_HEADER_KEY);
                String paramTenant = request.getParameter(TokenUtil.TENANT_PARAM_KEY);
                // 指定租户ID
                String tenantId = StringUtils.isBlank(headerTenant) ? paramTenant : headerTenant;
                String detailsPassword = userDetails.getPassword().substring(7);
                if (!detailsPassword.equals(DigestUtil.hex(password))) {
                    int count = getFailCount(tenantId, userDetails.getUsername());
                    // 用户存在但密码错误,超过次数则锁定账号
                    if (grantType != null && !grantType.equals(TokenUtil.REFRESH_TOKEN_KEY)) {
                        setFailCount(tenantId, userDetails.getUsername(), count);
                        throw new UserDeniedAuthorizationException(TokenUtil.USER_NOT_FOUND);
                    }
                    this.logger.debug("Failed to authenticate since password does not match stored value");
                    throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
                }
                // 成功则清除登录错误次数
                delFailCount(tenantId, userDetails.getUsername());
            }
        }
    }
    /**
     * 获取账号错误次数
     *
     * @param tenantId 租户id
     * @param username 账号
     * @return int
     */
    private int getFailCount(String tenantId, String username) {
        return Func.toInt(bladeRedis.get(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username)), 0);
    }
    /**
     * 设置账号错误次数
     *
     * @param tenantId 租户id
     * @param username 账号
     * @param count    次数
     */
    private void setFailCount(String tenantId, String username, int count) {
        bladeRedis.setEx(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username), count + 1, Duration.ofMinutes(30));
    }
    /**
     * 清空账号错误次数
     *
     * @param tenantId 租户id
     * @param username 账号
     */
    private void delFailCount(String tenantId, String username) {
        bladeRedis.del(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username));
    }
}
skjcmanager/skjcmanager-auth/src/main/java/cn/gistack/auth/config/SecurityConfiguration.java
@@ -19,9 +19,11 @@
import cn.gistack.auth.support.BladePasswordEncoderFactories;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@@ -36,6 +38,20 @@
@AllArgsConstructor
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Autowired
    private MyAuthenticationProvider myAuthenticationProvider;
    /**
     * 认证设置
     * @param auth 认证manager
     * @throws Exception 异常
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //设置自定义认证provider
        auth.authenticationProvider(myAuthenticationProvider);
    }
    @Bean
    @Override
    @SneakyThrows
skjcmanager/skjcmanager-auth/src/main/java/cn/gistack/auth/granter/BladeTokenGranter.java
@@ -43,6 +43,8 @@
        List<TokenGranter> granters = new ArrayList<>(Collections.singletonList(endpoints.getTokenGranter()));
        // 增加验证码模式
        granters.add(new CaptchaTokenGranter(authenticationManager, endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory(), bladeRedis));
        // 增加手机验证码模式
        granters.add(new PhoneTokenGranter(authenticationManager, endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory(), bladeRedis));
        // 增加第三方登陆模式
        granters.add(new SocialTokenGranter(endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory(), userClient, socialProperties));
        // 组合tokenGranter集合
skjcmanager/skjcmanager-auth/src/main/java/cn/gistack/auth/granter/PhoneTokenGranter.java
New file
@@ -0,0 +1,72 @@
package cn.gistack.auth.granter;
import cn.gistack.auth.utils.TokenUtil;
import cn.gistack.common.cache.CacheNames;
import org.springblade.core.redis.cache.BladeRedis;
import org.springblade.core.tool.utils.StringUtil;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.security.oauth2.common.exceptions.UserDeniedAuthorizationException;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.token.AbstractTokenGranter;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import java.util.LinkedHashMap;
import java.util.Map;
/**
 * 验证码TokenGranter
 *
 * @author Chill
 */
public class PhoneTokenGranter extends AbstractTokenGranter {
    private static final String GRANT_TYPE = "phone";
    private final AuthenticationManager authenticationManager;
    private BladeRedis bladeRedis;
    public PhoneTokenGranter(AuthenticationManager authenticationManager,
                             AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, BladeRedis bladeRedis) {
        this(authenticationManager, tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
        this.bladeRedis = bladeRedis;
    }
    protected PhoneTokenGranter(AuthenticationManager authenticationManager, AuthorizationServerTokenServices tokenServices,
                                ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, String grantType) {
        super(tokenServices, clientDetailsService, requestFactory, grantType);
        this.authenticationManager = authenticationManager;
    }
    @Override
    protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
        Map<String, String> parameters = new LinkedHashMap<String, String>(tokenRequest.getRequestParameters());
        String phone = parameters.get("username");
        String code = parameters.get("password");
        // 获取验证码
        String redisCode = bladeRedis.get(CacheNames.PHONE_KEY + phone);
        // 判断验证码
        if (code == null || !StringUtil.equalsIgnoreCase(redisCode, code)) {
            throw new UserDeniedAuthorizationException(TokenUtil.CAPTCHA_NOT_CORRECT);
        }
        // 通过手机号查询用户
        Authentication userAuth = new UsernamePasswordAuthenticationToken(phone, "CUSTOM_LOGIN_SMS");
        ((AbstractAuthenticationToken) userAuth).setDetails(parameters);
        try {
            userAuth = authenticationManager.authenticate(userAuth);
        }
        catch (AccountStatusException | BadCredentialsException ase) {
            //covers expired, locked, disabled cases (mentioned in section 5.2, draft 31)
            throw new InvalidGrantException(ase.getMessage());
        }
        // If the username/password are wrong the spec says we should send 400/invalid grant
        if (userAuth == null || !userAuth.isAuthenticated()) {
            throw new InvalidGrantException("Could not authenticate user: " + phone);
        }
        OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
        return new OAuth2Authentication(storedOAuth2Request, userAuth);
    }
}
skjcmanager/skjcmanager-auth/src/main/java/cn/gistack/auth/service/BladeUserDetailsServiceImpl.java
@@ -20,7 +20,6 @@
import cn.gistack.sm.sjztmd.entity.AttResManagePerson;
import cn.gistack.sm.sjztmd.feign.IAttResManagePersonClient;
import cn.gistack.system.entity.Dept;
import cn.gistack.system.entity.Role;
import com.alibaba.nacos.common.utils.StringUtils;
import io.jsonwebtoken.Claims;
import lombok.AllArgsConstructor;
@@ -78,7 +77,6 @@
        // 获取租户ID
        String headerTenant = request.getHeader(TokenUtil.TENANT_HEADER_KEY);
        String paramTenant = request.getParameter(TokenUtil.TENANT_PARAM_KEY);
        String password = request.getParameter(TokenUtil.PASSWORD_KEY);
        String grantType = request.getParameter(TokenUtil.GRANT_TYPE_KEY);
        // 判断租户请求头
        if (StringUtil.isAllBlank(headerTenant, paramTenant)) {
@@ -129,12 +127,7 @@
            // 用户不存在,但提示用户名与密码错误并锁定账号
            if (user == null || user.getId() == null) {
                setFailCount(tenantId, username, count);
                throw new UsernameNotFoundException(TokenUtil.USER_NOT_FOUND);
            }
            // 用户存在但密码错误,超过次数则锁定账号
            if (grantType != null && !grantType.equals(TokenUtil.REFRESH_TOKEN_KEY) && !user.getPassword().equals(DigestUtil.hex(password))) {
                setFailCount(tenantId, username, count);
                throw new UsernameNotFoundException(TokenUtil.USER_NOT_FOUND);
                throw new UserDeniedAuthorizationException(TokenUtil.USER_NOT_FOUND);
            }
            // 用户角色不存在
            if (Func.isEmpty(userInfo.getRoles())) {
@@ -166,12 +159,10 @@
            R<Dept> deptRes = sysClient.getDept(Long.valueOf(deptStr));
            if (StringUtil.isBlank(deptRes.getData().getAdCode()))
                // 部门为空
                throw new UsernameNotFoundException("没有找到有效组织机构~");
                throw new UserDeniedAuthorizationException("没有找到有效组织机构~");
            else
                userInfo.getDetail().set("adCodeBase",attResManagePersonClient.getAdBaseById(deptRes.getData().getAdCode()).getData());
            // 成功则清除登录错误次数
            delFailCount(tenantId, username);
            return new BladeUserDetails(user.getId(),
                user.getTenantId(), StringPool.EMPTY, user.getName(), user.getRealName(), user.getDeptId(), user.getPostId(), user.getRoleId(), Func.join(userInfo.getRoles()), Func.toStr(user.getAvatar(), TokenUtil.DEFAULT_AVATAR),
                username, AuthConstant.ENCRYPT + user.getPassword(), userInfo.getDetail(), true, true, true, true,
skjcmanager/skjcmanager-auth/src/main/java/cn/gistack/auth/support/BladePasswordEncoder.java
@@ -18,12 +18,14 @@
import org.springblade.core.tool.utils.DigestUtil;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
/**
 * 自定义密码加密
 *
 * @author Chill
 */
@Component
public class BladePasswordEncoder implements PasswordEncoder {
    @Override
skjcmanager/skjcmanager-auth/src/main/java/cn/gistack/auth/utils/TokenUtil.java
@@ -58,6 +58,8 @@
    public final static String DEPT_HEADER_KEY = "Dept-Id";
    public final static String ROLE_HEADER_KEY = "Role-Id";
    public final static String PHONE_HEADER_KEY = "Phone-Key";
    public final static String PHONE_HEADER_CODE = "Phone-Code";
    public final static String CAPTCHA_HEADER_KEY = "Captcha-Key";
    public final static String CAPTCHA_HEADER_CODE = "Captcha-Code";
    public final static String CAPTCHA_NOT_CORRECT = "验证码不正确";
skjcmanager/skjcmanager-common/src/main/java/cn/gistack/common/cache/CacheNames.java
@@ -52,6 +52,11 @@
    String CAPTCHA_KEY = "blade:auth::blade:captcha:";
    /**
     * 手机验证码key
     */
    String PHONE_KEY = "blade:auth::blade:phone:";
    /**
     * 登录失败key
     */
    String USER_FAIL_KEY = "blade:user::blade:fail:";
skjcmanager/skjcmanager-service-api/skjcmanager-user-api/src/main/java/cn/gistack/system/user/feign/IUserClient.java
@@ -27,6 +27,8 @@
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
/**
 * User Feign接口类
 *
@@ -45,6 +47,8 @@
    String USER_INFO_BY_TYPE = API_PREFIX + "/user-info-by-type";
    String USER_INFO_BY_ID = API_PREFIX + "/user-info-by-id";
    String USER_INFO_BY_ACCOUNT = API_PREFIX + "/user-info-by-account";
    String USER_INFO_BY_PHONE = API_PREFIX + "/user-info-by-phone";
    String USER_INFO_BY_PHONE_OR_ACCOUNT = API_PREFIX + "/user-info-by-phone-or-account";
    String USER_AUTH_INFO = API_PREFIX + "/user-auth-info";
    String SAVE_USER = API_PREFIX + "/save-user";
    String REMOVE_USER = API_PREFIX + "/remove-user";
@@ -83,6 +87,26 @@
     * 获取用户信息
     *
     * @param tenantId 租户ID
     * @param phone  手机号
     * @return
     */
    @GetMapping(USER_INFO_BY_PHONE)
    R<List<User>> userInfoByPhone(@RequestParam("tenantId") String tenantId, @RequestParam("phone") String phone);
    /**
     * 获取用户信息
     *
     * @param tenantId 租户ID
     * @param phone  手机号或者账户
     * @return
     */
    @GetMapping(USER_INFO_BY_PHONE_OR_ACCOUNT)
    R<List<User>> userInfoByPhoneOrAccount(@RequestParam("tenantId") String tenantId, @RequestParam("phone") String phone);
    /**
     * 获取用户信息
     *
     * @param tenantId 租户ID
     * @param account  账号
     * @param userType 用户平台
     * @return
skjcmanager/skjcmanager-service/skjcmanager-sm/pom.xml
@@ -47,6 +47,11 @@
            <version>3.0.1.RELEASE</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>cn.gistack</groupId>
            <artifactId>skjcmanager-user-api</artifactId>
            <version>3.0.1.RELEASE</version>
        </dependency>
    </dependencies>
    <build>
skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/intelligentCall/controller/CallController.java
@@ -5,9 +5,8 @@
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.tool.api.R;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import java.util.*;
/**
@@ -28,20 +27,10 @@
     * 创建任务
     * @return
     */
    @GetMapping("/createCall")
    public R createCall(){
        List<Map<String,String>> list = new ArrayList<>();
        Map<String,String> maps = new HashMap<>();
        maps.put("phone","15170720695");
        maps.put("name","测试水库");
        maps.put("over","1.2");
        maps.put("code","1241241122");
        maps.put("userName","zrj");
        maps.put("userId","123456");
        list.add(maps);
        callService.createTask(list);
    @PostMapping("/createCall")
    public R createCall(@RequestBody List<Map<String,String>> list){
        // 返回
        return R.status(true);
        return R.data(callService.createTask(list));
    }
    /**
skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/intelligentCall/entity/User.java
File was deleted
skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/intelligentCall/service/CallService.java
@@ -6,7 +6,18 @@
public interface CallService {
    void createTask(List<Map<String, String>> params);
    /**
     * 创建任务
     * @param params
     * @return
     */
    Object createTask(List<Map<String, String>> params);
    /**
     * 获取通话详情
     * @param taskId
     * @param calleeNumber
     * @return
     */
    Object getCallDetail(String taskId, String calleeNumber);
}
skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/intelligentCall/service/impl/CallServiceImpl.java
@@ -53,8 +53,13 @@
    private final static String CREATE_TASK_URL = "/api/task/createTask.json";
    private final static String CALL_DETAIL_URL = "/api/task/callDetail.json";
    /**
     * 创建任务
     * @param params
     * @return
     */
    @Override
    public void createTask(List<Map<String, String>> params) {
    public Object createTask(List<Map<String, String>> params) {
        String requestUrl = url + CREATE_TASK_URL;
        LocalDateTime now = LocalDateTime.now();
        String timestamp = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss SSS"));
@@ -90,8 +95,15 @@
        if (response.getStatusCode() != HttpStatus.OK) {
            log.error("创建智能外呼任务失败:{}", response.getStatusCode());
        }
        return response;
    }
    /**
     * 获取通话详情
     * @param taskId
     * @param calleeNumber
     * @return
     */
    @Override
    public Object getCallDetail(String taskId, String calleeNumber) {
        String requestUrl = url + CALL_DETAIL_URL;
skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/sms/controller/SmsController.java
@@ -3,13 +3,24 @@
import cn.com.flaginfo.sdk.cmc.api.sms.dynsend.DynSMSSendRequest;
import cn.com.flaginfo.sdk.cmc.api.sms.send.SMSSendRequest;
import cn.gistack.sm.sms.entity.SmsRecord;
import cn.gistack.sm.sms.entity.SmsRequest;
import cn.gistack.sm.sms.service.ISmsRecordService;
import cn.gistack.sm.sms.util.SmsUtils;
import cn.gistack.system.user.entity.User;
import cn.gistack.system.user.feign.IUserClient;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.redis.cache.BladeRedis;
import org.springblade.core.tool.api.R;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static cn.gistack.common.cache.CacheNames.PHONE_KEY;
/**
 * 短信发送控制层
@@ -24,31 +35,76 @@
    private final ISmsRecordService smsRecordService;
    private final BladeRedis bladeRedis;
    private final IUserClient userClient;
    @GetMapping("/test")
    public void setRedis(){
        saveCodeForRedis("super_admin","123456");
    }
    /**
     * 发送登录验证码
     * request 必须包含手机号(多个手机号以英文逗号分隔)
     * request 必须包含手机号
     * @param request
     * @return
     */
    @PostMapping("/sendCode")
    public R test(@RequestBody SMSSendRequest request){
        //设置模板相关信息
        String templateId = "2431012205498";
        String randNumber = SmsUtils.getRandNumber();
        String content = "您的登录验证码是" + randNumber + ",5分钟内有效,请勿泄漏,如非本人操作,请忽略此信息";
        request.setMessageContent(content);
        request.setTemplateId(templateId);
        // 发送操作
        String sendMsg = SmsUtils.sendMsg(request);
        // 记录发送记录操作
        saveSendRecord(request,sendMsg);
        // 保存验证码到 redis
        saveCodeForRedis(request.getUserNumber(),randNumber);
        // 手机号码校验
        if(validatePhone(request.getUserNumber())) {
            //设置模板相关信息
            String templateId = "2431012205498";
            String randNumber = SmsUtils.getRandNumber();
            String content = "您的登录验证码是" + randNumber + ",5分钟内有效,请勿泄漏,如非本人操作,请忽略此信息";
            request.setMessageContent(content);
            request.setTemplateId(templateId);
            // 发送操作
            String sendMsg = SmsUtils.sendMsg(request);
            JSONObject jsonObject = JSON.parseObject(sendMsg);
            // 记录发送记录操作
            saveSendRecord(request, sendMsg);
            // 保存验证码到 redis
            saveCodeForRedis(request.getUserNumber(), randNumber);
            // 返回发送信息
            return R.data(jsonObject);
        }
        // 返回发送信息
        return R.data(sendMsg);
        return R.fail(400,"该手机号不存在!");
    }
    /**
     * 校验手机号码正确性
     * @param userNumber
     * @return
     */
    private boolean validatePhone(String userNumber) {
        // 校验手机号码正确性
        if (validator(userNumber)){
            // 校验该账户是否已注册,通过手机号查询用户是否存在,tenantId 暂时写死,未登陆情况 tenantId 获取不到
            R<List<User>> list = userClient.userInfoByPhoneOrAccount("000000", userNumber);
            if (list.getData().size()>0){
                return true;
            }
            return false;
        }
        return false;
    }
    /**
     * 校验手机号码
     */
    public static boolean validator(String phone) {
        String regex = "^((13[0-9])|(14[5,7,9])|(15([0-3]|[5-9]))|(166)|(17[0,1,3,5,6,7,8])|(18[0-9])|(19[8|9]))\\d{8}$";
        if (phone.length() != 11) {
            return false;
        } else {
            Pattern p = Pattern.compile(regex);
            Matcher m = p.matcher(phone);
            boolean isMatch = m.matches();
            return isMatch;
        }
    }
    /**
@@ -57,7 +113,7 @@
     * @param randNumber
     */
    private void saveCodeForRedis(String userNumber, String randNumber) {
        bladeRedis.setEx(userNumber,randNumber,5*60L);
        bladeRedis.setEx(PHONE_KEY + userNumber,randNumber,5*60L);
    }
    /**
@@ -75,8 +131,23 @@
        smsRecordService.save(smsRecord);
    }
    @PostMapping("/test1")
    public R test1(@RequestBody DynSMSSendRequest request){
        return R.data(SmsUtils.dynSendMethod(request));
    /**
     * 发送邮件提醒
     * @param request
     * @return
     */
    @PostMapping("/sendEmailMsg")
    public R sendEmailMsg(@RequestBody SmsRequest request){
        // 处理数据(可能是多个手机号情况)
        List<String> list = Arrays.asList(request.getPhone().split(","));
        // 遍历
        for (String phone : list) {
            DynSMSSendRequest dynSMSSendRequest = new DynSMSSendRequest();
            dynSMSSendRequest.setTemplateId("2431012165350");
            dynSMSSendRequest.setDynData(new String[][]{{"手机号码","标题"},{phone,request.getContent()}});
            // 发送邮件短信
            SmsUtils.dynSendMethod(dynSMSSendRequest);
        }
        return R.status(true);
    }
}
skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/sms/entity/SmsRequest.java
New file
@@ -0,0 +1,21 @@
package cn.gistack.sm.sms.entity;
import lombok.Data;
/**
 * @author zhongrj
 * @date 2023-04-25
 */
@Data
public class SmsRequest {
    /**
     * 手机号
     */
    private String phone;
    /**
     * 内容
     */
    private String content;
}
skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/sms/util/SmsUtils.java
@@ -21,6 +21,8 @@
import cn.gistack.sm.sms.constant.SmsConstant;
import com.alibaba.fastjson.JSON;
import org.aspectj.lang.annotation.Before;
import org.springblade.core.redis.cache.BladeRedis;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@@ -29,10 +31,14 @@
import java.util.List;
import java.util.Random;
import static cn.gistack.common.cache.CacheNames.PHONE_KEY;
import static cn.gistack.sm.sms.constant.SmsConstant.*;
@Component
public class SmsUtils {
    @Autowired
    private BladeRedis bladeRedis;
    /**
     * 固定内容发送
@@ -50,30 +56,18 @@
        return JSON.toJSONString(request);
    }
//    public static void main(String[] args) {
//        SMSSendRequest sendRequest = new SMSSendRequest();
//        sendRequest.setMessageContent("你有一项编号为12349的事务需要处理;");
//        sendRequest.setTemplateId("2431012234034");
//        sendRequest.setUserNumber("15170720695");
//        String s = sendMsg(sendRequest);
//        System.out.println("s = " + s);
//    }
    /**
     * 获取随机字符串
     * @return
     */
    public static String getRandNumber() {
        // 计算token
//        Date date = new Date();
//        String dateFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(date);
        Random random = new Random();
        //把随机生成的数字转成字符串
        String str = String.valueOf(random.nextInt(9));
        for (int i = 0; i < 5; i++) {
            str += random.nextInt(9);
        }
        return String.valueOf(System.currentTimeMillis()) + str;
        return str;
    }
    /**
@@ -86,7 +80,6 @@
        DynSMSAPI api = (DynSMSAPI) provider.getApi(ApiEnum.SENDDYNSMS);
        api.setRequestUrl(SmsConstant.HOST + SmsConstant.sendVarUrl);
        //请求参数
//        sendRequest.setDynData(new String[][]{{"手机号码", "编号"}, {"13000000000", "456"}});
        sendRequest.setSerialNumber(new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date()));
        ComResult<DynSMSSendDataResult> result = api.request(sendRequest);
        return JSON.toJSONString(result);
skjcmanager/skjcmanager-service/skjcmanager-user/src/main/java/cn/gistack/system/user/feign/UserClient.java
@@ -31,6 +31,8 @@
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
 * 用户服务Feign实现类
 *
@@ -62,6 +64,18 @@
    }
    @Override
    @GetMapping(USER_INFO_BY_PHONE)
    public R<List<User>> userInfoByPhone(String tenantId, String phone) {
        return R.data(service.userInfoByPhone(tenantId, phone));
    }
    @Override
    @GetMapping(USER_INFO_BY_PHONE_OR_ACCOUNT)
    public R<List<User>> userInfoByPhoneOrAccount(String tenantId, String phone) {
        return R.data(service.userInfoByPhoneOrAccount(tenantId, phone));
    }
    @Override
    @GetMapping(USER_INFO_BY_TYPE)
    public R<UserInfo> userInfo(String tenantId, String account, String userType) {
        return R.data(service.userInfo(tenantId, account, UserEnum.of(userType)));
skjcmanager/skjcmanager-service/skjcmanager-user/src/main/java/cn/gistack/system/user/mapper/UserMapper.java
@@ -62,4 +62,12 @@
    List<UserExcel> exportUser(@Param("ew") Wrapper<User> queryWrapper);
    List<User> customizeGetList(@Param("user") UserVO user);
    /**
     * 根据手机号码或者账户查询用户信息
     * @param tenantId 租户id
     * @param key 账户或者手机号
     * @return
     */
    List<User> selectUserByPhoneOrAccount(@Param("tenantId")String tenantId, @Param("key")String key);
}
skjcmanager/skjcmanager-service/skjcmanager-user/src/main/java/cn/gistack/system/user/mapper/UserMapper.xml
@@ -78,4 +78,11 @@
        where D.AD_CODE = #{user.adCode} and U.ROLE_ID like CONCAT('%',(select id from BLADE_ROLE where ROLE_ALIAS = 'qxgly'),'%')
    </select>
    <!--根据手机号码或者账户查询用户信息-->
    <select id="selectUserByPhoneOrAccount" resultType="cn.gistack.system.user.entity.User">
        select * from BLADE_USER where is_deleted = 0
        and TENANT_ID = #{tenantId}
        and (ACCOUNT = #{key} or PHONE = #{key})
    </select>
</mapper>
skjcmanager/skjcmanager-service/skjcmanager-user/src/main/java/cn/gistack/system/user/service/IUserService.java
@@ -207,4 +207,20 @@
    UserVO platformDetail(User user);
    List<User> customizeGetList(UserVO user);
    /**
     * 根据手机号码查询用户信息
     * @param tenantId
     * @param phone
     * @return
     */
    List<User> userInfoByPhone(String tenantId, String phone);
    /**
     * 根据手机号码或者账户查询用户信息
     * @param tenantId 租户id
     * @param key 账户或者手机号
     * @return
     */
    List<User> userInfoByPhoneOrAccount(String tenantId, String key);
}
skjcmanager/skjcmanager-service/skjcmanager-user/src/main/java/cn/gistack/system/user/service/impl/UserServiceImpl.java
@@ -198,8 +198,11 @@
    @Override
    public UserInfo userInfo(String tenantId, String account, UserEnum userEnum) {
        User user = baseMapper.getUser(tenantId, account);
        return buildUserInfo(user, userEnum);
        List<User> list = baseMapper.selectUserByPhoneOrAccount(tenantId, account);
        if (list.size()>0){
            return buildUserInfo(list.get(0), userEnum);
        }
        return buildUserInfo(null, userEnum);
    }
    private UserInfo buildUserInfo(User user) {
@@ -435,4 +438,25 @@
        return baseMapper.customizeGetList(user);
    }
    /**
     * 根据手机号码查询用户信息
     * @param tenantId
     * @param phone
     * @return
     */
    @Override
    public List<User> userInfoByPhone(String tenantId, String phone) {
        return list(Wrappers.<User>query().lambda().eq(User::getTenantId, tenantId).eq(User::getPhone, phone).eq(User::getIsDeleted, BladeConstant.DB_NOT_DELETED));
    }
    /**
     * 根据手机号码或者账户查询用户信息
     * @param tenantId 租户id
     * @param key 账户或者手机号
     * @return
     */
    @Override
    public List<User> userInfoByPhoneOrAccount(String tenantId, String key) {
        return baseMapper.selectUserByPhoneOrAccount(tenantId,key);
    }
}