Commit b4b72711 by 慕容若冰 Committed by Gitee

!5 优化

Merge pull request !5 from 慕容若冰/dev
parents 4e925978 58cdf68b
package com.github.tangyi.common.core.constant;
/**
* 定义api返回编码和提示内容
*
* @author tangyi
* @date 2019-07-24 20:31
*/
public class ApiConstant {
/**
* 服务不可用
*/
public static final int SERVICE_NOT_AVAILABLE = 1;
}
...@@ -67,8 +67,22 @@ public class PageUtil { ...@@ -67,8 +67,22 @@ public class PageUtil {
* @date 2019/07/03 22:26:18 * @date 2019/07/03 22:26:18
*/ */
public static void copyProperties(PageInfo<?> source, PageInfo<?> target) { public static void copyProperties(PageInfo<?> source, PageInfo<?> target) {
target.setTotal(source.getTotal());
target.setPageSize(source.getPageSize());
target.setPageNum(source.getPageNum()); target.setPageNum(source.getPageNum());
target.setPageSize(source.getPageSize());
target.setSize(source.getSize());
target.setStartRow(source.getStartRow());
target.setEndRow(source.getEndRow());
target.setPages(source.getPages());
target.setPrePage(source.getPrePage());
target.setNextPage(source.getNextPage());
target.setIsFirstPage(source.isIsFirstPage());
target.setIsLastPage(source.isIsLastPage());
target.setHasPreviousPage(source.isHasPreviousPage());
target.setHasNextPage(source.isHasNextPage());
target.setNavigatePages(source.getNavigatePages());
target.setNavigatepageNums(source.getNavigatepageNums());
target.setNavigateFirstPage(source.getNavigateFirstPage());
target.setNavigateLastPage(source.getNavigateLastPage());
target.setTotal(source.getTotal());
} }
} }
...@@ -125,5 +125,10 @@ public class UserVo extends BaseEntity<UserVo> { ...@@ -125,5 +125,10 @@ public class UserVo extends BaseEntity<UserVo> {
*/ */
private String wechat; private String wechat;
/**
* 家庭角色,参考UserStudentConstant
*/
private Integer familyRole;
} }
...@@ -24,7 +24,7 @@ public class SecurityConstant { ...@@ -24,7 +24,7 @@ public class SecurityConstant {
/** /**
* 默认短信验证码过期时间 * 默认短信验证码过期时间
*/ */
public static final int DEFAULT_SMS_EXPIRE = 5 * 60; public static final int DEFAULT_SMS_EXPIRE = 15 * 60;
/** /**
* 正常状态 * 正常状态
......
package com.github.tangyi.common.security.core; package com.github.tangyi.common.security.core;
import com.github.tangyi.common.security.mobile.MobileUser;
import com.github.tangyi.common.security.wx.WxUser; import com.github.tangyi.common.security.wx.WxUser;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.core.userdetails.UsernameNotFoundException;
...@@ -28,11 +29,12 @@ public interface CustomUserDetailsService { ...@@ -28,11 +29,12 @@ public interface CustomUserDetailsService {
* *
* @param social social * @param social social
* @param tenantCode tenantCode * @param tenantCode tenantCode
* @param mobileUser mobileUser
* @return UserDetails * @return UserDetails
* @author tangyi * @author tangyi
* @date 2019/06/22 21:08 * @date 2019/06/22 21:08
*/ */
UserDetails loadUserBySocialAndTenantCode(String social, String tenantCode) throws UsernameNotFoundException; UserDetails loadUserBySocialAndTenantCode(String social, String tenantCode, MobileUser mobileUser) throws UsernameNotFoundException;
/** /**
* 根据微信openId和租户标识查询 * 根据微信openId和租户标识查询
......
package com.github.tangyi.common.security.mobile; package com.github.tangyi.common.security.mobile;
import com.github.tangyi.common.security.constant.SecurityConstant; import com.github.tangyi.common.security.constant.SecurityConstant;
import com.github.tangyi.common.security.utils.GsonHelper;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationEventPublisher; import org.springframework.security.authentication.AuthenticationEventPublisher;
import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.authentication.AuthenticationServiceException;
...@@ -18,6 +22,7 @@ import org.springframework.security.web.util.matcher.AntPathRequestMatcher; ...@@ -18,6 +22,7 @@ import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/** /**
* 手机登录filter * 手机登录filter
...@@ -25,6 +30,7 @@ import javax.servlet.http.HttpServletResponse; ...@@ -25,6 +30,7 @@ import javax.servlet.http.HttpServletResponse;
* @author tangyi * @author tangyi
* @date 2019/6/22 21:15 * @date 2019/6/22 21:15
*/ */
@Slf4j
public class MobileAuthenticationFilter extends AbstractAuthenticationProcessingFilter { public class MobileAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private static final String SPRING_SECURITY_FORM_MOBILE_KEY = "mobile"; private static final String SPRING_SECURITY_FORM_MOBILE_KEY = "mobile";
...@@ -61,6 +67,8 @@ public class MobileAuthenticationFilter extends AbstractAuthenticationProcessing ...@@ -61,6 +67,8 @@ public class MobileAuthenticationFilter extends AbstractAuthenticationProcessing
mobile = mobile.trim(); mobile = mobile.trim();
// 封装成token // 封装成token
MobileAuthenticationToken mobileAuthenticationToken = new MobileAuthenticationToken(mobile); MobileAuthenticationToken mobileAuthenticationToken = new MobileAuthenticationToken(mobile);
// 封装其它基本信息
setMobileUserDetails(request, mobileAuthenticationToken);
setDetails(request, mobileAuthenticationToken); setDetails(request, mobileAuthenticationToken);
Authentication authResult = null; Authentication authResult = null;
try { try {
...@@ -90,4 +98,22 @@ public class MobileAuthenticationFilter extends AbstractAuthenticationProcessing ...@@ -90,4 +98,22 @@ public class MobileAuthenticationFilter extends AbstractAuthenticationProcessing
private void setDetails(HttpServletRequest request, MobileAuthenticationToken authRequest) { private void setDetails(HttpServletRequest request, MobileAuthenticationToken authRequest) {
authRequest.setDetails(authenticationDetailsSource.buildDetails(request)); authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
} }
/**
* 设置姓名、性别、头像等信息
*
* @param request request
* @param authRequest authRequest
*/
private void setMobileUserDetails(HttpServletRequest request, MobileAuthenticationToken authRequest) {
try {
String result = IOUtils.toString(request.getReader());
if (StringUtils.isNotBlank(result)) {
MobileUser user = GsonHelper.getInstance().fromJson(result, MobileUser.class);
authRequest.setMobileUser(user);
}
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
} }
...@@ -28,7 +28,7 @@ public class MobileAuthenticationProvider implements AuthenticationProvider { ...@@ -28,7 +28,7 @@ public class MobileAuthenticationProvider implements AuthenticationProvider {
public Authentication authenticate(Authentication authentication) throws AuthenticationException { public Authentication authenticate(Authentication authentication) throws AuthenticationException {
MobileAuthenticationToken mobileAuthenticationToken = (MobileAuthenticationToken) authentication; MobileAuthenticationToken mobileAuthenticationToken = (MobileAuthenticationToken) authentication;
String principal = mobileAuthenticationToken.getPrincipal().toString(); String principal = mobileAuthenticationToken.getPrincipal().toString();
UserDetails userDetails = customUserDetailsService.loadUserBySocialAndTenantCode(principal, TenantContextHolder.getTenantCode()); UserDetails userDetails = customUserDetailsService.loadUserBySocialAndTenantCode(principal, TenantContextHolder.getTenantCode(), mobileAuthenticationToken.getMobileUser());
if (userDetails == null) { if (userDetails == null) {
log.debug("Authentication failed: no credentials provided"); log.debug("Authentication failed: no credentials provided");
throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.noopBindAccount", "Noop Bind Account")); throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.noopBindAccount", "Noop Bind Account"));
......
...@@ -15,6 +15,8 @@ public class MobileAuthenticationToken extends AbstractAuthenticationToken { ...@@ -15,6 +15,8 @@ public class MobileAuthenticationToken extends AbstractAuthenticationToken {
private final Object principal; private final Object principal;
private MobileUser mobileUser;
public MobileAuthenticationToken(String mobile) { public MobileAuthenticationToken(String mobile) {
super(null); super(null);
this.principal = mobile; this.principal = mobile;
...@@ -49,4 +51,12 @@ public class MobileAuthenticationToken extends AbstractAuthenticationToken { ...@@ -49,4 +51,12 @@ public class MobileAuthenticationToken extends AbstractAuthenticationToken {
public void eraseCredentials() { public void eraseCredentials() {
super.eraseCredentials(); super.eraseCredentials();
} }
public MobileUser getMobileUser() {
return mobileUser;
}
public void setMobileUser(MobileUser mobileUser) {
this.mobileUser = mobileUser;
}
} }
package com.github.tangyi.common.security.mobile;
import lombok.Data;
import java.io.Serializable;
/**
* @author tangyi
* @date 2019/08/04 13:28
*/
@Data
public class MobileUser implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 姓名
*/
private String name;
/**
* 性别
*/
private Integer sex;
/**
* 头像地址
*/
private String avatarUrl;
/**
* 详细描述
*/
private String userDesc;
/**
* 国家
*/
private String country;
/**
* 省
*/
private String province;
/**
* 市
*/
private String city;
/**
* 语言
*/
private String languang;
}
server: server:
port: 8000 port: 8000
# HTTPS
# ssl:
# enabled: false
# key-alias: test
# key-password: test
# key-store: classpath:www.it99.club.jks
# key-store-type: JKS
# key-store-provider: SUN
# key-store-password: test
spring: spring:
datasource: datasource:
url: jdbc:mysql://${MYSQL_HOST:localhost}:${MYSQL_PORT:3306}/microservice-gateway?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=CTT&characterEncoding=UTF-8 url: jdbc:mysql://${MYSQL_HOST:localhost}:${MYSQL_PORT:3306}/microservice-gateway?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=CTT&characterEncoding=UTF-8
......
...@@ -17,6 +17,60 @@ spring: ...@@ -17,6 +17,60 @@ spring:
password: ${ADMIN_PASSWORD:11} password: ${ADMIN_PASSWORD:11}
instance: instance:
service-base-url: http://${AUTH_SERVICE_HOST:localhost}:${server.port} service-base-url: http://${AUTH_SERVICE_HOST:localhost}:${server.port}
# sleuth 配置
sleuth:
web:
client:
enabled: true
sampler:
probability: 1.0 # 采样比例为: 0.1(即10%),设置的值介于0.0到1.0之间,1.0则表示全部采集。
# zipkin 配置
zipkin:
base-url: http://${ZIPKIN_HOST:localhost}:${ZIPKIN_PORT:9411} # 指定了Zipkin服务器的地址
security:
oauth2:
client:
client-id: web_app
client-secret: $2a$10$S84wOzmAw4wqYBzAqNtmPOx1ZRWdDl9omm/W8T1hhW8S9EK2u57xG
access-token-uri: http://${AUTH_SERVICE_HOST:localhost}:${AUTH_SERVICE_PORT:8000}/api/auth/oauth/token
user-authorization-uri: http://${AUTH_SERVICE_HOST:localhost}:${AUTH_SERVICE_PORT:8000}/api/auth/oauth/authorize
grant-type: password
scope: read
resource:
user-info-uri: http://${AUTH_SERVICE_HOST:localhost}:${AUTH_SERVICE_PORT:8000}/api/user/v1/user/info
prefer-token-info: false
jwt:
key-uri: http://${AUTH_SERVICE_HOST:localhost}:${AUTH_SERVICE_PORT:8000}/api/auth/oauth/token_key
key-value: |
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjxSawA+NUNmhB2ctiVnt
YH41WCNoD5STW2iPm5AIsKvm6X67lr6A88qOMRwx9oySdZnUnJ+8L3QJ51fkwuDe
ix5w9yA3f/7LUPWZU8M/7Oi+2nda05JMgU999TUlhTGsp9SPiBqq/iwuqMxU8xKu
F8bpTJTOpzrxH4e5BM6J/UJcOwDZLY6/3zP5w+tbhTvxjc995G4NtUyS4owE1MHe
lj8IJepknjePrE6nXD6ecdL401hstMY838UOOFiCnM8NpiBuNI0nY0qCbb9mhQZ+
7gP3jjM+Ft7R+MFTuEHWQ5UN8qHAPIT9UlLcu9IXdk6YwTsqNavwaTLUcP/ih9HB
6wIDAQAB
-----END PUBLIC KEY-----
# feign相关配置
feign:
httpclient:
enabled: false
okhttp:
enabled: true
hystrix:
enabled: true
# hystrix配置
hystrix:
shareSecurityContext: true
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 60000
management: management:
endpoints: endpoints:
...@@ -27,11 +81,16 @@ management: ...@@ -27,11 +81,16 @@ management:
health: health:
show-details: ALWAYS show-details: ALWAYS
# 短信配置
sms: sms:
appKey: test appKey: appKey
appSecret: test appSecret: appSecret
regionId: cn-hangzhou regionId: default
domain: dysmsapi.aliyuncs.com domain: dysmsapi.aliyuncs.com
signName: 在线考试系统演示
templateCode: SMS_171192294
version: 2017-05-25
action: SendSms
ignore: ignore:
urls: urls:
......
# API网关
HTTPS配置,两种方式:
1. NGINX配置SSL证书
2. API网关支持HTTPS
目前API网关已支持HTTPS,并且已经做了适配,转发请求时的协议会转为HTTP
配置见`gateway-service.yml`的ssl部分内容
# NGINX配置HTTPS
编辑 Nginx 根目录下的 conf/nginx.conf 文件。修改内容如下:
```
server {
listen 443; #SSL 访问端口号为 443
server_name www.domain.com; #填写绑定证书的域名
ssl on; #启用 SSL 功能
ssl_certificate 1_www.domain.com_bundle.crt; #证书文件名称
ssl_certificate_key 2_www.domain.com.key; #私钥文件名称
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #请按照这个协议配置
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE; #请按照这个套件配置,配置加密套件,写法遵循 openssl 标准。
ssl_prefer_server_ciphers on;
location / {
root /var/www/www.domain.com; #网站主页路径。此路径仅供参考,具体请您按照实际目录操作。
index index.html index.htm;
}
}
```
\ No newline at end of file
...@@ -30,7 +30,6 @@ public class RouteController extends BaseController { ...@@ -30,7 +30,6 @@ public class RouteController extends BaseController {
private final RouteService routeService; private final RouteService routeService;
/** /**
* 根据id获取路由 * 根据id获取路由
* *
......
package com.github.tangyi.gateway.filters;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.net.URI;
/**
* 将HTTPS改为HTTP
* 网关进来的协议是HTTPS,其它服务一般部署在内网,没必要走HTTPS
*
* @author tangyi
* @date 2019/08/03 12:03
*/
@Component
public class HttpsToHttpFilter implements GlobalFilter, Ordered {
private static final int HTTPS_TO_HTTP_FILTER_ORDER = 10099;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
URI originalUri = exchange.getRequest().getURI();
ServerHttpRequest request = exchange.getRequest();
ServerHttpRequest.Builder mutate = request.mutate();
String forwardedUri = request.getURI().toString();
if (forwardedUri != null && forwardedUri.startsWith("https")) {
try {
URI mutatedUri = new URI("http",
originalUri.getUserInfo(),
originalUri.getHost(),
originalUri.getPort(),
originalUri.getPath(),
originalUri.getQuery(),
originalUri.getFragment());
mutate.uri(mutatedUri);
} catch (Exception e) {
throw new IllegalArgumentException(e.getMessage(), e);
}
}
ServerHttpRequest build = mutate.build();
return chain.filter(exchange.mutate().request(build).build());
}
/**
* LoadBalancerClientFilter的order是10100
* 要在LoadBalancerClientFilter执行之前将HTTPS修改为HTTP,则这里的order设置为10099
*
* @return int
*/
@Override
public int getOrder() {
return HTTPS_TO_HTTP_FILTER_ORDER;
}
}
...@@ -63,9 +63,48 @@ export function loginByUsername (identifier, credential, code, randomStr) { ...@@ -63,9 +63,48 @@ export function loginByUsername (identifier, credential, code, randomStr) {
#### 二 手机号+验证码登录 #### 二 手机号+验证码登录
接口URL:`/api/auth/mobile/token` 发送短信接口:`/api/user/v1/mobile/sendSms/` + ${mobile},其中mobile为手机号
grant_type: mobile 登录接口URL:`/api/auth/mobile/token`
```
/**
* 根据手机号登录
* @param social 手机号
* @param code 验证码
*/
export function loginBySocial (social, code) {
const grantType = 'mobile'
const scope = 'read'
return request({
url: '/api/auth/mobile/token',
headers: {
'Authorization': basicAuthorization
},
method: 'post',
params: {mobile: social, code, grant_type: grantType, scope},
data: {
name: userInfo.nickName,
sex: userInfo.gender,
avatarUrl: userInfo.avatarUrl
}
})
}
```
响应体:
```
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9",
"token_type": "bearer",
"refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9",
"expires_in": 3599,
"scope": "read write",
"loginType": "SMS",
"tenantCode": "gitee",
"jti": "e446adad-df0a-490a-9cde-8ac69c846335"
}
```
#### 三 微信小程序+code登录 #### 三 微信小程序+code登录
......
...@@ -14,6 +14,7 @@ import com.github.tangyi.common.core.vo.RoleVo; ...@@ -14,6 +14,7 @@ import com.github.tangyi.common.core.vo.RoleVo;
import com.github.tangyi.common.core.vo.UserVo; import com.github.tangyi.common.core.vo.UserVo;
import com.github.tangyi.common.security.core.CustomUserDetailsService; import com.github.tangyi.common.security.core.CustomUserDetailsService;
import com.github.tangyi.common.security.core.GrantedAuthorityImpl; import com.github.tangyi.common.security.core.GrantedAuthorityImpl;
import com.github.tangyi.common.security.mobile.MobileUser;
import com.github.tangyi.common.security.wx.WxUser; import com.github.tangyi.common.security.wx.WxUser;
import com.github.tangyi.user.api.constant.MenuConstant; import com.github.tangyi.user.api.constant.MenuConstant;
import com.github.tangyi.user.api.dto.UserDto; import com.github.tangyi.user.api.dto.UserDto;
...@@ -75,17 +76,39 @@ public class CustomUserDetailsServiceImpl implements CustomUserDetailsService { ...@@ -75,17 +76,39 @@ public class CustomUserDetailsServiceImpl implements CustomUserDetailsService {
* *
* @param social social * @param social social
* @param tenantCode tenantCode * @param tenantCode tenantCode
* @param mobileUser mobileUser
* @return UserDetails * @return UserDetails
* @author tangyi * @author tangyi
* @date 2019/06/22 21:08 * @date 2019/06/22 21:08
*/ */
@Override @Override
public UserDetails loadUserBySocialAndTenantCode(String social, String tenantCode) throws UsernameNotFoundException { public UserDetails loadUserBySocialAndTenantCode(String social, String tenantCode, MobileUser mobileUser) throws UsernameNotFoundException {
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
Tenant tenant = this.validateTenantCode(tenantCode); Tenant tenant = this.validateTenantCode(tenantCode);
UserVo userVo = userServiceClient.findUserBySocial(social, tenantCode); UserVo userVo = userServiceClient.findUserByIdentifier(social, IdentityType.PHONE_NUMBER.getValue(), tenantCode);
if (userVo == null) // 第一次登录
throw new UsernameNotFoundException("用户手机号未注册."); if (userVo == null) {
UserDto userDto = new UserDto();
// 用户的基本信息
if (mobileUser != null)
BeanUtils.copyProperties(mobileUser, userDto);
userDto.setIdentifier(social);
userDto.setCredential(social);
userDto.setIdentityType(IdentityType.PHONE_NUMBER.getValue());
userDto.setLoginTime(DateUtils.asDate(LocalDateTime.now()));
// 注册账号
ResponseBean<Boolean> response = userServiceClient.registerUser(userDto);
if (response == null || !response.getData())
throw new CommonException("自动注册用户失败.");
// 重新获取用户信息
userVo = userServiceClient.findUserByIdentifier(social, IdentityType.PHONE_NUMBER.getValue(), tenantCode);
} else {
// TODO 记录登录时间,IP等信息
UserDto userDto = new UserDto();
BeanUtils.copyProperties(userVo, userDto);
userDto.setLoginTime(DateUtils.asDate(LocalDateTime.now()));
//userServiceClient.updateUser(userDto);
}
return new CustomUserDetails(userVo.getIdentifier(), userVo.getCredential(), CommonConstant.STATUS_NORMAL.equals(userVo.getStatus()), getAuthority(userVo), userVo.getTenantCode(), start, LoginType.SMS); return new CustomUserDetails(userVo.getIdentifier(), userVo.getCredential(), CommonConstant.STATUS_NORMAL.equals(userVo.getStatus()), getAuthority(userVo), userVo.getTenantCode(), start, LoginType.SMS);
} }
......
...@@ -60,6 +60,6 @@ public class CustomGlobalExceptionHandler extends ResponseEntityExceptionHandler ...@@ -60,6 +60,6 @@ public class CustomGlobalExceptionHandler extends ResponseEntityExceptionHandler
responseBean.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); responseBean.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
responseBean.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value()); responseBean.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
responseBean.setMsg(e.getMessage()); responseBean.setMsg(e.getMessage());
return new ResponseEntity<>(responseBean, HttpStatus.INTERNAL_SERVER_ERROR); return new ResponseEntity<>(responseBean, HttpStatus.OK);
} }
} }
...@@ -9,5 +9,5 @@ public class SmsConstant { ...@@ -9,5 +9,5 @@ public class SmsConstant {
/** /**
* 短信模板 * 短信模板
*/ */
public static final String SMS_TEMPLATE = "验证码:%s,本验证码有效时间5分钟,请勿告知其他人。"; public static final String SMS_TEMPLATE = "{\"code\":\"%s\"}";
} }
package com.github.tangyi.msc.api.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
* 封装短信服务返回的结果
*
* @author tangyi
* @date 2019/08/04 13:51
*/
@Data
public class SmsResponse {
@JsonProperty("Message")
private String message;
@JsonProperty("RequestId")
private String requestId;
@JsonProperty("Code")
private String code;
}
...@@ -3,6 +3,7 @@ package com.github.tangyi.msc.controller; ...@@ -3,6 +3,7 @@ package com.github.tangyi.msc.controller;
import com.github.tangyi.common.core.model.ResponseBean; import com.github.tangyi.common.core.model.ResponseBean;
import com.github.tangyi.common.core.web.BaseController; import com.github.tangyi.common.core.web.BaseController;
import com.github.tangyi.msc.api.dto.SmsDto; import com.github.tangyi.msc.api.dto.SmsDto;
import com.github.tangyi.msc.api.model.SmsResponse;
import com.github.tangyi.msc.service.SmsService; import com.github.tangyi.msc.service.SmsService;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
...@@ -36,11 +37,10 @@ public class SmsController extends BaseController { ...@@ -36,11 +37,10 @@ public class SmsController extends BaseController {
* @date 2019/06/22 13:12 * @date 2019/06/22 13:12
*/ */
@PostMapping("sendSms") @PostMapping("sendSms")
public ResponseBean<Object> sendSms(@RequestBody SmsDto smsDto) { public ResponseBean<SmsResponse> sendSms(@RequestBody SmsDto smsDto) {
log.info("发送短信给{},发送内容:{}", smsDto.getReceiver(), smsDto.getContent()); log.info("发送短信给{},发送内容:{}", smsDto.getReceiver(), smsDto.getContent());
// TODO 发送逻辑 SmsResponse smsResponse = smsService.sendSms(smsDto);
String result = smsService.sendSms(smsDto); log.info("发送短信成功,返回内容:{}", smsResponse);
log.info("发送短信成功,返回内容:{}", result); return new ResponseBean<>(smsResponse);
return new ResponseBean<>(result);
} }
} }
package com.github.tangyi.msc.error;
import com.github.tangyi.common.core.exceptions.CommonException;
import com.github.tangyi.common.core.model.ResponseBean;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import java.util.List;
import java.util.stream.Collectors;
/**
* 全局异常处理
*
* @author tangyi
* @date 2019/05/25 15:36
*/
@ControllerAdvice
public class CustomGlobalExceptionHandler extends ResponseEntityExceptionHandler {
/**
* 处理参数校验异常
*
* @param ex ex
* @param headers headers
* @param status status
* @param request request
* @return ResponseEntity
*/
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex,
HttpHeaders headers,
HttpStatus status, WebRequest request) {
// 获取参数校验异常信息
List<String> errors = ex.getBindingResult()
.getFieldErrors()
.stream()
.map(DefaultMessageSourceResolvable::getDefaultMessage)
.collect(Collectors.toList());
ResponseBean<List<String>> responseBean = new ResponseBean<>(errors);
responseBean.setStatus(status.value());
return new ResponseEntity<>(responseBean, headers, status);
}
/**
* 处理CommonException
*
* @param e e
* @return ResponseEntity
*/
@ExceptionHandler(CommonException.class)
public ResponseEntity<ResponseBean<String>> handleCommonException(Exception e) {
ResponseBean<String> responseBean = new ResponseBean<>();
responseBean.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
responseBean.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
responseBean.setMsg(e.getMessage());
return new ResponseEntity<>(responseBean, HttpStatus.OK);
}
}
...@@ -38,4 +38,24 @@ public class SmsProperties { ...@@ -38,4 +38,24 @@ public class SmsProperties {
* domain * domain
*/ */
private String domain; private String domain;
/**
* 签名
*/
private String signName;
/**
* 模板code
*/
private String templateCode;
/**
* 版本
*/
private String version;
/**
* action
*/
private String action;
} }
...@@ -6,7 +6,10 @@ import com.aliyuncs.DefaultAcsClient; ...@@ -6,7 +6,10 @@ import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient; import com.aliyuncs.IAcsClient;
import com.aliyuncs.http.MethodType; import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile; import com.aliyuncs.profile.DefaultProfile;
import com.github.tangyi.common.core.exceptions.CommonException;
import com.github.tangyi.common.core.utils.JsonMapper;
import com.github.tangyi.msc.api.dto.SmsDto; import com.github.tangyi.msc.api.dto.SmsDto;
import com.github.tangyi.msc.api.model.SmsResponse;
import com.github.tangyi.msc.properties.SmsProperties; import com.github.tangyi.msc.properties.SmsProperties;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
...@@ -27,11 +30,11 @@ public class SmsService { ...@@ -27,11 +30,11 @@ public class SmsService {
* 发送短信 * 发送短信
* *
* @param smsDto smsDto * @param smsDto smsDto
* @return String * @return SmsResponse
* @author tangyi * @author tangyi
* @date 2019/06/22 13:28 * @date 2019/06/22 13:28
*/ */
public String sendSms(SmsDto smsDto) { public SmsResponse sendSms(SmsDto smsDto) {
DefaultProfile profile = DefaultProfile.getProfile(smsProperties.getRegionId(), smsProperties.getAppKey(), smsProperties.getAppSecret()); DefaultProfile profile = DefaultProfile.getProfile(smsProperties.getRegionId(), smsProperties.getAppKey(), smsProperties.getAppSecret());
IAcsClient client = new DefaultAcsClient(profile); IAcsClient client = new DefaultAcsClient(profile);
CommonRequest request = new CommonRequest(); CommonRequest request = new CommonRequest();
...@@ -39,13 +42,27 @@ public class SmsService { ...@@ -39,13 +42,27 @@ public class SmsService {
request.setDomain(smsProperties.getDomain()); request.setDomain(smsProperties.getDomain());
request.putQueryParameter("RegionId", smsProperties.getRegionId()); request.putQueryParameter("RegionId", smsProperties.getRegionId());
request.putQueryParameter("PhoneNumbers", smsDto.getReceiver()); request.putQueryParameter("PhoneNumbers", smsDto.getReceiver());
request.putQueryParameter("SignName", smsProperties.getSignName());
request.putQueryParameter("TemplateCode", smsProperties.getTemplateCode());
request.putQueryParameter("TemplateParam", smsDto.getContent());
request.setVersion(smsProperties.getVersion());
request.setAction(smsProperties.getAction());
try { try {
CommonResponse response = client.getCommonResponse(request); CommonResponse response = client.getCommonResponse(request);
log.info("发送结果:{}", response.getData()); log.info("发送结果:{}", response.getData());
return response.getData(); if (response.getHttpStatus() != 200)
throw new CommonException(response.getData());
SmsResponse smsResponse = JsonMapper.getInstance().fromJson(response.getData(), SmsResponse.class);
if (smsResponse == null)
throw new CommonException("解析短信返回结果失败");
if (!"OK".equals(smsResponse.getCode()))
throw new CommonException(smsResponse.getMessage());
return smsResponse;
} catch (Exception e) { } catch (Exception e) {
log.error(e.getMessage(), e); log.error(e.getMessage(), e);
throw new CommonException("发送短信失败:" + e.getMessage());
} }
return null;
} }
} }
package com.github.tangyi.user.api.constant;
/**
* 用户学生常量
*
* @author tangyi
* @date 2019-07-25 13:08
*/
public class UserStudentConstant {
/**
* 爸爸
*/
public static final Integer RELATIONSHIP_TYPE_FATHER = 0;
/**
* 妈妈
*/
public static final Integer RELATIONSHIP_TYPE_MOTHER = 1;
/**
* 爷爷
*/
public static final Integer RELATIONSHIP_TYPE_GRAND_FATHER = 2;
/**
* 奶奶
*/
public static final Integer RELATIONSHIP_TYPE_GRAND_MOTHER = 3;
/**
* 外公
*/
public static final Integer RELATIONSHIP_TYPE_GRANDPA_FATHER = 5;
/**
* 外婆
*/
public static final Integer RELATIONSHIP_TYPE_GRANDMA_MOTHER = 6;
}
...@@ -13,7 +13,7 @@ import java.util.Date; ...@@ -13,7 +13,7 @@ import java.util.Date;
@Data @Data
public class StudentDto implements Serializable { public class StudentDto implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 2602249526687821147L;
private String id; private String id;
...@@ -74,9 +74,16 @@ public class StudentDto implements Serializable { ...@@ -74,9 +74,16 @@ public class StudentDto implements Serializable {
/** /**
* 就读年级 * 就读年级
*/ */
@ApiModelProperty(value = "就读年级") @ApiModelProperty(value = "就读年级ID")
private String grade; private String grade;
/**
* 就读年级
*/
@ApiModelProperty(value = "就读年级名称")
private String gradeName;
/** /**
* 就读学校 * 就读学校
*/ */
...@@ -84,14 +91,14 @@ public class StudentDto implements Serializable { ...@@ -84,14 +91,14 @@ public class StudentDto implements Serializable {
private String school; private String school;
/** /**
* 城市id * 就读城市
*/ */
@ApiModelProperty(value = "就读学校") @ApiModelProperty(value = "就读城市ID")
private String cityId; private String cityId;
/** /**
* 县id * 县id
*/ */
@ApiModelProperty(value = "县id") @ApiModelProperty(value = "就读区县ID")
private String countyId; private String countyId;
} }
\ No newline at end of file
...@@ -196,4 +196,10 @@ public class UserDto implements Serializable { ...@@ -196,4 +196,10 @@ public class UserDto implements Serializable {
*/ */
@ApiModelProperty(value = "微信") @ApiModelProperty(value = "微信")
private String wechat; private String wechat;
/**
* 家庭角色
*/
@ApiModelProperty(value = "家庭角色,0:爸爸,1:妈妈,2:爷爷,3:奶奶,5:外公,6:外婆", example = "0")
private Integer familyRole;
} }
...@@ -50,6 +50,11 @@ public class Student extends BaseEntity<Student> { ...@@ -50,6 +50,11 @@ public class Student extends BaseEntity<Student> {
private String grade; private String grade;
/** /**
* 就读班级名称
*/
private String gradeName;
/**
* 就读学校 * 就读学校
*/ */
private String school; private String school;
......
...@@ -113,4 +113,9 @@ public class User extends BaseEntity<User> { ...@@ -113,4 +113,9 @@ public class User extends BaseEntity<User> {
* 微信 * 微信
*/ */
private String wechat; private String wechat;
/**
* 家庭角色,参考UserStudentConstant
*/
private Integer familyRole;
} }
...@@ -120,6 +120,8 @@ public class StudentController extends BaseController { ...@@ -120,6 +120,8 @@ public class StudentController extends BaseController {
try { try {
Student student = new Student(); Student student = new Student();
BeanUtils.copyProperties(studentDto, student); BeanUtils.copyProperties(studentDto, student);
student.setId(studentDto.getId());
student.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), SysUtil.getTenantCode());
return new ResponseBean<>(studentService.update(student) > 0); return new ResponseBean<>(studentService.update(student) > 0);
} catch (Exception e) { } catch (Exception e) {
log.error("更新学生信息失败!", e); log.error("更新学生信息失败!", e);
......
...@@ -60,6 +60,6 @@ public class CustomGlobalExceptionHandler extends ResponseEntityExceptionHandler ...@@ -60,6 +60,6 @@ public class CustomGlobalExceptionHandler extends ResponseEntityExceptionHandler
responseBean.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); responseBean.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
responseBean.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value()); responseBean.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
responseBean.setMsg(e.getMessage()); responseBean.setMsg(e.getMessage());
return new ResponseEntity<>(responseBean, HttpStatus.INTERNAL_SERVER_ERROR); return new ResponseEntity<>(responseBean, HttpStatus.OK);
} }
} }
...@@ -43,23 +43,21 @@ public class MobileService { ...@@ -43,23 +43,21 @@ public class MobileService {
* @date 2019/07/02 09:36:52 * @date 2019/07/02 09:36:52
*/ */
public ResponseBean<Boolean> sendSms(String mobile, String tenantCode) { public ResponseBean<Boolean> sendSms(String mobile, String tenantCode) {
Object codeObj = redisTemplate.opsForValue().get(CommonConstant.DEFAULT_CODE_KEY + mobile); String key = tenantCode + ":" + CommonConstant.DEFAULT_CODE_KEY + LoginType.SMS.getType() + "@" + mobile;
if (codeObj != null) { // TODO 校验时间
log.info("手机号验证码未过期:{},{}", mobile, codeObj);
return new ResponseBean<>(Boolean.FALSE, "手机号未注册.");
}
String code = RandomUtil.randomNumbers(Integer.parseInt(CommonConstant.CODE_SIZE)); String code = RandomUtil.randomNumbers(Integer.parseInt(CommonConstant.CODE_SIZE));
log.debug("手机号生成验证码成功:{},{}", mobile, code); log.debug("手机号生成验证码成功:{},{}", mobile, code);
redisTemplate.opsForValue().set(tenantCode + ":" + CommonConstant.DEFAULT_CODE_KEY + LoginType.SMS.getType() + "@" + mobile redisTemplate.opsForValue().set(key, code, SecurityConstant.DEFAULT_SMS_EXPIRE, TimeUnit.SECONDS);
, code, SecurityConstant.DEFAULT_SMS_EXPIRE, TimeUnit.SECONDS);
// 调用消息中心服务,发送短信验证码 // 调用消息中心服务,发送短信验证码
SmsDto smsDto = new SmsDto(); SmsDto smsDto = new SmsDto();
smsDto.setReceiver(mobile); smsDto.setReceiver(mobile);
smsDto.setContent(String.format(SmsConstant.SMS_TEMPLATE, code)); smsDto.setContent(String.format(SmsConstant.SMS_TEMPLATE, code));
// ResponseBean<?> result = mscServiceClient.sendSms(smsDto); ResponseBean<?> result = mscServiceClient.sendSms(smsDto);
// if (result == null) if (result == null)
// throw new CommonException("发送失败."); throw new CommonException("发送短信失败.");
// log.info("发送验证码结果:{}", result.getData()); if (result.getCode() == ResponseBean.FAIL)
throw new CommonException(result.getMsg());
log.info("发送验证码结果:{}", result.getData());
return new ResponseBean<>(Boolean.TRUE, code); return new ResponseBean<>(Boolean.TRUE, code);
} }
} }
...@@ -4,6 +4,7 @@ import com.github.tangyi.common.core.exceptions.CommonException; ...@@ -4,6 +4,7 @@ import com.github.tangyi.common.core.exceptions.CommonException;
import com.github.tangyi.common.core.service.CrudService; import com.github.tangyi.common.core.service.CrudService;
import com.github.tangyi.common.core.utils.SysUtil; import com.github.tangyi.common.core.utils.SysUtil;
import com.github.tangyi.common.core.vo.UserVo; import com.github.tangyi.common.core.vo.UserVo;
import com.github.tangyi.user.api.constant.UserStudentConstant;
import com.github.tangyi.user.api.dto.StudentDto; import com.github.tangyi.user.api.dto.StudentDto;
import com.github.tangyi.user.api.module.Student; import com.github.tangyi.user.api.module.Student;
import com.github.tangyi.user.api.module.UserStudent; import com.github.tangyi.user.api.module.UserStudent;
...@@ -54,8 +55,9 @@ public class StudentService extends CrudService<StudentMapper, Student> { ...@@ -54,8 +55,9 @@ public class StudentService extends CrudService<StudentMapper, Student> {
userStudent.setCommonValue(currentUser, SysUtil.getSysCode(), tenantCode); userStudent.setCommonValue(currentUser, SysUtil.getSysCode(), tenantCode);
userStudent.setUserId(userId); userStudent.setUserId(userId);
userStudent.setStudentId(student.getId()); userStudent.setStudentId(student.getId());
// TODO // 默认关系类型是爸爸
userStudent.setRelationshipType(0); if (studentDto.getRelationshipType() == null)
userStudent.setRelationshipType(UserStudentConstant.RELATIONSHIP_TYPE_FATHER);
userStudentService.insert(userStudent); userStudentService.insert(userStudent);
// 保存学生 // 保存学生
return this.insert(student); return this.insert(student);
......
...@@ -86,6 +86,9 @@ public class UserService extends CrudService<UserMapper, User> { ...@@ -86,6 +86,9 @@ public class UserService extends CrudService<UserMapper, User> {
BeanUtils.copyProperties(userDto, user); BeanUtils.copyProperties(userDto, user);
// 先保存用户基本信息 // 先保存用户基本信息
user.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), SysUtil.getTenantCode()); user.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), SysUtil.getTenantCode());
// 保存父子账号关系
UserVo currentUser = this.findUserByIdentifier(userDto.getIdentityType(), SysUtil.getUser(), SysUtil.getTenantCode());
user.setParentUid(currentUser.getId());
if ((update = this.insert(user)) > 0) { if ((update = this.insert(user)) > 0) {
// 保存用户授权信息 // 保存用户授权信息
UserAuths userAuths = new UserAuths(); UserAuths userAuths = new UserAuths();
...@@ -608,8 +611,8 @@ public class UserService extends CrudService<UserMapper, User> { ...@@ -608,8 +611,8 @@ public class UserService extends CrudService<UserMapper, User> {
// 返回默认密码 // 返回默认密码
if (StringUtils.isBlank(encoded)) if (StringUtils.isBlank(encoded))
return CommonConstant.DEFAULT_PASSWORD; return CommonConstant.DEFAULT_PASSWORD;
// 微信注册不需要解密 // 微信、手机号注册不需要解密
if (IdentityType.WE_CHAT.getValue().equals(identityType)) if (IdentityType.WE_CHAT.getValue().equals(identityType) || IdentityType.PHONE_NUMBER.getValue().equals(identityType))
return encoded; return encoded;
// 解密密码 // 解密密码
try { try {
......
...@@ -9,10 +9,11 @@ ...@@ -9,10 +9,11 @@
<result column="sex" property="sex"/> <result column="sex" property="sex"/>
<result column="address" property="address"/> <result column="address" property="address"/>
<result column="grade" property="grade"/> <result column="grade" property="grade"/>
<result column="grade_name" property="gradeName"/>
<result column="school" property="school"/> <result column="school" property="school"/>
<result column="wechat" property="wechat"/> <result column="wechat" property="wechat"/>
<result column="cityId" property="city_id"/> <result column="city_id" property="cityId"/>
<result column="countyId" property="county_id"/> <result column="county_id" property="countyId"/>
<result column="creator" property="creator"/> <result column="creator" property="creator"/>
<result column="create_date" property="createDate" javaType="java.util.Date" jdbcType="TIMESTAMP"/> <result column="create_date" property="createDate" javaType="java.util.Date" jdbcType="TIMESTAMP"/>
<result column="modifier" property="modifier"/> <result column="modifier" property="modifier"/>
...@@ -28,10 +29,11 @@ ...@@ -28,10 +29,11 @@
a.sex, a.sex,
a.address, a.address,
a.grade, a.grade,
a.grade_name,
a.school, a.school,
a.wechat, a.wechat,
a.cityId, a.city_id,
a.countyId, a.county_id,
a.creator, a.creator,
a.create_date, a.create_date,
a.modifier, a.modifier,
...@@ -72,6 +74,7 @@ ...@@ -72,6 +74,7 @@
sex, sex,
address, address,
grade, grade,
grade_name,
school, school,
wechat, wechat,
city_id, city_id,
...@@ -89,6 +92,7 @@ ...@@ -89,6 +92,7 @@
#{sex}, #{sex},
#{address}, #{address},
#{grade}, #{grade},
#{gradeName},
#{school}, #{school},
#{wechat}, #{wechat},
#{cityId}, #{cityId},
...@@ -121,6 +125,9 @@ ...@@ -121,6 +125,9 @@
<if test="grade != null"> <if test="grade != null">
grade = #{grade}, grade = #{grade},
</if> </if>
<if test="gradeName != null">
grade_name = #{gradeName},
</if>
<if test="school != null"> <if test="school != null">
school = #{school}, school = #{school},
</if> </if>
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
<result column="login_time" property="loginTime" javaType="java.util.Date" jdbcType="TIMESTAMP"/> <result column="login_time" property="loginTime" javaType="java.util.Date" jdbcType="TIMESTAMP"/>
<result column="lock_time" property="lockTime" javaType="java.util.Date" jdbcType="TIMESTAMP"/> <result column="lock_time" property="lockTime" javaType="java.util.Date" jdbcType="TIMESTAMP"/>
<result column="wechat" property="wechat"/> <result column="wechat" property="wechat"/>
<result column="family_role" property="familyRole"/>
<result column="creator" property="creator"/> <result column="creator" property="creator"/>
<result column="create_date" property="createDate" javaType="java.util.Date" jdbcType="TIMESTAMP"/> <result column="create_date" property="createDate" javaType="java.util.Date" jdbcType="TIMESTAMP"/>
<result column="modifier" property="modifier"/> <result column="modifier" property="modifier"/>
...@@ -48,6 +49,7 @@ ...@@ -48,6 +49,7 @@
a.login_time, a.login_time,
a.lock_time, a.lock_time,
a.wechat, a.wechat,
a.family_role,
a.creator, a.creator,
a.create_date, a.create_date,
a.modifier, a.modifier,
...@@ -59,6 +61,9 @@ ...@@ -59,6 +61,9 @@
<!-- where 条件 --> <!-- where 条件 -->
<sql id="whereColumnList"> <sql id="whereColumnList">
<if test="parentUid != null and parentUid != ''">
and a.parent_uid = #{parentUid}
</if>
<if test="name != null and name != ''"> <if test="name != null and name != ''">
and a.name like CONCAT('%', #{name},'%') and a.name like CONCAT('%', #{name},'%')
</if> </if>
...@@ -130,6 +135,7 @@ ...@@ -130,6 +135,7 @@
login_time, login_time,
lock_time, lock_time,
wechat, wechat,
family_role,
creator, creator,
create_date, create_date,
modifier, modifier,
...@@ -156,6 +162,7 @@ ...@@ -156,6 +162,7 @@
#{loginTime, jdbcType=TIMESTAMP, javaType=java.util.Date}, #{loginTime, jdbcType=TIMESTAMP, javaType=java.util.Date},
#{lockTime, jdbcType=TIMESTAMP, javaType=java.util.Date}, #{lockTime, jdbcType=TIMESTAMP, javaType=java.util.Date},
#{wechat}, #{wechat},
#{familyRole},
#{creator}, #{creator},
#{createDate, jdbcType=TIMESTAMP, javaType=java.util.Date}, #{createDate, jdbcType=TIMESTAMP, javaType=java.util.Date},
#{modifier}, #{modifier},
...@@ -220,6 +227,9 @@ ...@@ -220,6 +227,9 @@
<if test="wechat != null"> <if test="wechat != null">
wechat = #{wechat} , wechat = #{wechat} ,
</if> </if>
<if test="familyRole != null">
family_role = #{familyRole} ,
</if>
<if test="creator != null"> <if test="creator != null">
creator = #{creator} , creator = #{creator} ,
</if> </if>
......
...@@ -71,7 +71,7 @@ ...@@ -71,7 +71,7 @@
<kaptcha.version>0.0.9</kaptcha.version> <kaptcha.version>0.0.9</kaptcha.version>
<json.version>20140107</json.version> <json.version>20140107</json.version>
<okhttp.version>3.8.1</okhttp.version> <okhttp.version>3.8.1</okhttp.version>
<aliyun.version>4.1.0</aliyun.version> <aliyun.version>4.0.3</aliyun.version>
<weixin.version>3.4.0</weixin.version> <weixin.version>3.4.0</weixin.version>
<!-- docker --> <!-- docker -->
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment