Compare commits
2 Commits
459fa434ac
...
673214481d
| Author | SHA1 | Date | |
|---|---|---|---|
| 673214481d | |||
| d57affd2ee |
@ -47,7 +47,7 @@ model SchoolCourse {
|
|||||||
createdBy Int @map("created_by")
|
createdBy Int @map("created_by")
|
||||||
changesSummary String? @map("changes_summary") // 修改说明
|
changesSummary String? @map("changes_summary") // 修改说明
|
||||||
usageCount Int @default(0) @map("usage_count")
|
usageCount Int @default(0) @map("usage_count")
|
||||||
status String @default("ACTIVE") // ACTIVE, PENDING_REVIEW, REJECTED
|
status String @default("ACTIVE") // ACTIVE, PENDING, REJECTED
|
||||||
|
|
||||||
createdAt DateTime @default(now()) @map("created_at")
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
updatedAt DateTime @updatedAt @map("updated_at")
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|||||||
@ -82,7 +82,7 @@ PUT /api/v1/admin/packages/{id}/courses
|
|||||||
POST /api/v1/admin/packages/{id}/submit
|
POST /api/v1/admin/packages/{id}/submit
|
||||||
```
|
```
|
||||||
|
|
||||||
✅ 提交成功,状态变为 `PENDING_REVIEW`
|
✅ 提交成功,状态变为 `PENDING`
|
||||||
|
|
||||||
#### 4. 审核通过
|
#### 4. 审核通过
|
||||||
|
|
||||||
|
|||||||
@ -241,7 +241,7 @@ export class CoursePackageService {
|
|||||||
return this.prisma.coursePackage.update({
|
return this.prisma.coursePackage.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: {
|
data: {
|
||||||
status: 'PENDING_REVIEW',
|
status: 'PENDING',
|
||||||
submittedAt: new Date(),
|
submittedAt: new Date(),
|
||||||
submittedBy: userId,
|
submittedBy: userId,
|
||||||
},
|
},
|
||||||
@ -262,7 +262,7 @@ export class CoursePackageService {
|
|||||||
throw new Error('套餐不存在');
|
throw new Error('套餐不存在');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pkg.status !== 'PENDING_REVIEW') {
|
if (pkg.status !== 'PENDING') {
|
||||||
throw new Error('只有待审核状态的套餐可以审核');
|
throw new Error('只有待审核状态的套餐可以审核');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -34,7 +34,7 @@ export interface CoursePackage {
|
|||||||
gradeLevels?: string;
|
gradeLevels?: string;
|
||||||
/** 课程数量 */
|
/** 课程数量 */
|
||||||
courseCount?: number;
|
courseCount?: number;
|
||||||
/** 状态:DRAFT、PENDING_REVIEW、APPROVED、REJECTED、PUBLISHED、OFFLINE */
|
/** 状态:DRAFT、PENDING、APPROVED、REJECTED、PUBLISHED、OFFLINE */
|
||||||
status?: string;
|
status?: string;
|
||||||
/** 提交时间 */
|
/** 提交时间 */
|
||||||
submittedAt?: string;
|
submittedAt?: string;
|
||||||
|
|||||||
@ -97,7 +97,7 @@ export function submitPackage(id: number | string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 审核套餐
|
// 审核套餐
|
||||||
export function reviewPackage(id: number | string, data: { approved: boolean; comment?: string }) {
|
export function reviewPackage(id: number | string, data: { approved: boolean; comment?: string; publish?: boolean }) {
|
||||||
return http.post(`/v1/admin/packages/${id}/review`, data);
|
return http.post(`/v1/admin/packages/${id}/review`, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -11,10 +11,8 @@ declare module 'vue' {
|
|||||||
AAvatar: typeof import('ant-design-vue/es')['Avatar']
|
AAvatar: typeof import('ant-design-vue/es')['Avatar']
|
||||||
ABadge: typeof import('ant-design-vue/es')['Badge']
|
ABadge: typeof import('ant-design-vue/es')['Badge']
|
||||||
AButton: typeof import('ant-design-vue/es')['Button']
|
AButton: typeof import('ant-design-vue/es')['Button']
|
||||||
AButtonGroup: typeof import('ant-design-vue/es')['ButtonGroup']
|
|
||||||
ACard: typeof import('ant-design-vue/es')['Card']
|
ACard: typeof import('ant-design-vue/es')['Card']
|
||||||
ACheckbox: typeof import('ant-design-vue/es')['Checkbox']
|
ACheckbox: typeof import('ant-design-vue/es')['Checkbox']
|
||||||
ACheckboxGroup: typeof import('ant-design-vue/es')['CheckboxGroup']
|
|
||||||
ACol: typeof import('ant-design-vue/es')['Col']
|
ACol: typeof import('ant-design-vue/es')['Col']
|
||||||
ADatePicker: typeof import('ant-design-vue/es')['DatePicker']
|
ADatePicker: typeof import('ant-design-vue/es')['DatePicker']
|
||||||
ADescriptions: typeof import('ant-design-vue/es')['Descriptions']
|
ADescriptions: typeof import('ant-design-vue/es')['Descriptions']
|
||||||
@ -25,8 +23,6 @@ declare module 'vue' {
|
|||||||
AEmpty: typeof import('ant-design-vue/es')['Empty']
|
AEmpty: typeof import('ant-design-vue/es')['Empty']
|
||||||
AForm: typeof import('ant-design-vue/es')['Form']
|
AForm: typeof import('ant-design-vue/es')['Form']
|
||||||
AFormItem: typeof import('ant-design-vue/es')['FormItem']
|
AFormItem: typeof import('ant-design-vue/es')['FormItem']
|
||||||
AImage: typeof import('ant-design-vue/es')['Image']
|
|
||||||
AImagePreviewGroup: typeof import('ant-design-vue/es')['ImagePreviewGroup']
|
|
||||||
AInput: typeof import('ant-design-vue/es')['Input']
|
AInput: typeof import('ant-design-vue/es')['Input']
|
||||||
AInputNumber: typeof import('ant-design-vue/es')['InputNumber']
|
AInputNumber: typeof import('ant-design-vue/es')['InputNumber']
|
||||||
AInputPassword: typeof import('ant-design-vue/es')['InputPassword']
|
AInputPassword: typeof import('ant-design-vue/es')['InputPassword']
|
||||||
@ -52,14 +48,11 @@ declare module 'vue' {
|
|||||||
ARate: typeof import('ant-design-vue/es')['Rate']
|
ARate: typeof import('ant-design-vue/es')['Rate']
|
||||||
ARow: typeof import('ant-design-vue/es')['Row']
|
ARow: typeof import('ant-design-vue/es')['Row']
|
||||||
ASelect: typeof import('ant-design-vue/es')['Select']
|
ASelect: typeof import('ant-design-vue/es')['Select']
|
||||||
ASelectOptGroup: typeof import('ant-design-vue/es')['SelectOptGroup']
|
|
||||||
ASelectOption: typeof import('ant-design-vue/es')['SelectOption']
|
ASelectOption: typeof import('ant-design-vue/es')['SelectOption']
|
||||||
ASkeleton: typeof import('ant-design-vue/es')['Skeleton']
|
ASkeleton: typeof import('ant-design-vue/es')['Skeleton']
|
||||||
ASpace: typeof import('ant-design-vue/es')['Space']
|
ASpace: typeof import('ant-design-vue/es')['Space']
|
||||||
ASpin: typeof import('ant-design-vue/es')['Spin']
|
ASpin: typeof import('ant-design-vue/es')['Spin']
|
||||||
AStatistic: typeof import('ant-design-vue/es')['Statistic']
|
AStatistic: typeof import('ant-design-vue/es')['Statistic']
|
||||||
AStep: typeof import('ant-design-vue/es')['Step']
|
|
||||||
ASteps: typeof import('ant-design-vue/es')['Steps']
|
|
||||||
ASubMenu: typeof import('ant-design-vue/es')['SubMenu']
|
ASubMenu: typeof import('ant-design-vue/es')['SubMenu']
|
||||||
ASwitch: typeof import('ant-design-vue/es')['Switch']
|
ASwitch: typeof import('ant-design-vue/es')['Switch']
|
||||||
ATable: typeof import('ant-design-vue/es')['Table']
|
ATable: typeof import('ant-design-vue/es')['Table']
|
||||||
|
|||||||
@ -84,7 +84,7 @@ const courseColumns = [
|
|||||||
|
|
||||||
const statusColors: Record<string, string> = {
|
const statusColors: Record<string, string> = {
|
||||||
DRAFT: 'default',
|
DRAFT: 'default',
|
||||||
PENDING_REVIEW: 'processing',
|
PENDING: 'processing',
|
||||||
APPROVED: 'success',
|
APPROVED: 'success',
|
||||||
REJECTED: 'error',
|
REJECTED: 'error',
|
||||||
PUBLISHED: 'blue',
|
PUBLISHED: 'blue',
|
||||||
@ -93,7 +93,7 @@ const statusColors: Record<string, string> = {
|
|||||||
|
|
||||||
const statusTexts: Record<string, string> = {
|
const statusTexts: Record<string, string> = {
|
||||||
DRAFT: '草稿',
|
DRAFT: '草稿',
|
||||||
PENDING_REVIEW: '待审核',
|
PENDING: '待审核',
|
||||||
APPROVED: '已通过',
|
APPROVED: '已通过',
|
||||||
REJECTED: '已拒绝',
|
REJECTED: '已拒绝',
|
||||||
PUBLISHED: '已发布',
|
PUBLISHED: '已发布',
|
||||||
|
|||||||
@ -27,7 +27,7 @@
|
|||||||
@change="fetchData"
|
@change="fetchData"
|
||||||
>
|
>
|
||||||
<a-select-option value="DRAFT">草稿</a-select-option>
|
<a-select-option value="DRAFT">草稿</a-select-option>
|
||||||
<a-select-option value="PENDING_REVIEW">待审核</a-select-option>
|
<a-select-option value="PENDING">待审核</a-select-option>
|
||||||
<a-select-option value="APPROVED">已通过</a-select-option>
|
<a-select-option value="APPROVED">已通过</a-select-option>
|
||||||
<a-select-option value="REJECTED">已拒绝</a-select-option>
|
<a-select-option value="REJECTED">已拒绝</a-select-option>
|
||||||
<a-select-option value="PUBLISHED">已发布</a-select-option>
|
<a-select-option value="PUBLISHED">已发布</a-select-option>
|
||||||
@ -81,7 +81,7 @@
|
|||||||
<a-button
|
<a-button
|
||||||
type="link"
|
type="link"
|
||||||
size="small"
|
size="small"
|
||||||
v-if="record.status === 'PENDING_REVIEW'"
|
v-if="record.status === 'PENDING'"
|
||||||
@click="handleReview(record)"
|
@click="handleReview(record)"
|
||||||
>
|
>
|
||||||
审核
|
审核
|
||||||
@ -143,7 +143,7 @@ const columns = [
|
|||||||
|
|
||||||
const statusColors: Record<string, string> = {
|
const statusColors: Record<string, string> = {
|
||||||
DRAFT: 'default',
|
DRAFT: 'default',
|
||||||
PENDING_REVIEW: 'processing',
|
PENDING: 'processing',
|
||||||
APPROVED: 'success',
|
APPROVED: 'success',
|
||||||
REJECTED: 'error',
|
REJECTED: 'error',
|
||||||
PUBLISHED: 'blue',
|
PUBLISHED: 'blue',
|
||||||
@ -152,7 +152,7 @@ const statusColors: Record<string, string> = {
|
|||||||
|
|
||||||
const statusTexts: Record<string, string> = {
|
const statusTexts: Record<string, string> = {
|
||||||
DRAFT: '草稿',
|
DRAFT: '草稿',
|
||||||
PENDING_REVIEW: '待审核',
|
PENDING: '待审核',
|
||||||
APPROVED: '已通过',
|
APPROVED: '已通过',
|
||||||
REJECTED: '已拒绝',
|
REJECTED: '已拒绝',
|
||||||
PUBLISHED: '已发布',
|
PUBLISHED: '已发布',
|
||||||
@ -183,7 +183,7 @@ const fetchData = async () => {
|
|||||||
pagination.total = res.total || 0;
|
pagination.total = res.total || 0;
|
||||||
// 获取待审核数量
|
// 获取待审核数量
|
||||||
try {
|
try {
|
||||||
const pendingRes = await getPackageList({ status: 'PENDING_REVIEW', pageNum: 1, pageSize: 1 }) as any;
|
const pendingRes = await getPackageList({ status: 'PENDING', pageNum: 1, pageSize: 1 }) as any;
|
||||||
pendingCount.value = pendingRes.total || 0;
|
pendingCount.value = pendingRes.total || 0;
|
||||||
} catch {
|
} catch {
|
||||||
pendingCount.value = 0;
|
pendingCount.value = 0;
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
<template #extra>
|
<template #extra>
|
||||||
<a-space>
|
<a-space>
|
||||||
<a-select v-model:value="filters.status" placeholder="全部状态" style="width: 120px" @change="fetchPackages">
|
<a-select v-model:value="filters.status" placeholder="全部状态" style="width: 120px" @change="fetchPackages">
|
||||||
<a-select-option value="PENDING_REVIEW">待审核</a-select-option>
|
<a-select-option value="PENDING">待审核</a-select-option>
|
||||||
<a-select-option value="REJECTED">已驳回</a-select-option>
|
<a-select-option value="REJECTED">已驳回</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
<a-button @click="fetchPackages">
|
<a-button @click="fetchPackages">
|
||||||
@ -39,8 +39,8 @@
|
|||||||
</a-tag>
|
</a-tag>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="column.key === 'status'">
|
<template v-else-if="column.key === 'status'">
|
||||||
<a-tag :color="record.status === 'PENDING_REVIEW' ? 'processing' : 'error'">
|
<a-tag :color="record.status === 'PENDING' ? 'processing' : 'error'">
|
||||||
{{ record.status === 'PENDING_REVIEW' ? '待审核' : '已驳回' }}
|
{{ record.status === 'PENDING' ? '待审核' : '已驳回' }}
|
||||||
</a-tag>
|
</a-tag>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="column.key === 'submittedAt'">
|
<template v-else-if="column.key === 'submittedAt'">
|
||||||
@ -48,7 +48,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<template v-else-if="column.key === 'actions'">
|
<template v-else-if="column.key === 'actions'">
|
||||||
<a-space>
|
<a-space>
|
||||||
<a-button v-if="record.status === 'PENDING_REVIEW'" type="primary" size="small" @click="showReviewModal(record)">
|
<a-button v-if="record.status === 'PENDING'" type="primary" size="small" @click="showReviewModal(record)">
|
||||||
审核
|
审核
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button v-if="record.status === 'REJECTED'" size="small" @click="viewRejectReason(record)">
|
<a-button v-if="record.status === 'REJECTED'" size="small" @click="viewRejectReason(record)">
|
||||||
@ -107,12 +107,15 @@
|
|||||||
</a-form>
|
</a-form>
|
||||||
|
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<a-space v-if="currentPackage.status === 'PENDING_REVIEW'">
|
<a-space v-if="currentPackage.status === 'PENDING'">
|
||||||
<a-button @click="closeReviewModal">取消</a-button>
|
<a-button @click="closeReviewModal">取消</a-button>
|
||||||
<a-button type="default" danger :loading="reviewing" @click="rejectPackage">
|
<a-button type="default" danger :loading="reviewing" @click="rejectPackage">
|
||||||
驳回
|
驳回
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button type="primary" :loading="reviewing" @click="approvePackage">
|
<a-button :loading="reviewing" @click="approveOnly">
|
||||||
|
通过
|
||||||
|
</a-button>
|
||||||
|
<a-button type="primary" :loading="reviewing" @click="approveAndPublish">
|
||||||
通过并发布
|
通过并发布
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-space>
|
</a-space>
|
||||||
@ -144,7 +147,7 @@ const loadingDetail = ref(false);
|
|||||||
const packages = ref<CoursePackage[]>([]);
|
const packages = ref<CoursePackage[]>([]);
|
||||||
|
|
||||||
const filters = reactive<{ status?: string }>({
|
const filters = reactive<{ status?: string }>({
|
||||||
status: 'PENDING_REVIEW',
|
status: 'PENDING',
|
||||||
});
|
});
|
||||||
|
|
||||||
const pagination = reactive({
|
const pagination = reactive({
|
||||||
@ -227,7 +230,8 @@ const closeReviewModal = () => {
|
|||||||
currentPackage.value = null;
|
currentPackage.value = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const approvePackage = async () => {
|
// 仅通过
|
||||||
|
const approveOnly = async () => {
|
||||||
if (!currentPackage.value) return;
|
if (!currentPackage.value) return;
|
||||||
|
|
||||||
reviewing.value = true;
|
reviewing.value = true;
|
||||||
@ -235,6 +239,28 @@ const approvePackage = async () => {
|
|||||||
await reviewPackage(currentPackage.value.id, {
|
await reviewPackage(currentPackage.value.id, {
|
||||||
approved: true,
|
approved: true,
|
||||||
comment: reviewComment.value || '审核通过',
|
comment: reviewComment.value || '审核通过',
|
||||||
|
publish: false,
|
||||||
|
});
|
||||||
|
message.success('审核通过');
|
||||||
|
closeReviewModal();
|
||||||
|
fetchPackages();
|
||||||
|
} catch (error: any) {
|
||||||
|
message.error(error.response?.data?.message || '审核失败');
|
||||||
|
} finally {
|
||||||
|
reviewing.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 通过并发布
|
||||||
|
const approveAndPublish = async () => {
|
||||||
|
if (!currentPackage.value) return;
|
||||||
|
|
||||||
|
reviewing.value = true;
|
||||||
|
try {
|
||||||
|
await reviewPackage(currentPackage.value.id, {
|
||||||
|
approved: true,
|
||||||
|
comment: reviewComment.value || '审核通过',
|
||||||
|
publish: true,
|
||||||
});
|
});
|
||||||
message.success('审核通过,套餐已发布');
|
message.success('审核通过,套餐已发布');
|
||||||
closeReviewModal();
|
closeReviewModal();
|
||||||
|
|||||||
@ -0,0 +1,58 @@
|
|||||||
|
package com.reading.platform.common.enums;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是/否枚举
|
||||||
|
* 用于替代数据库中的 0/1 魔法值
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public enum YesNo {
|
||||||
|
|
||||||
|
NO(0, "否"),
|
||||||
|
YES(1, "是");
|
||||||
|
|
||||||
|
private final Integer code;
|
||||||
|
private final String description;
|
||||||
|
|
||||||
|
YesNo(Integer code, String description) {
|
||||||
|
this.code = code;
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 code 获取枚举
|
||||||
|
*/
|
||||||
|
public static YesNo fromCode(Integer code) {
|
||||||
|
if (code == null) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
for (YesNo value : values()) {
|
||||||
|
if (value.getCode().equals(code)) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 Boolean 获取枚举
|
||||||
|
*/
|
||||||
|
public static YesNo fromBoolean(Boolean bool) {
|
||||||
|
return Boolean.TRUE.equals(bool) ? YES : NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否为"是"
|
||||||
|
*/
|
||||||
|
public static boolean isYes(Integer code) {
|
||||||
|
return YES.getCode().equals(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否为"否"
|
||||||
|
*/
|
||||||
|
public static boolean isNo(Integer code) {
|
||||||
|
return NO.getCode().equals(code);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -18,6 +18,9 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 课程管理控制器(超管端)
|
* 课程管理控制器(超管端)
|
||||||
*/
|
*/
|
||||||
@ -33,12 +36,12 @@ public class AdminCourseController {
|
|||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
@Operation(summary = "创建课程")
|
@Operation(summary = "创建课程")
|
||||||
public Result<Course> createCourse(@Valid @RequestBody CourseCreateRequest request) {
|
public Result<CourseResponse> createCourse(@Valid @RequestBody CourseCreateRequest request) {
|
||||||
log.info("收到课程创建请求,name={}, themeId={}, gradeTags={}", request.getName(), request.getThemeId(), request.getGradeTags());
|
log.info("收到课程创建请求,name={}, themeId={}, gradeTags={}", request.getName(), request.getThemeId(), request.getGradeTags());
|
||||||
try {
|
try {
|
||||||
Course course = courseService.createSystemCourse(request);
|
Course course = courseService.createSystemCourse(request);
|
||||||
log.info("课程创建成功,id={}", course.getId());
|
log.info("课程创建成功,id={}", course.getId());
|
||||||
return Result.success(course);
|
return Result.success(courseService.getCourseByIdWithLessons(course.getId()));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("课程创建失败", e);
|
log.error("课程创建失败", e);
|
||||||
throw e;
|
throw e;
|
||||||
@ -47,8 +50,9 @@ public class AdminCourseController {
|
|||||||
|
|
||||||
@PutMapping("/{id}")
|
@PutMapping("/{id}")
|
||||||
@Operation(summary = "更新课程")
|
@Operation(summary = "更新课程")
|
||||||
public Result<Course> updateCourse(@PathVariable Long id, @RequestBody CourseUpdateRequest request) {
|
public Result<CourseResponse> updateCourse(@PathVariable Long id, @RequestBody CourseUpdateRequest request) {
|
||||||
return Result.success(courseService.updateCourse(id, request));
|
courseService.updateCourse(id, request);
|
||||||
|
return Result.success(courseService.getCourseByIdWithLessons(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/{id}")
|
@GetMapping("/{id}")
|
||||||
@ -59,7 +63,7 @@ public class AdminCourseController {
|
|||||||
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
@Operation(summary = "分页查询课程")
|
@Operation(summary = "分页查询课程")
|
||||||
public Result<PageResult<Course>> getCoursePage(CoursePageQueryRequest request) {
|
public Result<PageResult<CourseResponse>> getCoursePage(CoursePageQueryRequest request) {
|
||||||
log.info("查询课程列表,pageNum={}, pageSize={}, keyword={}, category={}, status={}, reviewOnly={}",
|
log.info("查询课程列表,pageNum={}, pageSize={}, keyword={}, category={}, status={}, reviewOnly={}",
|
||||||
request.getPageNum(), request.getPageSize(), request.getKeyword(), request.getCategory(), request.getStatus(), request.getReviewOnly());
|
request.getPageNum(), request.getPageSize(), request.getKeyword(), request.getCategory(), request.getStatus(), request.getReviewOnly());
|
||||||
// 页码
|
// 页码
|
||||||
@ -75,7 +79,13 @@ public class AdminCourseController {
|
|||||||
request.getCategory(),
|
request.getCategory(),
|
||||||
request.getStatus(),
|
request.getStatus(),
|
||||||
request.getReviewOnly());
|
request.getReviewOnly());
|
||||||
PageResult<Course> result = PageResult.of(page);
|
|
||||||
|
// 转换为 CourseResponse
|
||||||
|
List<CourseResponse> responseList = page.getRecords().stream()
|
||||||
|
.map(course -> courseService.getCourseByIdWithLessons(course.getId()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
PageResult<CourseResponse> result = PageResult.of(responseList, page.getTotal(), page.getCurrent(), page.getSize());
|
||||||
log.info("课程列表查询结果,total={}, list={}", result.getTotal(), result.getList().size());
|
log.info("课程列表查询结果,total={}, list={}", result.getTotal(), result.getList().size());
|
||||||
return Result.success(result);
|
return Result.success(result);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,9 @@ import com.reading.platform.common.annotation.RequireRole;
|
|||||||
import com.reading.platform.common.enums.UserRole;
|
import com.reading.platform.common.enums.UserRole;
|
||||||
import com.reading.platform.common.response.Result;
|
import com.reading.platform.common.response.Result;
|
||||||
import com.reading.platform.dto.request.CourseLessonCreateRequest;
|
import com.reading.platform.dto.request.CourseLessonCreateRequest;
|
||||||
|
import com.reading.platform.dto.request.LessonStepCreateRequest;
|
||||||
|
import com.reading.platform.dto.response.CourseLessonResponse;
|
||||||
|
import com.reading.platform.dto.response.LessonStepResponse;
|
||||||
import com.reading.platform.entity.CourseLesson;
|
import com.reading.platform.entity.CourseLesson;
|
||||||
import com.reading.platform.entity.LessonStep;
|
import com.reading.platform.entity.LessonStep;
|
||||||
import com.reading.platform.service.CourseLessonService;
|
import com.reading.platform.service.CourseLessonService;
|
||||||
@ -14,6 +17,7 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 课程环节控制器(超管端)
|
* 课程环节控制器(超管端)
|
||||||
@ -28,33 +32,42 @@ public class AdminCourseLessonController {
|
|||||||
|
|
||||||
@GetMapping("/{courseId}/lessons")
|
@GetMapping("/{courseId}/lessons")
|
||||||
@Operation(summary = "获取课程的所有环节")
|
@Operation(summary = "获取课程的所有环节")
|
||||||
public Result<List<CourseLesson>> findAll(@PathVariable Long courseId) {
|
public Result<List<CourseLessonResponse>> findAll(@PathVariable Long courseId) {
|
||||||
return Result.success(courseLessonService.findByCourseId(courseId));
|
List<CourseLesson> lessons = courseLessonService.findByCourseId(courseId);
|
||||||
|
List<CourseLessonResponse> responses = lessons.stream()
|
||||||
|
.map(this::toLessonResponse)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
return Result.success(responses);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/{courseId}/lessons/{id}")
|
@GetMapping("/{courseId}/lessons/{id}")
|
||||||
@Operation(summary = "获取课程环节详情")
|
@Operation(summary = "获取课程环节详情")
|
||||||
public Result<CourseLesson> findOne(
|
public Result<CourseLessonResponse> findOne(
|
||||||
@PathVariable Long courseId,
|
@PathVariable Long courseId,
|
||||||
@PathVariable Long id) {
|
@PathVariable Long id) {
|
||||||
return Result.success(courseLessonService.findById(id));
|
CourseLesson lesson = courseLessonService.findById(id);
|
||||||
|
return Result.success(toLessonResponse(lesson));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/{courseId}/lessons/type/{lessonType}")
|
@GetMapping("/{courseId}/lessons/type/{lessonType}")
|
||||||
@Operation(summary = "按类型获取课程环节")
|
@Operation(summary = "按类型获取课程环节")
|
||||||
public Result<CourseLesson> findByType(
|
public Result<CourseLessonResponse> findByType(
|
||||||
@PathVariable Long courseId,
|
@PathVariable Long courseId,
|
||||||
@PathVariable String lessonType) {
|
@PathVariable String lessonType) {
|
||||||
return Result.success(courseLessonService.findByType(courseId, lessonType));
|
CourseLesson lesson = courseLessonService.findByType(courseId, lessonType);
|
||||||
|
if (lesson == null) {
|
||||||
|
return Result.success(null);
|
||||||
|
}
|
||||||
|
return Result.success(toLessonResponse(lesson));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{courseId}/lessons")
|
@PostMapping("/{courseId}/lessons")
|
||||||
@Operation(summary = "创建课程环节")
|
@Operation(summary = "创建课程环节")
|
||||||
@RequireRole(UserRole.ADMIN)
|
@RequireRole(UserRole.ADMIN)
|
||||||
public Result<CourseLesson> create(
|
public Result<CourseLessonResponse> create(
|
||||||
@PathVariable Long courseId,
|
@PathVariable Long courseId,
|
||||||
@Valid @RequestBody CourseLessonCreateRequest request) {
|
@Valid @RequestBody CourseLessonCreateRequest request) {
|
||||||
return Result.success(courseLessonService.create(
|
CourseLesson lesson = courseLessonService.create(
|
||||||
courseId,
|
courseId,
|
||||||
request.getLessonType(),
|
request.getLessonType(),
|
||||||
request.getName(),
|
request.getName(),
|
||||||
@ -72,17 +85,18 @@ public class AdminCourseLessonController {
|
|||||||
request.getReflection(),
|
request.getReflection(),
|
||||||
request.getAssessmentData(),
|
request.getAssessmentData(),
|
||||||
request.getUseTemplate()
|
request.getUseTemplate()
|
||||||
));
|
);
|
||||||
|
return Result.success(toLessonResponse(lesson));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/{courseId}/lessons/{id}")
|
@PutMapping("/{courseId}/lessons/{id}")
|
||||||
@Operation(summary = "更新课程环节")
|
@Operation(summary = "更新课程环节")
|
||||||
@RequireRole(UserRole.ADMIN)
|
@RequireRole(UserRole.ADMIN)
|
||||||
public Result<CourseLesson> update(
|
public Result<CourseLessonResponse> update(
|
||||||
@PathVariable Long courseId,
|
@PathVariable Long courseId,
|
||||||
@PathVariable Long id,
|
@PathVariable Long id,
|
||||||
@RequestBody CourseLessonCreateRequest request) {
|
@RequestBody CourseLessonCreateRequest request) {
|
||||||
return Result.success(courseLessonService.update(
|
CourseLesson lesson = courseLessonService.update(
|
||||||
id,
|
id,
|
||||||
request.getName(),
|
request.getName(),
|
||||||
request.getDescription(),
|
request.getDescription(),
|
||||||
@ -99,7 +113,8 @@ public class AdminCourseLessonController {
|
|||||||
request.getReflection(),
|
request.getReflection(),
|
||||||
request.getAssessmentData(),
|
request.getAssessmentData(),
|
||||||
request.getUseTemplate()
|
request.getUseTemplate()
|
||||||
));
|
);
|
||||||
|
return Result.success(toLessonResponse(lesson));
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/{courseId}/lessons/{id}")
|
@DeleteMapping("/{courseId}/lessons/{id}")
|
||||||
@ -126,44 +141,50 @@ public class AdminCourseLessonController {
|
|||||||
|
|
||||||
@GetMapping("/{courseId}/lessons/{lessonId}/steps")
|
@GetMapping("/{courseId}/lessons/{lessonId}/steps")
|
||||||
@Operation(summary = "获取课时的教学环节")
|
@Operation(summary = "获取课时的教学环节")
|
||||||
public Result<List<LessonStep>> findSteps(
|
public Result<List<LessonStepResponse>> findSteps(
|
||||||
@PathVariable Long courseId,
|
@PathVariable Long courseId,
|
||||||
@PathVariable Long lessonId) {
|
@PathVariable Long lessonId) {
|
||||||
return Result.success(courseLessonService.findSteps(lessonId));
|
List<LessonStep> steps = courseLessonService.findSteps(lessonId);
|
||||||
|
List<LessonStepResponse> responses = steps.stream()
|
||||||
|
.map(this::toStepResponse)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
return Result.success(responses);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{courseId}/lessons/{lessonId}/steps")
|
@PostMapping("/{courseId}/lessons/{lessonId}/steps")
|
||||||
@Operation(summary = "创建教学环节")
|
@Operation(summary = "创建教学环节")
|
||||||
@RequireRole(UserRole.ADMIN)
|
@RequireRole(UserRole.ADMIN)
|
||||||
public Result<LessonStep> createStep(
|
public Result<LessonStepResponse> createStep(
|
||||||
@PathVariable Long courseId,
|
@PathVariable Long courseId,
|
||||||
@PathVariable Long lessonId,
|
@PathVariable Long lessonId,
|
||||||
@RequestBody StepCreateRequest request) {
|
@Valid @RequestBody LessonStepCreateRequest request) {
|
||||||
return Result.success(courseLessonService.createStep(
|
LessonStep step = courseLessonService.createStep(
|
||||||
lessonId,
|
lessonId,
|
||||||
request.getName(),
|
request.getName(),
|
||||||
request.getContent(),
|
request.getContent(),
|
||||||
request.getDuration(),
|
request.getDuration(),
|
||||||
request.getObjective(),
|
request.getObjective(),
|
||||||
request.getResourceIds()
|
request.getResourceIds()
|
||||||
));
|
);
|
||||||
|
return Result.success(toStepResponse(step));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/{courseId}/lessons/steps/{stepId}")
|
@PutMapping("/{courseId}/lessons/steps/{stepId}")
|
||||||
@Operation(summary = "更新教学环节")
|
@Operation(summary = "更新教学环节")
|
||||||
@RequireRole(UserRole.ADMIN)
|
@RequireRole(UserRole.ADMIN)
|
||||||
public Result<LessonStep> updateStep(
|
public Result<LessonStepResponse> updateStep(
|
||||||
@PathVariable Long courseId,
|
@PathVariable Long courseId,
|
||||||
@PathVariable Long stepId,
|
@PathVariable Long stepId,
|
||||||
@RequestBody StepCreateRequest request) {
|
@Valid @RequestBody LessonStepCreateRequest request) {
|
||||||
return Result.success(courseLessonService.updateStep(
|
LessonStep step = courseLessonService.updateStep(
|
||||||
stepId,
|
stepId,
|
||||||
request.getName(),
|
request.getName(),
|
||||||
request.getContent(),
|
request.getContent(),
|
||||||
request.getDuration(),
|
request.getDuration(),
|
||||||
request.getObjective(),
|
request.getObjective(),
|
||||||
request.getResourceIds()
|
request.getResourceIds()
|
||||||
));
|
);
|
||||||
|
return Result.success(toStepResponse(step));
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/{courseId}/lessons/steps/{stepId}")
|
@DeleteMapping("/{courseId}/lessons/steps/{stepId}")
|
||||||
@ -188,24 +209,56 @@ public class AdminCourseLessonController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 教学环节创建请求
|
* 将 CourseLesson 实体转换为 CourseLessonResponse
|
||||||
*/
|
*/
|
||||||
public static class StepCreateRequest {
|
private CourseLessonResponse toLessonResponse(CourseLesson lesson) {
|
||||||
private String name;
|
// 获取教学环节
|
||||||
private String content;
|
List<LessonStep> steps = courseLessonService.findSteps(lesson.getId());
|
||||||
private Integer duration;
|
List<LessonStepResponse> stepResponses = steps.stream()
|
||||||
private String objective;
|
.map(this::toStepResponse)
|
||||||
private List<Long> resourceIds;
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
public String getName() { return name; }
|
return CourseLessonResponse.builder()
|
||||||
public void setName(String name) { this.name = name; }
|
.id(lesson.getId())
|
||||||
public String getContent() { return content; }
|
.courseId(lesson.getCourseId())
|
||||||
public void setContent(String content) { this.content = content; }
|
.lessonType(lesson.getLessonType())
|
||||||
public Integer getDuration() { return duration; }
|
.name(lesson.getName())
|
||||||
public void setDuration(Integer duration) { this.duration = duration; }
|
.description(lesson.getDescription())
|
||||||
public String getObjective() { return objective; }
|
.duration(lesson.getDuration())
|
||||||
public void setObjective(String objective) { this.objective = objective; }
|
.videoPath(lesson.getVideoPath())
|
||||||
public List<Long> getResourceIds() { return resourceIds; }
|
.videoName(lesson.getVideoName())
|
||||||
public void setResourceIds(List<Long> resourceIds) { this.resourceIds = resourceIds; }
|
.pptPath(lesson.getPptPath())
|
||||||
|
.pptName(lesson.getPptName())
|
||||||
|
.pdfPath(lesson.getPdfPath())
|
||||||
|
.pdfName(lesson.getPdfName())
|
||||||
|
.objectives(lesson.getObjectives())
|
||||||
|
.preparation(lesson.getPreparation())
|
||||||
|
.extension(lesson.getExtension())
|
||||||
|
.reflection(lesson.getReflection())
|
||||||
|
.assessmentData(lesson.getAssessmentData())
|
||||||
|
.useTemplate(lesson.getUseTemplate())
|
||||||
|
.sortOrder(lesson.getSortOrder())
|
||||||
|
.steps(stepResponses)
|
||||||
|
.createdAt(lesson.getCreatedAt())
|
||||||
|
.updatedAt(lesson.getUpdatedAt())
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* 将 LessonStep 实体转换为 LessonStepResponse
|
||||||
|
*/
|
||||||
|
private LessonStepResponse toStepResponse(LessonStep step) {
|
||||||
|
return LessonStepResponse.builder()
|
||||||
|
.id(step.getId())
|
||||||
|
.lessonId(step.getLessonId())
|
||||||
|
.name(step.getName())
|
||||||
|
.content(step.getContent())
|
||||||
|
.duration(step.getDuration())
|
||||||
|
.objective(step.getObjective())
|
||||||
|
.resourceIds(step.getResourceIds())
|
||||||
|
.sortOrder(step.getSortOrder())
|
||||||
|
.createdAt(step.getCreatedAt())
|
||||||
|
.updatedAt(step.getUpdatedAt())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,12 +1,13 @@
|
|||||||
package com.reading.platform.controller.admin;
|
package com.reading.platform.controller.admin;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
||||||
import com.reading.platform.common.annotation.RequireRole;
|
import com.reading.platform.common.annotation.RequireRole;
|
||||||
import com.reading.platform.common.enums.UserRole;
|
import com.reading.platform.common.enums.UserRole;
|
||||||
import com.reading.platform.common.security.SecurityUtils;
|
import com.reading.platform.common.security.SecurityUtils;
|
||||||
import com.reading.platform.common.response.PageResult;
|
import com.reading.platform.common.response.PageResult;
|
||||||
import com.reading.platform.common.response.Result;
|
import com.reading.platform.common.response.Result;
|
||||||
import com.reading.platform.dto.request.PackageCreateRequest;
|
import com.reading.platform.dto.request.PackageCreateRequest;
|
||||||
|
import com.reading.platform.dto.request.PackageGrantRequest;
|
||||||
|
import com.reading.platform.dto.request.PackageReviewRequest;
|
||||||
import com.reading.platform.dto.response.CoursePackageResponse;
|
import com.reading.platform.dto.response.CoursePackageResponse;
|
||||||
import com.reading.platform.entity.CoursePackage;
|
import com.reading.platform.entity.CoursePackage;
|
||||||
import com.reading.platform.service.CoursePackageService;
|
import com.reading.platform.service.CoursePackageService;
|
||||||
@ -19,6 +20,7 @@ import org.springframework.web.bind.annotation.*;
|
|||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 课程套餐控制器(超管端)
|
* 课程套餐控制器(超管端)
|
||||||
@ -49,24 +51,25 @@ public class AdminPackageController {
|
|||||||
@PostMapping
|
@PostMapping
|
||||||
@Operation(summary = "创建套餐")
|
@Operation(summary = "创建套餐")
|
||||||
@RequireRole(UserRole.ADMIN)
|
@RequireRole(UserRole.ADMIN)
|
||||||
public Result<CoursePackage> create(@Valid @RequestBody PackageCreateRequest request) {
|
public Result<CoursePackageResponse> create(@Valid @RequestBody PackageCreateRequest request) {
|
||||||
return Result.success(packageService.createPackage(
|
CoursePackage pkg = packageService.createPackage(
|
||||||
request.getName(),
|
request.getName(),
|
||||||
request.getDescription(),
|
request.getDescription(),
|
||||||
request.getPrice(),
|
request.getPrice(),
|
||||||
request.getDiscountPrice(),
|
request.getDiscountPrice(),
|
||||||
request.getDiscountType(),
|
request.getDiscountType(),
|
||||||
request.getGradeLevels()
|
request.getGradeLevels()
|
||||||
));
|
);
|
||||||
|
return Result.success(packageService.findOnePackage(pkg.getId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/{id}")
|
@PutMapping("/{id}")
|
||||||
@Operation(summary = "更新套餐")
|
@Operation(summary = "更新套餐")
|
||||||
@RequireRole(UserRole.ADMIN)
|
@RequireRole(UserRole.ADMIN)
|
||||||
public Result<CoursePackage> update(
|
public Result<CoursePackageResponse> update(
|
||||||
@PathVariable Long id,
|
@PathVariable Long id,
|
||||||
@RequestBody PackageCreateRequest request) {
|
@RequestBody PackageCreateRequest request) {
|
||||||
return Result.success(packageService.updatePackage(
|
packageService.updatePackage(
|
||||||
id,
|
id,
|
||||||
request.getName(),
|
request.getName(),
|
||||||
request.getDescription(),
|
request.getDescription(),
|
||||||
@ -74,7 +77,8 @@ public class AdminPackageController {
|
|||||||
request.getDiscountPrice(),
|
request.getDiscountPrice(),
|
||||||
request.getDiscountType(),
|
request.getDiscountType(),
|
||||||
request.getGradeLevels()
|
request.getGradeLevels()
|
||||||
));
|
);
|
||||||
|
return Result.success(packageService.findOnePackage(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/{id}")
|
@DeleteMapping("/{id}")
|
||||||
@ -108,8 +112,14 @@ public class AdminPackageController {
|
|||||||
@RequireRole(UserRole.ADMIN)
|
@RequireRole(UserRole.ADMIN)
|
||||||
public Result<Void> review(
|
public Result<Void> review(
|
||||||
@PathVariable Long id,
|
@PathVariable Long id,
|
||||||
@RequestBody ReviewRequest request) {
|
@Valid @RequestBody PackageReviewRequest request) {
|
||||||
packageService.reviewPackage(id, SecurityUtils.getCurrentUserId(), request.getApproved(), request.getComment());
|
packageService.reviewPackage(
|
||||||
|
id,
|
||||||
|
SecurityUtils.getCurrentUserId(),
|
||||||
|
request.getApproved(),
|
||||||
|
request.getComment(),
|
||||||
|
request.getPublish()
|
||||||
|
);
|
||||||
return Result.success();
|
return Result.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,8 +141,12 @@ public class AdminPackageController {
|
|||||||
|
|
||||||
@GetMapping("/all")
|
@GetMapping("/all")
|
||||||
@Operation(summary = "查询所有已发布的套餐列表")
|
@Operation(summary = "查询所有已发布的套餐列表")
|
||||||
public Result<List<CoursePackage>> getPublishedPackages() {
|
public Result<List<CoursePackageResponse>> getPublishedPackages() {
|
||||||
return Result.success(packageService.findPublishedPackages());
|
List<CoursePackage> packages = packageService.findPublishedPackages();
|
||||||
|
List<CoursePackageResponse> responses = packages.stream()
|
||||||
|
.map(pkg -> packageService.findOnePackage(pkg.getId()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
return Result.success(responses);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{id}/grant")
|
@PostMapping("/{id}/grant")
|
||||||
@ -140,7 +154,7 @@ public class AdminPackageController {
|
|||||||
@RequireRole(UserRole.ADMIN)
|
@RequireRole(UserRole.ADMIN)
|
||||||
public Result<Void> grantToTenant(
|
public Result<Void> grantToTenant(
|
||||||
@PathVariable Long id,
|
@PathVariable Long id,
|
||||||
@RequestBody GrantRequest request) {
|
@Valid @RequestBody PackageGrantRequest request) {
|
||||||
LocalDate endDate = LocalDate.parse(request.getEndDate(), DateTimeFormatter.ISO_DATE);
|
LocalDate endDate = LocalDate.parse(request.getEndDate(), DateTimeFormatter.ISO_DATE);
|
||||||
packageService.renewTenantPackage(
|
packageService.renewTenantPackage(
|
||||||
request.getTenantId(),
|
request.getTenantId(),
|
||||||
@ -150,33 +164,4 @@ public class AdminPackageController {
|
|||||||
);
|
);
|
||||||
return Result.success();
|
return Result.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 审核请求
|
|
||||||
*/
|
|
||||||
public static class ReviewRequest {
|
|
||||||
private Boolean approved;
|
|
||||||
private String comment;
|
|
||||||
|
|
||||||
public Boolean getApproved() { return approved; }
|
|
||||||
public void setApproved(Boolean approved) { this.approved = approved; }
|
|
||||||
public String getComment() { return comment; }
|
|
||||||
public void setComment(String comment) { this.comment = comment; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 授权请求
|
|
||||||
*/
|
|
||||||
public static class GrantRequest {
|
|
||||||
private Long tenantId;
|
|
||||||
private String endDate;
|
|
||||||
private Long pricePaid;
|
|
||||||
|
|
||||||
public Long getTenantId() { return tenantId; }
|
|
||||||
public void setTenantId(Long tenantId) { this.tenantId = tenantId; }
|
|
||||||
public String getEndDate() { return endDate; }
|
|
||||||
public void setEndDate(String endDate) { this.endDate = endDate; }
|
|
||||||
public Long getPricePaid() { return pricePaid; }
|
|
||||||
public void setPricePaid(Long pricePaid) { this.pricePaid = pricePaid; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,16 +5,24 @@ import com.reading.platform.common.annotation.RequireRole;
|
|||||||
import com.reading.platform.common.enums.UserRole;
|
import com.reading.platform.common.enums.UserRole;
|
||||||
import com.reading.platform.common.response.PageResult;
|
import com.reading.platform.common.response.PageResult;
|
||||||
import com.reading.platform.common.response.Result;
|
import com.reading.platform.common.response.Result;
|
||||||
|
import com.reading.platform.dto.request.ResourceLibraryCreateRequest;
|
||||||
|
import com.reading.platform.dto.request.ResourceLibraryUpdateRequest;
|
||||||
|
import com.reading.platform.dto.request.ResourceItemCreateRequest;
|
||||||
|
import com.reading.platform.dto.request.ResourceItemUpdateRequest;
|
||||||
|
import com.reading.platform.dto.response.ResourceLibraryResponse;
|
||||||
|
import com.reading.platform.dto.response.ResourceItemResponse;
|
||||||
import com.reading.platform.entity.ResourceItem;
|
import com.reading.platform.entity.ResourceItem;
|
||||||
import com.reading.platform.entity.ResourceLibrary;
|
import com.reading.platform.entity.ResourceLibrary;
|
||||||
import com.reading.platform.service.ResourceLibraryService;
|
import com.reading.platform.service.ResourceLibraryService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 资源库控制器(超管端)
|
* 资源库控制器(超管端)
|
||||||
@ -31,44 +39,52 @@ public class AdminResourceController {
|
|||||||
|
|
||||||
@GetMapping("/libraries")
|
@GetMapping("/libraries")
|
||||||
@Operation(summary = "分页查询资源库")
|
@Operation(summary = "分页查询资源库")
|
||||||
public Result<PageResult<ResourceLibrary>> findAllLibraries(
|
public Result<PageResult<ResourceLibraryResponse>> findAllLibraries(
|
||||||
@RequestParam(required = false) String libraryType,
|
@RequestParam(required = false) String libraryType,
|
||||||
@RequestParam(required = false) String keyword,
|
@RequestParam(required = false) String keyword,
|
||||||
@RequestParam(required = false, defaultValue = "1") Integer pageNum,
|
@RequestParam(required = false, defaultValue = "1") Integer pageNum,
|
||||||
@RequestParam(required = false, defaultValue = "10") Integer pageSize) {
|
@RequestParam(required = false, defaultValue = "10") Integer pageSize) {
|
||||||
Page<ResourceLibrary> page = resourceLibraryService.findAllLibraries(libraryType, keyword, pageNum, pageSize);
|
Page<ResourceLibrary> page = resourceLibraryService.findAllLibraries(libraryType, keyword, pageNum, pageSize);
|
||||||
return Result.success(PageResult.of(page.getRecords(), page.getTotal(), page.getCurrent(), page.getSize()));
|
|
||||||
|
List<ResourceLibraryResponse> responses = page.getRecords().stream()
|
||||||
|
.map(this::toLibraryResponse)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
return Result.success(PageResult.of(responses, page.getTotal(), page.getCurrent(), page.getSize()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/libraries/{id}")
|
@GetMapping("/libraries/{id}")
|
||||||
@Operation(summary = "查询资源库详情")
|
@Operation(summary = "查询资源库详情")
|
||||||
public Result<ResourceLibrary> findLibrary(@PathVariable String id) {
|
public Result<ResourceLibraryResponse> findLibrary(@PathVariable String id) {
|
||||||
return Result.success(resourceLibraryService.findLibraryById(id));
|
ResourceLibrary library = resourceLibraryService.findLibraryById(id);
|
||||||
|
return Result.success(toLibraryResponse(library));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/libraries")
|
@PostMapping("/libraries")
|
||||||
@Operation(summary = "创建资源库")
|
@Operation(summary = "创建资源库")
|
||||||
@RequireRole(UserRole.ADMIN)
|
@RequireRole(UserRole.ADMIN)
|
||||||
public Result<ResourceLibrary> createLibrary(@RequestBody LibraryCreateRequest request) {
|
public Result<ResourceLibraryResponse> createLibrary(@Valid @RequestBody ResourceLibraryCreateRequest request) {
|
||||||
return Result.success(resourceLibraryService.createLibrary(
|
ResourceLibrary library = resourceLibraryService.createLibrary(
|
||||||
request.getName(),
|
request.getName(),
|
||||||
request.getType(),
|
request.getType(),
|
||||||
request.getDescription(),
|
request.getDescription(),
|
||||||
request.getTenantId()
|
request.getTenantId()
|
||||||
));
|
);
|
||||||
|
return Result.success(toLibraryResponse(library));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/libraries/{id}")
|
@PutMapping("/libraries/{id}")
|
||||||
@Operation(summary = "更新资源库")
|
@Operation(summary = "更新资源库")
|
||||||
@RequireRole(UserRole.ADMIN)
|
@RequireRole(UserRole.ADMIN)
|
||||||
public Result<ResourceLibrary> updateLibrary(
|
public Result<ResourceLibraryResponse> updateLibrary(
|
||||||
@PathVariable String id,
|
@PathVariable String id,
|
||||||
@RequestBody LibraryUpdateRequest request) {
|
@Valid @RequestBody ResourceLibraryUpdateRequest request) {
|
||||||
return Result.success(resourceLibraryService.updateLibrary(
|
ResourceLibrary library = resourceLibraryService.updateLibrary(
|
||||||
id,
|
id,
|
||||||
request.getName(),
|
request.getName(),
|
||||||
request.getDescription()
|
request.getDescription()
|
||||||
));
|
);
|
||||||
|
return Result.success(toLibraryResponse(library));
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/libraries/{id}")
|
@DeleteMapping("/libraries/{id}")
|
||||||
@ -83,27 +99,33 @@ public class AdminResourceController {
|
|||||||
|
|
||||||
@GetMapping("/items")
|
@GetMapping("/items")
|
||||||
@Operation(summary = "分页查询资源项目")
|
@Operation(summary = "分页查询资源项目")
|
||||||
public Result<PageResult<ResourceItem>> findAllItems(
|
public Result<PageResult<ResourceItemResponse>> findAllItems(
|
||||||
@RequestParam(required = false) String libraryId,
|
@RequestParam(required = false) String libraryId,
|
||||||
@RequestParam(required = false) String fileType,
|
@RequestParam(required = false) String fileType,
|
||||||
@RequestParam(required = false) String keyword,
|
@RequestParam(required = false) String keyword,
|
||||||
@RequestParam(required = false, defaultValue = "1") Integer pageNum,
|
@RequestParam(required = false, defaultValue = "1") Integer pageNum,
|
||||||
@RequestParam(required = false, defaultValue = "20") Integer pageSize) {
|
@RequestParam(required = false, defaultValue = "20") Integer pageSize) {
|
||||||
Page<ResourceItem> page = resourceLibraryService.findAllItems(libraryId, fileType, keyword, pageNum, pageSize);
|
Page<ResourceItem> page = resourceLibraryService.findAllItems(libraryId, fileType, keyword, pageNum, pageSize);
|
||||||
return Result.success(PageResult.of(page.getRecords(), page.getTotal(), page.getCurrent(), page.getSize()));
|
|
||||||
|
List<ResourceItemResponse> responses = page.getRecords().stream()
|
||||||
|
.map(this::toItemResponse)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
return Result.success(PageResult.of(responses, page.getTotal(), page.getCurrent(), page.getSize()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/items/{id}")
|
@GetMapping("/items/{id}")
|
||||||
@Operation(summary = "查询资源项目详情")
|
@Operation(summary = "查询资源项目详情")
|
||||||
public Result<ResourceItem> findItem(@PathVariable String id) {
|
public Result<ResourceItemResponse> findItem(@PathVariable String id) {
|
||||||
return Result.success(resourceLibraryService.findItemById(id));
|
ResourceItem item = resourceLibraryService.findItemById(id);
|
||||||
|
return Result.success(toItemResponse(item));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/items")
|
@PostMapping("/items")
|
||||||
@Operation(summary = "创建资源项目")
|
@Operation(summary = "创建资源项目")
|
||||||
@RequireRole(UserRole.ADMIN)
|
@RequireRole(UserRole.ADMIN)
|
||||||
public Result<ResourceItem> createItem(@RequestBody ItemCreateRequest request) {
|
public Result<ResourceItemResponse> createItem(@Valid @RequestBody ResourceItemCreateRequest request) {
|
||||||
return Result.success(resourceLibraryService.createItem(
|
ResourceItem item = resourceLibraryService.createItem(
|
||||||
request.getLibraryId(),
|
request.getLibraryId(),
|
||||||
request.getTitle(),
|
request.getTitle(),
|
||||||
request.getFileType(),
|
request.getFileType(),
|
||||||
@ -112,21 +134,23 @@ public class AdminResourceController {
|
|||||||
request.getDescription(),
|
request.getDescription(),
|
||||||
request.getTags(),
|
request.getTags(),
|
||||||
request.getTenantId()
|
request.getTenantId()
|
||||||
));
|
);
|
||||||
|
return Result.success(toItemResponse(item));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/items/{id}")
|
@PutMapping("/items/{id}")
|
||||||
@Operation(summary = "更新资源项目")
|
@Operation(summary = "更新资源项目")
|
||||||
@RequireRole(UserRole.ADMIN)
|
@RequireRole(UserRole.ADMIN)
|
||||||
public Result<ResourceItem> updateItem(
|
public Result<ResourceItemResponse> updateItem(
|
||||||
@PathVariable String id,
|
@PathVariable String id,
|
||||||
@RequestBody ItemUpdateRequest request) {
|
@Valid @RequestBody ResourceItemUpdateRequest request) {
|
||||||
return Result.success(resourceLibraryService.updateItem(
|
ResourceItem item = resourceLibraryService.updateItem(
|
||||||
id,
|
id,
|
||||||
request.getTitle(),
|
request.getTitle(),
|
||||||
request.getDescription(),
|
request.getDescription(),
|
||||||
request.getTags()
|
request.getTags()
|
||||||
));
|
);
|
||||||
|
return Result.success(toItemResponse(item));
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/items/{id}")
|
@DeleteMapping("/items/{id}")
|
||||||
@ -154,81 +178,35 @@ public class AdminResourceController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 资源库创建请求
|
* 将 ResourceLibrary 实体转换为 ResourceLibraryResponse
|
||||||
*/
|
*/
|
||||||
public static class LibraryCreateRequest {
|
private ResourceLibraryResponse toLibraryResponse(ResourceLibrary library) {
|
||||||
private String name;
|
return ResourceLibraryResponse.builder()
|
||||||
private String type;
|
.id(library.getId())
|
||||||
private String description;
|
.tenantId(library.getTenantId())
|
||||||
private String tenantId;
|
.name(library.getName())
|
||||||
|
.description(library.getDescription())
|
||||||
public String getName() { return name; }
|
.type(library.getLibraryType())
|
||||||
public void setName(String name) { this.name = name; }
|
.createdBy(library.getCreatedBy() != null ? String.valueOf(library.getCreatedBy()) : null)
|
||||||
public String getType() { return type; }
|
.createdAt(library.getCreatedAt())
|
||||||
public void setType(String type) { this.type = type; }
|
.updatedAt(library.getUpdatedAt())
|
||||||
public String getDescription() { return description; }
|
.build();
|
||||||
public void setDescription(String description) { this.description = description; }
|
|
||||||
public String getTenantId() { return tenantId; }
|
|
||||||
public void setTenantId(String tenantId) { this.tenantId = tenantId; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 资源库更新请求
|
* 将 ResourceItem 实体转换为 ResourceItemResponse
|
||||||
*/
|
*/
|
||||||
public static class LibraryUpdateRequest {
|
private ResourceItemResponse toItemResponse(ResourceItem item) {
|
||||||
private String name;
|
return ResourceItemResponse.builder()
|
||||||
private String description;
|
.id(item.getId())
|
||||||
|
.libraryId(item.getLibraryId())
|
||||||
public String getName() { return name; }
|
.tenantId(item.getTenantId())
|
||||||
public void setName(String name) { this.name = name; }
|
.type(item.getFileType())
|
||||||
public String getDescription() { return description; }
|
.name(item.getTitle())
|
||||||
public void setDescription(String description) { this.description = description; }
|
.description(item.getDescription())
|
||||||
|
.status(item.getStatus())
|
||||||
|
.createdAt(item.getCreatedAt())
|
||||||
|
.updatedAt(item.getUpdatedAt())
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/**
|
|
||||||
* 资源项目创建请求(数字资源)
|
|
||||||
*/
|
|
||||||
public static class ItemCreateRequest {
|
|
||||||
private String libraryId;
|
|
||||||
private String title;
|
|
||||||
private String fileType;
|
|
||||||
private String filePath;
|
|
||||||
private Long fileSize;
|
|
||||||
private String description;
|
|
||||||
private String tags;
|
|
||||||
private String tenantId;
|
|
||||||
|
|
||||||
public String getLibraryId() { return libraryId; }
|
|
||||||
public void setLibraryId(String libraryId) { this.libraryId = libraryId; }
|
|
||||||
public String getTitle() { return title; }
|
|
||||||
public void setTitle(String title) { this.title = title; }
|
|
||||||
public String getFileType() { return fileType; }
|
|
||||||
public void setFileType(String fileType) { this.fileType = fileType; }
|
|
||||||
public String getFilePath() { return filePath; }
|
|
||||||
public void setFilePath(String filePath) { this.filePath = filePath; }
|
|
||||||
public Long getFileSize() { return fileSize; }
|
|
||||||
public void setFileSize(Long fileSize) { this.fileSize = fileSize; }
|
|
||||||
public String getDescription() { return description; }
|
|
||||||
public void setDescription(String description) { this.description = description; }
|
|
||||||
public String getTags() { return tags; }
|
|
||||||
public void setTags(String tags) { this.tags = tags; }
|
|
||||||
public String getTenantId() { return tenantId; }
|
|
||||||
public void setTenantId(String tenantId) { this.tenantId = tenantId; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 资源项目更新请求(数字资源)
|
|
||||||
*/
|
|
||||||
public static class ItemUpdateRequest {
|
|
||||||
private String title;
|
|
||||||
private String description;
|
|
||||||
private String tags;
|
|
||||||
|
|
||||||
public String getTitle() { return title; }
|
|
||||||
public void setTitle(String title) { this.title = title; }
|
|
||||||
public String getDescription() { return description; }
|
|
||||||
public void setDescription(String description) { this.description = description; }
|
|
||||||
public String getTags() { return tags; }
|
|
||||||
public void setTags(String tags) { this.tags = tags; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -11,6 +11,7 @@ import com.reading.platform.dto.response.PopularCourseItemResponse;
|
|||||||
import com.reading.platform.dto.response.RecentActivityItemResponse;
|
import com.reading.platform.dto.response.RecentActivityItemResponse;
|
||||||
import com.reading.platform.dto.response.StatsResponse;
|
import com.reading.platform.dto.response.StatsResponse;
|
||||||
import com.reading.platform.dto.response.StatsTrendResponse;
|
import com.reading.platform.dto.response.StatsTrendResponse;
|
||||||
|
import com.reading.platform.service.StatsService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@ -19,7 +20,6 @@ import org.springframework.web.bind.annotation.ModelAttribute;
|
|||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -32,55 +32,36 @@ import java.util.List;
|
|||||||
@Tag(name = "超管端 - 统计管理")
|
@Tag(name = "超管端 - 统计管理")
|
||||||
public class AdminStatsController {
|
public class AdminStatsController {
|
||||||
|
|
||||||
|
private final StatsService statsService;
|
||||||
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
@Operation(summary = "获取统计数据")
|
@Operation(summary = "获取统计数据")
|
||||||
public Result<StatsResponse> getStats() {
|
public Result<StatsResponse> getStats() {
|
||||||
// TODO: 实现统计数据查询
|
return Result.success(statsService.getStats());
|
||||||
return Result.success(StatsResponse.builder()
|
|
||||||
.totalTenants(0L)
|
|
||||||
.activeTenants(0L)
|
|
||||||
.totalTeachers(0L)
|
|
||||||
.totalStudents(0L)
|
|
||||||
.totalCourses(0L)
|
|
||||||
.totalLessons(0L)
|
|
||||||
.build());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/trend")
|
@GetMapping("/trend")
|
||||||
@Operation(summary = "获取趋势数据")
|
@Operation(summary = "获取趋势数据")
|
||||||
public Result<StatsTrendResponse> getTrendData() {
|
public Result<StatsTrendResponse> getTrendData() {
|
||||||
// TODO: 实现趋势数据查询
|
return Result.success(statsService.getTrendData());
|
||||||
return Result.success(StatsTrendResponse.builder()
|
|
||||||
.dates(new ArrayList<>())
|
|
||||||
.newStudents(new ArrayList<>())
|
|
||||||
.newTeachers(new ArrayList<>())
|
|
||||||
.newCourses(new ArrayList<>())
|
|
||||||
.build());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/tenants/active")
|
@GetMapping("/tenants/active")
|
||||||
@Operation(summary = "获取活跃租户")
|
@Operation(summary = "获取活跃租户")
|
||||||
public Result<List<ActiveTenantItemResponse>> getActiveTenants(@ModelAttribute ActiveTenantsQueryRequest request) {
|
public Result<List<ActiveTenantItemResponse>> getActiveTenants(@ModelAttribute ActiveTenantsQueryRequest request) {
|
||||||
// 返回数量限制
|
return Result.success(statsService.getActiveTenants(request));
|
||||||
// TODO: 实现活跃租户查询
|
|
||||||
return Result.success(new ArrayList<>());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/courses/popular")
|
@GetMapping("/courses/popular")
|
||||||
@Operation(summary = "获取热门课程")
|
@Operation(summary = "获取热门课程")
|
||||||
public Result<List<PopularCourseItemResponse>> getPopularCourses(@ModelAttribute PopularCoursesQueryRequest request) {
|
public Result<List<PopularCourseItemResponse>> getPopularCourses(@ModelAttribute PopularCoursesQueryRequest request) {
|
||||||
// 返回数量限制
|
return Result.success(statsService.getPopularCourses(request));
|
||||||
// TODO: 实现热门课程查询
|
|
||||||
return Result.success(new ArrayList<>());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/activities")
|
@GetMapping("/activities")
|
||||||
@Operation(summary = "获取最近活动")
|
@Operation(summary = "获取最近活动")
|
||||||
public Result<List<RecentActivityItemResponse>> getRecentActivities(@ModelAttribute RecentActivitiesQueryRequest request) {
|
public Result<List<RecentActivityItemResponse>> getRecentActivities(@ModelAttribute RecentActivitiesQueryRequest request) {
|
||||||
// 返回数量限制
|
return Result.success(statsService.getRecentActivities(request));
|
||||||
// TODO: 实现最近活动查询
|
|
||||||
return Result.success(new ArrayList<>());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
|
||||||
@ -1,20 +1,14 @@
|
|||||||
package com.reading.platform.controller.admin;
|
package com.reading.platform.controller.admin;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.reading.platform.common.annotation.RequireRole;
|
import com.reading.platform.common.annotation.RequireRole;
|
||||||
import com.reading.platform.common.enums.UserRole;
|
import com.reading.platform.common.enums.UserRole;
|
||||||
import com.reading.platform.common.mapper.TenantMapper;
|
|
||||||
import com.reading.platform.common.response.PageResult;
|
import com.reading.platform.common.response.PageResult;
|
||||||
import com.reading.platform.common.response.Result;
|
import com.reading.platform.common.response.Result;
|
||||||
import com.reading.platform.dto.request.TenantCreateRequest;
|
import com.reading.platform.dto.request.TenantCreateRequest;
|
||||||
import com.reading.platform.dto.request.TenantUpdateRequest;
|
import com.reading.platform.dto.request.TenantUpdateRequest;
|
||||||
import com.reading.platform.dto.response.TenantResponse;
|
import com.reading.platform.dto.response.TenantResponse;
|
||||||
import com.reading.platform.entity.Student;
|
|
||||||
import com.reading.platform.entity.Teacher;
|
|
||||||
import com.reading.platform.entity.Tenant;
|
import com.reading.platform.entity.Tenant;
|
||||||
import com.reading.platform.mapper.StudentMapper;
|
|
||||||
import com.reading.platform.mapper.TeacherMapper;
|
|
||||||
import com.reading.platform.service.TenantService;
|
import com.reading.platform.service.TenantService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
@ -25,6 +19,7 @@ import org.springframework.web.bind.annotation.*;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Tag(name = "超管端 - 租户管理", description = "Tenant Management APIs for Admin")
|
@Tag(name = "超管端 - 租户管理", description = "Tenant Management APIs for Admin")
|
||||||
@RestController
|
@RestController
|
||||||
@ -34,29 +29,26 @@ import java.util.Map;
|
|||||||
public class AdminTenantController {
|
public class AdminTenantController {
|
||||||
|
|
||||||
private final TenantService tenantService;
|
private final TenantService tenantService;
|
||||||
private final TenantMapper tenantMapper;
|
|
||||||
private final TeacherMapper teacherMapper;
|
|
||||||
private final StudentMapper studentMapper;
|
|
||||||
|
|
||||||
@Operation(summary = "Create tenant")
|
@Operation(summary = "Create tenant")
|
||||||
@PostMapping
|
@PostMapping
|
||||||
public Result<TenantResponse> createTenant(@Valid @RequestBody TenantCreateRequest request) {
|
public Result<TenantResponse> createTenant(@Valid @RequestBody TenantCreateRequest request) {
|
||||||
Tenant tenant = tenantService.createTenant(request);
|
Tenant tenant = tenantService.createTenant(request);
|
||||||
return Result.success(tenantMapper.toVO(tenant));
|
return Result.success(toResponse(tenant));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "Update tenant")
|
@Operation(summary = "Update tenant")
|
||||||
@PutMapping("/{id}")
|
@PutMapping("/{id}")
|
||||||
public Result<TenantResponse> updateTenant(@PathVariable Long id, @RequestBody TenantUpdateRequest request) {
|
public Result<TenantResponse> updateTenant(@PathVariable Long id, @RequestBody TenantUpdateRequest request) {
|
||||||
Tenant tenant = tenantService.updateTenant(id, request);
|
Tenant tenant = tenantService.updateTenant(id, request);
|
||||||
return Result.success(tenantMapper.toVO(tenant));
|
return Result.success(toResponse(tenant));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "Get tenant by ID")
|
@Operation(summary = "Get tenant by ID")
|
||||||
@GetMapping("/{id}")
|
@GetMapping("/{id}")
|
||||||
public Result<TenantResponse> getTenant(@PathVariable Long id) {
|
public Result<TenantResponse> getTenant(@PathVariable Long id) {
|
||||||
Tenant tenant = tenantService.getTenantById(id);
|
Tenant tenant = tenantService.getTenantById(id);
|
||||||
return Result.success(tenantMapper.toVO(tenant));
|
return Result.success(toResponse(tenant));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "Get tenant page")
|
@Operation(summary = "Get tenant page")
|
||||||
@ -66,27 +58,15 @@ public class AdminTenantController {
|
|||||||
@RequestParam(required = false, defaultValue = "10") Integer pageSize,
|
@RequestParam(required = false, defaultValue = "10") Integer pageSize,
|
||||||
@RequestParam(required = false) String keyword,
|
@RequestParam(required = false) String keyword,
|
||||||
@RequestParam(required = false) String status) {
|
@RequestParam(required = false) String status) {
|
||||||
Page<Tenant> pageResult = tenantService.getTenantPage(pageNum, pageSize, keyword, status);
|
// 调用 Service 层方法获取带统计数据的租户分页
|
||||||
List<TenantResponse> voList = tenantMapper.toVO(pageResult.getRecords());
|
Page<TenantResponse> pageResult = tenantService.getTenantPageWithStats(pageNum, pageSize, keyword, status);
|
||||||
|
|
||||||
// 填充教师数量和学生数量
|
return Result.success(PageResult.of(
|
||||||
for (TenantResponse vo : voList) {
|
pageResult.getRecords(),
|
||||||
if (vo.getId() != null) {
|
pageResult.getTotal(),
|
||||||
Long teacherCount = teacherMapper.selectCount(
|
pageResult.getCurrent(),
|
||||||
new LambdaQueryWrapper<Teacher>()
|
pageResult.getSize()
|
||||||
.eq(Teacher::getTenantId, vo.getId())
|
));
|
||||||
);
|
|
||||||
vo.setTeacherCount(teacherCount != null ? teacherCount.intValue() : 0);
|
|
||||||
|
|
||||||
Long studentCount = studentMapper.selectCount(
|
|
||||||
new LambdaQueryWrapper<Student>()
|
|
||||||
.eq(Student::getTenantId, vo.getId())
|
|
||||||
);
|
|
||||||
vo.setStudentCount(studentCount != null ? studentCount.intValue() : 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Result.success(PageResult.of(voList, pageResult.getTotal(), pageResult.getCurrent(), pageResult.getSize()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "Delete tenant")
|
@Operation(summary = "Delete tenant")
|
||||||
@ -100,7 +80,10 @@ public class AdminTenantController {
|
|||||||
@GetMapping("/active")
|
@GetMapping("/active")
|
||||||
public Result<List<TenantResponse>> getAllActiveTenants() {
|
public Result<List<TenantResponse>> getAllActiveTenants() {
|
||||||
List<Tenant> tenants = tenantService.getAllActiveTenants();
|
List<Tenant> tenants = tenantService.getAllActiveTenants();
|
||||||
return Result.success(tenantMapper.toVO(tenants));
|
List<TenantResponse> responses = tenants.stream()
|
||||||
|
.map(this::toResponse)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
return Result.success(responses);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "获取租户统计信息")
|
@Operation(summary = "获取租户统计信息")
|
||||||
@ -118,7 +101,7 @@ public class AdminTenantController {
|
|||||||
public Result<TenantResponse> updateTenantQuota(@PathVariable Long id, @RequestBody Map<String, Object> quota) {
|
public Result<TenantResponse> updateTenantQuota(@PathVariable Long id, @RequestBody Map<String, Object> quota) {
|
||||||
// TODO: 实现更新租户配额逻辑
|
// TODO: 实现更新租户配额逻辑
|
||||||
Tenant tenant = tenantService.getTenantById(id);
|
Tenant tenant = tenantService.getTenantById(id);
|
||||||
return Result.success(tenantMapper.toVO(tenant));
|
return Result.success(toResponse(tenant));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "更新租户状态")
|
@Operation(summary = "更新租户状态")
|
||||||
@ -126,7 +109,7 @@ public class AdminTenantController {
|
|||||||
public Result<TenantResponse> updateTenantStatus(@PathVariable Long id, @RequestBody Map<String, Object> status) {
|
public Result<TenantResponse> updateTenantStatus(@PathVariable Long id, @RequestBody Map<String, Object> status) {
|
||||||
// TODO: 实现更新租户状态逻辑
|
// TODO: 实现更新租户状态逻辑
|
||||||
Tenant tenant = tenantService.getTenantById(id);
|
Tenant tenant = tenantService.getTenantById(id);
|
||||||
return Result.success(tenantMapper.toVO(tenant));
|
return Result.success(toResponse(tenant));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "重置租户密码")
|
@Operation(summary = "重置租户密码")
|
||||||
@ -136,4 +119,34 @@ public class AdminTenantController {
|
|||||||
return Result.success();
|
return Result.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
/**
|
||||||
|
* 将 Tenant 实体转换为 TenantResponse
|
||||||
|
*/
|
||||||
|
private TenantResponse toResponse(Tenant tenant) {
|
||||||
|
return TenantResponse.builder()
|
||||||
|
.id(tenant.getId())
|
||||||
|
.name(tenant.getName())
|
||||||
|
.code(tenant.getCode())
|
||||||
|
.username(tenant.getUsername())
|
||||||
|
.contactName(tenant.getContactName())
|
||||||
|
.contactPhone(tenant.getContactPhone())
|
||||||
|
.contactEmail(tenant.getContactEmail())
|
||||||
|
.address(tenant.getAddress())
|
||||||
|
.logoUrl(tenant.getLogoUrl())
|
||||||
|
.status(tenant.getStatus())
|
||||||
|
.expireAt(tenant.getExpireAt())
|
||||||
|
.maxStudents(tenant.getMaxStudents())
|
||||||
|
.maxTeachers(tenant.getMaxTeachers())
|
||||||
|
.packageType(tenant.getPackageType())
|
||||||
|
.teacherQuota(tenant.getTeacherQuota())
|
||||||
|
.studentQuota(tenant.getStudentQuota())
|
||||||
|
.storageQuota(tenant.getStorageQuota())
|
||||||
|
.storageUsed(tenant.getStorageUsed())
|
||||||
|
.startDate(tenant.getStartDate())
|
||||||
|
.expireDate(tenant.getExpireDate())
|
||||||
|
.createdAt(tenant.getCreatedAt())
|
||||||
|
.updatedAt(tenant.getUpdatedAt())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -4,6 +4,7 @@ import com.reading.platform.common.annotation.RequireRole;
|
|||||||
import com.reading.platform.common.enums.UserRole;
|
import com.reading.platform.common.enums.UserRole;
|
||||||
import com.reading.platform.common.response.Result;
|
import com.reading.platform.common.response.Result;
|
||||||
import com.reading.platform.dto.request.ThemeCreateRequest;
|
import com.reading.platform.dto.request.ThemeCreateRequest;
|
||||||
|
import com.reading.platform.dto.response.ThemeResponse;
|
||||||
import com.reading.platform.entity.Theme;
|
import com.reading.platform.entity.Theme;
|
||||||
import com.reading.platform.service.ThemeService;
|
import com.reading.platform.service.ThemeService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
@ -13,6 +14,7 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 主题字典控制器(超管端)
|
* 主题字典控制器(超管端)
|
||||||
@ -27,40 +29,47 @@ public class AdminThemeController {
|
|||||||
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
@Operation(summary = "查询所有主题")
|
@Operation(summary = "查询所有主题")
|
||||||
public Result<List<Theme>> findAll() {
|
public Result<List<ThemeResponse>> findAll() {
|
||||||
return Result.success(themeService.findAll());
|
List<Theme> themes = themeService.findAll();
|
||||||
|
List<ThemeResponse> responses = themes.stream()
|
||||||
|
.map(this::toResponse)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
return Result.success(responses);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/{id}")
|
@GetMapping("/{id}")
|
||||||
@Operation(summary = "查询主题详情")
|
@Operation(summary = "查询主题详情")
|
||||||
public Result<Theme> findOne(@PathVariable Long id) {
|
public Result<ThemeResponse> findOne(@PathVariable Long id) {
|
||||||
return Result.success(themeService.findById(id));
|
Theme theme = themeService.findById(id);
|
||||||
|
return Result.success(toResponse(theme));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
@Operation(summary = "创建主题")
|
@Operation(summary = "创建主题")
|
||||||
@RequireRole(UserRole.ADMIN)
|
@RequireRole(UserRole.ADMIN)
|
||||||
public Result<Theme> create(@Valid @RequestBody ThemeCreateRequest request) {
|
public Result<ThemeResponse> create(@Valid @RequestBody ThemeCreateRequest request) {
|
||||||
return Result.success(themeService.create(
|
Theme theme = themeService.create(
|
||||||
request.getName(),
|
request.getName(),
|
||||||
request.getDescription(),
|
request.getDescription(),
|
||||||
request.getSortOrder()
|
request.getSortOrder()
|
||||||
));
|
);
|
||||||
|
return Result.success(toResponse(theme));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/{id}")
|
@PutMapping("/{id}")
|
||||||
@Operation(summary = "更新主题")
|
@Operation(summary = "更新主题")
|
||||||
@RequireRole(UserRole.ADMIN)
|
@RequireRole(UserRole.ADMIN)
|
||||||
public Result<Theme> update(
|
public Result<ThemeResponse> update(
|
||||||
@PathVariable Long id,
|
@PathVariable Long id,
|
||||||
@RequestBody ThemeCreateRequest request) {
|
@RequestBody ThemeCreateRequest request) {
|
||||||
return Result.success(themeService.update(
|
Theme theme = themeService.update(
|
||||||
id,
|
id,
|
||||||
request.getName(),
|
request.getName(),
|
||||||
request.getDescription(),
|
request.getDescription(),
|
||||||
request.getSortOrder(),
|
request.getSortOrder(),
|
||||||
null
|
null
|
||||||
));
|
);
|
||||||
|
return Result.success(toResponse(theme));
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/{id}")
|
@DeleteMapping("/{id}")
|
||||||
@ -78,4 +87,19 @@ public class AdminThemeController {
|
|||||||
themeService.reorder(ids);
|
themeService.reorder(ids);
|
||||||
return Result.success();
|
return Result.success();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* 将 Theme 实体转换为 ThemeResponse
|
||||||
|
*/
|
||||||
|
private ThemeResponse toResponse(Theme theme) {
|
||||||
|
return ThemeResponse.builder()
|
||||||
|
.id(theme.getId())
|
||||||
|
.name(theme.getName())
|
||||||
|
.description(theme.getDescription())
|
||||||
|
.sortOrder(theme.getSortOrder())
|
||||||
|
.status(theme.getStatus())
|
||||||
|
.createdAt(theme.getCreatedAt())
|
||||||
|
.updatedAt(theme.getUpdatedAt())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
package com.reading.platform.controller.teacher;
|
package com.reading.platform.controller.teacher;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.reading.platform.common.enums.CourseStatus;
|
||||||
import com.reading.platform.common.mapper.ClassMapper;
|
import com.reading.platform.common.mapper.ClassMapper;
|
||||||
import com.reading.platform.common.mapper.CourseMapper;
|
import com.reading.platform.common.mapper.CourseMapper;
|
||||||
import com.reading.platform.common.mapper.StudentMapper;
|
import com.reading.platform.common.mapper.StudentMapper;
|
||||||
@ -68,7 +69,7 @@ public class TeacherCourseController {
|
|||||||
@RequestParam(required = false) String keyword,
|
@RequestParam(required = false) String keyword,
|
||||||
@RequestParam(required = false) String category) {
|
@RequestParam(required = false) String category) {
|
||||||
Long tenantId = SecurityUtils.getCurrentTenantId();
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
Page<Course> page = courseService.getCoursePage(tenantId, pageNum, pageSize, keyword, category, "published");
|
Page<Course> page = courseService.getCoursePage(tenantId, pageNum, pageSize, keyword, category, CourseStatus.PUBLISHED.getCode());
|
||||||
List<CourseResponse> voList = courseMapper.toVO(page.getRecords());
|
List<CourseResponse> voList = courseMapper.toVO(page.getRecords());
|
||||||
return Result.success(PageResult.of(voList, page.getTotal(), page.getCurrent(), page.getSize()));
|
return Result.success(PageResult.of(voList, page.getTotal(), page.getCurrent(), page.getSize()));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,29 @@
|
|||||||
|
package com.reading.platform.dto.request;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 教学环节创建请求
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "教学环节创建请求")
|
||||||
|
public class LessonStepCreateRequest {
|
||||||
|
|
||||||
|
@Schema(description = "环节名称")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "环节内容")
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
@Schema(description = "时长(分钟)")
|
||||||
|
private Integer duration;
|
||||||
|
|
||||||
|
@Schema(description = "教学目标")
|
||||||
|
private String objective;
|
||||||
|
|
||||||
|
@Schema(description = "关联资源 ID 列表")
|
||||||
|
private List<Long> resourceIds;
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
package com.reading.platform.dto.request;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 套餐授权请求
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "套餐授权请求")
|
||||||
|
public class PackageGrantRequest {
|
||||||
|
|
||||||
|
@Schema(description = "租户 ID")
|
||||||
|
private Long tenantId;
|
||||||
|
|
||||||
|
@Schema(description = "结束日期")
|
||||||
|
private String endDate;
|
||||||
|
|
||||||
|
@Schema(description = "支付金额(分)")
|
||||||
|
private Long pricePaid;
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
package com.reading.platform.dto.request;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 套餐审核请求
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "套餐审核请求")
|
||||||
|
public class PackageReviewRequest {
|
||||||
|
|
||||||
|
@Schema(description = "是否通过")
|
||||||
|
private Boolean approved;
|
||||||
|
|
||||||
|
@Schema(description = "审核意见")
|
||||||
|
private String comment;
|
||||||
|
|
||||||
|
@Schema(description = "是否同时发布(仅当 approved=true 时有效)")
|
||||||
|
private Boolean publish;
|
||||||
|
}
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
package com.reading.platform.dto.request;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 资源项目创建请求
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "资源项目创建请求")
|
||||||
|
public class ResourceItemCreateRequest {
|
||||||
|
|
||||||
|
@Schema(description = "资源库 ID")
|
||||||
|
private String libraryId;
|
||||||
|
|
||||||
|
@Schema(description = "资源标题")
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
@Schema(description = "文件类型")
|
||||||
|
private String fileType;
|
||||||
|
|
||||||
|
@Schema(description = "文件路径")
|
||||||
|
private String filePath;
|
||||||
|
|
||||||
|
@Schema(description = "文件大小(字节)")
|
||||||
|
private Long fileSize;
|
||||||
|
|
||||||
|
@Schema(description = "资源描述")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@Schema(description = "标签")
|
||||||
|
private String tags;
|
||||||
|
|
||||||
|
@Schema(description = "租户 ID")
|
||||||
|
private String tenantId;
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
package com.reading.platform.dto.request;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 资源项目更新请求
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "资源项目更新请求")
|
||||||
|
public class ResourceItemUpdateRequest {
|
||||||
|
|
||||||
|
@Schema(description = "资源标题")
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
@Schema(description = "资源描述")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@Schema(description = "标签")
|
||||||
|
private String tags;
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
package com.reading.platform.dto.request;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 资源库创建请求
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "资源库创建请求")
|
||||||
|
public class ResourceLibraryCreateRequest {
|
||||||
|
|
||||||
|
@Schema(description = "资源库名称")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "资源库类型")
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
@Schema(description = "资源库描述")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@Schema(description = "租户 ID")
|
||||||
|
private String tenantId;
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
package com.reading.platform.dto.request;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 资源库更新请求
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "资源库更新请求")
|
||||||
|
public class ResourceLibraryUpdateRequest {
|
||||||
|
|
||||||
|
@Schema(description = "资源库名称")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "资源库描述")
|
||||||
|
private String description;
|
||||||
|
}
|
||||||
@ -16,7 +16,7 @@ import java.time.LocalDateTime;
|
|||||||
public class ResourceItemResponse {
|
public class ResourceItemResponse {
|
||||||
|
|
||||||
@Schema(description = "ID")
|
@Schema(description = "ID")
|
||||||
private String id;
|
private Long id;
|
||||||
|
|
||||||
@Schema(description = "资源库 ID")
|
@Schema(description = "资源库 ID")
|
||||||
private String libraryId;
|
private String libraryId;
|
||||||
|
|||||||
@ -16,7 +16,7 @@ import java.time.LocalDateTime;
|
|||||||
public class ResourceLibraryResponse {
|
public class ResourceLibraryResponse {
|
||||||
|
|
||||||
@Schema(description = "ID")
|
@Schema(description = "ID")
|
||||||
private String id;
|
private Long id;
|
||||||
|
|
||||||
@Schema(description = "租户 ID")
|
@Schema(description = "租户 ID")
|
||||||
private String tenantId;
|
private String tenantId;
|
||||||
|
|||||||
@ -37,7 +37,7 @@ public class CoursePackage extends BaseEntity {
|
|||||||
@Schema(description = "课程数量")
|
@Schema(description = "课程数量")
|
||||||
private Integer courseCount;
|
private Integer courseCount;
|
||||||
|
|
||||||
@Schema(description = "状态:DRAFT、PENDING_REVIEW、APPROVED、REJECTED、PUBLISHED、OFFLINE")
|
@Schema(description = "状态:DRAFT、PENDING、APPROVED、REJECTED、PUBLISHED、OFFLINE")
|
||||||
private String status;
|
private String status;
|
||||||
|
|
||||||
@Schema(description = "提交时间")
|
@Schema(description = "提交时间")
|
||||||
|
|||||||
@ -300,10 +300,15 @@ public class CoursePackageService extends ServiceImpl<CoursePackageMapper, Cours
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 审核套餐
|
* 审核套餐
|
||||||
|
* @param id 套餐ID
|
||||||
|
* @param userId 审核人ID
|
||||||
|
* @param approved 是否通过
|
||||||
|
* @param comment 审核意见
|
||||||
|
* @param publish 是否同时发布(仅当 approved=true 时有效)
|
||||||
*/
|
*/
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void reviewPackage(Long id, Long userId, Boolean approved, String comment) {
|
public void reviewPackage(Long id, Long userId, Boolean approved, String comment, Boolean publish) {
|
||||||
log.info("审核套餐,id={}, userId={}, approved={}", id, userId, approved);
|
log.info("审核套餐,id={}, userId={}, approved={}, publish={}", id, userId, approved, publish);
|
||||||
CoursePackage pkg = packageMapper.selectById(id);
|
CoursePackage pkg = packageMapper.selectById(id);
|
||||||
if (pkg == null) {
|
if (pkg == null) {
|
||||||
log.warn("套餐不存在,id={}", id);
|
log.warn("套餐不存在,id={}", id);
|
||||||
@ -315,12 +320,27 @@ public class CoursePackageService extends ServiceImpl<CoursePackageMapper, Cours
|
|||||||
throw new BusinessException("只有待审核状态的套餐可以审核");
|
throw new BusinessException("只有待审核状态的套餐可以审核");
|
||||||
}
|
}
|
||||||
|
|
||||||
pkg.setStatus(approved ? CourseStatus.APPROVED.getCode() : CourseStatus.REJECTED.getCode());
|
// 如果驳回
|
||||||
|
if (!Boolean.TRUE.equals(approved)) {
|
||||||
|
pkg.setStatus(CourseStatus.REJECTED.getCode());
|
||||||
|
}
|
||||||
|
// 如果通过且同时发布
|
||||||
|
else if (Boolean.TRUE.equals(publish)) {
|
||||||
|
pkg.setStatus(CourseStatus.PUBLISHED.getCode());
|
||||||
|
pkg.setPublishedAt(LocalDateTime.now());
|
||||||
|
}
|
||||||
|
// 仅通过
|
||||||
|
else {
|
||||||
|
pkg.setStatus(CourseStatus.APPROVED.getCode());
|
||||||
|
}
|
||||||
|
|
||||||
pkg.setReviewedAt(LocalDateTime.now());
|
pkg.setReviewedAt(LocalDateTime.now());
|
||||||
pkg.setReviewedBy(userId);
|
pkg.setReviewedBy(userId);
|
||||||
pkg.setReviewComment(comment);
|
pkg.setReviewComment(comment);
|
||||||
packageMapper.updateById(pkg);
|
packageMapper.updateById(pkg);
|
||||||
log.info("套餐审核成功,id={}, result={}", id, approved ? "approved" : "rejected");
|
log.info("套餐审核成功,id={}, result={}", id,
|
||||||
|
!Boolean.TRUE.equals(approved) ? "rejected" :
|
||||||
|
(Boolean.TRUE.equals(publish) ? "approved_and_published" : "approved"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -0,0 +1,43 @@
|
|||||||
|
package com.reading.platform.service;
|
||||||
|
|
||||||
|
import com.reading.platform.dto.request.ActiveTenantsQueryRequest;
|
||||||
|
import com.reading.platform.dto.request.PopularCoursesQueryRequest;
|
||||||
|
import com.reading.platform.dto.request.RecentActivitiesQueryRequest;
|
||||||
|
import com.reading.platform.dto.response.ActiveTenantItemResponse;
|
||||||
|
import com.reading.platform.dto.response.PopularCourseItemResponse;
|
||||||
|
import com.reading.platform.dto.response.RecentActivityItemResponse;
|
||||||
|
import com.reading.platform.dto.response.StatsResponse;
|
||||||
|
import com.reading.platform.dto.response.StatsTrendResponse;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统计服务接口
|
||||||
|
*/
|
||||||
|
public interface StatsService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取统计数据
|
||||||
|
*/
|
||||||
|
StatsResponse getStats();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取趋势数据
|
||||||
|
*/
|
||||||
|
StatsTrendResponse getTrendData();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取活跃租户
|
||||||
|
*/
|
||||||
|
List<ActiveTenantItemResponse> getActiveTenants(ActiveTenantsQueryRequest request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取热门课程
|
||||||
|
*/
|
||||||
|
List<PopularCourseItemResponse> getPopularCourses(PopularCoursesQueryRequest request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取最近活动
|
||||||
|
*/
|
||||||
|
List<RecentActivityItemResponse> getRecentActivities(RecentActivitiesQueryRequest request);
|
||||||
|
}
|
||||||
@ -3,6 +3,7 @@ package com.reading.platform.service;
|
|||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.reading.platform.dto.request.TenantCreateRequest;
|
import com.reading.platform.dto.request.TenantCreateRequest;
|
||||||
import com.reading.platform.dto.request.TenantUpdateRequest;
|
import com.reading.platform.dto.request.TenantUpdateRequest;
|
||||||
|
import com.reading.platform.dto.response.TenantResponse;
|
||||||
import com.reading.platform.entity.Tenant;
|
import com.reading.platform.entity.Tenant;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -32,6 +33,11 @@ public interface TenantService extends com.baomidou.mybatisplus.extension.servic
|
|||||||
*/
|
*/
|
||||||
Page<Tenant> getTenantPage(Integer pageNum, Integer pageSize, String keyword, String status);
|
Page<Tenant> getTenantPage(Integer pageNum, Integer pageSize, String keyword, String status);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询租户(带教师和学生统计)
|
||||||
|
*/
|
||||||
|
Page<TenantResponse> getTenantPageWithStats(Integer pageNum, Integer pageSize, String keyword, String status);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除租户
|
* 删除租户
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|||||||
import com.reading.platform.common.enums.CourseStatus;
|
import com.reading.platform.common.enums.CourseStatus;
|
||||||
import com.reading.platform.common.enums.ErrorCode;
|
import com.reading.platform.common.enums.ErrorCode;
|
||||||
import com.reading.platform.common.enums.TenantPackageStatus;
|
import com.reading.platform.common.enums.TenantPackageStatus;
|
||||||
|
import com.reading.platform.common.enums.YesNo;
|
||||||
import com.reading.platform.common.exception.BusinessException;
|
import com.reading.platform.common.exception.BusinessException;
|
||||||
import com.reading.platform.dto.request.CourseCreateRequest;
|
import com.reading.platform.dto.request.CourseCreateRequest;
|
||||||
import com.reading.platform.dto.request.CourseUpdateRequest;
|
import com.reading.platform.dto.request.CourseUpdateRequest;
|
||||||
@ -60,7 +61,7 @@ public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course>
|
|||||||
course.setDurationMinutes(request.getDurationMinutes());
|
course.setDurationMinutes(request.getDurationMinutes());
|
||||||
course.setObjectives(request.getObjectives());
|
course.setObjectives(request.getObjectives());
|
||||||
course.setStatus(CourseStatus.DRAFT.getCode());
|
course.setStatus(CourseStatus.DRAFT.getCode());
|
||||||
course.setIsSystem(0);
|
course.setIsSystem(YesNo.NO.getCode());
|
||||||
|
|
||||||
// Course Package Fields
|
// Course Package Fields
|
||||||
course.setCoreContent(request.getCoreContent());
|
course.setCoreContent(request.getCoreContent());
|
||||||
@ -90,10 +91,10 @@ public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course>
|
|||||||
course.setAssessmentData(nullIfEmptyJson(request.getAssessmentData()));
|
course.setAssessmentData(nullIfEmptyJson(request.getAssessmentData()));
|
||||||
course.setGradeTags(nullIfEmptyJson(request.getGradeTags()));
|
course.setGradeTags(nullIfEmptyJson(request.getGradeTags()));
|
||||||
course.setDomainTags(nullIfEmptyJson(request.getDomainTags()));
|
course.setDomainTags(nullIfEmptyJson(request.getDomainTags()));
|
||||||
course.setHasCollectiveLesson(request.getHasCollectiveLesson() != null && request.getHasCollectiveLesson() ? 1 : 0);
|
course.setHasCollectiveLesson(YesNo.fromBoolean(request.getHasCollectiveLesson()).getCode());
|
||||||
|
|
||||||
course.setVersion("1.0");
|
course.setVersion("1.0");
|
||||||
course.setIsLatest(1);
|
course.setIsLatest(YesNo.YES.getCode());
|
||||||
course.setUsageCount(0);
|
course.setUsageCount(0);
|
||||||
course.setTeacherCount(0);
|
course.setTeacherCount(0);
|
||||||
|
|
||||||
@ -121,7 +122,7 @@ public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course>
|
|||||||
course.setDurationMinutes(request.getDurationMinutes());
|
course.setDurationMinutes(request.getDurationMinutes());
|
||||||
course.setObjectives(request.getObjectives());
|
course.setObjectives(request.getObjectives());
|
||||||
course.setStatus(CourseStatus.DRAFT.getCode());
|
course.setStatus(CourseStatus.DRAFT.getCode());
|
||||||
course.setIsSystem(1);
|
course.setIsSystem(YesNo.YES.getCode());
|
||||||
|
|
||||||
// Course Package Fields
|
// Course Package Fields
|
||||||
course.setCoreContent(request.getCoreContent());
|
course.setCoreContent(request.getCoreContent());
|
||||||
@ -151,10 +152,10 @@ public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course>
|
|||||||
course.setAssessmentData(nullIfEmptyJson(request.getAssessmentData()));
|
course.setAssessmentData(nullIfEmptyJson(request.getAssessmentData()));
|
||||||
course.setGradeTags(nullIfEmptyJson(request.getGradeTags()));
|
course.setGradeTags(nullIfEmptyJson(request.getGradeTags()));
|
||||||
course.setDomainTags(nullIfEmptyJson(request.getDomainTags()));
|
course.setDomainTags(nullIfEmptyJson(request.getDomainTags()));
|
||||||
course.setHasCollectiveLesson(request.getHasCollectiveLesson() != null && request.getHasCollectiveLesson() ? 1 : 0);
|
course.setHasCollectiveLesson(YesNo.fromBoolean(request.getHasCollectiveLesson()).getCode());
|
||||||
|
|
||||||
course.setVersion("1.0");
|
course.setVersion("1.0");
|
||||||
course.setIsLatest(1);
|
course.setIsLatest(YesNo.YES.getCode());
|
||||||
course.setUsageCount(0);
|
course.setUsageCount(0);
|
||||||
course.setTeacherCount(0);
|
course.setTeacherCount(0);
|
||||||
|
|
||||||
@ -287,7 +288,7 @@ public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course>
|
|||||||
course.setDomainTags(nullIfEmptyJson(request.getDomainTags()));
|
course.setDomainTags(nullIfEmptyJson(request.getDomainTags()));
|
||||||
}
|
}
|
||||||
if (request.getHasCollectiveLesson() != null) {
|
if (request.getHasCollectiveLesson() != null) {
|
||||||
course.setHasCollectiveLesson(request.getHasCollectiveLesson() ? 1 : 0);
|
course.setHasCollectiveLesson(YesNo.fromBoolean(request.getHasCollectiveLesson()).getCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
courseMapper.updateById(course);
|
courseMapper.updateById(course);
|
||||||
@ -346,7 +347,7 @@ public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course>
|
|||||||
wrapper.and(w -> w
|
wrapper.and(w -> w
|
||||||
.eq(Course::getTenantId, tenantId)
|
.eq(Course::getTenantId, tenantId)
|
||||||
.or()
|
.or()
|
||||||
.eq(Course::getIsSystem, 1)
|
.eq(Course::getIsSystem, YesNo.YES.getCode())
|
||||||
);
|
);
|
||||||
|
|
||||||
if (StringUtils.hasText(keyword)) {
|
if (StringUtils.hasText(keyword)) {
|
||||||
@ -374,7 +375,7 @@ public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course>
|
|||||||
LambdaQueryWrapper<Course> wrapper = new LambdaQueryWrapper<>();
|
LambdaQueryWrapper<Course> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
|
||||||
// 只过滤系统课程
|
// 只过滤系统课程
|
||||||
wrapper.eq(Course::getIsSystem, 1);
|
wrapper.eq(Course::getIsSystem, YesNo.YES.getCode());
|
||||||
|
|
||||||
// 审核管理页:仅过滤待审核和已驳回,排除已通过/已发布
|
// 审核管理页:仅过滤待审核和已驳回,排除已通过/已发布
|
||||||
if (reviewOnly) {
|
if (reviewOnly) {
|
||||||
@ -453,7 +454,7 @@ public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course>
|
|||||||
.and(w -> w
|
.and(w -> w
|
||||||
.eq(Course::getTenantId, tenantId)
|
.eq(Course::getTenantId, tenantId)
|
||||||
.or()
|
.or()
|
||||||
.eq(Course::getIsSystem, 1)
|
.eq(Course::getIsSystem, YesNo.YES.getCode())
|
||||||
)
|
)
|
||||||
.eq(Course::getStatus, CourseStatus.PUBLISHED.getCode())
|
.eq(Course::getStatus, CourseStatus.PUBLISHED.getCode())
|
||||||
.orderByAsc(Course::getName)
|
.orderByAsc(Course::getName)
|
||||||
|
|||||||
@ -0,0 +1,224 @@
|
|||||||
|
package com.reading.platform.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.reading.platform.dto.request.ActiveTenantsQueryRequest;
|
||||||
|
import com.reading.platform.dto.request.PopularCoursesQueryRequest;
|
||||||
|
import com.reading.platform.dto.request.RecentActivitiesQueryRequest;
|
||||||
|
import com.reading.platform.dto.response.ActiveTenantItemResponse;
|
||||||
|
import com.reading.platform.dto.response.PopularCourseItemResponse;
|
||||||
|
import com.reading.platform.dto.response.RecentActivityItemResponse;
|
||||||
|
import com.reading.platform.dto.response.StatsResponse;
|
||||||
|
import com.reading.platform.dto.response.StatsTrendResponse;
|
||||||
|
import com.reading.platform.entity.Course;
|
||||||
|
import com.reading.platform.entity.CourseLesson;
|
||||||
|
import com.reading.platform.entity.Student;
|
||||||
|
import com.reading.platform.entity.Teacher;
|
||||||
|
import com.reading.platform.entity.Tenant;
|
||||||
|
import com.reading.platform.mapper.CourseLessonMapper;
|
||||||
|
import com.reading.platform.mapper.CourseMapper;
|
||||||
|
import com.reading.platform.mapper.StudentMapper;
|
||||||
|
import com.reading.platform.mapper.TeacherMapper;
|
||||||
|
import com.reading.platform.mapper.TenantMapper;
|
||||||
|
import com.reading.platform.service.StatsService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统计服务实现类
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class StatsServiceImpl implements StatsService {
|
||||||
|
|
||||||
|
private final TenantMapper tenantMapper;
|
||||||
|
private final TeacherMapper teacherMapper;
|
||||||
|
private final StudentMapper studentMapper;
|
||||||
|
private final CourseMapper courseMapper;
|
||||||
|
private final CourseLessonMapper courseLessonMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StatsResponse getStats() {
|
||||||
|
log.info("获取统计数据");
|
||||||
|
|
||||||
|
// 租户总数
|
||||||
|
Long totalTenants = tenantMapper.selectCount(null);
|
||||||
|
|
||||||
|
// 活跃租户数
|
||||||
|
Long activeTenants = tenantMapper.selectCount(
|
||||||
|
new LambdaQueryWrapper<Tenant>().eq(Tenant::getStatus, "ACTIVE")
|
||||||
|
);
|
||||||
|
|
||||||
|
// 教师总数
|
||||||
|
Long totalTeachers = teacherMapper.selectCount(null);
|
||||||
|
|
||||||
|
// 学生总数
|
||||||
|
Long totalStudents = studentMapper.selectCount(null);
|
||||||
|
|
||||||
|
// 课程总数
|
||||||
|
Long totalCourses = courseMapper.selectCount(null);
|
||||||
|
|
||||||
|
// 课时总数
|
||||||
|
Long totalLessons = courseLessonMapper.selectCount(null);
|
||||||
|
|
||||||
|
return StatsResponse.builder()
|
||||||
|
.totalTenants(totalTenants)
|
||||||
|
.activeTenants(activeTenants)
|
||||||
|
.totalTeachers(totalTeachers)
|
||||||
|
.totalStudents(totalStudents)
|
||||||
|
.totalCourses(totalCourses)
|
||||||
|
.totalLessons(totalLessons)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StatsTrendResponse getTrendData() {
|
||||||
|
log.info("获取趋势数据");
|
||||||
|
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
List<String> dates = new ArrayList<>();
|
||||||
|
List<Integer> newStudents = new ArrayList<>();
|
||||||
|
List<Integer> newTeachers = new ArrayList<>();
|
||||||
|
List<Integer> newCourses = new ArrayList<>();
|
||||||
|
|
||||||
|
// 获取最近 7 天的趋势数据
|
||||||
|
for (int i = 6; i >= 0; i--) {
|
||||||
|
LocalDate date = now.minusDays(i).toLocalDate();
|
||||||
|
dates.add(date.format(DateTimeFormatter.ofPattern("MM-dd")));
|
||||||
|
|
||||||
|
// 当天新增学生数
|
||||||
|
LocalDateTime dayStart = date.atStartOfDay();
|
||||||
|
LocalDateTime dayEnd = date.plusDays(1).atStartOfDay();
|
||||||
|
|
||||||
|
Long students = studentMapper.selectCount(
|
||||||
|
new LambdaQueryWrapper<Student>()
|
||||||
|
.ge(Student::getCreatedAt, dayStart)
|
||||||
|
.lt(Student::getCreatedAt, dayEnd)
|
||||||
|
);
|
||||||
|
newStudents.add(students != null ? students.intValue() : 0);
|
||||||
|
|
||||||
|
// 当天新增教师数
|
||||||
|
Long teachers = teacherMapper.selectCount(
|
||||||
|
new LambdaQueryWrapper<Teacher>()
|
||||||
|
.ge(Teacher::getCreatedAt, dayStart)
|
||||||
|
.lt(Teacher::getCreatedAt, dayEnd)
|
||||||
|
);
|
||||||
|
newTeachers.add(teachers != null ? teachers.intValue() : 0);
|
||||||
|
|
||||||
|
// 当天新增课程数
|
||||||
|
Long courses = courseMapper.selectCount(
|
||||||
|
new LambdaQueryWrapper<Course>()
|
||||||
|
.ge(Course::getCreatedAt, dayStart)
|
||||||
|
.lt(Course::getCreatedAt, dayEnd)
|
||||||
|
);
|
||||||
|
newCourses.add(courses != null ? courses.intValue() : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return StatsTrendResponse.builder()
|
||||||
|
.dates(dates)
|
||||||
|
.newStudents(newStudents)
|
||||||
|
.newTeachers(newTeachers)
|
||||||
|
.newCourses(newCourses)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ActiveTenantItemResponse> getActiveTenants(ActiveTenantsQueryRequest request) {
|
||||||
|
log.info("获取活跃租户,limit={}", request != null ? request.getLimit() : 10);
|
||||||
|
|
||||||
|
int limit = request != null && request.getLimit() != null ? request.getLimit() : 10;
|
||||||
|
|
||||||
|
// 查询所有活跃租户
|
||||||
|
List<Tenant> tenants = tenantMapper.selectList(
|
||||||
|
new LambdaQueryWrapper<Tenant>()
|
||||||
|
.eq(Tenant::getStatus, "ACTIVE")
|
||||||
|
.orderByDesc(Tenant::getUpdatedAt)
|
||||||
|
.last("LIMIT " + limit)
|
||||||
|
);
|
||||||
|
|
||||||
|
return tenants.stream().map(tenant -> {
|
||||||
|
// 查询该租户的活跃用户数(教师+学生)
|
||||||
|
Long teacherCount = teacherMapper.selectCount(
|
||||||
|
new LambdaQueryWrapper<Teacher>().eq(Teacher::getTenantId, tenant.getId())
|
||||||
|
);
|
||||||
|
Long studentCount = studentMapper.selectCount(
|
||||||
|
new LambdaQueryWrapper<Student>().eq(Student::getTenantId, tenant.getId())
|
||||||
|
);
|
||||||
|
|
||||||
|
// 查询该租户使用的课程数(通过租户套餐)
|
||||||
|
// 简化处理,返回 0
|
||||||
|
int courseCount = 0;
|
||||||
|
|
||||||
|
return ActiveTenantItemResponse.builder()
|
||||||
|
.tenantId(tenant.getId())
|
||||||
|
.tenantName(tenant.getName())
|
||||||
|
.activeUsers((teacherCount != null ? teacherCount.intValue() : 0) +
|
||||||
|
(studentCount != null ? studentCount.intValue() : 0))
|
||||||
|
.courseCount(courseCount)
|
||||||
|
.build();
|
||||||
|
}).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<PopularCourseItemResponse> getPopularCourses(PopularCoursesQueryRequest request) {
|
||||||
|
log.info("获取热门课程,limit={}", request != null ? request.getLimit() : 10);
|
||||||
|
|
||||||
|
int limit = request != null && request.getLimit() != null ? request.getLimit() : 10;
|
||||||
|
|
||||||
|
// 查询使用次数最多的课程
|
||||||
|
List<Course> courses = courseMapper.selectList(
|
||||||
|
new LambdaQueryWrapper<Course>()
|
||||||
|
.eq(Course::getIsSystem, 1)
|
||||||
|
.orderByDesc(Course::getUsageCount)
|
||||||
|
.last("LIMIT " + limit)
|
||||||
|
);
|
||||||
|
|
||||||
|
return courses.stream().map(course -> PopularCourseItemResponse.builder()
|
||||||
|
.courseId(course.getId())
|
||||||
|
.courseName(course.getName())
|
||||||
|
.usageCount(course.getUsageCount() != null ? course.getUsageCount() : 0)
|
||||||
|
.teacherCount(course.getTeacherCount() != null ? course.getTeacherCount() : 0)
|
||||||
|
.build()
|
||||||
|
).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<RecentActivityItemResponse> getRecentActivities(RecentActivitiesQueryRequest request) {
|
||||||
|
log.info("获取最近活动,limit={}", request != null ? request.getLimit() : 10);
|
||||||
|
|
||||||
|
int limit = request != null && request.getLimit() != null ? request.getLimit() : 10;
|
||||||
|
|
||||||
|
// 由于没有专门的活动记录表,这里返回空列表
|
||||||
|
// 实际项目中应该从操作日志表获取
|
||||||
|
List<RecentActivityItemResponse> activities = new ArrayList<>();
|
||||||
|
|
||||||
|
// 可以从最近的课程更新中生成一些活动记录
|
||||||
|
List<Course> recentCourses = courseMapper.selectList(
|
||||||
|
new LambdaQueryWrapper<Course>()
|
||||||
|
.orderByDesc(Course::getUpdatedAt)
|
||||||
|
.last("LIMIT " + limit)
|
||||||
|
);
|
||||||
|
|
||||||
|
for (Course course : recentCourses) {
|
||||||
|
activities.add(RecentActivityItemResponse.builder()
|
||||||
|
.activityId(course.getId())
|
||||||
|
.activityType("COURSE_UPDATE")
|
||||||
|
.description("课程「" + course.getName() + "」已更新")
|
||||||
|
.operatorId(course.getSubmittedBy())
|
||||||
|
.operatorName("系统")
|
||||||
|
.operationTime(course.getUpdatedAt())
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
return activities;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -7,9 +7,14 @@ import com.reading.platform.common.enums.TenantPackageStatus;
|
|||||||
import com.reading.platform.common.exception.BusinessException;
|
import com.reading.platform.common.exception.BusinessException;
|
||||||
import com.reading.platform.dto.request.TenantCreateRequest;
|
import com.reading.platform.dto.request.TenantCreateRequest;
|
||||||
import com.reading.platform.dto.request.TenantUpdateRequest;
|
import com.reading.platform.dto.request.TenantUpdateRequest;
|
||||||
|
import com.reading.platform.dto.response.TenantResponse;
|
||||||
import com.reading.platform.entity.CoursePackage;
|
import com.reading.platform.entity.CoursePackage;
|
||||||
|
import com.reading.platform.entity.Student;
|
||||||
|
import com.reading.platform.entity.Teacher;
|
||||||
import com.reading.platform.entity.Tenant;
|
import com.reading.platform.entity.Tenant;
|
||||||
import com.reading.platform.entity.TenantPackage;
|
import com.reading.platform.entity.TenantPackage;
|
||||||
|
import com.reading.platform.mapper.StudentMapper;
|
||||||
|
import com.reading.platform.mapper.TeacherMapper;
|
||||||
import com.reading.platform.mapper.TenantMapper;
|
import com.reading.platform.mapper.TenantMapper;
|
||||||
import com.reading.platform.mapper.TenantPackageMapper;
|
import com.reading.platform.mapper.TenantPackageMapper;
|
||||||
import com.reading.platform.service.CoursePackageService;
|
import com.reading.platform.service.CoursePackageService;
|
||||||
@ -22,6 +27,7 @@ import org.springframework.util.StringUtils;
|
|||||||
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 租户服务实现类
|
* 租户服务实现类
|
||||||
@ -35,6 +41,8 @@ public class TenantServiceImpl extends com.baomidou.mybatisplus.extension.servic
|
|||||||
private final TenantMapper tenantMapper;
|
private final TenantMapper tenantMapper;
|
||||||
private final TenantPackageMapper tenantPackageMapper;
|
private final TenantPackageMapper tenantPackageMapper;
|
||||||
private final CoursePackageService coursePackageService;
|
private final CoursePackageService coursePackageService;
|
||||||
|
private final TeacherMapper teacherMapper;
|
||||||
|
private final StudentMapper studentMapper;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
@ -240,4 +248,67 @@ public class TenantServiceImpl extends com.baomidou.mybatisplus.extension.servic
|
|||||||
return tenants;
|
return tenants;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Page<TenantResponse> getTenantPageWithStats(Integer pageNum, Integer pageSize, String keyword, String status) {
|
||||||
|
log.debug("分页查询租户(带统计),页码:{},每页数量:{}", pageNum, pageSize);
|
||||||
|
|
||||||
|
// 先查询租户分页数据
|
||||||
|
Page<Tenant> tenantPage = getTenantPage(pageNum, pageSize, keyword, status);
|
||||||
|
|
||||||
|
// 转换为 TenantResponse 并填充统计信息
|
||||||
|
List<TenantResponse> responses = tenantPage.getRecords().stream()
|
||||||
|
.map(this::toResponseWithStats)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
// 构建返回的分页对象
|
||||||
|
Page<TenantResponse> resultPage = new Page<>(tenantPage.getCurrent(), tenantPage.getSize(), tenantPage.getTotal());
|
||||||
|
resultPage.setRecords(responses);
|
||||||
|
|
||||||
|
return resultPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 Tenant 实体转换为 TenantResponse(带教师和学生统计)
|
||||||
|
*/
|
||||||
|
private TenantResponse toResponseWithStats(Tenant tenant) {
|
||||||
|
// 查询教师数量
|
||||||
|
Long teacherCount = teacherMapper.selectCount(
|
||||||
|
new LambdaQueryWrapper<Teacher>()
|
||||||
|
.eq(Teacher::getTenantId, tenant.getId())
|
||||||
|
);
|
||||||
|
|
||||||
|
// 查询学生数量
|
||||||
|
Long studentCount = studentMapper.selectCount(
|
||||||
|
new LambdaQueryWrapper<Student>()
|
||||||
|
.eq(Student::getTenantId, tenant.getId())
|
||||||
|
);
|
||||||
|
|
||||||
|
return TenantResponse.builder()
|
||||||
|
.id(tenant.getId())
|
||||||
|
.name(tenant.getName())
|
||||||
|
.code(tenant.getCode())
|
||||||
|
.username(tenant.getUsername())
|
||||||
|
.contactName(tenant.getContactName())
|
||||||
|
.contactPhone(tenant.getContactPhone())
|
||||||
|
.contactEmail(tenant.getContactEmail())
|
||||||
|
.address(tenant.getAddress())
|
||||||
|
.logoUrl(tenant.getLogoUrl())
|
||||||
|
.status(tenant.getStatus())
|
||||||
|
.expireAt(tenant.getExpireAt())
|
||||||
|
.maxStudents(tenant.getMaxStudents())
|
||||||
|
.maxTeachers(tenant.getMaxTeachers())
|
||||||
|
.packageType(tenant.getPackageType())
|
||||||
|
.teacherQuota(tenant.getTeacherQuota())
|
||||||
|
.studentQuota(tenant.getStudentQuota())
|
||||||
|
.storageQuota(tenant.getStorageQuota())
|
||||||
|
.storageUsed(tenant.getStorageUsed())
|
||||||
|
.teacherCount(teacherCount != null ? teacherCount.intValue() : 0)
|
||||||
|
.studentCount(studentCount != null ? studentCount.intValue() : 0)
|
||||||
|
.startDate(tenant.getStartDate())
|
||||||
|
.expireDate(tenant.getExpireDate())
|
||||||
|
.createdAt(tenant.getCreatedAt())
|
||||||
|
.updatedAt(tenant.getUpdatedAt())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user