Commit f5bec3eec687e356b31ac21648dfa3beb5c25dbf

Authored by YunaiV
1 parent ae71b713

✨ feat(im): 初始化表情包 v0.3:第四把 review(增加表情管理的界面)

Showing 18 changed files with 273 additions and 71 deletions
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/face/ImFaceUserItemController.java
... ... @@ -2,8 +2,8 @@ package cn.iocoder.yudao.module.im.controller.admin.face;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 4 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
5   -import cn.iocoder.yudao.module.im.controller.admin.face.vo.userItem.ImFaceUserItemRespVO;
6   -import cn.iocoder.yudao.module.im.controller.admin.face.vo.userItem.ImFaceUserItemSaveReqVO;
  5 +import cn.iocoder.yudao.module.im.controller.admin.face.vo.useritem.ImFaceUserItemRespVO;
  6 +import cn.iocoder.yudao.module.im.controller.admin.face.vo.useritem.ImFaceUserItemSaveReqVO;
7 7 import cn.iocoder.yudao.module.im.dal.dataobject.face.ImFaceUserItemDO;
8 8 import cn.iocoder.yudao.module.im.service.face.ImFaceUserItemService;
9 9 import io.swagger.v3.oas.annotations.Operation;
... ...
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/face/vo/pack/ImFacePackUserRespVO.java
... ... @@ -15,8 +15,8 @@ public class ImFacePackUserRespVO {
15 15 @Schema(description = "表情包名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "猫主子")
16 16 private String name;
17 17  
18   - @Schema(description = "表情包图标 URL", example = "https://cdn.example.com/face/pack/cat.png")
19   - private String iconUrl;
  18 + @Schema(description = "表情包图标", example = "https://cdn.example.com/face/pack/cat.png")
  19 + private String icon;
20 20  
21 21 @Schema(description = "表情列表", requiredMode = Schema.RequiredMode.REQUIRED)
22 22 private List<Item> items;
... ...
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/face/vo/userItem/ImFaceUserItemRespVO.java
1   -package cn.iocoder.yudao.module.im.controller.admin.face.vo.userItem;
  1 +package cn.iocoder.yudao.module.im.controller.admin.face.vo.useritem;
2 2  
3 3 import io.swagger.v3.oas.annotations.media.Schema;
4 4 import lombok.Data;
... ...
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/face/vo/userItem/ImFaceUserItemSaveReqVO.java
1   -package cn.iocoder.yudao.module.im.controller.admin.face.vo.userItem;
  1 +// TODO @AI:java 规范,应该是 useritem
  2 +package cn.iocoder.yudao.module.im.controller.admin.face.vo.useritem;
2 3  
3 4 import io.swagger.v3.oas.annotations.media.Schema;
4 5 import jakarta.validation.constraints.NotBlank;
... ... @@ -28,7 +29,4 @@ public class ImFaceUserItemSaveReqVO {
28 29 @NotNull(message = "渲染高度不能为空")
29 30 private Integer height;
30 31  
31   - @Schema(description = "来源消息编号(从消息「添加到表情」时传入;自己上传则不传)", example = "10086")
32   - private Long sourceMessageId;
33   -
34 32 }
... ...
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/ImFaceUserItemManagerController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.im.controller.admin.manager.face;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  7 +import cn.iocoder.yudao.module.im.controller.admin.manager.face.vo.useritem.ImFaceUserItemManagerPageReqVO;
  8 +import cn.iocoder.yudao.module.im.controller.admin.manager.face.vo.useritem.ImFaceUserItemManagerRespVO;
  9 +import cn.iocoder.yudao.module.im.dal.dataobject.face.ImFaceUserItemDO;
  10 +import cn.iocoder.yudao.module.im.service.face.ImFaceUserItemService;
  11 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  12 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  13 +import io.swagger.v3.oas.annotations.Operation;
  14 +import io.swagger.v3.oas.annotations.Parameter;
  15 +import io.swagger.v3.oas.annotations.tags.Tag;
  16 +import jakarta.annotation.Resource;
  17 +import jakarta.validation.Valid;
  18 +import org.springframework.security.access.prepost.PreAuthorize;
  19 +import org.springframework.validation.annotation.Validated;
  20 +import org.springframework.web.bind.annotation.*;
  21 +
  22 +import java.util.List;
  23 +import java.util.Map;
  24 +
  25 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  26 +
  27 +@Tag(name = "管理后台 - IM 用户表情")
  28 +@RestController
  29 +@RequestMapping("/im/manager/face-user-item")
  30 +@Validated
  31 +public class ImFaceUserItemManagerController {
  32 +
  33 + @Resource
  34 + private ImFaceUserItemService faceUserItemService;
  35 + @Resource
  36 + private AdminUserApi adminUserApi;
  37 +
  38 + @GetMapping("/page")
  39 + @Operation(summary = "获得用户表情分页")
  40 + @PreAuthorize("@ss.hasPermission('im:manager:face-user-item:query')")
  41 + public CommonResult<PageResult<ImFaceUserItemManagerRespVO>> getFaceUserItemPage(
  42 + @Valid ImFaceUserItemManagerPageReqVO pageReqVO) {
  43 + PageResult<ImFaceUserItemDO> pageResult = faceUserItemService.getFaceUserItemPage(pageReqVO);
  44 + // 关联回填用户昵称
  45 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
  46 + CollectionUtils.convertSet(pageResult.getList(), ImFaceUserItemDO::getUserId));
  47 + List<ImFaceUserItemManagerRespVO> voList = CollectionUtils.convertList(pageResult.getList(), item -> {
  48 + ImFaceUserItemManagerRespVO vo = BeanUtils.toBean(item, ImFaceUserItemManagerRespVO.class);
  49 + AdminUserRespDTO user = userMap.get(item.getUserId());
  50 + if (user != null) {
  51 + vo.setUserNickname(user.getNickname());
  52 + }
  53 + return vo;
  54 + });
  55 + return success(new PageResult<>(voList, pageResult.getTotal()));
  56 + }
  57 +
  58 + @DeleteMapping("/delete")
  59 + @Operation(summary = "删除用户表情")
  60 + @Parameter(name = "id", description = "编号", required = true, example = "4096")
  61 + @PreAuthorize("@ss.hasPermission('im:manager:face-user-item:delete')")
  62 + public CommonResult<Boolean> deleteFaceUserItem(@RequestParam("id") Long id) {
  63 + faceUserItemService.deleteFaceUserItem(id);
  64 + return success(true);
  65 + }
  66 +
  67 +}
... ...
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/vo/item/ImFacePackItemPageReqVO.java
... ... @@ -5,7 +5,6 @@ import io.swagger.v3.oas.annotations.media.Schema;
5 5 import lombok.Data;
6 6 import lombok.EqualsAndHashCode;
7 7  
8   -// TODO @AI:拆分到 vo/item 目录下,如果是 item 的话;更简练;
9 8 @Schema(description = "管理后台 - IM 表情包项分页 Request VO")
10 9 @Data
11 10 @EqualsAndHashCode(callSuper = true)
... ...
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/vo/pack/ImFacePackRespVO.java
... ... @@ -15,8 +15,8 @@ public class ImFacePackRespVO {
15 15 @Schema(description = "表情包名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "猫主子")
16 16 private String name;
17 17  
18   - @Schema(description = "表情包图标 URL", example = "https://cdn.example.com/face/pack/cat.png")
19   - private String iconUrl;
  18 + @Schema(description = "表情包图标", example = "https://cdn.example.com/face/pack/cat.png")
  19 + private String icon;
20 20  
21 21 @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
22 22 private Integer sort;
... ...
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/vo/pack/ImFacePackSaveReqVO.java
... ... @@ -18,9 +18,9 @@ public class ImFacePackSaveReqVO {
18 18 @Size(max = 64, message = "表情包名称长度不能超过 64")
19 19 private String name;
20 20  
21   - @Schema(description = "表情包图标 URL", example = "https://cdn.example.com/face/pack/cat.png")
22   - @Size(max = 512, message = "图标 URL 长度不能超过 512")
23   - private String iconUrl;
  21 + @Schema(description = "表情包图标", example = "https://cdn.example.com/face/pack/cat.png")
  22 + @Size(max = 512, message = "图标长度不能超过 512")
  23 + private String icon;
24 24  
25 25 @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
26 26 @NotNull(message = "排序不能为空")
... ...
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/vo/useritem/ImFaceUserItemManagerPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.im.controller.admin.manager.face.vo.useritem;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import org.springframework.format.annotation.DateTimeFormat;
  8 +
  9 +import java.time.LocalDateTime;
  10 +
  11 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  12 +
  13 +@Schema(description = "管理后台 - IM 用户表情分页 Request VO")
  14 +@Data
  15 +@EqualsAndHashCode(callSuper = true)
  16 +public class ImFaceUserItemManagerPageReqVO extends PageParam {
  17 +
  18 + @Schema(description = "所属用户编号", example = "1024")
  19 + private Long userId;
  20 +
  21 + @Schema(description = "表情名,模糊匹配", example = "狗")
  22 + private String name;
  23 +
  24 + @Schema(description = "添加时间", example = "[2026-04-01 00:00:00, 2026-04-30 23:59:59]")
  25 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  26 + private LocalDateTime[] createTime;
  27 +
  28 +}
... ...
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/vo/useritem/ImFaceUserItemManagerRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.im.controller.admin.manager.face.vo.useritem;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +
  8 +@Schema(description = "管理后台 - IM 用户表情 Response VO")
  9 +@Data
  10 +public class ImFaceUserItemManagerRespVO {
  11 +
  12 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "4096")
  13 + private Long id;
  14 +
  15 + @Schema(description = "所属用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  16 + private Long userId;
  17 +
  18 + @Schema(description = "所属用户昵称", example = "张三")
  19 + private String userNickname;
  20 +
  21 + @Schema(description = "表情图 URL", requiredMode = Schema.RequiredMode.REQUIRED,
  22 + example = "https://cdn.example.com/face/user/abc.gif")
  23 + private String url;
  24 +
  25 + @Schema(description = "表情名", example = "狗头")
  26 + private String name;
  27 +
  28 + @Schema(description = "渲染宽度(像素)", example = "200")
  29 + private Integer width;
  30 +
  31 + @Schema(description = "渲染高度(像素)", example = "200")
  32 + private Integer height;
  33 +
  34 + @Schema(description = "添加时间", requiredMode = Schema.RequiredMode.REQUIRED)
  35 + private LocalDateTime createTime;
  36 +
  37 +}
... ...
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/face/ImFacePackDO.java
... ... @@ -31,13 +31,12 @@ public class ImFacePackDO extends BaseDO {
31 31 * 表情包名称
32 32 */
33 33 private String name;
34   - // TODO @AI:icon 一般不带 url;类似 avatar,特有单词;
35 34 /**
36   - * 表情包图标 URL
  35 + * 表情包图标
37 36 * <p>
38 37 * 面板底部 tab 栏显示
39 38 */
40   - private String iconUrl;
  39 + private String icon;
41 40 /**
42 41 * 排序
43 42 */
... ...
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/face/ImFaceUserItemDO.java
... ... @@ -28,8 +28,8 @@ public class ImFaceUserItemDO extends BaseDO {
28 28 private Long id;
29 29 /**
30 30 * 所属用户编号
31   - *
32   - * // TODO @AI:是不是要关联下 userid
  31 + * <p>
  32 + * 关联 AdminUserDO 的 id 编号
33 33 */
34 34 private Long userId;
35 35 /**
... ... @@ -51,14 +51,6 @@ public class ImFaceUserItemDO extends BaseDO {
51 51 */
52 52 private Integer height;
53 53 /**
54   - * 来源消息编号
55   - * <p>
56   - * 从消息「添加到表情」时记录;自己上传则为 NULL;用于排查 / 后续溯源
57   - *
58   - * TODO @AI:关联字段;
59   - */
60   - private Long sourceMessageId;
61   - /**
62 54 * 排序
63 55 */
64 56 private Integer sort;
... ...
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/face/ImFaceUserItemMapper.java
1 1 package cn.iocoder.yudao.module.im.dal.mysql.face;
2 2  
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
3 4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
4 5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.module.im.controller.admin.manager.face.vo.useritem.ImFaceUserItemManagerPageReqVO;
5 7 import cn.iocoder.yudao.module.im.dal.dataobject.face.ImFaceUserItemDO;
6 8 import org.apache.ibatis.annotations.Mapper;
7 9  
... ... @@ -15,7 +17,6 @@ import java.util.List;
15 17 @Mapper
16 18 public interface ImFaceUserItemMapper extends BaseMapperX<ImFaceUserItemDO> {
17 19  
18   - /** 取用户的所有个人表情,按 sort 升序、id 倒序(最近添加在前) */
19 20 default List<ImFaceUserItemDO> selectListByUserId(Long userId) {
20 21 return selectList(new LambdaQueryWrapperX<ImFaceUserItemDO>()
21 22 .eq(ImFaceUserItemDO::getUserId, userId)
... ... @@ -23,15 +24,18 @@ public interface ImFaceUserItemMapper extends BaseMapperX&lt;ImFaceUserItemDO&gt; {
23 24 .orderByDesc(ImFaceUserItemDO::getId));
24 25 }
25 26  
26   - default Long selectCountByUserId(Long userId) {
27   - return selectCount(new LambdaQueryWrapperX<ImFaceUserItemDO>()
28   - .eq(ImFaceUserItemDO::getUserId, userId));
29   - }
30   -
31 27 default ImFaceUserItemDO selectByUserIdAndUrl(Long userId, String url) {
32 28 return selectOne(new LambdaQueryWrapperX<ImFaceUserItemDO>()
33 29 .eq(ImFaceUserItemDO::getUserId, userId)
34 30 .eq(ImFaceUserItemDO::getUrl, url));
35 31 }
36 32  
  33 + default PageResult<ImFaceUserItemDO> selectPage(ImFaceUserItemManagerPageReqVO reqVO) {
  34 + return selectPage(reqVO, new LambdaQueryWrapperX<ImFaceUserItemDO>()
  35 + .eqIfPresent(ImFaceUserItemDO::getUserId, reqVO.getUserId())
  36 + .likeIfPresent(ImFaceUserItemDO::getName, reqVO.getName())
  37 + .betweenIfPresent(ImFaceUserItemDO::getCreateTime, reqVO.getCreateTime())
  38 + .orderByDesc(ImFaceUserItemDO::getId));
  39 + }
  40 +
37 41 }
... ...
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/face/ImFacePackItemService.java
... ... @@ -9,8 +9,6 @@ import jakarta.validation.Valid;
9 9 import java.util.Collection;
10 10 import java.util.List;
11 11  
12   -// TODO @AI:已经分了用户端、和管理后台,注释上就不用写【用户端】【【管理后台】】了。。。
13   -// TODO @AI:注释的风格,参考下别的模块,一些 params、return 都要写呀;
14 12 /**
15 13 * IM 表情包项 Service 接口
16 14 *
... ... @@ -21,49 +19,73 @@ public interface ImFacePackItemService {
21 19 // ==================== 用户端 ====================
22 20  
23 21 /**
24   - * 【用户端】按 packIds 批量取启用项,给前端聚合接口用
  22 + * 按 packIds 批量取启用项,给前端聚合接口用
  23 + *
  24 + * @param packIds 表情包编号列表
  25 + * @return 启用状态的表情列表
25 26 */
26 27 List<ImFacePackItemDO> getEnabledItemListByPackIds(Collection<Long> packIds);
27 28  
28 29 /**
29 30 * 取某个表情包下的表情数量;ImFacePackService 删除前校验「包下无项」用
  31 + *
  32 + * @param packId 表情包编号
  33 + * @return 数量
30 34 */
31 35 Long getFacePackItemCount(Long packId);
32 36  
33 37 /**
34 38 * 取多个表情包下的表情数量合计;ImFacePackService 批量删除前校验用
  39 + *
  40 + * @param packIds 表情包编号列表
  41 + * @return 数量合计
35 42 */
36 43 Long getFacePackItemCount(Collection<Long> packIds);
37 44  
38 45 // ==================== 管理后台 ====================
39 46  
40 47 /**
41   - * 【管理后台】分页查询
  48 + * 分页查询表情包项
  49 + *
  50 + * @param reqVO 分页查询条件
  51 + * @return 表情包项分页
42 52 */
43 53 PageResult<ImFacePackItemDO> getFacePackItemPage(ImFacePackItemPageReqVO reqVO);
44 54  
45 55 /**
46   - * 【管理后台】获取详情
  56 + * 获取表情包项详情
  57 + *
  58 + * @param id 表情包项编号
  59 + * @return 表情包项 DO
47 60 */
48 61 ImFacePackItemDO getFacePackItem(Long id);
49 62  
50 63 /**
51   - * 【管理后台】新增,返回新增 id
  64 + * 新增表情包项
  65 + *
  66 + * @param reqVO 新增请求
  67 + * @return 新增表情包项编号
52 68 */
53 69 Long createFacePackItem(@Valid ImFacePackItemSaveReqVO reqVO);
54 70  
55 71 /**
56   - * 【管理后台】修改
  72 + * 修改表情包项
  73 + *
  74 + * @param reqVO 修改请求
57 75 */
58 76 void updateFacePackItem(@Valid ImFacePackItemSaveReqVO reqVO);
59 77  
60 78 /**
61   - * 【管理后台】删除
  79 + * 删除表情包项
  80 + *
  81 + * @param id 表情包项编号
62 82 */
63 83 void deleteFacePackItem(Long id);
64 84  
65 85 /**
66   - * 【管理后台】批量删除
  86 + * 批量删除表情包项
  87 + *
  88 + * @param ids 表情包项编号列表
67 89 */
68 90 void deleteFacePackItemList(List<Long> ids);
69 91  
... ...
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/face/ImFacePackService.java
... ... @@ -15,17 +15,20 @@ import java.util.List;
15 15 */
16 16 public interface ImFacePackService {
17 17  
18   - // TODO @AI:已经区分了 === 就不用写【用户端】【管理后台】了。
19   -
20 18 // ==================== 用户端 ====================
21 19  
22 20 /**
23   - * 【用户端】获取所有启用的表情包,按 sort 升序
  21 + * 获取所有启用的表情包,按 sort 升序
  22 + *
  23 + * @return 启用的表情包列表
24 24 */
25 25 List<ImFacePackDO> getEnabledFacePackList();
26 26  
27 27 /**
28 28 * 校验表情包存在
  29 + *
  30 + * @param id 表情包编号
  31 + * @return 表情包 DO
29 32 */
30 33 @SuppressWarnings("UnusedReturnValue")
31 34 ImFacePackDO validateFacePackExists(Long id);
... ... @@ -33,32 +36,47 @@ public interface ImFacePackService {
33 36 // ==================== 管理后台 ====================
34 37  
35 38 /**
36   - * 【管理后台】分页查询表情包
  39 + * 分页查询表情包
  40 + *
  41 + * @param reqVO 分页查询条件
  42 + * @return 表情包分页
37 43 */
38 44 PageResult<ImFacePackDO> getFacePackPage(ImFacePackPageReqVO reqVO);
39 45  
40 46 /**
41   - * 【管理后台】获取表情包详情
  47 + * 获取表情包详情
  48 + *
  49 + * @param id 表情包编号
  50 + * @return 表情包 DO
42 51 */
43 52 ImFacePackDO getFacePack(Long id);
44 53  
45 54 /**
46   - * 【管理后台】新增表情包,返回新增 id
  55 + * 新增表情包
  56 + *
  57 + * @param reqVO 新增请求
  58 + * @return 新增表情包编号
47 59 */
48 60 Long createFacePack(@Valid ImFacePackSaveReqVO reqVO);
49 61  
50 62 /**
51   - * 【管理后台】修改表情包
  63 + * 修改表情包
  64 + *
  65 + * @param reqVO 修改请求
52 66 */
53 67 void updateFacePack(@Valid ImFacePackSaveReqVO reqVO);
54 68  
55 69 /**
56   - * 【管理后台】删除表情包;存在表情时拒绝
  70 + * 删除表情包;包下存在表情时拒绝,避免历史 face 消息无法回查归属
  71 + *
  72 + * @param id 表情包编号
57 73 */
58 74 void deleteFacePack(Long id);
59 75  
60 76 /**
61   - * 【管理后台】批量删除表情包
  77 + * 批量删除表情包;任一包下存在表情时整批拒绝,避免「只删一半」中间态
  78 + *
  79 + * @param ids 表情包编号列表
62 80 */
63 81 void deleteFacePackList(List<Long> ids);
64 82  
... ...
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/face/ImFaceUserItemService.java
1 1 package cn.iocoder.yudao.module.im.service.face;
2 2  
3   -import cn.iocoder.yudao.module.im.controller.admin.face.vo.userItem.ImFaceUserItemSaveReqVO;
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.im.controller.admin.face.vo.useritem.ImFaceUserItemSaveReqVO;
  5 +import cn.iocoder.yudao.module.im.controller.admin.manager.face.vo.useritem.ImFaceUserItemManagerPageReqVO;
4 6 import cn.iocoder.yudao.module.im.dal.dataobject.face.ImFaceUserItemDO;
5 7 import jakarta.validation.Valid;
6 8  
... ... @@ -15,19 +17,44 @@ public interface ImFaceUserItemService {
15 17  
16 18 /**
17 19 * 获取指定用户的个人表情列表
  20 + *
  21 + * @param userId 用户编号
  22 + * @return 个人表情列表
18 23 */
19 24 List<ImFaceUserItemDO> getFaceUserItemList(Long userId);
20 25  
21 26 /**
22   - * 添加个人表情;返回新增记录 id
23   - * <p>
24   - * 同 URL 重复添加抛 FACE_USER_ITEM_DUPLICATED;表上 (user_id, url) 唯一约束兜底并发 race
  27 + * 添加个人表情
  28 + *
  29 + * @param userId 用户编号
  30 + * @param reqVO 添加请求
  31 + * @return 新增表情编号
25 32 */
26 33 Long createFaceUserItem(Long userId, @Valid ImFaceUserItemSaveReqVO reqVO);
27 34  
28 35 /**
29 36 * 删除指定用户的某条个人表情
  37 + *
  38 + * @param userId 用户编号
  39 + * @param id 表情编号
30 40 */
31 41 void deleteFaceUserItem(Long userId, Long id);
32 42  
  43 + // ==================== 管理后台 ====================
  44 +
  45 + /**
  46 + * 分页查询所有用户的个人表情;管理后台审计 / 删除违规图用
  47 + *
  48 + * @param reqVO 分页查询条件
  49 + * @return 个人表情分页
  50 + */
  51 + PageResult<ImFaceUserItemDO> getFaceUserItemPage(ImFaceUserItemManagerPageReqVO reqVO);
  52 +
  53 + /**
  54 + * 管理后台直接删除某条个人表情;不做归属校验
  55 + *
  56 + * @param id 表情编号
  57 + */
  58 + void deleteFaceUserItem(Long id);
  59 +
33 60 }
... ...
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/face/ImFaceUserItemServiceImpl.java
1 1 package cn.iocoder.yudao.module.im.service.face;
2 2  
3 3 import cn.hutool.core.util.ObjectUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
5   -import cn.iocoder.yudao.module.im.controller.admin.face.vo.userItem.ImFaceUserItemSaveReqVO;
  6 +import cn.iocoder.yudao.module.im.controller.admin.face.vo.useritem.ImFaceUserItemSaveReqVO;
  7 +import cn.iocoder.yudao.module.im.controller.admin.manager.face.vo.useritem.ImFaceUserItemManagerPageReqVO;
6 8 import cn.iocoder.yudao.module.im.dal.dataobject.face.ImFaceUserItemDO;
7 9 import cn.iocoder.yudao.module.im.dal.mysql.face.ImFaceUserItemMapper;
8 10 import jakarta.annotation.Resource;
9   -import org.springframework.dao.DuplicateKeyException;
10 11 import org.springframework.stereotype.Service;
11 12 import org.springframework.validation.annotation.Validated;
12 13  
... ... @@ -41,15 +42,9 @@ public class ImFaceUserItemServiceImpl implements ImFaceUserItemService {
41 42 throw exception(FACE_USER_ITEM_DUPLICATED);
42 43 }
43 44  
44   - // 2. 入库;表 (user_id, url, tenant_id) 唯一约束兜底并发 race —— 两个请求同时过 step 1 时 DB 拦截
45   - // TODO @AI:不加唯一索引。不然删除后恢复麻烦。按照我说的来。
46   - ImFaceUserItemDO item = BeanUtils.toBean(reqVO, ImFaceUserItemDO.class)
47   - .setUserId(userId);
48   - try {
49   - faceUserItemMapper.insert(item);
50   - } catch (DuplicateKeyException ignored) {
51   - throw exception(FACE_USER_ITEM_DUPLICATED);
52   - }
  45 + // 2. 入库
  46 + ImFaceUserItemDO item = BeanUtils.toBean(reqVO, ImFaceUserItemDO.class).setUserId(userId);
  47 + faceUserItemMapper.insert(item);
53 48 return item.getId();
54 49 }
55 50  
... ... @@ -69,4 +64,22 @@ public class ImFaceUserItemServiceImpl implements ImFaceUserItemService {
69 64 faceUserItemMapper.deleteById(id);
70 65 }
71 66  
  67 + // ==================== 管理后台 ====================
  68 +
  69 + @Override
  70 + public PageResult<ImFaceUserItemDO> getFaceUserItemPage(ImFaceUserItemManagerPageReqVO reqVO) {
  71 + return faceUserItemMapper.selectPage(reqVO);
  72 + }
  73 +
  74 + @Override
  75 + public void deleteFaceUserItem(Long id) {
  76 + // 1. 校验存在
  77 + if (faceUserItemMapper.selectById(id) == null) {
  78 + throw exception(FACE_USER_ITEM_NOT_EXISTS);
  79 + }
  80 +
  81 + // 2. 删除
  82 + faceUserItemMapper.deleteById(id);
  83 + }
  84 +
72 85 }
... ...
yudao-module-im/src/test/resources/sql/create_tables.sql
... ... @@ -149,7 +149,7 @@ CREATE TABLE IF NOT EXISTS &quot;im_group_request&quot; (
149 149 CREATE TABLE IF NOT EXISTS "im_face_pack" (
150 150 "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY COMMENT '编号',
151 151 "name" varchar(64) NOT NULL COMMENT '表情包名称',
152   - "icon_url" varchar(512) DEFAULT NULL COMMENT '表情包图标 URL(面板底部 tab 显示)',
  152 + "icon" varchar(512) DEFAULT NULL COMMENT '表情包图标(面板底部 tab 显示)',
153 153 "sort" int NOT NULL DEFAULT 0 COMMENT '排序',
154 154 "status" tinyint NOT NULL COMMENT '状态',
155 155 "creator" varchar(64) DEFAULT '',
... ... @@ -186,7 +186,6 @@ CREATE TABLE IF NOT EXISTS &quot;im_face_user_item&quot; (
186 186 "name" varchar(64) DEFAULT NULL COMMENT '表情名(可选)',
187 187 "width" int NOT NULL DEFAULT 0 COMMENT '渲染宽度(像素)',
188 188 "height" int NOT NULL DEFAULT 0 COMMENT '渲染高度(像素)',
189   - "source_message_id" bigint DEFAULT NULL COMMENT '来源消息编号(从消息「添加到表情」时记录;自己上传则为 NULL)',
190 189 "sort" int NOT NULL DEFAULT 0 COMMENT '排序',
191 190 "creator" varchar(64) DEFAULT '',
192 191 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
... ... @@ -194,6 +193,5 @@ CREATE TABLE IF NOT EXISTS &quot;im_face_user_item&quot; (
194 193 "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
195 194 "deleted" bit NOT NULL DEFAULT FALSE,
196 195 "tenant_id" bigint NOT NULL DEFAULT 0,
197   - PRIMARY KEY ("id"),
198   - CONSTRAINT "uk_im_face_user_item_user_url" UNIQUE ("user_id", "url", "tenant_id")
  196 + PRIMARY KEY ("id")
199 197 ) COMMENT 'IM 用户私有表情表';
... ...