RewardService.java 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. package com.zxyj.service;
  2. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  3. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  4. import com.zxyj.dto.CreateRewardDTO;
  5. import com.zxyj.dto.RewardDTO;
  6. import com.zxyj.entity.Child;
  7. import com.zxyj.entity.PointsLog;
  8. import com.zxyj.entity.Reward;
  9. import com.zxyj.entity.User;
  10. import com.zxyj.mapper.ChildMapper;
  11. import com.zxyj.mapper.PointsLogMapper;
  12. import com.zxyj.mapper.RewardMapper;
  13. import com.zxyj.mapper.UserMapper;
  14. import com.zxyj.service.api.RewardServiceInterface;
  15. import javax.annotation.Resource;
  16. import lombok.extern.slf4j.Slf4j;
  17. import org.springframework.stereotype.Service;
  18. import org.springframework.transaction.annotation.Transactional;
  19. import java.util.Arrays;
  20. import java.util.Date;
  21. import java.util.List;
  22. import java.util.Map;
  23. import java.util.stream.Collectors;
  24. @Slf4j
  25. @Service
  26. public class RewardService implements RewardServiceInterface {
  27. @Resource
  28. private RewardMapper rewardMapper;
  29. @Resource
  30. private ChildMapper childMapper;
  31. @Resource
  32. private PointsLogMapper pointsLogMapper;
  33. @Resource
  34. private UserService userService;
  35. @Resource
  36. private UserMapper userMapper;
  37. /**
  38. * REWARD-001A: 孩子创建心愿单
  39. * 孩子添加想要的奖励到心愿单,状态为 pending_approval(待家长填写积分)
  40. */
  41. @Transactional
  42. public Long createWishlist(Long childId, CreateRewardDTO dto) {
  43. Child child = childMapper.selectById(childId);
  44. if (child == null) {
  45. throw new IllegalArgumentException("孩子不存在");
  46. }
  47. Reward reward = new Reward();
  48. reward.setFamilyId(child.getFamilyId());
  49. reward.setChildId(childId);
  50. reward.setTitle(dto.getTitle());
  51. reward.setCategory(dto.getCategory());
  52. // 孩子创建心愿时积分为0,等待家长填写
  53. reward.setPointsRequired(0);
  54. reward.setStatus("pending_approval"); // 待家长审批填写积分
  55. reward.setIsTemplate(0);
  56. reward.setCreatedAt(new Date());
  57. reward.setUpdatedAt(new Date());
  58. rewardMapper.insert(reward);
  59. log.info("孩子{}创建心愿: {}", childId, dto.getTitle());
  60. return reward.getId();
  61. }
  62. /**
  63. * REWARD-001A: 家长填写积分,心愿进入奖励库
  64. * 家长审批并填写所需积分后,心愿转为可兑换状态
  65. */
  66. @Transactional
  67. public boolean approveWishlist(Long rewardId, Long parentId, Integer pointsRequired) {
  68. Reward reward = rewardMapper.selectById(rewardId);
  69. if (reward == null) {
  70. return false;
  71. }
  72. // 验证家长权限:必须是同一家庭的家长
  73. User parent = userMapper.selectById(parentId);
  74. if (parent == null || !parent.getFamilyId().equals(reward.getFamilyId())) {
  75. log.warn("家长{}无权操作奖励{}", parentId, rewardId);
  76. return false;
  77. }
  78. // 验证状态:只有待审批的心愿可以填写积分
  79. if (!"pending_approval".equals(reward.getStatus())) {
  80. log.warn("奖励{}状态不是待审批: {}", rewardId, reward.getStatus());
  81. return false;
  82. }
  83. // 更新积分和状态
  84. reward.setPointsRequired(pointsRequired);
  85. reward.setStatus("available"); // 进入奖励库,可兑换
  86. reward.setUpdatedAt(new Date());
  87. rewardMapper.updateById(reward);
  88. log.info("家长{}审批心愿{},设置积分: {}", parentId, rewardId, pointsRequired);
  89. return true;
  90. }
  91. /**
  92. * 创建奖励(原有功能:家长直接创建奖励)
  93. */
  94. @Transactional
  95. public Long createReward(Long userId, CreateRewardDTO dto) {
  96. // 参数校验
  97. if (dto.getTitle() == null || dto.getTitle().trim().isEmpty()) {
  98. throw new IllegalArgumentException("请填写心愿名称");
  99. }
  100. if (dto.getPointsRequired() == null || dto.getPointsRequired() < 1) {
  101. throw new IllegalArgumentException("请填写有效的积分值");
  102. }
  103. // 获取用户信息并验证家庭归属
  104. User user = userService.getUserInfo(userId);
  105. if (user == null || user.getFamilyId() == null) {
  106. throw new IllegalArgumentException("用户必须属于某个家庭");
  107. }
  108. Reward reward = new Reward();
  109. reward.setFamilyId(user.getFamilyId()); // 强制关联家庭
  110. reward.setChildId(dto.getChildId());
  111. reward.setTitle(dto.getTitle().trim());
  112. reward.setPointsRequired(dto.getPointsRequired());
  113. reward.setCategory(dto.getCategory());
  114. reward.setStatus("available");
  115. reward.setIsTemplate(0);
  116. reward.setCreatedAt(new Date());
  117. reward.setUpdatedAt(new Date());
  118. rewardMapper.insert(reward);
  119. return reward.getId();
  120. }
  121. public List<RewardDTO> getWishlist(Long childId) {
  122. // 获取孩子信息以验证家庭归属
  123. Child child = childMapper.selectById(childId);
  124. if (child == null) {
  125. return null;
  126. }
  127. // 按家庭ID和孩子ID筛选奖励,确保奖励必须关联家庭
  128. List<Reward> rewards = rewardMapper.selectList(new LambdaQueryWrapper<Reward>()
  129. .eq(Reward::getFamilyId, child.getFamilyId())
  130. .eq(Reward::getChildId, childId)
  131. .eq(Reward::getStatus, "available")
  132. .orderByAsc(Reward::getPointsRequired));
  133. final int currentPoints = child != null ? child.getTotalPoints() : 0;
  134. return rewards.stream().map(r -> {
  135. RewardDTO dto = new RewardDTO();
  136. dto.setId(r.getId());
  137. dto.setTitle(r.getTitle());
  138. dto.setPointsRequired(r.getPointsRequired());
  139. dto.setCategory(r.getCategory());
  140. dto.setStatus(r.getStatus());
  141. dto.setExchangeApproved(r.getExchangeApproved());
  142. dto.setProgress(currentPoints);
  143. return dto;
  144. }).collect(Collectors.toList());
  145. }
  146. @Transactional
  147. public Map<String, Object> exchangeReward(Long rewardId, Long childId) {
  148. Reward reward = rewardMapper.selectById(rewardId);
  149. if (reward == null || !reward.getChildId().equals(childId)) {
  150. return null;
  151. }
  152. if (!"available".equals(reward.getStatus())) {
  153. return null;
  154. }
  155. Child child = childMapper.selectById(childId);
  156. if (child.getTotalPoints() < reward.getPointsRequired()) {
  157. return null; // 积分不足
  158. }
  159. // 扣除积分
  160. child.setTotalPoints(child.getTotalPoints() - reward.getPointsRequired());
  161. child.setUpdatedAt(new Date());
  162. childMapper.updateById(child);
  163. // 记录积分消耗
  164. PointsLog log = new PointsLog();
  165. log.setChildId(childId);
  166. log.setAmount(-reward.getPointsRequired());
  167. log.setType("spend");
  168. log.setDescription("兑换奖励: " + reward.getTitle());
  169. log.setCreatedAt(new Date());
  170. pointsLogMapper.insert(log);
  171. // 更新奖励状态
  172. reward.setStatus("pending"); // 待审批
  173. reward.setUpdatedAt(new Date());
  174. rewardMapper.updateById(reward);
  175. Map<String, Object> result = new java.util.HashMap<>();
  176. result.put("success", true);
  177. result.put("newBalance", child.getTotalPoints());
  178. return result;
  179. }
  180. @Transactional
  181. public boolean approveReward(Long rewardId, boolean approved) {
  182. Reward reward = rewardMapper.selectById(rewardId);
  183. if (reward == null) {
  184. return false;
  185. }
  186. if (approved) {
  187. reward.setStatus("exchanged");
  188. reward.setExchangeApproved(1);
  189. reward.setExchangedAt(new Date());
  190. } else {
  191. // 拒绝,返还积分
  192. Child child = childMapper.selectById(reward.getChildId());
  193. child.setTotalPoints(child.getTotalPoints() + reward.getPointsRequired());
  194. child.setUpdatedAt(new Date());
  195. childMapper.updateById(child);
  196. // 记录积分退还
  197. PointsLog log = new PointsLog();
  198. log.setChildId(reward.getChildId());
  199. log.setAmount(reward.getPointsRequired());
  200. log.setType("refund");
  201. log.setDescription("奖励兑换被拒绝,积分退还");
  202. log.setCreatedAt(new Date());
  203. pointsLogMapper.insert(log);
  204. reward.setStatus("rejected");
  205. reward.setExchangeApproved(0);
  206. }
  207. reward.setUpdatedAt(new Date());
  208. rewardMapper.updateById(reward);
  209. return true;
  210. }
  211. public List<Reward> getTemplates() {
  212. // 模板也必须关联家庭,这里返回所有家庭的模板(family_id > 0)
  213. return rewardMapper.selectList(new LambdaQueryWrapper<Reward>()
  214. .eq(Reward::getIsTemplate, 1)
  215. .gt(Reward::getFamilyId, 0)); // 确保模板关联到具体家庭
  216. }
  217. public Page<Reward> getExchangeHistory(Long childId, Integer page, Integer size) {
  218. Page<Reward> pageParam = new Page<>(page, size);
  219. return rewardMapper.selectPage(pageParam, new LambdaQueryWrapper<Reward>()
  220. .eq(Reward::getChildId, childId)
  221. .in(Reward::getStatus, Arrays.asList("exchanged", "rejected"))
  222. .orderByDesc(Reward::getExchangedAt));
  223. }
  224. /**
  225. * REWARD-002A: 获取家庭奖励库
  226. * 返回家庭所有可用的奖励(不含模板),多家长可查看
  227. */
  228. public List<RewardDTO> getFamilyRewardLibrary(Long familyId) {
  229. List<Reward> rewards = rewardMapper.selectList(new LambdaQueryWrapper<Reward>()
  230. .eq(Reward::getFamilyId, familyId)
  231. .eq(Reward::getIsTemplate, 0)
  232. .in(Reward::getStatus, Arrays.asList("available", "pending_approval"))
  233. .orderByAsc(Reward::getPointsRequired));
  234. return rewards.stream().map(r -> {
  235. RewardDTO dto = new RewardDTO();
  236. dto.setId(r.getId());
  237. dto.setTitle(r.getTitle());
  238. dto.setPointsRequired(r.getPointsRequired());
  239. dto.setCategory(r.getCategory());
  240. dto.setStatus(r.getStatus());
  241. dto.setChildId(r.getChildId());
  242. dto.setExchangeApproved(r.getExchangeApproved());
  243. return dto;
  244. }).collect(Collectors.toList());
  245. }
  246. /**
  247. * REWARD-002A: 家长修改奖励积分(多家长可调整)
  248. * 只有同一家庭的家长可以修改
  249. */
  250. @Transactional
  251. public boolean updateRewardPoints(Long rewardId, Long parentId, Integer newPoints) {
  252. Reward reward = rewardMapper.selectById(rewardId);
  253. if (reward == null) {
  254. return false;
  255. }
  256. // 验证家长权限
  257. User parent = userMapper.selectById(parentId);
  258. if (parent == null || !parent.getFamilyId().equals(reward.getFamilyId())) {
  259. log.warn("家长{}无权修改奖励{}", parentId, rewardId);
  260. return false;
  261. }
  262. // 只有可用状态或待审批状态可以修改
  263. if (!"available".equals(reward.getStatus()) && !"pending_approval".equals(reward.getStatus())) {
  264. log.warn("奖励{}状态不可修改: {}", rewardId, reward.getStatus());
  265. return false;
  266. }
  267. reward.setPointsRequired(newPoints);
  268. reward.setUpdatedAt(new Date());
  269. rewardMapper.updateById(reward);
  270. log.info("家长{}修改奖励{}积分为: {}", parentId, rewardId, newPoints);
  271. return true;
  272. }
  273. /**
  274. * REWARD-002B: 获取孩子的待审批心愿单(按孩子隔离)
  275. * 只返回指定孩子的心愿,不包含其他孩子
  276. */
  277. public List<RewardDTO> getChildPendingWishlists(Long childId) {
  278. Child child = childMapper.selectById(childId);
  279. if (child == null) {
  280. return null;
  281. }
  282. List<Reward> rewards = rewardMapper.selectList(new LambdaQueryWrapper<Reward>()
  283. .eq(Reward::getFamilyId, child.getFamilyId())
  284. .eq(Reward::getChildId, childId)
  285. .eq(Reward::getStatus, "pending_approval")
  286. .orderByDesc(Reward::getCreatedAt));
  287. return rewards.stream().map(r -> {
  288. RewardDTO dto = new RewardDTO();
  289. dto.setId(r.getId());
  290. dto.setTitle(r.getTitle());
  291. dto.setPointsRequired(r.getPointsRequired());
  292. dto.setCategory(r.getCategory());
  293. dto.setStatus(r.getStatus());
  294. dto.setChildId(r.getChildId());
  295. return dto;
  296. }).collect(Collectors.toList());
  297. }
  298. /**
  299. * REWARD-002B: 获取家长待处理的心愿审批列表(所有孩子)
  300. * 家长可以看到所有孩子的待审批心愿
  301. */
  302. public List<RewardDTO> getPendingApprovalList(Long familyId) {
  303. List<Reward> rewards = rewardMapper.selectList(new LambdaQueryWrapper<Reward>()
  304. .eq(Reward::getFamilyId, familyId)
  305. .eq(Reward::getStatus, "pending_approval")
  306. .eq(Reward::getIsTemplate, 0)
  307. .orderByDesc(Reward::getCreatedAt));
  308. return rewards.stream().map(r -> {
  309. RewardDTO dto = new RewardDTO();
  310. dto.setId(r.getId());
  311. dto.setTitle(r.getTitle());
  312. dto.setPointsRequired(r.getPointsRequired());
  313. dto.setCategory(r.getCategory());
  314. dto.setStatus(r.getStatus());
  315. dto.setChildId(r.getChildId());
  316. // 获取孩子昵称
  317. Child child = childMapper.selectById(r.getChildId());
  318. if (child != null) {
  319. dto.setChildName(child.getNickname());
  320. }
  321. return dto;
  322. }).collect(Collectors.toList());
  323. }
  324. }