调查投票提交接口多选修正

This commit is contained in:
liweiyi 2025-06-21 17:03:49 +08:00
parent 3b838ec2b5
commit eeb7d951ab
19 changed files with 113 additions and 66 deletions

View File

@ -77,6 +77,6 @@ public class CmsArticleDetailDAO extends BackupServiceImpl<CmsArticleDetailMappe
if (StringUtils.isEmpty(backupIds)) {
return;
}
this.getBackupMapper().deleteBatchIds(backupIds);
this.getBackupMapper().deleteByIds(backupIds);
}
}

View File

@ -90,6 +90,6 @@ public class CmsContentDAO extends BackupServiceImpl<CmsContentMapper, CmsConten
if (StringUtils.isEmpty(backupIds)) {
return;
}
this.getBackupMapper().deleteBatchIds(backupIds);
this.getBackupMapper().deleteByIds(backupIds);
}
}

View File

@ -73,7 +73,7 @@ public class ImageListener {
new LambdaQueryWrapper<BCmsImage>().select(List.of(BCmsImage::getImageId))
.eq(BCmsImage::getSiteId, site.getSiteId())
).getRecords().stream().map(BCmsImage::getBackupId).toList();
backupMapper.deleteBatchIds(backupIds);
backupMapper.deleteByIds(backupIds);
}
} catch (Exception e) {
AsyncTaskManager.addErrMessage("删除图片内容备份错误:" + e.getMessage());

View File

@ -79,7 +79,7 @@ public class MediaListener {
.eq(BCmsAudio::getSiteId, site.getSiteId())
).getRecords().stream().map(BCmsAudio::getBackupId).toList();
backupMapper.deleteBatchIds(backupIds);
backupMapper.deleteByIds(backupIds);
}
} catch (Exception e) {
AsyncTaskManager.addErrMessage("删除音频内容备份错误:" + e.getMessage());
@ -116,7 +116,7 @@ public class MediaListener {
.eq(BCmsVideo::getSiteId, site.getSiteId())
).getRecords().stream().map(BCmsVideo::getBackupId).toList();
backupMapper.deleteBatchIds(backupIds);
backupMapper.deleteByIds(backupIds);
}
} catch (Exception e) {
AsyncTaskManager.addErrMessage("删除视频内容备份错误:" + e.getMessage());

View File

@ -124,7 +124,7 @@ public class BackupServiceImpl<M extends BaseMapper<T>, T extends IBackupable<B>
@Override
public void deleteBackupByIds(Collection<Serializable> backupIds) {
this.backupMapper.deleteBatchIds(backupIds);
this.backupMapper.deleteByIds(backupIds);
}
@Override

View File

@ -155,7 +155,7 @@ public class SysScheduledTaskController extends BaseRestController {
@DeleteMapping("/logs")
public R<?> removeLogs(@RequestBody @NotEmpty List<Long> logIds) {
Assert.isTrue(IdUtils.validate(logIds), () -> CommonErrorCode.INVALID_REQUEST_ARG.exception());
this.logMapper.deleteBatchIds(logIds);
this.logMapper.deleteByIds(logIds);
return R.ok();
}
}

View File

@ -94,7 +94,7 @@ public class GroovyController extends BaseRestController {
@Log(title = "刪除Groovy脚本", businessType = BusinessType.DELETE)
@DeleteMapping("/delete")
public R<?> deleteGroovyScript(@RequestBody @NotEmpty List<Long> scriptIds) {
this.groovyScriptMapper.deleteBatchIds(scriptIds);
this.groovyScriptMapper.deleteByIds(scriptIds);
return R.ok();
}

View File

@ -18,7 +18,6 @@ package com.chestnut.vote.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.chestnut.common.domain.R;
import com.chestnut.common.exception.CommonErrorCode;
import com.chestnut.common.i18n.I18nUtils;
import com.chestnut.common.log.annotation.Log;
import com.chestnut.common.log.enums.BusinessType;
import com.chestnut.common.security.anno.Priv;
@ -40,7 +39,6 @@ import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@RequiredArgsConstructor
@RestController
@ -74,17 +72,13 @@ public class VoteController extends BaseRestController {
@Priv(type = AdminUserType.TYPE, value = VotePriv.View)
@GetMapping("/userTypes")
public R<?> getVoteUserTypes() {
List<Map<String, String>> list = this.userTypes.stream()
.map(vut -> Map.of("id", vut.getId(), "name", I18nUtils.get(vut.getName()))).toList();
return R.ok(list);
return bindSelectOptions(this.userTypes, IVoteUserType::getId, IVoteUserType::getName);
}
@Priv(type = AdminUserType.TYPE)
@GetMapping("/item/types")
public R<?> getVoteItemTypes() {
List<Map<String, String>> list = this.itemTypes.stream()
.map(vut -> Map.of("id", vut.getId(), "name", I18nUtils.get(vut.getName()))).toList();
return R.ok(list);
return bindSelectOptions(this.itemTypes, IVoteItemType::getId, IVoteItemType::getName);
}
@Log(title = "新增问卷调查", businessType = BusinessType.INSERT)

View File

@ -23,15 +23,15 @@ package com.chestnut.vote.core;
*/
public interface IVoteItemType {
public String BEAN_PREFIX = "VoteItemType_";
String BEAN_PREFIX = "VoteItemType_";
/**
* 问卷调查选项类型ID唯一标识
*/
public String getId();
String getId();
/**
* 问卷调查选项类型名称
*/
public String getName();
String getName();
}

View File

@ -23,7 +23,7 @@ package com.chestnut.vote.core;
*/
public interface IVoteUserType {
public String BEAN_PREFIX = "VoteUserType_";
String BEAN_PREFIX = "VoteUserType_";
/**
* 问卷调查用户类型ID唯一标识

View File

@ -15,19 +15,19 @@
*/
package com.chestnut.vote.domain;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.Map;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import com.chestnut.vote.domain.dto.VoteSubmitDTO;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.List;
/**
* 问卷调查日志表
*
@ -63,11 +63,9 @@ public class VoteLog implements Serializable {
/**
* 投票结果
*
* 格式<subjectId, itemId|inputText>
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private Map<Long, String> result;
private List<VoteSubmitDTO.SubjectResult> result;
/**
* 日志记录时间

View File

@ -15,11 +15,12 @@
*/
package com.chestnut.vote.domain.dto;
import java.util.List;
import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
@Getter
@Setter
public class VoteSubmitDTO {
@ -52,10 +53,15 @@ public class VoteSubmitDTO {
* 主题ID
*/
private Long subjectId;
/**
* 主题类型
*/
private String type;
/**
* 结果itemId || inputText
*/
private String result;
private ArrayList<String> result;
}
}

View File

@ -17,6 +17,8 @@ package com.chestnut.vote.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.chestnut.vote.domain.Vote;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;
/**
* <p>
@ -28,4 +30,7 @@ import com.chestnut.vote.domain.Vote;
*/
public interface VoteMapper extends BaseMapper<Vote> {
@Update("UPDATE " + Vote.TABLE_NAME + " SET total = total + ${delta} WHERE vote_id = #{voteId}")
void increaseVoteTotal(@Param("voteId") Long voteId, @Param("delta") Integer delta);
}

View File

@ -17,6 +17,8 @@ package com.chestnut.vote.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.chestnut.vote.domain.VoteSubjectItem;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;
/**
* <p>
@ -28,4 +30,6 @@ import com.chestnut.vote.domain.VoteSubjectItem;
*/
public interface VoteSubjectItemMapper extends BaseMapper<VoteSubjectItem> {
@Update("UPDATE " + VoteSubjectItem.TABLE_NAME + " SET total = total + ${delta} WHERE item_id = #{itemId}")
void increaseVoteSubjectItemTotal(@Param("itemId") Long itemId, @Param("delta") Integer delta);
}

View File

@ -22,7 +22,6 @@ import com.chestnut.common.utils.DateUtils;
import com.chestnut.vote.core.IVoteUserType;
import com.chestnut.vote.domain.VoteLog;
import com.chestnut.vote.domain.dto.VoteSubmitDTO;
import com.chestnut.vote.domain.dto.VoteSubmitDTO.SubjectResult;
import com.chestnut.vote.domain.vo.VoteVO;
import com.chestnut.vote.exception.VoteErrorCode;
import com.chestnut.vote.service.IVoteApiService;
@ -35,8 +34,6 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
@ -82,13 +79,11 @@ public class VoteApiServiceImpl implements IVoteApiService {
Assert.isTrue(dayCount < vote.getDayLimit(), VoteErrorCode.VOTE_DAY_LIMIT::exception);
// 记录日志
Map<Long, String> result = dto.getSubjects().stream()
.collect(Collectors.toMap(SubjectResult::getSubjectId, SubjectResult::getResult));
VoteLog voteLog = new VoteLog();
voteLog.setVoteId(dto.getVoteId());
voteLog.setUserType(vote.getUserType());
voteLog.setUserId(userId);
voteLog.setResult(result);
voteLog.setResult(dto.getSubjects());
voteLog.setLogTime(LocalDateTime.now());
voteLog.setIp(dto.getIp());
voteLog.setUserAgent(dto.getUserAgent());

View File

@ -16,7 +16,6 @@
package com.chestnut.vote.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.chestnut.common.exception.CommonErrorCode;
@ -30,6 +29,7 @@ import com.chestnut.vote.domain.Vote;
import com.chestnut.vote.domain.VoteLog;
import com.chestnut.vote.domain.VoteSubject;
import com.chestnut.vote.domain.VoteSubjectItem;
import com.chestnut.vote.domain.dto.VoteSubmitDTO;
import com.chestnut.vote.domain.vo.VoteSubjectItemVO;
import com.chestnut.vote.domain.vo.VoteSubjectVO;
import com.chestnut.vote.domain.vo.VoteVO;
@ -51,8 +51,7 @@ import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;
@RequiredArgsConstructor
@ -67,12 +66,14 @@ public class VoteServiceImpl extends ServiceImpl<VoteMapper, Vote> implements IV
private final VoteSubjectItemMapper itemMapper;
private final VoteMapper voteMapper;
private final VoteLogMapper voteLogMapper;
private final RedisCache redisCache;
private final Map<String, IVoteUserType> voteUserTypes;
private final Map<String, IVoteItemType> voteItemTypes;
private final RedissonClient redissonClient;
@ -235,22 +236,66 @@ public class VoteServiceImpl extends ServiceImpl<VoteMapper, Vote> implements IV
try {
VoteVO vote = this.getVote(voteLog.getVoteId());
// 问卷调查参与数+1
Vote voteEntity = this.lambdaQuery().select(Vote::getVoteId, Vote::getTotal)
.eq(Vote::getVoteId, voteLog.getVoteId()).one();
this.lambdaUpdate().set(Vote::getTotal, voteEntity.getTotal() + 1)
.eq(Vote::getVoteId, voteLog.getVoteId()).update();
vote.setTotal(Objects.requireNonNullElse(vote.getTotal(), 0) + 1);
voteMapper.increaseVoteTotal(vote.getVoteId(), 1);
// 单选/多选主题选项票数+1
vote.getSubjects().forEach(subject -> {
if (!VoteSubjectType.isInput(subject.getType())) {
String result = voteLog.getResult().get(subject.getSubjectId());
VoteSubjectItem item = new LambdaQueryChainWrapper<>(this.itemMapper)
.select(VoteSubjectItem::getItemId, VoteSubjectItem::getTotal)
.eq(VoteSubjectItem::getItemId, result)
.one();
new LambdaUpdateChainWrapper<>(this.itemMapper)
.set(VoteSubjectItem::getTotal, item.getTotal() + 1)
.eq(VoteSubjectItem::getItemId, item.getItemId())
.update();
List<VoteSubmitDTO.SubjectResult> subjectResults = voteLog.getResult();
Optional<VoteSubmitDTO.SubjectResult> opt = subjectResults.stream()
.filter(r -> subject.getSubjectId().equals(r.getSubjectId()) && subject.getType().equals(r.getType()))
.findFirst();
if (opt.isPresent()) {
VoteSubmitDTO.SubjectResult result = opt.get();
for (String itemIdStr : result.getResult()) {
Long itemId = Long.parseLong(itemIdStr);
this.itemMapper.increaseVoteSubjectItemTotal(itemId, 1);
}
}
}
});
// 更新缓存
this.redisCache.setCacheObject(CACHE_PREFIX + vote.getVoteId(), vote);
} finally {
lock.unlock();
}
}
public void resetVoteStat(Long voteId) {
RLock lock = redissonClient.getLock("VoteTotalUpdate-" + voteId);
lock.lock();
try {
VoteVO vote = this.getVote(voteId);
List<VoteLog> voteLogs = voteLogMapper.selectList(new LambdaQueryWrapper<VoteLog>()
.eq(VoteLog::getVoteId, vote.getVoteId()));
// 问卷调查参与数
vote.setTotal(voteLogs.size());
this.lambdaUpdate().set(Vote::getTotal, voteLogs.size()).eq(Vote::getVoteId, vote.getVoteId()).update();
// 单选/多选主题选项票数
vote.getSubjects().forEach(subject -> {
if (!VoteSubjectType.isInput(subject.getType())) {
Map<Long, Integer> itemTotalMap = new HashMap<>();
List<Long> itemIds = subject.getItems().stream().map(VoteSubjectItemVO::getItemId).toList();
for (VoteLog voteLog : voteLogs) {
Optional<VoteSubmitDTO.SubjectResult> opt = voteLog.getResult().stream().filter(r ->
r.getSubjectId().equals(subject.getSubjectId()) && r.getType().equals(subject.getTitle())
).findFirst();
if (opt.isPresent()) {
VoteSubmitDTO.SubjectResult result = opt.get();
for (String itemIdStr : result.getResult()) {
long itemId = Long.parseLong(itemIdStr);
if (itemIds.contains(itemId)) {
itemTotalMap.put(itemId, itemTotalMap.getOrDefault(itemId, 0) + 1);
}
}
}
}
for (Map.Entry<Long, Integer> entry : itemTotalMap.entrySet()) {
new LambdaUpdateChainWrapper<>(itemMapper).set(VoteSubjectItem::getTotal, entry.getValue())
.eq(VoteSubjectItem::getItemId, entry.getKey())
.update();
}
}
});
// 更新缓存

View File

@ -143,7 +143,7 @@ public class VoteSubjectServiceImpl extends ServiceImpl<VoteSubjectMapper, VoteS
List<Long> removeItemIds = dbItems.stream().map(VoteSubjectItem::getItemId)
.filter(itemId -> !updateItemIds.contains(itemId)).toList();
if (!removeItemIds.isEmpty()) {
this.voteSubjectItemMapper.deleteBatchIds(removeItemIds);
this.voteSubjectItemMapper.deleteByIds(removeItemIds);
}
Map<Long, VoteSubjectItem> updateMap = dbItems.stream().filter(item -> updateItemIds.contains(item.getItemId()))

View File

@ -172,9 +172,9 @@
<el-radio-group v-model="form.userType">
<el-radio
v-for="ut in userTypeOptions"
:key="ut.id"
:label="ut.id"
>{{ ut.name }}</el-radio>
:key="ut.value"
:label="ut.value"
>{{ ut.label }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="$t('Vote.DayLimit')" prop="totalLimit">

View File

@ -76,9 +76,9 @@
<el-select v-model="scope2.row.type" size="mini" disabled>
<el-option
v-for="type in subjectItemTypes"
:key="type.id"
:label="type.name"
:value="type.id"
:key="type.value"
:label="type.label"
:value="type.value"
/>
</el-select>
</template>
@ -221,14 +221,14 @@
<el-select v-model="scope.row.type">
<el-option
v-for="type in subjectItemTypes"
:key="type.id"
:label="type.name"
:value="type.id"
:key="type.value"
:label="type.label"
:value="type.value"
/>
</el-select>
</template>
</el-table-column>
<el-table-column :label="$t('Comment.Details')">
<el-table-column :label="$t('Common.Details')">
<template slot-scope="scope">
<el-input v-if="scope.row.type==='Text'" type="text" v-model="scope.row.content"></el-input>
</template>