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 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 data, HttpServletRequest request) { UserInfo cacheUser = getUserFromToken(request); if (cacheUser == null) { return Result.failed("登录已过期"); } List 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 result = new HashMap<>(); result.put("discountInfo", discountInfo); return Result.success(result); } /** * 微信小程序登录 */ @PostMapping("/login") @Transactional(rollbackFor = Exception.class) public Result login(@RequestBody Map 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 result = new HashMap<>(); result.put("userInfo", userInfo); result.put("token", token); return Result.success(result); } Map 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 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 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 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 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 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 result = new HashMap<>(); result.put("accountInfo", user); return Result.success(result); } /** * 获取Banner列表 */ @PostMapping("/getBanners") public Result getBanners() { List banners = bannerInfoService.lambdaQuery() .eq(BannerInfo::getStatus, 1) .orderByAsc(BannerInfo::getSort) .list(); Map result = new HashMap<>(); result.put("banners", banners); return Result.success(result); } /** * 获取广告位 */ @PostMapping("/getAdvertising") public Result getAdvertising() { List banners = advertisingService.lambdaQuery() .eq(Advertising::getStatus, 1) .orderByAsc(Advertising::getSort) .list(); Map 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 list = userFeedbackService.lambdaQuery() .eq(UserFeedback::getUserId, cacheUser.getId()) .orderByDesc(UserFeedback::getCreateTime) .list(); Map 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 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); } }