|
|
@@ -0,0 +1,468 @@
|
|
|
+package com.zsElectric.boot.app.api;
|
|
|
+
|
|
|
+import cn.hutool.core.img.ImgUtil;
|
|
|
+import cn.hutool.core.util.ObjectUtil;
|
|
|
+import cn.hutool.extra.qrcode.QrCodeUtil;
|
|
|
+import com.fasterxml.jackson.databind.JsonNode;
|
|
|
+import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
+import com.zsElectric.boot.app.model.entity.*;
|
|
|
+import com.zsElectric.boot.app.service.*;
|
|
|
+import com.zsElectric.boot.core.web.Result;
|
|
|
+import jakarta.servlet.http.HttpServletRequest;
|
|
|
+import jakarta.servlet.http.HttpServletResponse;
|
|
|
+import lombok.RequiredArgsConstructor;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import okhttp3.MediaType;
|
|
|
+import okhttp3.OkHttpClient;
|
|
|
+import okhttp3.Request;
|
|
|
+import org.springframework.beans.factory.annotation.Value;
|
|
|
+import org.springframework.data.redis.core.RedisTemplate;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
+import org.springframework.web.bind.annotation.*;
|
|
|
+import org.springframework.web.multipart.MultipartFile;
|
|
|
+
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.util.*;
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 用户API接口 - 面向小程序/APP
|
|
|
+ */
|
|
|
+@Slf4j
|
|
|
+@RestController
|
|
|
+@RequestMapping("/userApi")
|
|
|
+@RequiredArgsConstructor
|
|
|
+public class UserApiController {
|
|
|
+
|
|
|
+ private final UserInfoService userInfoService;
|
|
|
+ private final UserAccountService userAccountService;
|
|
|
+ private final BannerInfoService bannerInfoService;
|
|
|
+ private final UserFeedbackService userFeedbackService;
|
|
|
+ private final NewUserDiscountService newUserDiscountService;
|
|
|
+ private final ChargeOrderInfoService chargeOrderInfoService;
|
|
|
+ private final UserFirmService userFirmService;
|
|
|
+ private final FirmInfoService firmInfoService;
|
|
|
+ private final AdvertisingService advertisingService;
|
|
|
+ private final RedisTemplate<String, Object> redisTemplate;
|
|
|
+
|
|
|
+ private final OkHttpClient okHttpClient = new OkHttpClient();
|
|
|
+ private final ObjectMapper objectMapper = new ObjectMapper();
|
|
|
+ private final String TOKEN_PREFIX = "user:token:";
|
|
|
+
|
|
|
+ @Value("${wx.miniapp.app-id:}")
|
|
|
+ private String wxAppid;
|
|
|
+
|
|
|
+ @Value("${wx.miniapp.app-secret:}")
|
|
|
+ private String wxAppSecret;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 新用户活动信息查询
|
|
|
+ */
|
|
|
+ @PostMapping("/checkNewUser")
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public Result<?> checkNewUser(@RequestBody Map<String, Object> data, HttpServletRequest request) {
|
|
|
+ UserInfo cacheUser = getUserFromToken(request);
|
|
|
+ if (cacheUser == null) {
|
|
|
+ return Result.failed("登录已过期");
|
|
|
+ }
|
|
|
+
|
|
|
+ List<NewUserDiscount> list = newUserDiscountService.lambdaQuery()
|
|
|
+ .eq(NewUserDiscount::getStatus, 1)
|
|
|
+ .le(NewUserDiscount::getBeginTime, LocalDateTime.now())
|
|
|
+ .ge(NewUserDiscount::getEndTime, LocalDateTime.now())
|
|
|
+ .list();
|
|
|
+
|
|
|
+ if (list == null || list.isEmpty()) {
|
|
|
+ return Result.success("暂无新用户活动");
|
|
|
+ }
|
|
|
+
|
|
|
+ NewUserDiscount discountInfo = list.get(0);
|
|
|
+
|
|
|
+ long orderCount = chargeOrderInfoService.lambdaQuery()
|
|
|
+ .eq(ChargeOrderInfo::getUserId, cacheUser.getId())
|
|
|
+ .count();
|
|
|
+
|
|
|
+ if (orderCount > 0) {
|
|
|
+ return Result.success("不是新用户");
|
|
|
+ }
|
|
|
+
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
+ result.put("discountInfo", discountInfo);
|
|
|
+ return Result.success(result);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 微信小程序登录
|
|
|
+ */
|
|
|
+ @PostMapping("/login")
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public Result<?> login(@RequestBody Map<String, Object> data, HttpServletRequest request) {
|
|
|
+ UserInfo cacheUser = getUserFromToken(request);
|
|
|
+
|
|
|
+ // 检查是否已登录
|
|
|
+ if (cacheUser != null && cacheUser.getId() != null) {
|
|
|
+ String token = request.getHeader("token");
|
|
|
+ if (token == null) {
|
|
|
+ return Result.failed("token不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (data.get("checkStatus") != null) {
|
|
|
+ UserInfo userInfo = userInfoService.getById(cacheUser.getId());
|
|
|
+ saveUserToCache(token, userInfo);
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
+ result.put("userInfo", userInfo);
|
|
|
+ result.put("token", token);
|
|
|
+ return Result.success(result);
|
|
|
+ }
|
|
|
+
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
+ result.put("userInfo", cacheUser);
|
|
|
+ result.put("token", token);
|
|
|
+ return Result.success(result);
|
|
|
+ }
|
|
|
+
|
|
|
+ String code = (String) data.get("code");
|
|
|
+ log.info("[微信登录]code:{}", code);
|
|
|
+ log.info("[微信登录]wxAppid:{}", wxAppid);
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 调用微信接口获取openid
|
|
|
+ String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + wxAppid +
|
|
|
+ "&secret=" + wxAppSecret +
|
|
|
+ "&grant_type=authorization_code&js_code=" + code;
|
|
|
+ String body = okHttpClient.newCall(new Request.Builder().url(url).get().build())
|
|
|
+ .execute().body().string();
|
|
|
+
|
|
|
+ log.info("[微信登录]回复报文:{}", body);
|
|
|
+ JsonNode jsonNode = objectMapper.readTree(body);
|
|
|
+
|
|
|
+ String openid = jsonNode.has("openid") ? jsonNode.get("openid").asText() : null;
|
|
|
+ if (openid == null) {
|
|
|
+ log.error("[微信登录]失败,回复报文:{}", body);
|
|
|
+ return Result.failed("登录失败,请稍后再试");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询或创建用户
|
|
|
+ UserInfo userInfo = userInfoService.lambdaQuery()
|
|
|
+ .eq(UserInfo::getWechat, openid)
|
|
|
+ .one();
|
|
|
+
|
|
|
+ if (userInfo == null) {
|
|
|
+ userInfo = new UserInfo();
|
|
|
+ userInfo.setWechat(openid);
|
|
|
+ userInfoService.save(userInfo);
|
|
|
+
|
|
|
+ // 创建账户
|
|
|
+ UserAccount userAccount = new UserAccount();
|
|
|
+ userAccount.setUserId(userInfo.getId());
|
|
|
+ userAccountService.save(userAccount);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 生成token并保存到缓存
|
|
|
+ String loginToken = UUID.randomUUID().toString().replace("-", "");
|
|
|
+ saveUserToCache(loginToken, userInfo);
|
|
|
+
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
+ result.put("userInfo", userInfo);
|
|
|
+ result.put("token", loginToken);
|
|
|
+ return Result.success(result);
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[微信登录]异常", e);
|
|
|
+ return Result.failed("登录失败,请稍后再试");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取手机号
|
|
|
+ */
|
|
|
+ @PostMapping("/getPhone")
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public Result<?> getPhone(@RequestBody Map<String, Object> data, HttpServletRequest request) {
|
|
|
+ UserInfo cacheUser = getUserFromToken(request);
|
|
|
+ if (cacheUser == null) {
|
|
|
+ return Result.failed("登录已过期");
|
|
|
+ }
|
|
|
+
|
|
|
+ String code = (String) data.get("code");
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 获取access_token
|
|
|
+ String tokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" +
|
|
|
+ wxAppid + "&secret=" + wxAppSecret;
|
|
|
+ String tokenBody = okHttpClient.newCall(new Request.Builder().url(tokenUrl).get().build())
|
|
|
+ .execute().body().string();
|
|
|
+
|
|
|
+ log.info("[获取token]回复报文:{}", tokenBody);
|
|
|
+ JsonNode tokenNode = objectMapper.readTree(tokenBody);
|
|
|
+ String accessToken = tokenNode.has("access_token") ? tokenNode.get("access_token").asText() : null;
|
|
|
+
|
|
|
+ if (accessToken == null) {
|
|
|
+ log.error("[获取token]失败,回复报文:{}", tokenBody);
|
|
|
+ return Result.failed("获取失败,请稍后再试");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取手机号
|
|
|
+ Map<String, Object> phoneParams = new HashMap<>();
|
|
|
+ phoneParams.put("code", code);
|
|
|
+ okhttp3.RequestBody requestBody = okhttp3.RequestBody.create(
|
|
|
+ MediaType.parse("application/json"), objectMapper.writeValueAsString(phoneParams));
|
|
|
+
|
|
|
+ String phoneUrl = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" + accessToken;
|
|
|
+ String phoneBody = okHttpClient.newCall(new Request.Builder().url(phoneUrl).post(requestBody).build())
|
|
|
+ .execute().body().string();
|
|
|
+
|
|
|
+ log.info("[获取手机号]回复报文:{}", phoneBody);
|
|
|
+ JsonNode phoneNode = objectMapper.readTree(phoneBody);
|
|
|
+ Integer errcode = phoneNode.has("errcode") ? phoneNode.get("errcode").asInt() : -1;
|
|
|
+
|
|
|
+ if (errcode != 0) {
|
|
|
+ log.error("[获取手机号]失败,回复报文:{}", phoneBody);
|
|
|
+ return Result.failed("获取失败,请稍后再试");
|
|
|
+ }
|
|
|
+
|
|
|
+ JsonNode phoneInfo = phoneNode.get("phone_info");
|
|
|
+ String phone = phoneInfo.has("phoneNumber") ? phoneInfo.get("phoneNumber").asText() : null;
|
|
|
+
|
|
|
+ // 检查手机号是否已存在
|
|
|
+ UserInfo existUser = userInfoService.lambdaQuery()
|
|
|
+ .eq(UserInfo::getPhone, phone)
|
|
|
+ .ne(UserInfo::getGroupId, -1L)
|
|
|
+ .one();
|
|
|
+
|
|
|
+ if (existUser != null) {
|
|
|
+ // 删除原账户的微信绑定
|
|
|
+ UserInfo oldUser = new UserInfo();
|
|
|
+ oldUser.setId(cacheUser.getId());
|
|
|
+ oldUser.setWechat(cacheUser.getId() + "_" + cacheUser.getWechat());
|
|
|
+ userInfoService.updateById(oldUser);
|
|
|
+
|
|
|
+ // 更新现有用户的微信id
|
|
|
+ existUser.setWechat(cacheUser.getWechat());
|
|
|
+ userInfoService.updateById(existUser);
|
|
|
+
|
|
|
+ String token = request.getHeader("token");
|
|
|
+ saveUserToCache(token, existUser);
|
|
|
+
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
+ result.put("userInfo", existUser);
|
|
|
+ return Result.success(result);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新当前用户的手机号
|
|
|
+ UserInfo updateUser = new UserInfo();
|
|
|
+ updateUser.setId(cacheUser.getId());
|
|
|
+ updateUser.setPhone(phone);
|
|
|
+ userInfoService.updateById(updateUser);
|
|
|
+
|
|
|
+ UserInfo userInfo = userInfoService.getById(cacheUser.getId());
|
|
|
+ String token = request.getHeader("token");
|
|
|
+ saveUserToCache(token, userInfo);
|
|
|
+
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
+ result.put("userInfo", userInfo);
|
|
|
+ return Result.success(result);
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[获取手机号]异常", e);
|
|
|
+ return Result.failed("获取失败,请稍后再试");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取用户账户信息
|
|
|
+ */
|
|
|
+ @PostMapping("/getUserAccount")
|
|
|
+ public Result<?> getUserAccount(HttpServletRequest request) {
|
|
|
+ UserInfo cacheUser = getUserFromToken(request);
|
|
|
+ if (cacheUser == null) {
|
|
|
+ return Result.failed("登录已过期");
|
|
|
+ }
|
|
|
+
|
|
|
+ UserInfo user = userInfoService.getById(cacheUser.getId());
|
|
|
+ if (user == null) {
|
|
|
+ return Result.failed("未查询到用户信息");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ObjectUtil.isNotEmpty(user.getFirmId())) {
|
|
|
+ FirmInfo firmInfo = firmInfoService.getById(user.getFirmId());
|
|
|
+ if (ObjectUtil.isNotEmpty(firmInfo)) {
|
|
|
+ user.setFirmInfoName(firmInfo.getName());
|
|
|
+ user.setFirmType(firmInfo.getStatus());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
+ result.put("accountInfo", user);
|
|
|
+ return Result.success(result);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取Banner列表
|
|
|
+ */
|
|
|
+ @PostMapping("/getBanners")
|
|
|
+ public Result<?> getBanners() {
|
|
|
+ List<BannerInfo> banners = bannerInfoService.lambdaQuery()
|
|
|
+ .eq(BannerInfo::getStatus, 1)
|
|
|
+ .orderByAsc(BannerInfo::getSort)
|
|
|
+ .list();
|
|
|
+
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
+ result.put("banners", banners);
|
|
|
+ return Result.success(result);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取广告位
|
|
|
+ */
|
|
|
+ @PostMapping("/getAdvertising")
|
|
|
+ public Result<?> getAdvertising() {
|
|
|
+ List<Advertising> banners = advertisingService.lambdaQuery()
|
|
|
+ .eq(Advertising::getStatus, 1)
|
|
|
+ .orderByAsc(Advertising::getSort)
|
|
|
+ .list();
|
|
|
+
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
+ result.put("banners", banners);
|
|
|
+ return Result.success(result);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 新增用户反馈
|
|
|
+ */
|
|
|
+ @PostMapping("/addUserFeedback")
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public Result<?> addUserFeedback(@RequestBody UserFeedback data, HttpServletRequest request) {
|
|
|
+ UserInfo cacheUser = getUserFromToken(request);
|
|
|
+ if (cacheUser == null) {
|
|
|
+ return Result.failed("登录已过期");
|
|
|
+ }
|
|
|
+
|
|
|
+ data.setUserId(cacheUser.getId());
|
|
|
+ boolean saved = userFeedbackService.save(data);
|
|
|
+ if (!saved) {
|
|
|
+ return Result.failed("提交失败");
|
|
|
+ }
|
|
|
+
|
|
|
+ return Result.success();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查看用户反馈
|
|
|
+ */
|
|
|
+ @PostMapping("/getMyFeekBack")
|
|
|
+ public Result<?> getMyFeekBack(HttpServletRequest request) {
|
|
|
+ UserInfo cacheUser = getUserFromToken(request);
|
|
|
+ if (cacheUser == null) {
|
|
|
+ return Result.failed("登录已过期");
|
|
|
+ }
|
|
|
+
|
|
|
+ List<UserFeedback> list = userFeedbackService.lambdaQuery()
|
|
|
+ .eq(UserFeedback::getUserId, cacheUser.getId())
|
|
|
+ .orderByDesc(UserFeedback::getCreateTime)
|
|
|
+ .list();
|
|
|
+
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
+ result.put("list", list);
|
|
|
+ return Result.success(result);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 上传文件
|
|
|
+ */
|
|
|
+ @PostMapping("/upFile")
|
|
|
+ public Result<?> upFile(@RequestParam("file") MultipartFile file) {
|
|
|
+ try {
|
|
|
+ // TODO: 实现文件上传逻辑,使用ossProperties配置
|
|
|
+ String fileUrl = "/uploads/" + file.getOriginalFilename();
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
+ result.put("fileUrl", fileUrl);
|
|
|
+ return Result.success(result);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("文件上传失败", e);
|
|
|
+ return Result.failed("文件上传失败");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取邀请员工的二维码
|
|
|
+ */
|
|
|
+ @GetMapping("/get-invite-qr")
|
|
|
+ public void getInviteQr(HttpServletRequest request, HttpServletResponse response) {
|
|
|
+ try {
|
|
|
+ UserInfo cacheUser = getUserFromToken(request);
|
|
|
+ if (cacheUser == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ UserFirm firm = userFirmService.lambdaQuery()
|
|
|
+ .eq(UserFirm::getUserId, cacheUser.getId())
|
|
|
+ .one();
|
|
|
+ if (ObjectUtil.isEmpty(firm)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (firm.getUserType() == 2) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ String content = "https://cd.api.zswlgz.com/deviceid?frimId=" + firm.getFirmId();
|
|
|
+ QrCodeUtil.generate(content, 300, 300, ImgUtil.IMAGE_TYPE_PNG, response.getOutputStream());
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("生成二维码失败", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 邀请员工加入企业
|
|
|
+ */
|
|
|
+ @PostMapping("/add-firm-user")
|
|
|
+ public Result<?> addFirmUser(HttpServletRequest request, @RequestParam("firmId") Long firmId) {
|
|
|
+ UserInfo cacheUser = getUserFromToken(request);
|
|
|
+ if (cacheUser == null) {
|
|
|
+ return Result.failed("登录已过期");
|
|
|
+ }
|
|
|
+
|
|
|
+ UserFirm userFirm = new UserFirm();
|
|
|
+ userFirm.setUserId(cacheUser.getId());
|
|
|
+ userFirm.setFirmId(firmId);
|
|
|
+ userFirm.setUserType(2L);
|
|
|
+
|
|
|
+ try {
|
|
|
+ boolean saved = userFirmService.save(userFirm);
|
|
|
+ if (saved) {
|
|
|
+ FirmInfo firmInfo = firmInfoService.getById(firmId);
|
|
|
+ return Result.success("你已经成功加入" + firmInfo.getName() + ",现在充电享受企业专享价");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ return Result.failed(e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ return Result.failed("加入失败");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从请求头获取token并从缓存中获取用户信息
|
|
|
+ */
|
|
|
+ private UserInfo getUserFromToken(HttpServletRequest request) {
|
|
|
+ String token = request.getHeader("token");
|
|
|
+ if (token == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ Object obj = redisTemplate.opsForValue().get(TOKEN_PREFIX + token);
|
|
|
+ if (obj instanceof UserInfo) {
|
|
|
+ // 刷新token过期时间
|
|
|
+ redisTemplate.expire(TOKEN_PREFIX + token, 30, TimeUnit.MINUTES);
|
|
|
+ return (UserInfo) obj;
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 保存用户信息到缓存
|
|
|
+ */
|
|
|
+ private void saveUserToCache(String token, UserInfo userInfo) {
|
|
|
+ redisTemplate.opsForValue().set(TOKEN_PREFIX + token, userInfo, 30, TimeUnit.MINUTES);
|
|
|
+ }
|
|
|
+}
|