feat(website): 新增应用发布管理功能
- 在CmsWebsite实体中添加发布状态、定价模式、应用描述等相关字段 - 实现应用上架审核流程:提交审核、撤回申请、下架、管理员审批等功能 - 添加审核列表分页查询接口,支持按发布状态筛选 - 更新路由配置,调整开发者资源路径 - 添加数据库表结构变更SQL脚本,增加发布管理相关字段
This commit is contained in:
@@ -26,7 +26,7 @@ import java.util.Map;
|
||||
@Slf4j
|
||||
@Tag(name = "开发者资源管理")
|
||||
@RestController
|
||||
@RequestMapping("/api/_modules/app/developer-resource")
|
||||
@RequestMapping("/api/app/developer-resource")
|
||||
public class AppResourceController extends BaseController {
|
||||
|
||||
@Resource
|
||||
|
||||
@@ -562,4 +562,85 @@ public class CmsWebsiteController extends BaseController {
|
||||
return success("清除成功");
|
||||
}
|
||||
|
||||
// ─── 发布管理 ────────────────────────────────────────────────────────
|
||||
|
||||
@Operation(summary = "提交上架审核")
|
||||
@PostMapping("/submitReview")
|
||||
public ApiResult<?> submitReview(@RequestBody Map<String, Object> body) {
|
||||
Integer websiteId = body.get("websiteId") instanceof Number ? ((Number) body.get("websiteId")).intValue() : null;
|
||||
if (websiteId == null) return fail("websiteId 不能为空");
|
||||
String priceType = (String) body.get("priceType");
|
||||
Integer price = body.get("price") instanceof Number ? ((Number) body.get("price")).intValue() : 0;
|
||||
String subscriptionPeriod = (String) body.get("subscriptionPeriod");
|
||||
String appDescription = (String) body.get("appDescription");
|
||||
String detailDescription = (String) body.get("detailDescription");
|
||||
String screenshots = (String) body.get("screenshots");
|
||||
Integer userId = getLoginUserId();
|
||||
try {
|
||||
cmsWebsiteService.submitReview(websiteId, priceType, price, subscriptionPeriod,
|
||||
appDescription, detailDescription, screenshots, userId);
|
||||
return success("上架申请提交成功,等待审核");
|
||||
} catch (Exception e) {
|
||||
return fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "撤回审核申请")
|
||||
@PostMapping("/withdrawReview/{websiteId}")
|
||||
public ApiResult<?> withdrawReview(@PathVariable Integer websiteId) {
|
||||
try {
|
||||
cmsWebsiteService.withdrawReview(websiteId, getLoginUserId());
|
||||
return success("已撤回审核申请");
|
||||
} catch (Exception e) {
|
||||
return fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "下架应用")
|
||||
@PostMapping("/unpublish/{websiteId}")
|
||||
public ApiResult<?> unpublish(@PathVariable Integer websiteId) {
|
||||
try {
|
||||
cmsWebsiteService.unpublish(websiteId, getLoginUserId());
|
||||
return success("已下架");
|
||||
} catch (Exception e) {
|
||||
return fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@PreAuthorize("hasAuthority('cms:website:update')")
|
||||
@Operation(summary = "管理员审核通过")
|
||||
@PostMapping("/approveReview/{websiteId}")
|
||||
public ApiResult<?> approveReview(@PathVariable Integer websiteId) {
|
||||
try {
|
||||
cmsWebsiteService.approveReview(websiteId, getLoginUserId());
|
||||
return success("审核通过,应用已上架");
|
||||
} catch (Exception e) {
|
||||
return fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@PreAuthorize("hasAuthority('cms:website:update')")
|
||||
@Operation(summary = "管理员审核拒绝")
|
||||
@PostMapping("/rejectReview")
|
||||
public ApiResult<?> rejectReview(@RequestBody Map<String, Object> body) {
|
||||
Integer websiteId = body.get("websiteId") instanceof Number ? ((Number) body.get("websiteId")).intValue() : null;
|
||||
String rejectReason = (String) body.get("rejectReason");
|
||||
if (websiteId == null) return fail("websiteId 不能为空");
|
||||
if (rejectReason == null || rejectReason.isBlank()) return fail("请填写拒绝原因");
|
||||
try {
|
||||
cmsWebsiteService.rejectReview(websiteId, rejectReason, getLoginUserId());
|
||||
return success("已拒绝该应用上架申请");
|
||||
} catch (Exception e) {
|
||||
return fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@PreAuthorize("hasAuthority('cms:website:update')")
|
||||
@Operation(summary = "获取审核列表(管理员)")
|
||||
@GetMapping("/pageReviews")
|
||||
public ApiResult<PageResult<CmsWebsite>> pageReviews(CmsWebsiteParam param) {
|
||||
return success(cmsWebsiteService.pageReviews(param));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -321,6 +321,50 @@ public class CmsWebsite implements Serializable {
|
||||
@TableField(exist = false)
|
||||
private CmsWebsiteSetting setting;
|
||||
|
||||
// ─── 发布管理字段 ──────────────────────────────────────────────────
|
||||
|
||||
@Schema(description = "发布状态: developing开发中 pending_review待审核 published已上架 rejected审核未通过 deprecated已下架")
|
||||
private String publishStatus;
|
||||
|
||||
@Schema(description = "定价模式: free免费 one_time一次性 subscription订阅")
|
||||
private String priceType;
|
||||
|
||||
@Schema(description = "订阅周期: month按月 year按年")
|
||||
private String subscriptionPeriod;
|
||||
|
||||
@Schema(description = "应用简介(市场展示用)")
|
||||
private String appDescription;
|
||||
|
||||
@Schema(description = "详细说明(富文本)")
|
||||
private String detailDescription;
|
||||
|
||||
@Schema(description = "应用截图(JSON数组字符串)")
|
||||
private String screenshots;
|
||||
|
||||
@Schema(description = "安装/使用次数")
|
||||
private Integer installCount;
|
||||
|
||||
@Schema(description = "评分(1-5)")
|
||||
private java.math.BigDecimal rating;
|
||||
|
||||
@Schema(description = "审核拒绝原因")
|
||||
private String rejectReason;
|
||||
|
||||
@Schema(description = "提交审核时间")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private java.util.Date publishApplyTime;
|
||||
|
||||
@Schema(description = "正式发布上架时间")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private java.util.Date publishTime;
|
||||
|
||||
@Schema(description = "审核人用户ID")
|
||||
private Integer reviewerId;
|
||||
|
||||
@Schema(description = "审核操作时间")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private java.util.Date reviewTime;
|
||||
|
||||
public String getPhone(){
|
||||
return DesensitizedUtil.mobilePhone(this.phone);
|
||||
}
|
||||
|
||||
@@ -225,4 +225,7 @@ public class CmsWebsiteParam extends BaseParam {
|
||||
@QueryField(type = QueryType.EQ)
|
||||
private String adminPhone;
|
||||
|
||||
@Schema(description = "发布状态筛选: developing/pending_review/published/rejected/deprecated")
|
||||
private String publishStatus;
|
||||
|
||||
}
|
||||
|
||||
@@ -67,4 +67,26 @@ public interface CmsWebsiteService extends IService<CmsWebsite> {
|
||||
* @param tenantId 租户ID
|
||||
*/
|
||||
void clearSiteInfoCache(Integer tenantId);
|
||||
|
||||
// ─── 发布管理 ──────────────────────────────────────────────────
|
||||
|
||||
/** 提交上架审核 */
|
||||
void submitReview(Integer websiteId, String priceType, Integer price,
|
||||
String subscriptionPeriod, String appDescription,
|
||||
String detailDescription, String screenshots, Integer userId);
|
||||
|
||||
/** 撤回审核申请(回到开发中) */
|
||||
void withdrawReview(Integer websiteId, Integer userId);
|
||||
|
||||
/** 下架应用 */
|
||||
void unpublish(Integer websiteId, Integer userId);
|
||||
|
||||
/** 管理员审核通过 */
|
||||
void approveReview(Integer websiteId, Integer reviewerId);
|
||||
|
||||
/** 管理员审核拒绝 */
|
||||
void rejectReview(Integer websiteId, String rejectReason, Integer reviewerId);
|
||||
|
||||
/** 分页查询审核列表(管理员) */
|
||||
PageResult<CmsWebsite> pageReviews(CmsWebsiteParam param);
|
||||
}
|
||||
|
||||
@@ -417,9 +417,113 @@ public class CmsWebsiteServiceImpl extends ServiceImpl<CmsWebsiteMapper, CmsWebs
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从数据库获取网站信息
|
||||
*/
|
||||
// ─── 发布管理 ──────────────────────────────────────────────────────────
|
||||
|
||||
@Override
|
||||
public void submitReview(Integer websiteId, String priceType, Integer price,
|
||||
String subscriptionPeriod, String appDescription,
|
||||
String detailDescription, String screenshots, Integer userId) {
|
||||
CmsWebsite website = getById(websiteId);
|
||||
if (website == null) throw new RuntimeException("应用不存在");
|
||||
// 只有开发中 / 审核未通过 才能提交
|
||||
String cur = website.getPublishStatus();
|
||||
if (!"developing".equals(cur) && !"rejected".equals(cur) && cur != null) {
|
||||
throw new RuntimeException("当前状态不允许提交审核");
|
||||
}
|
||||
CmsWebsite update = new CmsWebsite();
|
||||
update.setWebsiteId(websiteId);
|
||||
update.setPublishStatus("pending_review");
|
||||
update.setPriceType(priceType != null ? priceType : "free");
|
||||
update.setPrice(price != null ? new java.math.BigDecimal(price).movePointLeft(2) : java.math.BigDecimal.ZERO);
|
||||
update.setSubscriptionPeriod(subscriptionPeriod);
|
||||
update.setAppDescription(appDescription);
|
||||
update.setDetailDescription(detailDescription);
|
||||
update.setScreenshots(screenshots);
|
||||
update.setPublishApplyTime(new java.util.Date());
|
||||
update.setRejectReason(null);
|
||||
updateById(update);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void withdrawReview(Integer websiteId, Integer userId) {
|
||||
CmsWebsite website = getById(websiteId);
|
||||
if (website == null) throw new RuntimeException("应用不存在");
|
||||
if (!"pending_review".equals(website.getPublishStatus())) {
|
||||
throw new RuntimeException("当前状态不是待审核,无法撤回");
|
||||
}
|
||||
CmsWebsite update = new CmsWebsite();
|
||||
update.setWebsiteId(websiteId);
|
||||
update.setPublishStatus("developing");
|
||||
updateById(update);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unpublish(Integer websiteId, Integer userId) {
|
||||
CmsWebsite website = getById(websiteId);
|
||||
if (website == null) throw new RuntimeException("应用不存在");
|
||||
if (!"published".equals(website.getPublishStatus())) {
|
||||
throw new RuntimeException("应用未上架,无法下架");
|
||||
}
|
||||
CmsWebsite update = new CmsWebsite();
|
||||
update.setWebsiteId(websiteId);
|
||||
update.setPublishStatus("deprecated");
|
||||
updateById(update);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void approveReview(Integer websiteId, Integer reviewerId) {
|
||||
CmsWebsite website = getById(websiteId);
|
||||
if (website == null) throw new RuntimeException("应用不存在");
|
||||
if (!"pending_review".equals(website.getPublishStatus())) {
|
||||
throw new RuntimeException("当前状态不是待审核");
|
||||
}
|
||||
CmsWebsite update = new CmsWebsite();
|
||||
update.setWebsiteId(websiteId);
|
||||
update.setPublishStatus("published");
|
||||
update.setMarket(true);
|
||||
update.setReviewerId(reviewerId);
|
||||
update.setReviewTime(new java.util.Date());
|
||||
update.setPublishTime(new java.util.Date());
|
||||
update.setRejectReason(null);
|
||||
updateById(update);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rejectReview(Integer websiteId, String rejectReason, Integer reviewerId) {
|
||||
CmsWebsite website = getById(websiteId);
|
||||
if (website == null) throw new RuntimeException("应用不存在");
|
||||
if (!"pending_review".equals(website.getPublishStatus())) {
|
||||
throw new RuntimeException("当前状态不是待审核");
|
||||
}
|
||||
CmsWebsite update = new CmsWebsite();
|
||||
update.setWebsiteId(websiteId);
|
||||
update.setPublishStatus("rejected");
|
||||
update.setRejectReason(rejectReason);
|
||||
update.setReviewerId(reviewerId);
|
||||
update.setReviewTime(new java.util.Date());
|
||||
updateById(update);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<CmsWebsite> pageReviews(CmsWebsiteParam param) {
|
||||
com.baomidou.mybatisplus.extension.plugins.pagination.Page<CmsWebsite> page =
|
||||
new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(param.getCurrent(), param.getSize() > 0 ? param.getSize() : 20);
|
||||
com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<CmsWebsite> wrapper =
|
||||
new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<CmsWebsite>()
|
||||
.eq(CmsWebsite::getDeleted, 0)
|
||||
.isNotNull(CmsWebsite::getPublishStatus)
|
||||
.ne(CmsWebsite::getPublishStatus, "developing")
|
||||
.eq(cn.hutool.core.util.ObjectUtil.isNotEmpty(param.getPublishStatus()),
|
||||
CmsWebsite::getPublishStatus, param.getPublishStatus())
|
||||
.and(cn.hutool.core.util.ObjectUtil.isNotEmpty(param.getKeywords()), q ->
|
||||
q.like(CmsWebsite::getWebsiteName, param.getKeywords())
|
||||
.or().like(CmsWebsite::getWebsiteCode, param.getKeywords()))
|
||||
.last("ORDER BY FIELD(publish_status,'pending_review','rejected','published','deprecated'), publish_apply_time DESC");
|
||||
baseMapper.selectPage(page, wrapper);
|
||||
return new com.gxwebsoft.common.core.web.PageResult<>(page.getRecords(), page.getTotal());
|
||||
}
|
||||
|
||||
|
||||
private CmsWebsite getWebsiteFromDatabase(Integer tenantId) {
|
||||
return getByTenantId(tenantId);
|
||||
}
|
||||
|
||||
17
src/main/java/com/gxwebsoft/cms/sql/cms_website_publish.sql
Normal file
17
src/main/java/com/gxwebsoft/cms/sql/cms_website_publish.sql
Normal file
@@ -0,0 +1,17 @@
|
||||
-- 为 cms_website 表新增发布管理相关字段
|
||||
-- 执行前请确认已连接正确的数据库
|
||||
|
||||
ALTER TABLE `cms_website`
|
||||
ADD COLUMN `publish_status` VARCHAR(20) NULL DEFAULT 'developing' COMMENT '发布状态: developing开发中 pending_review待审核 published已上架 rejected审核未通过 deprecated已下架' AFTER `market`,
|
||||
ADD COLUMN `price_type` VARCHAR(20) NULL COMMENT '定价模式: free免费 one_time一次性 subscription订阅' AFTER `publish_status`,
|
||||
ADD COLUMN `subscription_period` VARCHAR(10) NULL COMMENT '订阅周期: month按月 year按年' AFTER `price_type`,
|
||||
ADD COLUMN `app_description` VARCHAR(500) NULL COMMENT '应用简介(市场展示用)' AFTER `subscription_period`,
|
||||
ADD COLUMN `detail_description` TEXT NULL COMMENT '详细说明(富文本)' AFTER `app_description`,
|
||||
ADD COLUMN `screenshots` TEXT NULL COMMENT '应用截图(JSON数组字符串)' AFTER `detail_description`,
|
||||
ADD COLUMN `install_count` INT NULL DEFAULT 0 COMMENT '安装/使用次数' AFTER `screenshots`,
|
||||
ADD COLUMN `rating` DECIMAL(3,1) NULL DEFAULT 0.0 COMMENT '评分(1-5)' AFTER `install_count`,
|
||||
ADD COLUMN `reject_reason` VARCHAR(500) NULL COMMENT '审核拒绝原因' AFTER `rating`,
|
||||
ADD COLUMN `publish_apply_time` DATETIME NULL COMMENT '提交审核时间' AFTER `reject_reason`,
|
||||
ADD COLUMN `publish_time` DATETIME NULL COMMENT '正式发布上架时间' AFTER `publish_apply_time`,
|
||||
ADD COLUMN `reviewer_id` INT NULL COMMENT '审核人用户ID' AFTER `publish_time`,
|
||||
ADD COLUMN `review_time` DATETIME NULL COMMENT '审核操作时间' AFTER `reviewer_id`;
|
||||
Reference in New Issue
Block a user