end #6

Merged
pficv7wyt merged 8 commits from branch1 into main 10 months ago

@ -0,0 +1,155 @@
<template>
<!-- 使用 Vue 的过渡组件根据传入的 transitionName 决定过渡效果 -->
<transition :name="transitionName">
<!-- visible true 时显示返回顶部按钮应用自定义样式 -->
<div v-show="visible" :style="customStyle" class="back-to-ceiling" @click="backToTop">
<!-- 返回顶部按钮的图标 -->
<svg width="16" height="16" viewBox="0 0 17 17" xmlns="http://www.w3.org/2000/svg" class="Icon Icon--backToTopArrow" aria-hidden="true" style="height:16px;width:16px"><path d="M12.036 15.59a1 1 0 0 1-.997.995H5.032a.996.996 0 0 1-.997-.996V8.584H1.03c-1.1 0-1.36-.633-.578-1.416L7.33.29a1.003 1.003 0 0 1 1.412 0l6.878 6.88c.782.78.523 1.415-.58 1.415h-3.004v7.004z" /></svg>
</div>
</transition>
</template>
<script>
export default {
//
name: 'BackToTop',
//
props: {
// 400
visibilityHeight: {
type: Number,
default: 400
},
// 0
backPosition: {
type: Number,
default: 0
},
//
customStyle: {
type: Object,
default: function() {
return {
right: '50px',
bottom: '50px',
width: '40px',
height: '40px',
'border-radius': '4px',
'line-height': '45px',
background: '#e7eaf1'
}
}
},
// 'fade'
transitionName: {
type: String,
default: 'fade'
}
},
//
data() {
return {
//
visible: false,
//
interval: null,
//
isMoving: false
}
},
//
mounted() {
//
window.addEventListener('scroll', this.handleScroll)
},
//
beforeDestroy() {
//
window.removeEventListener('scroll', this.handleScroll)
//
if (this.interval) {
clearInterval(this.interval)
}
},
//
methods: {
//
handleScroll() {
// visibilityHeight
this.visible = window.pageYOffset > this.visibilityHeight
},
//
backToTop() {
//
if (this.isMoving) return
//
const start = window.pageYOffset
let i = 0
//
this.isMoving = true
//
this.interval = setInterval(() => {
// 使
const next = Math.floor(this.easeInOutQuad(10 * i, start, -start, 500))
if (next <= this.backPosition) {
//
window.scrollTo(0, this.backPosition)
//
clearInterval(this.interval)
//
this.isMoving = false
} else {
//
window.scrollTo(0, next)
}
i++
}, 16.7)
},
/**
* 缓动函数实现二次方的缓入缓出效果
* @param {number} t - 当前时间从动画开始到现在的时间
* @param {number} b - 起始值
* @param {number} c - 变化量最终值 - 起始值
* @param {number} d - 持续时间
* @returns {number} - 当前时间对应的数值
*/
easeInOutQuad(t, b, c, d) {
if ((t /= d / 2) < 1) return c / 2 * t * t + b
return -c / 2 * (--t * (t - 2) - 1) + b
}
}
}
</script>
<style scoped>
/* 返回顶部按钮的基础样式 */
.back-to-ceiling {
position: fixed;
display: inline-block;
text-align: center;
cursor: pointer;
}
/* 返回顶部按钮的悬停样式 */
.back-to-ceiling:hover {
background: #d5dbe7;
}
/* 淡入动画的激活状态样式,过渡时间为 0.5 秒 */
.fade-enter-active,
.fade-leave-active {
transition: opacity .5s;
}
/* 淡入动画的起始状态和淡出动画的结束状态样式,透明度为 0 */
.fade-enter,
.fade-leave-to {
opacity: 0
}
/* 返回顶部按钮图标的样式 */
.back-to-ceiling .Icon {
fill: #9aaabf;
background: none;
}
</style>

@ -0,0 +1,138 @@
<template>
<!-- 定义面包屑导航组件使用 Element UI el-breadcrumb 组件设置分隔符为 / -->
<el-breadcrumb class="app-breadcrumb" separator="/">
<!-- 使用过渡组件实现面包屑项切换时的动画效果动画名称为 breadcrumb -->
<transition-group name="breadcrumb">
<!-- 遍历 levelList 数组为每个元素生成一个面包屑项 -->
<el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path">
<!-- 如果当前项不需要重定向或者是最后一项则显示为普通文本 -->
<span v-if="item.redirect==='noRedirect'||index==levelList.length-1" class="no-redirect">{{ item.meta.title }}</span>
<!-- 否则显示为可点击的链接点击时调用 handleLink 方法 -->
<a v-else @click.prevent="handleLink(item)">{{ item.meta.title }}</a>
</el-breadcrumb-item>
</transition-group>
</el-breadcrumb>
</template>
<script>
// pathToRegexp
import pathToRegexp from 'path-to-regexp'
export default {
/**
* 组件的数据函数返回组件的初始数据
* @returns {Object} 包含 levelList 的数据对象
*/
data() {
return {
//
levelList: null
}
},
/**
* 组件的监听器监听路由变化
*/
watch: {
/**
* 监听 $route 对象的变化
* @param {Object} route - 新的路由对象
*/
$route(route) {
//
if (route.path.startsWith('/redirect/')) {
return
}
// getBreadcrumb
this.getBreadcrumb()
}
},
/**
* 组件创建完成后的钩子函数
*/
created() {
// getBreadcrumb
this.getBreadcrumb()
},
/**
* 组件的方法
*/
methods: {
/**
* 获取面包屑导航数据
*/
getBreadcrumb() {
// meta.title
let matched = this.$route.matched.filter(item => item.meta && item.meta.title)
//
const first = matched[0]
//
if (!this.isDashboard(first)) {
matched = [{ path: '/dashboard', meta: { title: '控制台' }}].concat(matched)
}
// meta.title meta.breadcrumb false
this.levelList = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)
},
/**
* 判断路由是否为控制台页面
* @param {Object} route - 路由对象
* @returns {boolean} 是否为控制台页面
*/
isDashboard(route) {
//
const name = route && route.name
// false
if (!name) {
return false
}
//
return name.trim().toLocaleLowerCase() === '控制台'.toLocaleLowerCase()
},
/**
* 编译路径解决路径参数问题
* @param {string} path - 原始路径
* @returns {string} 编译后的路径
*/
pathCompile(path) {
//
const { params } = this.$route
// 使 pathToRegexp
var toPath = pathToRegexp.compile(path)
//
return toPath(params)
},
/**
* 处理面包屑项的点击事件
* @param {Object} item - 点击的面包屑项
*/
handleLink(item) {
//
const { redirect, path } = item
//
if (redirect) {
this.$router.push(redirect)
return
}
//
this.$router.push(this.pathCompile(path))
}
}
}
</script>
<style lang="scss" scoped>
/* 定义面包屑导航的样式 */
.app-breadcrumb.el-breadcrumb {
display: inline-block;
font-size: 14px;
line-height: 50px;
margin-left: 8px;
/* 定义不可点击的面包屑项的样式 */
.no-redirect {
color: #97a8be;
cursor: text;
}
}
</style>

@ -0,0 +1,296 @@
<template>
<!-- 组件容器使用 app-container 类名 -->
<div class="app-container">
<!-- 筛选条件容器 -->
<div class="filter-container">
<!-- 插槽用于插入自定义筛选内容 -->
<slot name="filter-content" />
<!-- Element UI 行布局 -->
<el-row>
<!-- Element UI 列布局 -->
<el-col>
<!-- options.addRoute 存在时显示添加按钮 -->
<el-button v-if="options.addRoute" type="primary" icon="el-icon-plus" @click="handleAdd"></el-button>
</el-col>
</el-row>
</div>
<!-- multiShow true options.multiActions 存在时显示批量操作容器 -->
<div v-show="multiShow && options.multiActions" class="filter-container">
<!-- Element UI 下拉选择框用于选择批量操作 -->
<el-select v-model="multiNow" :placeholder="selectedLabel" class="filter-item" style="width: 130px" @change="handleOption">
<!-- 循环渲染批量操作选项 -->
<el-option
v-for="item in options.multiActions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</div>
<!-- Element UI 表格显示数据列表 -->
<el-table
v-loading="listLoading"
:data="dataList.records"
:header-cell-style="{'background':'#f2f3f4', 'color':'#555', 'font-weight':'bold', 'line-height':'32px'}"
border
fit
highlight-current-row
@selection-change="handleSelection"
>
<!-- options.multi true 时显示多选列 -->
<el-table-column
v-if="options.multi"
align="center"
type="selection"
width="55"
/>
<!-- 插槽用于插入自定义表格列 -->
<slot name="data-columns" />
</el-table>
<!-- 分页组件当总数据量大于 0 时显示 -->
<pagination v-show="dataList.total>0" :total="dataList.total" :page.sync="listQuery.current" :limit.sync="listQuery.size" @pagination="getList" />
</div>
</template>
<script>
// API
import { fetchList, deleteData, changeState } from '@/api/common'
//
import Pagination from '@/components/Pagination'
export default {
//
name: 'PagingTable',
//
components: { Pagination },
//
props: {
options: {
type: Object,
default: () => {
return {
//
multiActions: [],
// API
listUrl: '/exam/api',
// API
deleteUrl: '',
// API
stateUrl: '',
//
multi: false
}
}
},
//
listQuery: {
type: Object,
default: () => {
return {
//
current: 1,
//
size: 10,
//
params: {},
//
t: 0
}
}
}
},
data() {
return {
//
dataList: {
//
total: 0
},
//
listLoading: true,
// ID
selectedIds: [],
//
selectedObjs: [],
//
selectedLabel: '',
//
multiShow: false,
//
multiNow: ''
}
},
watch: {
//
listQuery: {
handler() {
this.getList()
},
deep: true
}
},
//
created() {
this.getList()
},
methods: {
/**
* 添加数据跳转方法
* 如果 options.addRoute 存在则跳转到指定路由否则打印提示信息
*/
handleAdd() {
if (this.options.addRoute) {
this.$router.push({ name: this.options.addRoute, params: {}})
return
}
console.log('未设置添加数据跳转路由!')
},
/**
* 查询数据列表方法
* 发送请求获取数据列表并更新组件数据
*/
getList() {
this.listLoading = true
this.listQuery.t = new Date().getTime()
fetchList(this.options.listUrl, this.listQuery).then(response => {
this.dataList = response.data
this.listLoading = false
})
},
/**
* 搜索方法
* 重新调用获取数据列表的方法
*/
handleFilter() {
//
this.getList()
},
/**
* 批量操作回调方法
* 根据选中的批量操作标识执行相应操作
* @param {string} v - 当前选中的批量操作标识
*/
handleOption(v) {
this.multiNow = ''
//
if (v === 'delete') {
this.handleDelete()
return
}
if (v === 'enable') {
this.handleState(0)
return
}
if (v === 'disable') {
this.handleState(1)
return
}
//
this.$emit('multi-actions', { opt: v, ids: this.selectedIds })
},
/**
* 修改状态方法用于启用或禁用数据
* 发送请求修改选中数据的状态并在成功后重新获取数据列表
* @param {number} state - 要修改成的状态值
*/
handleState(state) {
//
changeState(this.options.stateUrl, this.selectedIds, state).then(response => {
if (response.code === 0) {
this.$message({
type: 'success',
message: '状态修改成功!'
})
//
this.getList()
}
})
},
/**
* 删除数据方法
* 先检查是否有选中数据有则弹出确认对话框确认后发送删除请求并重新获取数据列表
*/
handleDelete() {
if (this.selectedIds.length === 0) {
this.$message({
message: '请至少选择一条数据!',
type: 'warning'
})
return
}
//
this.$confirm('确实要删除吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
deleteData(this.options.deleteUrl, this.selectedIds).then(() => {
this.$message({
type: 'success',
message: '删除成功!'
})
this.getList()
})
})
},
/**
* 列表多选操作方法
* 处理表格多选变化事件更新选中数据的相关信息并触发事件
* @param {Array} val - 当前选中的数据对象数组
*/
handleSelection(val) {
const ids = []
val.forEach(row => {
ids.push(row.id)
})
this.selectedObjs = val
this.selectedIds = ids
this.multiShow = ids.length > 0
this.selectedLabel = '已选' + ids.length + '项'
this.$emit('select-changed', { ids: this.selectedIds, objs: this.selectedObjs })
}
}
}
</script>
<style>
/* 筛选容器内筛选项的样式,设置左外边距 */
.filter-container .filter-item{
margin-left: 5px;
}
/* 筛选容器内第一个筛选项的样式,取消左外边距 */
.filter-container .filter-item:first-child{
margin-left: 0px;
}
</style>

@ -0,0 +1,250 @@
<!-- 树状选择器 -->
<template>
<!-- Element UI 弹出框组件用于显示树状菜单 -->
<el-popover
ref="popover"
placement="bottom-start"
trigger="click"
@show="onShowPopover"
@hide="onHidePopover"
>
<!-- Element UI 树组件用于显示部门树状结构 -->
<el-tree
ref="tree"
:style="`min-width: ${treeWidth}`"
:data="data"
:props="props"
:expand-on-click-node="false"
:filter-node-method="filterNode"
placeholder="选择部门"
class="select-tree"
:check-strictly="false"
highlight-current
default-expand-all
@node-click="onClickNode"
/>
<!-- Element UI 输入框组件作为弹出框的触发元素 -->
<el-input
slot="reference"
ref="input"
v-model="labelModel"
:style="`width: ${width}px`"
:class="{ 'rotate': showStatus }"
:placeholder="placeholder"
clearable
suffix-icon="el-icon-arrow-down"
/>
</el-popover>
</template>
<script>
export default {
//
name: 'DepartTree',
// prop value selected
model: {
prop: 'value',
event: 'selected'
},
//
props: {
//
value: String,
//
width: String,
//
options: {
type: Array,
required: true
},
//
placeholder: {
type: String,
required: false,
default: '请选择'
},
//
props: {
type: Object,
required: false,
default: () => ({
parent: 'parentId',
value: 'rowGuid',
label: 'areaName',
children: 'children'
})
}
},
//
data() {
return {
//
showStatus: false,
//
treeWidth: 'auto',
//
labelModel: '',
//
valueModel: '0'
}
},
//
computed: {
//
dataType() {
const jsonStr = JSON.stringify(this.options)
return jsonStr.indexOf(this.props.children) !== -1
},
//
data() {
return this.dataType ? this.options : this.switchTree()
}
},
//
watch: {
//
labelModel(val) {
if (!val) {
this.valueModel = ''
}
this.$refs.tree.filter(val)
},
//
value(val) {
this.labelModel = this.queryTree(this.data, val)
}
},
//
created() {
//
if (this.value) {
this.labelModel = this.queryTree(this.data, this.value)
}
// DOM
this.$nextTick(() => {
this.treeWidth = `${(this.width || this.$refs.input.$refs.input.clientWidth) - 24}px`
})
},
//
methods: {
//
onClickNode(node) {
//
this.labelModel = node[this.props.label]
//
this.valueModel = node[this.props.value]
//
this.onCloseTree()
},
//
switchTree() {
return this.cleanChildren(this.buildTree(this.options, '0'))
},
//
onCloseTree() {
this.$refs.popover.showPopper = false
},
//
onShowPopover() {
//
this.showStatus = true
//
this.$refs.tree.filter(false)
},
//
onHidePopover() {
//
this.showStatus = false
// selected
this.$emit('selected', this.valueModel)
},
//
filterNode(query, data) {
//
if (!query) return true
// label
return data[this.props.label].indexOf(query) !== -1
},
// ID label
queryTree(tree, id) {
let stark = []
stark = stark.concat(tree)
while (stark.length) {
const temp = stark.shift()
if (temp[this.props.children]) {
stark = stark.concat(temp[this.props.children])
}
if (temp[this.props.value] === id) {
return temp[this.props.label]
}
}
return ''
},
//
buildTree(data, id = '0') {
const fa = (parentId) => {
const temp = []
for (let i = 0; i < data.length; i++) {
const n = data[i]
if (n[this.props.parent] === parentId) {
n.children = fa(n.rowGuid)
temp.push(n)
}
}
return temp
}
return fa(id)
},
// children
cleanChildren(data) {
const fa = (list) => {
list.map((e) => {
if (e.children.length) {
fa(e.children)
} else {
delete e.children
}
return e
})
return list
}
return fa(data)
}
}
}
</script>
<style>
/* 带后缀图标的输入框样式 */
.el-input.el-input--suffix {
cursor: pointer;
overflow: hidden;
}
/* 带后缀图标且旋转状态的输入框样式,使后缀图标旋转 180 度 */
.el-input.el-input--suffix.rotate .el-input__suffix {
transform: rotate(180deg);
}
/* 树状选择器样式,设置最大高度和垂直滚动条 */
.select-tree {
max-height: 350px;
overflow-y: scroll;
}
/* 菜单滚动条样式 */
.select-tree::-webkit-scrollbar {
z-index: 11;
width: 6px;
}
.select-tree::-webkit-scrollbar-track,
.select-tree::-webkit-scrollbar-corner {
background: #fff;
}
.select-tree::-webkit-scrollbar-thumb {
border-radius: 5px;
width: 6px;
background: #b4bccc;
}
.select-tree::-webkit-scrollbar-track-piece {
background: #fff;
width: 6px;
}
</style>

@ -0,0 +1,58 @@
// 从 '@/utils/request' 模块导入 post 方法,用于发送 POST 请求
import { post } from '@/utils/request'
/**
* 获取列表数据
* @param {string} url - 请求的 API 接口地址
* @param {Object} query - 请求携带的查询参数
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function fetchList(url, query) {
// 发送 POST 请求到指定的 API 接口,并携带查询参数
return post(url, query)
}
/**
* 获取详情数据
* @param {string} url - 请求的 API 接口地址
* @param {string|number} id - 要获取详情数据的唯一标识
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function fetchDetail(url, id) {
// 发送 POST 请求到指定的 API 接口,并携带包含 id 的参数对象
return post(url, { 'id': id })
}
/**
* 保存数据
* @param {string} url - 请求的 API 接口地址
* @param {Object} data - 需要保存的数据对象
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function saveData(url, data) {
// 发送 POST 请求到指定的 API 接口,并携带需要保存的数据
return post(url, data)
}
/**
* 删除数据
* @param {string} url - 请求的 API 接口地址
* @param {Array} ids - 要删除数据的唯一标识数组
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function deleteData(url, ids) {
// 发送 POST 请求到指定的 API 接口,并携带包含 ids 的参数对象
return post(url, { 'ids': ids })
}
/**
* 更改数据状态
* @param {string} url - 请求的 API 接口地址
* @param {Array} ids - 要更改状态的数据的唯一标识数组
* @param {string|number} state - 要更改成的状态值
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function changeState(url, ids, state) {
// 发送 POST 请求到指定的 API 接口,并携带包含 ids 和 state 的参数对象
return post(url, { 'ids': ids, 'state': state })
}

@ -0,0 +1,31 @@
// 从 '@/utils/request' 模块导入 post 方法,用于发送 POST 请求
import { post } from '@/utils/request'
/**
* 获取题库详情
* @param {string|number} id - 题库的唯一标识
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function fetchDetail(id) {
// 发送 POST 请求到指定接口,携带题库 id 参数
return post('/exam/api/exam/exam/detail', { id: id })
}
/**
* 保存题库信息
* @param {Object} data - 包含题库信息的对象
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function saveData(data) {
// 发送 POST 请求到指定接口,携带题库信息
return post('/exam/api/exam/exam/save', data)
}
/**
* 获取题库列表
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function fetchList() {
// 发送 POST 请求到指定接口,设置当前页码为 1每页显示 100 条记录
return post('/exam/api/exam/exam/paging', { current: 1, size: 100 })
}

@ -0,0 +1,81 @@
// 从 '@/utils/request' 模块导入 post 方法,用于发送 POST 请求
import { post } from '@/utils/request'
/**
* 创建试卷
* @param {Object} data - 创建试卷所需的数据对象
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function createPaper(data) {
// 发送 POST 请求到创建试卷的接口,并传递数据
return post('/exam/api/paper/paper/create-paper', data)
}
/**
* 获取试卷详情
* @param {Object} data - 获取试卷详情所需的数据对象通常包含试卷 ID
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function paperDetail(data) {
// 发送 POST 请求到获取试卷详情的接口,并传递数据
return post('/exam/api/paper/paper/paper-detail', data)
}
/**
* 获取题目详情
* @param {Object} data - 获取题目详情所需的数据对象通常包含题目 ID
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function quDetail(data) {
// 发送 POST 请求到获取题目详情的接口,并传递数据
return post('/exam/api/paper/paper/qu-detail', data)
}
/**
* 填充答案
* @param {Object} data - 填充答案所需的数据对象包含题目答案等信息
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function fillAnswer(data) {
// 发送 POST 请求到填充答案的接口,并传递数据
return post('/exam/api/paper/paper/fill-answer', data)
}
/**
* 交卷
* @param {Object} data - 交卷所需的数据对象可能包含试卷 ID 等信息
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function handExam(data) {
// 发送 POST 请求到交卷的接口,并传递数据
return post('/exam/api/paper/paper/hand-exam', data)
}
/**
* 获取试卷结果
* @param {Object} data - 获取试卷结果所需的数据对象通常包含试卷 ID
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function paperResult(data) {
// 发送 POST 请求到获取试卷结果的接口,并传递数据
return post('/exam/api/paper/paper/paper-result', data)
}
/**
* 错题训练
* @param {Object} data - 错题训练所需的数据对象可能包含用户 ID错题类型等信息
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function training(data) {
// 发送 POST 请求到错题训练的接口,并传递数据
return post('/exam/api/paper/paper/training', data)
}
/**
* 检查是否有进行中的考试
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function checkProcess() {
// 发送 POST 请求到检查进行中考试的接口,不传递额外数据
return post('/exam/api/paper/paper/check-process', {})
}

@ -0,0 +1,13 @@
// 从 '@/utils/request' 模块导入 post 方法,用于发送 POST 请求
import { post } from '@/utils/request'
/**
* 获取试卷列表
* @param {string|number} userId - 用户的唯一标识用于筛选该用户相关的试卷
* @param {string|number} examId - 考试的唯一标识用于筛选该考试下的试卷
*/
export function listPaper(userId, examId) {
// 发送 POST 请求到指定的试卷分页接口
// 请求体中包含当前页码、每页显示数量以及筛选参数(用户 ID 和考试 ID
return post('/exam/api/paper/paper/paging', { current: 1, size: 5, params: { userId: userId, examId: examId }})
}

@ -0,0 +1,54 @@
// 从 '@/utils/request' 模块导入 post、upload 和 download 方法
// post 方法用于发送 POST 请求
// upload 方法用于文件上传请求
// download 方法用于文件下载请求
import { post, upload, download } from '@/utils/request'
/**
* 获取题库详情
* @param {string|number} id - 题库的唯一标识
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function fetchDetail(id) {
// 发送 POST 请求到指定接口,携带题库 id 参数
return post('/exam/api/qu/qu/detail', { id: id })
}
/**
* 保存题库信息
* @param {Object} data - 包含题库信息的对象
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function saveData(data) {
// 发送 POST 请求到指定接口,携带题库信息
return post('/exam/api/qu/qu/save', data)
}
/**
* 导出题库数据为 Excel 文件
* @param {Object} data - 导出所需的参数对象
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function exportExcel(data) {
// 发送下载请求到指定接口,携带导出参数,设置文件名为 '导出的数据.xlsx'
return download('/exam/api/qu/qu/export', data, '导出的数据.xlsx')
}
/**
* 下载导入模板文件
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function importTemplate() {
// 发送下载请求到指定接口,不携带额外参数,设置文件名为 'qu-import-template.xlsx'
return download('/exam/api/qu/qu/import/template', {}, 'qu-import-template.xlsx')
}
/**
* 导入 Excel 文件到题库
* @param {File} file - 要上传的 Excel 文件
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function importExcel(file) {
// 发送上传请求到指定接口,携带要上传的文件
return upload('/exam/api/qu/qu/import', file)
}

@ -0,0 +1,42 @@
// 从 '@/utils/request' 模块导入 post 方法,用于发送 POST 请求
import { post } from '@/utils/request'
/**
* 获取题库详情
* @param {Object} data - 请求所需的参数对象包含获取题库详情必要的信息
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function fetchDetail(data) {
// 发送 POST 请求到指定的获取题库详情接口,并传递参数
return post('/exam/api/repo/detail', data)
}
/**
* 保存题库信息
* @param {Object} data - 请求所需的参数对象包含需要保存的题库信息
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function saveData(data) {
// 发送 POST 请求到指定的保存题库信息接口,并传递参数
return post('/exam/api/repo/save', data)
}
/**
* 分页获取题库列表
* @param {Object} data - 请求所需的参数对象包含分页信息和筛选条件等
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function fetchPaging(data) {
// 发送 POST 请求到指定的分页获取题库列表接口,并传递参数
return post('/exam/api/repo/paging', data)
}
/**
* 对题库进行批量操作
* @param {Object} data - 请求所需的参数对象包含批量操作的类型和相关数据
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function batchAction(data) {
// 发送 POST 请求到指定的题库批量操作接口,并传递参数
return post('/exam/api/repo/batch-action', data)
}

@ -0,0 +1,21 @@
// 从 '@/utils/request' 模块导入 post 方法,用于发送 POST 请求
import { post } from '@/utils/request'
/**
* 获得用户协议详情固定 ID 1
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果请求成功时Promise 将解析为服务器响应数据请求失败时Promise 将被拒绝
*/
export function fetchDetail() {
// 发送 POST 请求到指定接口,携带用户协议的 ID 参数
return post('/exam/api/sys/config/detail', { id: '1' })
}
/**
* 保存系统配置数据
* @param {Object} data - 包含要保存的系统配置信息的对象
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果请求成功时Promise 将解析为服务器响应数据请求失败时Promise 将被拒绝
*/
export function saveData(data) {
// 发送 POST 请求到指定接口,携带要保存的系统配置数据
return post('/exam/api/sys/config/save', data)
}

@ -0,0 +1,69 @@
// 从 '@/utils/request' 模块导入 post 方法,用于发送 POST 请求
import { post } from '@/utils/request'
/**
* 分页获取部门树数据
* @param {Object} data - 请求所需的参数对象包含分页信息等
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function pagingTree(data) {
// 发送 POST 请求到分页获取部门数据的接口,并传递参数
return post('/exam/api/sys/depart/paging', data)
}
/**
* 获取部门树数据
* @param {Object} data - 请求所需的参数对象
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function fetchTree(data) {
// 发送 POST 请求到获取部门树数据的接口,并传递参数
return post('/exam/api/sys/depart/tree', data)
}
/**
* 获取部门详情
* @param {string|number} id - 部门的唯一标识
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function fetchDetail(id) {
// 构建包含部门 id 的请求数据对象
const data = { id: id }
// 发送 POST 请求到获取部门详情的接口,并传递参数
return post('/exam/api/sys/depart/detail', data)
}
/**
* 删除部门数据
* @param {Array} ids - 要删除的部门的唯一标识数组
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function deleteData(ids) {
// 构建包含要删除部门 ids 的请求数据对象
const data = { ids: ids }
// 发送 POST 请求到删除部门数据的接口,并传递参数
return post('/exam/api/sys/depart/delete', data)
}
/**
* 保存部门数据
* @param {Object} data - 包含要保存的部门信息的对象
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function saveData(data) {
// 发送 POST 请求到保存部门数据的接口,并传递参数
return post('/exam/api/sys/depart/save', data)
}
/**
* 对部门进行排序
* @param {string|number} id - 要排序的部门的唯一标识
* @param {number} sort - 排序值
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function sortData(id, sort) {
// 构建包含部门 id 和排序值的请求数据对象
const data = { id: id, sort: sort }
// 发送 POST 请求到对部门进行排序的接口,并传递参数
return post('/exam/api/sys/depart/sort', data)
}

@ -0,0 +1,11 @@
// 从 '@/utils/request' 模块导入 post 方法,用于发送 POST 请求
import { post } from '@/utils/request'
/**
* 获取角色列表
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果当请求成功时Promise 将解析为服务器返回的角色列表数据请求失败时Promise 将被拒绝
*/
export function fetchList() {
// 发送 POST 请求到指定的获取角色列表接口,请求体为空对象
return post('/exam/api/sys/role/list', {})
}

@ -0,0 +1,32 @@
// 从 '@/utils/request' 模块导入 post 方法,用于发送 POST 请求
import { post } from '@/utils/request'
/**
* 更新用户数据
* @param {Object} data - 包含要更新的用户数据的对象
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function updateData(data) {
// 发送 POST 请求到更新用户数据的接口,并传递用户数据
return post('/exam/api/sys/user/update', data)
}
/**
* 保存用户数据
* @param {Object} data - 包含要保存的用户数据的对象
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function saveData(data) {
// 发送 POST 请求到保存用户数据的接口,并传递用户数据
return post('/exam/api/sys/user/save', data)
}
/**
* 用户注册
* @param {Object} data - 包含用户注册信息的对象
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function userReg(data) {
// 发送 POST 请求到用户注册接口,并传递用户注册信息
return post('/exam/api/sys/user/reg', data)
}

@ -0,0 +1,41 @@
// 从 '@/utils/request' 模块导入 post 方法,用于发送 POST 请求
import { post } from '@/utils/request'
/**
* 用户登录接口
* @param {Object} data - 包含用户登录所需信息的对象如用户名密码等
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function login(data) {
// 发送 POST 请求到用户登录接口,并传递登录信息
return post('/exam/api/sys/user/login', data)
}
/**
* 获取用户信息接口
* @param {string} token - 用户的身份验证令牌
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function getInfo(token) {
// 发送 POST 请求到获取用户信息接口,将 token 附加到 URL 参数中
return post('/exam/api/sys/user/info?token=' + token)
}
/**
* 用户登出接口
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function logout() {
// 发送 POST 请求到用户登出接口,请求体为空对象
return post('/exam/api/sys/user/logout', {})
}
/**
* 用户注册接口
* @param {Object} data - 包含用户注册所需信息的对象如用户名密码邮箱等
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function reg(data) {
// 发送 POST 请求到用户注册接口,并传递注册信息
return post('/exam/api/sys/user/reg', data)
}

@ -0,0 +1,13 @@
// 从 '@/utils/request' 模块导入 post 方法,用于发送 POST 请求
import { post } from '@/utils/request'
/**
* 获取下一道错题信息
* @param {string|number} examId - 考试的唯一标识
* @param {string|number} quId - 题目的唯一标识
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function nextQu(examId, quId) {
// 发送 POST 请求到指定接口,携带考试 ID 和题目 ID 参数
return post('/exam/api/user/wrong-book/next', { examId: examId, quId: quId })
}

@ -0,0 +1,64 @@
// 从 '@/utils/request' 模块导入 post 方法,用于发送 POST 请求
import { post } from '@/utils/request'
/**
* 开始训练
* @param {string} mode - 训练模式
* @param {string|number} repoId - 题库的唯一标识
* @param {string|number} userId - 用户的唯一标识
* @param {boolean} clear - 是否清除之前的训练数据
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function startTrain(mode, repoId, userId, clear) {
// 发送 POST 请求到开始训练的接口,携带训练模式、题库 ID、用户 ID 和是否清除数据的参数
return post('/exam/api/user/repo/start', { mode: mode, repoId: repoId, userId: userId, clear: clear })
}
/**
* 填充训练结果
* @param {string|number} id - 题目的唯一标识
* @param {Array|string} answers - 用户给出的答案
* @param {boolean} isRight - 答案是否正确
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function fillResult(id, answers, isRight) {
// 发送 POST 请求到填充结果的接口,携带题目 ID、用户答案和答案是否正确的参数
return post('/exam/api/user/repo/fill', { id: id, answers: answers, isRight: isRight })
}
/**
* 检查是否存在进行中的训练
* @param {string} mode - 训练模式
* @param {string|number} repoId - 题库的唯一标识
* @param {string|number} userId - 用户的唯一标识
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function hasTrain(mode, repoId, userId) {
// 发送 POST 请求到检查训练状态的接口,携带训练模式、题库 ID 和用户 ID 参数
return post('/exam/api/user/repo/check', { mode: mode, repoId: repoId, userId: userId })
}
/**
* 获取答题卡列表
* @param {string} mode - 训练模式
* @param {string|number} repoId - 题库的唯一标识
* @param {string|number} userId - 用户的唯一标识
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function listCard(mode, repoId, userId) {
// 发送 POST 请求到获取答题卡列表的接口,携带训练模式、题库 ID 和用户 ID 参数
return post('/exam/api/user/repo/card', { mode: mode, repoId: repoId, userId: userId })
}
/**
* 获取下一题
* @param {string} mode - 训练模式
* @param {string|number} repoId - 题库的唯一标识
* @param {string|number} userId - 用户的唯一标识
* @param {string|number} sequence - 题目序号
* @returns {Promise} - 返回一个 Promise 对象用于处理请求结果
*/
export function nextQu(mode, repoId, userId, sequence) {
// 发送 POST 请求到获取下一题的接口,携带训练模式、题库 ID、用户 ID 和题目序号参数
return post('/exam/api/user/repo/next', { mode: mode, repoId: repoId, userId: userId, sequence: sequence })
}

@ -0,0 +1,82 @@
<template>
<!-- 组件根容器 -->
<div>
<!-- 引入本地文件上传子组件使用 v-model 双向绑定文件地址通过 props 传递文件类型提示信息和列表类型 -->
<file-upload-local v-model="fileUrl" :accept="accept" :tips="tips" :list-type="listType" />
</div>
</template>
<script>
//
import FileUploadLocal from './local'
export default {
//
name: 'FileUpload',
//
components: { FileUploadLocal },
//
props: {
//
value: String,
//
accept: {
type: String,
default: '*'
},
//
tips: String,
//
listType: {
type: String,
default: 'picture'
}
},
//
data() {
return {
//
fileUrl: ''
}
},
//
watch: {
// value
value: {
handler() {
// value fillValue
this.fillValue()
}
},
//
fileUrl: {
handler() {
// input
this.$emit('input', this.fileUrl)
}
}
},
//
mounted() {
},
//
created() {
// fillValue
this.fillValue()
},
//
methods: {
/**
* 将父组件传递的 value 属性值赋值给本地的 fileUrl
*/
fillValue() {
this.fileUrl = this.value
}
}
}
</script>

@ -0,0 +1,30 @@
<!-- 声明 XML 文档版本为 1.0,并指定字符编码为 UTF-8 -->
<?xml version="1.0" encoding="UTF-8"?>
<!-- 定义文档类型,引用 MyBatis Mapper 的 DTD 规范,用于验证当前 XML 文档结构是否符合 MyBatis Mapper 3.0 标准 -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 定义一个 MyBatis Mapper 命名空间,将此 XML 映射文件与对应的 Java Mapper 接口关联起来 -->
<!-- namespace 属性值为 Java Mapper 接口的全限定名 -->
<mapper namespace="com.yf.exam.modules.exam.mapper.ExamDepartMapper">
<!-- 通用查询映射结果 -->
<!-- 定义一个结果映射,将数据库查询结果映射到 Java 对象 -->
<!-- id 属性为结果映射的唯一标识,可在其他 SQL 语句中引用 -->
<!-- type 属性指定映射的 Java 对象的全限定类名 -->
<resultMap id="BaseResultMap" type="com.yf.exam.modules.exam.entity.ExamDepart">
<!-- 映射数据库表的主键列到 Java 对象的属性 -->
<!-- column 属性为数据库表的列名 -->
<!-- property 属性为 Java 对象的属性名 -->
<id column="id" property="id" />
<!-- 映射数据库表的普通列到 Java 对象的属性 -->
<result column="exam_id" property="examId" />
<result column="depart_id" property="departId" />
</resultMap>
<!-- 通用查询结果列 -->
<!-- 定义一段可复用的 SQL 片段,包含常用的查询列 -->
<!-- id 属性为 SQL 片段的唯一标识,可在其他 SQL 语句中通过 <include> 标签引用 -->
<sql id="Base_Column_List">
`id`,`exam_id`,`depart_id`
</sql>
</mapper>

@ -0,0 +1,123 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 定义文档类型,引用 MyBatis Mapper 3.0 的 DTD用于验证 XML 文档结构是否符合 MyBatis 规范 -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 定义 Mapper 命名空间,关联到对应的 Java Mapper 接口,方便 MyBatis 找到对应的操作方法 -->
<mapper namespace="com.yf.exam.modules.exam.mapper.ExamMapper">
<!-- 通用查询映射结果,将数据库查询结果映射到 Java 对象 com.yf.exam.modules.exam.entity.Exam -->
<resultMap id="BaseResultMap" type="com.yf.exam.modules.exam.entity.Exam">
<!-- 映射数据库表的主键列 id 到 Java 对象的 id 属性 -->
<id column="id" property="id" />
<!-- 映射数据库表的 title 列到 Java 对象的 title 属性 -->
<result column="title" property="title" />
<!-- 映射数据库表的 content 列到 Java 对象的 content 属性 -->
<result column="content" property="content" />
<!-- 映射数据库表的 open_type 列到 Java 对象的 openType 属性 -->
<result column="open_type" property="openType" />
<!-- 映射数据库表的 state 列到 Java 对象的 state 属性 -->
<result column="state" property="state" />
<!-- 映射数据库表的 time_limit 列到 Java 对象的 timeLimit 属性 -->
<result column="time_limit" property="timeLimit" />
<!-- 映射数据库表的 start_time 列到 Java 对象的 startTime 属性 -->
<result column="start_time" property="startTime" />
<!-- 映射数据库表的 end_time 列到 Java 对象的 endTime 属性 -->
<result column="end_time" property="endTime" />
<!-- 映射数据库表的 create_time 列到 Java 对象的 createTime 属性 -->
<result column="create_time" property="createTime" />
<!-- 映射数据库表的 update_time 列到 Java 对象的 updateTime 属性 -->
<result column="update_time" property="updateTime" />
<!-- 映射数据库表的 total_score 列到 Java 对象的 totalScore 属性 -->
<result column="total_score" property="totalScore" />
<!-- 映射数据库表的 total_time 列到 Java 对象的 totalTime 属性 -->
<result column="total_time" property="totalTime" />
<!-- 映射数据库表的 qualify_score 列到 Java 对象的 qualifyScore 属性 -->
<result column="qualify_score" property="qualifyScore" />
</resultMap>
<!-- 定义通用查询结果列,可在其他 SQL 语句中通过 <include> 标签引用 -->
<sql id="Base_Column_List">
`id`,`title`,`content`,`open_type`,`join_type`,`level`,`state`,`time_limit`,`start_time`,`end_time`,`create_time`,`update_time`,`total_score`,`total_time`,`qualify_score`
</sql>
<!-- 定义用于审核分页查询的结果映射,继承自 BaseResultMap扩展了 examUser 和 unreadPaper 字段的映射 -->
<resultMap id="ReviewResultMap"
type="com.yf.exam.modules.exam.dto.response.ExamReviewRespDTO"
extends="BaseResultMap">
<!-- 映射子查询得到的 examUser 列到 Java 对象的 examUser 属性 -->
<result column="examUser" property="examUser" />
<!-- 映射子查询得到的 unreadPaper 列到 Java 对象的 unreadPaper 属性 -->
<result column="unreadPaper" property="unreadPaper" />
</resultMap>
<!-- 定义用于列表查询的结果映射,继承自 BaseResultMap -->
<resultMap id="ListResultMap"
type="com.yf.exam.modules.exam.dto.ExamDTO"
extends="BaseResultMap">
</resultMap>
<!-- 定义分页查询的 SQL 语句,使用 ListResultMap 进行结果映射 -->
<select id="paging" resultMap="ListResultMap">
SELECT * FROM el_exam
<!-- <where> 标签会自动处理 SQL 语句中的 AND 或 OR 关键字,避免多余的连接词 -->
<where>
<!-- 判断查询参数 query 是否不为空 -->
<if test="query!=null">
<!-- 判断查询参数中的 title 是否不为空且不为空字符串 -->
<if test="query.title!=null and query.title!=''">
AND title LIKE CONCAT('%',#{query.title},'%')
</if>
<!-- 判断查询参数中的 openType 是否不为空 -->
<if test="query.openType!=null">
AND open_type = #{query.openType}
</if>
<!-- 判断查询参数中的 startTime 是否不为空 -->
<if test="query.startTime!=null">
AND start_time >= #{query.startTime}
</if>
<!-- 判断查询参数中的 endTime 是否不为空 -->
<if test="query.endTime!=null">
AND end_time &lt;= #{query.endTime}
</if>
</if>
</where>
</select>
<!-- 定义审核分页查询的 SQL 语句,使用 ReviewResultMap 进行结果映射 -->
<select id="reviewPaging" resultMap="ReviewResultMap">
SELECT ex.*,
-- 子查询,统计不同用户参加考试的数量
(SELECT COUNT(DISTINCT user_id) FROM el_paper WHERE exam_id=ex.id) as examUser,
-- 子查询,统计状态为 1 的未批阅试卷数量
(SELECT COUNT(0) FROM el_paper WHERE exam_id=ex.id AND state=1) as unreadPaper
FROM el_exam ex
WHERE ex.has_saq=1
</select>
<!-- 定义在线考试查询的结果映射,继承自 BaseResultMap -->
<resultMap id="OnlineResultMap"
type="com.yf.exam.modules.exam.dto.response.ExamOnlineRespDTO"
extends="BaseResultMap">
</resultMap>
<!-- 定义在线考试查询的 SQL 语句,使用 OnlineResultMap 进行结果映射 -->
<select id="online" resultMap="OnlineResultMap">
SELECT ex.*
FROM el_exam ex
LEFT JOIN el_exam_depart dept ON ex.id=dept.exam_id AND ex.open_type=2
LEFT JOIN sys_user uc ON uc.depart_id=dept.depart_id
WHERE ex.state=0 AND (ex.open_type=1 OR ex.open_type=3 OR uc.id='{{userId}}')
<!-- 判断查询参数 query 是否不为空 -->
<if test="query!=null">
<!-- 判断查询参数中的 title 是否不为空且不为空字符串 -->
<if test="query.title!=null and query.title!=''">
AND ex.title LIKE CONCAT('%',#{query.title},'%')
</if>
<!-- 判断查询参数中的 openType 是否不为空 -->
<if test="query.openType!=null">
AND ex.open_type=#{query.openType}
</if>
</if>
</select>
</mapper>

@ -0,0 +1,60 @@
<!-- 声明 XML 文档版本为 1.0,指定字符编码为 UTF-8确保文档能正确处理各种字符 -->
<?xml version="1.0" encoding="UTF-8"?>
<!-- 定义文档类型,引用 MyBatis Mapper 3.0 的 DTD用于验证当前 XML 文档是否符合 MyBatis Mapper 规范 -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 定义 Mapper 命名空间,将此 XML 映射文件与对应的 Java Mapper 接口关联起来,方便 MyBatis 找到对应的操作方法 -->
<mapper namespace="com.yf.exam.modules.exam.mapper.ExamRepoMapper">
<!-- 通用查询映射结果,将数据库查询结果映射到 Java 实体类 com.yf.exam.modules.exam.entity.ExamRepo -->
<resultMap id="BaseResultMap" type="com.yf.exam.modules.exam.entity.ExamRepo">
<!-- 映射数据库表的主键列 id 到 Java 实体类的 id 属性 -->
<id column="id" property="id" />
<!-- 映射数据库表的 exam_id 列到 Java 实体类的 examId 属性 -->
<result column="exam_id" property="examId" />
<!-- 映射数据库表的 repo_id 列到 Java 实体类的 repoId 属性 -->
<result column="repo_id" property="repoId" />
<!-- 映射数据库表的 radio_count 列到 Java 实体类的 radioCount 属性 -->
<result column="radio_count" property="radioCount" />
<!-- 映射数据库表的 radio_score 列到 Java 实体类的 radioScore 属性 -->
<result column="radio_score" property="radioScore" />
<!-- 映射数据库表的 multi_count 列到 Java 实体类的 multiCount 属性 -->
<result column="multi_count" property="multiCount" />
<!-- 映射数据库表的 multi_score 列到 Java 实体类的 multiScore 属性 -->
<result column="multi_score" property="multiScore" />
<!-- 映射数据库表的 judge_count 列到 Java 实体类的 judgeCount 属性 -->
<result column="judge_count" property="judgeCount" />
<!-- 映射数据库表的 judge_score 列到 Java 实体类的 judgeScore 属性 -->
<result column="judge_score" property="judgeScore" />
</resultMap>
<!-- 定义通用查询结果列,可在其他 SQL 语句中通过 <include> 标签引用,提高代码复用性 -->
<sql id="Base_Column_List">
`id`,`exam_id`,`repo_id`,`radio_count`,`radio_score`,`multi_count`,`multi_score`,`judge_count`,`judge_score`
</sql>
<!-- 扩展查询映射结果,继承 BaseResultMap将额外的查询结果映射到 Java DTO 类 com.yf.exam.modules.exam.dto.ext.ExamRepoExtDTO -->
<resultMap id="ExtResultMap" type="com.yf.exam.modules.exam.dto.ext.ExamRepoExtDTO" extends="BaseResultMap">
<!-- 映射子查询结果 totalRadio 列到 Java DTO 类的 totalRadio 属性 -->
<result column="totalRadio" property="totalRadio" />
<!-- 映射子查询结果 totalMulti 列到 Java DTO 类的 totalMulti 属性 -->
<result column="totalMulti" property="totalMulti" />
<!-- 映射子查询结果 totalJudge 列到 Java DTO 类的 totalJudge 属性 -->
<result column="totalJudge" property="totalJudge" />
</resultMap>
<!-- 定义查询方法,根据 examId 查询考试仓库信息,并使用 ExtResultMap 进行结果映射 -->
<select id="listByExam" resultMap="ExtResultMap">
<!-- 查询 el_exam_repo 表的所有列 -->
SELECT ep.*,
<!-- 子查询统计题库中题型为单选题qu_type=1的题目数量 -->
(SELECT COUNT(0) FROM el_qu_repo WHERE repo_id=ep.repo_id AND qu_type=1) AS totalRadio,
<!-- 子查询统计题库中题型为多选题qu_type=2的题目数量 -->
(SELECT COUNT(0) FROM el_qu_repo WHERE repo_id=ep.repo_id AND qu_type=2) AS totalMulti,
<!-- 子查询统计题库中题型为判断题qu_type=3的题目数量 -->
(SELECT COUNT(0) FROM el_qu_repo WHERE repo_id=ep.repo_id AND qu_type=3) AS totalJudge
FROM el_exam_repo ep
<!-- 根据传入的 examId 进行筛选 -->
WHERE ep.exam_id=#{examId}
</select>
</mapper>

@ -0,0 +1,102 @@
<!-- 声明 XML 文档版本为 1.0,指定字符编码为 UTF-8确保文档能正确处理各种字符 -->
<?xml version="1.0" encoding="UTF-8"?>
<!-- 定义文档类型,引用 MyBatis Mapper 3.0 的 DTD用于验证当前 XML 文档是否符合 MyBatis Mapper 规范 -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 定义 Mapper 命名空间,将此 XML 映射文件与对应的 Java Mapper 接口关联起来,方便 MyBatis 找到对应的操作方法 -->
<mapper namespace="com.yf.exam.modules.paper.mapper.PaperMapper">
<!-- 通用查询映射结果,将数据库查询结果映射到 Java 实体类 com.yf.exam.modules.paper.entity.Paper -->
<resultMap id="BaseResultMap" type="com.yf.exam.modules.paper.entity.Paper">
<!-- 映射数据库表的主键列 id 到 Java 实体类的 id 属性 -->
<id column="id" property="id" />
<!-- 映射数据库表的 user_id 列到 Java 实体类的 userId 属性 -->
<result column="user_id" property="userId" />
<!-- 映射数据库表的 depart_id 列到 Java 实体类的 departId 属性 -->
<result column="depart_id" property="departId" />
<!-- 映射数据库表的 exam_id 列到 Java 实体类的 examId 属性 -->
<result column="exam_id" property="examId" />
<!-- 映射数据库表的 title 列到 Java 实体类的 title 属性 -->
<result column="title" property="title" />
<!-- 映射数据库表的 total_time 列到 Java 实体类的 totalTime 属性 -->
<result column="total_time" property="totalTime" />
<!-- 映射数据库表的 user_time 列到 Java 实体类的 userTime 属性 -->
<result column="user_time" property="userTime" />
<!-- 映射数据库表的 total_score 列到 Java 实体类的 totalScore 属性 -->
<result column="total_score" property="totalScore" />
<!-- 映射数据库表的 qualify_score 列到 Java 实体类的 qualifyScore 属性 -->
<result column="qualify_score" property="qualifyScore" />
<!-- 映射数据库表的 obj_score 列到 Java 实体类的 objScore 属性 -->
<result column="obj_score" property="objScore" />
<!-- 映射数据库表的 subj_score 列到 Java 实体类的 subjScore 属性 -->
<result column="subj_score" property="subjScore" />
<!-- 映射数据库表的 user_score 列到 Java 实体类的 userScore 属性 -->
<result column="user_score" property="userScore" />
<!-- 映射数据库表的 has_saq 列到 Java 实体类的 hasSaq 属性 -->
<result column="has_saq" property="hasSaq" />
<!-- 映射数据库表的 state 列到 Java 实体类的 state 属性 -->
<result column="state" property="state" />
<!-- 映射数据库表的 create_time 列到 Java 实体类的 createTime 属性 -->
<result column="create_time" property="createTime" />
<!-- 映射数据库表的 update_time 列到 Java 实体类的 updateTime 属性 -->
<result column="update_time" property="updateTime" />
<!-- 映射数据库表的 limit_time 列到 Java 实体类的 limitTime 属性 -->
<result column="limit_time" property="limitTime" />
</resultMap>
<!-- 定义通用查询结果列,可在其他 SQL 语句中通过 <include> 标签引用,提高代码复用性 -->
<sql id="Base_Column_List">
`id`,`user_id`,`depart_id`,`exam_id`,`title`,`total_time`,`user_time`,`total_score`,`qualify_score`,`obj_score`,`subj_score`,`user_score`,`has_saq`,`state`,`create_time`,`update_time`,`limit_time`
</sql>
<!-- 定义列表查询结果映射,继承 BaseResultMap将结果映射到 Java DTO 类 com.yf.exam.modules.paper.dto.response.PaperListRespDTO -->
<resultMap id="ListResultMap"
extends="BaseResultMap"
type="com.yf.exam.modules.paper.dto.response.PaperListRespDTO">
<!-- 映射数据库表的 real_name 列到 Java DTO 类的 realName 属性 -->
<result column="real_name" property="realName" />
</resultMap>
<!-- 定义分页查询方法,使用 ListResultMap 进行结果映射 -->
<select id="paging" resultMap="ListResultMap">
<!-- 查询 el_paper 表的所有列和 sys_user 表的 real_name 列 -->
SELECT pp.*,uc.real_name FROM el_paper pp
<!-- 左连接 sys_user 表,通过 user_id 关联 -->
LEFT JOIN sys_user uc ON pp.user_id=uc.id
<!-- <where> 标签会自动处理 SQL 语句中的 AND 关键字,避免多余的连接词 -->
<where>
<!-- 判断查询参数 query 是否不为空 -->
<if test="query!=null">
<!-- 判断查询参数中的 examId 是否不为空且不为空字符串 -->
<if test="query.examId!=null and query.examId!=''">
<!-- 添加筛选条件,根据 examId 过滤 -->
AND pp.exam_id=#{query.examId}
</if>
<!-- 判断查询参数中的 userId 是否不为空且不为空字符串 -->
<if test="query.userId!=null and query.userId!=''">
<!-- 添加筛选条件,根据 userId 过滤 -->
AND pp.user_id=#{query.userId}
</if>
<!-- 判断查询参数中的 departId 是否不为空且不为空字符串 -->
<if test="query.departId!=null and query.departId!=''">
<!-- 添加筛选条件,根据 departId 过滤 -->
AND pp.depart_id=#{query.departId}
</if>
<!-- 判断查询参数中的 state 是否不为空 -->
<if test="query.state!=null">
<!-- 添加筛选条件,根据 state 过滤 -->
AND pp.state=#{query.state}
</if>
<!-- 判断查询参数中的 realName 是否不为空且不为空字符串 -->
<if test="query.realName!=null and query.realName!=''">
<!-- 添加模糊查询条件,根据 realName 过滤 -->
AND uc.real_name LIKE CONCAT('%',#{query.realName},'%')
</if>
</if>
</where>
<!-- 按创建时间倒序排序 -->
ORDER BY create_time DESC
</select>
</mapper>

@ -0,0 +1,75 @@
<!-- 声明 XML 文档版本为 1.0,并指定字符编码为 UTF-8确保文档能正确处理各种字符 -->
<?xml version="1.0" encoding="UTF-8"?>
<!-- 定义文档类型,引用 MyBatis Mapper 3.0 的 DTD用于验证当前 XML 文档是否符合 MyBatis Mapper 规范 -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 定义 Mapper 命名空间,将此 XML 映射文件与对应的 Java Mapper 接口关联起来,方便 MyBatis 找到对应的操作方法 -->
<mapper namespace="com.yf.exam.modules.paper.mapper.PaperQuAnswerMapper">
<!-- 通用查询映射结果 -->
<!-- 定义一个基础结果映射,将数据库查询结果映射到 Java 实体类 com.yf.exam.modules.paper.entity.PaperQuAnswer -->
<resultMap id="BaseResultMap" type="com.yf.exam.modules.paper.entity.PaperQuAnswer">
<!-- 映射数据库表的主键列 id 到 Java 实体类的 id 属性 -->
<id column="id" property="id" />
<!-- 映射数据库表的 paper_id 列到 Java 实体类的 paperId 属性 -->
<result column="paper_id" property="paperId" />
<!-- 映射数据库表的 answer_id 列到 Java 实体类的 answerId 属性 -->
<result column="answer_id" property="answerId" />
<!-- 映射数据库表的 qu_id 列到 Java 实体类的 quId 属性 -->
<result column="qu_id" property="quId" />
<!-- 映射数据库表的 is_right 列到 Java 实体类的 isRight 属性 -->
<result column="is_right" property="isRight" />
<!-- 映射数据库表的 checked 列到 Java 实体类的 checked 属性 -->
<result column="checked" property="checked" />
<!-- 映射数据库表的 sort 列到 Java 实体类的 sort 属性 -->
<result column="sort" property="sort" />
<!-- 映射数据库表的 abc 列到 Java 实体类的 abc 属性 -->
<result column="abc" property="abc" />
</resultMap>
<!-- 通用查询结果列 -->
<!-- 定义一段可复用的 SQL 片段,包含常用的查询列,可在其他 SQL 语句中通过 <include> 标签引用 -->
<sql id="Base_Column_List">
`id`,`paper_id`,`answer_id`,`qu_id`,`is_right`,`checked`,`sort`,`abc`
</sql>
<!-- 定义一个扩展的结果映射,继承自 BaseResultMap将结果映射到 Java DTO 类 com.yf.exam.modules.paper.dto.ext.PaperQuAnswerExtDTO -->
<resultMap id="ListResultMap"
type="com.yf.exam.modules.paper.dto.ext.PaperQuAnswerExtDTO"
extends="BaseResultMap">
<!-- 映射数据库表的 image 列到 Java DTO 类的 image 属性 -->
<result column="image" property="image" />
<!-- 映射数据库表的 content 列到 Java DTO 类的 content 属性 -->
<result column="content" property="content" />
</resultMap>
<!-- 定义一个查询方法,用于根据试卷 ID 和题目 ID 查询相关答案列表 -->
<!-- id 属性为查询方法的唯一标识,对应 Java Mapper 接口中的方法名 -->
<!-- resultMap 属性指定使用 ListResultMap 进行结果映射 -->
<select id="list" resultMap="ListResultMap">
<!-- 查询指定列,包括 el_paper_qu_answer 表和 el_qu_answer 表的相关列 -->
SELECT pa.`id`,pa.`paper_id`,pa.`answer_id`,pa.`qu_id`,pa.`checked`,pa.`sort`,pa.`abc`,qa.content,qa.image
FROM el_paper_qu_answer pa
<!-- 左连接 el_qu_answer 表,通过 answer_id 和 id 关联 -->
LEFT JOIN el_qu_answer qa ON pa.answer_id=qa.id
<!-- 根据传入的 paperId 和 quId 进行筛选 -->
WHERE pa.paper_id=#{paperId} AND pa.qu_id=#{quId}
<!-- 按 sort 字段升序排序 -->
ORDER BY pa.sort ASC
</select>
<!-- 定义一个查询方法,用于根据试卷 ID 和题目 ID 查询相关答案列表,用于展示场景 -->
<!-- id 属性为查询方法的唯一标识,对应 Java Mapper 接口中的方法名 -->
<!-- resultMap 属性指定使用 ListResultMap 进行结果映射 -->
<select id="listForShow" resultMap="ListResultMap">
<!-- 查询指定列,包括 el_paper_qu_answer 表和 el_qu_answer 表的相关列,包含 is_right 列 -->
SELECT pa.`id`,pa.`paper_id`,pa.`answer_id`,pa.`qu_id`,pa.`checked`,pa.`sort`,pa.`abc`,qa.content,qa.is_right,qa.image
FROM el_paper_qu_answer pa
<!-- 左连接 el_qu_answer 表,通过 answer_id 和 id 关联 -->
LEFT JOIN el_qu_answer qa ON pa.answer_id=qa.id
<!-- 根据传入的 paperId 和 quId 进行筛选 -->
WHERE pa.paper_id=#{paperId} AND pa.qu_id=#{quId}
<!-- 按 sort 字段升序排序 -->
ORDER BY pa.sort ASC
</select>
</mapper>

@ -0,0 +1,94 @@
<!-- 声明 XML 文档版本为 1.0,指定字符编码为 UTF-8确保文档能正确处理各种字符 -->
<?xml version="1.0" encoding="UTF-8"?>
<!-- 定义文档类型,引用 MyBatis Mapper 3.0 的 DTD用于验证当前 XML 文档是否符合 MyBatis Mapper 规范 -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 定义 Mapper 命名空间,将此 XML 映射文件与对应的 Java Mapper 接口关联起来,方便 MyBatis 找到对应的操作方法 -->
<mapper namespace="com.yf.exam.modules.paper.mapper.PaperQuMapper">
<!-- 通用查询映射结果,将数据库查询结果映射到 Java 实体类 com.yf.exam.modules.paper.entity.PaperQu -->
<resultMap id="BaseResultMap" type="com.yf.exam.modules.paper.entity.PaperQu">
<!-- 映射数据库表的主键列 id 到 Java 实体类的 id 属性 -->
<id column="id" property="id" />
<!-- 映射数据库表的 paper_id 列到 Java 实体类的 paperId 属性 -->
<result column="paper_id" property="paperId" />
<!-- 映射数据库表的 qu_id 列到 Java 实体类的 quId 属性 -->
<result column="qu_id" property="quId" />
<!-- 映射数据库表的 qu_type 列到 Java 实体类的 quType 属性 -->
<result column="qu_type" property="quType" />
<!-- 映射数据库表的 answered 列到 Java 实体类的 answered 属性 -->
<result column="answered" property="answered" />
<!-- 映射数据库表的 answer 列到 Java 实体类的 answer 属性 -->
<result column="answer" property="answer" />
<!-- 映射数据库表的 sort 列到 Java 实体类的 sort 属性 -->
<result column="sort" property="sort" />
<!-- 映射数据库表的 score 列到 Java 实体类的 score 属性 -->
<result column="score" property="score" />
<!-- 映射数据库表的 actual_score 列到 Java 实体类的 actualScore 属性 -->
<result column="actual_score" property="actualScore" />
<!-- 映射数据库表的 is_right 列到 Java 实体类的 isRight 属性 -->
<result column="is_right" property="isRight" />
</resultMap>
<!-- 通用查询结果列,定义一段可复用的 SQL 片段,包含常用的查询列 -->
<!-- 可在其他 SQL 语句中通过 <include refid="Base_Column_List"/> 标签引用 -->
<sql id="Base_Column_List">
`id`,`paper_id`,`qu_id`,`qu_type`,`answered`,`answer`,`sort`,`score`,`actual_score`,`is_right`
</sql>
<!-- 计算客观题总分 -->
<!-- id: 查询方法的唯一标识,对应 Java Mapper 接口中的方法名 -->
<!-- resultType: 查询结果的数据类型,这里为整数类型 -->
<select id="sumObjective" resultType="int">
<!-- 使用 IFNULL 函数处理 SUM(actual_score) 为 NULL 的情况,若为 NULL 则返回 0将结果命名为 total -->
SELECT IFNULL(SUM(actual_score),0) as total
FROM el_paper_qu
<!-- 根据传入的 paperId 进行筛选 -->
WHERE paper_id=#{paperId}
<!-- 筛选出回答正确的记录 -->
AND is_right=true
<!-- 筛选出题型小于 4 的客观题 -->
AND qu_type &lt; 4
</select>
<!-- 计算主观题总分 -->
<!-- id: 查询方法的唯一标识,对应 Java Mapper 接口中的方法名 -->
<!-- resultType: 查询结果的数据类型,这里为整数类型 -->
<select id="sumSubjective" resultType="int">
<!-- 使用 IFNULL 函数处理 SUM(actual_score) 为 NULL 的情况,若为 NULL 则返回 0将结果命名为 total -->
SELECT IFNULL(SUM(actual_score),0) as total
FROM el_paper_qu
<!-- 根据传入的 paperId 进行筛选 -->
WHERE paper_id=#{paperId}
<!-- 筛选出题型为 4 的主观题 -->
AND qu_type=4
</select>
<!-- 扩展查询结果映射,继承自 BaseResultMap将结果映射到 Java DTO 类 com.yf.exam.modules.paper.dto.ext.PaperQuDetailDTO -->
<resultMap id="ListResultMap" extends="BaseResultMap" type="com.yf.exam.modules.paper.dto.ext.PaperQuDetailDTO">
<!-- 映射数据库表的 image 列到 Java DTO 类的 image 属性 -->
<result column="image" property="image" />
<!-- 映射数据库表的 content 列到 Java DTO 类的 content 属性 -->
<result column="content" property="content" />
<!-- 定义一个集合映射,将关联查询结果映射到 Java DTO 类的 answerList 属性 -->
<!-- column: 传递给关联查询的参数paperId 和 quId -->
<!-- select: 指定关联查询的方法,调用 PaperQuAnswerMapper 中的 listForShow 方法 -->
<collection property="answerList" column="{paperId=paper_id,quId=qu_id}"
select="com.yf.exam.modules.paper.mapper.PaperQuAnswerMapper.listForShow" />
</resultMap>
<!-- 根据试卷 ID 查询题目列表 -->
<!-- id: 查询方法的唯一标识,对应 Java Mapper 接口中的方法名 -->
<!-- resultMap: 指定使用 ListResultMap 进行结果映射 -->
<select id="listByPaper" resultMap="ListResultMap">
<!-- 查询 el_paper_qu 表的所有列以及 el_qu 表的 content 和 image 列 -->
SELECT pq.*,eq.content,eq.image
FROM el_paper_qu pq
<!-- 左连接 el_qu 表,通过 qu_id 和 id 关联 -->
LEFT JOIN el_qu eq ON pq.qu_id = eq.id
<!-- 根据传入的 paperId 进行筛选 -->
WHERE pq.paper_id=#{paperId}
<!-- 按 sort 字段升序排序 -->
ORDER BY pq.sort ASC
</select>
</mapper>

@ -0,0 +1,38 @@
<!-- 声明 XML 文档的版本为 1.0,并指定字符编码为 UTF-8确保文档能正确处理各种字符 -->
<?xml version="1.0" encoding="UTF-8"?>
<!-- 定义文档类型,引用 MyBatis Mapper 3.0 的 DTD 规范,用于验证当前 XML 文档是否符合 MyBatis Mapper 标准 -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 定义 Mapper 命名空间,将此 XML 映射文件与对应的 Java Mapper 接口关联起来,方便 MyBatis 找到对应的操作方法 -->
<mapper namespace="com.yf.exam.modules.qu.mapper.QuAnswerMapper">
<!-- 通用查询映射结果
定义一个基础结果映射,将数据库查询结果映射到 Java 实体类 com.yf.exam.modules.qu.entity.QuAnswer
id: 结果映射的唯一标识,可在其他 SQL 语句中引用
type: 指定映射的 Java 实体类的全限定名
-->
<resultMap id="BaseResultMap" type="com.yf.exam.modules.qu.entity.QuAnswer">
<!-- 映射数据库表的主键列到 Java 实体类的属性
column: 数据库表的列名
property: Java 实体类的属性名
-->
<id column="id" property="id" />
<!-- 映射数据库表的普通列到 Java 实体类的属性
column: 数据库表的列名
property: Java 实体类的属性名
-->
<result column="qu_id" property="quId" />
<result column="is_right" property="isRight" />
<result column="image" property="image" />
<result column="content" property="content" />
<result column="analysis" property="analysis" />
</resultMap>
<!-- 通用查询结果列
定义一段可复用的 SQL 片段,包含常用的查询列
id: SQL 片段的唯一标识,可在其他 SQL 语句中通过 <include> 标签引用
-->
<sql id="Base_Column_List">
`id`,`qu_id`,`is_right`,`image`,`content`,`analysis`
</sql>
</mapper>

@ -0,0 +1,158 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 定义文档类型,引用 MyBatis Mapper 3.0 的 DTD用于验证 XML 文档结构是否符合 MyBatis 规范 -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 定义 Mapper 命名空间,关联到对应的 Java Mapper 接口,方便 MyBatis 找到对应的操作方法 -->
<mapper namespace="com.yf.exam.modules.qu.mapper.QuMapper">
<!-- 通用查询映射结果 -->
<!-- 定义一个结果映射,将数据库查询结果映射到 Java 实体类 com.yf.exam.modules.qu.entity.Qu -->
<resultMap id="BaseResultMap" type="com.yf.exam.modules.qu.entity.Qu">
<!-- 映射数据库表的主键列 id 到 Java 实体类的 id 属性 -->
<id column="id" property="id" />
<!-- 映射数据库表的 qu_type 列到 Java 实体类的 quType 属性 -->
<result column="qu_type" property="quType" />
<!-- 映射数据库表的 level 列到 Java 实体类的 level 属性 -->
<result column="level" property="level" />
<!-- 映射数据库表的 image 列到 Java 实体类的 image 属性 -->
<result column="image" property="image" />
<!-- 映射数据库表的 content 列到 Java 实体类的 content 属性 -->
<result column="content" property="content" />
<!-- 映射数据库表的 create_time 列到 Java 实体类的 createTime 属性 -->
<result column="create_time" property="createTime" />
<!-- 映射数据库表的 update_time 列到 Java 实体类的 updateTime 属性 -->
<result column="update_time" property="updateTime" />
<!-- 映射数据库表的 remark 列到 Java 实体类的 remark 属性 -->
<result column="remark" property="remark" />
<!-- 映射数据库表的 analysis 列到 Java 实体类的 analysis 属性 -->
<result column="analysis" property="analysis" />
</resultMap>
<!-- 通用查询结果列 -->
<!-- 定义一段可复用的 SQL 片段,包含常用的查询列,可在其他 SQL 语句中通过 <include> 标签引用 -->
<sql id="Base_Column_List">
`id`,`qu_type`,`level`,`image`,`content`,`create_time`,`update_time`,`remark`,`analysis`
</sql>
<!-- 随机取数据 -->
<!-- 定义一个查询方法,根据条件随机获取题目数据 -->
<select id="listByRandom" resultMap="BaseResultMap">
<!-- 从 el_qu 表中选取所有列,别名为 a -->
SELECT a.*
FROM el_qu a
<!-- 左连接 el_qu_repo 表,别名为 b通过 id 和 qu_id 关联 -->
LEFT JOIN el_qu_repo b ON a.id=b.qu_id
<!-- 筛选条件:指定题库 ID 和题目类型 -->
WHERE b.repo_id=#{repoId} AND a.qu_type=#{quType}
<!-- 判断 excludes 参数是否不为空 -->
<if test="excludes!=null">
<!-- 排除指定 ID 的题目 -->
AND a.id NOT IN
<!-- 遍历 excludes 集合,将集合元素用逗号分隔,用括号包裹 -->
<foreach item="item" collection="excludes" separator="," open="(" close=")" index="">'${item}'</foreach>
</if>
<!-- 按随机顺序排序 -->
ORDER BY RAND()
<!-- 限制返回结果数量 -->
LIMIT ${size}
</select>
<!-- 定义导出数据的结果映射,将数据库查询结果映射到 Java DTO 类 com.yf.exam.modules.qu.dto.export.QuExportDTO -->
<resultMap id="ExportResultMap" type="com.yf.exam.modules.qu.dto.export.QuExportDTO">
<!-- 映射数据库表的 q_id 列到 Java DTO 类的 qId 属性 -->
<id column="q_id" property="qId" />
<!-- 映射数据库表的 qu_type 列到 Java DTO 类的 quType 属性 -->
<result column="qu_type" property="quType" />
<!-- 映射数据库表的 q_content 列到 Java DTO 类的 qContent 属性 -->
<result column="q_content" property="qContent" />
<!-- 映射数据库表的 q_analysis 列到 Java DTO 类的 qAnalysis 属性 -->
<result column="q_analysis" property="qAnalysis" />
<!-- 映射数据库表的 a_is_right 列到 Java DTO 类的 aIsRight 属性 -->
<result column="a_is_right" property="aIsRight" />
<!-- 映射数据库表的 a_content 列到 Java DTO 类的 aContent 属性 -->
<result column="a_content" property="aContent" />
<!-- 映射数据库表的 a_analysis 列到 Java DTO 类的 aAnalysis 属性 -->
<result column="a_analysis" property="aAnalysis" />
<!-- 定义一个集合映射,通过 q_id 作为参数调用 selectRepos 方法,将结果映射到 repoList 属性 -->
<collection property="repoList" column="q_id" select="selectRepos"/>
</resultMap>
<!-- 定义一个查询方法,根据题目 ID 查询所属题库 ID 列表 -->
<select id="selectRepos" resultType="String">
<!-- 从 el_qu_repo 表中选取 repo_id 列 -->
SELECT repo_id FROM el_qu_repo po WHERE po.qu_id=#{qId}
</select>
<!-- 定义查询条件的 SQL 片段,可在其他 SQL 语句中通过 <include> 标签引用 -->
<sql id="query">
<!-- <where> 标签会自动处理 SQL 语句中的 AND 关键字,避免多余的连接词 -->
<where>
<!-- 判断 query 参数是否不为空 -->
<if test="query!=null">
<!-- 判断 query 中的 quType 属性是否不为空 -->
<if test="query.quType!=null">
<!-- 添加筛选条件,根据题目类型过滤 -->
AND q.qu_type = #{query.quType}
</if>
<!-- 判断 query 中的 repoIds 属性是否不为空且集合大小大于 0 -->
<if test="query.repoIds!=null and query.repoIds.size()>0">
<!-- 添加筛选条件,根据题库 ID 列表过滤 -->
AND po.repo_id IN
<!-- 遍历 query.repoIds 集合,将集合元素用逗号分隔,用括号包裹 -->
<foreach collection="query.repoIds" open="(" close=")" separator="," item="repoId">#{repoId}</foreach>
</if>
<!-- 判断 query 中的 content 属性是否不为空且不为空字符串 -->
<if test="query.content!=null and query.content!=''">
<!-- 添加模糊查询条件,根据题目内容过滤 -->
AND q.content LIKE CONCAT('%',#{query.content},'%')
</if>
<!-- 判断 query 中的 excludes 属性是否不为空且集合大小大于 0 -->
<if test="query.excludes!=null and query.excludes.size()>0">
<!-- 添加排除条件,排除指定 ID 的题目 -->
AND q.id NOT IN
<!-- 遍历 query.excludes 集合,将集合元素用逗号分隔,用括号包裹 -->
<foreach collection="query.excludes" open="(" close=")" separator="," item="quId">
#{quId}
</foreach>
</if>
</if>
</where>
</sql>
<!-- 定义分页查询方法,使用 BaseResultMap 进行结果映射 -->
<select id="paging" resultMap="BaseResultMap">
<!-- 从 el_qu 表中选取所有列,别名为 q -->
SELECT q.*
FROM el_qu q
<!-- 左连接 el_qu_repo 表,别名为 po通过 id 和 qu_id 关联 -->
LEFT JOIN el_qu_repo po ON q.id=po.qu_id
<!-- 引用 query SQL 片段,添加查询条件 -->
<include refid="query" />
<!-- 按题目 ID 分组,按更新时间倒序排序 -->
GROUP BY q.id ORDER BY q.update_time DESC
</select>
<!-- 定义导出数据的查询方法,使用 ExportResultMap 进行结果映射 -->
<select id="listForExport" resultMap="ExportResultMap">
<!-- 选取需要导出的列,并指定别名 -->
SELECT
q.id as q_id,
q.qu_type,
q.content AS q_content,
q.analysis as q_analysis,
a.content as a_content,
a.is_right as a_is_right,
a.analysis as a_analysis
FROM el_qu q
<!-- 左连接 el_qu_answer 表,别名为 a通过 id 和 qu_id 关联 -->
LEFT JOIN el_qu_answer a ON q.id=a.qu_id
<!-- 左连接 el_qu_repo 表,别名为 po通过 id 和 qu_id 关联 -->
LEFT JOIN el_qu_repo po ON q.id=po.qu_id
<!-- 引用 query SQL 片段,添加查询条件 -->
<include refid="query" />
<!-- 按答案 ID 分组,按题目 ID 排序 -->
GROUP BY a.id ORDER BY q.id
<!-- 限制返回结果数量为 10000 条 -->
LIMIT 10000
</select>
</mapper>

@ -0,0 +1,38 @@
<!-- 声明 XML 文档的版本为 1.0,并指定字符编码为 UTF-8确保文档能正确处理各种字符 -->
<?xml version="1.0" encoding="UTF-8"?>
<!-- 定义文档类型,引用 MyBatis Mapper 3.0 的 DTD用于验证当前 XML 文档是否符合 MyBatis Mapper 规范 -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 定义 Mapper 命名空间,将此 XML 映射文件与对应的 Java Mapper 接口关联起来,方便 MyBatis 找到对应的操作方法 -->
<mapper namespace="com.yf.exam.modules.qu.mapper.QuRepoMapper">
<!-- 通用查询映射结果
定义一个结果映射,将数据库查询结果映射到 Java 实体类 com.yf.exam.modules.qu.entity.QuRepo
id: 结果映射的唯一标识,可在其他 SQL 语句中引用
type: 指定映射的 Java 实体类的全限定名
-->
<resultMap id="BaseResultMap" type="com.yf.exam.modules.qu.entity.QuRepo">
<!-- 映射数据库表的主键列到 Java 实体类的属性
column: 数据库表的列名
property: Java 实体类的属性名
-->
<id column="id" property="id" />
<!-- 映射数据库表的普通列到 Java 实体类的属性
column: 数据库表的列名
property: Java 实体类的属性名
-->
<result column="qu_id" property="quId" />
<result column="repo_id" property="repoId" />
<result column="qu_type" property="quType" />
<result column="sort" property="sort" />
</resultMap>
<!-- 通用查询结果列
定义一段可复用的 SQL 片段,包含常用的查询列
id: SQL 片段的唯一标识,可在其他 SQL 语句中通过 <include> 标签引用
-->
<sql id="Base_Column_List">
`id`,`qu_id`,`repo_id`,`qu_type`,`sort`
</sql>
</mapper>

@ -0,0 +1,76 @@
<!-- 声明 XML 文档版本为 1.0,指定字符编码为 UTF-8确保文档能正确处理各种字符 -->
<?xml version="1.0" encoding="UTF-8"?>
<!-- 定义文档类型,引用 MyBatis Mapper 3.0 的 DTD用于验证当前 XML 文档是否符合 MyBatis Mapper 规范 -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 定义 Mapper 命名空间,将此 XML 映射文件与对应的 Java Mapper 接口关联起来,方便 MyBatis 找到对应的操作方法 -->
<mapper namespace="com.yf.exam.modules.repo.mapper.RepoMapper">
<!-- 通用查询映射结果 -->
<!-- 定义一个基础结果映射,将数据库查询结果映射到 Java 实体类 com.yf.exam.modules.repo.entity.Repo -->
<resultMap id="BaseResultMap" type="com.yf.exam.modules.repo.entity.Repo">
<!-- 映射数据库表的主键列 id 到 Java 实体类的 id 属性 -->
<id column="id" property="id" />
<!-- 映射数据库表的 code 列到 Java 实体类的 code 属性 -->
<result column="code" property="code" />
<!-- 映射数据库表的 title 列到 Java 实体类的 title 属性 -->
<result column="title" property="title" />
<!-- 映射数据库表的 remark 列到 Java 实体类的 remark 属性 -->
<result column="remark" property="remark" />
<!-- 映射数据库表的 create_time 列到 Java 实体类的 createTime 属性 -->
<result column="create_time" property="createTime" />
<!-- 映射数据库表的 update_time 列到 Java 实体类的 updateTime 属性 -->
<result column="update_time" property="updateTime" />
</resultMap>
<!-- 通用查询结果列 -->
<!-- 定义一段可复用的 SQL 片段,包含常用的查询列,可在其他 SQL 语句中通过 <include> 标签引用 -->
<sql id="Base_Column_List">
`id`,`code`,`title`,`radio_count`,`multi_count`,`judge_count`,`remark`,`create_time`,`update_time`
</sql>
<!-- 定义列表查询结果映射,继承自 BaseResultMap将结果映射到 Java DTO 类 com.yf.exam.modules.repo.dto.response.RepoRespDTO -->
<resultMap id="ListResultMap"
type="com.yf.exam.modules.repo.dto.response.RepoRespDTO"
extends="BaseResultMap">
<!-- 映射数据库表的 radio_count 列到 Java DTO 类的 radioCount 属性 -->
<result column="radio_count" property="radioCount" />
<!-- 映射数据库表的 multi_count 列到 Java DTO 类的 multiCount 属性 -->
<result column="multi_count" property="multiCount" />
<!-- 映射数据库表的 judge_count 列到 Java DTO 类的 judgeCount 属性 -->
<result column="judge_count" property="judgeCount" />
</resultMap>
<!-- 定义分页查询方法,使用 ListResultMap 进行结果映射 -->
<select id="paging" resultMap="ListResultMap">
<!-- 查询指定列,包括子查询得到的题型数量 -->
SELECT `id`, `code`, `title`, `remark`, `create_time`, `update_time`,
-- 子查询统计题库中题型为单选题qu_type=1的题目数量
(SELECT COUNT(0) FROM el_qu_repo WHERE repo_id=repo.id AND qu_type=1) AS radio_count,
-- 子查询统计题库中题型为多选题qu_type=2的题目数量
(SELECT COUNT(0) FROM el_qu_repo WHERE repo_id=repo.id AND qu_type=2) AS multi_count,
-- 子查询统计题库中题型为判断题qu_type=3的题目数量
(SELECT COUNT(0) FROM el_qu_repo WHERE repo_id=repo.id AND qu_type=3) AS judge_count
FROM el_repo repo
<!-- <where> 标签会自动处理 SQL 语句中的 AND 关键字,避免多余的连接词 -->
<where>
<!-- 判断查询参数 query 是否不为空 -->
<if test="query!=null">
<!-- 判断查询参数中的 title 是否不为空且不为空字符串 -->
<if test="query.title!=null and query.title!=''">
-- 添加模糊查询条件,根据 title 过滤
AND repo.title LIKE CONCAT('%',#{query.title}, '%')
</if>
<!-- 判断查询参数中的 excludes 集合是否不为空且元素数量大于 0 -->
<if test="query.excludes!=null and query.excludes.size()>0">
-- 添加排除条件,排除指定 id 的记录
AND repo.id NOT IN
<!-- 遍历 query.excludes 集合,将集合元素用逗号分隔,用括号包裹 -->
<foreach collection="query.excludes" open="(" close=")" separator="," item="id">
#{id}
</foreach>
</if>
</if>
</where>
</select>
</mapper>

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 定义文档类型,引用 MyBatis Mapper 3.0 的 DTD 规范,用于验证 XML 文档结构是否符合 MyBatis Mapper 标准 -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 定义 Mapper 命名空间,关联到对应的 Java Mapper 接口,方便 MyBatis 找到对应的操作方法 -->
<mapper namespace="com.yf.exam.modules.sys.depart.mapper.SysDepartMapper">
<!-- 通用查询映射结果,将数据库查询结果映射到 Java 实体类 com.yf.exam.modules.sys.depart.entity.SysDepart -->
<resultMap id="BaseResultMap" type="com.yf.exam.modules.sys.depart.entity.SysDepart">
<!-- 映射数据库表的主键列 id 到 Java 实体类的 id 属性 -->
<id column="id" property="id" />
<!-- 映射数据库表的 dept_type 列到 Java 实体类的 deptType 属性 -->
<result column="dept_type" property="deptType" />
<!-- 映射数据库表的 parent_id 列到 Java 实体类的 parentId 属性 -->
<result column="parent_id" property="parentId" />
<!-- 映射数据库表的 dept_name 列到 Java 实体类的 deptName 属性 -->
<result column="dept_name" property="deptName" />
<!-- 映射数据库表的 dept_code 列到 Java 实体类的 deptCode 属性 -->
<result column="dept_code" property="deptCode" />
<!-- 映射数据库表的 sort 列到 Java 实体类的 sort 属性 -->
<result column="sort" property="sort" />
</resultMap>
<!-- 通用查询结果列,定义可复用的 SQL 片段,包含常用查询列,可在其他 SQL 语句中引用 -->
<sql id="Base_Column_List">
`id`,`dept_type`,`parent_id`,`dept_name`,`dept_code`,`sort`
</sql>
<!-- 树形结构查询结果映射,将结果映射到 Java DTO 类 com.yf.exam.modules.sys.depart.dto.response.SysDepartTreeDTO继承自 BaseResultMap -->
<resultMap id="TreeResultMap"
type="com.yf.exam.modules.sys.depart.dto.response.SysDepartTreeDTO"
extends="BaseResultMap">
<!-- 集合映射,将关联查询结果映射到 Java DTO 类的 children 属性 -->
<!-- column: 传递给关联查询的参数,这里传递 id 列的值 -->
<!-- select: 指定关联查询的方法,调用本 Mapper 中的 findChildren 方法 -->
<collection property="children" column="id" select="findChildren"></collection>
</resultMap>
<!-- 查询指定父部门下的子部门列表,使用 TreeResultMap 进行结果映射 -->
<select id="findChildren" resultMap="TreeResultMap">
<!-- 从 sys_depart 表中查询所有列,筛选出 parent_id 等于传入参数 id 的记录 -->
SELECT * FROM sys_depart WHERE parent_id=#{id}
</select>
<!-- 分页查询顶级部门列表,使用 TreeResultMap 进行结果映射 -->
<select id="paging" resultMap="TreeResultMap">
<!-- 从 sys_depart 表中查询所有列,筛选出 parent_id 等于 '0' 的顶级部门记录 -->
SELECT * FROM sys_depart WHERE parent_id='0'
<!-- 判断查询参数 query 是否不为空 -->
<if test="query!=null">
<!-- 判断查询参数中的 deptName 属性是否不为空且不为空字符串 -->
<if test="query.deptName!=null and query.deptName!=''">
<!-- 添加模糊查询条件,根据部门名称过滤 -->
AND dept_name LIKE CONCAT('%',#{query.deptName},'%')
</if>
</if>
</select>
</mapper>

@ -0,0 +1,16 @@
<!-- 声明 XML 文档版本为 1.0,指定字符编码为 UTF-8确保文档能正确处理各种字符 -->
<?xml version="1.0" encoding="UTF-8"?>
<!-- 定义文档类型,引用 MyBatis Mapper 3.0 的 DTD用于验证当前 XML 文档是否符合 MyBatis Mapper 规范 -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 定义 Mapper 命名空间,将此 XML 映射文件与对应的 Java Mapper 接口关联起来,方便 MyBatis 找到对应的操作方法 -->
<mapper namespace="com.yf.exam.modules.sys.system.mapper.SysDictMapper">
<!-- 定义一个查询方法,方法名为 findDict将查询结果映射为 String 类型 -->
<!-- 该方法用于从指定表中根据键值对查询单个字段的值 -->
<select id="findDict" resultType="String">
<!-- 使用动态参数构建 SQL 语句,从指定表中查询指定字段 -->
<!-- 注意:使用 ${} 进行参数替换,存在 SQL 注入风险 -->
SELECT ${text} FROM ${table} WHERE ${key}=${value} LIMIT 1
</select>
</mapper>

@ -0,0 +1,34 @@
<!-- 声明 XML 文档版本为 1.0,指定字符编码为 UTF-8确保文档能正确处理各种字符 -->
<?xml version="1.0" encoding="UTF-8"?>
<!-- 定义文档类型,引用 MyBatis Mapper 3.0 的 DTD用于验证当前 XML 文档是否符合 MyBatis Mapper 规范 -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 定义 Mapper 命名空间,将此 XML 映射文件与对应的 Java Mapper 接口关联起来,方便 MyBatis 找到对应的操作方法 -->
<mapper namespace="com.yf.exam.modules.sys.user.mapper.SysRoleMapper">
<!-- 通用查询映射结果
定义一个结果映射,将数据库查询结果映射到 Java 实体类 com.yf.exam.modules.sys.user.entity.SysRole
id: 结果映射的唯一标识,可在其他 SQL 语句中引用
type: 指定映射的 Java 实体类的全限定名
-->
<resultMap id="BaseResultMap" type="com.yf.exam.modules.sys.user.entity.SysRole">
<!-- 映射数据库表的主键列到 Java 实体类的属性
column: 数据库表的列名
property: Java 实体类的属性名
-->
<id column="id" property="id" />
<!-- 映射数据库表的普通列到 Java 实体类的属性
column: 数据库表的列名
property: Java 实体类的属性名
-->
<result column="role_name" property="roleName" />
</resultMap>
<!-- 通用查询结果列
定义一段可复用的 SQL 片段,包含常用的查询列
id: SQL 片段的唯一标识,可在其他 SQL 语句中通过 <include> 标签引用
-->
<sql id="Base_Column_List">
`id`,`role_name`
</sql>
</mapper>

@ -0,0 +1,48 @@
<!-- 声明 XML 文档版本为 1.0,指定字符编码为 UTF-8确保文档能正确处理各种字符 -->
<?xml version="1.0" encoding="UTF-8"?>
<!-- 定义文档类型,引用 MyBatis Mapper 3.0 的 DTD用于验证当前 XML 文档是否符合 MyBatis Mapper 规范 -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 定义 Mapper 命名空间,将此 XML 映射文件与对应的 Java Mapper 接口关联起来,方便 MyBatis 找到对应的操作方法 -->
<mapper namespace="com.yf.exam.modules.sys.user.mapper.SysUserMapper">
<!-- 通用查询映射结果
定义一个结果映射,将数据库查询结果映射到 Java 实体类 com.yf.exam.modules.sys.user.entity.SysUser
id: 结果映射的唯一标识,可在其他 SQL 语句中引用
type: 指定映射的 Java 实体类的全限定名
-->
<resultMap id="BaseResultMap" type="com.yf.exam.modules.sys.user.entity.SysUser">
<!-- 映射数据库表的主键列到 Java 实体类的属性
column: 数据库表的列名
property: Java 实体类的属性名
-->
<id column="id" property="id" />
<!-- 映射数据库表的用户名字段到 Java 实体类的属性 -->
<result column="user_name" property="userName" />
<!-- 映射数据库表的真实姓名字段到 Java 实体类的属性 -->
<result column="real_name" property="realName" />
<!-- 映射数据库表的密码字段到 Java 实体类的属性 -->
<result column="password" property="password" />
<!-- 映射数据库表的盐值字段到 Java 实体类的属性,通常用于密码加密 -->
<result column="salt" property="salt" />
<!-- 映射数据库表的角色 ID 字段到 Java 实体类的属性 -->
<result column="role_ids" property="roleIds" />
<!-- 映射数据库表的部门 ID 字段到 Java 实体类的属性 -->
<result column="depart_id" property="departId" />
<!-- 映射数据库表的创建时间字段到 Java 实体类的属性 -->
<result column="create_time" property="createTime" />
<!-- 映射数据库表的更新时间字段到 Java 实体类的属性 -->
<result column="update_time" property="updateTime" />
<!-- 映射数据库表的状态字段到 Java 实体类的属性 -->
<result column="state" property="state" />
</resultMap>
<!-- 通用查询结果列
定义一段可复用的 SQL 片段,包含常用的查询列
id: SQL 片段的唯一标识,可在其他 SQL 语句中通过 <include> 标签引用
-->
<sql id="Base_Column_List">
-- 定义需要查询的列,使用反引号避免与 SQL 关键字冲突
`id`,`user_name`,`real_name`,`password`,`salt`,`role_ids`,`depart_id`,`create_time`,`update_time`,`state`
</sql>
</mapper>

@ -0,0 +1,33 @@
<!-- 声明 XML 文档的版本为 1.0,并指定字符编码为 UTF-8确保文档能正确处理各种字符 -->
<?xml version="1.0" encoding="UTF-8"?>
<!-- 定义文档类型,引用 MyBatis Mapper 3.0 的 DTD用于验证当前 XML 文档是否符合 MyBatis Mapper 规范 -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 定义 Mapper 命名空间,将此 XML 映射文件与对应的 Java Mapper 接口关联起来,方便 MyBatis 找到对应的操作方法 -->
<mapper namespace="com.yf.exam.modules.sys.user.mapper.SysUserRoleMapper">
<!-- 通用查询映射结果
定义一个结果映射,将数据库查询结果映射到 Java 实体类 com.yf.exam.modules.sys.user.entity.SysUserRole
id: 结果映射的唯一标识,可在其他 SQL 语句中引用
type: 指定映射的 Java 实体类的全限定名
-->
<resultMap id="BaseResultMap" type="com.yf.exam.modules.sys.user.entity.SysUserRole">
<!-- 映射数据库表的主键列到 Java 实体类的属性
column: 数据库表的列名
property: Java 实体类的属性名
-->
<id column="id" property="id" />
<!-- 映射数据库表的用户 ID 列到 Java 实体类的属性 -->
<result column="user_id" property="userId" />
<!-- 映射数据库表的角色 ID 列到 Java 实体类的属性 -->
<result column="role_id" property="roleId" />
</resultMap>
<!-- 通用查询结果列
定义一段可复用的 SQL 片段,包含常用的查询列
id: SQL 片段的唯一标识,可在其他 SQL 语句中通过 <include> 标签引用
-->
<sql id="Base_Column_List">
`id`,`user_id`,`role_id`
</sql>
</mapper>

@ -0,0 +1,46 @@
<!-- 声明 XML 文档的版本为 1.0,并指定字符编码为 UTF-8确保文档能正确处理各种字符 -->
<?xml version="1.0" encoding="UTF-8"?>
<!-- 定义文档类型,引用 MyBatis Mapper 3.0 的 DTD用于验证当前 XML 文档是否符合 MyBatis Mapper 规范 -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 定义 Mapper 命名空间,将此 XML 映射文件与对应的 Java Mapper 接口关联起来,方便 MyBatis 找到对应的操作方法 -->
<mapper namespace="com.yf.exam.modules.user.book.mapper.UserBookMapper">
<!-- 通用查询映射结果
定义一个结果映射,将数据库查询结果映射到 Java 实体类 com.yf.exam.modules.user.book.entity.UserBook
id: 结果映射的唯一标识,可在其他 SQL 语句中引用
type: 指定映射的 Java 实体类的全限定名
-->
<resultMap id="BaseResultMap" type="com.yf.exam.modules.user.book.entity.UserBook">
<!-- 映射数据库表的主键列到 Java 实体类的属性
column: 数据库表的列名
property: Java 实体类的属性名
-->
<id column="id" property="id" />
<!-- 映射数据库表的 exam_id 列到 Java 实体类的 examId 属性 -->
<result column="exam_id" property="examId" />
<!-- 映射数据库表的 user_id 列到 Java 实体类的 userId 属性 -->
<result column="user_id" property="userId" />
<!-- 映射数据库表的 qu_id 列到 Java 实体类的 quId 属性 -->
<result column="qu_id" property="quId" />
<!-- 映射数据库表的 create_time 列到 Java 实体类的 createTime 属性 -->
<result column="create_time" property="createTime" />
<!-- 映射数据库表的 update_time 列到 Java 实体类的 updateTime 属性 -->
<result column="update_time" property="updateTime" />
<!-- 映射数据库表的 wrong_count 列到 Java 实体类的 wrongCount 属性 -->
<result column="wrong_count" property="wrongCount" />
<!-- 映射数据库表的 title 列到 Java 实体类的 title 属性 -->
<result column="title" property="title" />
<!-- 映射数据库表的 sort 列到 Java 实体类的 sort 属性 -->
<result column="sort" property="sort" />
</resultMap>
<!-- 通用查询结果列
定义一段可复用的 SQL 片段,包含常用的查询列
id: SQL 片段的唯一标识,可在其他 SQL 语句中通过 <include> 标签引用
-->
<sql id="Base_Column_List">
-- 定义需要查询的列,使用反引号避免与 SQL 关键字冲突
`id`,`exam_id`,`user_id`,`qu_id`,`create_time`,`update_time`,`wrong_count`,`title`,`sort`
</sql>
</mapper>

@ -0,0 +1,101 @@
<!-- 声明 XML 文档的版本为 1.0,并指定字符编码为 UTF-8确保文档能正确处理各种字符 -->
<?xml version="1.0" encoding="UTF-8"?>
<!-- 定义文档类型,引用 MyBatis Mapper 3.0 的 DTD用于验证当前 XML 文档是否符合 MyBatis Mapper 规范 -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 定义 Mapper 命名空间,将此 XML 映射文件与对应的 Java Mapper 接口关联起来,方便 MyBatis 找到对应的操作方法 -->
<mapper namespace="com.yf.exam.modules.user.exam.mapper.UserExamMapper">
<!-- 通用查询映射结果
定义一个基础结果映射,将数据库查询结果映射到 Java 实体类 com.yf.exam.modules.user.exam.entity.UserExam
id: 结果映射的唯一标识,可在其他 SQL 语句中引用
type: 指定映射的 Java 实体类的全限定名
-->
<resultMap id="BaseResultMap" type="com.yf.exam.modules.user.exam.entity.UserExam">
<!-- 映射数据库表的主键列到 Java 实体类的属性
column: 数据库表的列名
property: Java 实体类的属性名
-->
<id column="id" property="id" />
<!-- 映射用户 ID 列到 Java 实体类的 userId 属性 -->
<result column="user_id" property="userId" />
<!-- 映射考试 ID 列到 Java 实体类的 examId 属性 -->
<result column="exam_id" property="examId" />
<!-- 映射尝试次数列到 Java 实体类的 tryCount 属性 -->
<result column="try_count" property="tryCount" />
<!-- 映射最高分数列到 Java 实体类的 maxScore 属性 -->
<result column="max_score" property="maxScore" />
<!-- 映射是否通过列到 Java 实体类的 passed 属性 -->
<result column="passed" property="passed" />
<!-- 映射创建时间列到 Java 实体类的 createTime 属性 -->
<result column="create_time" property="createTime" />
<!-- 映射更新时间列到 Java 实体类的 updateTime 属性 -->
<result column="update_time" property="updateTime" />
</resultMap>
<!-- 通用查询结果列
定义一段可复用的 SQL 片段,包含常用的查询列
id: SQL 片段的唯一标识,可在其他 SQL 语句中通过 <include> 标签引用
-->
<sql id="Base_Column_List">
`id`,`user_id`,`exam_id`,`try_count`,`max_score`,`passed`,`create_time`,`update_time`
</sql>
<!-- 列表查询结果映射
定义一个结果映射,继承自 BaseResultMap将数据库查询结果映射到 Java DTO 类 com.yf.exam.modules.user.exam.dto.response.UserExamRespDTO
id: 结果映射的唯一标识,可在其他 SQL 语句中引用
type: 指定映射的 Java DTO 类的全限定名
extends: 继承的基础结果映射
-->
<resultMap id="ListResultMap"
type="com.yf.exam.modules.user.exam.dto.response.UserExamRespDTO"
extends="BaseResultMap">
<!-- 映射考试标题列到 Java DTO 类的 title 属性 -->
<result column="title" property="title" />
<!-- 映射用户真实姓名列到 Java DTO 类的 realName 属性 -->
<result column="real_name" property="realName" />
</resultMap>
<!-- 分页查询方法
定义一个查询方法,根据条件进行分页查询,使用 ListResultMap 进行结果映射
id: 查询方法的唯一标识,对应 Java Mapper 接口中的方法名
resultMap: 指定使用的结果映射
-->
<select id="paging" resultMap="ListResultMap">
<!-- 从 el_user_exam 表中选取所有列,同时选取 el_exam 表的 title 列和 sys_user 表的 real_name 列 -->
SELECT ue.*,ee.title,uc.real_name FROM el_user_exam ue
<!-- 左连接 el_exam 表,通过 exam_id 和 id 关联 -->
LEFT JOIN el_exam ee ON ue.exam_id=ee.id
<!-- 左连接 sys_user 表,通过 user_id 和 id 关联 -->
LEFT JOIN sys_user uc ON ue.user_id=uc.id
<!-- 筛选条件:确保 el_exam 表和 sys_user 表的关联记录存在 -->
WHERE ee.id IS NOT NULL AND uc.id IS NOT NULL
<!-- 判断查询参数 query 是否不为空 -->
<if test="query!=null">
<!-- 判断查询参数中的 userId 属性是否不为空且不为空字符串 -->
<if test="query.userId!=null and query.userId!=''">
<!-- 注意:此处存在错误,应使用 #{query.userId} 进行参数绑定,避免 SQL 注入
正确写法AND ue.user_id = #{query.userId}
-->
AND ue.user_id='{{userId}}'
</if>
<!-- 判断查询参数中的 examId 属性是否不为空且不为空字符串 -->
<if test="query.examId!=null and query.examId!=''">
<!-- 添加筛选条件,根据考试 ID 过滤 -->
AND ue.exam_id = #{query.examId}
</if>
<!-- 判断查询参数中的 title 属性是否不为空且不为空字符串 -->
<if test="query.title!=null and query.title!=''">
<!-- 添加模糊查询条件,根据考试标题过滤 -->
AND ee.title LIKE CONCAT('%',#{query.title},'%')
</if>
<!-- 判断查询参数中的 realName 属性是否不为空且不为空字符串 -->
<if test="query.realName!=null and query.realName!=''">
<!-- 添加模糊查询条件,根据用户真实姓名过滤 -->
AND uc.real_name LIKE CONCAT('%',#{query.realName},'%')
</if>
</if>
</select>
</mapper>

@ -0,0 +1,132 @@
package com.yf.exam.modules.repo.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.yf.exam.core.api.ApiRest;
import com.yf.exam.core.api.controller.BaseController;
import com.yf.exam.core.api.dto.BaseIdReqDTO;
import com.yf.exam.core.api.dto.BaseIdsReqDTO;
import com.yf.exam.core.api.dto.PagingReqDTO;
import com.yf.exam.modules.qu.dto.request.QuRepoBatchReqDTO;
import com.yf.exam.modules.qu.service.QuRepoService;
import com.yf.exam.modules.repo.dto.RepoDTO;
import com.yf.exam.modules.repo.dto.request.RepoReqDTO;
import com.yf.exam.modules.repo.dto.response.RepoRespDTO;
import com.yf.exam.modules.repo.entity.Repo;
import com.yf.exam.modules.repo.service.RepoService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
* <p>
* HTTP
* </p>
*
* @author
* @since 2020-05-25 13:25
*/
@Api(tags={"题库"})
@RestController
@RequestMapping("/exam/api/repo")
public class RepoController extends BaseController {
/**
*
*/
@Autowired
private RepoService baseService;
/**
*
*/
@Autowired
private QuRepoService quRepoService;
/**
*
* @param reqDTO
* @return ApiRest
*/
@RequiresRoles("sa")
@ApiOperation(value = "添加或修改")
@RequestMapping(value = "/save", method = { RequestMethod.POST})
public ApiRest save(@RequestBody RepoDTO reqDTO) {
// 调用服务层方法保存或更新题库信息
baseService.save(reqDTO);
// 返回操作成功的响应
return super.success();
}
/**
*
* @param reqDTO ID
* @return ApiRest
*/
@RequiresRoles("sa")
@ApiOperation(value = "批量删除")
@RequestMapping(value = "/delete", method = { RequestMethod.POST})
public ApiRest edit(@RequestBody BaseIdsReqDTO reqDTO) {
// 根据 ID 列表删除题库
baseService.removeByIds(reqDTO.getIds());
// 返回操作成功的响应
return super.success();
}
/**
*
* @param reqDTO ID
* @return ApiRest
*/
@RequiresRoles("sa")
@ApiOperation(value = "查找详情")
@RequestMapping(value = "/detail", method = { RequestMethod.POST})
public ApiRest<RepoDTO> find(@RequestBody BaseIdReqDTO reqDTO) {
// 根据 ID 获取题库实体
Repo entity = baseService.getById(reqDTO.getId());
// 创建一个新的 DTO 对象
RepoDTO dto = new RepoDTO();
// 将实体对象的属性复制到 DTO 对象中
BeanUtils.copyProperties(entity, dto);
// 返回包含 DTO 对象的操作成功响应
return super.success(dto);
}
/**
*
* @param reqDTO
* @return ApiRest
*/
@RequiresRoles("sa")
@ApiOperation(value = "分页查找")
@RequestMapping(value = "/paging", method = { RequestMethod.POST})
public ApiRest<IPage<RepoRespDTO>> paging(@RequestBody PagingReqDTO<RepoReqDTO> reqDTO) {
// 调用服务层方法进行分页查询并转换结果
IPage<RepoRespDTO> page = baseService.paging(reqDTO);
// 返回包含分页结果的操作成功响应
return super.success(page);
}
/**
*
* @param reqDTO
* @return ApiRest
*/
@RequiresRoles("sa")
@ApiOperation(value = "批量操作", notes = "批量加入或从题库移除")
@RequestMapping(value = "/batch-action", method = { RequestMethod.POST})
public ApiRest batchAction(@RequestBody QuRepoBatchReqDTO reqDTO) {
// 调用服务层方法进行批量操作
quRepoService.batchAction(reqDTO);
// 返回操作成功的响应
return super.success();
}
}

@ -0,0 +1,67 @@
package com.yf.exam.modules.repo.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* <p>
* Serializable
* </p>
*
* @author
* @since 2020-05-25 13:23
*/
@Data
@ApiModel(value="题库", description="题库")
public class RepoDTO implements Serializable {
// 序列化版本号,确保序列化和反序列化的兼容性
private static final long serialVersionUID = 1L;
/**
* ID
*
*/
@ApiModelProperty(value = "题库ID", required=true)
private String id;
/**
*
*
*/
@ApiModelProperty(value = "题库编号", required=true)
private String code;
/**
*
*
*/
@ApiModelProperty(value = "题库名称", required=true)
private String title;
/**
*
*
*/
@ApiModelProperty(value = "题库备注", required=true)
private String remark;
/**
*
*
*/
@ApiModelProperty(value = "创建时间", required=true)
private Date createTime;
/**
*
*
*/
@ApiModelProperty(value = "更新时间", required=true)
private Date updateTime;
}

@ -0,0 +1,40 @@
package com.yf.exam.modules.repo.dto.request;
import com.yf.exam.modules.repo.dto.RepoDTO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
/**
* <p>
* RepoDTO
* </p>
*
* @author
* @since 2020-05-25 13:23
*/
@Data
@ApiModel(value="题库分页请求类", description="题库分页请求类")
public class RepoReqDTO extends RepoDTO {
// 序列化版本号,确保序列化和反序列化的兼容性
private static final long serialVersionUID = 1L;
/**
* ID ID
*
*/
@ApiModelProperty(value = "排除题库ID", required=true)
private List<String> excludes;
/**
*
*
*
*/
@ApiModelProperty(value = "单选题数量", required=true)
private String title;
}

@ -0,0 +1,45 @@
package com.yf.exam.modules.repo.dto.response;
import com.yf.exam.modules.repo.dto.RepoDTO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* <p>
*
* RepoDTO
* </p>
*
* @author
* @since 2020-05-25 13:23
*/
@Data
@ApiModel(value="题库分页响应类", description="题库分页响应类")
public class RepoRespDTO extends RepoDTO {
// 序列化版本号,用于在序列化和反序列化过程中确保版本的兼容性
private static final long serialVersionUID = 1L;
/**
*
*
*/
@ApiModelProperty(value = "多选题数量", required=true)
private Integer multiCount;
/**
*
*
*/
@ApiModelProperty(value = "单选题数量", required=true)
private Integer radioCount;
/**
*
*
*/
@ApiModelProperty(value = "判断题数量", required=true)
private Integer judgeCount;
}

@ -0,0 +1,62 @@
package com.yf.exam.modules.repo.entity;
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.activerecord.Model;
import lombok.Data;
import java.util.Date;
/**
* <p>
* `el_repo`
* MyBatis-Plus Model 使 ActiveRecord
* </p>
*
* @author
* @since 2020-05-25 13:23
*/
@Data
// 表明该实体类对应数据库中的 el_repo 表
@TableName("el_repo")
public class Repo extends Model<Repo> {
// 序列化版本号,确保序列化和反序列化的兼容性
private static final long serialVersionUID = 1L;
/**
* ID使 MyBatis-Plus ASSIGN_ID ID
*/
@TableId(value = "id", type = IdType.ASSIGN_ID)
private String id;
/**
*
*/
private String code;
/**
*
*/
private String title;
/**
*
*/
private String remark;
/**
* create_time
*/
@TableField("create_time")
private Date createTime;
/**
* update_time
*/
@TableField("update_time")
private Date updateTime;
}

@ -0,0 +1,31 @@
package com.yf.exam.modules.repo.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yf.exam.modules.repo.dto.request.RepoReqDTO;
import com.yf.exam.modules.repo.dto.response.RepoRespDTO;
import com.yf.exam.modules.repo.entity.Repo;
import org.apache.ibatis.annotations.Param;
/**
* <p>
* Mapper MyBatis-Plus BaseMapper
* BaseMapper
* </p>
*
* @author
* @since 2020-05-25 13:23
*/
public interface RepoMapper extends BaseMapper<Repo> {
/**
*
*
* @param page
* @param query ID
* @return RepoRespDTO RepoRespDTO
*/
IPage<RepoRespDTO> paging(Page page, @Param("query") RepoReqDTO query);
}

@ -0,0 +1,38 @@
package com.yf.exam.modules.repo.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yf.exam.core.api.dto.PagingReqDTO;
import com.yf.exam.modules.repo.dto.RepoDTO;
import com.yf.exam.modules.repo.dto.request.RepoReqDTO;
import com.yf.exam.modules.repo.dto.response.RepoRespDTO;
import com.yf.exam.modules.repo.entity.Repo;
/**
* <p>
*
* MyBatis-Plus IService 使 CRUD
* </p>
*
* @author
* @since 2020-05-25 13:23
*/
public interface RepoService extends IService<Repo> {
/**
*
*
* @param reqDTO
* PagingReqDTO RepoReqDTO ID
* @return IPage RepoRespDTO
*/
IPage<RepoRespDTO> paging(PagingReqDTO<RepoReqDTO> reqDTO);
/**
*
*
* @param reqDTO ID
* DTO ID ID
*/
void save(RepoDTO reqDTO);
}

@ -0,0 +1,53 @@
package com.yf.exam.modules.repo.service.impl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yf.exam.core.api.dto.PagingReqDTO;
import com.yf.exam.core.utils.BeanMapper;
import com.yf.exam.modules.repo.dto.RepoDTO;
import com.yf.exam.modules.repo.dto.request.RepoReqDTO;
import com.yf.exam.modules.repo.dto.response.RepoRespDTO;
import com.yf.exam.modules.repo.entity.Repo;
import com.yf.exam.modules.repo.mapper.RepoMapper;
import com.yf.exam.modules.repo.service.RepoService;
import org.springframework.stereotype.Service;
/**
* <p>
* RepoService MyBatis-Plus ServiceImpl
*
* </p>
*
* @author
* @since 2020-05-25 13:23
*/
@Service
public class RepoServiceImpl extends ServiceImpl<RepoMapper, Repo> implements RepoService {
/**
*
*
* @param reqDTO
* @return IPage RepoRespDTO
*/
@Override
public IPage<RepoRespDTO> paging(PagingReqDTO<RepoReqDTO> reqDTO) {
// 调用 RepoMapper 的 paging 方法进行分页查询,传入分页对象和查询参数
return baseMapper.paging(reqDTO.toPage(), reqDTO.getParams());
}
/**
*
*
* @param reqDTO
*/
@Override
public void save(RepoDTO reqDTO) {
// 复制 DTO 对象的属性到实体对象
Repo entity = new Repo();
BeanMapper.copy(reqDTO, entity);
// 调用 MyBatis-Plus 的 saveOrUpdate 方法保存或更新实体对象
this.saveOrUpdate(entity);
}
}

@ -0,0 +1,92 @@
package com.yf.exam.modules.sys.config.controller;
// 导入 API 响应结果类,用于封装接口返回数据
import com.yf.exam.core.api.ApiRest;
// 导入基础控制器类,提供一些通用的控制器方法
import com.yf.exam.core.api.controller.BaseController;
// 导入基础 ID 响应数据传输对象类
import com.yf.exam.core.api.dto.BaseIdRespDTO;
// 导入 Bean 映射工具类,用于对象属性的复制
import com.yf.exam.core.utils.BeanMapper;
// 导入图片校验工具类
import com.yf.exam.modules.qu.utils.ImageCheckUtils;
// 导入系统配置数据传输对象类
import com.yf.exam.modules.sys.config.dto.SysConfigDTO;
// 导入系统配置实体类
import com.yf.exam.modules.sys.config.entity.SysConfig;
// 导入系统配置服务接口
import com.yf.exam.modules.sys.config.service.SysConfigService;
// 导入 Swagger 注解,用于生成 API 文档,标记控制器的标签
import io.swagger.annotations.Api;
// 导入 Swagger 注解,用于生成 API 文档,标记接口的操作描述
import io.swagger.annotations.ApiOperation;
// 导入 Shiro 注解,用于权限控制,要求用户具有指定角色才能访问接口
import org.apache.shiro.authz.annotation.RequiresRoles;
// 导入 Spring 依赖注入注解
import org.springframework.beans.factory.annotation.Autowired;
// 导入 Spring 请求体注解,用于将请求体中的数据绑定到方法参数上
import org.springframework.web.bind.annotation.RequestBody;
// 导入 Spring 请求映射注解,用于映射请求路径
import org.springframework.web.bind.annotation.RequestMapping;
// 导入 Spring 请求方法注解,用于指定请求的 HTTP 方法
import org.springframework.web.bind.annotation.RequestMethod;
// 导入 Spring 控制器注解,标记该类为 RESTful 风格的控制器
import org.springframework.web.bind.annotation.RestController;
/**
* <p>
*
* </p>
*
* @author
* @since 2020-04-17 09:12
*/
@Api(tags={"通用配置"})
@RestController
@RequestMapping("/exam/api/sys/config")
public class SysConfigController extends BaseController {
// 自动注入系统配置服务实例,用于调用系统配置相关的业务逻辑
@Autowired
private SysConfigService baseService;
// 自动注入图片校验工具实例,用于校验图片地址
@Autowired
private ImageCheckUtils imageCheckUtils;
/**
*
* @param reqDTO
* @return ID API
*/
@RequiresRoles("sa")
@ApiOperation(value = "添加或修改")
@RequestMapping(value = "/save", method = { RequestMethod.POST})
public ApiRest<BaseIdRespDTO> save(@RequestBody SysConfigDTO reqDTO) {
// 复制请求数据传输对象中的属性到系统配置实体对象中
SysConfig entity = new SysConfig();
BeanMapper.copy(reqDTO, entity);
// 调用图片校验工具类的方法,校验系统配置中的背景 LOGO 地址是否合法
imageCheckUtils.checkImage(entity.getBackLogo(), "系统LOGO地址错误");
// 调用系统配置服务的方法,保存或更新系统配置信息
baseService.saveOrUpdate(entity);
// 调用父类的成功响应方法,返回包含配置 ID 的响应对象
return super.success(new BaseIdRespDTO(entity.getId()));
}
/**
*
* @return API
*/
@ApiOperation(value = "查找详情")
@RequestMapping(value = "/detail", method = { RequestMethod.POST})
public ApiRest<SysConfigDTO> find() {
// 调用系统配置服务的方法,获取系统配置详情数据传输对象
SysConfigDTO dto = baseService.find();
// 调用父类的成功响应方法,返回包含系统配置详情的响应对象
return super.success(dto);
}
}

@ -0,0 +1,61 @@
// 定义包名,表明该类所属的模块和功能目录
package com.yf.exam.modules.sys.config.dto;
// 导入 Swagger 注解,用于生成 API 文档,标记类或属性的描述信息
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
// 导入 Lombok 的 Data 注解,自动生成 getter、setter、toString 等方法
import lombok.Data;
// 导入 Serializable 接口,表明该类的对象可以被序列化
import java.io.Serializable;
/**
* <p>
*
* </p>
*
* @author
* @since 2020-04-17 09:12
*/
// 自动生成 getter、setter、equals、hashCode 和 toString 方法
@Data
// 为 Swagger 文档提供类的描述信息
@ApiModel(value="通用配置", description="通用配置")
public class SysConfigDTO implements Serializable {
// 序列化版本号,用于在反序列化时验证版本一致性
private static final long serialVersionUID = 1L;
/**
*
*
*/
@ApiModelProperty(value = "ID", required=true)
private String id;
/**
*
*/
@ApiModelProperty(value = "系统名称")
private String siteName;
/**
* LOGO
*/
@ApiModelProperty(value = "前端LOGO")
private String frontLogo;
/**
* LOGO
*/
@ApiModelProperty(value = "后台LOGO")
private String backLogo;
/**
*
*/
@ApiModelProperty(value = "版权信息")
private String copyRight;
}

@ -0,0 +1,53 @@
package com.yf.exam.modules.sys.config.entity;
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.activerecord.Model;
import lombok.Data;
/**
* <p>
*
* </p>
*
* @author
* @since 2020-04-17 09:12
*/
@Data
@TableName("sys_config")
public class SysConfig extends Model<SysConfig> {
private static final long serialVersionUID = 1L;
/**
* ID
*/
@TableId(value = "id", type = IdType.ASSIGN_ID)
private String id;
/**
*
*/
@TableField("site_name")
private String siteName;
/**
* LOGO
*/
@TableField("front_logo")
private String frontLogo;
/**
* LOGO
*/
@TableField("back_logo")
private String backLogo;
/**
*
*/
@TableField("copy_right")
private String copyRight;
}

@ -0,0 +1,25 @@
/**
*
*/
package com.yf.exam.modules.sys.config.mapper;
/**
* MyBatis-Plus BaseMapper
*/
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
*
*/
import com.yf.exam.modules.sys.config.entity.SysConfig;
/**
* <p>
* Mapper BaseMapper 使 CRUD
* </p>
*
* @author
* @since 2020-04-17 09:12
*/
public interface SysConfigMapper extends BaseMapper<SysConfig> {
// 该接口目前未自定义额外的数据库操作方法,可根据需求在此添加
}

@ -0,0 +1,34 @@
/**
*
*/
package com.yf.exam.modules.sys.config.service;
/**
* MyBatis-Plus
*/
import com.baomidou.mybatisplus.extension.service.IService;
/**
*
*/
import com.yf.exam.modules.sys.config.dto.SysConfigDTO;
/**
*
*/
import com.yf.exam.modules.sys.config.entity.SysConfig;
/**
* <p>
* IService 使
* </p>
*
* @author
* @since 2020-04-17 09:12
*/
public interface SysConfigService extends IService<SysConfig> {
/**
* SysConfigDTO
* @return
*/
SysConfigDTO find();
}

@ -0,0 +1,54 @@
// 定义当前类所在的包
package com.yf.exam.modules.sys.config.service.impl;
// 导入 MyBatis-Plus 框架的查询条件构造器类
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
// 导入 MyBatis-Plus 框架的服务实现基类
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
// 导入自定义的 Bean 映射工具类,用于对象属性的复制
import com.yf.exam.core.utils.BeanMapper;
// 导入系统配置数据传输对象类
import com.yf.exam.modules.sys.config.dto.SysConfigDTO;
// 导入系统配置实体类
import com.yf.exam.modules.sys.config.entity.SysConfig;
// 导入系统配置映射器接口
import com.yf.exam.modules.sys.config.mapper.SysConfigMapper;
// 导入系统配置服务接口
import com.yf.exam.modules.sys.config.service.SysConfigService;
// 导入 Spring 框架的服务注解,将该类标记为服务层组件
import org.springframework.stereotype.Service;
/**
* <p>
*
* </p>
*
* @author
* @since 2020-04-17 09:12
*/
// 将该类标记为 Spring 服务组件,使其可以被 Spring 容器管理
@Service
public class SysConfigServiceImpl extends ServiceImpl<SysConfigMapper, SysConfig> implements SysConfigService {
/**
* DTO
*
* @return
*/
@Override
public SysConfigDTO find() {
// 创建一个查询条件构造器,用于构建对 SysConfig 实体的查询条件
QueryWrapper<SysConfig> wrapper = new QueryWrapper<>();
// 在查询语句末尾添加 "LIMIT 1",表示只查询一条记录
wrapper.last(" LIMIT 1");
// 调用父类的 getOne 方法,根据查询条件获取一个 SysConfig 实体对象false 表示不严格校验是否只返回一条记录
SysConfig entity = this.getOne(wrapper, false);
// 创建一个新的系统配置数据传输对象
SysConfigDTO dto = new SysConfigDTO();
// 使用 BeanMapper 工具类将 SysConfig 实体对象的属性复制到 SysConfigDTO 对象中
BeanMapper.copy(entity, dto);
// 返回填充好数据的系统配置数据传输对象
return dto;
}
}

@ -0,0 +1,186 @@
// 定义当前类所在的包,表明该类属于系统部门控制器模块
package com.yf.exam.modules.sys.depart.controller;
// 导入 MyBatis-Plus 的查询条件构造器,用于构建数据库查询条件
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
// 导入 MyBatis-Plus 的分页元数据接口,用于处理分页查询结果
import com.baomidou.mybatisplus.core.metadata.IPage;
// 导入自定义的 API 统一响应类,用于封装接口返回数据
import com.yf.exam.core.api.ApiRest;
// 导入自定义的基础控制器类,提供一些通用的控制器方法
import com.yf.exam.core.api.controller.BaseController;
// 导入自定义的基础单个 ID 请求数据传输对象类
import com.yf.exam.core.api.dto.BaseIdReqDTO;
// 导入自定义的基础多个 ID 请求数据传输对象类
import com.yf.exam.core.api.dto.BaseIdsReqDTO;
// 导入自定义的分页请求数据传输对象类
import com.yf.exam.core.api.dto.PagingReqDTO;
// 导入自定义的 Bean 映射工具类,用于对象属性的复制
import com.yf.exam.core.utils.BeanMapper;
// 导入系统部门数据传输对象类,用于在不同层之间传输部门信息
import com.yf.exam.modules.sys.depart.dto.SysDepartDTO;
// 导入部门排序请求数据传输对象类,用于接收部门排序的请求信息
import com.yf.exam.modules.sys.depart.dto.request.DepartSortReqDTO;
// 导入系统部门树状数据传输对象类,用于返回部门的树状结构信息
import com.yf.exam.modules.sys.depart.dto.response.SysDepartTreeDTO;
// 导入系统部门实体类,对应数据库中的部门表
import com.yf.exam.modules.sys.depart.entity.SysDepart;
// 导入系统部门服务接口,用于调用部门相关的业务逻辑
import com.yf.exam.modules.sys.depart.service.SysDepartService;
// 导入 Swagger 注解,用于生成 API 文档,标记控制器的标签
import io.swagger.annotations.Api;
// 导入 Swagger 注解,用于生成 API 文档,标记接口的操作描述
import io.swagger.annotations.ApiOperation;
// 导入 Shiro 注解,用于权限控制,要求用户具有指定角色才能访问接口
import org.apache.shiro.authz.annotation.RequiresRoles;
// 导入 Spring 框架的依赖注入注解,用于自动注入依赖的 Bean
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
// 导入 Spring 框架的请求体注解,用于将请求体中的数据绑定到方法参数上
import org.springframework.web.bind.annotation.RequestBody;
// 导入 Spring 框架的请求映射注解,用于映射请求路径
import org.springframework.web.bind.annotation.RequestMapping;
// 导入 Spring 框架的请求方法注解,用于指定请求的 HTTP 方法
import org.springframework.web.bind.annotation.RequestMethod;
// 导入 Spring 框架的控制器注解,标记该类为 RESTful 风格的控制器
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* <p>
*
* </p>
*
* @author
* @since 2020-09-02 17:25
*/
// 为 Swagger 文档标记该控制器的标签为 "部门信息"
@Api(tags={"部门信息"})
// 标记该类为 RESTful 风格的控制器,返回数据直接作为 HTTP 响应体
@RestController
// 定义该控制器处理的请求路径前缀
@RequestMapping("/exam/api/sys/depart")
public class SysDepartController extends BaseController {
// 自动注入系统部门服务实例,用于调用部门相关的业务逻辑
@Autowired
private SysDepartService baseService;
/**
*
* @param reqDTO
* @return API
*/
// 要求用户具有 "sa" 角色才能访问该接口
@RequiresRoles("sa")
// 为 Swagger 文档标记该接口的操作描述为 "添加或修改"
@ApiOperation(value = "添加或修改")
// 映射请求路径为 "/save",并指定请求方法为 POST
@RequestMapping(value = "/save", method = { RequestMethod.POST})
public ApiRest save(@RequestBody SysDepartDTO reqDTO) {
// 调用部门服务的保存方法,保存或更新部门信息
baseService.save(reqDTO);
// 调用父类的成功响应方法,返回操作成功的响应对象
return super.success();
}
/**
*
* @param reqDTO ID
* @return API
*/
@RequiresRoles("sa")
@ApiOperation(value = "批量删除")
@RequestMapping(value = "/delete", method = { RequestMethod.POST})
public ApiRest edit(@RequestBody BaseIdsReqDTO reqDTO) {
// 根据传入的部门 ID 列表,调用部门服务的删除方法删除部门信息
baseService.removeByIds(reqDTO.getIds());
return super.success();
}
/**
*
* @param reqDTO ID
* @return API
*/
@RequiresRoles("sa")
@ApiOperation(value = "查找详情")
@RequestMapping(value = "/detail", method = { RequestMethod.POST})
public ApiRest<SysDepartDTO> find(@RequestBody BaseIdReqDTO reqDTO) {
// 根据传入的部门 ID调用部门服务的获取方法获取部门实体对象
SysDepart entity = baseService.getById(reqDTO.getId());
// 创建一个新的部门数据传输对象
SysDepartDTO dto = new SysDepartDTO();
// 将部门实体对象的属性复制到部门数据传输对象中
BeanUtils.copyProperties(entity, dto);
return super.success(dto);
}
/**
*
* @param reqDTO
* @return API
*/
@RequiresRoles("sa")
@ApiOperation(value = "分页查找")
@RequestMapping(value = "/paging", method = { RequestMethod.POST})
public ApiRest<IPage<SysDepartTreeDTO>> paging(@RequestBody PagingReqDTO<SysDepartDTO> reqDTO) {
// 调用部门服务的分页查询方法,进行分页查询并将结果转换为树状数据传输对象
IPage<SysDepartTreeDTO> page = baseService.paging(reqDTO);
return super.success(page);
}
/**
* 200
* @param reqDTO
* @return API
*/
@RequiresRoles("sa")
@ApiOperation(value = "查找列表")
@RequestMapping(value = "/list", method = { RequestMethod.POST})
public ApiRest<List<SysDepartDTO>> list(@RequestBody SysDepartDTO reqDTO) {
// 创建一个 MyBatis-Plus 的查询条件构造器
QueryWrapper<SysDepart> wrapper = new QueryWrapper<>();
// 调用部门服务的列表查询方法,根据查询条件获取部门实体列表
List<SysDepart> list = baseService.list(wrapper);
// 使用 Bean 映射工具类将部门实体列表转换为部门数据传输对象列表
List<SysDepartDTO> dtoList = BeanMapper.mapList(list, SysDepartDTO.class);
return super.success(dtoList);
}
/**
*
* @return API
*/
@RequiresRoles("sa")
@ApiOperation(value = "树列表")
@RequestMapping(value = "/tree", method = { RequestMethod.POST})
public ApiRest<List<SysDepartTreeDTO>> tree() {
// 调用部门服务的查找树列表方法,获取部门树列表数据传输对象列表
List<SysDepartTreeDTO> dtoList = baseService.findTree();
return super.success(dtoList);
}
/**
*
* @param reqDTO ID
* @return API
*/
@RequiresRoles("sa")
@ApiOperation(value = "分类排序")
@RequestMapping(value = "/sort", method = { RequestMethod.POST})
public ApiRest sort(@RequestBody DepartSortReqDTO reqDTO) {
// 调用部门服务的排序方法,根据传入的部门 ID 和排序值对部门进行排序
baseService.sort(reqDTO.getId(), reqDTO.getSort());
return super.success();
}
}

@ -0,0 +1,73 @@
// 定义该类所在的包,表明其在项目模块中的位置
package com.yf.exam.modules.sys.depart.dto;
// 导入 Swagger 注解,用于在生成 API 文档时描述类的信息
import io.swagger.annotations.ApiModel;
// 导入 Swagger 注解,用于在生成 API 文档时描述类属性的信息
import io.swagger.annotations.ApiModelProperty;
// 导入 Lombok 的 Data 注解,自动生成 getter、setter、equals、hashCode 和 toString 方法
import lombok.Data;
// 导入 Serializable 接口,使该类的对象可以被序列化和反序列化
import java.io.Serializable;
/**
* <p>
* 访
* </p>
*
* @author
* @since 2020-09-02 17:25
*/
// 使用 Lombok 的 Data 注解,自动生成常用方法
@Data
// 使用 Swagger 的 ApiModel 注解,为 API 文档描述该类的信息
@ApiModel(value="部门信息", description="部门信息")
public class SysDepartDTO implements Serializable {
// 序列化版本号,确保序列化和反序列化时类的版本一致性
private static final long serialVersionUID = 1L;
/**
* ID
* API
*/
@ApiModelProperty(value = "ID", required=true)
private String id;
/**
* 1 2
* API
*/
@ApiModelProperty(value = "1公司2部门", required=true)
private Integer deptType;
/**
* ID
* API
*/
@ApiModelProperty(value = "所属上级", required=true)
private String parentId;
/**
*
* API
*/
@ApiModelProperty(value = "部门名称", required=true)
private String deptName;
/**
*
* API
*/
@ApiModelProperty(value = "部门编码", required=true)
private String deptCode;
/**
*
* API
*/
@ApiModelProperty(value = "排序", required=true)
private Integer sort;
}

@ -0,0 +1,42 @@
// 声明该类所在的包,表明其在项目模块中的位置
package com.yf.exam.modules.sys.depart.dto.request;
// 导入 Swagger 注解,用于生成 API 文档,标记类的描述信息
import io.swagger.annotations.ApiModel;
// 导入 Swagger 注解,用于生成 API 文档,标记类属性的描述信息
import io.swagger.annotations.ApiModelProperty;
// 导入 Lombok 的 Data 注解,自动生成 getter、setter、toString 等方法
import lombok.Data;
// 导入 Serializable 接口,表明该类的对象可以被序列化
import java.io.Serializable;
/**
* <p>
*
* </p>
*
* @author
* @since 2020-03-14 10:37
*/
// 自动生成 getter、setter、equals、hashCode 和 toString 方法
@Data
// 为 Swagger 文档提供类的描述信息
@ApiModel(value="部门排序请求类", description="部门排序请求类")
public class DepartSortReqDTO implements Serializable {
// 序列化版本号,用于在反序列化时验证版本一致性
private static final long serialVersionUID = 1L;
/**
* ID
*/
@ApiModelProperty(value = "分类ID")
private String id;
/**
* 0 1
*/
@ApiModelProperty(value = "排序0下降1上升")
private Integer sort;
}

@ -0,0 +1,41 @@
// 定义包名,表明该类所属的模块和功能目录
package com.yf.exam.modules.sys.depart.dto.response;
// 导入 SysDepartDTO 类,该类是当前类的父类,包含部门的基本信息
import com.yf.exam.modules.sys.depart.dto.SysDepartDTO;
// 导入 Swagger 注解,用于生成 API 文档,标记类的描述信息
import io.swagger.annotations.ApiModel;
// 导入 Swagger 注解,用于生成 API 文档,标记类属性的描述信息
import io.swagger.annotations.ApiModelProperty;
// 导入 Lombok 的 Data 注解,自动生成 getter、setter、toString 等方法
import lombok.Data;
// 导入 List 接口,用于存储子部门列表
import java.util.List;
/**
* <p>
* SysDepartDTO
* </p>
*
* @author
* @since 2020-09-02 17:25
*/
// 自动生成 getter、setter、equals、hashCode 和 toString 方法
@Data
// 为 Swagger 文档提供类的描述信息
@ApiModel(value="部门树结构响应类", description="部门树结构响应类")
public class SysDepartTreeDTO extends SysDepartDTO {
// 序列化版本号,用于在反序列化时验证版本一致性
private static final long serialVersionUID = 1L;
/**
*
* API
*/
@ApiModelProperty(value = "子列表", required=true)
private List<SysDepartTreeDTO> children;
}

@ -0,0 +1,59 @@
package com.yf.exam.modules.sys.depart.entity;
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.activerecord.Model;
import lombok.Data;
/**
* <p>
*
* </p>
*
* @author
* @since 2020-09-02 17:25
*/
@Data
@TableName("sys_depart")
public class SysDepart extends Model<SysDepart> {
private static final long serialVersionUID = 1L;
/**
* ID
*/
@TableId(value = "id", type = IdType.ASSIGN_ID)
private String id;
/**
* 12
*/
@TableField("dept_type")
private Integer deptType;
/**
*
*/
@TableField("parent_id")
private String parentId;
/**
*
*/
@TableField("dept_name")
private String deptName;
/**
*
*/
@TableField("dept_code")
private String deptCode;
/**
*
*/
private Integer sort;
}

@ -0,0 +1,28 @@
package com.yf.exam.modules.sys.depart.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yf.exam.modules.sys.depart.dto.SysDepartDTO;
import com.yf.exam.modules.sys.depart.dto.response.SysDepartTreeDTO;
import com.yf.exam.modules.sys.depart.entity.SysDepart;
import org.apache.ibatis.annotations.Param;
/**
* <p>
* Mapper
* </p>
*
* @author
* @since 2020-09-02 17:25
*/
public interface SysDepartMapper extends BaseMapper<SysDepart> {
/**
*
* @param page
* @param query
* @return
*/
IPage<SysDepartTreeDTO> paging(Page page, @Param("query") SysDepartDTO query);
}

@ -0,0 +1,62 @@
package com.yf.exam.modules.sys.depart.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yf.exam.core.api.dto.PagingReqDTO;
import com.yf.exam.modules.sys.depart.dto.SysDepartDTO;
import com.yf.exam.modules.sys.depart.dto.response.SysDepartTreeDTO;
import com.yf.exam.modules.sys.depart.entity.SysDepart;
import java.util.List;
/**
* <p>
*
* </p>
*
* @author
* @since 2020-09-02 17:25
*/
public interface SysDepartService extends IService<SysDepart> {
/**
*
* @param reqDTO
*/
void save(SysDepartDTO reqDTO);
/**
*
* @param reqDTO
* @return
*/
IPage<SysDepartTreeDTO> paging(PagingReqDTO<SysDepartDTO> reqDTO);
/**
*
* @return
*/
List<SysDepartTreeDTO> findTree();
/**
*
* @param ids
* @return
*/
List<SysDepartTreeDTO> findTree(List<String> ids);
/**
*
* @param id
* @param sort
*/
void sort(String id, Integer sort);
/**
* IDID
* @param id
* @return
*/
List<String> listAllSubIds( String id);
}

@ -0,0 +1,368 @@
package com.yf.exam.modules.sys.depart.service.impl;
// 导入 MyBatis-Plus 的查询条件构造器,用于构建数据库查询条件
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
// 导入 MyBatis-Plus 的分页元数据接口,用于处理分页查询结果
import com.baomidou.mybatisplus.core.metadata.IPage;
// 导入 MyBatis-Plus 的分页类,用于创建分页对象
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
// 导入 MyBatis-Plus 的服务实现基类,提供通用的服务层方法
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
// 导入自定义的分页请求数据传输对象类,用于封装分页查询请求参数
import com.yf.exam.core.api.dto.PagingReqDTO;
// 导入自定义的 Bean 映射工具类,用于对象属性的复制
import com.yf.exam.core.utils.BeanMapper;
// 导入系统部门数据传输对象类,用于在不同层之间传输部门信息
import com.yf.exam.modules.sys.depart.dto.SysDepartDTO;
// 导入系统部门树状数据传输对象类,用于返回部门的树状结构信息
import com.yf.exam.modules.sys.depart.dto.response.SysDepartTreeDTO;
// 导入系统部门实体类,对应数据库中的部门表
import com.yf.exam.modules.sys.depart.entity.SysDepart;
// 导入系统部门映射器接口,用于与数据库进行交互
import com.yf.exam.modules.sys.depart.mapper.SysDepartMapper;
// 导入系统部门服务接口,定义部门相关的业务方法
import com.yf.exam.modules.sys.depart.service.SysDepartService;
// 导入 Apache Commons Lang3 工具类,用于字符串操作
import org.apache.commons.lang3.StringUtils;
// 导入 Spring 工具类,用于集合操作
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* <p>
*
* </p>
*
* @author
* @since 2020-09-02 17:25
*/
// 将该类标记为 Spring 服务组件,使其可以被 Spring 容器管理
@Service
public class SysDepartServiceImpl extends ServiceImpl<SysDepartMapper, SysDepart> implements SysDepartService {
/**
* 0
*/
private static final String ROOT_TAG = "0";
/**
*
* ID
* @param reqDTO
*/
@Override
public void save(SysDepartDTO reqDTO) {
// 判断传入的部门 ID 是否为空
if(StringUtils.isBlank(reqDTO.getId())) {
// 若为空,则填充部门编码
this.fillCode(reqDTO);
} else {
// 若不为空,则清空排序和部门编码信息
reqDTO.setSort(null);
reqDTO.setDeptCode(null);
}
// 创建一个新的部门实体对象
SysDepart entity = new SysDepart();
// 使用 BeanMapper 工具类将部门数据传输对象的属性复制到部门实体对象中
BeanMapper.copy(reqDTO, entity);
// 调用 MyBatis-Plus 的保存或更新方法,保存或更新部门信息
this.saveOrUpdate(entity);
}
/**
*
* @param reqDTO
* @return
*/
@Override
public IPage<SysDepartTreeDTO> paging(PagingReqDTO<SysDepartDTO> reqDTO) {
// 创建分页对象,传入当前页码和每页记录数
Page query = new Page(reqDTO.getCurrent(), reqDTO.getSize());
// 获取请求参数中的部门信息
SysDepartDTO params = reqDTO.getParams();
// 调用映射器的分页查询方法,将结果转换为部门树状结构数据传输对象列表
IPage<SysDepartTreeDTO> pageData = baseMapper.paging(query, params);
return pageData;
}
/**
* ID
* @return
*/
@Override
public List<SysDepartTreeDTO> findTree() {
return this.findTree(null);
}
/**
* ID
* @param ids ID
* @return
*/
@Override
public List<SysDepartTreeDTO> findTree(List<String> ids) {
// 创建查询条件构造器
QueryWrapper<SysDepart> wrapper = new QueryWrapper();
// 按部门排序字段升序排序
wrapper.lambda().orderByAsc(SysDepart::getSort);
// 判断传入的部门 ID 列表是否不为空
if(!CollectionUtils.isEmpty(ids)) {
// 存储所有相关部门 ID 的列表
List<String> fullIds = new ArrayList<>();
// 遍历传入的部门 ID 列表
for(String id: ids) {
// 递归获取该部门及其所有父部门的 ID 并添加到 fullIds 列表中
this.cycleAllParent(fullIds, id);
}
// 判断 fullIds 列表是否不为空
if(!CollectionUtils.isEmpty(fullIds)) {
// 将查询条件限制为 fullIds 列表中的部门 ID
wrapper.lambda().in(SysDepart::getId, fullIds);
}
}
// 根据查询条件获取所有部门列表
List<SysDepart> list = this.list(wrapper);
// 使用 BeanMapper 工具类将部门实体列表转换为部门树状结构数据传输对象列表
List<SysDepartTreeDTO> dtoList = BeanMapper.mapList(list, SysDepartTreeDTO.class);
// 存储每个父部门 ID 对应的子部门列表的映射
Map<String,List<SysDepartTreeDTO>> map = new HashMap<>(16);
// 遍历部门树状结构数据传输对象列表
for(SysDepartTreeDTO item: dtoList) {
// 判断映射中是否已存在该父部门 ID
if(map.containsKey(item.getParentId())) {
// 若存在,则将该部门添加到对应的子部门列表中
map.get(item.getParentId()).add(item);
continue;
}
// 若不存在,则创建一个新的子部门列表,并将该部门添加到列表中
List<SysDepartTreeDTO> a = new ArrayList<>();
a.add(item);
// 将该父部门 ID 和对应的子部门列表添加到映射中
map.put(item.getParentId(), a);
}
// 获取根部门(父部门 ID 为 0的子部门列表
List<SysDepartTreeDTO> topList = map.get(ROOT_TAG);
// 判断根部门的子部门列表是否不为空
if(!CollectionUtils.isEmpty(topList)) {
// 遍历根部门的子部门列表
for(SysDepartTreeDTO item: topList) {
// 递归填充每个部门的子部门信息
this.fillChildren(map, item);
}
}
return topList;
}
/**
*
* @param id ID
* @param sort 0 1
*/
@Override
public void sort(String id, Integer sort) {
// 根据部门 ID 获取要排序的部门实体对象
SysDepart depart = this.getById(id);
// 用于交换排序的部门实体对象
SysDepart exchange = null;
// 创建查询条件构造器
QueryWrapper<SysDepart> wrapper = new QueryWrapper<>();
// 限制查询条件为与要排序的部门同级(父部门相同)
wrapper.lambda()
.eq(SysDepart::getParentId, depart.getParentId());
// 限制查询结果只返回一条记录
wrapper.last("LIMIT 1");
// 判断排序方式是否为上升
if(sort == 0) {
// 限制查询条件为排序值小于要排序部门的排序值,并按排序值降序排序
wrapper.lambda()
.lt(SysDepart::getSort, depart.getSort())
.orderByDesc(SysDepart::getSort);
// 获取满足条件的部门实体对象
exchange = this.getOne(wrapper, false);
}
// 判断排序方式是否为下降
if(sort == 1) {
// 限制查询条件为排序值大于要排序部门的排序值,并按排序值升序排序
wrapper.lambda()
.gt(SysDepart::getSort, depart.getSort())
.orderByAsc(SysDepart::getSort);
// 获取满足条件的部门实体对象
exchange = this.getOne(wrapper, false);
}
// 判断是否找到可交换排序的部门实体对象
if(exchange != null) {
// 创建一个新的部门实体对象,用于更新要排序的部门的排序值
SysDepart a = new SysDepart();
a.setId(id);
a.setSort(exchange.getSort());
// 创建一个新的部门实体对象,用于更新可交换排序的部门的排序值
SysDepart b = new SysDepart();
b.setId(exchange.getId());
b.setSort(depart.getSort());
// 调用 MyBatis-Plus 的更新方法,更新两个部门的排序值
this.updateById(a);
this.updateById(b);
}
}
/**
*
* @param reqDTO
*/
private void fillCode(SysDepartDTO reqDTO) {
// 部门编码的前缀
String code = "";
// 判断传入的父部门 ID 是否不为空且不为根部门 ID
if(StringUtils.isNotBlank(reqDTO.getParentId())
&& !ROOT_TAG.equals(reqDTO.getParentId())) {
// 根据父部门 ID 获取父部门实体对象
SysDepart parent = this.getById(reqDTO.getParentId());
// 将父部门的编码作为前缀
code = parent.getDeptCode();
}
// 创建查询条件构造器
QueryWrapper<SysDepart> wrapper = new QueryWrapper<>();
// 限制查询条件为与要填充编码的部门同级(父部门相同),并按排序值降序排序
wrapper.lambda()
.eq(SysDepart::getParentId, reqDTO.getParentId())
.orderByDesc(SysDepart::getSort);
// 限制查询结果只返回一条记录
wrapper.last("LIMIT 1");
// 获取满足条件的部门实体对象
SysDepart depart = this.getOne(wrapper, false);
// 判断是否找到同级部门实体对象
if(depart != null) {
// 生成新的部门编码,将同级部门的排序值加 1 并格式化后添加到前缀后面
code += this.formatCode(depart.getSort() + 1);
// 设置要填充编码的部门的排序值为同级部门的排序值加 1
reqDTO.setSort(depart.getSort() + 1);
} else {
// 若未找到同级部门实体对象,则生成新的部门编码,排序值为 1 并格式化后添加到前缀后面
code += this.formatCode(1);
// 设置要填充编码的部门的排序值为 1
reqDTO.setSort(1);
}
// 设置要填充编码的部门的部门编码
reqDTO.setDeptCode(code);
}
/**
*
* @param sort
* @return
*/
private String formatCode(Integer sort) {
// 判断排序值是否小于 10
if(sort < 10) {
// 若小于 10则在排序值前添加 "A0"
return "A0" + sort;
}
// 若不小于 10则在排序值前添加 "A"
return "A" + sort;
}
/**
*
* @param map ID
* @param item
*/
private void fillChildren(Map<String,List<SysDepartTreeDTO>> map, SysDepartTreeDTO item) {
// 判断映射中是否存在该部门 ID 对应的子部门列表
if(map.containsKey(item.getId())) {
// 获取该部门 ID 对应的子部门列表
List<SysDepartTreeDTO> children = map.get(item.getId());
// 判断子部门列表是否不为空
if(!CollectionUtils.isEmpty(children)) {
// 遍历子部门列表
for(SysDepartTreeDTO sub: children) {
// 递归填充每个子部门的子部门信息
this.fillChildren(map, sub);
}
}
// 设置该部门的子部门列表
item.setChildren(children);
}
}
/**
* ID
* @param id ID ID
* @return ID
*/
@Override
public List<String> listAllSubIds( String id) {
// 存储指定部门及其所有子部门 ID 的列表
List<String> ids = new ArrayList<>();
// 递归获取指定部门及其所有子部门的 ID 并添加到列表中
this.cycleAllSubs(ids, id);
return ids;
}
/**
* ID
* @param list ID
* @param id ID
*/
private void cycleAllSubs(List<String> list, String id) {
// 将当前部门 ID 添加到列表中
list.add(id);
// 创建查询条件构造器
QueryWrapper<SysDepart> wrapper = new QueryWrapper<>();
// 限制查询条件为父部门 ID 等于当前部门 ID并按排序值降序排序
wrapper.lambda()
.eq(SysDepart::getParentId, id)
.orderByDesc(SysDepart::getSort);
// 获取满足条件的子部门列表
List<SysDepart> subList = this.list(wrapper);
// 判断子部门列表是否不为空
if(!CollectionUtils.isEmpty(subList)) {
// 遍历子部门列表
for(SysDepart item: subList) {
// 递归处理每个子部门
this.cycleAllSubs(list, item.getId());
}
}
}
/**
* ID
* @param list ID
* @param id ID
*/
private void cycleAllParent(List<String> list, String id) {
// 将当前部门 ID 添加到列表中
list.add(id);
// 根据部门 ID 获取部门实体对象
SysDepart depart = this.getById(id);
// 判断该部门的父部门 ID 是否不为空且不为根部门 ID
if(StringUtils.isNotBlank(depart.getParentId())
&& !ROOT_TAG.equals(depart.getParentId())) {
// 递归处理该部门的父部门
this.cycleAllParent(list, depart.getParentId());
}
}
}

@ -0,0 +1,40 @@
/**
* mapper
*/
package com.yf.exam.modules.sys.system.mapper;
/**
* MyBatis Mapper MyBatis
*/
import org.apache.ibatis.annotations.Mapper;
/**
* MyBatis Param SQL 使
*/
import org.apache.ibatis.annotations.Param;
/**
* <p>
* Mapper
* </p>
*
* @author
* @since 2020-08-22 13:46
*/
// 标识该接口是 MyBatis 的映射器接口Spring 会自动扫描并将其注册到 MyBatis 中。
@Mapper
public interface SysDictMapper {
/**
*
*
* @param table
* @param text
* @param key
* @param value
* @return null
*/
String findDict(@Param("table") String table,
@Param("text") String text,
@Param("key") String key,
@Param("value") String value);
}

@ -0,0 +1,26 @@
/**
*
*/
package com.yf.exam.modules.sys.system.service;
/**
*
*
* @author bool
*/
public interface SysDictService {
/**
*
*
* @param table
* @param text
* @param key
* @param value 使
* @return null
*/
String findDict(String table,
String text,
String key,
String value);
}

@ -0,0 +1,45 @@
// 定义包名,指定该类所在的包路径
package com.yf.exam.modules.sys.system.service.impl;
// 导入系统字典映射器接口,用于与数据库进行交互
import com.yf.exam.modules.sys.system.mapper.SysDictMapper;
// 导入系统字典服务接口,定义了系统字典相关的业务方法
import com.yf.exam.modules.sys.system.service.SysDictService;
// 导入 Spring 框架的自动装配注解,用于依赖注入
import org.springframework.beans.factory.annotation.Autowired;
// 导入 Spring 框架的服务注解,将该类标记为服务层组件
import org.springframework.stereotype.Service;
/**
* SysDictServiceImpl SysDictService
* SysDictMapper
*
* @author bool
*/
// 将该类标记为 Spring 服务组件,使其可以被 Spring 容器管理
@Service
public class SysDictServiceImpl implements SysDictService {
/**
*
* Spring SysDictMapper
*/
@Autowired
private SysDictMapper sysDictMapper;
/**
*
* SysDictMapper findDict
*
* @param table
* @param text
* @param key
* @param value
* @return null
*/
@Override
public String findDict(String table, String text, String key, String value) {
// 调用 SysDictMapper 的 findDict 方法执行数据库查询
return sysDictMapper.findDict(table, text, key, value);
}
}

@ -0,0 +1,106 @@
// 声明当前类所在的包,表明该类属于系统用户控制器模块
package com.yf.exam.modules.sys.user.controller;
// 导入 MyBatis-Plus 的查询条件构造器,用于构建数据库查询条件
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
// 导入 MyBatis-Plus 的分页元数据接口,用于处理分页查询结果
import com.baomidou.mybatisplus.core.metadata.IPage;
// 导入自定义的 API 统一响应类,用于封装接口返回数据
import com.yf.exam.core.api.ApiRest;
// 导入自定义的基础控制器类,提供一些通用的控制器方法
import com.yf.exam.core.api.controller.BaseController;
// 导入自定义的分页请求数据传输对象类,用于封装分页查询请求参数
import com.yf.exam.core.api.dto.PagingReqDTO;
// 导入自定义的 Bean 映射工具类,用于对象属性的复制
import com.yf.exam.core.utils.BeanMapper;
// 导入系统角色数据传输对象类,用于在不同层之间传输角色信息
import com.yf.exam.modules.sys.user.dto.SysRoleDTO;
// 导入系统角色实体类,对应数据库中的角色表
import com.yf.exam.modules.sys.user.entity.SysRole;
// 导入系统角色服务接口,用于调用角色相关的业务逻辑
import com.yf.exam.modules.sys.user.service.SysRoleService;
// 导入 Swagger 注解,用于生成 API 文档,标记控制器的标签
import io.swagger.annotations.Api;
// 导入 Swagger 注解,用于生成 API 文档,标记接口的操作描述
import io.swagger.annotations.ApiOperation;
// 导入 Shiro 注解,用于权限控制,要求用户具有指定角色才能访问接口
import org.apache.shiro.authz.annotation.RequiresRoles;
// 导入 Spring 框架的依赖注入注解,用于自动注入依赖的 Bean
import org.springframework.beans.factory.annotation.Autowired;
// 导入 Spring 框架的请求体注解,用于将请求体中的数据绑定到方法参数上
import org.springframework.web.bind.annotation.RequestBody;
// 导入 Spring 框架的请求映射注解,用于映射请求路径
import org.springframework.web.bind.annotation.RequestMapping;
// 导入 Spring 框架的请求方法注解,用于指定请求的 HTTP 方法
import org.springframework.web.bind.annotation.RequestMethod;
// 导入 Spring 框架的控制器注解,标记该类为 RESTful 风格的控制器
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* <p>
*
* </p>
*
* @author
* @since 2020-04-13 16:57
*/
// 为 Swagger 文档标记该控制器的标签为 "管理用户"
@Api(tags = {"管理用户"})
// 标记该类为 RESTful 风格的控制器,返回数据直接作为 HTTP 响应体
@RestController
// 定义该控制器处理的请求路径前缀
@RequestMapping("/exam/api/sys/role")
public class SysRoleController extends BaseController {
// 自动注入系统角色服务实例,用于调用角色相关的业务逻辑
@Autowired
private SysRoleService baseService;
/**
*
*
* @param reqDTO
* @return API
*/
// 要求用户具有 "sa" 角色才能访问该接口
@RequiresRoles("sa")
// 为 Swagger 文档标记该接口的操作描述为 "分页查找"
@ApiOperation(value = "分页查找")
// 映射请求路径为 "/paging",并指定请求方法为 POST
@RequestMapping(value = "/paging", method = { RequestMethod.POST})
public ApiRest<IPage<SysRoleDTO>> paging(@RequestBody PagingReqDTO<SysRoleDTO> reqDTO) {
// 调用角色服务的分页查询方法,进行分页查询并将结果转换为角色数据传输对象
IPage<SysRoleDTO> page = baseService.paging(reqDTO);
// 调用父类的成功响应方法,返回包含分页结果的响应对象
return super.success(page);
}
/**
* 200
*
* @return API
*/
// 要求用户具有 "sa" 角色才能访问该接口
@RequiresRoles("sa")
// 为 Swagger 文档标记该接口的操作描述为 "查找列表"
@ApiOperation(value = "查找列表")
// 映射请求路径为 "/list",并指定请求方法为 POST
@RequestMapping(value = "/list", method = { RequestMethod.POST})
public ApiRest<List<SysRoleDTO>> list() {
// 创建一个 MyBatis-Plus 的查询条件构造器,用于构建查询条件
QueryWrapper<SysRole> wrapper = new QueryWrapper<>();
// 调用角色服务的列表查询方法,根据查询条件获取角色实体列表
List<SysRole> list = baseService.list(wrapper);
// 使用 Bean 映射工具类将角色实体列表转换为角色数据传输对象列表
List<SysRoleDTO> dtoList = BeanMapper.mapList(list, SysRoleDTO.class);
// 调用父类的成功响应方法,返回包含角色列表的响应对象
return super.success(dtoList);
}
}

@ -0,0 +1,245 @@
// 声明当前类所在的包,该包属于系统用户控制器模块
package com.yf.exam.modules.sys.user.controller;
// 导入 MyBatis-Plus 的查询条件构造器,用于构建数据库查询条件
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
// 导入 MyBatis-Plus 的分页元数据接口,用于处理分页查询结果
import com.baomidou.mybatisplus.core.metadata.IPage;
// 导入自定义的 API 统一响应类,用于封装接口返回数据
import com.yf.exam.core.api.ApiRest;
// 导入自定义的基础控制器类,提供一些通用的控制器方法
import com.yf.exam.core.api.controller.BaseController;
// 导入自定义的基础多个 ID 请求数据传输对象类,用于批量操作时传递 ID 列表
import com.yf.exam.core.api.dto.BaseIdsReqDTO;
// 导入自定义的基础状态请求数据传输对象类,用于传递状态修改请求
import com.yf.exam.core.api.dto.BaseStateReqDTO;
// 导入自定义的分页请求数据传输对象类,用于分页查询时传递请求参数
import com.yf.exam.core.api.dto.PagingReqDTO;
// 导入系统用户数据传输对象类,用于在不同层之间传输用户信息
import com.yf.exam.modules.sys.user.dto.SysUserDTO;
// 导入系统用户登录请求数据传输对象类,用于用户登录时传递请求信息
import com.yf.exam.modules.sys.user.dto.request.SysUserLoginReqDTO;
// 导入系统用户保存请求数据传输对象类,用于保存或修改用户信息时传递请求信息
import com.yf.exam.modules.sys.user.dto.request.SysUserSaveReqDTO;
// 导入系统用户登录响应数据传输对象类,用于用户登录成功后返回响应信息
import com.yf.exam.modules.sys.user.dto.response.SysUserLoginDTO;
// 导入系统用户实体类,对应数据库中的用户表
import com.yf.exam.modules.sys.user.entity.SysUser;
// 导入系统用户服务接口,用于调用用户相关的业务逻辑
import com.yf.exam.modules.sys.user.service.SysUserService;
// 导入 Swagger 注解,用于生成 API 文档,标记控制器的标签
import io.swagger.annotations.Api;
// 导入 Swagger 注解,用于生成 API 文档,标记接口的操作描述
import io.swagger.annotations.ApiOperation;
// 导入 Shiro 注解,用于权限控制,要求用户具有指定角色才能访问接口
import org.apache.shiro.authz.annotation.RequiresRoles;
// 导入 Spring 框架的依赖注入注解,用于自动注入依赖的 Bean
import org.springframework.beans.factory.annotation.Autowired;
// 导入 Spring 框架的跨域请求注解,允许跨域访问该接口
import org.springframework.web.bind.annotation.CrossOrigin;
// 导入 Spring 框架的请求体注解,用于将请求体中的数据绑定到方法参数上
import org.springframework.web.bind.annotation.RequestBody;
// 导入 Spring 框架的请求映射注解,用于映射请求路径
import org.springframework.web.bind.annotation.RequestMapping;
// 导入 Spring 框架的请求方法注解,用于指定请求的 HTTP 方法
import org.springframework.web.bind.annotation.RequestMethod;
// 导入 Spring 框架的请求参数注解,用于获取请求参数
import org.springframework.web.bind.annotation.RequestParam;
// 导入 Spring 框架的控制器注解,标记该类为 RESTful 风格的控制器
import org.springframework.web.bind.annotation.RestController;
// 导入 HttpServletRequest 类,用于获取 HTTP 请求信息
import javax.servlet.http.HttpServletRequest;
/**
* <p>
*
* </p>
*
* @author
* @since 2020-04-13 16:57
*/
// 为 Swagger 文档标记该控制器的标签为 "管理用户"
@Api(tags = {"管理用户"})
// 标记该类为 RESTful 风格的控制器,返回数据直接作为 HTTP 响应体
@RestController
// 定义该控制器处理的请求路径前缀
@RequestMapping("/exam/api/sys/user")
public class SysUserController extends BaseController {
// 自动注入系统用户服务实例,用于调用用户相关的业务逻辑
@Autowired
private SysUserService baseService;
/**
*
*
* @param reqDTO
* @return API
*/
@CrossOrigin
@ApiOperation(value = "用户登录")
@RequestMapping(value = "/login", method = {RequestMethod.POST})
public ApiRest<SysUserLoginDTO> login(@RequestBody SysUserLoginReqDTO reqDTO) {
// 调用服务层的登录方法,传入用户名和密码,获取登录响应信息
SysUserLoginDTO respDTO = baseService.login(reqDTO.getUsername(), reqDTO.getPassword());
// 调用父类的成功响应方法,返回包含登录响应信息的响应对象
return super.success(respDTO);
}
/**
* HTTP token
*
* @param request HTTP token
* @return API
*/
@CrossOrigin
@ApiOperation(value = "用户登录")
@RequestMapping(value = "/logout", method = {RequestMethod.POST})
public ApiRest logout(HttpServletRequest request) {
// 从请求头中获取 token
String token = request.getHeader("token");
// 打印当前会话的 token 信息
System.out.println("+++++当前会话为:"+token);
// 调用服务层的登出方法,传入 token 进行登出操作
baseService.logout(token);
// 调用父类的成功响应方法,返回操作成功的响应对象
return super.success();
}
/**
* token token
*
* @param token token
* @return API
*/
@ApiOperation(value = "获取会话")
@RequestMapping(value = "/info", method = {RequestMethod.POST})
public ApiRest info(@RequestParam("token") String token) {
// 调用服务层的 token 方法,传入 token 获取用户会话信息
SysUserLoginDTO respDTO = baseService.token(token);
// 调用父类的成功响应方法,返回包含会话信息的响应对象
return success(respDTO);
}
/**
*
*
* @param reqDTO
* @return API
*/
@ApiOperation(value = "修改用户资料")
@RequestMapping(value = "/update", method = {RequestMethod.POST})
public ApiRest update(@RequestBody SysUserDTO reqDTO) {
// 调用服务层的更新方法,传入用户资料修改信息进行用户资料修改
baseService.update(reqDTO);
// 调用父类的成功响应方法,返回操作成功的响应对象
return success();
}
/**
* "sa"
*
* @param reqDTO
* @return API
*/
@RequiresRoles("sa")
@ApiOperation(value = "保存或修改")
@RequestMapping(value = "/save", method = {RequestMethod.POST})
public ApiRest save(@RequestBody SysUserSaveReqDTO reqDTO) {
// 调用服务层的保存方法,传入用户保存或修改信息进行用户保存或修改操作
baseService.save(reqDTO);
// 调用父类的成功响应方法,返回操作成功的响应对象
return success();
}
/**
* "sa" ID ID
*
* @param reqDTO ID
* @return API
*/
@RequiresRoles("sa")
@ApiOperation(value = "批量删除")
@RequestMapping(value = "/delete", method = { RequestMethod.POST})
public ApiRest edit(@RequestBody BaseIdsReqDTO reqDTO) {
// 根据传入的用户 ID 列表,调用服务层的删除方法批量删除用户
baseService.removeByIds(reqDTO.getIds());
// 调用父类的成功响应方法,返回操作成功的响应对象
return super.success();
}
/**
* "sa"
*
* @param reqDTO
* @return API
*/
@RequiresRoles("sa")
@ApiOperation(value = "分页查找")
@RequestMapping(value = "/paging", method = { RequestMethod.POST})
public ApiRest<IPage<SysUserDTO>> paging(@RequestBody PagingReqDTO<SysUserDTO> reqDTO) {
// 调用服务层的分页查询方法,传入分页请求信息进行分页查询并转换结果
IPage<SysUserDTO> page = baseService.paging(reqDTO);
// 调用父类的成功响应方法,返回包含分页查询结果的响应对象
return super.success(page);
}
/**
* "sa"
*
* @param reqDTO
* @return API
*/
@RequiresRoles("sa")
@ApiOperation(value = "修改状态")
@RequestMapping(value = "/state", method = { RequestMethod.POST})
public ApiRest state(@RequestBody BaseStateReqDTO reqDTO) {
// 创建 MyBatis-Plus 的查询条件构造器,用于构建查询条件
QueryWrapper<SysUser> wrapper = new QueryWrapper<>();
// 使用 Lambda 表达式构建查询条件,筛选出 ID 在请求 ID 列表中且用户名不为 "admin" 的用户
wrapper.lambda()
.in(SysUser::getId, reqDTO.getIds())
.ne(SysUser::getUserName, "admin");
// 创建一个新的用户实体对象,用于设置要修改的状态
SysUser record = new SysUser();
// 设置用户状态为请求中的状态
record.setState(reqDTO.getState());
// 调用服务层的更新方法,根据查询条件更新用户状态
baseService.update(record, wrapper);
// 调用父类的成功响应方法,返回操作成功的响应对象
return super.success();
}
/**
*
*
* @param reqDTO
* @return API
*/
@ApiOperation(value = "学员注册")
@RequestMapping(value = "/reg", method = {RequestMethod.POST})
public ApiRest<SysUserLoginDTO> reg(@RequestBody SysUserDTO reqDTO) {
// 调用服务层的注册方法,传入学员注册信息进行学员注册,获取注册响应信息
SysUserLoginDTO respDTO = baseService.reg(reqDTO);
// 调用父类的成功响应方法,返回包含注册响应信息的响应对象
return success(respDTO);
}
/**
*
*
* @param reqDTO
* @return API
*/
@ApiOperation(value = "快速注册")
@RequestMapping(value = "/quick-reg", method = {RequestMethod.POST})
public ApiRest<SysUserLoginDTO> quick(@RequestBody SysUserDTO reqDTO) {
// 调用服务层的快速注册方法,传入用户信息进行快速注册,获取注册或登录响应信息
SysUserLoginDTO respDTO = baseService.quickReg(reqDTO);
// 调用父类的成功响应方法,返回包含注册或登录响应信息的响应对象
return success(respDTO);
}
}

@ -0,0 +1,45 @@
// 定义该类所在的包,表明其在项目模块中的位置
package com.yf.exam.modules.sys.user.dto;
// 导入 Swagger 注解,用于在生成 API 文档时描述类的信息
import io.swagger.annotations.ApiModel;
// 导入 Swagger 注解,用于在生成 API 文档时描述类属性的信息
import io.swagger.annotations.ApiModelProperty;
// 导入 Lombok 的 Data 注解,自动生成 getter、setter、equals、hashCode 和 toString 方法
import lombok.Data;
// 导入 Serializable 接口,使该类的对象可以被序列化和反序列化
import java.io.Serializable;
/**
* <p>
* 访
* </p>
*
* @author
* @since 2020-04-13 16:57
*/
// 使用 Lombok 的 Data 注解,自动生成常用方法
@Data
// 使用 Swagger 的 ApiModel 注解,为 API 文档描述该类的信息
@ApiModel(value="角色", description="角色")
public class SysRoleDTO implements Serializable {
// 序列化版本号,确保序列化和反序列化时类的版本一致性
private static final long serialVersionUID = 1L;
/**
* ID
* API 使
*/
@ApiModelProperty(value = "角色ID", required=true)
private String id;
/**
*
* API 使
*/
@ApiModelProperty(value = "角色名称", required=true)
private String roleName;
}

@ -0,0 +1,108 @@
/**
*
*/
package com.yf.exam.modules.sys.user.dto;
// 导入 Swagger 注解,用于为 API 文档提供类的描述信息
import io.swagger.annotations.ApiModel;
// 导入 Swagger 注解,用于为 API 文档提供类属性的描述信息
import io.swagger.annotations.ApiModelProperty;
// 导入 Lombok 的 Data 注解,自动生成 getter、setter、equals、hashCode 和 toString 方法
import lombok.Data;
// 导入 Serializable 接口,使该类的对象可以被序列化
import java.io.Serializable;
// 导入 Date 类,用于表示日期和时间
import java.util.Date;
/**
* <p>
* 访
* </p>
*
* @author
* @since 2020-04-13 16:57
*/
// 使用 Lombok 的 Data 注解,自动生成常用的 getter、setter 等方法
@Data
// 使用 Swagger 的 ApiModel 注解,为 API 文档描述该类的信息
@ApiModel(value="管理用户", description="管理用户")
public class SysUserDTO implements Serializable {
/**
*
*
*/
private static final long serialVersionUID = 1L;
/**
* ID
* API 使
*/
@ApiModelProperty(value = "ID", required=true)
private String id;
/**
*
* API 使
*/
@ApiModelProperty(value = "用户名", required=true)
private String userName;
/**
*
* API 使
*/
@ApiModelProperty(value = "真实姓名", required=true)
private String realName;
/**
*
* API 使
*/
@ApiModelProperty(value = "密码", required=true)
private String password;
/**
*
* API 使
*/
@ApiModelProperty(value = "密码盐", required=true)
private String salt;
/**
* ID ID
* API 使
*/
@ApiModelProperty(value = "角色列表", required=true)
private String roleIds;
/**
* ID
* API 使
*/
@ApiModelProperty(value = "部门ID", required=true)
private String departId;
/**
* 使 Date
* API 使
*/
@ApiModelProperty(value = "创建时间", required=true)
private Date createTime;
/**
* 使 Date
* API 使
*/
@ApiModelProperty(value = "更新时间", required=true)
private Date updateTime;
/**
* 使
* API 使
*/
@ApiModelProperty(value = "状态", required=true)
private Integer state;
}

@ -0,0 +1,55 @@
// 定义包名,指定该类所属的模块和目录结构
package com.yf.exam.modules.sys.user.dto;
// 导入 Swagger 注解,用于为 API 文档定义模型信息
import io.swagger.annotations.ApiModel;
// 导入 Swagger 注解,用于为 API 文档定义模型属性信息
import io.swagger.annotations.ApiModelProperty;
// 导入 Lombok 的 Data 注解,自动生成 getter、setter、equals、hashCode 和 toString 方法
import lombok.Data;
// 导入 Serializable 接口,使该类的对象可以被序列化和反序列化
import java.io.Serializable;
/**
* <p>
*
* </p>
*
* @author
* @since 2020-04-13 16:57
*/
// 使用 Lombok 的 Data 注解,自动生成常用的 getter、setter 等方法
@Data
// 使用 Swagger 的 ApiModel 注解,为 API 文档定义该类的信息
@ApiModel(value="用户角色", description="用户角色")
public class SysUserRoleDTO implements Serializable {
/**
*
*
*/
private static final long serialVersionUID = 1L;
/**
* ID
* API 使
*/
@ApiModelProperty(value = "ID", required=true)
private String id;
/**
* ID
* API 使
*/
@ApiModelProperty(value = "用户ID", required=true)
private String userId;
/**
* ID
* API 使
*/
@ApiModelProperty(value = "角色ID", required=true)
private String roleId;
}

@ -0,0 +1,45 @@
// 定义当前类所在的包,表明该类属于系统用户模块下的请求数据传输对象包
package com.yf.exam.modules.sys.user.dto.request;
// 导入 Swagger 注解,用于生成 API 文档,标记类的描述信息
import io.swagger.annotations.ApiModel;
// 导入 Swagger 注解,用于生成 API 文档,标记类属性的描述信息
import io.swagger.annotations.ApiModelProperty;
// 导入 Lombok 的 Data 注解,自动生成 getter、setter、toString 等方法
import lombok.Data;
// 导入 Serializable 接口,表明该类的对象可以被序列化
import java.io.Serializable;
/**
* <p>
*
* </p>
*
* @author
* @since 2020-04-13 16:57
*/
// 使用 Lombok 的 Data 注解,自动生成 getter、setter、equals、hashCode 和 toString 方法
@Data
// 为 Swagger 文档提供类的描述信息
@ApiModel(value="管理员登录请求类", description="管理员登录请求类")
public class SysUserLoginReqDTO implements Serializable {
// 序列化版本号,用于在反序列化时验证版本一致性
private static final long serialVersionUID = 1L;
/**
* 使
* API
*/
@ApiModelProperty(value = "用户名", required=true)
private String username;
/**
* 使
* API
*/
@ApiModelProperty(value = "密码", required=true)
private String password;
}

@ -0,0 +1,83 @@
// 声明该类所在的包,明确其在项目模块中的位置
package com.yf.exam.modules.sys.user.dto.request;
// 导入 Swagger 注解,用于在生成 API 文档时描述类的信息
import io.swagger.annotations.ApiModel;
// 导入 Swagger 注解,用于在生成 API 文档时描述类属性的信息
import io.swagger.annotations.ApiModelProperty;
// 导入 Lombok 的 Data 注解,自动生成 getter、setter、equals、hashCode 和 toString 方法
import lombok.Data;
// 导入 Serializable 接口,使该类的对象可以进行序列化和反序列化操作
import java.io.Serializable;
// 导入 List 接口,用于存储角色 ID 列表
import java.util.List;
/**
* <p>
*
*
* </p>
*
* @author
* @since 2020-04-13 16:57
*/
// 使用 Lombok 的 Data 注解,自动生成常用的 getter、setter 等方法
@Data
// 使用 Swagger 的 ApiModel 注解,为 API 文档描述该类的信息
@ApiModel(value="管理员保存请求类", description="管理员保存请求类")
public class SysUserSaveReqDTO implements Serializable {
// 序列化版本号,确保序列化和反序列化时类的版本一致性
private static final long serialVersionUID = 1L;
/**
* ID
* API
*/
@ApiModelProperty(value = "ID", required=true)
private String id;
/**
*
* API
*/
@ApiModelProperty(value = "用户名", required=true)
private String userName;
/**
*
* API
*/
@ApiModelProperty(value = "头像", required=true)
private String avatar;
/**
*
* API
*/
@ApiModelProperty(value = "真实姓名", required=true)
private String realName;
/**
*
* API
*/
@ApiModelProperty(value = "密码", required=true)
private String password;
/**
* ID
* API
*/
@ApiModelProperty(value = "部门", required=true)
private String departId;
/**
* ID
* API
*/
@ApiModelProperty(value = "角色列表", required=true)
private List<String> roles;
}

@ -0,0 +1,38 @@
// 定义包名,表明该类所属的模块和功能目录
package com.yf.exam.modules.sys.user.dto.request;
// 导入 Swagger 注解,用于生成 API 文档,标记类的描述信息
import io.swagger.annotations.ApiModel;
// 导入 Swagger 注解,用于生成 API 文档,标记类属性的描述信息
import io.swagger.annotations.ApiModelProperty;
// 导入 Lombok 的 Data 注解,自动生成 getter、setter、toString 等方法
import lombok.Data;
// 导入 Serializable 接口,使该类的对象可以被序列化和反序列化
import java.io.Serializable;
/**
* <p>
*
* </p>
*
* @author
* @since 2020-04-13 16:57
*/
// 使用 Lombok 的 Data 注解,自动生成 getter、setter、equals、hashCode 和 toString 方法
@Data
// 为 Swagger 文档提供类的描述信息
@ApiModel(value="会话检查请求类", description="会话检查请求类")
public class SysUserTokenReqDTO implements Serializable {
// 序列化版本号,用于在反序列化时验证版本一致性
private static final long serialVersionUID = 1L;
/**
*
* API
*/
@ApiModelProperty(value = "用户名", required=true)
private String token;
}

@ -0,0 +1,106 @@
// 定义包名,表明该类属于系统用户模块下的响应数据传输对象包
package com.yf.exam.modules.sys.user.dto.response;
// 导入 Swagger 注解,用于生成 API 文档,标记类的描述信息
import io.swagger.annotations.ApiModel;
// 导入 Swagger 注解,用于生成 API 文档,标记类属性的描述信息
import io.swagger.annotations.ApiModelProperty;
// 导入 Lombok 的 Data 注解,自动生成 getter、setter、toString 等方法
import lombok.Data;
// 导入 Serializable 接口,表明该类的对象可以被序列化
import java.io.Serializable;
// 导入 Date 类,用于表示日期和时间
import java.util.Date;
// 导入 List 接口,用于存储角色列表
import java.util.List;
/**
* <p>
*
*
* </p>
*
* @author
* @since 2020-04-13 16:57
*/
// 使用 Lombok 的 Data 注解,自动生成 getter、setter、equals、hashCode 和 toString 方法
@Data
// 为 Swagger 文档提供类的描述信息
@ApiModel(value="管理用户登录响应类", description="管理用户登录响应类")
public class SysUserLoginDTO implements Serializable {
// 序列化版本号,用于在反序列化时验证版本一致性
private static final long serialVersionUID = 1L;
/**
* ID
* API
*/
@ApiModelProperty(value = "ID", required=true)
private String id;
/**
*
* API
*/
@ApiModelProperty(value = "用户名", required=true)
private String userName;
/**
*
* API
*/
@ApiModelProperty(value = "真实姓名", required=true)
private String realName;
/**
* ID
* API
*/
@ApiModelProperty(value = "角色列表", required=true)
private String roleIds;
/**
* ID
* API
*/
@ApiModelProperty(value = "部门ID", required=true)
private String departId;
/**
*
* API
*/
@ApiModelProperty(value = "创建时间", required=true)
private Date createTime;
/**
*
* API
*/
@ApiModelProperty(value = "更新时间", required=true)
private Date updateTime;
/**
* 使
* API
*/
@ApiModelProperty(value = "状态", required=true)
private Integer state;
/**
*
* API
*/
@ApiModelProperty(value = "角色列表", required=true)
private List<String> roles;
/**
*
* API
*/
@ApiModelProperty(value = "登录令牌", required=true)
private String token;
}

@ -0,0 +1,54 @@
/**
*
*/
package com.yf.exam.modules.sys.user.entity;
// 导入 MyBatis-Plus 提供的主键生成策略枚举类
import com.baomidou.mybatisplus.annotation.IdType;
// 导入 MyBatis-Plus 提供的表字段注解,用于指定数据库表字段名
import com.baomidou.mybatisplus.annotation.TableField;
// 导入 MyBatis-Plus 提供的表主键注解,用于指定数据库表的主键
import com.baomidou.mybatisplus.annotation.TableId;
// 导入 MyBatis-Plus 提供的表名注解,用于指定实体类对应的数据库表名
import com.baomidou.mybatisplus.annotation.TableName;
// 导入 MyBatis-Plus 提供的 ActiveRecord 模式基类,方便进行数据库操作
import com.baomidou.mybatisplus.extension.activerecord.Model;
// 导入 Lombok 提供的 Data 注解,自动生成 getter、setter、equals、hashCode 和 toString 方法
import lombok.Data;
/**
* <p>
* sys_role
* MyBatis-Plus Model 使 ActiveRecord
* </p>
*
* @author
* @since 2020-04-13 16:57
*/
// 使用 Lombok 的 Data 注解,自动生成常用的 getter、setter 等方法
@Data
// 指定该实体类对应的数据库表名为 sys_role
@TableName("sys_role")
public class SysRole extends Model<SysRole> {
/**
*
*
*/
private static final long serialVersionUID = 1L;
/**
* ID id
* 使 MyBatis-Plus TableId ASSIGN_ID ID
*/
@TableId(value = "id", type = IdType.ASSIGN_ID)
private String id;
/**
* role_name
* 使 MyBatis-Plus TableField
*/
@TableField("role_name")
private String roleName;
}

@ -0,0 +1,103 @@
// 声明当前类所在的包,明确该类属于系统用户实体类所在的包
package com.yf.exam.modules.sys.user.entity;
// 导入 MyBatis-Plus 注解,用于指定主键生成策略
import com.baomidou.mybatisplus.annotation.IdType;
// 导入 MyBatis-Plus 注解,用于指定数据库表字段名
import com.baomidou.mybatisplus.annotation.TableField;
// 导入 MyBatis-Plus 注解,用于指定数据库表的主键
import com.baomidou.mybatisplus.annotation.TableId;
// 导入 MyBatis-Plus 注解,用于指定实体类对应的数据库表名
import com.baomidou.mybatisplus.annotation.TableName;
// 导入 MyBatis-Plus 扩展的 ActiveRecord 模式基类
import com.baomidou.mybatisplus.extension.activerecord.Model;
// 导入 Lombok 注解,自动生成 getter、setter、equals、hashCode 和 toString 方法
import lombok.Data;
// 导入 Date 类,用于表示日期和时间
import java.util.Date;
/**
* <p>
* sys_user
* MyBatis-Plus Model 使 ActiveRecord
* </p>
*
* @author
* @since 2020-04-13 16:57
*/
// 使用 Lombok 的 @Data 注解,自动生成 getter、setter、equals、hashCode 和 toString 方法
@Data
// 指定该实体类对应的数据库表名为 sys_user
@TableName("sys_user")
public class SysUser extends Model<SysUser> {
// 序列化版本号,用于在反序列化时验证版本一致性
private static final long serialVersionUID = 1L;
/**
* ID
* 使 MyBatis-Plus @TableId
* value type ASSIGN_ID
*/
@TableId(value = "id", type = IdType.ASSIGN_ID)
private String id;
/**
* user_name
* 使 MyBatis-Plus @TableField
*/
@TableField("user_name")
private String userName;
/**
* real_name
* 使 MyBatis-Plus @TableField
*/
@TableField("real_name")
private String realName;
/**
* @TableField
*/
private String password;
/**
* @TableField
*/
private String salt;
/**
* ID role_ids
* 使 MyBatis-Plus @TableField
*/
@TableField("role_ids")
private String roleIds;
/**
* ID depart_id
* 使 MyBatis-Plus @TableField
*/
@TableField("depart_id")
private String departId;
/**
* create_time
* 使 MyBatis-Plus @TableField
*/
@TableField("create_time")
private Date createTime;
/**
* update_time
* 使 MyBatis-Plus @TableField
*/
@TableField("update_time")
private Date updateTime;
/**
* @TableField
*/
private Integer state;
}

@ -0,0 +1,53 @@
// 声明当前类所在的包,该包属于系统用户模块下的实体类包
package com.yf.exam.modules.sys.user.entity;
// 导入 MyBatis-Plus 提供的主键生成策略枚举类
import com.baomidou.mybatisplus.annotation.IdType;
// 导入 MyBatis-Plus 提供的表字段注解,用于指定数据库表字段名
import com.baomidou.mybatisplus.annotation.TableField;
// 导入 MyBatis-Plus 提供的表主键注解,用于指定数据库表的主键
import com.baomidou.mybatisplus.annotation.TableId;
// 导入 MyBatis-Plus 提供的表名注解,用于指定实体类对应的数据库表名
import com.baomidou.mybatisplus.annotation.TableName;
// 导入 MyBatis-Plus 提供的 ActiveRecord 模式基类,方便进行数据库操作
import com.baomidou.mybatisplus.extension.activerecord.Model;
// 导入 Lombok 提供的 Data 注解,自动生成 getter、setter、equals、hashCode 和 toString 方法
import lombok.Data;
/**
* <p>
* sys_user_role
* </p>
*
* @author
* @since 2020-04-13 16:57
*/
// 使用 Lombok 的 Data 注解,自动生成常用的 getter、setter 等方法
@Data
// 指定该实体类对应的数据库表名为 sys_user_role
@TableName("sys_user_role")
public class SysUserRole extends Model<SysUserRole> {
// 序列化版本号,确保序列化和反序列化时类的版本一致性
private static final long serialVersionUID = 1L;
/**
* ID
* 使 TableId ASSIGN_ID
*/
@TableId(value = "id", type = IdType.ASSIGN_ID)
private String id;
/**
* ID user_id
*/
@TableField("user_id")
private String userId;
/**
* ID role_id
*/
@TableField("role_id")
private String roleId;
}

@ -0,0 +1,32 @@
/**
*
*
*/
package com.yf.exam.modules.sys.user.mapper;
/**
* MyBatis-Plus BaseMapper
* CRUD
* SQL
*/
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
*
* Mapper
*/
import com.yf.exam.modules.sys.user.entity.SysRole;
/**
* <p>
* Mapper MyBatis-Plus BaseMapper
*
* BaseMapper
* </p>
*
* @author
* @since 2020-04-13 16:57
*/
public interface SysRoleMapper extends BaseMapper<SysRole> {
// 目前该接口仅继承 BaseMapper 的通用方法,未自定义额外的数据库操作方法
}

@ -0,0 +1,32 @@
/**
*
*
*/
package com.yf.exam.modules.sys.user.mapper;
/**
* MyBatis-Plus BaseMapper
* BaseMapper CRUD
*
*/
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* SysUser
*
* Mapper
*/
import com.yf.exam.modules.sys.user.entity.SysUser;
/**
* <p>
* Mapper
* MyBatis-Plus BaseMapper 使
* </p>
*
* @author
* @since 2020-04-13 16:57
*/
public interface SysUserMapper extends BaseMapper<SysUser> {
// 目前该接口仅继承 BaseMapper 的通用方法,未自定义额外的数据库操作方法
}

@ -0,0 +1,32 @@
/**
* mapper
*/
package com.yf.exam.modules.sys.user.mapper;
/**
* MyBatis-Plus BaseMapper
* CRUD
*
*/
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
*
*
* Mapper
*/
import com.yf.exam.modules.sys.user.entity.SysUserRole;
/**
* <p>
* Mapper MyBatis-Plus BaseMapper
*
* BaseMapper
* </p>
*
* @author
* @since 2020-04-13 16:57
*/
public interface SysUserRoleMapper extends BaseMapper<SysUserRole> {
// 目前该接口仅继承 BaseMapper 的通用方法,未自定义额外的数据库操作方法
}

@ -0,0 +1,52 @@
/**
*
*
*/
package com.yf.exam.modules.sys.user.service;
/**
* MyBatis-Plus
*
*/
import com.baomidou.mybatisplus.core.metadata.IPage;
/**
* MyBatis-Plus
* CRUD
*/
import com.baomidou.mybatisplus.extension.service.IService;
/**
*
*
*/
import com.yf.exam.modules.sys.user.dto.SysRoleDTO;
/**
*
*
*/
import com.yf.exam.modules.sys.user.entity.SysRole;
/**
*
*
*/
import com.yf.exam.core.api.dto.PagingReqDTO;
/**
* <p>
*
* MyBatis-Plus IService 使
* </p>
*
* @author
* @since 2020-04-13 16:57
*/
public interface SysRoleService extends IService<SysRole> {
/**
*
*
*
* @param reqDTO
* @return
*/
IPage<SysRoleDTO> paging(PagingReqDTO<SysRoleDTO> reqDTO);
}

@ -0,0 +1,94 @@
/**
*
*/
package com.yf.exam.modules.sys.user.service;
/**
* MyBatis-Plus
*/
import com.baomidou.mybatisplus.core.metadata.IPage;
/**
* MyBatis-Plus CRUD
*/
import com.baomidou.mybatisplus.extension.service.IService;
/**
*
*/
import com.yf.exam.modules.sys.user.dto.SysUserRoleDTO;
/**
*
*/
import com.yf.exam.modules.sys.user.entity.SysUserRole;
/**
*
*/
import com.yf.exam.core.api.dto.PagingReqDTO;
import java.util.List;
/**
* <p>
*
* MyBatis-Plus IService 使
* </p>
*
* @author
* @since 2020-04-13 16:57
*/
public interface SysUserRoleService extends IService<SysUserRole> {
/**
*
*
*
* @param reqDTO
* @return
*/
IPage<SysUserRoleDTO> paging(PagingReqDTO<SysUserRoleDTO> reqDTO);
/**
*
* ID ID
*
* @param userId
* @return ID
*/
List<String> listRoles(String userId);
/**
*
*
*
* @param userId
* @param ids ID
* @return ID
*/
String saveRoles(String userId, List<String> ids);
/**
*
* ID
*
* @param userId
* @return true false
*/
boolean isStudent(String userId);
/**
*
* ID
*
* @param userId
* @return true false
*/
boolean isTeacher(String userId);
/**
*
* ID
*
* @param userId
* @return true false
*/
boolean isAdmin(String userId);
}

@ -0,0 +1,99 @@
// 声明当前接口所在的包,明确该接口属于系统用户服务模块
package com.yf.exam.modules.sys.user.service;
// 导入 MyBatis-Plus 分页元数据接口,用于处理分页查询结果
import com.baomidou.mybatisplus.core.metadata.IPage;
// 导入 MyBatis-Plus 扩展服务接口,提供通用的 CRUD 操作方法
import com.baomidou.mybatisplus.extension.service.IService;
// 导入系统用户数据传输对象类,用于在不同层之间传输用户相关数据
import com.yf.exam.modules.sys.user.dto.SysUserDTO;
// 导入系统用户保存请求数据传输对象类,用于封装用户保存操作的请求参数
import com.yf.exam.modules.sys.user.dto.request.SysUserSaveReqDTO;
// 导入系统用户登录响应数据传输对象类,用于封装用户登录操作的响应信息
import com.yf.exam.modules.sys.user.dto.response.SysUserLoginDTO;
// 导入系统用户实体类,用于映射数据库中的用户表
import com.yf.exam.modules.sys.user.entity.SysUser;
// 导入自定义的分页请求数据传输对象类,用于封装分页查询的请求参数
import com.yf.exam.core.api.dto.PagingReqDTO;
/**
* <p>
*
* MyBatis-Plus IService 使
* </p>
*
* @author
* @since 2020-04-13 16:57
*/
public interface SysUserService extends IService<SysUser> {
/**
*
*
*
* @param reqDTO
* @return
*/
IPage<SysUserDTO> paging(PagingReqDTO<SysUserDTO> reqDTO);
/**
*
*
*
* @param userName
* @param password
* @return
*/
SysUserLoginDTO login(String userName, String password);
/**
*
*
*
* @param token
* @return
*/
SysUserLoginDTO token(String token);
/**
* 退
* 使
*
* @param token
*/
void logout(String token);
/**
*
*
*
* @param reqDTO
*/
void update(SysUserDTO reqDTO);
/**
*
*
*
* @param reqDTO
*/
void save(SysUserSaveReqDTO reqDTO);
/**
*
*
*
* @param reqDTO
* @return
*/
SysUserLoginDTO reg(SysUserDTO reqDTO);
/**
*
*
*
* @param reqDTO
* @return
*/
SysUserLoginDTO quickReg(SysUserDTO reqDTO);
}

@ -0,0 +1,62 @@
// 声明该类所在的包,明确其在项目中的模块和层级位置
package com.yf.exam.modules.sys.user.service.impl;
// 导入阿里巴巴的 FastJSON 库,用于 JSON 数据的序列化和反序列化
import com.alibaba.fastjson.JSON;
// 导入 FastJSON 的 TypeReference 类,用于处理泛型类型的 JSON 反序列化
import com.alibaba.fastjson.TypeReference;
// 导入 MyBatis-Plus 的查询条件构造器类,用于构建数据库查询条件
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
// 导入 MyBatis-Plus 的分页元数据接口,用于表示分页查询的结果
import com.baomidou.mybatisplus.core.metadata.IPage;
// 导入 MyBatis-Plus 的分页实现类,用于创建分页对象
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
// 导入 MyBatis-Plus 的服务实现基类,提供了通用的服务层方法实现
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
// 导入系统角色数据传输对象类,用于在不同层之间传输角色相关的数据
import com.yf.exam.modules.sys.user.dto.SysRoleDTO;
// 导入系统角色实体类,用于映射数据库中的角色表
import com.yf.exam.modules.sys.user.entity.SysRole;
// 导入系统角色数据访问接口,用于与数据库进行角色数据的交互
import com.yf.exam.modules.sys.user.mapper.SysRoleMapper;
// 导入系统角色服务接口,定义了角色相关的业务方法
import com.yf.exam.modules.sys.user.service.SysRoleService;
// 导入分页请求数据传输对象类,用于封装分页查询的请求参数
import com.yf.exam.core.api.dto.PagingReqDTO;
// 导入 Spring 的服务注解,将该类标记为一个服务组件,由 Spring 容器进行管理
import org.springframework.stereotype.Service;
/**
* <p>
*
* MyBatis-Plus ServiceImpl 使 SysRoleMapper SysRole
* </p>
*
* @author
* @since 2020-04-13 16:57
*/
@Service
public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> implements SysRoleService {
/**
*
*
* @param reqDTO
* @return
*/
@Override
public IPage<SysRoleDTO> paging(PagingReqDTO<SysRoleDTO> reqDTO) {
// 创建分页对象,根据请求中的当前页码和每页记录数初始化
IPage<SysRole> query = new Page<>(reqDTO.getCurrent(), reqDTO.getSize());
// 创建查询条件构造器,用于构建数据库查询条件,当前未添加具体查询条件
QueryWrapper<SysRole> wrapper = new QueryWrapper<>();
// 调用父类的 page 方法进行分页查询,获取包含 SysRole 实体的分页结果
IPage<SysRole> page = this.page(query, wrapper);
// 将包含 SysRole 实体的分页结果转换为 JSON 字符串,再反序列化为包含 SysRoleDTO 的分页结果
IPage<SysRoleDTO> pageData = JSON.parseObject(JSON.toJSONString(page), new TypeReference<Page<SysRoleDTO>>(){});
return pageData;
}
}

@ -0,0 +1,200 @@
package com.yf.exam.modules.sys.user.service.impl;
// 导入阿里巴巴的 FastJSON 库,用于 JSON 数据的序列化和反序列化
import com.alibaba.fastjson.JSON;
// 导入 FastJSON 的 TypeReference 类,用于处理泛型类型的 JSON 反序列化
import com.alibaba.fastjson.TypeReference;
// 导入 MyBatis-Plus 的查询条件构造器类,用于构建数据库查询条件
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
// 导入 MyBatis-Plus 的分页元数据接口,用于表示分页查询的结果
import com.baomidou.mybatisplus.core.metadata.IPage;
// 导入 MyBatis-Plus 的分页实现类,用于创建分页对象
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
// 导入 MyBatis-Plus 的服务实现基类,提供了通用的服务层方法实现
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
// 导入系统用户角色数据传输对象类,用于在不同层之间传输用户角色相关的数据
import com.yf.exam.modules.sys.user.dto.SysUserRoleDTO;
// 导入系统用户角色实体类,用于映射数据库中的用户角色表
import com.yf.exam.modules.sys.user.entity.SysUserRole;
// 导入系统用户角色数据访问接口,用于与数据库进行用户角色数据的交互
import com.yf.exam.modules.sys.user.mapper.SysUserRoleMapper;
// 导入系统用户角色服务接口,定义了用户角色相关的业务方法
import com.yf.exam.modules.sys.user.service.SysUserRoleService;
// 导入分页请求数据传输对象类,用于封装分页查询的请求参数
import com.yf.exam.core.api.dto.PagingReqDTO;
// 导入 Spring 的服务注解,将该类标记为一个服务组件,由 Spring 容器进行管理
import org.springframework.stereotype.Service;
// 导入 Spring 的集合工具类,用于判断集合是否为空
import org.springframework.util.CollectionUtils;
// 导入 Spring 的字符串工具类,用于判断字符串是否为空
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.List;
/**
* <p>
*
* MyBatis-Plus ServiceImpl 使 SysUserRoleMapper SysUserRole
* </p>
*
* @author
* @since 2020-04-13 16:57
*/
@Service
public class SysUserRoleServiceImpl extends ServiceImpl<SysUserRoleMapper, SysUserRole> implements SysUserRoleService {
/**
*
*
* @param reqDTO
* @return
*/
@Override
public IPage<SysUserRoleDTO> paging(PagingReqDTO<SysUserRoleDTO> reqDTO) {
// 创建分页对象,根据请求中的当前页码和每页记录数初始化
IPage<SysUserRole> query = new Page<>(reqDTO.getCurrent(), reqDTO.getSize());
// 创建查询条件构造器,用于构建数据库查询条件,当前未添加具体查询条件
QueryWrapper<SysUserRole> wrapper = new QueryWrapper<>();
// 调用父类的 page 方法进行分页查询,获取包含 SysUserRole 实体的分页结果
IPage<SysUserRole> page = this.page(query, wrapper);
// 将包含 SysUserRole 实体的分页结果转换为 JSON 字符串,再反序列化为包含 SysUserRoleDTO 的分页结果
IPage<SysUserRoleDTO> pageData = JSON.parseObject(JSON.toJSONString(page), new TypeReference<Page<SysUserRoleDTO>>(){});
return pageData;
}
/**
* ID
*
* @param userId
* @return ID
*/
@Override
public List<String> listRoles(String userId) {
// 创建查询条件构造器,添加用户 ID 作为查询条件
QueryWrapper<SysUserRole> wrapper = new QueryWrapper<>();
wrapper.lambda().eq(SysUserRole::getUserId, userId);
// 调用父类的 list 方法进行查询,获取符合条件的用户角色实体列表
List<SysUserRole> list = this.list(wrapper);
// 初始化一个空的角色 ID 列表
List<String> roles = new ArrayList<>();
// 判断查询结果列表是否不为空
if(!CollectionUtils.isEmpty(list)){
// 遍历用户角色实体列表,将每个实体的角色 ID 添加到角色 ID 列表中
for(SysUserRole item: list){
roles.add(item.getRoleId());
}
}
return roles;
}
/**
*
*
* @param userId
* @param ids ID
* @return ID
*/
@Override
public String saveRoles(String userId, List<String> ids) {
// 创建查询条件构造器,添加用户 ID 作为查询条件,用于删除用户原有的所有角色
QueryWrapper<SysUserRole> wrapper = new QueryWrapper<>();
wrapper.lambda().eq(SysUserRole::getUserId, userId);
// 调用父类的 remove 方法删除符合条件的用户角色记录
this.remove(wrapper);
// 判断新的角色 ID 列表是否不为空
if(!CollectionUtils.isEmpty(ids)){
// 初始化一个空的用户角色实体列表
List<SysUserRole> list = new ArrayList<>();
// 用于存储以逗号连接的角色 ID 字符串
String roleIds = null;
// 遍历新的角色 ID 列表
for(String item: ids){
// 创建一个新的用户角色实体对象
SysUserRole role = new SysUserRole();
// 设置角色 ID
role.setRoleId(item);
// 设置用户 ID
role.setUserId(userId);
// 将用户角色实体对象添加到列表中
list.add(role);
// 判断 roleIds 是否为空
if(StringUtils.isEmpty(roleIds)){
// 若为空,则直接赋值为当前角色 ID
roleIds = item;
}else{
// 若不为空,则将当前角色 ID 追加到 roleIds 后面,用逗号分隔
roleIds+=","+item;
}
}
// 调用父类的 saveBatch 方法批量保存用户角色实体列表
this.saveBatch(list);
return roleIds;
}
return "";
}
/**
*
*
* @param userId
* @return true false
*/
@Override
public boolean isStudent(String userId) {
// 创建查询条件构造器,添加用户 ID 和角色 IDstudent作为查询条件
QueryWrapper<SysUserRole> wrapper = new QueryWrapper<>();
wrapper.lambda().eq(SysUserRole::getUserId, userId)
.eq(SysUserRole::getRoleId, "student");
// 调用父类的 count 方法统计符合条件的记录数,若大于 0 则表示用户具有学生角色
return this.count(wrapper) > 0;
}
/**
*
*
* @param userId
* @return true false
*/
@Override
public boolean isTeacher(String userId) {
// 创建查询条件构造器,添加用户 ID 和角色 IDteacher作为查询条件
QueryWrapper<SysUserRole> wrapper = new QueryWrapper<>();
wrapper.lambda().eq(SysUserRole::getUserId, userId)
.eq(SysUserRole::getRoleId, "teacher");
// 调用父类的 count 方法统计符合条件的记录数,若大于 0 则表示用户具有教师角色
return this.count(wrapper) > 0;
}
/**
*
*
* @param userId
* @return true false
*/
@Override
public boolean isAdmin(String userId) {
// 创建查询条件构造器,添加用户 ID 和角色 IDsa作为查询条件
QueryWrapper<SysUserRole> wrapper = new QueryWrapper<>();
wrapper.lambda().eq(SysUserRole::getUserId, userId)
.eq(SysUserRole::getRoleId, "sa");
// 调用父类的 count 方法统计符合条件的记录数,若大于 0 则表示用户具有管理员角色
return this.count(wrapper) > 0;
}
}

@ -0,0 +1,383 @@
// 声明该类所在的包,表明它属于系统用户服务实现模块
package com.yf.exam.modules.sys.user.service.impl;
// 导入阿里巴巴 FastJSON 库,用于 JSON 数据的序列化和反序列化
import com.alibaba.fastjson.JSON;
// 导入 FastJSON 的 TypeReference 类,用于处理泛型类型的 JSON 反序列化
import com.alibaba.fastjson.TypeReference;
// 导入 MyBatis-Plus 的查询条件构造器,用于构建数据库查询条件
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
// 导入 MyBatis-Plus 的分页元数据接口,用于表示分页查询结果
import com.baomidou.mybatisplus.core.metadata.IPage;
// 导入 MyBatis-Plus 的 ID 生成工具类,用于生成分布式 ID
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
// 导入 MyBatis-Plus 的分页实现类,用于创建分页对象
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
// 导入 MyBatis-Plus 的服务实现基类,提供通用的服务层方法实现
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
// 导入自定义的 API 错误类,用于封装 API 错误信息
import com.yf.exam.core.api.ApiError;
// 导入自定义的分页请求数据传输对象类,用于封装分页查询请求参数
import com.yf.exam.core.api.dto.PagingReqDTO;
// 导入自定义的通用状态枚举类,定义通用的状态常量
import com.yf.exam.core.enums.CommonState;
// 导入自定义的服务异常类,用于抛出服务层异常
import com.yf.exam.core.exception.ServiceException;
// 导入自定义的 Bean 映射工具类,用于对象属性的复制
import com.yf.exam.core.utils.BeanMapper;
// 导入自定义的密码处理工具类,用于密码的加密和验证
import com.yf.exam.core.utils.passwd.PassHandler;
// 导入自定义的密码信息类,用于封装密码和盐值
import com.yf.exam.core.utils.passwd.PassInfo;
// 导入自定义的 JWT 工具类,用于生成和验证 JWT 令牌
import com.yf.exam.ability.shiro.jwt.JwtUtils;
// 导入自定义的系统用户数据传输对象类,用于在不同层之间传输用户数据
import com.yf.exam.modules.sys.user.dto.SysUserDTO;
// 导入自定义的系统用户保存请求数据传输对象类,用于封装用户保存请求参数
import com.yf.exam.modules.sys.user.dto.request.SysUserSaveReqDTO;
// 导入自定义的系统用户登录响应数据传输对象类,用于封装用户登录响应信息
import com.yf.exam.modules.sys.user.dto.response.SysUserLoginDTO;
// 导入自定义的系统用户实体类,用于映射数据库中的用户表
import com.yf.exam.modules.sys.user.entity.SysUser;
// 导入自定义的系统用户数据访问接口,用于与数据库进行用户数据交互
import com.yf.exam.modules.sys.user.mapper.SysUserMapper;
// 导入自定义的系统用户角色服务接口,用于处理用户角色相关业务
import com.yf.exam.modules.sys.user.service.SysUserRoleService;
// 导入自定义的系统用户服务接口,定义用户相关的业务方法
import com.yf.exam.modules.sys.user.service.SysUserService;
// 导入自定义的用户工具类,提供用户相关的工具方法
import com.yf.exam.modules.user.UserUtils;
// 导入 Apache Commons Lang3 的字符串工具类,提供字符串操作方法
import org.apache.commons.lang3.StringUtils;
// 导入 Apache Shiro 的安全工具类,用于进行身份验证和授权操作
import org.apache.shiro.SecurityUtils;
// 导入 Spring 的自动装配注解,用于自动注入依赖的 Bean
import org.springframework.beans.factory.annotation.Autowired;
// 导入 Spring 的服务注解,将该类标记为服务组件,由 Spring 容器管理
import org.springframework.stereotype.Service;
// 导入 Spring 的事务管理注解,用于声明事务方法
import org.springframework.transaction.annotation.Transactional;
// 导入 Spring 的集合工具类,用于判断集合是否为空
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
/**
* <p>
*
* MyBatis-Plus ServiceImpl 使 SysUserMapper SysUser
* </p>
*
* @author
* @since 2020-04-13 16:57
*/
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {
// 自动注入系统用户角色服务类的实例,用于处理用户角色相关业务
@Autowired
private SysUserRoleService sysUserRoleService;
/**
*
*
* @param reqDTO
* @return
*/
@Override
public IPage<SysUserDTO> paging(PagingReqDTO<SysUserDTO> reqDTO) {
// 创建分页对象,指定当前页码和每页记录数
IPage<SysUser> query = new Page<>(reqDTO.getCurrent(), reqDTO.getSize());
// 创建查询条件构造器,用于构建数据库查询条件
QueryWrapper<SysUser> wrapper = new QueryWrapper<>();
// 获取查询参数
SysUserDTO params = reqDTO.getParams();
// 如果查询参数不为空
if(params!=null){
// 如果用户名不为空,添加用户名模糊查询条件
if(!StringUtils.isBlank(params.getUserName())){
wrapper.lambda().like(SysUser::getUserName, params.getUserName());
}
// 如果真实姓名不为空,添加真实姓名模糊查询条件
if(!StringUtils.isBlank(params.getRealName())){
wrapper.lambda().like(SysUser::getRealName, params.getRealName());
}
}
// 执行分页查询,获取包含系统用户实体的分页结果
IPage<SysUser> page = this.page(query, wrapper);
// 将包含系统用户实体的分页结果转换为包含系统用户数据传输对象的分页结果
IPage<SysUserDTO> pageData = JSON.parseObject(JSON.toJSONString(page), new TypeReference<Page<SysUserDTO>>(){});
return pageData;
}
/**
*
*
* @param userName
* @param password
* @return
* @throws ServiceException
*/
@Override
public SysUserLoginDTO login(String userName, String password) {
// 创建查询条件构造器,添加用户名等于查询条件
QueryWrapper<SysUser> wrapper = new QueryWrapper<>();
wrapper.lambda().eq(SysUser::getUserName, userName);
// 根据查询条件获取一个用户实体
SysUser user = this.getOne(wrapper, false);
// 如果用户不存在,抛出用户名或密码错误异常
if(user == null){
throw new ServiceException(ApiError.ERROR_90010002);
}
// 如果用户状态为异常(被禁用),抛出用户被禁用异常
if(user.getState().equals(CommonState.ABNORMAL)){
throw new ServiceException(ApiError.ERROR_90010005);
}
// 验证密码是否正确
boolean check = PassHandler.checkPass(password,user.getSalt(), user.getPassword());
// 如果密码不正确,抛出用户名或密码错误异常
if(!check){
throw new ServiceException(ApiError.ERROR_90010002);
}
// 为用户设置令牌并返回登录响应信息
return this.setToken(user);
}
/**
*
*
* @param token
* @return
* @throws ServiceException
*/
@Override
public SysUserLoginDTO token(String token) {
// 从令牌中获取用户名
String username = JwtUtils.getUsername(token);
// 验证令牌是否有效
boolean check = JwtUtils.verify(token, username);
// 如果令牌验证失败,抛出用户名或密码错误异常
if(!check){
throw new ServiceException(ApiError.ERROR_90010002);
}
// 创建查询条件构造器,添加用户名等于查询条件
QueryWrapper<SysUser> wrapper = new QueryWrapper<>();
wrapper.lambda().eq(SysUser::getUserName, username);
// 根据查询条件获取一个用户实体
SysUser user = this.getOne(wrapper, false);
// 如果用户不存在,抛出用户不存在异常
if(user == null){
throw new ServiceException(ApiError.ERROR_10010002);
}
// 如果用户状态为异常(被禁用),抛出用户被禁用异常
if(user.getState().equals(CommonState.ABNORMAL)){
throw new ServiceException(ApiError.ERROR_90010005);
}
// 为用户设置令牌并返回登录响应信息
return this.setToken(user);
}
/**
* 退
*
* @param token
*/
@Override
public void logout(String token) {
// 仅退出当前会话
SecurityUtils.getSubject().logout();
}
/**
*
*
* @param reqDTO
*/
@Override
public void update(SysUserDTO reqDTO) {
// 获取用户输入的新密码
String pass = reqDTO.getPassword();
// 如果新密码不为空
if(!StringUtils.isBlank(pass)){
// 生成新的密码信息,包含加密后的密码和盐值
PassInfo passInfo = PassHandler.buildPassword(pass);
// 根据当前用户 ID 获取用户实体
SysUser user = this.getById(UserUtils.getUserId());
// 更新用户密码
user.setPassword(passInfo.getPassword());
// 更新用户密码盐值
user.setSalt(passInfo.getSalt());
// 更新用户信息到数据库
this.updateById(user);
}
}
/**
*
*
* @param reqDTO
* @throws ServiceException
*/
@Transactional(rollbackFor = Exception.class)
@Override
public void save(SysUserSaveReqDTO reqDTO) {
// 获取用户的角色列表
List<String> roles = reqDTO.getRoles();
// 如果角色列表为空,抛出角色不能为空异常
if(CollectionUtils.isEmpty(roles)){
throw new ServiceException(ApiError.ERROR_90010003);
}
// 创建系统用户实体对象
SysUser user = new SysUser();
// 将请求数据传输对象的属性复制到用户实体对象中
BeanMapper.copy(reqDTO, user);
// 如果用户 ID 为空,说明是新增用户,生成新的用户 ID
if(StringUtils.isBlank(user.getId())){
user.setId(IdWorker.getIdStr());
}
// 如果请求中包含密码,更新用户密码信息
if(!StringUtils.isBlank(reqDTO.getPassword())){
// 生成新的密码信息,包含加密后的密码和盐值
PassInfo pass = PassHandler.buildPassword(reqDTO.getPassword());
// 更新用户密码
user.setPassword(pass.getPassword());
// 更新用户密码盐值
user.setSalt(pass.getSalt());
}
// 保存用户角色信息,并返回角色 ID 字符串
String roleIds = sysUserRoleService.saveRoles(user.getId(), roles);
// 设置用户的角色 ID 字符串
user.setRoleIds(roleIds);
// 保存或更新用户信息到数据库
this.saveOrUpdate(user);
}
/**
*
*
* @param reqDTO
* @return
* @throws ServiceException
*/
@Transactional(rollbackFor = Exception.class)
@Override
public SysUserLoginDTO reg(SysUserDTO reqDTO) {
// 创建查询条件构造器,添加用户名等于查询条件
QueryWrapper<SysUser> wrapper = new QueryWrapper<>();
wrapper.lambda().eq(SysUser::getUserName, reqDTO.getUserName());
// 统计符合条件的用户数量
int count = this.count(wrapper);
// 如果用户数量大于 0说明用户名已存在抛出用户名已存在异常
if(count > 0){
throw new ServiceException(1, "用户名已存在,换一个吧!");
}
// 创建系统用户实体对象
SysUser user = new SysUser();
// 生成新的用户 ID
user.setId(IdWorker.getIdStr());
// 设置用户名
user.setUserName(reqDTO.getUserName());
// 设置真实姓名
user.setRealName(reqDTO.getRealName());
// 生成新的密码信息,包含加密后的密码和盐值
PassInfo passInfo = PassHandler.buildPassword(reqDTO.getPassword());
// 设置用户密码
user.setPassword(passInfo.getPassword());
// 设置用户密码盐值
user.setSalt(passInfo.getSalt());
// 创建角色列表,默认添加学生角色
List<String> roles = new ArrayList<>();
roles.add("student");
// 保存用户角色信息,并返回角色 ID 字符串
String roleIds = sysUserRoleService.saveRoles(user.getId(), roles);
// 设置用户的角色 ID 字符串
user.setRoleIds(roleIds);
// 保存用户信息到数据库
this.save(user);
// 为用户设置令牌并返回登录响应信息
return this.setToken(user);
}
/**
*
*
* @param reqDTO
* @return
*/
@Override
public SysUserLoginDTO quickReg(SysUserDTO reqDTO) {
// 创建查询条件构造器,添加用户名等于查询条件,并限制查询结果为 1 条
QueryWrapper<SysUser> wrapper = new QueryWrapper<>();
wrapper.lambda().eq(SysUser::getUserName, reqDTO.getUserName());
wrapper.last(" LIMIT 1 ");
// 根据查询条件获取一个用户实体
SysUser user = this.getOne(wrapper);
// 如果用户存在,为用户设置令牌并返回登录响应信息
if(user!=null){
return this.setToken(user);
}
// 如果用户不存在,进行注册操作
return this.reg(reqDTO);
}
/**
*
*
* @param user
* @return
*/
private SysUserLoginDTO setToken(SysUser user){
// 创建系统用户登录响应数据传输对象
SysUserLoginDTO respDTO = new SysUserLoginDTO();
// 将用户实体对象的属性复制到登录响应数据传输对象中
BeanMapper.copy(user, respDTO);
// 生成 JWT 令牌
String token = JwtUtils.sign(user.getUserName());
// 设置登录响应数据传输对象的令牌
respDTO.setToken(token);
// 获取用户的角色列表
List<String> roles = sysUserRoleService.listRoles(user.getId());
// 设置登录响应数据传输对象的角色列表
respDTO.setRoles(roles);
return respDTO;
}
}

@ -0,0 +1,75 @@
package com.yf.exam.modules.user;
// 导入自定义的 API 错误信息类
import com.yf.exam.core.api.ApiError;
// 导入自定义的服务异常类
import com.yf.exam.core.exception.ServiceException;
// 导入系统用户登录信息响应数据传输对象
import com.yf.exam.modules.sys.user.dto.response.SysUserLoginDTO;
// 导入 Shiro 安全工具类
import org.apache.shiro.SecurityUtils;
/**
*
* @author bool
*/
public class UserUtils {
/**
* ID
*
* @param throwable ID
* true ServiceException
* false null
* @return ID throwable false null
*/
public static String getUserId(boolean throwable){
try {
// 从 Shiro 的 Subject 中获取当前用户的主身份信息,并转换为 SysUserLoginDTO 对象,然后获取用户 ID
return ((SysUserLoginDTO) SecurityUtils.getSubject().getPrincipal()).getId();
}catch (Exception e){
if(throwable){
// 若 throwable 为 true获取用户 ID 失败时抛出服务异常
throw new ServiceException(ApiError.ERROR_10010002);
}
// 若 throwable 为 false获取用户 ID 失败时返回 null
return null;
}
}
/**
*
*
* @param throwable
* true ServiceException
* false false
* @return "sa" true false
* throwable false false
*/
public static boolean isAdmin(boolean throwable){
try {
// 从 Shiro 的 Subject 中获取当前用户的主身份信息,并转换为 SysUserLoginDTO 对象
SysUserLoginDTO dto = ((SysUserLoginDTO) SecurityUtils.getSubject().getPrincipal());
// 判断用户的角色列表是否包含 "sa"
return dto.getRoles().contains("sa");
}catch (Exception e){
if(throwable){
// 若 throwable 为 true获取用户信息失败时抛出服务异常
throw new ServiceException(ApiError.ERROR_10010002);
}
}
// 若获取用户信息失败且 throwable 为 false返回 false
return false;
}
/**
* ID
* getUserId(boolean throwable) throwable true
*
* @return ID ServiceException
*/
public static String getUserId(){
return getUserId(true);
}
}

@ -0,0 +1,79 @@
package com.yf.exam.modules.user.book.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.yf.exam.core.api.ApiRest;
import com.yf.exam.core.api.controller.BaseController;
import com.yf.exam.core.api.dto.BaseIdRespDTO;
import com.yf.exam.core.api.dto.BaseIdsReqDTO;
import com.yf.exam.core.api.dto.PagingReqDTO;
import com.yf.exam.modules.user.book.dto.UserBookDTO;
import com.yf.exam.modules.user.book.service.UserBookService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
* <p>
* HTTP
* </p>
*
* @author
* @since 2020-05-27 17:56
*/
@Api(tags={"错题本"})
@RestController
@RequestMapping("/exam/api/user/wrong-book")
public class UserBookController extends BaseController {
/**
*
*/
@Autowired
private UserBookService baseService;
/**
*
* @param reqDTO ID
* @return
*/
@ApiOperation(value = "批量删除")
@RequestMapping(value = "/delete", method = { RequestMethod.POST})
public ApiRest delete(@RequestBody BaseIdsReqDTO reqDTO) {
// 根据传入的ID列表删除对应的错题本记录
baseService.removeByIds(reqDTO.getIds());
// 返回操作成功的响应
return super.success();
}
/**
*
* @param reqDTO
* @return
*/
@ApiOperation(value = "分页查找")
@RequestMapping(value = "/paging", method = { RequestMethod.POST})
public ApiRest<IPage<UserBookDTO>> paging(@RequestBody PagingReqDTO<UserBookDTO> reqDTO) {
// 调用服务层方法进行分页查询并将结果转换为UserBookDTO对象
IPage<UserBookDTO> page = baseService.paging(reqDTO);
// 返回包含分页结果的成功响应
return super.success(page);
}
/**
* 200
* @param reqDTO IDID
* @return ID
*/
@ApiOperation(value = "查找列表")
@RequestMapping(value = "/next", method = { RequestMethod.POST})
public ApiRest<BaseIdRespDTO> nextQu(@RequestBody UserBookDTO reqDTO) {
// 调用服务层方法查找下一个错题的题目ID
String quId = baseService.findNext(reqDTO.getExamId(), reqDTO.getQuId());
// 将下一个题目ID封装到响应对象中并返回成功响应
return super.success(new BaseIdRespDTO(quId));
}
}

@ -0,0 +1,79 @@
package com.yf.exam.modules.user.book.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* <p>
*
* IDIDID
* </p>
*
* @author
* @since 2020-05-27 17:56
*/
@Data
@ApiModel(value="错题本", description="错题本")
public class UserBookDTO implements Serializable {
// 序列化版本号,用于保证序列化和反序列化过程中类的版本一致性
private static final long serialVersionUID = 1L;
/**
* ID
*/
@ApiModelProperty(value = "ID", required=true)
private String id;
/**
* ID
*/
@ApiModelProperty(value = "考试ID", required=true)
private String examId;
/**
* ID
*/
@ApiModelProperty(value = "用户ID", required=true)
private String userId;
/**
* ID
*/
@ApiModelProperty(value = "题目ID", required=true)
private String quId;
/**
*
*/
@ApiModelProperty(value = "加入时间", required=true)
private Date createTime;
/**
*
*/
@ApiModelProperty(value = "最近错误时间", required=true)
private Date updateTime;
/**
*
*/
@ApiModelProperty(value = "错误时间", required=true)
private Integer wrongCount;
/**
*
*/
@ApiModelProperty(value = "题目标题", required=true)
private String title;
/**
*
*/
@ApiModelProperty(value = "错题序号", required=true)
private Integer sort;
}

@ -0,0 +1,80 @@
package com.yf.exam.modules.user.book.entity;
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.activerecord.Model;
import lombok.Data;
import java.util.Date;
/**
* <p>
* `el_user_book`
* MyBatis-Plus `Model` 使 ActiveRecord
* </p>
*
* @author
* @since 2020-05-27 17:56
*/
@Data
@TableName("el_user_book")
public class UserBook extends Model<UserBook> {
// 序列化版本号,确保序列化和反序列化过程中类版本的一致性
private static final long serialVersionUID = 1L;
/**
* ID
*/
@TableId(value = "id", type = IdType.ASSIGN_ID)
private String id;
/**
* ID
*/
@TableField("exam_id")
private String examId;
/**
* ID
*/
@TableField("user_id")
private String userId;
/**
* ID
*/
@TableField("qu_id")
private String quId;
/**
*
*/
@TableField("create_time")
private Date createTime;
/**
*
*/
@TableField("update_time")
private Date updateTime;
/**
*
*/
@TableField("wrong_count")
private Integer wrongCount;
/**
* 便
*/
private String title;
/**
*
*/
private Integer sort;
}

@ -0,0 +1,18 @@
package com.yf.exam.modules.user.book.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yf.exam.modules.user.book.entity.UserBook;
/**
* <p>
* Mapper
* MyBatis-PlusBaseMapper使BaseMapperCRUD
* </p>
*
* @author
* @since 2020-05-27 17:56
*/
public interface UserBookMapper extends BaseMapper<UserBook> {
// 若需要自定义数据库操作方法,可在此处添加接口方法声明
// 对应的SQL语句可以在XML文件中实现或者使用@Select、@Insert等注解
}

@ -0,0 +1,41 @@
package com.yf.exam.modules.user.book.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yf.exam.core.api.dto.PagingReqDTO;
import com.yf.exam.modules.user.book.dto.UserBookDTO;
import com.yf.exam.modules.user.book.entity.UserBook;
/**
* <p>
*
* MyBatis-Plus IService 使
* </p>
*
* @author
* @since 2020-05-27 17:56
*/
public interface UserBookService extends IService<UserBook> {
/**
*
* @param reqDTO UserBookDTO
* @return UserBookDTO IPage
*/
IPage<UserBookDTO> paging(PagingReqDTO<UserBookDTO> reqDTO);
/**
*
* @param examId ID
* @param quId ID
*/
void addBook(String examId, String quId);
/**
*
* @param examId ID
* @param quId ID
* @return ID null
*/
String findNext(String examId, String quId);
}

@ -0,0 +1,192 @@
package com.yf.exam.modules.user.book.service.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yf.exam.core.api.dto.PagingReqDTO;
import com.yf.exam.modules.qu.entity.Qu;
import com.yf.exam.modules.qu.service.QuService;
import com.yf.exam.modules.user.UserUtils;
import com.yf.exam.modules.user.book.dto.UserBookDTO;
import com.yf.exam.modules.user.book.entity.UserBook;
import com.yf.exam.modules.user.book.mapper.UserBookMapper;
import com.yf.exam.modules.user.book.service.UserBookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
/**
* <p>
*
* </p>
*
* @author
* @since 2020-05-27 17:56
*/
@Service
public class UserBookServiceImpl extends ServiceImpl<UserBookMapper, UserBook> implements UserBookService {
/**
*
*/
@Autowired
private QuService quService;
/**
*
* @param reqDTO
* @return DTO
*/
@Override
public IPage<UserBookDTO> paging(PagingReqDTO<UserBookDTO> reqDTO) {
// 创建分页对象,指定当前页码和每页显示数量
Page query = new Page<>(reqDTO.getCurrent(), reqDTO.getSize());
// 构建查询条件包装器
QueryWrapper<UserBook> wrapper = new QueryWrapper<>();
// 只查询当前用户的错题记录
wrapper.lambda().eq(UserBook::getUserId, UserUtils.getUserId(true));
// 获取查询参数
UserBookDTO params = reqDTO.getParams();
if (params != null) {
// 如果标题参数不为空,添加模糊查询条件
if (!StringUtils.isEmpty(params.getTitle())) {
wrapper.lambda().like(UserBook::getTitle, params.getTitle());
}
// 如果考试 ID 参数不为空,添加精确查询条件
if (!StringUtils.isEmpty(params.getExamId())) {
wrapper.lambda().eq(UserBook::getExamId, params.getExamId());
}
}
// 执行分页查询,获取错题本实体分页数据
IPage<UserBook> page = this.page(query, wrapper);
// 将实体分页数据转换为 DTO 分页数据
IPage<UserBookDTO> pageData = JSON.parseObject(JSON.toJSONString(page), new TypeReference<Page<UserBookDTO>>(){});
return pageData;
}
/**
*
* @param examId ID
* @param quId ID
*/
@Override
public void addBook(String examId, String quId) {
// 构建查询条件,查找该用户在本次考试中该题目的错题记录
QueryWrapper<UserBook> wrapper = new QueryWrapper<>();
wrapper.lambda()
.eq(UserBook::getUserId, UserUtils.getUserId())
.eq(UserBook::getExamId, examId)
.eq(UserBook::getQuId, quId);
// 查找已有的错题信息
UserBook book = this.getOne(wrapper, false);
// 获取题目信息
Qu qu = quService.getById(quId);
if (book == null) {
// 如果该错题记录不存在,则创建新的错题记录
book = new UserBook();
book.setExamId(examId);
book.setUserId(UserUtils.getUserId());
book.setTitle(qu.getContent());
book.setQuId(quId);
book.setWrongCount(1);
// 获取当前考试中用户错题的最大排序值,并加 1 作为新记录的排序值
Integer maxSort = this.findMaxSort(examId, UserUtils.getUserId());
book.setSort(maxSort + 1);
// 保存新的错题记录
this.save(book);
} else {
// 如果该错题记录已存在,错误次数加 1
book.setWrongCount(book.getWrongCount() + 1);
// 更新错题记录
this.updateById(book);
}
}
/**
* ID
* @param examId ID
* @param quId ID
* @return ID null
*/
@Override
public String findNext(String examId, String quId) {
// 初始化排序值为一个较大值
Integer sort = 999999;
if (!StringUtils.isEmpty(quId)) {
// 构建查询条件,查找当前错题记录
QueryWrapper<UserBook> wrapper = new QueryWrapper<>();
wrapper.lambda()
.eq(UserBook::getUserId, UserUtils.getUserId())
.eq(UserBook::getExamId, examId)
.eq(UserBook::getQuId, quId);
// 按排序值降序排序
wrapper.last(" ORDER BY `sort` DESC");
// 获取当前错题记录
UserBook last = this.getOne(wrapper, false);
if (last != null) {
// 如果找到当前错题记录,获取其排序值
sort = last.getSort();
}
}
// 构建查询条件,查找排序值小于当前错题的下一个错题记录
QueryWrapper<UserBook> wrapper = new QueryWrapper<>();
wrapper.lambda()
.eq(UserBook::getUserId, UserUtils.getUserId())
.eq(UserBook::getExamId, examId)
.lt(UserBook::getSort, sort);
// 按排序值降序排序
wrapper.last(" ORDER BY `sort` DESC");
// 获取下一个错题记录
UserBook next = this.getOne(wrapper, false);
if (next != null) {
// 如果找到下一个错题记录,返回其题目 ID
return next.getQuId();
}
return null;
}
/**
*
* @param examId ID
* @param userId ID
* @return 0
*/
private Integer findMaxSort(String examId, String userId) {
// 构建查询条件,查找指定考试中用户的错题记录
QueryWrapper<UserBook> wrapper = new QueryWrapper<>();
wrapper.lambda()
.eq(UserBook::getExamId, examId)
.eq(UserBook::getUserId, userId);
// 按排序值降序排序
wrapper.last(" ORDER BY `sort` DESC");
// 获取排序值最大的错题记录
UserBook book = this.getOne(wrapper, false);
if (book == null) {
// 如果没有记录,返回 0
return 0;
}
// 返回最大排序值
return book.getSort();
}
}

@ -0,0 +1,65 @@
package com.yf.exam.modules.user.exam.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.yf.exam.core.api.ApiRest;
import com.yf.exam.core.api.controller.BaseController;
import com.yf.exam.core.api.dto.PagingReqDTO;
import com.yf.exam.modules.user.exam.dto.request.UserExamReqDTO;
import com.yf.exam.modules.user.exam.dto.response.UserExamRespDTO;
import com.yf.exam.modules.user.exam.service.UserExamService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
* <p>
* HTTP
*
* </p>
*
* @author
* @since 2020-09-21 15:13
*/
@Api(tags={"考试记录"})
@RestController
@RequestMapping("/exam/api/user/exam")
public class UserExamController extends BaseController {
/**
*
*/
@Autowired
private UserExamService baseService;
/**
*
* @param reqDTO UserExamReqDTO
* @return UserExamRespDTO
*/
@ApiOperation(value = "分页查找")
@RequestMapping(value = "/paging", method = { RequestMethod.POST})
public ApiRest<IPage<UserExamRespDTO>> paging(@RequestBody PagingReqDTO<UserExamReqDTO> reqDTO) {
// 调用服务层的分页查询方法,获取分页后的考试记录
IPage<UserExamRespDTO> page = baseService.paging(reqDTO);
// 返回包含分页结果的成功响应
return super.success(page);
}
/**
*
* @param reqDTO UserExamReqDTO
* @return UserExamRespDTO
*/
@ApiOperation(value = "分页查找")
@RequestMapping(value = "/my-paging", method = { RequestMethod.POST})
public ApiRest<IPage<UserExamRespDTO>> myPaging(@RequestBody PagingReqDTO<UserExamReqDTO> reqDTO) {
// 调用服务层的分页查询方法,获取当前用户分页后的考试记录
IPage<UserExamRespDTO> page = baseService.myPaging(reqDTO);
// 返回包含分页结果的成功响应
return super.success(page);
}
}

@ -0,0 +1,84 @@
// 声明该类所在的包,此包用于存放用户考试相关的数据传输对象
package com.yf.exam.modules.user.exam.dto;
// 导入自定义的字典注解,用于处理数据字典映射
import com.yf.exam.core.annon.Dict;
// 导入 Swagger 相关注解,用于生成 API 文档
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
// 导入 Lombok 的 Data 注解,自动生成 getter、setter、equals、hashCode 和 toString 方法
import lombok.Data;
// 导入日期类,用于表示时间相关的属性
import java.util.Date;
// 导入序列化接口,使该类的对象可以被序列化和反序列化
import java.io.Serializable;
/**
* <p>
* 访
* IDID
* </p>
*
* @author
* @since 2020-09-21 15:13
*/
// 使用 Lombok 的 Data 注解,简化代码,自动生成常用方法
@Data
// 使用 Swagger 的 ApiModel 注解,为 API 文档提供该类的描述信息
@ApiModel(value="考试记录", description="考试记录")
public class UserExamDTO implements Serializable {
// 序列化版本号,用于保证序列化和反序列化过程中类的版本一致性,避免版本不一致导致的错误
private static final long serialVersionUID = 1L;
/**
* ID
*/
private String id;
/**
* ID
*/
@ApiModelProperty(value = "用户ID", required=true)
private String userId;
/**
* ID
* @Dict `el_exam` `id` `title`
* 便
*/
@Dict(dictTable = "el_exam", dicText = "title", dicCode = "id")
@ApiModelProperty(value = "考试ID", required=true)
private String examId;
/**
*
*/
@ApiModelProperty(value = "考试次数", required=true)
private Integer tryCount;
/**
*
*/
@ApiModelProperty(value = "最高分数", required=true)
private Integer maxScore;
/**
*
*/
@ApiModelProperty(value = "是否通过", required=true)
private Boolean passed;
/**
*
*/
@ApiModelProperty(value = "创建时间")
private Date createTime;
/**
*
*/
@ApiModelProperty(value = "更新时间")
private Date updateTime;
}

@ -0,0 +1,42 @@
package com.yf.exam.modules.user.exam.dto.request;
// 导入 UserExamDTO 类,作为当前类的父类
import com.yf.exam.modules.user.exam.dto.UserExamDTO;
// 导入 Swagger 注解,用于 API 文档生成
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
// 导入 Lombok 的 Data 注解,自动生成 getter、setter、toString 等方法
import lombok.Data;
/**
* <p>
*
* UserExamDTO
* </p>
*
* @author
* @since 2020-09-21 15:13
*/
// 使用 Lombok 的 Data 注解,自动生成常用方法
@Data
// 使用 Swagger 的 ApiModel 注解,为 API 文档提供模型描述
@ApiModel(value="考试记录", description="考试记录")
public class UserExamReqDTO extends UserExamDTO {
// 序列化版本号,用于保证序列化和反序列化过程中类的版本一致性
private static final long serialVersionUID = 1L;
/**
*
*/
// 使用 Swagger 的 ApiModelProperty 注解,为 API 文档描述该属性
@ApiModelProperty(value = "考试名称", required=true)
private String title;
/**
*
*/
// 使用 Swagger 的 ApiModelProperty 注解,为 API 文档描述该属性
@ApiModelProperty(value = "人员名称", required=true)
private String realName;
}

@ -0,0 +1,36 @@
package com.yf.exam.modules.user.exam.dto.response;
import com.yf.exam.modules.user.exam.dto.UserExamDTO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* <p>
*
* UserExamDTO
* </p>
*
* @author
* @since 2020-09-21 15:13
*/
@Data
@ApiModel(value="考试记录", description="考试记录")
public class UserExamRespDTO extends UserExamDTO {
// 序列化版本号,用于保证序列化和反序列化过程中类的版本一致性
private static final long serialVersionUID = 1L;
/**
*
*/
@ApiModelProperty(value = "考试名称", required=true)
private String title;
/**
*
*/
@ApiModelProperty(value = "人员名称", required=true)
private String realName;
}

@ -0,0 +1,85 @@
package com.yf.exam.modules.user.exam.entity;
// 导入 MyBatis-Plus 主键类型注解,用于指定主键生成策略
import com.baomidou.mybatisplus.annotation.IdType;
// 导入 MyBatis-Plus 表字段注解,用于指定实体类属性与数据库表字段的映射关系
import com.baomidou.mybatisplus.annotation.TableField;
// 导入 MyBatis-Plus 表主键注解,用于指定实体类的主键属性
import com.baomidou.mybatisplus.annotation.TableId;
// 导入 MyBatis-Plus 表名注解,用于指定实体类对应的数据库表名
import com.baomidou.mybatisplus.annotation.TableName;
// 导入 MyBatis-Plus 活动记录模型类,实体类继承该类可使用活动记录模式操作数据库
import com.baomidou.mybatisplus.extension.activerecord.Model;
// 导入 Lombok 的 Data 注解,自动生成 getter、setter、equals、hashCode 和 toString 方法
import lombok.Data;
// 导入日期类,用于表示时间相关的属性
import java.util.Date;
/**
* <p>
* `el_user_exam`
* MyBatis-Plus Model 使
* </p>
*
* @author
* @since 2020-09-21 15:13
*/
// 使用 Lombok 的 Data 注解,简化代码,自动生成常用方法
@Data
// 使用 MyBatis-Plus 的 TableName 注解,指定该实体类对应的数据库表名为 `el_user_exam`
@TableName("el_user_exam")
public class UserExam extends Model<UserExam> {
// 序列化版本号,用于保证序列化和反序列化过程中类的版本一致性
private static final long serialVersionUID = 1L;
/**
* ID `id`
* 使 MyBatis-Plus TableId ASSIGN_ID ID
*/
@TableId(value = "id", type = IdType.ASSIGN_ID)
private String id;
/**
* ID `user_id`
*/
@TableField("user_id")
private String userId;
/**
* ID `exam_id`
*/
@TableField("exam_id")
private String examId;
/**
* `try_count`
*/
@TableField("try_count")
private Integer tryCount;
/**
* `max_score`
*/
@TableField("max_score")
private Integer maxScore;
/**
*
*
*/
private Boolean passed;
/**
* `create_time`
*/
@TableField("create_time")
private Date createTime;
/**
* `update_time`
*/
@TableField("update_time")
private Date updateTime;
}

@ -0,0 +1,37 @@
package com.yf.exam.modules.user.exam.mapper;
// 导入 MyBatis-Plus 基础 Mapper 接口,提供基本的数据库 CRUD 操作
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
// 导入 MyBatis-Plus 分页查询结果接口,用于封装分页查询结果
import com.baomidou.mybatisplus.core.metadata.IPage;
// 导入 MyBatis-Plus 分页对象,用于设置分页查询的参数
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
// 导入考试记录请求数据传输对象,用于封装查询条件
import com.yf.exam.modules.user.exam.dto.request.UserExamReqDTO;
// 导入考试记录响应数据传输对象,用于封装查询结果
import com.yf.exam.modules.user.exam.dto.response.UserExamRespDTO;
// 导入考试记录实体类,对应数据库中的考试记录表
import com.yf.exam.modules.user.exam.entity.UserExam;
// 导入 MyBatis 参数注解,用于在 SQL 语句中引用方法参数
import org.apache.ibatis.annotations.Param;
/**
* <p>
* Mapper
* MyBatis-Plus BaseMapper 使
* </p>
*
* @author
* @since 2020-09-21 15:13
*/
public interface UserExamMapper extends BaseMapper<UserExam> {
/**
*
* @param page
* @param query
* @return IPage
*/
IPage<UserExamRespDTO> paging(Page page, @Param("query") UserExamReqDTO query);
}

@ -0,0 +1,50 @@
package com.yf.exam.modules.user.exam.service;
// 导入 MyBatis-Plus 分页元数据接口,用于封装分页查询结果
import com.baomidou.mybatisplus.core.metadata.IPage;
// 导入 MyBatis-Plus 扩展服务接口,提供基础的 CRUD 操作
import com.baomidou.mybatisplus.extension.service.IService;
// 导入自定义的分页请求数据传输对象,用于封装分页查询的请求参数
import com.yf.exam.core.api.dto.PagingReqDTO;
// 导入考试记录请求数据传输对象,用于封装考试记录查询的具体条件
import com.yf.exam.modules.user.exam.dto.request.UserExamReqDTO;
// 导入考试记录响应数据传输对象,用于封装考试记录查询的返回结果
import com.yf.exam.modules.user.exam.dto.response.UserExamRespDTO;
// 导入考试记录实体类,对应数据库中的考试记录数据
import com.yf.exam.modules.user.exam.entity.UserExam;
/**
* <p>
*
* MyBatis-Plus IService 使
* </p>
*
* @author
* @since 2020-09-21 15:13
*/
public interface UserExamService extends IService<UserExam> {
/**
*
* @param reqDTO UserExamReqDTO
* @return IPage
*/
IPage<UserExamRespDTO> paging(PagingReqDTO<UserExamReqDTO> reqDTO);
/**
*
* @param reqDTO UserExamReqDTO
* @return IPage
*/
IPage<UserExamRespDTO> myPaging(PagingReqDTO<UserExamReqDTO> reqDTO);
/**
*
*
* @param userId ID
* @param examId ID
* @param score
* @param passed
*/
void joinResult(String userId, String examId, Integer score, boolean passed);
}

@ -0,0 +1,116 @@
package com.yf.exam.modules.user.exam.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yf.exam.core.api.dto.PagingReqDTO;
import com.yf.exam.modules.user.UserUtils;
import com.yf.exam.modules.user.exam.dto.request.UserExamReqDTO;
import com.yf.exam.modules.user.exam.dto.response.UserExamRespDTO;
import com.yf.exam.modules.user.exam.entity.UserExam;
import com.yf.exam.modules.user.exam.mapper.UserExamMapper;
import com.yf.exam.modules.user.exam.service.UserExamService;
import org.springframework.stereotype.Service;
import java.util.Date;
/**
* <p>
* UserExamService
* MyBatis-Plus ServiceImpl 使
* </p>
*
* @author
* @since 2020-09-21 15:13
*/
@Service
public class UserExamServiceImpl extends ServiceImpl<UserExamMapper, UserExam> implements UserExamService {
/**
*
* @param reqDTO UserExamReqDTO
* @return UserExamRespDTO IPage
*/
@Override
public IPage<UserExamRespDTO> paging(PagingReqDTO<UserExamReqDTO> reqDTO) {
// 调用 Mapper 层的分页查询方法,将请求的分页信息和查询参数传入
// 并将查询结果转换为包含 UserExamRespDTO 的分页数据
IPage<UserExamRespDTO> pageData = baseMapper.paging(reqDTO.toPage(), reqDTO.getParams());
return pageData;
}
/**
*
* @param reqDTO UserExamReqDTO
* @return UserExamRespDTO IPage
*/
@Override
public IPage<UserExamRespDTO> myPaging(PagingReqDTO<UserExamReqDTO> reqDTO) {
// 获取请求中的查询参数
UserExamReqDTO params = reqDTO.getParams();
// 若查询参数为空,则创建一个新的 UserExamReqDTO 对象
if(params == null){
params = new UserExamReqDTO();
}
// 设置当前用户的 ID 到查询参数中
params.setUserId(UserUtils.getUserId());
// 调用 Mapper 层的分页查询方法,将请求的分页信息和更新后的查询参数传入
// 并将查询结果转换为包含 UserExamRespDTO 的分页数据
IPage<UserExamRespDTO> pageData = baseMapper.paging(reqDTO.toPage(), params);
return pageData;
}
/**
*
* @param userId ID
* @param examId ID
* @param score
* @param passed
*/
@Override
public void joinResult(String userId, String examId, Integer score, boolean passed) {
// 构建查询条件,根据用户 ID 和考试 ID 查询考试记录
QueryWrapper<UserExam> wrapper = new QueryWrapper<>();
wrapper.lambda().eq(UserExam::getUserId, userId)
.eq(UserExam::getExamId, examId);
// 根据查询条件获取一条考试记录
UserExam record = this.getOne(wrapper, false);
if(record == null){
// 若记录不存在,则创建一条新的考试记录
record = new UserExam();
// 设置记录创建时间
record.setCreateTime(new Date());
// 设置记录更新时间
record.setUpdateTime(new Date());
// 设置用户 ID
record.setUserId(userId);
// 设置考试 ID
record.setExamId(examId);
// 设置最高分数为本次考试得分
record.setMaxScore(score);
// 设置是否通过考试
record.setPassed(passed);
// 保存新的考试记录
this.save(record);
return;
}
// 修复低分数不加入统计问题,增加考试次数
record.setTryCount(record.getTryCount() + 1);
// 更新记录更新时间
record.setUpdateTime(new Date());
// 若本次考试得分高于之前的最高分数,则更新最高分数和是否通过考试状态
if(record.getMaxScore() < score){
record.setMaxScore(score);
record.setPassed(passed);
}
// 根据记录 ID 更新考试记录
this.updateById(record);
}
}
Loading…
Cancel
Save