|
|
@@ -395,6 +395,231 @@ public class WorkKnowledgeBasePointExchangeService extends CrudService<WorkKnowl
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
+ * 批量兑换并获取整合导出数据(基础积分+贡献积分同时兑换)
|
|
|
+ * 导出表格包含两类积分的兑换情况,兑换金额为两类合计
|
|
|
+ * 管理员不参与兑换,不导出;按部门排序,部门内按兑换金额倒序
|
|
|
+ * @return 整合导出用的用户积分数据列表
|
|
|
+ */
|
|
|
+ @Transactional(readOnly = false)
|
|
|
+ public List<WorkKnowledgeBasePoint> batchExchangeBothForExport() {
|
|
|
+ // 1. 获取两类积分的用户ID并集
|
|
|
+ java.util.Set<String> userIdSet = new java.util.LinkedHashSet<>();
|
|
|
+ List<String> basicUserIds = pointDetailDao.findUserIdsByColumnId("1");
|
|
|
+ List<String> contribUserIds = pointDetailDao.findUserIdsByColumnId("2");
|
|
|
+ if (basicUserIds != null) userIdSet.addAll(basicUserIds);
|
|
|
+ if (contribUserIds != null) userIdSet.addAll(contribUserIds);
|
|
|
+
|
|
|
+ if (userIdSet.isEmpty()) {
|
|
|
+ return new java.util.ArrayList<>();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 先查询所有用户积分汇总(用于获取姓名、部门等信息,以及过滤管理员)
|
|
|
+ List<WorkKnowledgeBasePoint> allUsers = pointDetailDao.findAllUserPointSummary();
|
|
|
+ Map<String, WorkKnowledgeBasePoint> userMap = new HashMap<>();
|
|
|
+ for (WorkKnowledgeBasePoint u : allUsers) {
|
|
|
+ userMap.put(u.getUserId(), u);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 读取兑换规则
|
|
|
+ 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<>();
|
|
|
+ Map<String, Boolean> isFirstExchangeMap = new HashMap<>();
|
|
|
+ // 记录参与兑换的用户ID
|
|
|
+ java.util.Set<String> exchangedUserIds = new java.util.HashSet<>();
|
|
|
+
|
|
|
+ // 4. 逐用户同时执行基础积分和贡献积分兑换
|
|
|
+ for (String userId : userIdSet) {
|
|
|
+ try {
|
|
|
+ // 管理员不参与兑换,直接跳过
|
|
|
+ WorkKnowledgeBasePoint userInfo = userMap.get(userId);
|
|
|
+ if (userInfo != null && "管理员".equals(userInfo.getUserName())) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // === 基础积分兑换 ===
|
|
|
+ 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);
|
|
|
+ isFirstExchangeMap.put(userId, isFirstExchange);
|
|
|
+ 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.setIsManualScore("0");
|
|
|
+ detail.setDelFlag("0");
|
|
|
+ detail.preInsert();
|
|
|
+ pointDetailDao.insert(detail);
|
|
|
+ if (isFirstExchange) {
|
|
|
+ exchangeFlagDao.updateFirstExchangeStatus(userId, 1);
|
|
|
+ }
|
|
|
+ exchangedUserIds.add(userId);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ exchangedBasicMap.put(userId, basicExchanged);
|
|
|
+
|
|
|
+ // === 贡献积分兑换(全额清零) ===
|
|
|
+ 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.setIsManualScore("0");
|
|
|
+ cpDetail.setDelFlag("0");
|
|
|
+ cpDetail.preInsert();
|
|
|
+ pointDetailDao.insert(cpDetail);
|
|
|
+ exchangedUserIds.add(userId);
|
|
|
+ }
|
|
|
+ exchangedContribMap.put(userId, contribExchanged);
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 单个用户失败不影响其他用户,继续处理
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 5. 组装导出数据(只保留实际参与兑换的用户,剩余=兑换前-兑换量)
|
|
|
+ List<WorkKnowledgeBasePoint> exportList = new java.util.ArrayList<>();
|
|
|
+ for (WorkKnowledgeBasePoint item : allUsers) {
|
|
|
+ if (!exchangedUserIds.contains(item.getUserId())) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ // 管理员不导出
|
|
|
+ if ("管理员".equals(item.getUserName())) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ int exBasic = exchangedBasicMap.containsKey(item.getUserId()) ? exchangedBasicMap.get(item.getUserId()) : 0;
|
|
|
+ int exContrib = exchangedContribMap.containsKey(item.getUserId()) ? exchangedContribMap.get(item.getUserId()) : 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(origBasic - exBasic);
|
|
|
+ item.setRemainingContributionPoint(origContrib - exContrib);
|
|
|
+ item.setOriginalBasicPoint(origBasic);
|
|
|
+ item.setOriginalContributionPoint(origContrib);
|
|
|
+
|
|
|
+ // 兑换金额 = 基础积分兑换金额 + 贡献积分兑换金额
|
|
|
+ boolean isFirst = isFirstExchangeMap.containsKey(item.getUserId()) ? isFirstExchangeMap.get(item.getUserId()) : false;
|
|
|
+ int basicAmt = calculateExchangeAmount("basic", exBasic, isFirst);
|
|
|
+ int contribAmt = calculateExchangeAmount("contribution", exContrib, false);
|
|
|
+ item.setExchangeAmount(basicAmt + contribAmt);
|
|
|
+
|
|
|
+ exportList.add(item);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 6. 排序:先按部门排序,部门内按兑换金额倒序
|
|
|
+ exportList.sort((a, b) -> {
|
|
|
+ String deptA = a.getOfficeName() != null ? a.getOfficeName() : "";
|
|
|
+ String deptB = b.getOfficeName() != null ? b.getOfficeName() : "";
|
|
|
+ int deptCmp = deptA.compareTo(deptB);
|
|
|
+ if (deptCmp != 0) {
|
|
|
+ return deptCmp;
|
|
|
+ }
|
|
|
+ int amtA = a.getExchangeAmount() != null ? a.getExchangeAmount() : 0;
|
|
|
+ int amtB = b.getExchangeAmount() != null ? b.getExchangeAmount() : 0;
|
|
|
+ return Integer.compare(amtB, amtA);
|
|
|
+ });
|
|
|
+
|
|
|
+ return exportList;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 取消上次兑换(用于导出文档出错时的回退)
|
|
|
+ * 不依赖remarks,兼容历史兑换记录:一次兑换在同一请求内连续插入,create_date非常接近;
|
|
|
+ * 从最新一条兑换记录往前收集,相邻两条时间间隔超过阈值即视为上一批次,停止收集。
|
|
|
+ * 收集到的记录物理删除(与取消点赞删除积分记录的处理方式一致),删除后积分自动恢复;
|
|
|
+ * 若某用户的基础积分兑换记录被全部删除,说明被取消的即是其首次兑换,重置首次兑换标记。
|
|
|
+ * @return 操作结果信息
|
|
|
+ */
|
|
|
+ @Transactional(readOnly = false)
|
|
|
+ public String cancelLastExchange() {
|
|
|
+ // 1. 查询最近7天内的兑换扣减记录(按创建时间倒序)
|
|
|
+ Date since = new Date(System.currentTimeMillis() - 7L * 24 * 60 * 60 * 1000);
|
|
|
+ List<WorkKnowledgeBasePointDetail> recentRecords = pointDetailDao.findExchangeRecordsAfter(since);
|
|
|
+ if (recentRecords == null || recentRecords.isEmpty()) {
|
|
|
+ return "未找到可取消的兑换记录";
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 从最新记录开始聚类:相邻两条间隔超过10分钟即视为不同批次
|
|
|
+ final long BATCH_GAP_MILLIS = 10L * 60 * 1000;
|
|
|
+ List<WorkKnowledgeBasePointDetail> lastBatch = new java.util.ArrayList<>();
|
|
|
+ Date prevDate = null;
|
|
|
+ for (WorkKnowledgeBasePointDetail record : recentRecords) {
|
|
|
+ if (record.getCreateDate() == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (prevDate != null && (prevDate.getTime() - record.getCreateDate().getTime()) > BATCH_GAP_MILLIS) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ lastBatch.add(record);
|
|
|
+ prevDate = record.getCreateDate();
|
|
|
+ }
|
|
|
+ if (lastBatch.isEmpty()) {
|
|
|
+ return "未找到可取消的兑换记录";
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 删除该批次记录,并收集涉及基础积分兑换的用户
|
|
|
+ java.util.Set<String> basicExchangeUserIds = new java.util.HashSet<>();
|
|
|
+ for (WorkKnowledgeBasePointDetail record : lastBatch) {
|
|
|
+ if ("1".equals(record.getCategoryId())) {
|
|
|
+ basicExchangeUserIds.add(record.getUserId());
|
|
|
+ }
|
|
|
+ pointDetailDao.delete(record);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 恢复首次兑换标记:用户已无其他基础兑换记录,说明被取消的即是首次兑换
|
|
|
+ int restoredCount = 0;
|
|
|
+ for (String userId : basicExchangeUserIds) {
|
|
|
+ int remainCount = pointDetailDao.countUserBasicExchangeRecords(userId);
|
|
|
+ if (remainCount == 0) {
|
|
|
+ WorkKnowledgeBaseUserExchangeFlag flag = exchangeFlagDao.getByUserId(userId);
|
|
|
+ if (flag != null && flag.getIsFirstExchanged() != null && flag.getIsFirstExchanged() == 1) {
|
|
|
+ exchangeFlagDao.updateFirstExchangeStatus(userId, 0);
|
|
|
+ restoredCount++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return "已取消上次兑换,共删除" + lastBatch.size() + "条兑换记录";
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
* 预览兑换明细(只读,不写DB)
|
|
|
* 计算每个用户的兑换情况:当前积分、预计兑换量、兑换后剩余
|
|
|
* @param exchangeType 兑换类型:basic-基础积分, contribution-贡献积分
|
|
|
@@ -470,6 +695,10 @@ public class WorkKnowledgeBasePointExchangeService extends CrudService<WorkKnowl
|
|
|
if (!previewUserIds.contains(item.getUserId())) {
|
|
|
continue;
|
|
|
}
|
|
|
+ // 管理员不可兑换,不展示
|
|
|
+ if ("管理员".equals(item.getUserName())) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
|
|
|
int exBasic = previewExchangedBasicMap.containsKey(item.getUserId()) ? previewExchangedBasicMap.get(item.getUserId()) : 0;
|
|
|
int exContrib = previewExchangedContribMap.containsKey(item.getUserId()) ? previewExchangedContribMap.get(item.getUserId()) : 0;
|