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

Commit 568cbd03 authored by wangyifan's avatar wangyifan

admin后台管理申请、专题功能;删除goblin_artist_auth表

parent c8e94dc0
...@@ -24,24 +24,6 @@ CREATE TABLE `goblin_artist_auth_apply` ( ...@@ -24,24 +24,6 @@ CREATE TABLE `goblin_artist_auth_apply` (
KEY `idx_created_at` (`created_at`) KEY `idx_created_at` (`created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='艺人关联申请表'; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='艺人关联申请表';
-- 店铺-艺人授权关系表
CREATE TABLE `goblin_artist_auth` (
`mid` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增主键',
`auth_id` varchar(64) NOT NULL COMMENT '授权ID',
`store_id` varchar(64) NOT NULL COMMENT '店铺ID',
`artist_id` varchar(64) NOT NULL COMMENT '艺人ID',
`apply_id` varchar(64) NOT NULL COMMENT '来源申请ID',
`expire_at` datetime NOT NULL COMMENT '授权截止时间',
`status` tinyint(4) NOT NULL DEFAULT 1 COMMENT '1有效 0失效',
`created_at` datetime NOT NULL COMMENT '创建时间',
`updated_at` datetime NOT NULL COMMENT '更新时间',
PRIMARY KEY (`mid`),
UNIQUE KEY `uk_auth_id` (`auth_id`),
UNIQUE KEY `uk_store_artist` (`store_id`, `artist_id`),
KEY `idx_expire_at` (`expire_at`),
KEY `idx_store_status` (`store_id`, `status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='店铺艺人授权关系表';
-- 艺人专题表 -- 艺人专题表
CREATE TABLE `goblin_artist_topic` ( CREATE TABLE `goblin_artist_topic` (
`mid` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增主键', `mid` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增主键',
......
...@@ -476,33 +476,9 @@ public class GoblinRedisConst { ...@@ -476,33 +476,9 @@ public class GoblinRedisConst {
/* ----------------------------------------------------------------- */ /* ----------------------------------------------------------------- */
/** /**
* 艺人专题信息 * 艺人专题信息(详情缓存)
* {goblin:artist:topic:{store_id}_{topic_id}, GoblinArtistTopicVo} * {goblin:artist:topic:{topic_id}, GoblinArtistTopicVo}
*/ */
public static final String ARTIST_TOPIC = PREFIX.concat("artist:topic:"); public static final String ARTIST_TOPIC = PREFIX.concat("artist:topic:");
/**
* 店铺艺人专题列表
* {goblin:artist:topic:list:{store_id}, List<GoblinArtistTopicVo>}
*/
public static final String ARTIST_TOPIC_LIST = PREFIX.concat("artist:topic:list:");
/**
* 艺人授权关系
* {goblin:artist:auth:{store_id}_{artist_id}, GoblinArtistAuthVo}
*/
public static final String ARTIST_AUTH = PREFIX.concat("artist:auth:");
/**
* 店铺艺人授权列表
* {goblin:artist:auth:list:{store_id}, List<GoblinArtistAuthVo>}
*/
public static final String ARTIST_AUTH_LIST = PREFIX.concat("artist:auth:list:");
/**
* 艺人关联申请
* {goblin:artist:apply:{store_id}_{apply_id}, GoblinArtistAuthApplyVo}
*/
public static final String ARTIST_APPLY = PREFIX.concat("artist:apply:");
} }
package com.liquidnet.service.goblin.dto.vo;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
public class GoblinArtistAuthVo implements Serializable {
private static final long serialVersionUID = 1L;
private String authId;
private String storeId;
private String artistId;
private String applyId;
private LocalDateTime expireAt;
private Integer status;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
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));
}
@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());
}
}
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 GoblinArtistAuth implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "mid", type = IdType.AUTO)
private Long mid;
private String authId;
private String storeId;
private String artistId;
private String applyId;
private LocalDateTime expireAt;
/** 1有效 0失效 */
private Integer status;
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.GoblinArtistAuth;
public interface GoblinArtistAuthMapper extends BaseMapper<GoblinArtistAuth> {
}
...@@ -24,7 +24,6 @@ import com.liquidnet.service.goblin.dto.manage.vo.GoblinStoreMgtArtistTopicDetai ...@@ -24,7 +24,6 @@ import com.liquidnet.service.goblin.dto.manage.vo.GoblinStoreMgtArtistTopicDetai
import com.liquidnet.service.goblin.dto.manage.vo.GoblinStoreMgtArtistTopicGoodsSearchItemVo; import com.liquidnet.service.goblin.dto.manage.vo.GoblinStoreMgtArtistTopicGoodsSearchItemVo;
import com.liquidnet.service.goblin.dto.manage.vo.GoblinStoreMgtArtistTopicPageVo; import com.liquidnet.service.goblin.dto.manage.vo.GoblinStoreMgtArtistTopicPageVo;
import com.liquidnet.service.goblin.dto.vo.GoblinArtistAuthApplyVo; import com.liquidnet.service.goblin.dto.vo.GoblinArtistAuthApplyVo;
import com.liquidnet.service.goblin.dto.vo.GoblinArtistAuthVo;
import com.liquidnet.service.goblin.dto.vo.GoblinArtistTopicVo; import com.liquidnet.service.goblin.dto.vo.GoblinArtistTopicVo;
import com.liquidnet.service.goblin.dto.vo.GoblinGoodsInfoVo; import com.liquidnet.service.goblin.dto.vo.GoblinGoodsInfoVo;
import com.liquidnet.service.goblin.dto.vo.GoblinStoreInfoVo; import com.liquidnet.service.goblin.dto.vo.GoblinStoreInfoVo;
...@@ -51,6 +50,7 @@ import java.time.LocalDateTime; ...@@ -51,6 +50,7 @@ import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
...@@ -259,23 +259,30 @@ public class GoblinStoreMgtArtistServiceImpl implements IGoblinStoreMgtArtistSer ...@@ -259,23 +259,30 @@ public class GoblinStoreMgtArtistServiceImpl implements IGoblinStoreMgtArtistSer
return ResponseDto.failure(storeResp.getMessage()); return ResponseDto.failure(storeResp.getMessage());
} }
String storeId = storeResp.getData().getStoreId(); String storeId = storeResp.getData().getStoreId();
List<GoblinArtistAuthVo> auths = loadAuthsByStore(storeId);
LocalDateTime now = LocalDateTime.now(); LocalDateTime now = LocalDateTime.now();
auths = auths.stream()
.filter(a -> Objects.equals(a.getStatus(), GoblinArtistConst.AUTH_VALID) List<GoblinArtistAuthApplyVo> all = loadAppliesByStore(storeId);
&& a.getExpireAt() != null && a.getExpireAt().isAfter(now)) List<GoblinArtistAuthApplyVo> approved = all.stream()
.filter(a -> Objects.equals(a.getStatus(), GoblinArtistConst.APPLY_APPROVED)
&& a.getAuthExpireAt() != null && a.getAuthExpireAt().isAfter(now))
.collect(Collectors.toList()); .collect(Collectors.toList());
Map<String, KylinArtist> artistMap = loadArtistMap(auths.stream() Map<String, GoblinArtistAuthApplyVo> authByArtist = new LinkedHashMap<>();
.map(GoblinArtistAuthVo::getArtistId).collect(Collectors.toSet())); for (GoblinArtistAuthApplyVo a : approved) {
GoblinArtistAuthApplyVo exist = authByArtist.get(a.getArtistId());
if (exist == null || a.getAuthExpireAt().isAfter(exist.getAuthExpireAt())) {
authByArtist.put(a.getArtistId(), a);
}
}
Map<String, KylinArtist> artistMap = loadArtistMap(authByArtist.keySet());
GoblinStoreMgtArtistAuthListVo result = new GoblinStoreMgtArtistAuthListVo(); GoblinStoreMgtArtistAuthListVo result = new GoblinStoreMgtArtistAuthListVo();
for (GoblinArtistAuthVo auth : auths) { for (GoblinArtistAuthApplyVo apply : authByArtist.values()) {
GoblinStoreMgtArtistAuthListVo.Item item = new GoblinStoreMgtArtistAuthListVo.Item(); GoblinStoreMgtArtistAuthListVo.Item item = new GoblinStoreMgtArtistAuthListVo.Item();
item.setArtistId(auth.getArtistId()); item.setArtistId(apply.getArtistId());
item.setExpireAt(auth.getExpireAt()); item.setExpireAt(apply.getAuthExpireAt());
KylinArtist artist = artistMap.get(auth.getArtistId()); KylinArtist artist = artistMap.get(apply.getArtistId());
if (artist != null) { if (artist != null) {
item.setArtistName(artist.getArtistName()); item.setArtistName(artist.getArtistName());
item.setAvatarUrl(artist.getAvatarUrl()); item.setAvatarUrl(artist.getAvatarUrl());
...@@ -450,7 +457,7 @@ public class GoblinStoreMgtArtistServiceImpl implements IGoblinStoreMgtArtistSer ...@@ -450,7 +457,7 @@ public class GoblinStoreMgtArtistServiceImpl implements IGoblinStoreMgtArtistSer
topicVo.setGoods(goodsItems); topicVo.setGoods(goodsItems);
mongoTemplate.insert(topicVo, GoblinArtistTopicVo.class.getSimpleName()); mongoTemplate.insert(topicVo, GoblinArtistTopicVo.class.getSimpleName());
String topicKey = GoblinRedisConst.ARTIST_TOPIC + storeId + "_" + topicId; String topicKey = GoblinRedisConst.ARTIST_TOPIC + topicId;
redisUtils.set(topicKey, topicVo, TOPIC_TTL); redisUtils.set(topicKey, topicVo, TOPIC_TTL);
queueTopicInsertMysql(topicVo, now); queueTopicInsertMysql(topicVo, now);
...@@ -513,7 +520,7 @@ public class GoblinStoreMgtArtistServiceImpl implements IGoblinStoreMgtArtistSer ...@@ -513,7 +520,7 @@ public class GoblinStoreMgtArtistServiceImpl implements IGoblinStoreMgtArtistSer
update.set("updatedAt", now); update.set("updatedAt", now);
mongoTemplate.updateFirst(query, update, GoblinArtistTopicVo.class.getSimpleName()); mongoTemplate.updateFirst(query, update, GoblinArtistTopicVo.class.getSimpleName());
String topicKey = GoblinRedisConst.ARTIST_TOPIC + storeId + "_" + params.getTopicId(); String topicKey = GoblinRedisConst.ARTIST_TOPIC + params.getTopicId();
redisUtils.del(topicKey); redisUtils.del(topicKey);
queueTopicUpdateMysql(params.getTopicId(), params, now); queueTopicUpdateMysql(params.getTopicId(), params, now);
...@@ -540,7 +547,7 @@ public class GoblinStoreMgtArtistServiceImpl implements IGoblinStoreMgtArtistSer ...@@ -540,7 +547,7 @@ public class GoblinStoreMgtArtistServiceImpl implements IGoblinStoreMgtArtistSer
new Update().set("delFlg", GoblinArtistConst.DEL_YES).set("updatedAt", now), new Update().set("delFlg", GoblinArtistConst.DEL_YES).set("updatedAt", now),
GoblinArtistTopicVo.class.getSimpleName()); GoblinArtistTopicVo.class.getSimpleName());
String topicKey = GoblinRedisConst.ARTIST_TOPIC + storeId + "_" + params.getTopicId(); String topicKey = GoblinRedisConst.ARTIST_TOPIC + params.getTopicId();
redisUtils.del(topicKey); redisUtils.del(topicKey);
LinkedList<String> sqls = CollectionUtil.linkedListString(); LinkedList<String> sqls = CollectionUtil.linkedListString();
...@@ -578,7 +585,7 @@ public class GoblinStoreMgtArtistServiceImpl implements IGoblinStoreMgtArtistSer ...@@ -578,7 +585,7 @@ public class GoblinStoreMgtArtistServiceImpl implements IGoblinStoreMgtArtistSer
new Update().set("status", status).set("updatedAt", now), new Update().set("status", status).set("updatedAt", now),
GoblinArtistTopicVo.class.getSimpleName()); GoblinArtistTopicVo.class.getSimpleName());
String topicKey = GoblinRedisConst.ARTIST_TOPIC + storeId + "_" + params.getTopicId(); String topicKey = GoblinRedisConst.ARTIST_TOPIC + params.getTopicId();
redisUtils.del(topicKey); redisUtils.del(topicKey);
LinkedList<String> sqls = CollectionUtil.linkedListString(); LinkedList<String> sqls = CollectionUtil.linkedListString();
...@@ -631,12 +638,13 @@ public class GoblinStoreMgtArtistServiceImpl implements IGoblinStoreMgtArtistSer ...@@ -631,12 +638,13 @@ public class GoblinStoreMgtArtistServiceImpl implements IGoblinStoreMgtArtistSer
return ResponseDto.success(null); return ResponseDto.success(null);
} }
LocalDateTime now = LocalDateTime.now(); LocalDateTime now = LocalDateTime.now();
List<GoblinArtistAuthVo> auths = loadAuthsByStore(storeId); long count = mongoTemplate.count(
boolean valid = auths.stream().anyMatch(a -> new Query(Criteria.where("storeId").is(storeId)
Objects.equals(a.getArtistId(), artistId) .and("artistId").is(artistId)
&& Objects.equals(a.getStatus(), GoblinArtistConst.AUTH_VALID) .and("status").is(GoblinArtistConst.APPLY_APPROVED)
&& a.getExpireAt() != null && a.getExpireAt().isAfter(now)); .and("authExpireAt").gt(now)),
if (!valid) { GoblinArtistAuthApplyVo.class.getSimpleName());
if (count == 0) {
return ResponseDto.failure("未获得该艺人有效授权"); return ResponseDto.failure("未获得该艺人有效授权");
} }
return ResponseDto.success(null); return ResponseDto.success(null);
...@@ -667,13 +675,6 @@ public class GoblinStoreMgtArtistServiceImpl implements IGoblinStoreMgtArtistSer ...@@ -667,13 +675,6 @@ public class GoblinStoreMgtArtistServiceImpl implements IGoblinStoreMgtArtistSer
return applies == null ? Collections.emptyList() : applies; return applies == null ? Collections.emptyList() : applies;
} }
private List<GoblinArtistAuthVo> loadAuthsByStore(String storeId) {
List<GoblinArtistAuthVo> auths = mongoTemplate.find(
new Query(Criteria.where("storeId").is(storeId)),
GoblinArtistAuthVo.class, GoblinArtistAuthVo.class.getSimpleName());
return auths == null ? Collections.emptyList() : auths;
}
private List<GoblinArtistTopicVo> loadTopicsByStore(String storeId) { private List<GoblinArtistTopicVo> loadTopicsByStore(String storeId) {
List<GoblinArtistTopicVo> topics = mongoTemplate.find( List<GoblinArtistTopicVo> topics = mongoTemplate.find(
new Query(Criteria.where("storeId").is(storeId).and("delFlg").is(GoblinArtistConst.DEL_NO)), new Query(Criteria.where("storeId").is(storeId).and("delFlg").is(GoblinArtistConst.DEL_NO)),
...@@ -682,7 +683,7 @@ public class GoblinStoreMgtArtistServiceImpl implements IGoblinStoreMgtArtistSer ...@@ -682,7 +683,7 @@ public class GoblinStoreMgtArtistServiceImpl implements IGoblinStoreMgtArtistSer
} }
private GoblinArtistTopicVo loadTopic(String storeId, String topicId) { private GoblinArtistTopicVo loadTopic(String storeId, String topicId) {
String redisKey = GoblinRedisConst.ARTIST_TOPIC + storeId + "_" + topicId; String redisKey = GoblinRedisConst.ARTIST_TOPIC + topicId;
Object cached = redisUtils.get(redisKey); Object cached = redisUtils.get(redisKey);
if (cached instanceof GoblinArtistTopicVo) { if (cached instanceof GoblinArtistTopicVo) {
GoblinArtistTopicVo topic = (GoblinArtistTopicVo) cached; GoblinArtistTopicVo topic = (GoblinArtistTopicVo) cached;
......
...@@ -211,9 +211,6 @@ goblin_artist_auth_apply.insert=INSERT INTO goblin_artist_auth_apply (apply_id,s ...@@ -211,9 +211,6 @@ goblin_artist_auth_apply.insert=INSERT INTO goblin_artist_auth_apply (apply_id,s
goblin_artist_auth_apply.revoke=UPDATE goblin_artist_auth_apply SET status=3,updated_at=? WHERE apply_id=? 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.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=? 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\u6388\u6743\u5173\u7CFB
goblin_artist_auth.insert=INSERT INTO goblin_artist_auth (auth_id,store_id,artist_id,apply_id,expire_at,status,created_at,updated_at)VALUES(?,?,?,?,?,?,?,?)
goblin_artist_auth.update_expire=UPDATE goblin_artist_auth SET expire_at=?,updated_at=? WHERE store_id=? AND artist_id=? AND status=1
#---- \u827A\u4EBA\u4E13\u9898 #---- \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.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.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=?
......
...@@ -162,7 +162,7 @@ public class GoblinStoreMgtArtistServiceImplTest { ...@@ -162,7 +162,7 @@ public class GoblinStoreMgtArtistServiceImplTest {
GoblinStoreMgtArtistApplySubmitParams submitParams = new GoblinStoreMgtArtistApplySubmitParams(); GoblinStoreMgtArtistApplySubmitParams submitParams = new GoblinStoreMgtArtistApplySubmitParams();
submitParams.setArtistId(artistId); submitParams.setArtistId(artistId);
submitParams.setAttachmentUrls(new ArrayList<>()); submitParams.setAttachmentUrls(new ArrayList<>());
submitParams.getAttachmentUrls().add("https://example.com/test.jpg"); submitParams.getAttachmentUrls().add("https://img.zhengzai.tv/other/2022/07/18/b519c4ff8e4c4ea280898acfb68e6048.jpeg");
ResponseDto<String> submitResp = artistService.submitApply(submitParams); ResponseDto<String> submitResp = artistService.submitApply(submitParams);
log.info("submitApply: success={} msg={} data={}", log.info("submitApply: success={} msg={} data={}",
...@@ -206,7 +206,7 @@ public class GoblinStoreMgtArtistServiceImplTest { ...@@ -206,7 +206,7 @@ public class GoblinStoreMgtArtistServiceImplTest {
*/ */
@Test @Test
public void applyList_filterByStatus() { public void applyList_filterByStatus() {
ResponseDto<PagedResult<GoblinStoreMgtArtistApplyListItemVo>> resp = artistService.applyList(GoblinArtistConst.APPLY_PENDING, 1, 10); ResponseDto<PagedResult<GoblinStoreMgtArtistApplyListItemVo>> resp = artistService.applyList(GoblinArtistConst.APPLY_APPROVED, 1, 10);
Assert.assertTrue("applyList status=0: " + resp.getMessage(), resp.isSuccess()); Assert.assertTrue("applyList status=0: " + resp.getMessage(), resp.isSuccess());
log.info("applyList status=0 total={}", resp.getData().getTotal()); log.info("applyList status=0 total={}", resp.getData().getTotal());
} }
......
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