|
|
@@ -1,10 +1,15 @@
|
|
|
package com.haha.service.impl;
|
|
|
|
|
|
+import cn.dev33.satoken.stp.StpUtil;
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
|
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
+import com.haha.common.constant.RedisConstants;
|
|
|
import com.haha.common.exception.BusinessException;
|
|
|
+import com.haha.common.vo.LoginVO;
|
|
|
+import com.haha.common.vo.Result;
|
|
|
+import com.haha.common.vo.UserVO;
|
|
|
import com.haha.entity.Replenisher;
|
|
|
import com.haha.entity.ReplenisherDevice;
|
|
|
import com.haha.entity.dto.ReplenisherCreateDTO;
|
|
|
@@ -15,13 +20,18 @@ import com.haha.mapper.ReplenisherMapper;
|
|
|
import com.haha.service.ReplenisherService;
|
|
|
import lombok.RequiredArgsConstructor;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.beans.factory.annotation.Value;
|
|
|
+import org.springframework.data.redis.core.StringRedisTemplate;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
import org.springframework.util.StringUtils;
|
|
|
+import org.springframework.web.client.RestTemplate;
|
|
|
|
|
|
import java.time.LocalDateTime;
|
|
|
import java.util.ArrayList;
|
|
|
import java.util.List;
|
|
|
+import java.util.UUID;
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
|
|
|
/**
|
|
|
* 补货员服务实现类
|
|
|
@@ -33,6 +43,14 @@ public class ReplenisherServiceImpl extends ServiceImpl<ReplenisherMapper, Reple
|
|
|
|
|
|
private final ReplenisherMapper replenisherMapper;
|
|
|
private final ReplenisherDeviceMapper replenisherDeviceMapper;
|
|
|
+ private final RestTemplate restTemplate;
|
|
|
+ private final StringRedisTemplate stringRedisTemplate;
|
|
|
+
|
|
|
+ @Value("${wechat.admin-miniapp.app-id}")
|
|
|
+ private String adminMiniappAppId;
|
|
|
+
|
|
|
+ @Value("${wechat.admin-miniapp.secret}")
|
|
|
+ private String adminMiniappSecret;
|
|
|
|
|
|
@Override
|
|
|
public IPage<Replenisher> getPage(ReplenisherQueryDTO queryDTO) {
|
|
|
@@ -257,6 +275,224 @@ public class ReplenisherServiceImpl extends ServiceImpl<ReplenisherMapper, Reple
|
|
|
return replenisherMapper.searchByKeyword(keyword);
|
|
|
}
|
|
|
|
|
|
+ @Override
|
|
|
+ public Result<LoginVO> loginByWechat(String code) {
|
|
|
+ try {
|
|
|
+ log.info("[补货员] 微信静默登录 - code: {}",
|
|
|
+ code != null ? code.substring(0, Math.min(8, code.length())) + "..." : "null");
|
|
|
+
|
|
|
+ // 1. 通过code获取openid
|
|
|
+ String openid = getWechatOpenid(code);
|
|
|
+ if (openid == null || openid.isEmpty()) {
|
|
|
+ log.warn("[补货员] 获取openid失败 - code: {}",
|
|
|
+ code != null ? code.substring(0, Math.min(8, code.length())) + "..." : "null");
|
|
|
+ return Result.error(400, "获取用户标识失败,请重试");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 根据openid查找补货员(仅限已绑定微信的账号)
|
|
|
+ LambdaQueryWrapper<Replenisher> wrapper = new LambdaQueryWrapper<>();
|
|
|
+ wrapper.eq(Replenisher::getWechatOpenid, openid);
|
|
|
+ Replenisher replenisher = this.getOne(wrapper);
|
|
|
+
|
|
|
+ if (replenisher == null) {
|
|
|
+ // 不允许自动注册:补货员必须先由后台创建并通过扫码绑定微信
|
|
|
+ log.warn("[补货员] 微信登录 - openid未绑定任何补货员账号: openid={}",
|
|
|
+ openid.substring(0, Math.min(10, openid.length())) + "...");
|
|
|
+ return Result.error(403, "该微信未绑定补货员账号,请联系管理员获取绑定二维码");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 检查状态
|
|
|
+ if (replenisher.getStatus() == null || replenisher.getStatus() == 0) {
|
|
|
+ log.warn("[补货员] 账号已被禁用: id={}, openid={}", replenisher.getId(), openid);
|
|
|
+ return Result.error(403, "您的账号已被禁用,请联系管理员");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 生成token
|
|
|
+ StpUtil.login(replenisher.getId());
|
|
|
+ StpUtil.getTokenSession().set("userType", "REPLENISHER");
|
|
|
+ String token = StpUtil.getTokenValue();
|
|
|
+
|
|
|
+ log.info("[补货员] 微信登录成功: id={}, name={}", replenisher.getId(), replenisher.getName());
|
|
|
+
|
|
|
+ // 5. 构建返回结果
|
|
|
+ UserVO userVO = UserVO.builder()
|
|
|
+ .id(replenisher.getId())
|
|
|
+ .nickname(replenisher.getName())
|
|
|
+ .avatar(replenisher.getAvatar())
|
|
|
+ .phone(replenisher.getPhone())
|
|
|
+ .build();
|
|
|
+
|
|
|
+ LoginVO loginVO = LoginVO.builder()
|
|
|
+ .token(token)
|
|
|
+ .userInfo(userVO)
|
|
|
+ .build();
|
|
|
+
|
|
|
+ return Result.success("登录成功", loginVO);
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[补货员] 微信静默登录异常", e);
|
|
|
+ return Result.error(500, "登录失败:" + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public String generateBindingCode(Long replenisherId) {
|
|
|
+ // 1. 检查补货员是否存在
|
|
|
+ Replenisher replenisher = this.getById(replenisherId);
|
|
|
+ if (replenisher == null) {
|
|
|
+ throw new BusinessException(404, "补货员不存在");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 如果已经绑定了微信,不允许再次生成绑定码
|
|
|
+ if (StringUtils.hasText(replenisher.getWechatOpenid())) {
|
|
|
+ throw new BusinessException(400, "该补货员已绑定微信,无需重新绑定");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 生成UUID绑定码并存入Redis(24小时有效)
|
|
|
+ String bindingCode = UUID.randomUUID().toString().replace("-", "").substring(0, 24).toUpperCase();
|
|
|
+ String redisKey = String.format(RedisConstants.REPLENISHER_BIND_CODE_KEY, bindingCode);
|
|
|
+ stringRedisTemplate.opsForValue().set(redisKey, String.valueOf(replenisherId), 24, TimeUnit.HOURS);
|
|
|
+
|
|
|
+ log.info("[补货员] 生成绑定码: replenisherId={}, bindingCode={}", replenisherId, bindingCode);
|
|
|
+
|
|
|
+ return bindingCode;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public Result<LoginVO> bindWechat(String bindingCode, String wechatCode) {
|
|
|
+ try {
|
|
|
+ log.info("[补货员] 扫码绑定微信 - bindingCode: {}",
|
|
|
+ bindingCode != null ? bindingCode.substring(0, Math.min(8, bindingCode.length())) + "..." : "null");
|
|
|
+
|
|
|
+ // 1. 验证绑定码
|
|
|
+ String redisKey = String.format(RedisConstants.REPLENISHER_BIND_CODE_KEY, bindingCode);
|
|
|
+ String replenisherIdStr = stringRedisTemplate.opsForValue().get(redisKey);
|
|
|
+ if (replenisherIdStr == null || replenisherIdStr.isEmpty()) {
|
|
|
+ log.warn("[补货员] 绑定码无效或已过期: bindingCode={}", bindingCode);
|
|
|
+ return Result.error(400, "绑定码无效或已过期,请联系管理员重新生成");
|
|
|
+ }
|
|
|
+
|
|
|
+ Long replenisherId = Long.valueOf(replenisherIdStr);
|
|
|
+
|
|
|
+ // 2. 获取微信openid
|
|
|
+ String openid = getWechatOpenid(wechatCode);
|
|
|
+ if (openid == null || openid.isEmpty()) {
|
|
|
+ log.warn("[补货员] 绑定 - 获取openid失败");
|
|
|
+ return Result.error(400, "获取微信标识失败,请重试");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 检查openid是否已被其他补货员绑定
|
|
|
+ LambdaQueryWrapper<Replenisher> openidCheck = new LambdaQueryWrapper<>();
|
|
|
+ openidCheck.eq(Replenisher::getWechatOpenid, openid)
|
|
|
+ .ne(Replenisher::getId, replenisherId);
|
|
|
+ if (this.count(openidCheck) > 0) {
|
|
|
+ log.warn("[补货员] 绑定 - openid已被其他补货员绑定: openid={}", openid);
|
|
|
+ return Result.error(400, "该微信已绑定其他补货员账号");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 检查补货员是否存在
|
|
|
+ Replenisher replenisher = this.getById(replenisherId);
|
|
|
+ if (replenisher == null) {
|
|
|
+ log.warn("[补货员] 绑定 - 补货员不存在: id={}", replenisherId);
|
|
|
+ return Result.error(404, "补货员不存在");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 5. 如果已绑定微信且不是同一个,拒绝
|
|
|
+ if (StringUtils.hasText(replenisher.getWechatOpenid()) && !openid.equals(replenisher.getWechatOpenid())) {
|
|
|
+ log.warn("[补货员] 绑定 - 补货员已绑定其他微信: id={}", replenisherId);
|
|
|
+ return Result.error(400, "该补货员已绑定其他微信账号");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 6. 绑定微信openid + 激活账号
|
|
|
+ replenisher.setWechatOpenid(openid);
|
|
|
+ replenisher.setStatus(1);
|
|
|
+ replenisher.setUpdateTime(LocalDateTime.now());
|
|
|
+ this.updateById(replenisher);
|
|
|
+
|
|
|
+ // 7. 删除已使用的绑定码
|
|
|
+ stringRedisTemplate.delete(redisKey);
|
|
|
+
|
|
|
+ log.info("[补货员] 绑定微信成功: id={}, name={}, openid={}",
|
|
|
+ replenisherId, replenisher.getName(),
|
|
|
+ openid.substring(0, Math.min(10, openid.length())) + "...");
|
|
|
+
|
|
|
+ // 8. 登录
|
|
|
+ StpUtil.login(replenisher.getId());
|
|
|
+ StpUtil.getTokenSession().set("userType", "REPLENISHER");
|
|
|
+ String token = StpUtil.getTokenValue();
|
|
|
+
|
|
|
+ UserVO userVO = UserVO.builder()
|
|
|
+ .id(replenisher.getId())
|
|
|
+ .nickname(replenisher.getName())
|
|
|
+ .avatar(replenisher.getAvatar())
|
|
|
+ .phone(replenisher.getPhone())
|
|
|
+ .build();
|
|
|
+
|
|
|
+ LoginVO loginVO = LoginVO.builder()
|
|
|
+ .token(token)
|
|
|
+ .userInfo(userVO)
|
|
|
+ .build();
|
|
|
+
|
|
|
+ return Result.success("绑定并登录成功", loginVO);
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[补货员] 扫码绑定微信异常", e);
|
|
|
+ return Result.error(500, "绑定失败:" + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 通过微信登录凭证code获取openid
|
|
|
+ */
|
|
|
+ private String getWechatOpenid(String code) {
|
|
|
+ try {
|
|
|
+ log.info("[微信API-管理端] 开始获取openid - appid: {}, code: {}",
|
|
|
+ adminMiniappAppId,
|
|
|
+ code != null ? code.substring(0, Math.min(10, code.length())) + "..." : "null");
|
|
|
+
|
|
|
+ String url = String.format(
|
|
|
+ "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code",
|
|
|
+ adminMiniappAppId, adminMiniappSecret, code
|
|
|
+ );
|
|
|
+
|
|
|
+ log.debug("[微信API-管理端] 请求URL: {}", url.replace(adminMiniappSecret, "***"));
|
|
|
+
|
|
|
+ String response = restTemplate.getForObject(url, String.class);
|
|
|
+ log.info("[微信API-管理端] 获取openid响应: {}", response);
|
|
|
+
|
|
|
+ if (response != null && !response.isEmpty()) {
|
|
|
+ try {
|
|
|
+ com.fasterxml.jackson.databind.ObjectMapper objectMapper = new com.fasterxml.jackson.databind.ObjectMapper();
|
|
|
+ java.util.Map<String, Object> result = objectMapper.readValue(response, java.util.Map.class);
|
|
|
+
|
|
|
+ if (result.containsKey("errcode")) {
|
|
|
+ Integer errcode = (Integer) result.get("errcode");
|
|
|
+ String errmsg = (String) result.get("errmsg");
|
|
|
+ log.error("[微信API-管理端] 获取openid失败 - errcode: {}, errmsg: {}", errcode, errmsg);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (result.containsKey("openid")) {
|
|
|
+ String openid = (String) result.get("openid");
|
|
|
+ log.info("[微信API-管理端] 获取openid成功 - openid: {}",
|
|
|
+ openid != null ? openid.substring(0, Math.min(10, openid.length())) + "..." : "null");
|
|
|
+ return openid;
|
|
|
+ }
|
|
|
+ } catch (Exception parseEx) {
|
|
|
+ log.error("[微信API-管理端] 解析响应JSON失败", parseEx);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ log.error("[微信API-管理端] 获取openid失败 - response: {}", response);
|
|
|
+ return null;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[微信API-管理端] 调用jscode2session异常", e);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 填充状态标签
|
|
|
*/
|