新增 AuthUtil 工具类,优化微信及会员服务实现
This commit is contained in:
+16
-10
@@ -8,7 +8,7 @@ import cn.novalon.gym.manage.member.service.MemberService;
|
|||||||
import cn.novalon.gym.manage.member.service.WechatAuthService;
|
import cn.novalon.gym.manage.member.service.WechatAuthService;
|
||||||
import cn.novalon.gym.manage.member.service.WechatOfficialService;
|
import cn.novalon.gym.manage.member.service.WechatOfficialService;
|
||||||
import cn.novalon.gym.manage.member.util.AesUtil;
|
import cn.novalon.gym.manage.member.util.AesUtil;
|
||||||
import cn.novalon.gym.manage.member.util.AuthUtil;
|
import cn.novalon.gym.manage.sys.util.AuthUtil;
|
||||||
import cn.novalon.gym.manage.sys.security.JwtTokenProvider;
|
import cn.novalon.gym.manage.sys.security.JwtTokenProvider;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -42,7 +42,7 @@ public class MemberHandler {
|
|||||||
* 获取会员信息
|
* 获取会员信息
|
||||||
*
|
*
|
||||||
* GET /api/member/info
|
* GET /api/member/info
|
||||||
* Header: X-Member-Id: 123
|
* header: { "Authorization": "Bearer xxx" }
|
||||||
*/
|
*/
|
||||||
public Mono<ServerResponse> getMemberInfo(ServerRequest request) {
|
public Mono<ServerResponse> getMemberInfo(ServerRequest request) {
|
||||||
|
|
||||||
@@ -51,18 +51,16 @@ public class MemberHandler {
|
|||||||
log.info("获取会员信息, memberId: {}", memberId);
|
log.info("获取会员信息, memberId: {}", memberId);
|
||||||
|
|
||||||
return memberService.getMemberInfo(memberId)
|
return memberService.getMemberInfo(memberId)
|
||||||
.flatMap(info -> {
|
.flatMap(info -> ServerResponse.ok()
|
||||||
return ServerResponse.ok()
|
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.bodyValue(info);
|
.bodyValue(info));
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新会员信息
|
* 更新会员信息
|
||||||
*
|
*
|
||||||
* PUT /api/member/info
|
* PUT /api/member/info
|
||||||
* Header: X-Member-Id: 123
|
* header: { "Authorization": "Bearer xxx" }
|
||||||
* Body: {
|
* Body: {
|
||||||
* "nickname": "新昵称",
|
* "nickname": "新昵称",
|
||||||
* "gender": 1,
|
* "gender": 1,
|
||||||
@@ -86,9 +84,8 @@ public class MemberHandler {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 绑定手机号(微信小程序)
|
* 绑定手机号(微信小程序)
|
||||||
*
|
* header: { "Authorization": "Bearer xxx" }
|
||||||
* POST /api/member/phone/bind?code=PHONE_CODE
|
* POST /api/member/phone/bind?code=PHONE_CODE
|
||||||
* Header: X-Member-Id: 123
|
|
||||||
*/
|
*/
|
||||||
public Mono<ServerResponse> bindPhone(ServerRequest request) {
|
public Mono<ServerResponse> bindPhone(ServerRequest request) {
|
||||||
|
|
||||||
@@ -110,7 +107,6 @@ public class MemberHandler {
|
|||||||
* 查询服务号关注状态
|
* 查询服务号关注状态
|
||||||
*
|
*
|
||||||
* GET /api/member/subscribe/status
|
* GET /api/member/subscribe/status
|
||||||
* Header: X-Member-Id: 123
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public Mono<ServerResponse> checkSubscribeStatus(ServerRequest request) {
|
public Mono<ServerResponse> checkSubscribeStatus(ServerRequest request) {
|
||||||
@@ -131,6 +127,7 @@ public class MemberHandler {
|
|||||||
* 管理员更新手机号
|
* 管理员更新手机号
|
||||||
*
|
*
|
||||||
* POST /api/admin/member/123/phone
|
* POST /api/admin/member/123/phone
|
||||||
|
* header: { "Authorization": "Bearer xxx" }
|
||||||
* Body: { "phone": "13800138000" }
|
* Body: { "phone": "13800138000" }
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@@ -180,6 +177,8 @@ public class MemberHandler {
|
|||||||
long memberId = NumberUtils.toLong(memberIdStr, 0L);
|
long memberId = NumberUtils.toLong(memberIdStr, 0L);
|
||||||
if(memberId <= 0) throw new IllegalArgumentException("会员ID格式错误");
|
if(memberId <= 0) throw new IllegalArgumentException("会员ID格式错误");
|
||||||
|
|
||||||
|
log.info("前台查看会员信息, adminId: {}, memberId: {}", adminId, memberId);
|
||||||
|
|
||||||
// TODO 多表查询:会员信息、团课信息、会员卡信息
|
// TODO 多表查询:会员信息、团课信息、会员卡信息
|
||||||
|
|
||||||
return ServerResponse.ok()
|
return ServerResponse.ok()
|
||||||
@@ -202,6 +201,8 @@ public class MemberHandler {
|
|||||||
long memberId = NumberUtils.toLong(memberIdStr, 0L);
|
long memberId = NumberUtils.toLong(memberIdStr, 0L);
|
||||||
if(memberId <= 0L) throw new IllegalArgumentException("会员ID格式错误");
|
if(memberId <= 0L) throw new IllegalArgumentException("会员ID格式错误");
|
||||||
|
|
||||||
|
log.info("前台编辑会员信息, adminId: {}, memberId: {}", adminId, memberId);
|
||||||
|
|
||||||
// TODO 多表查询:会员信息、团课信息、会员卡信息
|
// TODO 多表查询:会员信息、团课信息、会员卡信息
|
||||||
|
|
||||||
return ServerResponse.ok()
|
return ServerResponse.ok()
|
||||||
@@ -224,6 +225,9 @@ public class MemberHandler {
|
|||||||
int pageNum = NumberUtils.toInt(request.queryParam("pageNum").orElse("1"), 1);
|
int pageNum = NumberUtils.toInt(request.queryParam("pageNum").orElse("1"), 1);
|
||||||
int pageSize = NumberUtils.toInt(request.queryParam("pageSize").orElse("10"), 10);
|
int pageSize = NumberUtils.toInt(request.queryParam("pageSize").orElse("10"), 10);
|
||||||
|
|
||||||
|
log.info("前台搜索会员列表, adminId: {}, keyword: {}, filter: {}, pageNum: {}, pageSize: {}",
|
||||||
|
adminId, keyword, filter, pageNum, pageSize);
|
||||||
|
|
||||||
return memberService.searchMember(new SearchMemberDto(keyword, filter, pageNum, pageSize))
|
return memberService.searchMember(new SearchMemberDto(keyword, filter, pageNum, pageSize))
|
||||||
.map(member -> {
|
.map(member -> {
|
||||||
// 解密手机号
|
// 解密手机号
|
||||||
@@ -258,6 +262,8 @@ public class MemberHandler {
|
|||||||
int pageNum = NumberUtils.toInt(request.queryParam("pageNum").orElse("1"), 1);
|
int pageNum = NumberUtils.toInt(request.queryParam("pageNum").orElse("1"), 1);
|
||||||
int pageSize = NumberUtils.toInt(request.queryParam("pageSize").orElse("10"), 10);
|
int pageSize = NumberUtils.toInt(request.queryParam("pageSize").orElse("10"), 10);
|
||||||
|
|
||||||
|
log.info("前台查看会员列表, adminId: {}, pageNum: {}, pageSize: {}", adminId, pageNum, pageSize);
|
||||||
|
|
||||||
return memberService.findAll(pageNum, pageSize)
|
return memberService.findAll(pageNum, pageSize)
|
||||||
.map(member -> {
|
.map(member -> {
|
||||||
// 解密手机号
|
// 解密手机号
|
||||||
|
|||||||
+30
-35
@@ -1,5 +1,9 @@
|
|||||||
package cn.novalon.gym.manage.member.service.impl;
|
package cn.novalon.gym.manage.member.service.impl;
|
||||||
|
|
||||||
|
import cn.novalon.gym.manage.common.exception.ConflictException;
|
||||||
|
import cn.novalon.gym.manage.common.exception.ErrorCode;
|
||||||
|
import cn.novalon.gym.manage.common.exception.NotFoundException;
|
||||||
|
import cn.novalon.gym.manage.common.exception.SystemException;
|
||||||
import cn.novalon.gym.manage.member.config.WechatProperties;
|
import cn.novalon.gym.manage.member.config.WechatProperties;
|
||||||
import cn.novalon.gym.manage.member.dto.SearchMemberDto;
|
import cn.novalon.gym.manage.member.dto.SearchMemberDto;
|
||||||
import cn.novalon.gym.manage.member.dto.UpdateMemberInfoDto;
|
import cn.novalon.gym.manage.member.dto.UpdateMemberInfoDto;
|
||||||
@@ -14,13 +18,10 @@ import cn.novalon.gym.manage.member.vo.MemberInfoVO;
|
|||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.data.domain.PageRequest;
|
import org.springframework.data.domain.PageRequest;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.data.domain.Sort;
|
import org.springframework.data.domain.Sort;
|
||||||
import org.springframework.http.HttpStatus;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.web.server.ResponseStatusException;
|
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
@@ -49,16 +50,14 @@ public class MemberServiceImpl implements MemberService {
|
|||||||
this.memberSyncer = esSyncUtils.bind(Member.class, MemberES.class, memberESRepository);
|
this.memberSyncer = esSyncUtils.bind(Member.class, MemberES.class, memberESRepository);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Value("${wechat.aes.secret-key:}")
|
|
||||||
private String aesSecretKey;
|
|
||||||
|
|
||||||
@Value("${wechat.aes.iv:}")
|
|
||||||
private String aesIv;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<MemberInfoVO> getMemberInfo(Long memberId) {
|
public Mono<MemberInfoVO> getMemberInfo(Long memberId) {
|
||||||
return memberRepository.findById(memberId)
|
return memberRepository.findById(memberId)
|
||||||
.map(this::buildMemberInfoResponse);
|
.map(this::buildMemberInfoResponse)
|
||||||
|
.switchIfEmpty(Mono.error(() -> {
|
||||||
|
log.error("会员不存在: memberId={}", memberId);
|
||||||
|
throw new NotFoundException(ErrorCode.NOT_FOUND_USER, "会员不存在");
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -66,10 +65,6 @@ public class MemberServiceImpl implements MemberService {
|
|||||||
log.info("会员更新个人信息, memberId: {}", memberId);
|
log.info("会员更新个人信息, memberId: {}", memberId);
|
||||||
|
|
||||||
return memberRepository.findById(memberId)
|
return memberRepository.findById(memberId)
|
||||||
.switchIfEmpty(Mono.defer(() -> {
|
|
||||||
log.error("会员不存在: memberId={}", memberId);
|
|
||||||
return Mono.error(new ResponseStatusException(HttpStatus.NOT_FOUND, "会员不存在"));
|
|
||||||
}))
|
|
||||||
.flatMap(member -> {
|
.flatMap(member -> {
|
||||||
if (updateDto.getNickname() != null) {
|
if (updateDto.getNickname() != null) {
|
||||||
member.setNickname(updateDto.getNickname());
|
member.setNickname(updateDto.getNickname());
|
||||||
@@ -93,10 +88,13 @@ public class MemberServiceImpl implements MemberService {
|
|||||||
.map(savedMember -> {
|
.map(savedMember -> {
|
||||||
log.info("会员信息更新成功, memberId: {}", savedMember.getId());
|
log.info("会员信息更新成功, memberId: {}", savedMember.getId());
|
||||||
return buildMemberInfoResponse(savedMember);
|
return buildMemberInfoResponse(savedMember);
|
||||||
});
|
})
|
||||||
|
.switchIfEmpty(Mono.error(() -> {
|
||||||
|
log.error("会员不存在: memberId={}", memberId);
|
||||||
|
throw new NotFoundException(ErrorCode.NOT_FOUND_USER, "会员不存在");
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 会员信息响应
|
|
||||||
private MemberInfoVO buildMemberInfoResponse(Member member) {
|
private MemberInfoVO buildMemberInfoResponse(Member member) {
|
||||||
String phone = member.getPhone();
|
String phone = member.getPhone();
|
||||||
String maskedPhone = phone != null ? phone.replace(phone.substring(3, 7), "****") : null;
|
String maskedPhone = phone != null ? phone.replace(phone.substring(3, 7), "****") : null;
|
||||||
@@ -119,31 +117,24 @@ public class MemberServiceImpl implements MemberService {
|
|||||||
|
|
||||||
String encryptedPhone;
|
String encryptedPhone;
|
||||||
try {
|
try {
|
||||||
encryptedPhone = AesUtil.encrypt(phone, aesSecretKey, aesIv);
|
String secretKey = wechatProperties.getPhoneEncryption().getSecretKey();
|
||||||
|
String iv = wechatProperties.getPhoneEncryption().getIv();
|
||||||
|
encryptedPhone = AesUtil.encrypt(phone, secretKey, iv);
|
||||||
log.info("手机号加密成功");
|
log.info("手机号加密成功");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("手机号加密失败", e);
|
log.error("手机号加密失败", e);
|
||||||
return Mono.error(new ResponseStatusException(
|
throw new SystemException(ErrorCode.SYSTEM_INTERNAL_ERROR, "手机号加密失败: " + e.getMessage());
|
||||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
|
||||||
"手机号加密失败: " + e.getMessage()
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return memberRepository.findByPhone(encryptedPhone)
|
return memberRepository.findByPhone(encryptedPhone)
|
||||||
.<Boolean>flatMap(existingMember -> {
|
.<Boolean>flatMap(existingMember -> {
|
||||||
if (existingMember.getId().equals(memberId)) {
|
if (existingMember.getId().equals(memberId)) {
|
||||||
log.warn("手机号已是当前用户的: memberId={}", memberId);
|
log.warn("手机号已是当前用户的: memberId={}", memberId);
|
||||||
return Mono.error(new ResponseStatusException(
|
throw new ConflictException(ErrorCode.CONFLICT_DUPLICATE_USER, "重复绑定");
|
||||||
HttpStatus.CONFLICT,
|
|
||||||
"重复绑定"
|
|
||||||
));
|
|
||||||
} else {
|
} else {
|
||||||
log.warn("手机号已被其他用户绑定: memberId={}, existingMemberId={}",
|
log.warn("手机号已被其他用户绑定: memberId={}, existingMemberId={}",
|
||||||
memberId, existingMember.getId());
|
memberId, existingMember.getId());
|
||||||
return Mono.error(new ResponseStatusException(
|
throw new ConflictException(ErrorCode.CONFLICT_DUPLICATE_USER, "该手机号已被其他会员绑定");
|
||||||
HttpStatus.CONFLICT,
|
|
||||||
"该手机号已被其他会员绑定"
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.switchIfEmpty(Mono.defer(() -> {
|
.switchIfEmpty(Mono.defer(() -> {
|
||||||
@@ -154,24 +145,27 @@ public class MemberServiceImpl implements MemberService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Flux<MemberES> searchMember(SearchMemberDto searchMemberDto) {
|
public Flux<MemberES> searchMember(SearchMemberDto searchMemberDto) {
|
||||||
|
log.info("搜索会员, searchValue: {}, filter: {}, pageNum: {}, pageSize: {}",
|
||||||
|
searchMemberDto.getSearchValue(),
|
||||||
|
searchMemberDto.getFilter(),
|
||||||
|
searchMemberDto.getPageNum(),
|
||||||
|
searchMemberDto.getPageSize());
|
||||||
|
|
||||||
String searchValue = searchMemberDto.getSearchValue();
|
String searchValue = searchMemberDto.getSearchValue();
|
||||||
|
|
||||||
// 1. 处理手机号加密
|
|
||||||
if(searchValue != null && searchValue.matches("^1[3-9]\\d{9}$")){
|
if(searchValue != null && searchValue.matches("^1[3-9]\\d{9}$")){
|
||||||
|
log.debug("搜索值为手机号格式,进行加密处理");
|
||||||
String secretKey = wechatProperties.getPhoneEncryption().getSecretKey();
|
String secretKey = wechatProperties.getPhoneEncryption().getSecretKey();
|
||||||
String iv = wechatProperties.getPhoneEncryption().getIv();
|
String iv = wechatProperties.getPhoneEncryption().getIv();
|
||||||
searchValue = AesUtil.encrypt(searchValue,secretKey,iv);
|
searchValue = AesUtil.encrypt(searchValue,secretKey,iv);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 分页参数
|
|
||||||
Pageable pageable = PageRequest.of(
|
Pageable pageable = PageRequest.of(
|
||||||
searchMemberDto.getPageNum() - 1,
|
searchMemberDto.getPageNum() - 1,
|
||||||
searchMemberDto.getPageSize(),
|
searchMemberDto.getPageSize(),
|
||||||
Sort.by(Sort.Direction.DESC, "update_at")
|
Sort.by(Sort.Direction.DESC, "update_at")
|
||||||
);
|
);
|
||||||
|
|
||||||
// 3. 调用 Repository 查询
|
|
||||||
return memberESRepository.findByMemberNoOrPhoneOrNicknameContainingAndGender(
|
return memberESRepository.findByMemberNoOrPhoneOrNicknameContainingAndGender(
|
||||||
searchValue,
|
searchValue,
|
||||||
searchValue,
|
searchValue,
|
||||||
@@ -183,6 +177,8 @@ public class MemberServiceImpl implements MemberService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Flux<Member> findAll(Integer pageNum, Integer pageSize) {
|
public Flux<Member> findAll(Integer pageNum, Integer pageSize) {
|
||||||
|
log.info("查询所有会员列表, pageNum: {}, pageSize: {}", pageNum, pageSize);
|
||||||
|
|
||||||
Pageable pageable = PageRequest.of(
|
Pageable pageable = PageRequest.of(
|
||||||
pageNum - 1,
|
pageNum - 1,
|
||||||
pageSize
|
pageSize
|
||||||
@@ -191,7 +187,6 @@ public class MemberServiceImpl implements MemberService {
|
|||||||
return memberRepository.findAllBy(pageable);
|
return memberRepository.findAllBy(pageable);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新会员手机号
|
|
||||||
private Mono<Boolean> updateMemberPhone(Long memberId, String encryptedPhone) {
|
private Mono<Boolean> updateMemberPhone(Long memberId, String encryptedPhone) {
|
||||||
return memberRepository.findById(memberId)
|
return memberRepository.findById(memberId)
|
||||||
.flatMap(member -> {
|
.flatMap(member -> {
|
||||||
@@ -205,9 +200,9 @@ public class MemberServiceImpl implements MemberService {
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.switchIfEmpty(Mono.defer(() -> {
|
.switchIfEmpty(Mono.error(() -> {
|
||||||
log.error("会员不存在: memberId={}", memberId);
|
log.error("会员不存在: memberId={}", memberId);
|
||||||
return Mono.error(new ResponseStatusException(HttpStatus.NOT_FOUND, "会员不存在"));
|
throw new NotFoundException(ErrorCode.NOT_FOUND_USER, "会员不存在");
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-53
@@ -1,5 +1,7 @@
|
|||||||
package cn.novalon.gym.manage.member.service.impl;
|
package cn.novalon.gym.manage.member.service.impl;
|
||||||
|
|
||||||
|
import cn.novalon.gym.manage.common.exception.ErrorCode;
|
||||||
|
import cn.novalon.gym.manage.common.exception.SystemException;
|
||||||
import cn.novalon.gym.manage.member.config.WechatProperties;
|
import cn.novalon.gym.manage.member.config.WechatProperties;
|
||||||
import cn.novalon.gym.manage.member.service.WechatApiService;
|
import cn.novalon.gym.manage.member.service.WechatApiService;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@@ -29,21 +31,11 @@ public class WechatApiServiceImpl implements WechatApiService {
|
|||||||
|
|
||||||
private final WechatProperties wechatProperties;
|
private final WechatProperties wechatProperties;
|
||||||
|
|
||||||
/**
|
|
||||||
* WebClient实例 - 用于发送HTTP请求
|
|
||||||
* 最大内存大小为10MB
|
|
||||||
*/
|
|
||||||
private final WebClient webClient = WebClient.builder()
|
private final WebClient webClient = WebClient.builder()
|
||||||
.baseUrl("https://api.weixin.qq.com")
|
.baseUrl("https://api.weixin.qq.com")
|
||||||
.codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(10 * 1024 * 1024))
|
.codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(10 * 1024 * 1024))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
/**
|
|
||||||
* 小程序
|
|
||||||
*
|
|
||||||
* @param code 小程序登录的code
|
|
||||||
* @return Mono<Map<String, String>> session_keyopenidunionid响应格式
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<Map<String, String>> jsCode2Session(String code) {
|
public Mono<Map<String, String>> jsCode2Session(String code) {
|
||||||
log.info("微信jsCode2Session API");
|
log.info("微信jsCode2Session API");
|
||||||
@@ -54,7 +46,6 @@ public class WechatApiServiceImpl implements WechatApiService {
|
|||||||
log.info(" - code: {}", code);
|
log.info(" - code: {}", code);
|
||||||
|
|
||||||
return webClient.get()
|
return webClient.get()
|
||||||
// 构建URI
|
|
||||||
.uri(uriBuilder -> uriBuilder
|
.uri(uriBuilder -> uriBuilder
|
||||||
.path("/sns/jscode2session")
|
.path("/sns/jscode2session")
|
||||||
.queryParam("appid", wechatProperties.getMiniapp().getAppId())
|
.queryParam("appid", wechatProperties.getMiniapp().getAppId())
|
||||||
@@ -62,35 +53,29 @@ public class WechatApiServiceImpl implements WechatApiService {
|
|||||||
.queryParam("js_code", code)
|
.queryParam("js_code", code)
|
||||||
.queryParam("grant_type", "authorization_code")
|
.queryParam("grant_type", "authorization_code")
|
||||||
.build())
|
.build())
|
||||||
// 获取响应
|
|
||||||
.retrieve()
|
.retrieve()
|
||||||
.bodyToMono(String.class)
|
.bodyToMono(String.class)
|
||||||
// 处理响应
|
|
||||||
.map(responseBody -> {
|
.map(responseBody -> {
|
||||||
log.info("微信API响应: {}", responseBody);
|
log.info("微信API响应: {}", responseBody);
|
||||||
|
|
||||||
// 解析JSON响应
|
|
||||||
Map<String, Object> response;
|
Map<String, Object> response;
|
||||||
try {
|
try {
|
||||||
// 使用Jackson解析JSON响应
|
|
||||||
com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();
|
com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();
|
||||||
response = mapper.readValue(responseBody, Map.class);
|
response = mapper.readValue(responseBody, Map.class);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("微信API响应失败", e);
|
log.error("微信API响应失败", e);
|
||||||
throw new RuntimeException("微信API响应失败: " + e.getMessage());
|
throw new SystemException(ErrorCode.SYSTEM_INTERNAL_ERROR, "微信API响应失败: " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, String> result = new HashMap<>();
|
Map<String, String> result = new HashMap<>();
|
||||||
|
|
||||||
// 检查错误码
|
|
||||||
if (response.containsKey("errcode")) {
|
if (response.containsKey("errcode")) {
|
||||||
Integer errcode = (Integer) response.get("errcode");
|
Integer errcode = (Integer) response.get("errcode");
|
||||||
String errmsg = (String) response.get("errmsg");
|
String errmsg = (String) response.get("errmsg");
|
||||||
log.error("微信API失败, errcode: {}, errmsg: {}", errcode, errmsg);
|
log.error("微信API失败, errcode: {}, errmsg: {}", errcode, errmsg);
|
||||||
throw new RuntimeException("微信API失败 [" + errcode + "]: " + errmsg);
|
throw new SystemException(ErrorCode.SYSTEM_INTERNAL_ERROR, "微信API失败 [" + errcode + "]: " + errmsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取session_keyopenidunionid
|
|
||||||
result.put("session_key", (String) response.get("session_key"));
|
result.put("session_key", (String) response.get("session_key"));
|
||||||
result.put("openid", (String) response.get("openid"));
|
result.put("openid", (String) response.get("openid"));
|
||||||
result.put("unionid", (String) response.get("unionid"));
|
result.put("unionid", (String) response.get("unionid"));
|
||||||
@@ -99,7 +84,6 @@ public class WechatApiServiceImpl implements WechatApiService {
|
|||||||
result.get("openid"), result.get("unionid"));
|
result.get("openid"), result.get("unionid"));
|
||||||
return result;
|
return result;
|
||||||
})
|
})
|
||||||
// 异常处理
|
|
||||||
.onErrorResume(e -> {
|
.onErrorResume(e -> {
|
||||||
log.error("微信API响应异常 - URL: https://api.weixin.qq.com/sns/jscode2session");
|
log.error("微信API响应异常 - URL: https://api.weixin.qq.com/sns/jscode2session");
|
||||||
log.error("异常: {}", e.getClass().getName());
|
log.error("异常: {}", e.getClass().getName());
|
||||||
@@ -107,16 +91,13 @@ public class WechatApiServiceImpl implements WechatApiService {
|
|||||||
if (e.getCause() != null) {
|
if (e.getCause() != null) {
|
||||||
log.error("异常原因: {}", e.getCause().getMessage());
|
log.error("异常原因: {}", e.getCause().getMessage());
|
||||||
}
|
}
|
||||||
return Mono.error(new RuntimeException("微信API响应异常 " + e.getMessage()));
|
if (e instanceof SystemException) {
|
||||||
|
return Mono.error(e);
|
||||||
|
}
|
||||||
|
throw new SystemException(ErrorCode.SYSTEM_INTERNAL_ERROR, "微信API响应异常 " + e.getMessage());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取手机号
|
|
||||||
*
|
|
||||||
* @param code 手机号或获取手机号的code
|
|
||||||
* @return Mono<String> 手机号
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<String> getPhoneNumber(String code) {
|
public Mono<String> getPhoneNumber(String code) {
|
||||||
log.debug("微信getPhoneNumber API, code: {}", code);
|
log.debug("微信getPhoneNumber API, code: {}", code);
|
||||||
@@ -149,31 +130,23 @@ public class WechatApiServiceImpl implements WechatApiService {
|
|||||||
} else {
|
} else {
|
||||||
String errmsg = (String) response.get("errmsg");
|
String errmsg = (String) response.get("errmsg");
|
||||||
log.error("获取手机号失败 {}", errmsg);
|
log.error("获取手机号失败 {}", errmsg);
|
||||||
throw new RuntimeException("获取手机号失败 " + errmsg);
|
throw new SystemException(ErrorCode.SYSTEM_INTERNAL_ERROR, "获取手机号失败 " + errmsg);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
//
|
|
||||||
.onErrorResume(e -> {
|
.onErrorResume(e -> {
|
||||||
log.error("获取手机号失败", e);
|
log.error("获取手机号失败", e);
|
||||||
return Mono.error(new RuntimeException("获取手机号失败 " + e.getMessage()));
|
if (e instanceof SystemException) {
|
||||||
|
return Mono.error(e);
|
||||||
|
}
|
||||||
|
throw new SystemException(ErrorCode.SYSTEM_INTERNAL_ERROR, "获取手机号失败 " + e.getMessage());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取Access Token
|
|
||||||
*
|
|
||||||
* @param appType 应用类型
|
|
||||||
* @return Mono<String> access_token
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<String> getAccessToken(String appType) {
|
public Mono<String> getAccessToken(String appType) {
|
||||||
log.debug("获取access_token, appType: {}", appType);
|
log.debug("获取access_token, appType: {}", appType);
|
||||||
|
|
||||||
// TODO: 实现缓存逻辑
|
|
||||||
// 使用 Caffeine 或 Redis 缓存
|
|
||||||
// 目前直接调用微信API
|
|
||||||
|
|
||||||
String appId, appSecret;
|
String appId, appSecret;
|
||||||
if ("miniapp".equals(appType)) {
|
if ("miniapp".equals(appType)) {
|
||||||
appId = wechatProperties.getMiniapp().getAppId();
|
appId = wechatProperties.getMiniapp().getAppId();
|
||||||
@@ -197,27 +170,15 @@ public class WechatApiServiceImpl implements WechatApiService {
|
|||||||
String accessToken = (String) response.get("access_token");
|
String accessToken = (String) response.get("access_token");
|
||||||
Integer expiresIn = (Integer) response.get("expires_in");
|
Integer expiresIn = (Integer) response.get("expires_in");
|
||||||
log.info("获取access_token成功, expires_in: {}s", expiresIn);
|
log.info("获取access_token成功, expires_in: {}s", expiresIn);
|
||||||
|
|
||||||
// TODO: 加入缓存
|
|
||||||
// cache.put("wechat:token:" + appType, accessToken, expiresIn - 200, TimeUnit.SECONDS);
|
|
||||||
|
|
||||||
return accessToken;
|
return accessToken;
|
||||||
} else {
|
} else {
|
||||||
String errmsg = (String) response.get("errmsg");
|
String errmsg = (String) response.get("errmsg");
|
||||||
log.error("获取access_token失败: {}", errmsg);
|
log.error("获取access_token失败: {}", errmsg);
|
||||||
throw new RuntimeException("获取access_token失败: " + errmsg);
|
throw new SystemException(ErrorCode.SYSTEM_INTERNAL_ERROR, "获取access_token失败: " + errmsg);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 验证微信消息签名
|
|
||||||
*
|
|
||||||
* @param signature 微信消息签名
|
|
||||||
* @param timestamp 创建时间戳
|
|
||||||
* @param nonce 随机字符串
|
|
||||||
* @return boolean true-签名有效false-签名无效
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean checkSignature(String signature, String timestamp, String nonce) {
|
public boolean checkSignature(String signature, String timestamp, String nonce) {
|
||||||
log.debug("验证微信消息签名, signature: {}, timestamp: {}, nonce: {}",
|
log.debug("验证微信消息签名, signature: {}, timestamp: {}, nonce: {}",
|
||||||
|
|||||||
+21
-70
@@ -1,5 +1,9 @@
|
|||||||
package cn.novalon.gym.manage.member.service.impl;
|
package cn.novalon.gym.manage.member.service.impl;
|
||||||
|
|
||||||
|
import cn.novalon.gym.manage.common.exception.ConflictException;
|
||||||
|
import cn.novalon.gym.manage.common.exception.ErrorCode;
|
||||||
|
import cn.novalon.gym.manage.common.exception.NotFoundException;
|
||||||
|
import cn.novalon.gym.manage.common.exception.SystemException;
|
||||||
import cn.novalon.gym.manage.member.config.WechatProperties;
|
import cn.novalon.gym.manage.member.config.WechatProperties;
|
||||||
import cn.novalon.gym.manage.member.dto.WechatLoginDto;
|
import cn.novalon.gym.manage.member.dto.WechatLoginDto;
|
||||||
import cn.novalon.gym.manage.member.entity.Member;
|
import cn.novalon.gym.manage.member.entity.Member;
|
||||||
@@ -52,12 +56,6 @@ public class WechatAuthServiceImpl implements WechatAuthService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 小程序登录 - 通过微信 code 完成登录
|
|
||||||
*
|
|
||||||
* @param request 微信登录请求
|
|
||||||
* @return Mono<WechatLoginVO> 微信登录响应
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<WechatLoginVO> miniappLogin(WechatLoginDto request) {
|
public Mono<WechatLoginVO> miniappLogin(WechatLoginDto request) {
|
||||||
log.info("开始小程序登录");
|
log.info("开始小程序登录");
|
||||||
@@ -140,17 +138,13 @@ public class WechatAuthServiceImpl implements WechatAuthService {
|
|||||||
})
|
})
|
||||||
.onErrorResume(e -> {
|
.onErrorResume(e -> {
|
||||||
log.error("小程序登录失败", e);
|
log.error("小程序登录失败", e);
|
||||||
return Mono.error(new RuntimeException("登录失败: " + e.getMessage()));
|
if (e instanceof SystemException) {
|
||||||
|
return Mono.error(e);
|
||||||
|
}
|
||||||
|
return Mono.error(new SystemException(ErrorCode.SYSTEM_INTERNAL_ERROR, "登录失败: " + e.getMessage()));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 绑定手机号 - 通过微信 code 获取并绑定手机号
|
|
||||||
*
|
|
||||||
* @param memberId 会员 ID
|
|
||||||
* @param code 微信手机号 code
|
|
||||||
* @return Mono<Boolean>
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<Boolean> bindPhone(Long memberId, String code) {
|
public Mono<Boolean> bindPhone(Long memberId, String code) {
|
||||||
log.info("开始绑定手机号, memberId: {}", memberId);
|
log.info("开始绑定手机号, memberId: {}", memberId);
|
||||||
@@ -165,7 +159,7 @@ public class WechatAuthServiceImpl implements WechatAuthService {
|
|||||||
.flatMap(existingMember -> {
|
.flatMap(existingMember -> {
|
||||||
if (!existingMember.getId().equals(memberId)) {
|
if (!existingMember.getId().equals(memberId)) {
|
||||||
log.warn("手机号已被其他会员绑定, currentMemberId={}, existingMemberId={}", memberId, existingMember.getId());
|
log.warn("手机号已被其他会员绑定, currentMemberId={}, existingMemberId={}", memberId, existingMember.getId());
|
||||||
return Mono.error(new RuntimeException("手机号已被其他会员绑定"));
|
throw new ConflictException(ErrorCode.CONFLICT_DUPLICATE_USER, "手机号已被其他会员绑定");
|
||||||
} else {
|
} else {
|
||||||
log.info("更新会员手机号, memberId: {}", memberId);
|
log.info("更新会员手机号, memberId: {}", memberId);
|
||||||
return updateMemberPhone(memberId, encryptedPhone);
|
return updateMemberPhone(memberId, encryptedPhone);
|
||||||
@@ -178,17 +172,13 @@ public class WechatAuthServiceImpl implements WechatAuthService {
|
|||||||
})
|
})
|
||||||
.onErrorResume(e -> {
|
.onErrorResume(e -> {
|
||||||
log.error("绑定手机号失败", e);
|
log.error("绑定手机号失败", e);
|
||||||
return Mono.error(new RuntimeException("绑定失败: " + e.getMessage()));
|
if (e instanceof SystemException) {
|
||||||
|
return Mono.error(e);
|
||||||
|
}
|
||||||
|
return Mono.error(new SystemException(ErrorCode.SYSTEM_INTERNAL_ERROR, "绑定失败: " + e.getMessage()));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新会员手机号
|
|
||||||
*
|
|
||||||
* @param memberId 会员 ID
|
|
||||||
* @param encryptedPhone 加密后的手机号(Base64 编码)
|
|
||||||
* @return Mono<Boolean> 是否更新成功
|
|
||||||
*/
|
|
||||||
private Mono<Boolean> updateMemberPhone(Long memberId, String encryptedPhone) {
|
private Mono<Boolean> updateMemberPhone(Long memberId, String encryptedPhone) {
|
||||||
return memberRepository.findById(memberId)
|
return memberRepository.findById(memberId)
|
||||||
.flatMap(member -> {
|
.flatMap(member -> {
|
||||||
@@ -201,18 +191,12 @@ public class WechatAuthServiceImpl implements WechatAuthService {
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.switchIfEmpty(Mono.defer(() -> {
|
.switchIfEmpty(Mono.error(() -> {
|
||||||
log.error("会员不存在, memberId={}", memberId);
|
log.error("会员不存在, memberId={}", memberId);
|
||||||
return Mono.error(new RuntimeException("会员不存在"));
|
throw new NotFoundException(ErrorCode.NOT_FOUND_USER, "会员不存在");
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 手机号加密
|
|
||||||
*
|
|
||||||
* @param phoneNumber 明文手机号
|
|
||||||
* @return 加密后的手机号(Base64 编码)
|
|
||||||
*/
|
|
||||||
private String encryptPhone(String phoneNumber) {
|
private String encryptPhone(String phoneNumber) {
|
||||||
try {
|
try {
|
||||||
String secretKey = wechatProperties.getPhoneEncryption().getSecretKey();
|
String secretKey = wechatProperties.getPhoneEncryption().getSecretKey();
|
||||||
@@ -224,16 +208,10 @@ public class WechatAuthServiceImpl implements WechatAuthService {
|
|||||||
return encryptedPhone;
|
return encryptedPhone;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("手机号加密失败", e);
|
log.error("手机号加密失败", e);
|
||||||
throw new RuntimeException("手机号加密失败 " + e.getMessage());
|
throw new SystemException(ErrorCode.SYSTEM_INTERNAL_ERROR, "手机号加密失败 " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* AES 解密手机号
|
|
||||||
*
|
|
||||||
* @param encryptedPhone 加密后的手机号(Base64 编码)
|
|
||||||
* @return 明文手机号
|
|
||||||
*/
|
|
||||||
public String decryptPhone(String encryptedPhone) {
|
public String decryptPhone(String encryptedPhone) {
|
||||||
try {
|
try {
|
||||||
String secretKey = wechatProperties.getPhoneEncryption().getSecretKey();
|
String secretKey = wechatProperties.getPhoneEncryption().getSecretKey();
|
||||||
@@ -245,51 +223,36 @@ public class WechatAuthServiceImpl implements WechatAuthService {
|
|||||||
return phoneNumber;
|
return phoneNumber;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("手机号解密失败", e);
|
log.error("手机号解密失败", e);
|
||||||
throw new RuntimeException("手机号解密失败 " + e.getMessage());
|
throw new SystemException(ErrorCode.SYSTEM_INTERNAL_ERROR, "手机号解密失败 " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建新会员(首次登录)
|
|
||||||
*
|
|
||||||
* @param unionId 微信 UnionID
|
|
||||||
* @param openid 小程序 OpenID
|
|
||||||
* @param sessionKey 会话密钥
|
|
||||||
* @param phoneCode 手机号 code(可选,前端调用 wx.getPhoneNumber())
|
|
||||||
* @return Mono<WechatLoginVO> 登录响应
|
|
||||||
*/
|
|
||||||
private Mono<WechatLoginVO> createNewMember(String unionId, String openid, String sessionKey, String phoneCode) {
|
private Mono<WechatLoginVO> createNewMember(String unionId, String openid, String sessionKey, String phoneCode) {
|
||||||
log.info("开始创建新会员, unionId: {}, openid: {}", unionId, openid);
|
log.info("开始创建新会员, unionId: {}, openid: {}", unionId, openid);
|
||||||
|
|
||||||
// Step 1: 生成会员号
|
|
||||||
String memberNo = MemberNoGenerator.generate();
|
String memberNo = MemberNoGenerator.generate();
|
||||||
log.info("生成会员号: {}", memberNo);
|
log.info("生成会员号: {}", memberNo);
|
||||||
|
|
||||||
// Step 2: 构建 Member 实体(仅保存标识信息)
|
|
||||||
Member member = Member.builder()
|
Member member = Member.builder()
|
||||||
.memberNo(memberNo) // 小程序注册时自动生成会员号
|
.memberNo(memberNo)
|
||||||
.unionId(unionId) // 存储 UnionID 用于统一身份
|
.unionId(unionId)
|
||||||
.miniappOpenId(openid) // 存储小程序 OpenID
|
.miniappOpenId(openid)
|
||||||
.lastLoginAt(LocalDateTime.now()) // 记录首次登录时间
|
.lastLoginAt(LocalDateTime.now())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
log.info("用户未注册,创建新会员(仅保存标识信息)");
|
log.info("用户未注册,创建新会员(仅保存标识信息)");
|
||||||
|
|
||||||
// Step 3: 保存 Member
|
|
||||||
return memberRepository.save(member)
|
return memberRepository.save(member)
|
||||||
.doOnSuccess(memberSyncer::sync)
|
.doOnSuccess(memberSyncer::sync)
|
||||||
.flatMap(savedMember -> {
|
.flatMap(savedMember -> {
|
||||||
log.info("保存 Member 成功, id: {}, memberNo: {}", savedMember.getId(), savedMember.getMemberNo());
|
log.info("保存 Member 成功, id: {}, memberNo: {}", savedMember.getId(), savedMember.getMemberNo());
|
||||||
// Step 4: 如果有 phoneCode,尝试获取手机号
|
|
||||||
if (phoneCode != null && !phoneCode.isEmpty()) {
|
if (phoneCode != null && !phoneCode.isEmpty()) {
|
||||||
log.info("检测到 phoneCode,尝试获取手机号");
|
log.info("检测到 phoneCode,尝试获取手机号");
|
||||||
return wechatPhoneUtil.getPhoneNumber(phoneCode)
|
return wechatPhoneUtil.getPhoneNumber(phoneCode)
|
||||||
.flatMap(phoneNumber -> {
|
.flatMap(phoneNumber -> {
|
||||||
if (phoneNumber != null && !phoneNumber.isEmpty()) {
|
if (phoneNumber != null && !phoneNumber.isEmpty()) {
|
||||||
log.info("获取到手机号: {}", phoneNumber);
|
log.info("获取到手机号: {}", phoneNumber);
|
||||||
// 加密手机号
|
|
||||||
String encryptedPhone = encryptPhone(phoneNumber);
|
String encryptedPhone = encryptPhone(phoneNumber);
|
||||||
// 为新会员绑定手机号
|
|
||||||
savedMember.setPhone(encryptedPhone);
|
savedMember.setPhone(encryptedPhone);
|
||||||
return memberRepository.save(savedMember)
|
return memberRepository.save(savedMember)
|
||||||
.doOnSuccess(memberSyncer::sync)
|
.doOnSuccess(memberSyncer::sync)
|
||||||
@@ -303,20 +266,11 @@ public class WechatAuthServiceImpl implements WechatAuthService {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// 没有 phoneCode,直接返回
|
|
||||||
return Mono.just(buildLoginResponse(savedMember, true, sessionKey));
|
return Mono.just(buildLoginResponse(savedMember, true, sessionKey));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 构建登录响应 - 封装返回数据(前端调用)
|
|
||||||
*
|
|
||||||
* @param member 会员实体
|
|
||||||
* @param isNewUser 是否为新用户
|
|
||||||
* @param sessionKey 会话密钥(后续可用于解密)
|
|
||||||
* @return WechatLoginVO 登录响应
|
|
||||||
*/
|
|
||||||
private WechatLoginVO buildLoginResponse(Member member, boolean isNewUser, String sessionKey) {
|
private WechatLoginVO buildLoginResponse(Member member, boolean isNewUser, String sessionKey) {
|
||||||
log.debug("构建登录响应, memberId: {}, isNewUser: {}", member.getId(), isNewUser);
|
log.debug("构建登录响应, memberId: {}, isNewUser: {}", member.getId(), isNewUser);
|
||||||
|
|
||||||
@@ -327,14 +281,11 @@ public class WechatAuthServiceImpl implements WechatAuthService {
|
|||||||
member.getPhone() != null ? "已绑定" : "未绑定");
|
member.getPhone() != null ? "已绑定" : "未绑定");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 统一使用 JwtTokenProvider 生成 Token
|
|
||||||
// 使用空的角色列表,会员不需要角色
|
|
||||||
List<String> roles = new ArrayList<>();
|
List<String> roles = new ArrayList<>();
|
||||||
String accessToken = jwtTokenProvider.generateToken(String.valueOf(member.getId()), member.getId(), roles);
|
String accessToken = jwtTokenProvider.generateToken(String.valueOf(member.getId()), member.getId(), roles);
|
||||||
|
|
||||||
log.info("JWT Token 生成成功, memberId: {}", member.getId());
|
log.info("JWT Token 生成成功, memberId: {}", member.getId());
|
||||||
|
|
||||||
// 使用 JwtTokenProvider 默认过期时间(86400 秒)
|
|
||||||
int expiresIn = 86400;
|
int expiresIn = 86400;
|
||||||
|
|
||||||
return WechatLoginVO.builder()
|
return WechatLoginVO.builder()
|
||||||
|
|||||||
+5
-1
@@ -234,10 +234,14 @@ public class WechatOfficialServiceImpl implements WechatOfficialService {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Mono<Boolean> checkSubscribeStatus(Long memberId) {
|
public Mono<Boolean> checkSubscribeStatus(Long memberId) {
|
||||||
|
log.info("查询用户关注状态, memberId: {}", memberId);
|
||||||
|
|
||||||
return memberRepository.findById(memberId)
|
return memberRepository.findById(memberId)
|
||||||
.map(member -> {
|
.map(member -> {
|
||||||
Boolean subscribed = member.getSubscribed();
|
Boolean subscribed = member.getSubscribed();
|
||||||
return subscribed != null && subscribed;
|
boolean result = subscribed != null && subscribed;
|
||||||
|
log.info("查询用户关注状态结果, memberId: {}, subscribed: {}", memberId, result);
|
||||||
|
return result;
|
||||||
})
|
})
|
||||||
.defaultIfEmpty(false);
|
.defaultIfEmpty(false);
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-16
@@ -1,22 +1,23 @@
|
|||||||
package cn.novalon.gym.manage.app.config;
|
package cn.novalon.gym.manage.app.config;
|
||||||
|
|
||||||
import cn.novalon.gym.manage.sys.handler.auth.SysAuthHandler;
|
import cn.novalon.gym.manage.checkIn.handler.CheckInHandler;
|
||||||
import cn.novalon.gym.manage.sys.handler.auth.PasswordDiagnosticHandler;
|
import cn.novalon.gym.manage.file.handler.SysFileHandler;
|
||||||
import cn.novalon.gym.manage.sys.handler.config.SysConfigHandler;
|
import cn.novalon.gym.manage.member.handler.MemberHandler;
|
||||||
import cn.novalon.gym.manage.sys.handler.dictionary.DictionaryHandler;
|
import cn.novalon.gym.manage.member.handler.WechatAuthHandler;
|
||||||
import cn.novalon.gym.manage.sys.handler.dict.SysDictHandler;
|
|
||||||
import cn.novalon.gym.manage.sys.handler.log.SysLogHandler;
|
|
||||||
import cn.novalon.gym.manage.sys.handler.log.OperationLogHandler;
|
|
||||||
import cn.novalon.gym.manage.sys.handler.menu.MenuHandler;
|
|
||||||
import cn.novalon.gym.manage.sys.handler.role.SysRoleHandler;
|
|
||||||
import cn.novalon.gym.manage.sys.handler.permission.SysPermissionHandler;
|
|
||||||
import cn.novalon.gym.manage.sys.handler.stats.StatsHandler;
|
|
||||||
import cn.novalon.gym.manage.sys.handler.user.SysUserHandler;
|
|
||||||
import cn.novalon.gym.manage.notify.handler.SysNoticeHandler;
|
import cn.novalon.gym.manage.notify.handler.SysNoticeHandler;
|
||||||
import cn.novalon.gym.manage.notify.handler.SysUserMessageHandler;
|
import cn.novalon.gym.manage.notify.handler.SysUserMessageHandler;
|
||||||
import cn.novalon.gym.manage.file.handler.SysFileHandler;
|
import cn.novalon.gym.manage.sys.handler.auth.PasswordDiagnosticHandler;
|
||||||
import cn.novalon.gym.manage.member.handler.WechatAuthHandler;
|
import cn.novalon.gym.manage.sys.handler.auth.SysAuthHandler;
|
||||||
import cn.novalon.gym.manage.member.handler.MemberHandler;
|
import cn.novalon.gym.manage.sys.handler.config.SysConfigHandler;
|
||||||
|
import cn.novalon.gym.manage.sys.handler.dict.SysDictHandler;
|
||||||
|
import cn.novalon.gym.manage.sys.handler.dictionary.DictionaryHandler;
|
||||||
|
import cn.novalon.gym.manage.sys.handler.log.OperationLogHandler;
|
||||||
|
import cn.novalon.gym.manage.sys.handler.log.SysLogHandler;
|
||||||
|
import cn.novalon.gym.manage.sys.handler.menu.MenuHandler;
|
||||||
|
import cn.novalon.gym.manage.sys.handler.permission.SysPermissionHandler;
|
||||||
|
import cn.novalon.gym.manage.sys.handler.role.SysRoleHandler;
|
||||||
|
import cn.novalon.gym.manage.sys.handler.stats.StatsHandler;
|
||||||
|
import cn.novalon.gym.manage.sys.handler.user.SysUserHandler;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.web.reactive.function.server.RouterFunction;
|
import org.springframework.web.reactive.function.server.RouterFunction;
|
||||||
@@ -55,7 +56,8 @@ public class SystemRouter {
|
|||||||
SysPermissionHandler permissionHandler,
|
SysPermissionHandler permissionHandler,
|
||||||
MemberHandler memberHandler,
|
MemberHandler memberHandler,
|
||||||
WechatAuthHandler wechatAuthHandler,
|
WechatAuthHandler wechatAuthHandler,
|
||||||
PasswordDiagnosticHandler passwordDiagnosticHandler) {
|
PasswordDiagnosticHandler passwordDiagnosticHandler,
|
||||||
|
CheckInHandler checkInHandler) {
|
||||||
|
|
||||||
return route()
|
return route()
|
||||||
// ========== 诊断路由 ==========
|
// ========== 诊断路由 ==========
|
||||||
@@ -214,6 +216,10 @@ public class SystemRouter {
|
|||||||
.PUT("/api/admin/member/{id}", memberHandler::adminUpdateMemberInfo)
|
.PUT("/api/admin/member/{id}", memberHandler::adminUpdateMemberInfo)
|
||||||
.GET("/api/admin/members", memberHandler::searchMembers)
|
.GET("/api/admin/members", memberHandler::searchMembers)
|
||||||
.GET("/api/admin/members/all", memberHandler::getAllMembers)
|
.GET("/api/admin/members/all", memberHandler::getAllMembers)
|
||||||
|
|
||||||
|
// ========== 签到模块路由 ==========
|
||||||
|
.POST("/api/checkin", checkInHandler::checkIn)
|
||||||
|
.GET("/api/QRCode", checkInHandler::getQRCode)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package cn.novalon.gym.manage.sys.util;
|
||||||
|
|
||||||
|
import cn.novalon.gym.manage.sys.security.JwtTokenProvider;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||||
|
import org.springframework.web.server.ResponseStatusException;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AuthUtil {
|
||||||
|
|
||||||
|
private final JwtTokenProvider jwtTokenProvider;
|
||||||
|
|
||||||
|
public String extractToken(ServerRequest request) {
|
||||||
|
String authorization = request.headers().firstHeader(HttpHeaders.AUTHORIZATION);
|
||||||
|
if (authorization == null || !authorization.startsWith("Bearer ")) return null;
|
||||||
|
return authorization.substring(7);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getMemberIdOrThrow(ServerRequest request) {
|
||||||
|
String token = extractToken(request);
|
||||||
|
if (token == null) throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "缺少 Token");
|
||||||
|
if (!jwtTokenProvider.validateToken(token)) throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "Token 无效或已过期");
|
||||||
|
if (jwtTokenProvider.getUserIdFromToken(token) <= 0L) throw new IllegalArgumentException("ID无效");
|
||||||
|
return jwtTokenProvider.getUserIdFromToken(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user