|
|
@@ -0,0 +1,397 @@
|
|
|
+package com.jeeplus.modules.WorkKnowledgeBase.service;
|
|
|
+
|
|
|
+import com.jeeplus.common.service.CrudService;
|
|
|
+import com.jeeplus.common.utils.StringUtils;
|
|
|
+import com.jeeplus.modules.WorkKnowledgeBase.dao.WorkKnowledgeBasePointDetailDao;
|
|
|
+import com.jeeplus.modules.WorkKnowledgeBase.dao.WorkKnowledgeBasePointRuleDao;
|
|
|
+import com.jeeplus.modules.WorkKnowledgeBase.dao.WorkKnowledgeBaseUserExchangeFlagDao;
|
|
|
+import com.jeeplus.modules.WorkKnowledgeBase.entity.*;
|
|
|
+import com.jeeplus.modules.sys.entity.User;
|
|
|
+import com.jeeplus.modules.sys.utils.UserUtils;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
+
|
|
|
+import java.util.Date;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 知识库积分兑换 Service
|
|
|
+ * 提供:基础积分兑换、贡献积分兑换、兑换记录查询等功能
|
|
|
+ * @author: 徐滕
|
|
|
+ * @version: 2026-06-18
|
|
|
+ */
|
|
|
+@Service
|
|
|
+@Transactional(readOnly = true)
|
|
|
+public class WorkKnowledgeBasePointExchangeService extends CrudService<WorkKnowledgeBasePointDetailDao, WorkKnowledgeBasePointDetail> {
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private WorkKnowledgeBasePointDetailDao pointDetailDao;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private WorkKnowledgeBaseUserExchangeFlagDao exchangeFlagDao;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private WorkKnowledgeBasePointRuleDao pointRuleDao;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取用户当前可用基础积分(分类ID=1)
|
|
|
+ * @param userId 用户ID
|
|
|
+ * @return 可用基础积分
|
|
|
+ */
|
|
|
+ public int getUserBasicPoints(String userId) {
|
|
|
+ return pointDetailDao.sumUserPointByColumnId(userId, "1");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取用户当前可用贡献积分(分类ID=2)
|
|
|
+ * @param userId 用户ID
|
|
|
+ * @return 可用贡献积分
|
|
|
+ */
|
|
|
+ public int getUserContributionPoints(String userId) {
|
|
|
+ return pointDetailDao.sumUserPointByColumnId(userId, "2");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 执行基础积分兑换
|
|
|
+ * @param userId 用户ID
|
|
|
+ * @return 兑换结果信息
|
|
|
+ */
|
|
|
+ @Transactional(readOnly = false)
|
|
|
+ public String executeBasicPointExchange(String userId) {
|
|
|
+ // 1. 获取用户当前可用基础积分
|
|
|
+ int basicPoints = getUserBasicPoints(userId);
|
|
|
+
|
|
|
+ // 2. 获取用户首次兑换标记
|
|
|
+ WorkKnowledgeBaseUserExchangeFlag flag = exchangeFlagDao.getByUserId(userId);
|
|
|
+ if (flag == null) {
|
|
|
+ // 如果不存在标记,创建默认标记(未兑换)
|
|
|
+ flag = new WorkKnowledgeBaseUserExchangeFlag();
|
|
|
+ flag.setId(com.jeeplus.common.utils.IdGen.uuid());
|
|
|
+ flag.setUserId(userId);
|
|
|
+ flag.setIsFirstExchanged(0);
|
|
|
+ flag.setDelFlag("0");
|
|
|
+ flag.preInsert();
|
|
|
+ exchangeFlagDao.insert(flag);
|
|
|
+ }
|
|
|
+
|
|
|
+ boolean isFirstExchange = (flag.getIsFirstExchanged() == 0 || flag.getIsFirstExchanged() == null);
|
|
|
+
|
|
|
+ // 3. 读取兑换规则配置
|
|
|
+ // rule_type=91: 首次兑换门槛(默认300)
|
|
|
+ // rule_type=92: 非首次兑换门槛(默认100)
|
|
|
+ // rule_type=93: 累积步长(默认100)
|
|
|
+ Integer firstThreshold = getRuleValueByType("91"); // 首次门槛
|
|
|
+ Integer nonFirstThreshold = getRuleValueByType("92"); // 非首次门槛
|
|
|
+ Integer stepValue = getRuleValueByType("93"); // 累积步长
|
|
|
+
|
|
|
+ if (firstThreshold == null) firstThreshold = 300;
|
|
|
+ if (nonFirstThreshold == null) nonFirstThreshold = 100;
|
|
|
+ if (stepValue == null) stepValue = 100;
|
|
|
+
|
|
|
+ // 4. 判断是否满足兑换条件并计算可兑换额度
|
|
|
+ int exchangeablePoints = 0;
|
|
|
+ String ruleTypeName = "";
|
|
|
+
|
|
|
+ if (isFirstExchange) {
|
|
|
+ // 首次兑换:需要 >= 300
|
|
|
+ if (basicPoints < firstThreshold) {
|
|
|
+ return "基础积分不足" + firstThreshold + "分,无法进行首次兑换(当前" + basicPoints + "分)";
|
|
|
+ }
|
|
|
+ // 可兑换额度 = 向下取整百
|
|
|
+ exchangeablePoints = (basicPoints / stepValue) * stepValue;
|
|
|
+ ruleTypeName = "首次兑换";
|
|
|
+ } else {
|
|
|
+ // 非首次兑换:需要 >= 100
|
|
|
+ if (basicPoints < nonFirstThreshold) {
|
|
|
+ return "基础积分不足" + nonFirstThreshold + "分,无法进行兑换(当前" + basicPoints + "分)";
|
|
|
+ }
|
|
|
+ // 可兑换额度 = 向下取整百
|
|
|
+ exchangeablePoints = (basicPoints / stepValue) * stepValue;
|
|
|
+ ruleTypeName = "非首次兑换";
|
|
|
+ }
|
|
|
+
|
|
|
+ if (exchangeablePoints <= 0) {
|
|
|
+ return "无可兑换积分";
|
|
|
+ }
|
|
|
+
|
|
|
+ // 5. 创建兑换扣减记录(change_type=9)
|
|
|
+ WorkKnowledgeBasePointDetail detail = new WorkKnowledgeBasePointDetail();
|
|
|
+ detail.setId(com.jeeplus.common.utils.IdGen.uuid());
|
|
|
+ detail.setUserId(userId);
|
|
|
+ detail.setChangeType(9); // 9表示兑换扣减
|
|
|
+ detail.setCategoryId("9"); // 兑换积分分类ID
|
|
|
+ detail.setPoint(-exchangeablePoints); // 负数表示扣减
|
|
|
+ detail.setDelFlag("0");
|
|
|
+ detail.preInsert();
|
|
|
+ pointDetailDao.insert(detail);
|
|
|
+
|
|
|
+ // 6. 如果是首次兑换,更新标记为已兑换
|
|
|
+ if (isFirstExchange) {
|
|
|
+ exchangeFlagDao.updateFirstExchangeStatus(userId, 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ return "兑换成功!" + ruleTypeName + "扣除" + exchangeablePoints + "分,剩余" + (basicPoints - exchangeablePoints) + "分";
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 执行贡献积分兑换(全额清零)
|
|
|
+ * @param userId 用户ID
|
|
|
+ * @return 兑换结果信息
|
|
|
+ */
|
|
|
+ @Transactional(readOnly = false)
|
|
|
+ public String executeContributionPointExchange(String userId) {
|
|
|
+ // 1. 获取用户当前可用贡献积分
|
|
|
+ int contributionPoints = getUserContributionPoints(userId);
|
|
|
+
|
|
|
+ if (contributionPoints <= 0) {
|
|
|
+ return "无可用贡献积分,无法兑换";
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 创建兑换扣减记录(change_type=9)
|
|
|
+ WorkKnowledgeBasePointDetail detail = new WorkKnowledgeBasePointDetail();
|
|
|
+ detail.setId(com.jeeplus.common.utils.IdGen.uuid());
|
|
|
+ detail.setUserId(userId);
|
|
|
+ detail.setChangeType(9); // 9表示兑换扣减
|
|
|
+ detail.setCategoryId("9"); // 兑换积分分类ID
|
|
|
+ detail.setPoint(-contributionPoints); // 负数表示扣减,全额清零
|
|
|
+ detail.setDelFlag("0");
|
|
|
+ detail.preInsert();
|
|
|
+ pointDetailDao.insert(detail);
|
|
|
+
|
|
|
+ return "兑换成功!贡献积分全额扣除" + contributionPoints + "分,剩余0分";
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 批量执行所有用户的基础积分兑换
|
|
|
+ * @return 兑换结果统计
|
|
|
+ */
|
|
|
+ @Transactional(readOnly = false)
|
|
|
+ public String batchExecuteBasicPointExchange() {
|
|
|
+ // 获取所有有积分的用户列表
|
|
|
+ List<String> userIdList = pointDetailDao.findAllUserIdsWithPoints();
|
|
|
+
|
|
|
+ if (userIdList == null || userIdList.isEmpty()) {
|
|
|
+ return "没有可兑换的用户";
|
|
|
+ }
|
|
|
+
|
|
|
+ int successCount = 0;
|
|
|
+ int failCount = 0;
|
|
|
+ StringBuilder resultMsg = new StringBuilder();
|
|
|
+ resultMsg.append("批量兑换完成:\n");
|
|
|
+
|
|
|
+ for (String userId : userIdList) {
|
|
|
+ try {
|
|
|
+ String result = executeBasicPointExchange(userId);
|
|
|
+ if (result.contains("兑换成功")) {
|
|
|
+ successCount++;
|
|
|
+ resultMsg.append("用户[").append(userId).append("]: ").append(result).append("\n");
|
|
|
+ } else {
|
|
|
+ failCount++;
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ failCount++;
|
|
|
+ resultMsg.append("用户[").append(userId).append("]: 兑换失败 - ").append(e.getMessage()).append("\n");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ resultMsg.append("\n总计:成功").append(successCount).append("人,失败").append(failCount).append("人");
|
|
|
+ return resultMsg.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 批量执行所有用户的贡献积分兑换
|
|
|
+ * @return 兑换结果统计
|
|
|
+ */
|
|
|
+ @Transactional(readOnly = false)
|
|
|
+ public String batchExecuteContributionPointExchange() {
|
|
|
+ // 获取所有有积分的用户列表
|
|
|
+ List<String> userIdList = pointDetailDao.findAllUserIdsWithPoints();
|
|
|
+
|
|
|
+ if (userIdList == null || userIdList.isEmpty()) {
|
|
|
+ return "没有可兑换的用户";
|
|
|
+ }
|
|
|
+
|
|
|
+ int successCount = 0;
|
|
|
+ int failCount = 0;
|
|
|
+ StringBuilder resultMsg = new StringBuilder();
|
|
|
+ resultMsg.append("批量兑换完成:\n");
|
|
|
+
|
|
|
+ for (String userId : userIdList) {
|
|
|
+ try {
|
|
|
+ String result = executeContributionPointExchange(userId);
|
|
|
+ if (result.contains("兑换成功")) {
|
|
|
+ successCount++;
|
|
|
+ resultMsg.append("用户[").append(userId).append("]: ").append(result).append("\n");
|
|
|
+ } else {
|
|
|
+ failCount++;
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ failCount++;
|
|
|
+ resultMsg.append("用户[").append(userId).append("]: 兑换失败 - ").append(e.getMessage()).append("\n");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ resultMsg.append("\n总计:成功").append(successCount).append("人,失败").append(failCount).append("人");
|
|
|
+ return resultMsg.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 批量兑换并获取导出数据(按指定类型)
|
|
|
+ * @param exchangeType 兑换类型:basic-基础积分, contribution-贡献积分
|
|
|
+ * @return 导出用的用户积分数据列表(只包含参与兑换的用户)
|
|
|
+ */
|
|
|
+ @Transactional(readOnly = false)
|
|
|
+ public List<WorkKnowledgeBasePoint> batchExchangeForExport(String exchangeType) {
|
|
|
+ // 1. 根据兑换类型获取有对应积分的用户ID列表
|
|
|
+ String columnId = "basic".equals(exchangeType) ? "1" : "2";
|
|
|
+ List<String> userIdList = pointDetailDao.findUserIdsByColumnId(columnId);
|
|
|
+
|
|
|
+ if (userIdList == null || userIdList.isEmpty()) {
|
|
|
+ return new java.util.ArrayList<>();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 读取兑换规则
|
|
|
+ Integer firstThreshold = getRuleValueByType("91");
|
|
|
+ Integer nonFirstThreshold = getRuleValueByType("92");
|
|
|
+ Integer stepValue = getRuleValueByType("93");
|
|
|
+ if (firstThreshold == null) firstThreshold = 300;
|
|
|
+ if (nonFirstThreshold == null) nonFirstThreshold = 100;
|
|
|
+ if (stepValue == null) stepValue = 100;
|
|
|
+
|
|
|
+ // 记录每个用户的兑换量
|
|
|
+ Map<String, Integer> exchangedBasicMap = new HashMap<>();
|
|
|
+ Map<String, Integer> exchangedContribMap = new HashMap<>();
|
|
|
+ // 记录每个用户的原始积分(兑换前的值,用于显示“可兑换积分”)
|
|
|
+ Map<String, Integer> originalBasicMap = new HashMap<>();
|
|
|
+ Map<String, Integer> originalContribMap = new HashMap<>();
|
|
|
+ // 记录参与兑换的用户ID(用于过滤导出列表)
|
|
|
+ java.util.Set<String> exchangedUserIds = new java.util.HashSet<>();
|
|
|
+
|
|
|
+ // 3. 逐用户执行兑换(根据类型决定兑换哪种)
|
|
|
+ for (String userId : userIdList) {
|
|
|
+ try {
|
|
|
+ // === 基础积分兑换 ===
|
|
|
+ if ("basic".equals(exchangeType)) {
|
|
|
+ int basicPoints = pointDetailDao.sumUserPointByColumnId(userId, "1");
|
|
|
+ // 记录原始基础积分(兑换前)
|
|
|
+ originalBasicMap.put(userId, basicPoints);
|
|
|
+
|
|
|
+ WorkKnowledgeBaseUserExchangeFlag flag = exchangeFlagDao.getByUserId(userId);
|
|
|
+ if (flag == null) {
|
|
|
+ flag = new WorkKnowledgeBaseUserExchangeFlag();
|
|
|
+ flag.setId(com.jeeplus.common.utils.IdGen.uuid());
|
|
|
+ flag.setUserId(userId);
|
|
|
+ flag.setIsFirstExchanged(0);
|
|
|
+ flag.setDelFlag("0");
|
|
|
+ flag.preInsert();
|
|
|
+ exchangeFlagDao.insert(flag);
|
|
|
+ }
|
|
|
+ boolean isFirstExchange = (flag.getIsFirstExchanged() == 0 || flag.getIsFirstExchanged() == null);
|
|
|
+ int threshold = isFirstExchange ? firstThreshold : nonFirstThreshold;
|
|
|
+ int basicExchanged = 0;
|
|
|
+ if (basicPoints >= threshold) {
|
|
|
+ basicExchanged = (basicPoints / stepValue) * stepValue;
|
|
|
+ if (basicExchanged > 0) {
|
|
|
+ WorkKnowledgeBasePointDetail detail = new WorkKnowledgeBasePointDetail();
|
|
|
+ detail.setId(com.jeeplus.common.utils.IdGen.uuid());
|
|
|
+ detail.setUserId(userId);
|
|
|
+ detail.setChangeType(9);
|
|
|
+ detail.setCategoryId("1"); // 基础积分分类,使扣减自然计入基础积分汇总
|
|
|
+ detail.setPoint(-basicExchanged);
|
|
|
+ detail.setDelFlag("0");
|
|
|
+ detail.preInsert();
|
|
|
+ pointDetailDao.insert(detail);
|
|
|
+ if (isFirstExchange) {
|
|
|
+ exchangeFlagDao.updateFirstExchangeStatus(userId, 1);
|
|
|
+ }
|
|
|
+ // 只有实际兑换了才加入集合
|
|
|
+ exchangedUserIds.add(userId);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ exchangedBasicMap.put(userId, basicExchanged);
|
|
|
+ }
|
|
|
+
|
|
|
+ // === 贡献积分兑换(全额清零) ===
|
|
|
+ if ("contribution".equals(exchangeType)) {
|
|
|
+ int contributionPoints = pointDetailDao.sumUserPointByColumnId(userId, "2");
|
|
|
+ // 记录原始贡献积分(兑换前)
|
|
|
+ originalContribMap.put(userId, contributionPoints);
|
|
|
+
|
|
|
+ int contribExchanged = 0;
|
|
|
+ if (contributionPoints > 0) {
|
|
|
+ contribExchanged = contributionPoints;
|
|
|
+ WorkKnowledgeBasePointDetail cpDetail = new WorkKnowledgeBasePointDetail();
|
|
|
+ cpDetail.setId(com.jeeplus.common.utils.IdGen.uuid());
|
|
|
+ cpDetail.setUserId(userId);
|
|
|
+ cpDetail.setChangeType(9);
|
|
|
+ cpDetail.setCategoryId("2"); // 贡献积分分类,使扣减自然计入贡献积分汇总
|
|
|
+ cpDetail.setPoint(-contribExchanged);
|
|
|
+ cpDetail.setDelFlag("0");
|
|
|
+ cpDetail.preInsert();
|
|
|
+ pointDetailDao.insert(cpDetail);
|
|
|
+ // 贡献积分只要有就兑换,加入集合
|
|
|
+ exchangedUserIds.add(userId);
|
|
|
+ }
|
|
|
+ exchangedContribMap.put(userId, contribExchanged);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 单个用户失败不影响其他用户,继续处理
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 查询所有用户的积分数据(此时数据已反映兑换后的状态)
|
|
|
+ List<WorkKnowledgeBasePoint> allUsers = pointDetailDao.findAllUserPointSummary();
|
|
|
+
|
|
|
+ // 5. 过滤出参与兑换的用户,并设置兑换量和剩余量
|
|
|
+ List<WorkKnowledgeBasePoint> exportList = new java.util.ArrayList<>();
|
|
|
+ for (WorkKnowledgeBasePoint item : allUsers) {
|
|
|
+ // 只保留参与兑换的用户
|
|
|
+ if (!exchangedUserIds.contains(item.getUserId())) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ int exBasic = exchangedBasicMap.containsKey(item.getUserId()) ? exchangedBasicMap.get(item.getUserId()) : 0;
|
|
|
+ int exContrib = exchangedContribMap.containsKey(item.getUserId()) ? exchangedContribMap.get(item.getUserId()) : 0;
|
|
|
+ // basicPoint/contributionPoint 已是兑换后的剩余值(因为兑换记录的category_id已正确归类)
|
|
|
+ int remainBasic = item.getBasicPoint() != null ? item.getBasicPoint() : 0;
|
|
|
+ int remainContrib = item.getContributionPoint() != null ? item.getContributionPoint() : 0;
|
|
|
+ // 兑换前的原始积分(可兑换积分)
|
|
|
+ int origBasic = originalBasicMap.containsKey(item.getUserId()) ? originalBasicMap.get(item.getUserId()) : 0;
|
|
|
+ int origContrib = originalContribMap.containsKey(item.getUserId()) ? originalContribMap.get(item.getUserId()) : 0;
|
|
|
+
|
|
|
+ item.setExchangedBasicPoint(exBasic);
|
|
|
+ item.setExchangedContributionPoint(exContrib);
|
|
|
+ item.setRemainingBasicPoint(remainBasic);
|
|
|
+ item.setRemainingContributionPoint(remainContrib);
|
|
|
+ // 设置原始积分(可兑换积分)到新字段,不覆盖basicPoint/contributionPoint
|
|
|
+ item.setOriginalBasicPoint(origBasic);
|
|
|
+ item.setOriginalContributionPoint(origContrib);
|
|
|
+
|
|
|
+ exportList.add(item);
|
|
|
+ }
|
|
|
+
|
|
|
+ return exportList;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据rule_type获取规则配置的point_value值
|
|
|
+ * @param ruleType 规则类型(91/92/93)
|
|
|
+ * @return 规则值,如果不存在返回null
|
|
|
+ */
|
|
|
+ private Integer getRuleValueByType(String ruleType) {
|
|
|
+ // 从 work_knowledge_base_point_rule 表查询
|
|
|
+ WorkKnowledgeBasePointRule rule = pointRuleDao.findByRuleType(Integer.parseInt(ruleType));
|
|
|
+ if (rule != null && StringUtils.isNotBlank(rule.getPointValue())) {
|
|
|
+ try {
|
|
|
+ return Integer.parseInt(rule.getPointValue());
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+}
|