feat: 套餐审核支持通过时同时发布
- 后端 PackageReviewRequest 新增 publish 字段 - 后端 CoursePackageService.reviewPackage 支持审核通过后直接发布 - 前端审核弹窗拆分为"通过"和"通过并发布"两个按钮 状态流转: - 驳回: status → REJECTED - 仅通过: status → APPROVED - 通过并发布: status → PUBLISHED Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
459fa434ac
commit
d57affd2ee
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<template #extra>
|
||||
<a-space>
|
||||
<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>
|
||||
<a-button @click="fetchPackages">
|
||||
@ -39,8 +39,8 @@
|
||||
</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'status'">
|
||||
<a-tag :color="record.status === 'PENDING_REVIEW' ? 'processing' : 'error'">
|
||||
{{ record.status === 'PENDING_REVIEW' ? '待审核' : '已驳回' }}
|
||||
<a-tag :color="record.status === 'PENDING' ? 'processing' : 'error'">
|
||||
{{ record.status === 'PENDING' ? '待审核' : '已驳回' }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'submittedAt'">
|
||||
@ -48,7 +48,7 @@
|
||||
</template>
|
||||
<template v-else-if="column.key === 'actions'">
|
||||
<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 v-if="record.status === 'REJECTED'" size="small" @click="viewRejectReason(record)">
|
||||
@ -107,12 +107,15 @@
|
||||
</a-form>
|
||||
|
||||
<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 type="default" danger :loading="reviewing" @click="rejectPackage">
|
||||
驳回
|
||||
</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-space>
|
||||
@ -144,7 +147,7 @@ const loadingDetail = ref(false);
|
||||
const packages = ref<CoursePackage[]>([]);
|
||||
|
||||
const filters = reactive<{ status?: string }>({
|
||||
status: 'PENDING_REVIEW',
|
||||
status: 'PENDING',
|
||||
});
|
||||
|
||||
const pagination = reactive({
|
||||
@ -227,7 +230,8 @@ const closeReviewModal = () => {
|
||||
currentPackage.value = null;
|
||||
};
|
||||
|
||||
const approvePackage = async () => {
|
||||
// 仅通过
|
||||
const approveOnly = async () => {
|
||||
if (!currentPackage.value) return;
|
||||
|
||||
reviewing.value = true;
|
||||
@ -235,6 +239,28 @@ const approvePackage = async () => {
|
||||
await reviewPackage(currentPackage.value.id, {
|
||||
approved: true,
|
||||
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('审核通过,套餐已发布');
|
||||
closeReviewModal();
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
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.enums.UserRole;
|
||||
import com.reading.platform.common.security.SecurityUtils;
|
||||
import com.reading.platform.common.response.PageResult;
|
||||
import com.reading.platform.common.response.Result;
|
||||
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.entity.CoursePackage;
|
||||
import com.reading.platform.service.CoursePackageService;
|
||||
@ -19,6 +20,7 @@ import org.springframework.web.bind.annotation.*;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 课程套餐控制器(超管端)
|
||||
@ -49,24 +51,25 @@ public class AdminPackageController {
|
||||
@PostMapping
|
||||
@Operation(summary = "创建套餐")
|
||||
@RequireRole(UserRole.ADMIN)
|
||||
public Result<CoursePackage> create(@Valid @RequestBody PackageCreateRequest request) {
|
||||
return Result.success(packageService.createPackage(
|
||||
public Result<CoursePackageResponse> create(@Valid @RequestBody PackageCreateRequest request) {
|
||||
CoursePackage pkg = packageService.createPackage(
|
||||
request.getName(),
|
||||
request.getDescription(),
|
||||
request.getPrice(),
|
||||
request.getDiscountPrice(),
|
||||
request.getDiscountType(),
|
||||
request.getGradeLevels()
|
||||
));
|
||||
);
|
||||
return Result.success(packageService.findOnePackage(pkg.getId()));
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
@Operation(summary = "更新套餐")
|
||||
@RequireRole(UserRole.ADMIN)
|
||||
public Result<CoursePackage> update(
|
||||
public Result<CoursePackageResponse> update(
|
||||
@PathVariable Long id,
|
||||
@RequestBody PackageCreateRequest request) {
|
||||
return Result.success(packageService.updatePackage(
|
||||
packageService.updatePackage(
|
||||
id,
|
||||
request.getName(),
|
||||
request.getDescription(),
|
||||
@ -74,7 +77,8 @@ public class AdminPackageController {
|
||||
request.getDiscountPrice(),
|
||||
request.getDiscountType(),
|
||||
request.getGradeLevels()
|
||||
));
|
||||
);
|
||||
return Result.success(packageService.findOnePackage(id));
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@ -108,8 +112,14 @@ public class AdminPackageController {
|
||||
@RequireRole(UserRole.ADMIN)
|
||||
public Result<Void> review(
|
||||
@PathVariable Long id,
|
||||
@RequestBody ReviewRequest request) {
|
||||
packageService.reviewPackage(id, SecurityUtils.getCurrentUserId(), request.getApproved(), request.getComment());
|
||||
@Valid @RequestBody PackageReviewRequest request) {
|
||||
packageService.reviewPackage(
|
||||
id,
|
||||
SecurityUtils.getCurrentUserId(),
|
||||
request.getApproved(),
|
||||
request.getComment(),
|
||||
request.getPublish()
|
||||
);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@ -131,8 +141,12 @@ public class AdminPackageController {
|
||||
|
||||
@GetMapping("/all")
|
||||
@Operation(summary = "查询所有已发布的套餐列表")
|
||||
public Result<List<CoursePackage>> getPublishedPackages() {
|
||||
return Result.success(packageService.findPublishedPackages());
|
||||
public Result<List<CoursePackageResponse>> getPublishedPackages() {
|
||||
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")
|
||||
@ -140,7 +154,7 @@ public class AdminPackageController {
|
||||
@RequireRole(UserRole.ADMIN)
|
||||
public Result<Void> grantToTenant(
|
||||
@PathVariable Long id,
|
||||
@RequestBody GrantRequest request) {
|
||||
@Valid @RequestBody PackageGrantRequest request) {
|
||||
LocalDate endDate = LocalDate.parse(request.getEndDate(), DateTimeFormatter.ISO_DATE);
|
||||
packageService.renewTenantPackage(
|
||||
request.getTenantId(),
|
||||
@ -150,33 +164,4 @@ public class AdminPackageController {
|
||||
);
|
||||
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; }
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -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)
|
||||
public void reviewPackage(Long id, Long userId, Boolean approved, String comment) {
|
||||
log.info("审核套餐,id={}, userId={}, approved={}", id, userId, approved);
|
||||
public void reviewPackage(Long id, Long userId, Boolean approved, String comment, Boolean publish) {
|
||||
log.info("审核套餐,id={}, userId={}, approved={}, publish={}", id, userId, approved, publish);
|
||||
CoursePackage pkg = packageMapper.selectById(id);
|
||||
if (pkg == null) {
|
||||
log.warn("套餐不存在,id={}", id);
|
||||
@ -315,12 +320,27 @@ public class CoursePackageService extends ServiceImpl<CoursePackageMapper, Cours
|
||||
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.setReviewedBy(userId);
|
||||
pkg.setReviewComment(comment);
|
||||
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"));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Loading…
Reference in New Issue
Block a user