记得上下班打卡 | git大法好,push需谨慎

Commit b85d862d authored by wangyifan's avatar wangyifan

Merge branch 'dev-wyf-v1.8.0-goblin' into test

parents b74ea203 42f0228b
-- 正在现场 v1.8.0 商城艺人专题
-- 关联开发文档:docu/zhengzai-v1.8.0-艺人专题-开发文档.md
-- 艺人关联申请表
CREATE TABLE `goblin_artist_auth_apply` (
`mid` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增主键',
`apply_id` varchar(64) NOT NULL COMMENT '申请ID',
`store_id` varchar(64) NOT NULL COMMENT '申请店铺ID',
`artist_id` varchar(64) NOT NULL COMMENT '申请关联艺人ID,对应 kylin_artist.artist_id',
`attachment_urls` text NOT NULL COMMENT '申请资料图片URL列表,JSON数组',
`status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '0待审核 1已通过 2已拒绝 3已撤销',
`reviewer_id` varchar(64) DEFAULT NULL COMMENT '审核人ID',
`reviewer_name` varchar(100) DEFAULT NULL COMMENT '审核人名称',
`review_time` datetime DEFAULT NULL COMMENT '审核时间',
`review_remark` varchar(500) DEFAULT NULL COMMENT '通过备注',
`reject_reason` varchar(500) DEFAULT NULL COMMENT '驳回原因',
`auth_expire_at` datetime DEFAULT NULL COMMENT '授权截止时间(通过时写入)',
`created_at` datetime NOT NULL COMMENT '申请时间',
`updated_at` datetime NOT NULL COMMENT '更新时间',
PRIMARY KEY (`mid`),
UNIQUE KEY `uk_apply_id` (`apply_id`),
KEY `idx_store_status` (`store_id`, `status`),
KEY `idx_artist_status` (`artist_id`, `status`),
KEY `idx_created_at` (`created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='艺人关联申请表';
-- 艺人专题表
CREATE TABLE `goblin_artist_topic` (
`mid` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增主键',
`topic_id` varchar(64) NOT NULL COMMENT '专题ID',
`store_id` varchar(64) NOT NULL COMMENT '所属店铺ID',
`topic_name` varchar(100) NOT NULL COMMENT '专题名称',
`artist_id` varchar(64) DEFAULT NULL COMMENT '关联艺人ID,可空',
`template_type` tinyint(4) NOT NULL DEFAULT 1 COMMENT '1双列网格 2单列列表',
`banner_url` varchar(500) DEFAULT NULL COMMENT '专题banner',
`banner_height` int(11) NOT NULL DEFAULT 240 COMMENT 'banner高度px',
`on_sale_start` datetime DEFAULT NULL COMMENT '上架区间开始',
`on_sale_end` datetime DEFAULT NULL COMMENT '上架区间结束',
`status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '0待上架 1已上架 2已下架',
`sort` int(11) NOT NULL DEFAULT 0 COMMENT 'Admin排序值,越小越靠前',
`recommend_position` tinyint(4) NOT NULL DEFAULT 0 COMMENT '0无推荐 1~5推荐位',
`del_flg` tinyint(4) NOT NULL DEFAULT 0 COMMENT '0正常 1删除',
`created_at` datetime NOT NULL COMMENT '创建时间',
`updated_at` datetime NOT NULL COMMENT '更新时间',
PRIMARY KEY (`mid`),
UNIQUE KEY `uk_topic_id` (`topic_id`),
KEY `idx_store_status` (`store_id`, `status`, `del_flg`),
KEY `idx_sort` (`sort`, `created_at`),
KEY `idx_recommend` (`recommend_position`),
KEY `idx_artist_id` (`artist_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='艺人专题表';
-- 艺人专题商品关联表
CREATE TABLE `goblin_artist_topic_goods` (
`mid` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增主键',
`relation_id` varchar(64) NOT NULL COMMENT '关联ID',
`topic_id` varchar(64) NOT NULL COMMENT '专题ID',
`spu_id` varchar(64) NOT NULL COMMENT '本店商品SPU ID',
`sort` int(11) NOT NULL DEFAULT 0 COMMENT '专题内排序,越小越靠前',
`created_at` datetime NOT NULL COMMENT '创建时间',
`updated_at` datetime NOT NULL COMMENT '更新时间',
PRIMARY KEY (`mid`),
UNIQUE KEY `uk_relation_id` (`relation_id`),
UNIQUE KEY `uk_topic_spu` (`topic_id`, `spu_id`),
KEY `idx_topic_sort` (`topic_id`, `sort`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='艺人专题商品关联表';
package com.liquidnet.service.goblin.constant;
/**
* 艺人专题相关状态常量
*/
public final class GoblinArtistConst {
private GoblinArtistConst() {
}
/** 申请:待审核 */
public static final int APPLY_PENDING = 0;
/** 申请:已通过 */
public static final int APPLY_APPROVED = 1;
/** 申请:已拒绝 */
public static final int APPLY_REJECTED = 2;
/** 申请:已撤销 */
public static final int APPLY_REVOKED = 3;
/** 授权:有效 */
public static final int AUTH_VALID = 1;
/** 授权:失效 */
public static final int AUTH_INVALID = 0;
/** 专题:待上架 */
public static final int TOPIC_PENDING = 0;
/** 专题:已上架 */
public static final int TOPIC_ONLINE = 1;
/** 专题:已下架 */
public static final int TOPIC_OFFLINE = 2;
/** 模版:双列 */
public static final int TEMPLATE_GRID = 1;
/** 模版:单列 */
public static final int TEMPLATE_LIST = 2;
/** 未删除 */
public static final int DEL_NO = 0;
/** 已删除 */
public static final int DEL_YES = 1;
public static final int DEFAULT_BANNER_HEIGHT = 240;
}
...@@ -475,4 +475,14 @@ public class GoblinRedisConst { ...@@ -475,4 +475,14 @@ public class GoblinRedisConst {
*/ */
public static final String SQB_SKU_PRICE = PREFIX.concat("sqb:sku:price:"); public static final String SQB_SKU_PRICE = PREFIX.concat("sqb:sku:price:");
/* ----------------------------------------------------------------- */
/* 艺人专题 */
/* ----------------------------------------------------------------- */
/**
* 艺人专题信息(详情缓存)
* {goblin:artist:topic:{topic_id}, GoblinArtistTopicVo}
*/
public static final String ARTIST_TOPIC = PREFIX.concat("artist:topic:");
} }
package com.liquidnet.service.goblin.dto.manage;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import java.io.Serializable;
@ApiModel("商家端-撤销艺人关联申请入参")
@Data
public class GoblinStoreMgtArtistApplyRevokeParams implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(required = true, value = "申请ID")
@NotBlank(message = "申请ID不能为空")
private String applyId;
}
package com.liquidnet.service.goblin.dto.manage;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.util.List;
@ApiModel("商家端-提交艺人关联申请入参")
@Data
public class GoblinStoreMgtArtistApplySubmitParams implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(required = true, value = "艺人ID")
@NotBlank(message = "艺人ID不能为空")
private String artistId;
@ApiModelProperty(required = true, value = "申请资料图片URL列表")
@NotEmpty(message = "申请资料不能为空")
@Size(max = 20, message = "申请资料数量超限")
private List<String> attachmentUrls;
}
package com.liquidnet.service.goblin.dto.manage;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.List;
@ApiModel("商家端-创建艺人专题入参")
@Data
public class GoblinStoreMgtArtistTopicCreateParams implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(required = true, value = "专题名称")
@NotBlank(message = "专题名称不能为空")
@Size(max = 100, message = "专题名称长度超限")
private String topicName;
@ApiModelProperty(value = "关联艺人ID,非必填")
private String artistId;
@ApiModelProperty(value = "模版类型 1双列 2单列")
@NotNull(message = "模版类型不能为空")
private Integer templateType;
@ApiModelProperty(value = "专题banner")
private String bannerUrl;
@ApiModelProperty(value = "banner高度")
private Integer bannerHeight;
@ApiModelProperty(value = "上架开始时间")
private LocalDateTime onSaleStart;
@ApiModelProperty(value = "上架结束时间")
private LocalDateTime onSaleEnd;
@ApiModelProperty(value = "专题商品")
@Valid
private List<GoblinStoreMgtArtistTopicGoodsItemParams> goods;
}
package com.liquidnet.service.goblin.dto.manage;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import java.io.Serializable;
@ApiModel("商家端-删除艺人专题入参")
@Data
public class GoblinStoreMgtArtistTopicDeleteParams implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(required = true, value = "专题ID")
@NotBlank(message = "专题ID不能为空")
private String topicId;
}
package com.liquidnet.service.goblin.dto.manage;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
@ApiModel("商家端-专题商品项入参")
@Data
public class GoblinStoreMgtArtistTopicGoodsItemParams implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(required = true, value = "商品SPU ID")
@NotBlank(message = "商品SPU不能为空")
private String spuId;
@ApiModelProperty(required = true, value = "排序,越小越靠前")
@NotNull(message = "商品排序不能为空")
private Integer sort;
}
package com.liquidnet.service.goblin.dto.manage;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
@ApiModel("商家端-艺人专题上下架入参")
@Data
public class GoblinStoreMgtArtistTopicStatusParams implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(required = true, value = "专题ID")
@NotBlank(message = "专题ID不能为空")
private String topicId;
@ApiModelProperty(required = true, value = "状态 0待上架 1已上架 2已下架")
@NotNull(message = "状态不能为空")
private Integer status;
}
package com.liquidnet.service.goblin.dto.manage;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.List;
@ApiModel("商家端-编辑艺人专题入参")
@Data
public class GoblinStoreMgtArtistTopicUpdateParams implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(required = true, value = "专题ID")
@NotBlank(message = "专题ID不能为空")
private String topicId;
@ApiModelProperty(required = true, value = "专题名称")
@NotBlank(message = "专题名称不能为空")
@Size(max = 100, message = "专题名称长度超限")
private String topicName;
@ApiModelProperty(value = "关联艺人ID,非必填;传空字符串表示清空")
private String artistId;
@ApiModelProperty(value = "模版类型 1双列 2单列")
@NotNull(message = "模版类型不能为空")
private Integer templateType;
@ApiModelProperty(value = "专题banner")
private String bannerUrl;
@ApiModelProperty(value = "banner高度")
private Integer bannerHeight;
@ApiModelProperty(value = "上架开始时间")
private LocalDateTime onSaleStart;
@ApiModelProperty(value = "上架结束时间")
private LocalDateTime onSaleEnd;
@ApiModelProperty(value = "专题商品,全量替换")
@Valid
private List<GoblinStoreMgtArtistTopicGoodsItemParams> goods;
}
package com.liquidnet.service.goblin.dto.manage.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.List;
@ApiModel("商家端-艺人关联申请列表项")
@Data
public class GoblinStoreMgtArtistApplyListItemVo implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("申请ID")
private String applyId;
@ApiModelProperty("艺人ID")
private String artistId;
@ApiModelProperty("艺人名称")
private String artistName;
@ApiModelProperty("艺人头像")
private String avatarUrl;
@ApiModelProperty("申请附件")
private List<String> attachmentUrls;
@ApiModelProperty("申请时间")
private LocalDateTime applyTime;
@ApiModelProperty("审核状态 0待审核 1已通过 2已拒绝 3已撤销")
private Integer status;
@ApiModelProperty("审核时间")
private LocalDateTime reviewTime;
@ApiModelProperty("审核人")
private String reviewerName;
@ApiModelProperty("授权截止时间")
private LocalDateTime authExpireAt;
@ApiModelProperty("驳回原因")
private String rejectReason;
}
package com.liquidnet.service.goblin.dto.manage.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@ApiModel("商家端-已授权艺人列表出参")
@Data
public class GoblinStoreMgtArtistAuthListVo implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("列表")
private List<Item> list = new ArrayList<>();
@ApiModel("已授权艺人项")
@Data
public static class Item implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("艺人ID")
private String artistId;
@ApiModelProperty("艺人名称")
private String artistName;
@ApiModelProperty("艺人头像")
private String avatarUrl;
@ApiModelProperty("授权截止时间")
private LocalDateTime expireAt;
}
}
package com.liquidnet.service.goblin.dto.manage.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
@ApiModel("商家端-搜索艺人列表项")
@Data
public class GoblinStoreMgtArtistSearchItemVo implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("艺人ID")
private String artistId;
@ApiModelProperty("艺人名称")
private String artistName;
@ApiModelProperty("艺人头像")
private String avatarUrl;
@ApiModelProperty("艺人类型")
private Integer artistType;
}
package com.liquidnet.service.goblin.dto.manage.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@ApiModel("商家端-艺人专题详情出参")
@Data
public class GoblinStoreMgtArtistTopicDetailVo implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("专题ID")
private String topicId;
@ApiModelProperty("专题名称")
private String topicName;
@ApiModelProperty("关联艺人ID")
private String artistId;
@ApiModelProperty("关联艺人名称")
private String artistName;
@ApiModelProperty("艺人头像")
private String avatarUrl;
@ApiModelProperty("模版类型")
private Integer templateType;
@ApiModelProperty("banner")
private String bannerUrl;
@ApiModelProperty("banner高度")
private Integer bannerHeight;
@ApiModelProperty("上架开始")
private LocalDateTime onSaleStart;
@ApiModelProperty("上架结束")
private LocalDateTime onSaleEnd;
@ApiModelProperty("状态")
private Integer status;
@ApiModelProperty("商品列表")
private List<GoodsItem> goods = new ArrayList<>();
@ApiModel("专题详情商品项")
@Data
public static class GoodsItem implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("SPU ID")
private String spuId;
@ApiModelProperty("商品名称")
private String name;
@ApiModelProperty("封面")
private String coverPic;
@ApiModelProperty("售价")
private BigDecimal sellPrice;
@ApiModelProperty("排序")
private Integer sort;
}
}
package com.liquidnet.service.goblin.dto.manage.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
@ApiModel("商家端-专题可添加商品搜索项")
@Data
public class GoblinStoreMgtArtistTopicGoodsSearchItemVo implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("SPU ID")
private String spuId;
@ApiModelProperty("商品名称")
private String name;
@ApiModelProperty("封面")
private String coverPic;
@ApiModelProperty("售价")
private BigDecimal sellPrice;
}
package com.liquidnet.service.goblin.dto.manage.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@ApiModel("商家端-艺人专题分页出参")
@Data
public class GoblinStoreMgtArtistTopicPageVo implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("总数(当前筛选)")
private long total;
@ApiModelProperty("已上架数量")
private long countOnline;
@ApiModelProperty("待上架数量")
private long countPending;
@ApiModelProperty("已下架数量")
private long countOffline;
@ApiModelProperty("列表")
private List<Item> list = new ArrayList<>();
@ApiModel("艺人专题列表项")
@Data
public static class Item implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("专题ID")
private String topicId;
@ApiModelProperty("专题名称")
private String topicName;
@ApiModelProperty("关联艺人ID")
private String artistId;
@ApiModelProperty("关联艺人名称")
private String artistName;
@ApiModelProperty("艺人头像")
private String avatarUrl;
@ApiModelProperty("关联商品数量")
private Integer goodsCount;
@ApiModelProperty("上架开始时间")
private LocalDateTime onSaleStart;
@ApiModelProperty("上架结束时间")
private LocalDateTime onSaleEnd;
@ApiModelProperty("状态 0待上架 1已上架 2已下架")
private Integer status;
}
}
package com.liquidnet.service.goblin.dto.vo;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
public class GoblinArtistAuthApplyVo implements Serializable {
private static final long serialVersionUID = 1L;
private String applyId;
private String storeId;
private String artistId;
private String attachmentUrls;
private Integer status;
private String reviewerId;
private String reviewerName;
private LocalDateTime reviewTime;
private String reviewRemark;
private String rejectReason;
private LocalDateTime authExpireAt;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
package com.liquidnet.service.goblin.dto.vo;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Data
public class GoblinArtistTopicVo implements Serializable {
private static final long serialVersionUID = 1L;
private String topicId;
private String storeId;
private String topicName;
private String artistId;
private Integer templateType;
private String bannerUrl;
private Integer bannerHeight;
private LocalDateTime onSaleStart;
private LocalDateTime onSaleEnd;
private Integer status;
private Integer sort;
private Integer recommendPosition;
private Integer delFlg;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
private List<GoodsItem> goods = new ArrayList<>();
@Data
public static class GoodsItem implements Serializable {
private static final long serialVersionUID = 1L;
private String spuId;
private Integer sort;
private String name;
private String coverPic;
private BigDecimal sellPrice;
}
}
package com.liquidnet.service.goblin.dto.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@ApiModel(value = "GoblinFrontArtistTopicDetailVo", description = "C端艺人专题详情")
@Data
public class GoblinFrontArtistTopicDetailVo implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("专题ID")
private String topicId;
@ApiModelProperty("专题名称")
private String topicName;
@ApiModelProperty("关联艺人ID")
private String artistId;
@ApiModelProperty("艺人名称")
private String artistName;
@ApiModelProperty("艺人头像")
private String avatarUrl;
@ApiModelProperty("模版类型 1双列 2单列")
private Integer templateType;
@ApiModelProperty("专题banner")
private String bannerUrl;
@ApiModelProperty("banner高度px")
private Integer bannerHeight;
@ApiModelProperty("上架区间开始")
private LocalDateTime onSaleStart;
@ApiModelProperty("上架区间结束")
private LocalDateTime onSaleEnd;
@ApiModelProperty("商品列表(仅已上架可售)")
private List<GoodsItem> goods = new ArrayList<>();
@Data
public static class GoodsItem implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("商品SPU ID")
private String spuId;
@ApiModelProperty("商品名称")
private String name;
@ApiModelProperty("商品头图")
private String coverPic;
@ApiModelProperty("销售价")
private BigDecimal sellPrice;
@ApiModelProperty("价格区间MIN")
private BigDecimal priceGe;
@ApiModelProperty("价格区间MAX")
private BigDecimal priceLe;
@ApiModelProperty("专题内排序")
private Integer sort;
}
}
package com.liquidnet.service.goblin.dto.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@ApiModel(value = "GoblinFrontArtistTopicHomeVo", description = "C端艺人专题首页入口")
@Data
public class GoblinFrontArtistTopicHomeVo implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("推荐位专题(按推荐号位升序)")
private List<TopicItem> topics = new ArrayList<>();
@ApiModelProperty("可见专题总数(更多入口)")
private long total;
@Data
public static class TopicItem implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("专题ID")
private String topicId;
@ApiModelProperty("专题名称")
private String topicName;
@ApiModelProperty("专题banner")
private String bannerUrl;
@ApiModelProperty("banner高度px")
private Integer bannerHeight;
@ApiModelProperty("推荐号位1~5")
private Integer recommendPosition;
@ApiModelProperty("关联艺人ID")
private String artistId;
@ApiModelProperty("艺人名称")
private String artistName;
@ApiModelProperty("艺人头像")
private String avatarUrl;
}
}
package com.liquidnet.service.goblin.dto.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@ApiModel(value = "GoblinFrontArtistTopicListPageVo", description = "C端艺人专题列表分页")
@Data
public class GoblinFrontArtistTopicListPageVo implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("页码")
private int page;
@ApiModelProperty("每页条数")
private int size;
@ApiModelProperty("总数")
private long total;
@ApiModelProperty("专题列表")
private List<TopicItem> list = new ArrayList<>();
@Data
public static class TopicItem implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("专题ID")
private String topicId;
@ApiModelProperty("专题名称")
private String topicName;
@ApiModelProperty("专题banner")
private String bannerUrl;
@ApiModelProperty("banner高度px")
private Integer bannerHeight;
@ApiModelProperty("推荐号位1~5")
private Integer recommendPosition;
@ApiModelProperty("关联艺人ID")
private String artistId;
@ApiModelProperty("艺人名称")
private String artistName;
@ApiModelProperty("艺人头像")
private String avatarUrl;
}
}
package com.liquidnet.service.goblin.service.manage;
import com.liquidnet.service.base.PagedResult;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.goblin.dto.manage.GoblinStoreMgtArtistApplyRevokeParams;
import com.liquidnet.service.goblin.dto.manage.GoblinStoreMgtArtistApplySubmitParams;
import com.liquidnet.service.goblin.dto.manage.GoblinStoreMgtArtistTopicCreateParams;
import com.liquidnet.service.goblin.dto.manage.GoblinStoreMgtArtistTopicDeleteParams;
import com.liquidnet.service.goblin.dto.manage.GoblinStoreMgtArtistTopicStatusParams;
import com.liquidnet.service.goblin.dto.manage.GoblinStoreMgtArtistTopicUpdateParams;
import com.liquidnet.service.goblin.dto.manage.vo.GoblinStoreMgtArtistApplyListItemVo;
import com.liquidnet.service.goblin.dto.manage.vo.GoblinStoreMgtArtistAuthListVo;
import com.liquidnet.service.goblin.dto.manage.vo.GoblinStoreMgtArtistSearchItemVo;
import com.liquidnet.service.goblin.dto.manage.vo.GoblinStoreMgtArtistTopicDetailVo;
import com.liquidnet.service.goblin.dto.manage.vo.GoblinStoreMgtArtistTopicGoodsSearchItemVo;
import com.liquidnet.service.goblin.dto.manage.vo.GoblinStoreMgtArtistTopicPageVo;
/**
* 商家端-艺人关联申请 / 艺人专题
* <p>店铺上下文:登录 UID → Redis 店铺信息取 storeId(对齐售后单管理)</p>
*/
public interface IGoblinStoreMgtArtistService {
/**
* 搜索可申请艺人(仅启用状态),支持关键词模糊匹配,DB 真分页
*/
ResponseDto<PagedResult<GoblinStoreMgtArtistSearchItemVo>> searchArtist(String keyword, int page, int size);
/**
* 提交艺人关联申请,校验艺人存在且启用、同店同艺人无待审核申请
*
* @return data = applyId
*/
ResponseDto<String> submitApply(GoblinStoreMgtArtistApplySubmitParams params);
/**
* 本店艺人关联申请列表,支持按状态筛选
*/
ResponseDto<PagedResult<GoblinStoreMgtArtistApplyListItemVo>> applyList(Integer status, int page, int size);
/**
* 撤销艺人关联申请,仅待审核状态可撤销
*/
ResponseDto<Void> revokeApply(GoblinStoreMgtArtistApplyRevokeParams params);
/**
* 本店已授权艺人列表,过滤过期和失效授权
*/
ResponseDto<GoblinStoreMgtArtistAuthListVo> authList();
/**
* 本店艺人专题列表(未删除),响应含各状态数量统计
*/
ResponseDto<GoblinStoreMgtArtistTopicPageVo> topicList(Integer status, int page, int size);
/**
* 艺人专题详情,含内嵌商品列表(名称/图片/价格),艺人名联查 kylin_artist
*/
ResponseDto<GoblinStoreMgtArtistTopicDetailVo> topicDetail(String topicId);
/**
* 创建艺人专题,校验关联艺人有本店有效授权、专题名称不重复、商品属本店且未删除
*
* @return data = topicId
*/
ResponseDto<String> topicCreate(GoblinStoreMgtArtistTopicCreateParams params);
/**
* 编辑艺人专题,商品全量替换
*/
ResponseDto<Void> topicUpdate(GoblinStoreMgtArtistTopicUpdateParams params);
/**
* 删除艺人专题(逻辑删除 delFlg=1)
*/
ResponseDto<Void> topicDelete(GoblinStoreMgtArtistTopicDeleteParams params);
/**
* 专题上下架,0=待上架 1=已上架 2=已下架
*/
ResponseDto<Void> topicStatus(GoblinStoreMgtArtistTopicStatusParams params);
/**
* 搜索本店可添加商品(普通商品,非市场活动),支持关键词模糊匹配,DB 真分页
*/
ResponseDto<PagedResult<GoblinStoreMgtArtistTopicGoodsSearchItemVo>> searchGoods(String keyword, int page, int size);
}
package com.liquidnet.client.admin.web.controller.zhengzai.goblin.artist;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.liquidnet.service.goblin.entity.GoblinStoreInfo;
import com.liquidnet.service.goblin.mapper.GoblinStoreInfoMapper;
import com.liquidnet.service.kylin.entity.KylinArtist;
import com.liquidnet.service.kylin.mapper.KylinArtistMapper;
import org.apache.commons.lang3.StringUtils;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* Admin 艺人模块共享工具方法
*/
final class ArtistAdminHelper {
private ArtistAdminHelper() {}
static final DateTimeFormatter FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
/** 按店铺名模糊查店铺ID,无输入返回 null */
static List<String> findStoreIds(GoblinStoreInfoMapper mapper, String storeName) {
if (StringUtils.isBlank(storeName)) return null;
return mapper.selectList(
new LambdaQueryWrapper<GoblinStoreInfo>()
.like(GoblinStoreInfo::getStoreName, storeName)
.select(GoblinStoreInfo::getStoreId))
.stream().map(GoblinStoreInfo::getStoreId).collect(Collectors.toList());
}
/** 按艺人名模糊查艺人ID,无输入返回 null */
static List<String> findArtistIds(KylinArtistMapper mapper, String artistName) {
if (StringUtils.isBlank(artistName)) return null;
return mapper.selectList(
new LambdaQueryWrapper<KylinArtist>()
.like(KylinArtist::getArtistName, artistName)
.select(KylinArtist::getArtistId))
.stream().map(KylinArtist::getArtistId).collect(Collectors.toList());
}
/** 批量查店铺名 */
static Map<String, String> storeNameMap(GoblinStoreInfoMapper mapper, Set<String> ids) {
if (ids.isEmpty()) return Collections.emptyMap();
return mapper.selectList(new LambdaQueryWrapper<GoblinStoreInfo>()
.in(GoblinStoreInfo::getStoreId, ids))
.stream().collect(Collectors.toMap(GoblinStoreInfo::getStoreId, GoblinStoreInfo::getStoreName, (a, b) -> a));
}
/** 批量查艺人名 */
static Map<String, String> artistNameMap(KylinArtistMapper mapper, Set<String> ids) {
if (ids.isEmpty()) return Collections.emptyMap();
return mapper.selectList(new LambdaQueryWrapper<KylinArtist>()
.in(KylinArtist::getArtistId, ids))
.stream().collect(Collectors.toMap(KylinArtist::getArtistId, KylinArtist::getArtistName, (a, b) -> a));
}
/** 从实体列表中提取 ID 集合 */
static <T> Set<String> idsOf(List<T> list, Function<T, String> getter) {
return list.stream().map(getter).filter(Objects::nonNull).collect(Collectors.toSet());
}
/** 时间格式化为 yyyy-MM-dd HH:mm:ss */
static String formatTime(LocalDateTime time) {
return time != null ? time.format(FMT) : null;
}
}
package com.liquidnet.client.admin.web.controller.zhengzai.goblin.artist;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.fasterxml.jackson.core.type.TypeReference;
import com.liquidnet.commons.lang.util.JsonUtils;
import com.liquidnet.client.admin.common.annotation.Log;
import com.liquidnet.client.admin.common.core.controller.BaseController;
import com.liquidnet.client.admin.common.core.domain.AjaxResult;
import com.liquidnet.client.admin.common.core.page.TableDataInfo;
import com.liquidnet.client.admin.common.enums.BusinessType;
import com.liquidnet.client.admin.common.utils.ShiroUtils;
import com.liquidnet.client.admin.zhengzai.goblin.service.IGoblinArtistAuthAdminService;
import com.liquidnet.service.goblin.entity.GoblinArtistAuthApply;
import com.liquidnet.service.goblin.entity.GoblinStoreInfo;
import com.liquidnet.service.goblin.mapper.GoblinStoreInfoMapper;
import com.liquidnet.service.kylin.entity.KylinArtist;
import com.liquidnet.service.kylin.mapper.KylinArtistMapper;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import static com.liquidnet.client.admin.web.controller.zhengzai.goblin.artist.ArtistAdminHelper.*;
@Controller
@RequestMapping("goblin/artist/auth")
public class GoblinArtistAuthAdminController extends BaseController {
private final String prefix = "zhengzai/goblin/artist/auth";
@Autowired
private IGoblinArtistAuthAdminService authAdminService;
@Autowired
private KylinArtistMapper kylinArtistMapper;
@Autowired
private GoblinStoreInfoMapper storeInfoMapper;
@GetMapping()
public String index() {
return prefix + "/auth";
}
@RequiresPermissions("goblin:artist:auth:list")
@RequestMapping("/list")
@ResponseBody
public TableDataInfo list(GoblinArtistAuthApply apply,
@RequestParam(required = false) String artistName,
@RequestParam(required = false) String storeName) {
LambdaQueryWrapper<GoblinArtistAuthApply> qw = Wrappers.lambdaQuery(GoblinArtistAuthApply.class);
if (apply.getStatus() != null) {
qw.eq(GoblinArtistAuthApply::getStatus, apply.getStatus());
}
List<String> sids = findStoreIds(storeInfoMapper, storeName);
if (sids != null) {
if (sids.isEmpty()) return getDataTable(Collections.emptyList());
qw.in(GoblinArtistAuthApply::getStoreId, sids);
}
List<String> aids = findArtistIds(kylinArtistMapper, artistName);
if (aids != null) {
if (aids.isEmpty()) return getDataTable(Collections.emptyList());
qw.in(GoblinArtistAuthApply::getArtistId, aids);
}
qw.orderByDesc(GoblinArtistAuthApply::getCreatedAt);
startPage();
return getDataTable(withNames(authAdminService.list(qw)));
}
private List<Map<String, Object>> withNames(List<GoblinArtistAuthApply> list) {
if (list.isEmpty()) return Collections.emptyList();
Map<String, String> storeNames = storeNameMap(storeInfoMapper, idsOf(list, GoblinArtistAuthApply::getStoreId));
Map<String, String> artistNames = artistNameMap(kylinArtistMapper, idsOf(list, GoblinArtistAuthApply::getArtistId));
return list.stream().map(item -> {
Map<String, Object> row = new LinkedHashMap<>();
row.put("applyId", item.getApplyId());
row.put("storeId", item.getStoreId());
row.put("storeName", storeNames.get(item.getStoreId()));
row.put("artistId", item.getArtistId());
row.put("artistName", artistNames.get(item.getArtistId()));
row.put("status", item.getStatus());
row.put("createdAt", formatTime(item.getCreatedAt()));
row.put("reviewTime", formatTime(item.getReviewTime()));
row.put("reviewerName", item.getReviewerName());
row.put("authExpireAt", formatTime(item.getAuthExpireAt()));
return row;
}).collect(Collectors.toList());
}
@RequiresPermissions("goblin:artist:auth:info")
@GetMapping("detail/{applyId}")
@ResponseBody
public AjaxResult detail(@PathVariable String applyId) {
Map<String, Object> data = buildDetail(applyId);
if (data == null) return AjaxResult.error("申请不存在");
return AjaxResult.success(data);
}
@RequiresPermissions("goblin:artist:auth:info")
@GetMapping("detailView/{applyId}")
public String detailView(@PathVariable String applyId, ModelMap mmap) {
Map<String, Object> data = buildDetail(applyId);
if (data == null) return prefix + "/auth";
mmap.put("apply", data);
return prefix + "/detail";
}
private Map<String, Object> buildDetail(String applyId) {
GoblinArtistAuthApply apply = authAdminService.getOne(
new LambdaQueryWrapper<GoblinArtistAuthApply>()
.eq(GoblinArtistAuthApply::getApplyId, applyId).last("limit 1"));
if (apply == null) return null;
KylinArtist artist = kylinArtistMapper.selectOne(
new LambdaQueryWrapper<KylinArtist>()
.eq(KylinArtist::getArtistId, apply.getArtistId()).last("limit 1"));
GoblinStoreInfo store = storeInfoMapper.selectOne(
new LambdaQueryWrapper<GoblinStoreInfo>()
.eq(GoblinStoreInfo::getStoreId, apply.getStoreId()).last("limit 1"));
Map<String, Object> data = new LinkedHashMap<>();
data.put("applyId", apply.getApplyId());
data.put("status", apply.getStatus());
data.put("createdAt", apply.getCreatedAt());
data.put("applyTime", formatTime(apply.getCreatedAt()));
data.put("artistId", apply.getArtistId());
data.put("artistName", artist != null ? artist.getArtistName() : null);
data.put("storeId", apply.getStoreId());
data.put("storeName", store != null ? store.getStoreName() : null);
data.put("reviewerName", apply.getReviewerName());
data.put("reviewTime", formatTime(apply.getReviewTime()));
data.put("reviewRemark", apply.getReviewRemark());
data.put("rejectReason", apply.getRejectReason());
data.put("authExpireAt", formatTime(apply.getAuthExpireAt()));
List<String> urls = new ArrayList<>();
if (StringUtils.isNotBlank(apply.getAttachmentUrls())) {
try {
urls = JsonUtils.fromJson(apply.getAttachmentUrls(), new TypeReference<List<String>>() {});
} catch (Exception ignored) {}
}
data.put("attachmentUrls", urls != null ? urls : Collections.emptyList());
return data;
}
@Log(title = "艺人授权:通过", businessType = BusinessType.UPDATE)
@RequiresPermissions("goblin:artist:auth:approve")
@PostMapping("approve")
@ResponseBody
public AjaxResult approve(@RequestParam String applyId,
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime expireAt,
@RequestParam(required = false) String remark) {
String reviewerName = ShiroUtils.getLoginName();
String reviewerId = ShiroUtils.getUserId().toString();
return toAjax(authAdminService.approve(applyId, expireAt, remark, reviewerId, reviewerName));
}
@Log(title = "艺人授权:驳回", businessType = BusinessType.UPDATE)
@RequiresPermissions("goblin:artist:auth:reject")
@PostMapping("reject")
@ResponseBody
public AjaxResult reject(@RequestParam String applyId, @RequestParam String reason) {
String reviewerName = ShiroUtils.getLoginName();
String reviewerId = ShiroUtils.getUserId().toString();
return toAjax(authAdminService.reject(applyId, reason, reviewerId, reviewerName));
}
}
package com.liquidnet.client.admin.web.controller.zhengzai.goblin.artist;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.liquidnet.client.admin.common.annotation.Log;
import com.liquidnet.client.admin.common.core.controller.BaseController;
import com.liquidnet.client.admin.common.core.domain.AjaxResult;
import com.liquidnet.client.admin.common.core.page.TableDataInfo;
import com.liquidnet.client.admin.common.enums.BusinessType;
import com.liquidnet.client.admin.zhengzai.goblin.service.IGoblinArtistTopicAdminService;
import com.liquidnet.service.goblin.entity.GoblinArtistTopic;
import com.liquidnet.service.goblin.entity.GoblinStoreInfo;
import com.liquidnet.service.goblin.mapper.GoblinStoreInfoMapper;
import com.liquidnet.service.kylin.entity.KylinArtist;
import com.liquidnet.service.kylin.mapper.KylinArtistMapper;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
import static com.liquidnet.client.admin.web.controller.zhengzai.goblin.artist.ArtistAdminHelper.*;
@Controller
@RequestMapping("goblin/artist/topic")
public class GoblinArtistTopicAdminController extends BaseController {
private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
private final String prefix = "zhengzai/goblin/artist/topic";
@Autowired
private IGoblinArtistTopicAdminService topicAdminService;
@Autowired
private KylinArtistMapper kylinArtistMapper;
@Autowired
private GoblinStoreInfoMapper storeInfoMapper;
@GetMapping()
public String index() {
return prefix + "/topic";
}
@RequiresPermissions("goblin:artist:topic:list")
@RequestMapping("/list")
@ResponseBody
public TableDataInfo list(GoblinArtistTopic topic,
@RequestParam(required = false) String artistName,
@RequestParam(required = false) String storeName) {
LambdaQueryWrapper<GoblinArtistTopic> qw = Wrappers.lambdaQuery(GoblinArtistTopic.class);
qw.eq(GoblinArtistTopic::getDelFlg, 0);
if (topic.getStatus() != null) {
qw.eq(GoblinArtistTopic::getStatus, topic.getStatus());
}
if (StringUtils.isNotBlank(topic.getTopicName())) {
qw.like(GoblinArtistTopic::getTopicName, topic.getTopicName());
}
List<String> sids = findStoreIds(storeInfoMapper, storeName);
if (sids != null) {
if (sids.isEmpty()) return getDataTable(Collections.emptyList());
qw.in(GoblinArtistTopic::getStoreId, sids);
}
List<String> aids = findArtistIds(kylinArtistMapper, artistName);
if (aids != null) {
if (aids.isEmpty()) return getDataTable(Collections.emptyList());
qw.in(GoblinArtistTopic::getArtistId, aids);
}
qw.orderByAsc(GoblinArtistTopic::getSort);
qw.orderByDesc(GoblinArtistTopic::getCreatedAt);
startPage();
return getDataTable(withNames(topicAdminService.list(qw)));
}
private List<Map<String, Object>> withNames(List<GoblinArtistTopic> list) {
if (list.isEmpty()) return Collections.emptyList();
Map<String, String> storeNames = storeNameMap(storeInfoMapper, idsOf(list, GoblinArtistTopic::getStoreId));
Map<String, String> artistNames = artistNameMap(kylinArtistMapper, idsOf(list, GoblinArtistTopic::getArtistId));
Map<String, Integer> goodsCounts = topicAdminService.goodsCountMap(idsOf(list, GoblinArtistTopic::getTopicId));
return list.stream().map(item -> {
Map<String, Object> row = new LinkedHashMap<>();
row.put("topicId", item.getTopicId());
row.put("topicName", item.getTopicName());
row.put("artistId", item.getArtistId());
row.put("artistName", artistNames.get(item.getArtistId()));
row.put("storeId", item.getStoreId());
row.put("storeName", storeNames.get(item.getStoreId()));
row.put("status", item.getStatus());
row.put("sort", item.getSort());
row.put("recommendPosition", item.getRecommendPosition());
row.put("goodsCount", goodsCounts.getOrDefault(item.getTopicId(), 0));
row.put("createdAt", formatTime(item.getCreatedAt()));
row.put("onSaleStart", formatDate(item.getOnSaleStart()));
row.put("onSaleEnd", formatDate(item.getOnSaleEnd()));
return row;
}).collect(Collectors.toList());
}
private static String formatDate(LocalDateTime time) {
return time != null ? time.format(DATE_FMT) : null;
}
@RequiresPermissions("goblin:artist:topic:info")
@GetMapping("detail/{topicId}")
public String detail(@PathVariable String topicId, ModelMap mmap) {
Map<String, Object> topic = topicAdminService.topicDetail(topicId);
if (topic == null) {
return prefix + "/topic";
}
mmap.put("topic", formatDetailTime(topic));
String artistId = (String) topic.get("artistId");
if (StringUtils.isNotBlank(artistId)) {
KylinArtist artist = kylinArtistMapper.selectOne(
new LambdaQueryWrapper<KylinArtist>()
.eq(KylinArtist::getArtistId, artistId).last("limit 1"));
mmap.put("artist", artist);
}
String storeId = (String) topic.get("storeId");
if (StringUtils.isNotBlank(storeId)) {
mmap.put("store", storeInfoMapper.selectOne(
new LambdaQueryWrapper<GoblinStoreInfo>()
.eq(GoblinStoreInfo::getStoreId, storeId).last("limit 1")));
}
return prefix + "/detail";
}
private Map<String, Object> formatDetailTime(Map<String, Object> topic) {
Map<String, Object> copy = new LinkedHashMap<>(topic);
copy.put("createdAt", formatTime((LocalDateTime) copy.get("createdAt")));
copy.put("onSaleStart", formatTime((LocalDateTime) copy.get("onSaleStart")));
copy.put("onSaleEnd", formatTime((LocalDateTime) copy.get("onSaleEnd")));
return copy;
}
@Log(title = "艺人专题:设置排序", businessType = BusinessType.UPDATE)
@RequiresPermissions("goblin:artist:topic:sort")
@PostMapping("sort")
@ResponseBody
public AjaxResult setSort(@RequestParam String topicId, @RequestParam int sort) {
return toAjax(topicAdminService.setSort(topicId, sort));
}
@Log(title = "艺人专题:设置推荐位", businessType = BusinessType.UPDATE)
@RequiresPermissions("goblin:artist:topic:recommend")
@PostMapping("recommend")
@ResponseBody
public AjaxResult setRecommend(@RequestParam String topicId, @RequestParam int position) {
return toAjax(topicAdminService.setRecommend(topicId, position));
}
/** 查询推荐位当前占用专题(用于替换确认提示) */
@RequiresPermissions("goblin:artist:topic:recommend")
@GetMapping("recommendOwner")
@ResponseBody
public AjaxResult recommendOwner(@RequestParam int position) {
if (position <= 0) {
return AjaxResult.success();
}
GoblinArtistTopic owner = topicAdminService.getOne(new LambdaQueryWrapper<GoblinArtistTopic>()
.eq(GoblinArtistTopic::getRecommendPosition, position)
.eq(GoblinArtistTopic::getDelFlg, 0)
.last("limit 1"));
if (owner == null) {
return AjaxResult.success();
}
Map<String, Object> data = new LinkedHashMap<>();
data.put("topicId", owner.getTopicId());
data.put("topicName", owner.getTopicName());
return AjaxResult.success(data);
}
@Log(title = "艺人专题:上下架", businessType = BusinessType.UPDATE)
@RequiresPermissions("goblin:artist:topic:status")
@PostMapping("status")
@ResponseBody
public AjaxResult setStatus(@RequestParam String topicId, @RequestParam int status) {
return toAjax(topicAdminService.setStatus(topicId, status));
}
@Log(title = "艺人专题:删除", businessType = BusinessType.DELETE)
@RequiresPermissions("goblin:artist:topic:delete")
@PostMapping("delete")
@ResponseBody
public AjaxResult deleteTopic(@RequestParam String topicId) {
return toAjax(topicAdminService.deleteTopic(topicId));
}
}
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>授权申请详情</title>
<link th:href="@{/css/bootstrap.min.css}" rel="stylesheet"/>
<link th:href="@{/css/font-awesome.min.css}" rel="stylesheet"/>
<link th:href="@{/css/animate.css}" rel="stylesheet"/>
<link th:href="@{/css/style.css?v=20200903}" rel="stylesheet"/>
<link th:href="@{/ruoyi/css/ry-ui.css?v=4.6.1}" rel="stylesheet"/>
<style>
:root {
--paper: #F7F8FA;
--ink: #1F2937;
--ink-dim: #6B7280;
--line: #E5E7EB;
--blue: #2563EB;
--amber: #D97706;
--green: #0F766E;
--red: #DC2626;
--gray: #6B7280;
}
body {
background: var(--paper);
font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", "Segoe UI", sans-serif;
color: var(--ink);
}
.mono {
font-family: ui-monospace, SFMono-Regular, "JetBrains Mono", Consolas, "Courier New", monospace;
letter-spacing: .02em;
}
.auth-page { max-width: 680px; margin: 0 auto; padding: 28px 24px 48px; }
/* ---------- 区块卡片 ---------- */
.auth-card {
background: #fff; border: 1px solid var(--line); border-radius: 10px;
padding: 8px 24px 12px; margin-bottom: 18px;
box-shadow: 0 1px 2px rgba(31,41,55,.04);
}
.auth-card-title {
font-size: 14px; font-weight: 700; color: var(--ink);
padding: 16px 0 12px; border-bottom: 1px solid var(--line); margin-bottom: 4px;
}
/* 每条一行 */
.auth-row {
display: flex; align-items: flex-start; gap: 16px;
padding: 10px 0; border-bottom: 1px solid #F1F2F4;
}
.auth-row:last-child { border-bottom: none; }
.auth-row-label {
width: 88px; flex-shrink: 0; font-size: 13px; color: var(--ink-dim);
padding-top: 1px;
}
.auth-row-value {
flex: 1; font-size: 14px; color: var(--ink); word-break: break-all; line-height: 1.5;
}
.auth-row-value.placeholder { color: #9CA3AF; }
/* 状态徽章 */
.auth-badge {
display: inline-block; padding: 2px 12px; border-radius: 999px;
font-size: 12px; font-weight: 600; line-height: 1.6;
}
.badge-pending { background: #FFF3E0; color: #B45309; }
.badge-approved { background: #E6F6F3; color: #0F766E; }
.badge-rejected { background: #FDE8E8; color: #C53030; }
.badge-revoked { background: #F3F4F6; color: #6B7280; }
/* ---------- 附件画廊(横向排列) ---------- */
.auth-gallery { display: flex; flex-wrap: wrap; gap: 12px; padding: 16px 0 20px; }
.auth-attach {
width: 96px; height: 96px; border: 1px solid var(--line); border-radius: 8px;
overflow: hidden; cursor: zoom-in; background: #fff;
display: flex; align-items: center; justify-content: center;
transition: border-color .2s ease, transform .2s ease, box-shadow .2s ease;
}
.auth-attach:hover { border-color: var(--blue); transform: translateY(-2px); box-shadow: 0 4px 12px rgba(37,99,235,.12); }
.auth-attach img { max-width: 100%; max-height: 100%; object-fit: contain; }
.auth-attach-ico { font-size: 26px; color: #C4C9D1; }
.auth-empty {
padding: 24px 16px; text-align: center; color: #9CA3AF; font-size: 13px;
border: 1px dashed var(--line); border-radius: 8px; background: #FAFBFC; margin-bottom: 20px;
}
.auth-empty i { display: block; font-size: 26px; margin-bottom: 6px; }
/* ---------- 图片放大灯箱 ---------- */
.auth-lightbox {
display: none; position: fixed; inset: 0; z-index: 99999;
background: rgba(17,24,39,.88); align-items: center; justify-content: center;
}
.auth-lightbox.active { display: flex; }
.auth-lightbox img {
max-width: 92vw; max-height: 92vh; object-fit: contain;
border-radius: 8px; box-shadow: 0 8px 40px rgba(0,0,0,.5);
}
.auth-lightbox-close {
position: fixed; top: 20px; right: 28px; font-size: 36px; color: #fff;
cursor: pointer; line-height: 1; user-select: none; opacity: .85;
}
.auth-lightbox-close:hover { opacity: 1; }
@media (max-width: 640px) {
.auth-page { padding: 20px 14px 32px; }
.auth-card { padding: 8px 16px 12px; }
}
@media (prefers-reduced-motion: reduce) {
.auth-attach { transition: none; }
}
</style>
</head>
<body class="white-bg">
<div class="auth-page" th:object="${apply}">
<!-- 基本信息 -->
<div class="auth-card">
<div class="auth-card-title">基本信息</div>
<div class="auth-row">
<div class="auth-row-label">申请ID:</div>
<div class="auth-row-value mono" th:text="*{applyId} ?: '--'">A202601010001</div>
</div>
<div class="auth-row">
<div class="auth-row-label">申请商家:</div>
<div class="auth-row-value" th:text="*{storeName} ?: '--'">摩登天空</div>
</div>
<div class="auth-row">
<div class="auth-row-label">关联艺人:</div>
<div class="auth-row-value" th:text="*{artistName} ?: '-'">陈粒</div>
</div>
<div class="auth-row">
<div class="auth-row-label">申请时间:</div>
<div class="auth-row-value mono">
<span th:if="*{applyTime}" th:text="*{applyTime}">2026-01-01 10:00:00</span>
<span th:if="!*{applyTime}">--</span>
</div>
</div>
</div>
<!-- 附件信息 -->
<div class="auth-card" th:if="*{attachmentUrls != null and !attachmentUrls.isEmpty()}">
<div class="auth-card-title">附件信息</div>
<div class="auth-gallery">
<div class="auth-attach" th:each="url : ${apply.attachmentUrls}" th:title="点击放大查看">
<img th:src="${url}" alt="附件材料"
onerror="this.style.display='none';this.parentElement.innerHTML='<i class=&quot;fa fa-file-image-o auth-attach-ico&quot;></i>'"
onclick="authLightbox.open(this.src)"/>
</div>
</div>
</div>
<div class="auth-empty" th:if="*{attachmentUrls == null or attachmentUrls.isEmpty()}">
<i class="fa fa-file-o"></i>
暂无附件材料
</div>
<!-- 审批信息 -->
<div class="auth-card">
<div class="auth-card-title">审批信息</div>
<div class="auth-row">
<div class="auth-row-label">申请状态:</div>
<div class="auth-row-value">
<span class="auth-badge" th:classappend="*{status == 0 ? 'badge-pending' : (status == 1 ? 'badge-approved' : (status == 2 ? 'badge-rejected' : 'badge-revoked'))}">
<th:block th:if="*{status == 0}">待审核</th:block>
<th:block th:if="*{status == 1}">已通过</th:block>
<th:block th:if="*{status == 2}">已拒绝</th:block>
<th:block th:if="*{status == 3}">已撤销</th:block>
</span>
</div>
</div>
<div class="auth-row">
<div class="auth-row-label">审批人:</div>
<div class="auth-row-value" th:text="*{reviewerName} ?: '--'">admin</div>
</div>
<div class="auth-row">
<div class="auth-row-label">审批时间:</div>
<div class="auth-row-value mono">
<span th:if="*{reviewTime}" th:text="*{reviewTime}">2026-01-02 09:30:00</span>
<span th:if="!*{reviewTime}">--</span>
</div>
</div>
<div class="auth-row">
<div class="auth-row-label">授权截止:</div>
<div class="auth-row-value mono">
<span th:if="*{authExpireAt}" th:text="*{authExpireAt}">2027-01-01 00:00:00</span>
<span th:if="!*{authExpireAt}">--</span>
</div>
</div>
<div class="auth-row" th:if="*{reviewRemark}">
<div class="auth-row-label">备注:</div>
<div class="auth-row-value" th:text="*{reviewRemark}">通过</div>
</div>
<div class="auth-row" th:if="*{rejectReason}">
<div class="auth-row-label">驳回原因:</div>
<div class="auth-row-value" th:text="*{rejectReason}">材料不完整</div>
</div>
</div>
</div>
<!-- 图片放大灯箱 -->
<div id="auth-lightbox" class="auth-lightbox" onclick="authLightbox.close()">
<span class="auth-lightbox-close" onclick="authLightbox.close()">&times;</span>
<img id="auth-lightbox-img" src="" alt="" onclick="event.stopPropagation()">
</div>
<script>
var authLightbox = {
open: function (src) {
if (!src) return;
var lb = document.getElementById('auth-lightbox');
document.getElementById('auth-lightbox-img').src = src;
lb.classList.add('active');
document.body.style.overflow = 'hidden';
},
close: function () {
var lb = document.getElementById('auth-lightbox');
lb.classList.remove('active');
document.getElementById('auth-lightbox-img').src = '';
document.body.style.overflow = '';
}
};
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape') { authLightbox.close(); }
});
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<title>艺人专题详情</title>
<link th:href="@{/css/bootstrap.min.css}" rel="stylesheet"/>
<style>
body {
background-color: #fff;
color: #333;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
font-size: 14px;
padding: 24px 32px;
margin: 0;
}
.detail-container {
padding-bottom: 60px;
}
.detail-section {
margin-bottom: 24px;
}
.detail-section-title {
font-size: 15px;
font-weight: bold;
color: #262626;
padding-bottom: 10px;
margin-bottom: 16px;
border-bottom: 1px solid #f0f0f0;
}
.detail-list {
display: flex;
flex-direction: column;
gap: 14px;
}
.detail-item {
display: flex;
align-items: center;
line-height: 1.5;
}
.detail-item .label-text {
color: #8c8c8c;
width: 85px;
flex-shrink: 0;
white-space: nowrap;
}
.detail-item .value-text {
color: #262626;
margin-left: 12px;
word-break: break-all;
}
.goods-table {
width: 100%;
border-collapse: collapse;
margin-top: 12px;
}
.goods-table th, .goods-table td {
padding: 12px 16px;
text-align: left;
vertical-align: middle;
border-bottom: 1px solid #f0f0f0;
}
.goods-table th {
background: #fafafa;
font-weight: bold;
color: #262626;
border-top: none;
border-left: none;
border-right: none;
}
.goods-cover-wrap {
width: 52px;
height: 52px;
}
.goods-cover {
width: 52px;
height: 52px;
object-fit: cover;
border-radius: 4px;
display: block;
}
.goods-cover-placeholder {
width: 52px;
height: 52px;
border-radius: 4px;
background: #5c62d6;
color: #fff;
font-size: 12px;
font-weight: bold;
display: flex;
align-items: center;
justify-content: center;
}
.goods-name {
color: #262626;
}
.goods-spec {
color: #595959;
}
.goods-price {
color: #ff4d4f;
font-weight: bold;
}
.detail-empty {
color: #8c8c8c;
text-align: center;
padding: 32px 0;
font-size: 14px;
}
.detail-footer {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: #fff;
padding: 12px 24px;
text-align: right;
border-top: 1px solid #f0f0f0;
z-index: 10;
}
.btn-close {
background: #e0e0e0;
border: none;
color: #333;
font-size: 14px;
padding: 6px 22px;
border-radius: 4px;
cursor: pointer;
outline: none;
transition: background 0.2s;
}
.btn-close:hover {
background: #d4d4d4;
}
</style>
</head>
<body>
<div class="detail-container" th:object="${topic}">
<!-- 基本信息 -->
<div class="detail-section">
<div class="detail-section-title">基本信息</div>
<div class="detail-list">
<div class="detail-item">
<span class="label-text">专题名称:</span>
<span class="value-text" th:text="*{topicName} ?: '-'">-</span>
</div>
<div class="detail-item">
<span class="label-text">所属商家:</span>
<span class="value-text" th:text="${store != null ? store.storeName : '-'}">-</span>
</div>
<div class="detail-item">
<span class="label-text">关联艺人:</span>
<span class="value-text" th:text="${artist != null ? artist.artistName : '-'}">-</span>
</div>
<div class="detail-item">
<span class="label-text">创建时间:</span>
<span class="value-text" th:text="*{createdAt} ?: '-'">-</span>
</div>
</div>
</div>
<!-- 上架时间 -->
<div class="detail-section">
<div class="detail-section-title">上架时间</div>
<div class="detail-list">
<div class="detail-item">
<span class="label-text">开始时间:</span>
<span class="value-text" th:text="*{onSaleStart} ?: '-'">-</span>
</div>
<div class="detail-item">
<span class="label-text">结束时间:</span>
<span class="value-text" th:text="*{onSaleEnd} ?: '-'">-</span>
</div>
</div>
</div>
<!-- 关联商品 -->
<div class="detail-section">
<div class="detail-section-title" th:text="|关联商品(共 ${topic.goods != null ? #lists.size(topic.goods) : 0} 个)|">关联商品(共0个)</div>
<table class="goods-table" th:if="${topic.goods != null and !#lists.isEmpty(topic.goods)}">
<thead>
<tr>
<th style="width: 90px;">商品头图</th>
<th>商品名称</th>
<th>商品规格</th>
<th style="width: 120px;">商品价格</th>
</tr>
</thead>
<tbody>
<tr th:each="good : ${topic.goods}">
<td>
<div class="goods-cover-wrap">
<img class="goods-cover" th:if="${good.coverPic != null and !#strings.isEmpty(good.coverPic)}"
th:src="${good.coverPic}" alt="商品图"
onerror="this.style.display='none'; this.nextElementSibling.style.display='flex';"/>
<div class="goods-cover-placeholder"
th:style="${good.coverPic != null and !#strings.isEmpty(good.coverPic)} ? 'display:none;' : 'display:flex;'">
商品图
</div>
</div>
</td>
<td class="goods-name" th:text="${good.name} ?: '-'">-</td>
<td class="goods-spec" th:text="${good.spec} ?: '-'">-</td>
<td class="goods-price"
th:text="${good.sellPrice != null and !#strings.isEmpty(good.sellPrice) ? '¥' + good.sellPrice : '-'}">-</td>
</tr>
</tbody>
</table>
<div class="detail-empty" th:if="${topic.goods == null or #lists.isEmpty(topic.goods)}">暂未配置商品</div>
</div>
<!-- 底部关闭按钮 -->
<div class="detail-footer">
<button type="button" class="btn-close" onclick="closeDialog()">关 闭</button>
</div>
</div>
<script>
function closeDialog() {
var index = parent.layer.getFrameIndex(window.name);
parent.layer.close(index);
}
</script>
</body>
</html>
package com.liquidnet.client.admin.zhengzai.goblin.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.liquidnet.service.goblin.entity.GoblinArtistAuthApply;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
/**
* Admin 艺人授权审核
*/
public interface IGoblinArtistAuthAdminService extends IService<GoblinArtistAuthApply> {
/**
* 审核通过:更新申请表(status=1 + authExpireAt),同步写 MongoDB
*/
boolean approve(String applyId, LocalDateTime expireAt, String remark, String reviewerId, String reviewerName);
/**
* 审核驳回:更新申请表(status=2 + rejectReason),同步写 MongoDB
*/
boolean reject(String applyId, String reason, String reviewerId, String reviewerName);
/**
* 授权列表(MongoDB 查询,返回分页结果)
*/
List<Map<String, Object>> authList(int pageNum, int pageSize);
}
package com.liquidnet.client.admin.zhengzai.goblin.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.liquidnet.service.goblin.entity.GoblinArtistTopic;
import java.util.Collection;
import java.util.Map;
/**
* Admin 艺人专题管理
*/
public interface IGoblinArtistTopicAdminService extends IService<GoblinArtistTopic> {
/**
* 设置排序,同步写 MongoDB + MySQL
*/
boolean setSort(String topicId, int sort);
/**
* 设置推荐位,0~5,同步写 MongoDB + MySQL
*/
boolean setRecommend(String topicId, int position);
/**
* 上下架,同步写 MongoDB + MySQL
*/
boolean setStatus(String topicId, int status);
/**
* 逻辑删除,同步写 MongoDB + MySQL
*/
boolean deleteTopic(String topicId);
/**
* 专题详情(MySQL 读取:专题主表 + 商品关联表 + 商品/SKU 表,goods 附带商品规格)
*/
Map<String, Object> topicDetail(String topicId);
/**
* 批量查询专题的商品数量(MySQL 商品关联表)
*/
Map<String, Integer> goodsCountMap(Collection<String> topicIds);
}
package com.liquidnet.client.admin.zhengzai.goblin.service.impl;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.liquidnet.client.admin.zhengzai.goblin.service.IGoblinArtistAuthAdminService;
import com.liquidnet.service.goblin.constant.GoblinArtistConst;
import com.liquidnet.service.goblin.dto.vo.GoblinArtistAuthApplyVo;
import com.liquidnet.service.goblin.entity.GoblinArtistAuthApply;
import com.liquidnet.service.goblin.mapper.GoblinArtistAuthApplyMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Slf4j
@Service
public class GoblinArtistAuthAdminServiceImpl
extends ServiceImpl<GoblinArtistAuthApplyMapper, GoblinArtistAuthApply>
implements IGoblinArtistAuthAdminService {
@Autowired
private MongoTemplate mongoTemplate;
@Override
@Transactional
public boolean approve(String applyId, LocalDateTime expireAt, String remark,
String reviewerId, String reviewerName) {
LocalDateTime now = LocalDateTime.now();
LambdaUpdateWrapper<GoblinArtistAuthApply> applyWrapper = Wrappers.lambdaUpdate(GoblinArtistAuthApply.class);
applyWrapper.eq(GoblinArtistAuthApply::getApplyId, applyId);
applyWrapper.set(GoblinArtistAuthApply::getStatus, GoblinArtistConst.APPLY_APPROVED);
applyWrapper.set(GoblinArtistAuthApply::getReviewerId, reviewerId);
applyWrapper.set(GoblinArtistAuthApply::getReviewerName, reviewerName);
applyWrapper.set(GoblinArtistAuthApply::getReviewTime, now);
applyWrapper.set(GoblinArtistAuthApply::getReviewRemark, remark);
applyWrapper.set(GoblinArtistAuthApply::getAuthExpireAt, expireAt);
applyWrapper.set(GoblinArtistAuthApply::getUpdatedAt, now);
if (!this.update(applyWrapper)) {
return false;
}
syncMongoApprove(applyId, expireAt, reviewerId, reviewerName, remark, now);
return true;
}
@Override
@Transactional
public boolean reject(String applyId, String reason, String reviewerId, String reviewerName) {
LocalDateTime now = LocalDateTime.now();
LambdaUpdateWrapper<GoblinArtistAuthApply> applyWrapper = Wrappers.lambdaUpdate(GoblinArtistAuthApply.class);
applyWrapper.eq(GoblinArtistAuthApply::getApplyId, applyId);
applyWrapper.set(GoblinArtistAuthApply::getStatus, GoblinArtistConst.APPLY_REJECTED);
applyWrapper.set(GoblinArtistAuthApply::getReviewerId, reviewerId);
applyWrapper.set(GoblinArtistAuthApply::getReviewerName, reviewerName);
applyWrapper.set(GoblinArtistAuthApply::getReviewTime, now);
applyWrapper.set(GoblinArtistAuthApply::getRejectReason, reason);
applyWrapper.set(GoblinArtistAuthApply::getUpdatedAt, now);
if (!this.update(applyWrapper)) {
return false;
}
syncMongoReject(applyId, reason, reviewerId, reviewerName, now);
return true;
}
@Override
public List<Map<String, Object>> authList(int pageNum, int pageSize) {
List<Map<String, Object>> result = new ArrayList<>();
return result;
}
private void syncMongoApprove(String applyId, LocalDateTime expireAt,
String reviewerId, String reviewerName,
String remark, LocalDateTime now) {
Query query = Query.query(Criteria.where("applyId").is(applyId));
Update update = Update.update("status", GoblinArtistConst.APPLY_APPROVED)
.set("reviewerId", reviewerId)
.set("reviewerName", reviewerName)
.set("reviewTime", now)
.set("reviewRemark", remark)
.set("authExpireAt", expireAt)
.set("updatedAt", now);
mongoTemplate.updateFirst(query, update, GoblinArtistAuthApplyVo.class.getSimpleName());
}
private void syncMongoReject(String applyId, String reason,
String reviewerId, String reviewerName, LocalDateTime now) {
Query query = Query.query(Criteria.where("applyId").is(applyId));
Update update = Update.update("status", GoblinArtistConst.APPLY_REJECTED)
.set("reviewerId", reviewerId)
.set("reviewerName", reviewerName)
.set("reviewTime", now)
.set("rejectReason", reason)
.set("updatedAt", now);
mongoTemplate.updateFirst(query, update, GoblinArtistAuthApplyVo.class.getSimpleName());
}
}
...@@ -151,6 +151,8 @@ global-auth: ...@@ -151,6 +151,8 @@ global-auth:
- ${liquidnet.info.context}/health - ${liquidnet.info.context}/health
# 查推荐收钱吧商品 # 查推荐收钱吧商品
- ${liquidnet.info.context}/goblin/sqb/performance/selectGoods/** - ${liquidnet.info.context}/goblin/sqb/performance/selectGoods/**
# 艺人专题相关
- ${liquidnet.info.context}/front/artistTopic/**
oncheck-url-pattern: oncheck-url-pattern:
- -
# ----------------------------------------------------------- # -----------------------------------------------------------
......
package com.liquidnet.service.goblin.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 艺人关联申请
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class GoblinArtistAuthApply implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "mid", type = IdType.AUTO)
private Long mid;
private String applyId;
private String storeId;
private String artistId;
/** JSON 数组字符串 */
private String attachmentUrls;
/** 0待审核 1已通过 2已拒绝 3已撤销 */
private Integer status;
private String reviewerId;
private String reviewerName;
private LocalDateTime reviewTime;
private String reviewRemark;
private String rejectReason;
private LocalDateTime authExpireAt;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
package com.liquidnet.service.goblin.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 艺人专题
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class GoblinArtistTopic implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "mid", type = IdType.AUTO)
private Long mid;
private String topicId;
private String storeId;
private String topicName;
private String artistId;
/** 1双列 2单列 */
private Integer templateType;
private String bannerUrl;
private Integer bannerHeight;
private LocalDateTime onSaleStart;
private LocalDateTime onSaleEnd;
/** 0待上架 1已上架 2已下架 */
private Integer status;
private Integer sort;
private Integer recommendPosition;
/** 0正常 1删除 */
private Integer delFlg;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
package com.liquidnet.service.goblin.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 艺人专题商品关联
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class GoblinArtistTopicGoods implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "mid", type = IdType.AUTO)
private Long mid;
private String relationId;
private String topicId;
private String spuId;
private Integer sort;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
package com.liquidnet.service.goblin.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.liquidnet.service.goblin.entity.GoblinArtistAuthApply;
public interface GoblinArtistAuthApplyMapper extends BaseMapper<GoblinArtistAuthApply> {
}
package com.liquidnet.service.goblin.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.liquidnet.service.goblin.entity.GoblinArtistTopicGoods;
public interface GoblinArtistTopicGoodsMapper extends BaseMapper<GoblinArtistTopicGoods> {
}
package com.liquidnet.service.goblin.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.liquidnet.service.goblin.entity.GoblinArtistTopic;
public interface GoblinArtistTopicMapper extends BaseMapper<GoblinArtistTopic> {
}
package com.liquidnet.service.goblin.controller;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.goblin.dto.vo.GoblinFrontArtistTopicDetailVo;
import com.liquidnet.service.goblin.dto.vo.GoblinFrontArtistTopicHomeVo;
import com.liquidnet.service.goblin.dto.vo.GoblinFrontArtistTopicListPageVo;
import com.liquidnet.service.goblin.service.impl.GoblinFrontArtistTopicServiceImpl;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
/**
* C 端-艺人专题(周边商城)
*/
@Api(tags = "C端艺人专题")
@Slf4j
@Validated
@RestController
@RequestMapping("front/artistTopic")
public class GoblinFrontArtistTopicController {
@Autowired
private GoblinFrontArtistTopicServiceImpl artistTopicService;
@GetMapping("home")
@ApiOperation("首页入口(推荐位专题+更多)")
public ResponseDto<GoblinFrontArtistTopicHomeVo> home() {
return artistTopicService.home();
}
@GetMapping("list")
@ApiOperation("专题列表")
@ApiImplicitParams({
@ApiImplicitParam(name = "page", value = "页码", dataType = "int", required = true),
@ApiImplicitParam(name = "size", value = "每页条数", dataType = "int", required = true),
})
public ResponseDto<GoblinFrontArtistTopicListPageVo> list(
@Min(1) @RequestParam(defaultValue = "1") int page,
@Min(1) @RequestParam(defaultValue = "20") int size) {
return artistTopicService.list(page, size);
}
@GetMapping("detail")
@ApiOperation("专题详情(商品支持 default/price 排序)")
@ApiImplicitParams({
@ApiImplicitParam(name = "topicId", value = "专题ID", dataType = "String", required = true),
@ApiImplicitParam(name = "sortType", value = "商品排序 default-默认排序 price-价格排序", dataType = "String", allowableValues = "default,price"),
@ApiImplicitParam(name = "sortOrder", value = "价格排序方向 asc-升序 desc-降序(sortType=price时生效)", dataType = "String", allowableValues = "asc,desc"),
})
public ResponseDto<GoblinFrontArtistTopicDetailVo> detail(
@NotBlank(message = "专题ID不能为空") @RequestParam String topicId,
@RequestParam(required = false, defaultValue = "default") String sortType,
@RequestParam(required = false, defaultValue = "asc") String sortOrder) {
return artistTopicService.detail(topicId, sortType, sortOrder);
}
}
package com.liquidnet.service.goblin.controller; package com.liquidnet.service.goblin.controller;
import com.github.pagehelper.PageInfo;
import com.liquidnet.common.exception.LiquidnetServiceException; import com.liquidnet.common.exception.LiquidnetServiceException;
import com.liquidnet.commons.lang.util.CurrentUtil; import com.liquidnet.commons.lang.util.CurrentUtil;
import com.liquidnet.commons.lang.util.StringUtil; import com.liquidnet.commons.lang.util.StringUtil;
...@@ -74,6 +75,22 @@ public class GoblinFrontController { ...@@ -74,6 +75,22 @@ public class GoblinFrontController {
return ResponseDto.success(goblinFrontService.getSelectGoods(pageNumber, pageSize)); return ResponseDto.success(goblinFrontService.getSelectGoods(pageNumber, pageSize));
} }
@GetMapping("getSelectGoodsPage")
@ApiOperation("商城商品真分页列表(page从1开始)")
@ApiImplicitParams({
@ApiImplicitParam(name = "page", value = "页码,从1开始", dataType = "int"),
@ApiImplicitParam(name = "pageSize", value = "每页条数", dataType = "int"),
@ApiImplicitParam(name = "sortType", value = "排序类型 default-默认排序 price-价格排序", dataType = "String", allowableValues = "default,price"),
@ApiImplicitParam(name = "sortOrder", value = "价格排序方向 asc-升序 desc-降序(sortType=price时生效)", dataType = "String", allowableValues = "asc,desc"),
})
public ResponseDto<PageInfo<GoblinGoodsInfoVo>> getSelectGoodsPage(
@RequestParam(name = "page", required = false, defaultValue = "1") int page,
@RequestParam(name = "pageSize", required = false, defaultValue = "20") int pageSize,
@RequestParam(name = "sortType", required = false, defaultValue = "default") String sortType,
@RequestParam(name = "sortOrder", required = false, defaultValue = "asc") String sortOrder) {
return ResponseDto.success(goblinFrontService.getSelectGoodsPage(page, pageSize, sortType, sortOrder));
}
@GetMapping("getGoodsDetail") @GetMapping("getGoodsDetail")
@ApiOperation("获得商品详情") @ApiOperation("获得商品详情")
@ApiImplicitParams({ @ApiImplicitParams({
......
package com.liquidnet.service.goblin.controller.manage;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import com.liquidnet.service.base.PagedResult;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.goblin.dto.manage.*;
import com.liquidnet.service.goblin.dto.manage.vo.*;
import com.liquidnet.service.goblin.service.manage.IGoblinStoreMgtArtistService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
/**
* 商家端-艺人关联申请 / 艺人专题
* <p>店铺由登录 UID 解析,对齐售后单管理</p>
*/
@ApiSupport(order = 149020)
@Api(tags = "艺人管理")
@Slf4j
@Validated
@RestController
@RequestMapping("store/mgt/artist")
public class GoblinStoreMgtArtistController {
@Autowired
private IGoblinStoreMgtArtistService artistService;
@ApiOperationSupport(order = 1)
@ApiOperation("搜索可申请艺人")
@ApiImplicitParams({
@ApiImplicitParam(name = "keyword", value = "艺人名称关键词", dataType = "String"),
@ApiImplicitParam(name = "page", value = "页码", dataType = "int", required = true),
@ApiImplicitParam(name = "size", value = "每页条数", dataType = "int", required = true),
})
@GetMapping("list")
public ResponseDto<PagedResult<GoblinStoreMgtArtistSearchItemVo>> searchArtist(
@RequestParam(required = false) String keyword,
@Min(1) @RequestParam(defaultValue = "1") int page,
@Min(1) @RequestParam(defaultValue = "20") int size) {
return artistService.searchArtist(keyword, page, size);
}
@ApiOperationSupport(order = 2)
@ApiOperation("提交艺人关联申请")
@PostMapping("apply/submit")
public ResponseDto<String> submitApply(
@Valid @RequestBody GoblinStoreMgtArtistApplySubmitParams params) {
return artistService.submitApply(params);
}
@ApiOperationSupport(order = 3)
@ApiOperation("艺人关联申请列表")
@ApiImplicitParams({
@ApiImplicitParam(name = "status", value = "状态 0待审核 1已通过 2已拒绝 3已撤销", dataType = "int"),
@ApiImplicitParam(name = "page", value = "页码", dataType = "int", required = true),
@ApiImplicitParam(name = "size", value = "每页条数", dataType = "int", required = true),
})
@GetMapping("apply/list")
public ResponseDto<PagedResult<GoblinStoreMgtArtistApplyListItemVo>> applyList(
@RequestParam(required = false) Integer status,
@Min(1) @RequestParam(defaultValue = "1") int page,
@Min(1) @RequestParam(defaultValue = "20") int size) {
return artistService.applyList(status, page, size);
}
@ApiOperationSupport(order = 4)
@ApiOperation("撤销艺人关联申请")
@PostMapping("apply/revoke")
public ResponseDto<Void> revokeApply(
@Valid @RequestBody GoblinStoreMgtArtistApplyRevokeParams params) {
return artistService.revokeApply(params);
}
@ApiOperationSupport(order = 5)
@ApiOperation("本店已授权艺人列表")
@GetMapping("auth/list")
public ResponseDto<GoblinStoreMgtArtistAuthListVo> authList() {
return artistService.authList();
}
@ApiOperationSupport(order = 6)
@ApiOperation("艺人专题列表")
@ApiImplicitParams({
@ApiImplicitParam(name = "status", value = "状态 0待上架 1已上架 2已下架", dataType = "int"),
@ApiImplicitParam(name = "page", value = "页码", dataType = "int", required = true),
@ApiImplicitParam(name = "size", value = "每页条数", dataType = "int", required = true),
})
@GetMapping("topic/list")
public ResponseDto<GoblinStoreMgtArtistTopicPageVo> topicList(
@RequestParam(required = false) Integer status,
@Min(1) @RequestParam(defaultValue = "1") int page,
@Min(1) @RequestParam(defaultValue = "20") int size) {
return artistService.topicList(status, page, size);
}
@ApiOperationSupport(order = 7)
@ApiOperation("艺人专题详情")
@GetMapping("topic/detail")
public ResponseDto<GoblinStoreMgtArtistTopicDetailVo> topicDetail(
@NotBlank(message = "专题ID不能为空") @RequestParam String topicId) {
return artistService.topicDetail(topicId);
}
@ApiOperationSupport(order = 8)
@ApiOperation("创建艺人专题")
@PostMapping("topic/create")
public ResponseDto<String> topicCreate(
@Valid @RequestBody GoblinStoreMgtArtistTopicCreateParams params) {
return artistService.topicCreate(params);
}
@ApiOperationSupport(order = 9)
@ApiOperation("编辑艺人专题")
@PostMapping("topic/update")
public ResponseDto<Void> topicUpdate(
@Valid @RequestBody GoblinStoreMgtArtistTopicUpdateParams params) {
return artistService.topicUpdate(params);
}
@ApiOperationSupport(order = 10)
@ApiOperation("删除艺人专题")
@PostMapping("topic/delete")
public ResponseDto<Void> topicDelete(
@Valid @RequestBody GoblinStoreMgtArtistTopicDeleteParams params) {
return artistService.topicDelete(params);
}
@ApiOperationSupport(order = 11)
@ApiOperation("艺人专题上下架")
@PostMapping("topic/status")
public ResponseDto<Void> topicStatus(
@Valid @RequestBody GoblinStoreMgtArtistTopicStatusParams params) {
return artistService.topicStatus(params);
}
@ApiOperationSupport(order = 12)
@ApiOperation("搜索本店可添加商品")
@ApiImplicitParams({
@ApiImplicitParam(name = "keyword", value = "商品名称关键词", dataType = "String"),
@ApiImplicitParam(name = "page", value = "页码", dataType = "int", required = true),
@ApiImplicitParam(name = "size", value = "每页条数", dataType = "int", required = true),
})
@GetMapping("topic/searchGoods")
public ResponseDto<PagedResult<GoblinStoreMgtArtistTopicGoodsSearchItemVo>> searchGoods(
@RequestParam(required = false) String keyword,
@Min(1) @RequestParam(defaultValue = "1") int page,
@Min(1) @RequestParam(defaultValue = "20") int size) {
return artistService.searchGoods(keyword, page, size);
}
}
...@@ -2,6 +2,7 @@ package com.liquidnet.service.goblin.service.impl; ...@@ -2,6 +2,7 @@ package com.liquidnet.service.goblin.service.impl;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.pagehelper.PageInfo;
import com.liquidnet.common.cache.redis.util.RedisUtil; import com.liquidnet.common.cache.redis.util.RedisUtil;
import com.liquidnet.common.exception.LiquidnetServiceException; import com.liquidnet.common.exception.LiquidnetServiceException;
import com.liquidnet.commons.lang.util.*; import com.liquidnet.commons.lang.util.*;
...@@ -1027,8 +1028,7 @@ public class GoblinFrontServiceImpl implements GoblinFrontService { ...@@ -1027,8 +1028,7 @@ public class GoblinFrontServiceImpl implements GoblinFrontService {
if (StringUtil.isNotBlank(spuids)) { if (StringUtil.isNotBlank(spuids)) {
query.addCriteria(Criteria.where("spuId").nin(spuids.split(","))); query.addCriteria(Criteria.where("spuId").nin(spuids.split(",")));
} }
query.addCriteria(Criteria.where("delFlg").is("0").and("shelvesStatus").is("3").and("spuAppear").is("0").and("marketId").is(null) query.addCriteria(selectGoodsCriteria());
.and("spuType").ne(33).and("showInMall").ne(0).and("cateFid").nin("22196120924543", "22196122839313").and("name").not().regex(Pattern.compile(SELECT_GOODS_EXCLUDE_NAME)));
//redis里面获取排序规则 1、上架时间2、销量3、价格高到低4、价格低到高 //redis里面获取排序规则 1、上架时间2、销量3、价格高到低4、价格低到高
...@@ -1318,6 +1318,63 @@ public class GoblinFrontServiceImpl implements GoblinFrontService { ...@@ -1318,6 +1318,63 @@ public class GoblinFrontServiceImpl implements GoblinFrontService {
// return null; // return null;
} }
/**
* 商城商品真分页列表:sortType default-后台配置排序 price-价格排序(page 从1开始)
*/
public PageInfo<GoblinGoodsInfoVo> getSelectGoodsPage(int page, int pageSize, String sortType, String sortOrder) {
Query query = new Query(selectGoodsCriteria());
long total = mongoTemplate.count(query, GoblinGoodsInfoVo.class, GoblinGoodsInfoVo.class.getSimpleName());
Sort sort;
if ("price".equals(sortType)) {
Sort.Direction direction = "desc".equals(sortOrder) ? Sort.Direction.DESC : Sort.Direction.ASC;
sort = Sort.by(direction, "priceGe");
query.collation(numericCollation());
} else {
sort = configSort(query);
}
query.with(PageRequest.of(Math.max(page - 1, 0), pageSize, sort));
List<GoblinGoodsInfoVo> list = mongoTemplate.find(query, GoblinGoodsInfoVo.class, GoblinGoodsInfoVo.class.getSimpleName());
PageInfo<GoblinGoodsInfoVo> pageInfo = new PageInfo<>(list);
pageInfo.setPageNum(page);
pageInfo.setPageSize(pageSize);
pageInfo.setTotal(total);
pageInfo.setPages((int) ((total + pageSize - 1) / pageSize));
return pageInfo;
}
/** 后台配置排序规则 1、上架时间 2、销量 3、价格高到低 4、价格低到高(同 getspuId) */
private Sort configSort(Query query) {
int sortType = 0;
if (redisUtil.hasKey(GoblinRedisConst.SELECT_GOODS_SORT)) {
sortType = (int) redisUtil.get(GoblinRedisConst.SELECT_GOODS_SORT);
}
if (sortType == 1) {
return Sort.by(Sort.Direction.DESC, "shelvesAt");
}
if (sortType == 2) {
return Sort.by(Sort.Direction.DESC, "count", "createdAt");
}
query.collation(numericCollation());
return sortType == 3 ? Sort.by(Sort.Direction.DESC, "priceGe") : Sort.by(Sort.Direction.ASC, "priceGe");
}
/** 数值排序 collation(priceGe 字符串存储) */
private Collation numericCollation() {
Document source = new Document();
source.put("locale", "zh");
source.put("numericOrdering", true);
return Collation.from(source);
}
/** 精选商品过滤口径(与存量精选/首页货架一致) */
private Criteria selectGoodsCriteria() {
return Criteria.where("delFlg").is("0").and("shelvesStatus").is("3").and("spuAppear").is("0").and("marketId").is(null)
.and("spuType").ne(33).and("showInMall").ne(0).and("cateFid").nin("22196120924543", "22196122839313")
.and("name").not().regex(Pattern.compile(SELECT_GOODS_EXCLUDE_NAME));
}
@Override @Override
public GoblinSqbPerformanceSelectGoodVo getPerformanceSelectGoods(String performancesId, int page, int pageSize) { public GoblinSqbPerformanceSelectGoodVo getPerformanceSelectGoods(String performancesId, int page, int pageSize) {
GoblinSqbPerformanceSelectGoodVo respVo = GoblinSqbPerformanceSelectGoodVo.getNew(); GoblinSqbPerformanceSelectGoodVo respVo = GoblinSqbPerformanceSelectGoodVo.getNew();
......
...@@ -86,6 +86,10 @@ public class GoblinRedisUtils { ...@@ -86,6 +86,10 @@ public class GoblinRedisUtils {
return redisUtil.incr(key, delta); return redisUtil.incr(key, delta);
} }
public void set(String key, Object value, long seconds) {
redisUtil.set(key, value, seconds);
}
/* ---------------------------------------- 短信相关 ---------------------------------------- */ /* ---------------------------------------- 短信相关 ---------------------------------------- */
public boolean setSmsCodeByMobile(String mobile, String smsCode) { public boolean setSmsCodeByMobile(String mobile, String smsCode) {
......
...@@ -206,3 +206,16 @@ gpblin_bracelet_order_update= update goblin_bracelet_order set out_trans_id=?, e ...@@ -206,3 +206,16 @@ gpblin_bracelet_order_update= update goblin_bracelet_order set out_trans_id=?, e
goblin_bracelet_order_update_cardno=UPDATE goblin_bracelet_order SET cardno=?,updated_at=? WHERE order_id=? goblin_bracelet_order_update_cardno=UPDATE goblin_bracelet_order SET cardno=?,updated_at=? WHERE order_id=?
goblin_bracelet_order_update_fout_trade_no=UPDATE goblin_bracelet_order SET fout_trade_no=?,updated_at=? WHERE order_id=? goblin_bracelet_order_update_fout_trade_no=UPDATE goblin_bracelet_order SET fout_trade_no=?,updated_at=? WHERE order_id=?
goblin_bracelet_order_update_refund=UPDATE goblin_bracelet_order SET status=?,refund_status=?,refund_status_note=?,updated_at=? WHERE order_id=? goblin_bracelet_order_update_refund=UPDATE goblin_bracelet_order SET status=?,refund_status=?,refund_status_note=?,updated_at=? WHERE order_id=?
#---- \u827A\u4EBA\u5173\u8054\u7533\u8BF7
goblin_artist_auth_apply.insert=INSERT INTO goblin_artist_auth_apply (apply_id,store_id,artist_id,attachment_urls,status,created_at,updated_at)VALUES(?,?,?,?,?,?,?)
goblin_artist_auth_apply.revoke=UPDATE goblin_artist_auth_apply SET status=3,updated_at=? WHERE apply_id=?
goblin_artist_auth_apply.approve=UPDATE goblin_artist_auth_apply SET status=1,reviewer_id=?,reviewer_name=?,review_time=?,review_remark=?,auth_expire_at=?,updated_at=? WHERE apply_id=?
goblin_artist_auth_apply.reject=UPDATE goblin_artist_auth_apply SET status=2,reviewer_id=?,reviewer_name=?,review_time=?,reject_reason=?,updated_at=? WHERE apply_id=?
#---- \u827A\u4EBA\u4E13\u9898
goblin_artist_topic.insert=INSERT INTO goblin_artist_topic (topic_id,store_id,topic_name,artist_id,template_type,banner_url,banner_height,on_sale_start,on_sale_end,status,sort,recommend_position,del_flg,created_at,updated_at)VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
goblin_artist_topic.update=UPDATE goblin_artist_topic SET topic_name=?,artist_id=?,template_type=?,banner_url=?,banner_height=?,on_sale_start=?,on_sale_end=?,updated_at=? WHERE topic_id=?
goblin_artist_topic.delete=UPDATE goblin_artist_topic SET del_flg=1,updated_at=? WHERE topic_id=?
goblin_artist_topic.status=UPDATE goblin_artist_topic SET status=?,updated_at=? WHERE topic_id=?
#---- \u4E13\u9898\u5546\u54C1
goblin_artist_topic_goods.insert=INSERT INTO goblin_artist_topic_goods (relation_id,topic_id,spu_id,sort,created_at,updated_at)VALUES(?,?,?,?,?,?)
goblin_artist_topic_goods.delete_by_topic=DELETE FROM goblin_artist_topic_goods WHERE topic_id=?
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment