VipUserQuanyiController.java 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. package com.ruoyi.app.user;
  2. import java.text.ParseException;
  3. import java.text.SimpleDateFormat;
  4. import java.time.LocalDate;
  5. import java.time.format.DateTimeFormatter;
  6. import java.util.*;
  7. import java.util.stream.Collectors;
  8. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  9. import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
  10. import com.baomidou.mybatisplus.core.metadata.IPage;
  11. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  12. import com.github.pagehelper.PageHelper;
  13. import com.ruoyi.app.user.dto.ReceiveQuanyiInput;
  14. import com.ruoyi.common.annotation.Anonymous;
  15. import com.ruoyi.common.annotation.RepeatSubmit;
  16. import com.ruoyi.common.core.domain.R;
  17. import com.ruoyi.common.exception.ServiceException;
  18. import com.ruoyi.common.utils.MessageUtils;
  19. import com.ruoyi.common.utils.StringUtils;
  20. import com.ruoyi.system.domain.SalesPromotion;
  21. import com.ruoyi.system.domain.VipQuanyi;
  22. import com.ruoyi.system.service.ISalesPromotionService;
  23. import com.ruoyi.system.service.IVipQuanyiService;
  24. import com.ruoyi.system.utils.Auth;
  25. import com.ruoyi.system.utils.JwtUtil;
  26. import io.swagger.annotations.Api;
  27. import io.swagger.annotations.ApiOperation;
  28. import org.apache.commons.lang3.time.DateUtils;
  29. import org.springframework.security.access.prepost.PreAuthorize;
  30. import org.springframework.beans.factory.annotation.Autowired;
  31. import org.springframework.transaction.annotation.Transactional;
  32. import org.springframework.web.bind.annotation.*;
  33. import com.ruoyi.common.core.controller.BaseController;
  34. import com.ruoyi.common.core.domain.AjaxResult;
  35. import com.ruoyi.system.domain.VipUserQuanyi;
  36. import com.ruoyi.system.service.IVipUserQuanyiService;
  37. import com.ruoyi.common.core.page.TableDataInfo;
  38. import redis.clients.jedis.Jedis;
  39. /**
  40. * 用户优惠券
  41. * Controller
  42. *
  43. * @author ruoyi
  44. * @date 2025-05-09
  45. */
  46. @Api("用户优惠券管理")
  47. @RestController
  48. @RequestMapping("/system/userQuanyi")
  49. public class VipUserQuanyiController extends BaseController {
  50. @Autowired
  51. private IVipUserQuanyiService vipUserQuanyiService;
  52. @Autowired
  53. private IVipQuanyiService vipQuanyiService;
  54. @Autowired
  55. private ISalesPromotionService promotionService;
  56. @Anonymous
  57. @Auth
  58. @ApiOperation("可用活动分页列表")
  59. @GetMapping("/promotionList")
  60. public R<IPage<SalesPromotion>> promotionList(@RequestHeader String token,
  61. @RequestParam Integer pageNum,
  62. @RequestParam Integer pageSize,
  63. // @RequestParam String language,
  64. @RequestParam(defaultValue = "") String mdId,
  65. @RequestParam(defaultValue = "") String price
  66. ) {
  67. System.out.println("promotionList");
  68. JwtUtil jwtUtil = new JwtUtil();
  69. String uid = jwtUtil.getusid(token);
  70. String language = getLanguage(uid);
  71. IPage<SalesPromotion> stlist = new Page<>(pageNum, pageSize);
  72. LambdaQueryWrapper<SalesPromotion> queryWrapper = new LambdaQueryWrapper<>();
  73. queryWrapper.eq(SalesPromotion::getSalesState, "0");
  74. queryWrapper.ge(SalesPromotion::getEndTime,
  75. DateUtils.truncate(new Date(), java.util.Calendar.DAY_OF_MONTH));
  76. queryWrapper.eq(SalesPromotion::getDeleted, "0");
  77. queryWrapper.eq(SalesPromotion::getLanguage, language);
  78. if (!"".equals(price)) {
  79. queryWrapper.and(is -> is.le(SalesPromotion::getSalesCondition, price).or().isNull(SalesPromotion::getSalesCondition));
  80. }
  81. if (!"".equals(mdId)) {
  82. // 查询平台优惠券(mdId为空)或当前商家的优惠券
  83. queryWrapper.and(wrapper -> wrapper.isNull(SalesPromotion::getShId)
  84. .or()
  85. .eq(SalesPromotion::getShId, mdId));
  86. }
  87. IPage<SalesPromotion> list = promotionService.page(stlist, queryWrapper);
  88. return R.ok(list);
  89. }
  90. private String getLanguage(String uid) {
  91. Jedis jedis = new Jedis("localhost", 6379);
  92. String lang = jedis.get(uid);
  93. System.out.println("从redis获取的语言是:" + lang);
  94. String result = "0";
  95. if ("zh-CN".equals(lang)) {
  96. result = "2";
  97. }
  98. if ("zh-TW".equals(lang)) {
  99. result = "3";
  100. }
  101. if ("en-US".equals(lang)) {
  102. result = "1";
  103. }
  104. System.out.println("获取的语言是:" + result);
  105. return result;
  106. }
  107. /**
  108. * @param token
  109. * @param mdId
  110. * @param price
  111. * @param yfPrice
  112. * @return
  113. */
  114. @ApiOperation("用户生成订单时可用优惠券列表")
  115. @GetMapping("/orderyhList")
  116. @Anonymous
  117. @Auth
  118. public R<List<VipUserQuanyi>> orderyhList(@RequestHeader String token,
  119. @RequestParam(defaultValue = "") String mdId,
  120. @RequestParam(defaultValue = "") String price,
  121. @RequestParam(defaultValue = "") String yfPrice) {
  122. JwtUtil jwtUtil = new JwtUtil();
  123. String uid = jwtUtil.getusid(token);
  124. String language = getLanguage(uid);
  125. LambdaQueryWrapper<VipUserQuanyi> queryWrapper = new LambdaQueryWrapper<>();
  126. // 基础必选条件
  127. queryWrapper.eq(VipUserQuanyi::getUserId, uid)
  128. .eq(VipUserQuanyi::getDeleted, "0")
  129. .eq(VipUserQuanyi::getLanguage, language)
  130. .gt(VipUserQuanyi::getValidity, DateUtils.truncate(new Date(), Calendar.DAY_OF_MONTH))
  131. .eq(VipUserQuanyi::getState, "0");
  132. //传0 表示自提,去掉运费优惠券
  133. if (yfPrice.equals("0")) {
  134. queryWrapper.ne(VipUserQuanyi::getYhfangs, "1");
  135. }
  136. // 门店ID条件
  137. if (StringUtils.isNotBlank(mdId)) {
  138. queryWrapper.and(wrapper ->
  139. wrapper.eq(VipUserQuanyi::getShId, mdId)
  140. .or()
  141. .isNull(VipUserQuanyi::getShId)
  142. );
  143. }
  144. queryWrapper.and(sub -> {
  145. sub.nested(nonShip ->
  146. nonShip.nested(cond -> cond.le(VipUserQuanyi::getTiaojian, price)
  147. .or()
  148. .nested(cn -> cn.isNull(VipUserQuanyi::getTiaojian))
  149. )
  150. );
  151. }
  152. );
  153. // // 动态构建价格条件
  154. // boolean hasPrice = StringUtils.isNotBlank(price);
  155. // boolean hasYfPrice = StringUtils.isNotBlank(yfPrice);
  156. // if (hasPrice || hasYfPrice) {
  157. // queryWrapper.and(wrap -> {
  158. // // 非运费条件块(sales_type <> 1)
  159. // if (hasPrice) {
  160. // wrap.and(sub ->
  161. // sub.nested(nonShip ->
  162. // nonShip.nested(cond -> cond.le(VipUserQuanyi::getTiaojian, price).ne(VipUserQuanyi::getYhfangs, "1"))
  163. // .or()
  164. // .nested(cond -> cond.isNull(VipUserQuanyi::getTiaojian).ne(VipUserQuanyi::getYhfangs, "1"))
  165. // )
  166. // );
  167. // }
  168. //
  169. // // 运费条件块(sales_type = 1)
  170. // if (hasYfPrice) {
  171. // wrap.or(sub ->
  172. // sub.nested(ship ->
  173. // ship.nested(cond -> cond.le(VipUserQuanyi::getTiaojian, yfPrice).eq(VipUserQuanyi::getYhfangs, "1"))
  174. // .or()
  175. // .nested(cond -> cond.isNull(VipUserQuanyi::getTiaojian).eq(VipUserQuanyi::getYhfangs, "1"))
  176. // )
  177. // );
  178. // }
  179. // });
  180. // }
  181. // 排序
  182. queryWrapper.orderByDesc(VipUserQuanyi::getValidity);
  183. List<VipUserQuanyi> list = vipUserQuanyiService.list(queryWrapper);
  184. List<VipUserQuanyi> result = list.stream()
  185. .filter(item -> item.getQuanyiId() != null) // 可选:过滤掉quanyiId为null的
  186. .collect(Collectors.groupingBy(
  187. VipUserQuanyi::getQuanyiId,
  188. Collectors.minBy(Comparator.comparing(VipUserQuanyi::getValidity))
  189. ))
  190. .values().stream()
  191. .filter(Optional::isPresent)
  192. .map(Optional::get)
  193. .collect(Collectors.toList());
  194. return R.ok(result);
  195. }
  196. @ApiOperation("用户总的可用、过期优惠券分页列表")
  197. @GetMapping("/list")
  198. @Anonymous
  199. @Auth
  200. public R<IPage<VipUserQuanyi>> expiredList(@RequestHeader String token,
  201. @RequestParam Integer pageNum,
  202. @RequestParam Integer pageSize,
  203. @RequestParam int listType
  204. ) {
  205. JwtUtil jwtUtil = new JwtUtil();
  206. String uid = jwtUtil.getusid(token);
  207. String language = getLanguage(uid);
  208. IPage<VipUserQuanyi> stlist = new Page<>(pageNum, pageSize);
  209. LambdaQueryWrapper<VipUserQuanyi> queryWrapper = new LambdaQueryWrapper<>();
  210. queryWrapper.eq(VipUserQuanyi::getUserId, uid);
  211. queryWrapper.eq(VipUserQuanyi::getLanguage, language);
  212. if (listType == 2) {
  213. queryWrapper.lt(VipUserQuanyi::getValidity, DateUtils.truncate(new Date(), java.util.Calendar.DAY_OF_MONTH));
  214. queryWrapper.eq(VipUserQuanyi::getState, "0");
  215. }
  216. if (listType == 1) {
  217. queryWrapper.ge(VipUserQuanyi::getValidity, DateUtils.truncate(new Date(), java.util.Calendar.DAY_OF_MONTH));
  218. queryWrapper.eq(VipUserQuanyi::getState, "0");
  219. }
  220. queryWrapper.orderBy(true, false, VipUserQuanyi::getValidity);
  221. IPage<VipUserQuanyi> list = vipUserQuanyiService.page(stlist, queryWrapper);
  222. return R.ok(list);
  223. }
  224. @Anonymous
  225. @Auth
  226. @ApiOperation("待领取优惠券列表")
  227. @GetMapping("/userUnclaimedList")
  228. public TableDataInfo userUnclaimedList(@RequestHeader String token,
  229. @RequestParam Integer pageNum,
  230. @RequestParam Integer pageSize,
  231. @RequestParam(required = false) Long mdId
  232. ) {
  233. JwtUtil jwtUtil = new JwtUtil();
  234. String uid = jwtUtil.getusid(token);
  235. String language = getLanguage(uid);
  236. PageHelper.startPage(pageNum, pageSize);
  237. List<VipQuanyi> list = vipQuanyiService.userUnclaimedList(Long.valueOf(uid), language, mdId);
  238. return getDataTable(list);
  239. }
  240. @Anonymous
  241. @Auth
  242. @ApiOperation("用户领取优惠券")
  243. @PostMapping("/receive")
  244. @RepeatSubmit(interval = 1000, message = "请求过于频繁")
  245. @Transactional(rollbackFor = Exception.class)
  246. public R<String> receive(@RequestHeader String token, @RequestBody ReceiveQuanyiInput input) {
  247. JwtUtil jwtUtil = new JwtUtil();
  248. String userId = jwtUtil.getusid(token);
  249. LambdaQueryWrapper<VipQuanyi> query = new LambdaQueryWrapper<>();
  250. query.eq(VipQuanyi::getId, input.getQuanyiId());
  251. query.eq(VipQuanyi::getDeleted, "0");
  252. VipQuanyi quanyi = vipQuanyiService.getOne(query);
  253. if (quanyi == null) {
  254. throw new ServiceException(MessageUtils.message("no.youhuiquan.lqnodata"));
  255. }
  256. if ("1".equals(quanyi.getState())) {
  257. throw new ServiceException(MessageUtils.message("no.youhuiquan.lqyhqgb"));
  258. }
  259. if (quanyi.getTotal() != null && quanyi.getTotal().equals(quanyi.getClaimedCount())) {
  260. throw new ServiceException(MessageUtils.message("no.youhuiquan.lqyhqolqw"));
  261. }
  262. // 3. 检查用户输入数量是否合法
  263. if (input.num <= 0) {
  264. throw new ServiceException(MessageUtils.message("no.youhuiquan.lqyhqdy"));
  265. }
  266. // 4. 检查用户还能领取多少
  267. // 4.1 检查每人领取限制
  268. LambdaQueryWrapper<VipUserQuanyi> userCouponQuery = new LambdaQueryWrapper<>();
  269. userCouponQuery.eq(VipUserQuanyi::getUserId, userId)
  270. .eq(VipUserQuanyi::getQuanyiId, input.quanyiId);
  271. long userAlreadyClaimed = vipUserQuanyiService.count(userCouponQuery);
  272. int remainingPerUser = quanyi.getLqxz() - (int) userAlreadyClaimed;
  273. if (remainingPerUser <= 0) {
  274. throw new ServiceException(MessageUtils.message("no.youhuiquan.lqyhqysx"));
  275. }
  276. // 4.2 检查剩余库存
  277. long remainingStock = quanyi.getTotal() - quanyi.getClaimedCount();
  278. if (remainingStock <= 0) {
  279. throw new ServiceException(MessageUtils.message("no.youhuiquan.lqyhqolqw"));
  280. }
  281. // 4.3 确定实际可领取数量
  282. int actualReceiveNum = Math.min(input.num, remainingPerUser);
  283. actualReceiveNum = (int) Math.min(actualReceiveNum, remainingStock);
  284. Long version = quanyi.getVersion() != null ? quanyi.getVersion() + 1 : 0L;
  285. // 5. 使用乐观锁更新优惠券库存(防止并发超领)
  286. boolean updateSuccess = vipQuanyiService.update(
  287. new LambdaUpdateWrapper<VipQuanyi>()
  288. .eq(VipQuanyi::getId, input.quanyiId)
  289. .eq(VipQuanyi::getVersion, quanyi.getVersion()) // 乐观锁条件
  290. .set(VipQuanyi::getClaimedCount, quanyi.getClaimedCount() + actualReceiveNum)
  291. .set(VipQuanyi::getVersion, version)
  292. );
  293. if (!updateSuccess) {
  294. throw new ServiceException(MessageUtils.message("no.youhuiquan.lqyhqybxg"));
  295. }
  296. // 6. 为用户添加优惠券记录
  297. List<VipUserQuanyi> couponsToAdd = new ArrayList<>();
  298. Date now = new Date();
  299. Date expireTime = calculateExpireTime(now, quanyi.getValidity());
  300. for (int i = 0; i < actualReceiveNum; i++) {
  301. VipUserQuanyi coupon = new VipUserQuanyi();
  302. coupon.setUserId(Long.valueOf(userId));
  303. coupon.setQuanyiId(input.quanyiId);
  304. coupon.setCode(generateCouponCode());
  305. coupon.setState("0"); // 0-未使用
  306. coupon.setCreateTime(now);
  307. coupon.setValidity(expireTime);
  308. coupon.setYhfangs(quanyi.getYhfangs());
  309. coupon.setYhnum(quanyi.getYhnum());
  310. coupon.setName(quanyi.getName());
  311. coupon.setShuoming(quanyi.getShuoming());
  312. coupon.setTiaojian(quanyi.getTiaojian());
  313. coupon.setDeleted("0");
  314. coupon.setShId(quanyi.getShId());
  315. coupon.setSpId(quanyi.getSpId());
  316. coupon.setLanguage(quanyi.getLanguage());
  317. coupon.setPoster(quanyi.getPoster());
  318. coupon.setType(quanyi.getType());
  319. coupon.setMerchantId(quanyi.getMerchantId());
  320. couponsToAdd.add(coupon);
  321. }
  322. vipUserQuanyiService.saveBatch(couponsToAdd);
  323. return R.ok();
  324. }
  325. // 生成优惠券码
  326. private String generateCouponCode() {
  327. return UUID.randomUUID().toString().replace("-", "").substring(0, 12).toUpperCase();
  328. }
  329. // 计算过期时间
  330. private Date calculateExpireTime(Date now, Integer validityDays) {
  331. if (validityDays == null || validityDays <= 0) {
  332. return null; // 表示永久有效
  333. }
  334. Calendar calendar = Calendar.getInstance();
  335. calendar.setTime(now);
  336. calendar.add(Calendar.DAY_OF_MONTH, validityDays);
  337. return calendar.getTime();
  338. }
  339. /**
  340. * 获取用户优惠券
  341. * 详细信息
  342. */
  343. @Anonymous
  344. @Auth
  345. @ApiOperation("用户领取优惠券详情")
  346. @GetMapping(value = "/{id}")
  347. public AjaxResult getInfo(@PathVariable("id") Long id) {
  348. return success(vipUserQuanyiService.selectVipUserQuanyiById(id));
  349. }
  350. }