feat(source): 添加管理员关联管理功能

- 新增管理员取消关联接口,实现软删除功能
- 新增管理员恢复关联接口,支持已取消记录的重新激活
- 新增查询已取消关联记录的分页接口
- 在MemberSourceEntity实体类中添加deleted和deletedAt字段
- 更新多个Mapper XML文件中的查询条件,过滤已删除记录
- 实现在删除和恢复操作后清除相关缓存的逻辑
- 添加对已删除记录的时间格式化显示支持
This commit is contained in:
2026-02-11 17:31:53 +08:00
parent f80b15446a
commit 49094be1c5
10 changed files with 166 additions and 26 deletions

View File

@@ -95,4 +95,34 @@ public class SourceController {
return sourceService.pageByFaceId(sourceReqQuery);
}
/**
* 管理员取消关联(软删除)
* @param id member_source 记录 ID
* @return 操作结果
*/
@PostMapping("/admin/cancel/{id}")
public ApiResponse cancelRelation(@PathVariable("id") Long id) {
return sourceService.cancelRelation(id);
}
/**
* 管理员恢复已取消的关联
* @param id member_source 记录 ID
* @return 操作结果
*/
@PostMapping("/admin/reactivate/{id}")
public ApiResponse reactivateRelation(@PathVariable("id") Long id) {
return sourceService.reactivateRelation(id);
}
/**
* 管理员查询已取消的关联记录
* @param sourceReqQuery 查询参数(需设置faceId,可选type/scenicId)
* @return 分页已取消关联列表
*/
@PostMapping("/admin/pageDeletedByFaceId")
public ApiResponse pageDeletedByFaceId(@RequestBody SourceReqQuery sourceReqQuery) {
return sourceService.pageDeletedByFaceId(sourceReqQuery);
}
}

View File

@@ -195,4 +195,12 @@ public interface SourceMapper {
* @return source响应列表
*/
List<SourceRespVO> pageByFaceId(SourceReqQuery sourceReqQuery);
int softDeleteRelation(Long id);
int reactivateRelation(Long id);
List<SourceRespVO> pageDeletedByFaceId(SourceReqQuery sourceReqQuery);
MemberSourceEntity getMemberSourceById(Long id);
}

View File

@@ -3,6 +3,8 @@ package com.ycwl.basic.model.pc.source.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.util.Date;
@Data
@TableName("member_source")
public class MemberSourceEntity {
@@ -15,4 +17,6 @@ public class MemberSourceEntity {
private Integer isBuy;
private Long orderId;
private Integer isFree;
private Integer deleted;
private Date deletedAt;
}

View File

@@ -46,4 +46,6 @@ public class SourceRespVO {
// 是否购买:0未购买,1已购买
private int isBuy;
private int isFree;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date deletedAt;
}

View File

@@ -38,4 +38,25 @@ public interface SourceService {
* @return 分页结果
*/
ApiResponse<PageInfo<SourceRespVO>> pageByFaceId(SourceReqQuery sourceReqQuery);
/**
* 管理员软删除(取消)关联记录
* @param id member_source 记录 ID
* @return 操作结果
*/
ApiResponse<Void> cancelRelation(Long id);
/**
* 管理员恢复已取消的关联记录
* @param id member_source 记录 ID
* @return 操作结果
*/
ApiResponse<Void> reactivateRelation(Long id);
/**
* 分页查询已取消的关联记录(管理员用)
* @param sourceReqQuery 查询参数(需设置faceId)
* @return 分页结果
*/
ApiResponse<PageInfo<SourceRespVO>> pageDeletedByFaceId(SourceReqQuery sourceReqQuery);
}

View File

@@ -3,6 +3,7 @@ package com.ycwl.basic.service.pc.impl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.ycwl.basic.exception.BaseException;
import com.ycwl.basic.biz.FaceStatusManager;
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2DTO;
import com.ycwl.basic.mapper.SourceMapper;
import com.ycwl.basic.model.pc.device.entity.DeviceEntity;
@@ -12,6 +13,7 @@ import com.ycwl.basic.model.pc.source.entity.SourceEntity;
import com.ycwl.basic.model.pc.source.req.SourceReqQuery;
import com.ycwl.basic.model.pc.source.resp.SourceRespVO;
import com.ycwl.basic.repository.DeviceRepository;
import com.ycwl.basic.repository.MemberRelationRepository;
import com.ycwl.basic.repository.ScenicRepository;
import com.ycwl.basic.repository.SourceRepository;
import com.ycwl.basic.service.pc.ScenicService;
@@ -54,6 +56,10 @@ public class SourceServiceImpl implements SourceService {
private ScenicRepository scenicRepository;
@Autowired
private DeviceRepository deviceRepository;
@Autowired
private MemberRelationRepository memberRelationRepository;
@Autowired
private FaceStatusManager faceStatusManager;
@Override
public ApiResponse<PageInfo<SourceRespVO>> pageQuery(SourceReqQuery sourceReqQuery) {
@@ -232,4 +238,47 @@ public class SourceServiceImpl implements SourceService {
PageInfo<SourceRespVO> pageInfo = new PageInfo<>(list);
return ApiResponse.success(pageInfo);
}
@Override
public ApiResponse<Void> cancelRelation(Long id) {
MemberSourceEntity entity = sourceMapper.getMemberSourceById(id);
if (entity == null) {
return ApiResponse.fail("关联记录不存在");
}
int rows = sourceMapper.softDeleteRelation(id);
if (rows == 0) {
return ApiResponse.fail("记录已取消或不存在");
}
invalidateCacheByFace(entity.getFaceId());
return ApiResponse.success(null);
}
@Override
public ApiResponse<Void> reactivateRelation(Long id) {
MemberSourceEntity entity = sourceMapper.getMemberSourceById(id);
if (entity == null) {
return ApiResponse.fail("关联记录不存在");
}
int rows = sourceMapper.reactivateRelation(id);
if (rows == 0) {
return ApiResponse.fail("记录未处于取消状态");
}
invalidateCacheByFace(entity.getFaceId());
return ApiResponse.success(null);
}
@Override
public ApiResponse<PageInfo<SourceRespVO>> pageDeletedByFaceId(SourceReqQuery sourceReqQuery) {
PageHelper.startPage(sourceReqQuery.getPageNum(), sourceReqQuery.getPageSize());
List<SourceRespVO> list = sourceMapper.pageDeletedByFaceId(sourceReqQuery);
PageInfo<SourceRespVO> pageInfo = new PageInfo<>(list);
return ApiResponse.success(pageInfo);
}
private void invalidateCacheByFace(Long faceId) {
if (faceId != null) {
memberRelationRepository.clearSCacheByFace(faceId);
faceStatusManager.invalidatePuzzleSourceVersion(faceId);
}
}
}

View File

@@ -136,7 +136,7 @@
FROM `zt`.`face`
WHERE `scenic_id` = #{scenicId}
AND `create_at` &lt; #{endDate}
and `id` not in (select face_id from member_source where is_buy = 1)
and `id` not in (select face_id from member_source where is_buy = 1 AND deleted = 0)
and `id` not in (select face_id from member_video where is_buy = 1)
</select>
</mapper>

View File

@@ -94,14 +94,14 @@
FROM member_source ms
LEFT JOIN face f ON ms.face_id = f.id
LEFT JOIN source s ON ms.source_id = s.id
WHERE s.id IS NOT NULL
WHERE s.id IS NOT NULL AND ms.deleted = 0
),
member_source_aicam_data AS (
SELECT ms.member_id, ms.source_id, ms.face_id, f.face_url, s.url
FROM member_source ms
LEFT JOIN face f ON ms.face_id = f.id
LEFT JOIN source s ON ms.source_id = s.id
WHERE s.id IS NOT NULL AND ms.type = 3
WHERE s.id IS NOT NULL AND ms.type = 3 AND ms.deleted = 0
),
member_photo_data AS (
SELECT mp.member_id, 3 as type, mp.id, mp.crop_url as url, mp.quantity, mp.status, mp.create_time

View File

@@ -22,7 +22,7 @@
select s.scenic_id, s.device_id
from member_source ms
left join source s on ms.source_id = s.id
where ms.type = 1 and s.id is not null
where ms.type = 1 and s.id is not null and ms.deleted = 0
and s.create_time >= #{start}
and s.create_time &lt;= #{end}
group by s.scenic_id, s.device_id, ms.face_id
@@ -53,7 +53,7 @@
select s.scenic_id, s.device_id
from member_source ms
left join source s on ms.source_id = s.id
where ms.type = 2 and s.id is not null
where ms.type = 2 and s.id is not null and ms.deleted = 0
and s.create_time >= #{start}
and s.create_time &lt;= #{end}
group by s.scenic_id, s.device_id, ms.face_id

View File

@@ -165,11 +165,11 @@
</delete>
<delete id="deleteNotBuyRelations">
delete from member_source
where scenic_id = #{scenicId} and is_buy = 0 and create_time &lt;= #{endDate}
where scenic_id = #{scenicId} and is_buy = 0 and create_time &lt;= #{endDate} and deleted = 0
</delete>
<delete id="deleteNotBuyFaceRelation">
delete from member_source
where member_id = #{userId} and face_id = #{faceId} and is_buy = 0
where member_id = #{userId} and face_id = #{faceId} and is_buy = 0 and deleted = 0
</delete>
<delete id="deleteUselessSource">
delete from source where id not in (select source_id from member_source) and face_sample_id not in (select id from face_sample)
@@ -195,7 +195,7 @@
from member_source ms
left join source so on ms.source_id = so.id
where so.id = #{id} and ms.member_id = #{userId} and so.id is not null
where so.id = #{id} and ms.member_id = #{userId} and so.id is not null and ms.deleted = 0
</select>
<select id="getById" resultType="com.ycwl.basic.model.pc.source.resp.SourceRespVO">
select so.id, scenic_id, device_id, thumb_url, type, url, video_url, so.create_time, so.update_time
@@ -207,6 +207,7 @@
select ms.type, ms.is_buy
from member_source ms
<where>
and ms.deleted = 0
<if test="scenicId!= null">and ms.scenic_id = #{scenicId} </if>
<if test="memberId!= null">and ms.member_id = #{memberId} </if>
<if test="isBuy!=null">and ms.is_buy = #{isBuy}</if>
@@ -214,7 +215,7 @@
group by ms.type
</select>
<select id="countByMemberId" resultType="java.lang.Integer">
select count(1) from member_source where member_id = #{userId}
select count(1) from member_source where member_id = #{userId} and deleted = 0
</select>
<select id="listBySampleIds" resultType="com.ycwl.basic.model.pc.source.entity.SourceEntity">
select *
@@ -245,6 +246,7 @@
from member_source ms
left join source so on ms.source_id = so.id
<where>
and ms.deleted = 0
<if test="scenicId!= null">and ms.scenic_id = #{scenicId} </if>
<if test="isBuy!=null">and ms.is_buy = #{isBuy} </if>
<if test="type!=null">and ms.type = #{type} </if>
@@ -258,7 +260,7 @@
from member_source ms
left join source so on ms.source_id = so.id
where ms.member_id = #{userId} and ms.source_id = #{sourceId} and so.id is not null
where ms.member_id = #{userId} and ms.source_id = #{sourceId} and so.id is not null and ms.deleted = 0
limit 1
</select>
<select id="queryByRelation" resultType="com.ycwl.basic.model.pc.source.resp.SourceRespVO">
@@ -267,7 +269,7 @@
left join source so on ms.source_id = so.id
where
ms.member_id = #{memberId} and so.id is not null
ms.member_id = #{memberId} and so.id is not null and ms.deleted = 0
<if test="faceId!= null">and ms.face_id = #{faceId} </if>
<if test="type!=null">and ms.type = #{type} </if>
<if test="scenicId!= null">and ms.scenic_id = #{scenicId} </if>
@@ -302,7 +304,7 @@
ROW_NUMBER() OVER (PARTITION BY ms.face_id, ms.type ORDER BY so.create_time DESC) as rn
FROM member_source ms
LEFT JOIN source so ON ms.source_id = so.id
WHERE so.id IS NOT NULL
WHERE so.id IS NOT NULL AND ms.deleted = 0
<if test="faceIds != null and faceIds.size() > 0">
AND ms.face_id IN
<foreach collection="faceIds" item="id" open="(" separator="," close=")">
@@ -326,33 +328,33 @@
<select id="hasRelationTo" resultType="java.lang.Integer">
select count(1)
from member_source
where member_id = #{memberId} and source_id = #{sourceId} and type = #{type}
where member_id = #{memberId} and source_id = #{sourceId} and type = #{type} and deleted = 0
</select>
<select id="listVideoByFaceRelation" resultType="com.ycwl.basic.model.pc.source.entity.SourceEntity">
select s.*, ms.is_buy
from member_source ms
left join source s on ms.source_id = s.id
where ms.face_id = #{faceId} and ms.type = 1
where ms.face_id = #{faceId} and ms.type = 1 and ms.deleted = 0
order by create_time desc
</select>
<select id="listVideoByScenicFaceRelation" resultType="com.ycwl.basic.model.pc.source.entity.SourceEntity">
select s.*, ms.is_buy
from member_source ms
left join source s on ms.source_id = s.id
where ms.face_id = #{faceId} and ms.type = 1 and ms.scenic_id = #{scenicId}
where ms.face_id = #{faceId} and ms.type = 1 and ms.scenic_id = #{scenicId} and ms.deleted = 0
order by create_time desc
</select>
<select id="listImageByFaceRelation" resultType="com.ycwl.basic.model.pc.source.entity.SourceEntity">
select s.*, ms.is_buy
from member_source ms
left join source s on ms.source_id = s.id
where ms.face_id = #{faceId} and ms.type = 2
where ms.face_id = #{faceId} and ms.type = 2 and ms.deleted = 0
</select>
<select id="listAiCamImageByFaceRelation" resultType="com.ycwl.basic.model.pc.source.entity.SourceEntity">
select s.*, ms.is_buy
from member_source ms
left join source s on ms.source_id = s.id
where ms.face_id = #{faceId} and ms.type = 3
where ms.face_id = #{faceId} and ms.type = 3 and ms.deleted = 0
</select>
<select id="getEntity" resultType="com.ycwl.basic.model.pc.source.entity.SourceEntity">
select *
@@ -379,7 +381,7 @@
from member_source ms
left join source so on ms.source_id = so.id
where
ms.member_id = #{memberId} and so.id is not null
ms.member_id = #{memberId} and so.id is not null and ms.deleted = 0
<if test="scenicId!= null">and ms.scenic_id = #{scenicId} </if>
<if test="isBuy!=null">and ms.is_buy = #{isBuy} </if>
<if test="type!=null">and ms.type = #{type} </if>
@@ -388,7 +390,7 @@
<select id="listByFaceRelation" resultType="com.ycwl.basic.model.pc.source.entity.MemberSourceEntity">
select *
from member_source ms
where ms.face_id = #{faceId}
where ms.face_id = #{faceId} and ms.deleted = 0
<if test="type!=null">and ms.type = #{type} </if>
</select>
<update id="updateMemberIdByFaceId">
@@ -400,7 +402,7 @@
select s.*
from source s
inner join member_source ms on s.id = ms.source_id
where ms.face_id = #{faceId} and s.type = 2
where ms.face_id = #{faceId} and s.type = 2 and ms.deleted = 0
</select>
<select id="getBySampleIdAndType" resultType="com.ycwl.basic.model.pc.source.entity.SourceEntity">
select *
@@ -416,6 +418,7 @@
INNER JOIN source s ON ms.source_id = s.id
WHERE ms.face_id = #{faceId}
AND s.type = 2
AND ms.deleted = 0
</select>
<select id="getSourceByFaceAndDeviceIndex" resultType="com.ycwl.basic.model.pc.source.entity.SourceEntity">
@@ -445,6 +448,7 @@
INNER JOIN member_source ms ON s.id = ms.source_id
WHERE ms.face_id = #{faceId}
AND s.type = #{type}
AND ms.deleted = 0
)
SELECT *
FROM ranked_sources
@@ -456,7 +460,7 @@
SELECT DISTINCT s.device_id
FROM member_source ms
INNER JOIN source s ON ms.source_id = s.id
WHERE ms.face_id = #{faceId}
WHERE ms.face_id = #{faceId} AND ms.deleted = 0
ORDER BY s.device_id ASC
</select>
@@ -467,6 +471,7 @@
WHERE ms.face_id = #{faceId}
AND s.device_id = #{deviceId}
AND s.type = #{type}
AND ms.deleted = 0
<choose>
<when test='sortStrategy == "LATEST"'>
ORDER BY s.create_time DESC
@@ -505,19 +510,19 @@
<delete id="deleteRelationsByFaceIdAndType">
DELETE FROM member_source
WHERE face_id = #{faceId} AND `type` = #{type}
WHERE face_id = #{faceId} AND `type` = #{type} AND deleted = 0
</delete>
<select id="countFreeRelationsByFaceIdAndType" resultType="int">
SELECT COUNT(*) FROM member_source
WHERE face_id = #{faceId} AND `type` = #{type} AND is_free = 1
WHERE face_id = #{faceId} AND `type` = #{type} AND is_free = 1 AND deleted = 0
</select>
<select id="listSourceByFaceRelation" resultType="com.ycwl.basic.model.pc.source.entity.SourceEntity">
SELECT s.*
FROM member_source ms
INNER JOIN source s ON ms.source_id = s.id
WHERE ms.face_id = #{faceId}
WHERE ms.face_id = #{faceId} AND ms.deleted = 0
<if test="type != null">
AND ms.type = #{type}
</if>
@@ -526,7 +531,7 @@
<select id="listFaceIdsBySourceIds" resultType="com.ycwl.basic.model.pc.source.entity.MemberSourceEntity">
SELECT source_id, face_id
FROM member_source
WHERE source_id IN
WHERE deleted = 0 AND source_id IN
<foreach collection="list" item="item" open="(" separator="," close=")">
#{item}
</foreach>
@@ -537,10 +542,31 @@
ms.is_free, so.create_time, ms.is_buy, so.device_id
FROM member_source ms
LEFT JOIN source so ON ms.source_id = so.id
WHERE ms.face_id = #{faceId} AND so.id IS NOT NULL
WHERE ms.face_id = #{faceId} AND so.id IS NOT NULL AND ms.deleted = 0
<if test="type != null">AND ms.type = #{type}</if>
<if test="scenicId != null">AND ms.scenic_id = #{scenicId}</if>
<if test="isBuy != null">AND ms.is_buy = #{isBuy}</if>
ORDER BY so.create_time DESC
</select>
<update id="softDeleteRelation">
UPDATE member_source SET deleted = 1, deleted_at = NOW()
WHERE id = #{id} AND deleted = 0
</update>
<update id="reactivateRelation">
UPDATE member_source SET deleted = 0, deleted_at = NULL
WHERE id = #{id} AND deleted = 1
</update>
<select id="pageDeletedByFaceId" resultType="com.ycwl.basic.model.pc.source.resp.SourceRespVO">
SELECT so.id, ms.face_id, ms.scenic_id, ms.type, so.thumb_url, so.url, so.video_url,
ms.is_free, so.create_time, ms.is_buy, so.device_id, ms.deleted_at
FROM member_source ms
LEFT JOIN source so ON ms.source_id = so.id
WHERE ms.face_id = #{faceId} AND so.id IS NOT NULL AND ms.deleted = 1
<if test="type != null">AND ms.type = #{type}</if>
<if test="scenicId != null">AND ms.scenic_id = #{scenicId}</if>
ORDER BY ms.deleted_at DESC
</select>
<select id="getMemberSourceById" resultType="com.ycwl.basic.model.pc.source.entity.MemberSourceEntity">
SELECT * FROM member_source WHERE id = #{id}
</select>
</mapper>