fix:bug修改
This commit is contained in:
1 parent
c0fa58d2bc
commit
b220dc7e09
37 files changed
+1115
-110
No files matched your search
@@ -6,21 +6,30 @@ CREATE TABLE `member_recent`
|
||||
(
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '收件地址编号',
|
||||
`member_id` bigint NOT NULL COMMENT '用户编号',
|
||||
`cover_pic` varchar(256) NOT NULL COMMENT '主图',
|
||||
`describe` varchar(256) NOT NULL COMMENT '描述',
|
||||
`cover_pic` varchar(2048) DEFAULT NULL COMMENT '主图',
|
||||
`cover_video` varchar(2048) DEFAULT NULL COMMENT '视频',
|
||||
`description` varchar(1000) NOT NULL COMMENT '描述',
|
||||
`issue_long` decimal(10, 8) NOT NULL COMMENT '发布-经度',
|
||||
`issue_lat` decimal(10, 8) NOT NULL COMMENT '发布-纬度',
|
||||
`issue_province` varchar(64) DEFAULT NULL COMMENT '发布-省',
|
||||
`issue_city` varchar(64) DEFAULT NULL COMMENT '发布-市',
|
||||
`issue_district` varchar(64) DEFAULT NULL COMMENT '发布-区',
|
||||
`issue_address` varchar(256) DEFAULT NULL COMMENT '发布-详细地址',
|
||||
`visible` tinyint(1) NOT NULL DEFAULT 1 COMMENT '是否展示:0-隐藏,1-显示',
|
||||
`audit_status` tinyint NOT NULL DEFAULT 0 COMMENT '审核状态:0-审核中,1-审核通过,2-审核不通过',
|
||||
`audit_source` varchar(16) DEFAULT NULL COMMENT '审核来源:THIRD_PARTY-第三方,MANUAL-人工,HISTORY-历史数据',
|
||||
`audit_reason` varchar(512) DEFAULT NULL COMMENT '审核原因',
|
||||
`audit_time` datetime DEFAULT NULL COMMENT '审核时间',
|
||||
`audit_user_id` bigint DEFAULT NULL COMMENT '人工审核用户编号',
|
||||
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
KEY `idx_member_recent_audit_status_create_time` (`audit_status`, `create_time`),
|
||||
KEY `idx_member_recent_visible_audit_create_time` (`visible`, `audit_status`, `create_time`)
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 32 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '朋友圈';
|
||||
|
||||
-- ----------------------------
|
||||
@@ -318,4 +327,4 @@ CREATE TABLE `pay_wallet_tx_auth`
|
||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 32 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '提现金额审核';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 32 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '提现金额审核';
|
||||
@@ -0,0 +1,19 @@
|
||||
-- 动态审核字段。历史动态默认通过,避免上线后从公开列表消失。
|
||||
ALTER TABLE `member_recent`
|
||||
ADD COLUMN `audit_status` tinyint NULL DEFAULT 1 COMMENT '审核状态:0-审核中,1-审核通过,2-审核不通过' AFTER `issue_address`,
|
||||
ADD COLUMN `audit_source` varchar(16) NULL COMMENT '审核来源:THIRD_PARTY-第三方,MANUAL-人工,HISTORY-历史数据' AFTER `audit_status`,
|
||||
ADD COLUMN `audit_reason` varchar(512) NULL COMMENT '审核原因' AFTER `audit_source`,
|
||||
ADD COLUMN `audit_time` datetime NULL COMMENT '审核时间' AFTER `audit_reason`,
|
||||
ADD COLUMN `audit_user_id` bigint NULL COMMENT '人工审核用户编号' AFTER `audit_time`;
|
||||
|
||||
UPDATE `member_recent`
|
||||
SET `audit_source` = 'HISTORY',
|
||||
`audit_reason` = '历史数据默认通过',
|
||||
`audit_time` = COALESCE(`update_time`, `create_time`)
|
||||
WHERE `audit_status` = 1;
|
||||
|
||||
ALTER TABLE `member_recent`
|
||||
MODIFY COLUMN `audit_status` tinyint NOT NULL DEFAULT 0 COMMENT '审核状态:0-审核中,1-审核通过,2-审核不通过';
|
||||
|
||||
CREATE INDEX `idx_member_recent_audit_status_create_time`
|
||||
ON `member_recent` (`audit_status`, `create_time`);
|
||||
@@ -0,0 +1,6 @@
|
||||
-- 动态紧急下架字段。历史动态默认继续展示。
|
||||
ALTER TABLE `member_recent`
|
||||
ADD COLUMN `visible` tinyint(1) NOT NULL DEFAULT 1 COMMENT '是否展示:0-隐藏,1-显示' AFTER `issue_address`;
|
||||
|
||||
CREATE INDEX `idx_member_recent_visible_audit_create_time`
|
||||
ON `member_recent` (`visible`, `audit_status`, `create_time`);
|
||||
+3
-1
@@ -22,6 +22,7 @@ public interface MemberErrorCode {
|
||||
ErrorCode AUTH_LOGIN_USER_DISABLED = new ErrorCode(1_004_003_001, "登录失败,账号被禁用");
|
||||
ErrorCode AUTH_SOCIAL_USER_NOT_FOUND = new ErrorCode(1_004_003_005, "登录失败,解析不到三方登录信息");
|
||||
ErrorCode AUTH_MOBILE_USED = new ErrorCode(1_004_003_007, "手机号已经被使用");
|
||||
ErrorCode AUTH_MINI_PROGRAM_LOGIN_CODE_INVALID = new ErrorCode(1_004_003_008, "小程序登录凭证已失效,请重新发起支付");
|
||||
|
||||
// ========== 用户收件地址 1-004-004-000 ==========
|
||||
ErrorCode ADDRESS_NOT_EXISTS = new ErrorCode(1_004_004_000, "用户收件地址不存在");
|
||||
@@ -89,4 +90,5 @@ public interface MemberErrorCode {
|
||||
//========== 聊天相关 1-004-018-000 ==========
|
||||
ErrorCode CHAT_USER_COUNT_EXCEED = new ErrorCode(1_004_018_000, "当天免费聊天人数已用完");
|
||||
ErrorCode CHAT_MSG_COUNT_EXCEED = new ErrorCode(1_004_018_001, "当天免费聊天次数已用完");
|
||||
}
|
||||
ErrorCode CHAT_TOKEN_CREATE_FAILED = new ErrorCode(1_004_018_002, "聊天登录凭证签发失败,请稍后重试");
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package cn.iocoder.yudao.module.member.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 动态审核来源
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum MemberRecentAuditSourceEnum {
|
||||
|
||||
THIRD_PARTY("THIRD_PARTY", "第三方审核"),
|
||||
MANUAL("MANUAL", "人工审核"),
|
||||
HISTORY("HISTORY", "历史数据");
|
||||
|
||||
private final String source;
|
||||
private final String description;
|
||||
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package cn.iocoder.yudao.module.member.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 动态审核状态
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum MemberRecentAuditStatusEnum {
|
||||
|
||||
PENDING(0, "审核中"),
|
||||
APPROVED(1, "审核通过"),
|
||||
REJECTED(2, "审核不通过");
|
||||
|
||||
private final Integer status;
|
||||
private final String description;
|
||||
|
||||
public static boolean isManualAuditStatus(Integer status) {
|
||||
return Arrays.asList(APPROVED.status, REJECTED.status).contains(status);
|
||||
}
|
||||
|
||||
}
|
||||
+4
-2
@@ -282,12 +282,14 @@ public class ThirdAuthServiceImpl implements ThirdAuthService {
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
log.error("|视频审核-同步|,线程被中断videoUrl:{}error:{}", videoUrl, e.getMessage(), e);
|
||||
resultVo.setIsSuccess(true);
|
||||
resultVo.setIsSuccess(false);
|
||||
resultVo.setFirstErrorMsg("视频审核任务被中断,请人工审核");
|
||||
return resultVo;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
log.error("|视频审核-同步|,意外的错误videoUrl:{}error:{}", videoUrl, e.getMessage());
|
||||
resultVo.setIsSuccess(true);
|
||||
resultVo.setIsSuccess(false);
|
||||
resultVo.setFirstErrorMsg("视频审核服务异常,请人工审核");
|
||||
return resultVo;
|
||||
}
|
||||
}
|
||||
|
||||
+3
@@ -13,4 +13,7 @@ public class ChatUserOnLineStatusRespVo {
|
||||
@Schema(description = "是否在线")
|
||||
private Boolean online;
|
||||
|
||||
@Schema(description = "最后活跃时间,毫秒时间戳")
|
||||
private Long lastActiveTime;
|
||||
|
||||
}
|
||||
+18
-1
@@ -21,6 +21,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
||||
|
||||
@@ -65,6 +66,22 @@ public class RecentController {
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/audit")
|
||||
@Operation(summary = "人工审核动态")
|
||||
@PreAuthorize("@ss.hasPermission('member:recent:update')")
|
||||
public CommonResult<Boolean> auditRecent(@Valid @RequestBody RecentAuditReqVO auditReqVO) {
|
||||
recentService.auditRecent(auditReqVO, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/visible")
|
||||
@Operation(summary = "更新动态展示状态")
|
||||
@PreAuthorize("@ss.hasPermission('member:recent:update')")
|
||||
public CommonResult<Boolean> updateRecentVisible(@Valid @RequestBody RecentVisibleReqVO visibleReqVO) {
|
||||
recentService.updateRecentVisible(visibleReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得朋友圈")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@@ -93,4 +110,4 @@ public class RecentController {
|
||||
ExcelUtils.write(response, "朋友圈.xls", "数据", RecentRespVO.class, BeanUtils.toBean(list, RecentRespVO.class));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package cn.iocoder.yudao.module.member.controller.admin.recent.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
@Schema(description = "管理后台 - 动态人工审核 Request VO")
|
||||
@Data
|
||||
public class RecentAuditReqVO {
|
||||
|
||||
@Schema(description = "动态编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "动态编号不能为空")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "审核状态:1-通过,2-不通过", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "审核状态不能为空")
|
||||
private Integer auditStatus;
|
||||
|
||||
@Schema(description = "审核原因")
|
||||
@Size(max = 512, message = "审核原因不能超过 512 个字符")
|
||||
private String auditReason;
|
||||
|
||||
}
|
||||
+7
-1
@@ -73,6 +73,12 @@ public class RecentPageReqVO extends PageParam {
|
||||
@Schema(description = "是否是VIP")
|
||||
private Boolean isVip;
|
||||
|
||||
@Schema(description = "是否展示")
|
||||
private Boolean visible;
|
||||
|
||||
@Schema(description = "审核状态:0-审核中,1-审核通过,2-审核不通过")
|
||||
private Integer auditStatus;
|
||||
|
||||
/**
|
||||
* 用户tag相关
|
||||
*/
|
||||
@@ -88,4 +94,4 @@ public class RecentPageReqVO extends PageParam {
|
||||
@Schema(description = "当前经度")
|
||||
private Double currentLng;
|
||||
|
||||
}
|
||||
}
|
||||
+25
-2
@@ -34,7 +34,7 @@ public class RecentRespVO {
|
||||
|
||||
@Schema(description = "描述", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@ExcelProperty("描述")
|
||||
private String describe;
|
||||
private String description;
|
||||
|
||||
@Schema(description = "发布-经度", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@ExcelProperty("发布-经度")
|
||||
@@ -60,8 +60,31 @@ public class RecentRespVO {
|
||||
@ExcelProperty("发布-详细地址")
|
||||
private String issueAddress;
|
||||
|
||||
@Schema(description = "是否展示")
|
||||
@ExcelProperty("展示状态")
|
||||
private Boolean visible;
|
||||
|
||||
@Schema(description = "审核状态:0-审核中,1-审核通过,2-审核不通过")
|
||||
@ExcelProperty("审核状态")
|
||||
private Integer auditStatus;
|
||||
|
||||
@Schema(description = "审核来源")
|
||||
@ExcelProperty("审核来源")
|
||||
private String auditSource;
|
||||
|
||||
@Schema(description = "审核原因")
|
||||
@ExcelProperty("审核原因")
|
||||
private String auditReason;
|
||||
|
||||
@Schema(description = "审核时间")
|
||||
@ExcelProperty("审核时间")
|
||||
private LocalDateTime auditTime;
|
||||
|
||||
@Schema(description = "人工审核用户编号")
|
||||
private Long auditUserId;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@ExcelProperty("创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package cn.iocoder.yudao.module.member.controller.admin.recent.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@Schema(description = "管理后台 - 动态展示状态更新 Request VO")
|
||||
@Data
|
||||
public class RecentVisibleReqVO {
|
||||
|
||||
@Schema(description = "动态编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "动态编号不能为空")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "是否展示", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
|
||||
@NotNull(message = "是否展示不能为空")
|
||||
private Boolean visible;
|
||||
|
||||
}
|
||||
+20
@@ -69,6 +69,26 @@ public class AppAuthController {
|
||||
return success(authService.refreshToken(refreshToken));
|
||||
}
|
||||
|
||||
@PostMapping("/mini-program-login-code")
|
||||
@Operation(summary = "创建 H5 跳转微信小程序的一次性登录码")
|
||||
public CommonResult<String> createMiniProgramLoginCode() {
|
||||
return success(authService.createMiniProgramLoginCode(getLoginUserId()));
|
||||
}
|
||||
|
||||
@PostMapping("/mini-program-login")
|
||||
@Operation(summary = "使用一次性登录码登录微信小程序")
|
||||
@Parameter(name = "code", description = "一次性登录码", required = true)
|
||||
@PermitAll
|
||||
public CommonResult<AppAuthLoginRespVO> loginByMiniProgramCode(@RequestParam("code") String code) {
|
||||
return success(authService.loginByMiniProgramCode(code));
|
||||
}
|
||||
|
||||
@PostMapping("/chat-token")
|
||||
@Operation(summary = "使用当前登录态重新签发聊天令牌")
|
||||
public CommonResult<AppAuthChatTokenRespVO> reissueChatToken() {
|
||||
return success(new AppAuthChatTokenRespVO(authService.reissueChatToken(getLoginUserId())));
|
||||
}
|
||||
|
||||
// ========== 短信登录相关 ==========
|
||||
|
||||
@PostMapping("/sms-register")
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package cn.iocoder.yudao.module.member.controller.app.auth.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Schema(description = "用户 APP - 聊天令牌重新签发 Response VO")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AppAuthChatTokenRespVO {
|
||||
|
||||
@Schema(description = "聊天令牌", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private AppAuthLoginRespVO.ChatToken chatToken;
|
||||
|
||||
}
|
||||
+12
-1
@@ -151,6 +151,17 @@ public class AppRecentController {
|
||||
return success(recentService.dynamicPage(dto));
|
||||
}
|
||||
|
||||
@PostMapping("/update-and-resubmit")
|
||||
@Operation(summary = "修改并重新提交朋友圈审核")
|
||||
public CommonResult<Boolean> updateAndResubmitRecent(@Valid @RequestBody AppRecentUpdateReqVO dto) {
|
||||
if (ObjectUtil.isAllEmpty(dto.getCoverVideo(), dto.getCoverPic())) {
|
||||
throw new RuntimeException("图片、视频必须上传其中之一");
|
||||
}
|
||||
dto.setMemberId(getLoginUserId());
|
||||
recentService.updateAndResubmitRecent(BeanUtils.toBean(dto, RecentSaveReqVO.class), getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/evaluate-page")
|
||||
@Operation(summary = "我的评价")
|
||||
public CommonResult<PageResult<cn.iocoder.yudao.module.member.controller.admin.recent.vo.OperationRespVO>> evaluatePage(@Valid @RequestBody AppRecentLikeReqVo dto) {
|
||||
@@ -183,4 +194,4 @@ public class AppRecentController {
|
||||
return success(recentService.total(getLoginUserId()));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+3
@@ -13,4 +13,7 @@ public class AppRecentLikeReqVo extends PageParam {
|
||||
@Schema(description = "操作id")
|
||||
private Long operationId;
|
||||
|
||||
@Schema(description = "审核状态:0-审核中,1-审核通过,2-审核不通过")
|
||||
private Integer auditStatus;
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package cn.iocoder.yudao.module.member.controller.app.recent.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@Schema(description = "用户 APP - 修改并重新提交朋友圈 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class AppRecentUpdateReqVO extends AppRecentSaveReqVO {
|
||||
|
||||
@Schema(description = "朋友圈编号", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "朋友圈编号不能为空")
|
||||
private Long id;
|
||||
|
||||
}
|
||||
+35
-2
@@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.member.dal.dataobject.recent;
|
||||
|
||||
import lombok.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
@@ -78,4 +78,37 @@ public class RecentDO extends BaseDO {
|
||||
*/
|
||||
private String issueAddress;
|
||||
|
||||
}
|
||||
/**
|
||||
* 是否展示
|
||||
*
|
||||
* true: 显示
|
||||
* false: 隐藏
|
||||
*/
|
||||
private Boolean visible;
|
||||
|
||||
/**
|
||||
* 审核状态:0-审核中,1-审核通过,2-审核不通过
|
||||
*/
|
||||
private Integer auditStatus;
|
||||
|
||||
/**
|
||||
* 审核来源:THIRD_PARTY-第三方,MANUAL-人工,HISTORY-历史数据
|
||||
*/
|
||||
private String auditSource;
|
||||
|
||||
/**
|
||||
* 审核原因
|
||||
*/
|
||||
private String auditReason;
|
||||
|
||||
/**
|
||||
* 审核时间
|
||||
*/
|
||||
private LocalDateTime auditTime;
|
||||
|
||||
/**
|
||||
* 人工审核用户编号
|
||||
*/
|
||||
private Long auditUserId;
|
||||
|
||||
}
|
||||
+9
@@ -11,6 +11,7 @@ import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -44,6 +45,14 @@ public interface MemberUserMapper extends BaseMapperX<MemberUserDO> {
|
||||
.or().eq(MemberUserDO::getUserCode, "")));
|
||||
}
|
||||
|
||||
default int updateAppRefreshDateIfEarlier(Long id, LocalDateTime appRefreshDate) {
|
||||
return update(null, new LambdaUpdateWrapper<MemberUserDO>()
|
||||
.set(MemberUserDO::getAppRefreshDate, appRefreshDate)
|
||||
.eq(MemberUserDO::getId, id)
|
||||
.and(wrapper -> wrapper.isNull(MemberUserDO::getAppRefreshDate)
|
||||
.or().lt(MemberUserDO::getAppRefreshDate, appRefreshDate)));
|
||||
}
|
||||
|
||||
default List<MemberUserDO> selectListByNicknameLike(String nickname) {
|
||||
return selectList(new LambdaQueryWrapperX<MemberUserDO>()
|
||||
.likeIfPresent(MemberUserDO::getNickname, nickname));
|
||||
|
||||
+2
@@ -9,4 +9,6 @@ public interface RedisKeyConstants {
|
||||
|
||||
String MEMBER_COM_AUTH_CALLBACK = "member:com:auth:callback:%s";
|
||||
|
||||
String MEMBER_AUTH_MINI_PROGRAM_LOGIN_CODE = "member:auth:mini-program-login-code:%s";
|
||||
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package cn.iocoder.yudao.module.member.dal.redis.auth;
|
||||
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.iocoder.yudao.module.member.dal.redis.RedisKeyConstants;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.Duration;
|
||||
|
||||
/**
|
||||
* H5 跳转微信小程序时使用的一次性登录码 Redis DAO。
|
||||
*/
|
||||
@Repository
|
||||
public class MemberAuthLoginCodeRedisDAO {
|
||||
|
||||
private static final Duration LOGIN_CODE_TTL = Duration.ofMinutes(5);
|
||||
|
||||
@Resource
|
||||
private StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
public String create(Long userId) {
|
||||
String code = IdUtil.fastSimpleUUID();
|
||||
stringRedisTemplate.opsForValue().set(formatKey(code), userId.toString(), LOGIN_CODE_TTL);
|
||||
return code;
|
||||
}
|
||||
|
||||
public Long consume(String code) {
|
||||
String key = formatKey(code);
|
||||
String userId = stringRedisTemplate.opsForValue().get(key);
|
||||
if (userId == null || !Boolean.TRUE.equals(stringRedisTemplate.delete(key))) {
|
||||
return null;
|
||||
}
|
||||
return Long.valueOf(userId);
|
||||
}
|
||||
|
||||
private static String formatKey(String code) {
|
||||
return String.format(RedisKeyConstants.MEMBER_AUTH_MINI_PROGRAM_LOGIN_CODE, code);
|
||||
}
|
||||
|
||||
}
|
||||
+24
@@ -85,4 +85,28 @@ public interface MemberAuthService {
|
||||
* @return 登录结果
|
||||
*/
|
||||
AppAuthLoginRespVO refreshToken(String refreshToken);
|
||||
|
||||
/**
|
||||
* 创建 H5 跳转微信小程序时使用的一次性登录码。
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @return 一次性登录码
|
||||
*/
|
||||
String createMiniProgramLoginCode(Long userId);
|
||||
|
||||
/**
|
||||
* 使用一次性登录码登录微信小程序。
|
||||
*
|
||||
* @param code 一次性登录码
|
||||
* @return 登录结果
|
||||
*/
|
||||
AppAuthLoginRespVO loginByMiniProgramCode(String code);
|
||||
|
||||
/**
|
||||
* 使用当前主站登录态重新签发聊天令牌。
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @return 聊天令牌
|
||||
*/
|
||||
AppAuthLoginRespVO.ChatToken reissueChatToken(Long userId);
|
||||
}
|
||||
+44
@@ -15,6 +15,7 @@ import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserUpdateR
|
||||
import cn.iocoder.yudao.module.member.controller.app.auth.vo.*;
|
||||
import cn.iocoder.yudao.module.member.convert.auth.AuthConvert;
|
||||
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
|
||||
import cn.iocoder.yudao.module.member.dal.redis.auth.MemberAuthLoginCodeRedisDAO;
|
||||
import cn.iocoder.yudao.module.member.service.user.MemberRecentCfgService;
|
||||
import cn.iocoder.yudao.module.member.service.user.MemberRecentTagService;
|
||||
import cn.iocoder.yudao.module.member.service.user.MemberUserService;
|
||||
@@ -75,6 +76,8 @@ public class MemberAuthServiceImpl implements MemberAuthService {
|
||||
private OAuth2TokenApi oauth2TokenApi;
|
||||
@Resource
|
||||
private ChatApi chatApi;
|
||||
@Resource
|
||||
private MemberAuthLoginCodeRedisDAO authLoginCodeRedisDAO;
|
||||
|
||||
@Override
|
||||
public AppAuthLoginRespVO login(AppAuthLoginReqVO reqVO) {
|
||||
@@ -344,6 +347,47 @@ public class MemberAuthServiceImpl implements MemberAuthService {
|
||||
return AuthConvert.INSTANCE.convert(accessTokenDO, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createMiniProgramLoginCode(Long userId) {
|
||||
return authLoginCodeRedisDAO.create(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppAuthLoginRespVO loginByMiniProgramCode(String code) {
|
||||
Long userId = authLoginCodeRedisDAO.consume(code);
|
||||
if (userId == null) {
|
||||
throw exception(AUTH_MINI_PROGRAM_LOGIN_CODE_INVALID);
|
||||
}
|
||||
MemberUserDO user = userService.getUser(userId);
|
||||
if (user == null) {
|
||||
throw exception(USER_NOT_EXISTS);
|
||||
}
|
||||
if (CommonStatusEnum.isDisable(user.getStatus())) {
|
||||
createLoginLog(user.getId(), user.getMobile(), LoginLogTypeEnum.LOGIN_SOCIAL, LoginResultEnum.USER_DISABLED);
|
||||
throw exception(AUTH_LOGIN_USER_DISABLED);
|
||||
}
|
||||
return createTokenAfterLoginSuccess(user, user.getMobile(), LoginLogTypeEnum.LOGIN_SOCIAL, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppAuthLoginRespVO.ChatToken reissueChatToken(Long userId) {
|
||||
if (userService.getUser(userId) == null) {
|
||||
throw exception(USER_NOT_EXISTS);
|
||||
}
|
||||
if (!userService.syncChatUser(userId)) {
|
||||
throw exception(CHAT_TOKEN_CREATE_FAILED);
|
||||
}
|
||||
|
||||
ChatLoginReqVO loginReqVO = new ChatLoginReqVO();
|
||||
loginReqVO.setTerminal(1);
|
||||
loginReqVO.setUserId(userId);
|
||||
ChatLoginRespVO loginRespVO = chatApi.login(loginReqVO);
|
||||
if (ObjectUtil.isEmpty(loginRespVO)) {
|
||||
throw exception(CHAT_TOKEN_CREATE_FAILED);
|
||||
}
|
||||
return BeanUtils.toBean(loginRespVO, AppAuthLoginRespVO.ChatToken.class);
|
||||
}
|
||||
|
||||
private void createLogoutLog(Long userId) {
|
||||
LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO();
|
||||
reqDTO.setLogType(LoginLogTypeEnum.LOGOUT_SELF.getType());
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package cn.iocoder.yudao.module.member.service.recent;
|
||||
|
||||
/**
|
||||
* 动态第三方审核 Service
|
||||
*/
|
||||
public interface RecentAuditService {
|
||||
|
||||
/**
|
||||
* 异步审核动态内容。
|
||||
*
|
||||
* @param recentId 动态编号
|
||||
*/
|
||||
void auditRecentAsync(Long recentId);
|
||||
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
package cn.iocoder.yudao.module.member.service.recent;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.iocoder.yudao.module.member.api.thirdauth.ThirdAuthVo;
|
||||
import cn.iocoder.yudao.module.member.client.service.ThirdAuthService;
|
||||
import cn.iocoder.yudao.module.member.dal.dataobject.recent.RecentDO;
|
||||
import cn.iocoder.yudao.module.member.dal.mysql.recent.RecentMapper;
|
||||
import cn.iocoder.yudao.module.member.enums.MemberRecentAuditSourceEnum;
|
||||
import cn.iocoder.yudao.module.member.enums.MemberRecentAuditStatusEnum;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 动态第三方审核 Service 实现
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class RecentAuditServiceImpl implements RecentAuditService {
|
||||
|
||||
private static final int AUDIT_REASON_MAX_LENGTH = 512;
|
||||
|
||||
@Resource
|
||||
private RecentMapper recentMapper;
|
||||
@Resource
|
||||
private ThirdAuthService thirdAuthService;
|
||||
|
||||
@Override
|
||||
@Async
|
||||
public void auditRecentAsync(Long recentId) {
|
||||
try {
|
||||
RecentDO recent = recentMapper.selectById(recentId);
|
||||
if (recent == null || !ObjectUtil.equals(recent.getAuditStatus(),
|
||||
MemberRecentAuditStatusEnum.PENDING.getStatus())) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<CompletableFuture<String>> auditTasks = new ArrayList<>();
|
||||
if (StringUtils.isNotBlank(recent.getDescription())) {
|
||||
auditTasks.add(auditContent("文本", recent.getDescription(), thirdAuthService::textAuditSync));
|
||||
}
|
||||
addMediaAuditTasks(auditTasks, "图片", recent.getCoverPic(), thirdAuthService::imageAuditSync);
|
||||
addMediaAuditTasks(auditTasks, "视频", recent.getCoverVideo(), thirdAuthService::videoAuditSync);
|
||||
|
||||
List<String> failureReasons = auditTasks.stream()
|
||||
.map(CompletableFuture::join)
|
||||
.filter(StringUtils::isNotBlank)
|
||||
.collect(Collectors.toList());
|
||||
updateThirdPartyAuditResult(recentId, failureReasons);
|
||||
} catch (Exception ex) {
|
||||
// 保持审核中,后台仍可人工处理,避免第三方异常导致内容被自动放行。
|
||||
log.error("|动态异步审核|审核任务异常 recentId:{}", recentId, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void addMediaAuditTasks(List<CompletableFuture<String>> tasks, String contentType, String content,
|
||||
Function<String, ThirdAuthVo> auditFunction) {
|
||||
if (StringUtils.isBlank(content)) {
|
||||
return;
|
||||
}
|
||||
for (String item : StringUtils.split(content, ',')) {
|
||||
String url = StringUtils.trim(item);
|
||||
if (StringUtils.isNotBlank(url)) {
|
||||
tasks.add(auditContent(contentType, url, auditFunction));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private CompletableFuture<String> auditContent(String contentType, String content,
|
||||
Function<String, ThirdAuthVo> auditFunction) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
ThirdAuthVo result = auditFunction.apply(content);
|
||||
if (result == null) {
|
||||
return contentType + "审核服务无返回结果";
|
||||
}
|
||||
if (!result.isError()) {
|
||||
return null;
|
||||
}
|
||||
String reason = StringUtils.defaultIfBlank(result.getFirstErrorMsg(), result.getErrorMsg());
|
||||
return contentType + ":" + StringUtils.defaultIfBlank(reason, "审核不通过");
|
||||
});
|
||||
}
|
||||
|
||||
private void updateThirdPartyAuditResult(Long recentId, List<String> failureReasons) {
|
||||
boolean approved = failureReasons.isEmpty();
|
||||
String auditReason = approved ? null : StringUtils.left(String.join(";", failureReasons),
|
||||
AUDIT_REASON_MAX_LENGTH);
|
||||
LambdaUpdateWrapper<RecentDO> updateWrapper = new LambdaUpdateWrapper<RecentDO>()
|
||||
.eq(RecentDO::getId, recentId)
|
||||
.eq(RecentDO::getAuditStatus, MemberRecentAuditStatusEnum.PENDING.getStatus())
|
||||
.set(RecentDO::getAuditStatus, approved ? MemberRecentAuditStatusEnum.APPROVED.getStatus()
|
||||
: MemberRecentAuditStatusEnum.REJECTED.getStatus())
|
||||
.set(RecentDO::getAuditSource, MemberRecentAuditSourceEnum.THIRD_PARTY.getSource())
|
||||
.set(RecentDO::getAuditReason, auditReason)
|
||||
.set(RecentDO::getAuditTime, LocalDateTime.now())
|
||||
.set(RecentDO::getAuditUserId, null);
|
||||
int updateCount = recentMapper.update(null, updateWrapper);
|
||||
if (updateCount == 0) {
|
||||
log.info("|动态异步审核|动态已被人工处理,忽略第三方结果 recentId:{}", recentId);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+16
-1
@@ -39,6 +39,16 @@ public interface RecentService {
|
||||
*/
|
||||
void deleteRecent(Long id);
|
||||
|
||||
/**
|
||||
* 人工审核动态
|
||||
*/
|
||||
void auditRecent(@Valid RecentAuditReqVO auditReqVO, Long auditUserId);
|
||||
|
||||
/**
|
||||
* 更新动态展示状态
|
||||
*/
|
||||
void updateRecentVisible(@Valid RecentVisibleReqVO visibleReqVO);
|
||||
|
||||
/**
|
||||
* 朋友圈操作
|
||||
*/
|
||||
@@ -99,6 +109,11 @@ public interface RecentService {
|
||||
*/
|
||||
PageResult<RecentPageResVo> dynamicPage(@Valid AppRecentLikeReqVo dto);
|
||||
|
||||
/**
|
||||
* 修改并重新提交朋友圈审核
|
||||
*/
|
||||
void updateAndResubmitRecent(@Valid RecentSaveReqVO updateReqVO, Long memberId);
|
||||
|
||||
/**
|
||||
* 我的评价
|
||||
*/
|
||||
@@ -123,4 +138,4 @@ public interface RecentService {
|
||||
* 朋友圈我喜欢的、喜欢我的等总数统计
|
||||
*/
|
||||
OperationTotalGroupRecentRespVO total(Long userId);
|
||||
}
|
||||
}
|
||||
+147
-90
@@ -7,11 +7,8 @@ import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.module.member.api.thirdauth.ThirdAuthVo;
|
||||
import cn.iocoder.yudao.module.member.client.service.ChatApi;
|
||||
import cn.iocoder.yudao.module.member.client.service.TxMapApi;
|
||||
import cn.iocoder.yudao.module.member.client.vo.chat.user.ChatUserOnLineStatusReqVo;
|
||||
import cn.iocoder.yudao.module.member.client.vo.chat.user.ChatUserOnLineStatusRespVo;
|
||||
import cn.iocoder.yudao.module.member.client.vo.txmap.geocoder.TxMapGeocoderReqVO;
|
||||
import cn.iocoder.yudao.module.member.client.vo.txmap.geocoder.TxMapGeocoderRespVO;
|
||||
import cn.iocoder.yudao.module.member.controller.admin.recent.vo.*;
|
||||
@@ -24,13 +21,14 @@ import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberRecentTagDO;
|
||||
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
|
||||
import cn.iocoder.yudao.module.member.dal.mysql.recent.OperationMapper;
|
||||
import cn.iocoder.yudao.module.member.enums.MemberErrorCode;
|
||||
import cn.iocoder.yudao.module.member.enums.MemberRecentAuditSourceEnum;
|
||||
import cn.iocoder.yudao.module.member.enums.MemberRecentAuditStatusEnum;
|
||||
import cn.iocoder.yudao.module.member.enums.MemberRecentOperateEnum;
|
||||
import cn.iocoder.yudao.module.member.enums.MemberRecentSceneEnum;
|
||||
import cn.iocoder.yudao.module.member.enums.tag.MemberRecentTagEnum;
|
||||
import cn.iocoder.yudao.module.member.service.config.MemberConfigService;
|
||||
import cn.iocoder.yudao.module.member.service.location.LocationBO;
|
||||
import cn.iocoder.yudao.module.member.service.location.LocationService;
|
||||
import cn.iocoder.yudao.module.member.client.service.ThirdAuthService;
|
||||
import cn.iocoder.yudao.module.member.service.opentype.OpenTypeService;
|
||||
import cn.iocoder.yudao.module.member.service.photo.BO.MemberPhotoTotalExtBO;
|
||||
import cn.iocoder.yudao.module.member.service.photo.PhotoService;
|
||||
@@ -53,6 +51,8 @@ import org.springframework.stereotype.Service;
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.support.TransactionSynchronization;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import cn.iocoder.yudao.module.member.dal.dataobject.recent.RecentDO;
|
||||
@@ -65,8 +65,6 @@ import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
@@ -91,8 +89,6 @@ public class RecentServiceImpl implements RecentService {
|
||||
@Resource
|
||||
private LocationService locationService;
|
||||
@Resource
|
||||
private ThirdAuthService thirdAuthService;
|
||||
@Resource
|
||||
private PhotoService photoService;
|
||||
@Resource
|
||||
private OperationService operationService;
|
||||
@@ -102,6 +98,8 @@ public class RecentServiceImpl implements RecentService {
|
||||
private MemberRecentCfgService memberRecentCfgService;
|
||||
@Resource
|
||||
private OpenTypeService openTypeService;
|
||||
@Resource
|
||||
private RecentAuditService recentAuditService;
|
||||
|
||||
@Resource
|
||||
private TxMapApi txMapApi;
|
||||
@@ -119,75 +117,53 @@ public class RecentServiceImpl implements RecentService {
|
||||
if (ObjectUtil.isEmpty(user)) throw exception(MemberErrorCode.USER_NOT_EXISTS);
|
||||
// 判断是否为会员
|
||||
|
||||
// 判断是否有位置信息
|
||||
if (!ObjectUtil.isAllNotEmpty(vo.getIssueProvince(), vo.getIssueCity(), vo.getIssueDistrict(), vo.getIssueAddress())) {
|
||||
if (ObjectUtil.isAllNotEmpty(vo.getIssueLat(), vo.getIssueLong())) {
|
||||
TxMapGeocoderRespVO geocoder = txMapApi.geocoder(TxMapGeocoderReqVO.builder().location(vo.getIssueLat() + "," + vo.getIssueLong()).build());
|
||||
if (ObjectUtil.isNotEmpty(geocoder) && ObjectUtil.isNotEmpty(geocoder.getAddress_component())) {
|
||||
vo.setIssueProvince(geocoder.getAddress_component().getProvince());
|
||||
vo.setIssueCity(geocoder.getAddress_component().getCity());
|
||||
vo.setIssueDistrict(geocoder.getAddress_component().getDistrict());
|
||||
vo.setIssueAddress(geocoder.getAddress_component().getStreet());
|
||||
if (ObjectUtil.isNotEmpty(geocoder.getFormatted_addresses()) && ObjectUtil.isNotEmpty(geocoder.getFormatted_addresses().getRecommend())) {
|
||||
vo.setIssueAddress(geocoder.getFormatted_addresses().getRecommend());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fillIssueAddress(vo);
|
||||
|
||||
// 插入
|
||||
RecentDO recent = BeanUtils.toBean(vo, RecentDO.class);
|
||||
recent.setVisible(true);
|
||||
recent.setAuditStatus(MemberRecentAuditStatusEnum.PENDING.getStatus());
|
||||
recentMapper.insert(recent);
|
||||
|
||||
// 文字和图片违规处理
|
||||
CompletableFuture<ThirdAuthVo> textFuture = CompletableFuture.supplyAsync(
|
||||
() -> auditCommaSeparatedContent(vo.getDescription(), thirdAuthService::textAuditSync)
|
||||
);
|
||||
CompletableFuture<ThirdAuthVo> imageFuture = CompletableFuture.supplyAsync(
|
||||
() -> auditCommaSeparatedContent(vo.getCoverPic(), thirdAuthService::imageAuditSync)
|
||||
);
|
||||
|
||||
try {
|
||||
ThirdAuthVo textRes = textFuture.get();
|
||||
ThirdAuthVo imageRes = imageFuture.get();
|
||||
// 处理文字
|
||||
if (ObjectUtil.isEmpty(textRes)) {
|
||||
throw exception(MemberErrorCode.CHECK_TEXT_NOT_PASS, "未知错误");
|
||||
}
|
||||
if (textRes.isError() && ObjectUtil.isNotEmpty(textRes.getFirstErrorMsg())) {
|
||||
throw exception(MemberErrorCode.CHECK_TEXT_NOT_PASS, textRes.getFirstErrorMsg());
|
||||
}
|
||||
// 处理图片
|
||||
if (ObjectUtil.isEmpty(imageRes)) {
|
||||
throw exception(MemberErrorCode.CHECK_IMAGE_NOT_PASS, "未知错误");
|
||||
}
|
||||
if (imageRes.isError() && ObjectUtil.isNotEmpty(imageRes.getFirstErrorMsg())) {
|
||||
throw exception(MemberErrorCode.CHECK_IMAGE_NOT_PASS, imageRes.getFirstErrorMsg());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("|创建朋友圈|,意外的错误error:{}", e.getMessage());
|
||||
throw exception(MemberErrorCode.EMPTY, e.getMessage());
|
||||
}
|
||||
triggerAuditAfterCommit(recent.getId());
|
||||
|
||||
// 返回
|
||||
return recent.getId();
|
||||
}
|
||||
|
||||
private ThirdAuthVo auditCommaSeparatedContent(String content, Function<String, ThirdAuthVo> auditFunction) {
|
||||
if (StringUtils.isBlank(content)) {
|
||||
return new ThirdAuthVo();
|
||||
private void fillIssueAddress(RecentSaveReqVO vo) {
|
||||
if (ObjectUtil.isAllNotEmpty(vo.getIssueProvince(), vo.getIssueCity(), vo.getIssueDistrict(), vo.getIssueAddress())
|
||||
|| !ObjectUtil.isAllNotEmpty(vo.getIssueLat(), vo.getIssueLong())) {
|
||||
return;
|
||||
}
|
||||
for (String item : StringUtils.split(content, ',')) {
|
||||
String auditContent = StringUtils.trim(item);
|
||||
if (StringUtils.isBlank(auditContent)) {
|
||||
continue;
|
||||
}
|
||||
ThirdAuthVo result = auditFunction.apply(auditContent);
|
||||
if (ObjectUtil.isEmpty(result) || result.isError()) {
|
||||
return result;
|
||||
}
|
||||
TxMapGeocoderRespVO geocoder = txMapApi.geocoder(TxMapGeocoderReqVO.builder()
|
||||
.location(vo.getIssueLat() + "," + vo.getIssueLong()).build());
|
||||
if (ObjectUtil.isEmpty(geocoder) || ObjectUtil.isEmpty(geocoder.getAddress_component())) {
|
||||
return;
|
||||
}
|
||||
return new ThirdAuthVo();
|
||||
vo.setIssueProvince(geocoder.getAddress_component().getProvince());
|
||||
vo.setIssueCity(geocoder.getAddress_component().getCity());
|
||||
vo.setIssueDistrict(geocoder.getAddress_component().getDistrict());
|
||||
vo.setIssueAddress(geocoder.getAddress_component().getStreet());
|
||||
if (ObjectUtil.isNotEmpty(geocoder.getFormatted_addresses())
|
||||
&& ObjectUtil.isNotEmpty(geocoder.getFormatted_addresses().getRecommend())) {
|
||||
vo.setIssueAddress(geocoder.getFormatted_addresses().getRecommend());
|
||||
}
|
||||
}
|
||||
|
||||
private void triggerAuditAfterCommit(Long recentId) {
|
||||
if (!TransactionSynchronizationManager.isSynchronizationActive()) {
|
||||
recentAuditService.auditRecentAsync(recentId);
|
||||
return;
|
||||
}
|
||||
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|
||||
|
||||
@Override
|
||||
public void afterCommit() {
|
||||
recentAuditService.auditRecentAsync(recentId);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -208,9 +184,13 @@ public class RecentServiceImpl implements RecentService {
|
||||
*/
|
||||
@Override
|
||||
public RecentPageResVo getByRecentId(Long recentId, Long loginUserId) {
|
||||
validateRecentExists(recentId);
|
||||
// 查询朋友圈
|
||||
RecentDO recent = recentMapper.selectById(recentId);
|
||||
if (recent == null || (!ObjectUtil.equals(recent.getMemberId(), loginUserId)
|
||||
&& (!ObjectUtil.equals(recent.getAuditStatus(), MemberRecentAuditStatusEnum.APPROVED.getStatus())
|
||||
|| !Boolean.TRUE.equals(recent.getVisible())))) {
|
||||
throw exception(MemberErrorCode.RECENT_NOT_EXISTS);
|
||||
}
|
||||
RecentPageResVo result = BeanUtil.toBean(recent, RecentPageResVo.class);
|
||||
|
||||
LocationBO currentUserLastLocation = locationService.getCurrentUserLastLocation();
|
||||
@@ -248,6 +228,35 @@ public class RecentServiceImpl implements RecentService {
|
||||
recentMapper.deleteById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void auditRecent(RecentAuditReqVO auditReqVO, Long auditUserId) {
|
||||
validateRecentExists(auditReqVO.getId());
|
||||
if (!MemberRecentAuditStatusEnum.isManualAuditStatus(auditReqVO.getAuditStatus())) {
|
||||
throw exception(MemberErrorCode.EMPTY, "人工审核状态只能是通过或不通过");
|
||||
}
|
||||
if (ObjectUtil.equals(auditReqVO.getAuditStatus(), MemberRecentAuditStatusEnum.REJECTED.getStatus())
|
||||
&& StringUtils.isBlank(auditReqVO.getAuditReason())) {
|
||||
throw exception(MemberErrorCode.EMPTY, "审核不通过时必须填写原因");
|
||||
}
|
||||
|
||||
LambdaUpdateWrapper<RecentDO> updateWrapper = new LambdaUpdateWrapper<RecentDO>()
|
||||
.eq(RecentDO::getId, auditReqVO.getId())
|
||||
.set(RecentDO::getAuditStatus, auditReqVO.getAuditStatus())
|
||||
.set(RecentDO::getAuditSource, MemberRecentAuditSourceEnum.MANUAL.getSource())
|
||||
.set(RecentDO::getAuditReason, StringUtils.trimToNull(auditReqVO.getAuditReason()))
|
||||
.set(RecentDO::getAuditTime, LocalDateTime.now())
|
||||
.set(RecentDO::getAuditUserId, auditUserId);
|
||||
recentMapper.update(null, updateWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateRecentVisible(RecentVisibleReqVO visibleReqVO) {
|
||||
validateRecentExists(visibleReqVO.getId());
|
||||
recentMapper.updateById(new RecentDO()
|
||||
.setId(visibleReqVO.getId())
|
||||
.setVisible(visibleReqVO.getVisible()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 朋友圈操作
|
||||
*/
|
||||
@@ -445,6 +454,8 @@ public class RecentServiceImpl implements RecentService {
|
||||
MemberRecentCfgDO operationCfg = memberRecentCfgService.getOrCreate(operationUser.getId());
|
||||
RecentPageReqVO resultQuery = BeanUtil.toBean(bean, RecentPageReqVO.class);
|
||||
resultQuery.setSceneBizType(bean.getScene().getType());
|
||||
resultQuery.setAuditStatus(MemberRecentAuditStatusEnum.APPROVED.getStatus());
|
||||
resultQuery.setVisible(true);
|
||||
quickBeforeQuery(operationUser, operationCfg, resultQuery);
|
||||
|
||||
IPage<RecentPageResVo> recentPageList = recentMapper.pageJoin(new Page(bean.getPageNo(), bean.getPageSize()), resultQuery);
|
||||
@@ -492,8 +503,6 @@ public class RecentServiceImpl implements RecentService {
|
||||
List<RecentPageResVo> resultList = BeanUtils.toBean(recentPageList.getRecords(), RecentPageResVo.class);
|
||||
// 为RecentPageResVo设置Member信息
|
||||
convertRecentPageResVoSetupMember(resultList, isVip);
|
||||
// 远程查询用户是否在线
|
||||
convertRecentPageResVoSetupIsOnline(resultList);
|
||||
// 如果是`新人``女生`模糊`头像`和`用户名`
|
||||
List<MemberRecentSceneEnum> dimList = Arrays.asList(MemberRecentSceneEnum.SQUARE_NEW_PERSON, MemberRecentSceneEnum.SQUARE_FEMALE);
|
||||
// 查询用户tag
|
||||
@@ -610,6 +619,7 @@ public class RecentServiceImpl implements RecentService {
|
||||
|
||||
RecentPageReqVO dtoQuery = new RecentPageReqVO();
|
||||
dtoQuery.setMemberId(dto.getOperationMemberId());
|
||||
dtoQuery.setAuditStatus(dto.getAuditStatus());
|
||||
IPage<RecentPageResVo> pageList = recentMapper.pageJoin(new Page(dto.getPageNo(), dto.getPageSize()), dtoQuery);
|
||||
if (ObjectUtil.isEmpty(pageList)) return new PageResult<>();
|
||||
resultList = BeanUtil.copyToList(pageList.getRecords(), RecentPageResVo.class);
|
||||
@@ -618,6 +628,73 @@ public class RecentServiceImpl implements RecentService {
|
||||
return new PageResult<RecentPageResVo>(resultList, pageList.getTotal());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void updateAndResubmitRecent(RecentSaveReqVO updateReqVO, Long memberId) {
|
||||
RecentDO recent = recentMapper.selectById(updateReqVO.getId());
|
||||
if (recent == null || !ObjectUtil.equals(recent.getMemberId(), memberId)) {
|
||||
throw exception(MemberErrorCode.RECENT_NOT_EXISTS);
|
||||
}
|
||||
if (!ObjectUtil.equals(recent.getAuditStatus(), MemberRecentAuditStatusEnum.REJECTED.getStatus())) {
|
||||
throw exception(MemberErrorCode.EMPTY, "仅审核不通过的动态可以修改后重新提交");
|
||||
}
|
||||
|
||||
String description = StringUtils.trimToEmpty(updateReqVO.getDescription());
|
||||
String coverPic = normalizeMediaUrls(updateReqVO.getCoverPic());
|
||||
String coverVideo = normalizeMediaUrls(updateReqVO.getCoverVideo());
|
||||
if (StringUtils.isBlank(description)) {
|
||||
throw exception(MemberErrorCode.EMPTY, "描述不能为空");
|
||||
}
|
||||
if (coverPic == null && coverVideo == null) {
|
||||
throw exception(MemberErrorCode.EMPTY, "图片、视频必须上传其中之一");
|
||||
}
|
||||
boolean contentChanged = !Objects.equals(StringUtils.trimToEmpty(recent.getDescription()), description)
|
||||
|| !Objects.equals(normalizeMediaUrls(recent.getCoverPic()), coverPic)
|
||||
|| !Objects.equals(normalizeMediaUrls(recent.getCoverVideo()), coverVideo);
|
||||
if (!contentChanged) {
|
||||
throw exception(MemberErrorCode.EMPTY, "请先修改动态内容再重新发布");
|
||||
}
|
||||
|
||||
updateReqVO.setDescription(description);
|
||||
updateReqVO.setCoverPic(coverPic);
|
||||
updateReqVO.setCoverVideo(coverVideo);
|
||||
fillIssueAddress(updateReqVO);
|
||||
|
||||
int updated = recentMapper.update(null, new LambdaUpdateWrapper<RecentDO>()
|
||||
.eq(RecentDO::getId, updateReqVO.getId())
|
||||
.eq(RecentDO::getMemberId, memberId)
|
||||
.eq(RecentDO::getAuditStatus, MemberRecentAuditStatusEnum.REJECTED.getStatus())
|
||||
.set(RecentDO::getDescription, updateReqVO.getDescription())
|
||||
.set(RecentDO::getCoverPic, updateReqVO.getCoverPic())
|
||||
.set(RecentDO::getCoverVideo, updateReqVO.getCoverVideo())
|
||||
.set(RecentDO::getIssueLong, updateReqVO.getIssueLong())
|
||||
.set(RecentDO::getIssueLat, updateReqVO.getIssueLat())
|
||||
.set(RecentDO::getIssueProvince, updateReqVO.getIssueProvince())
|
||||
.set(RecentDO::getIssueCity, updateReqVO.getIssueCity())
|
||||
.set(RecentDO::getIssueDistrict, updateReqVO.getIssueDistrict())
|
||||
.set(RecentDO::getIssueAddress, updateReqVO.getIssueAddress())
|
||||
.set(RecentDO::getAuditStatus, MemberRecentAuditStatusEnum.PENDING.getStatus())
|
||||
.set(RecentDO::getAuditSource, null)
|
||||
.set(RecentDO::getAuditReason, null)
|
||||
.set(RecentDO::getAuditTime, null)
|
||||
.set(RecentDO::getAuditUserId, null));
|
||||
if (updated == 0) {
|
||||
throw exception(MemberErrorCode.EMPTY, "动态状态已变化,请刷新后重试");
|
||||
}
|
||||
triggerAuditAfterCommit(updateReqVO.getId());
|
||||
}
|
||||
|
||||
private String normalizeMediaUrls(String content) {
|
||||
if (StringUtils.isBlank(content)) {
|
||||
return null;
|
||||
}
|
||||
String normalized = Arrays.stream(StringUtils.split(content, ','))
|
||||
.map(StringUtils::trim)
|
||||
.filter(StringUtils::isNotBlank)
|
||||
.collect(Collectors.joining(","));
|
||||
return StringUtils.trimToNull(normalized);
|
||||
}
|
||||
|
||||
/**
|
||||
* 我的评价
|
||||
*/
|
||||
@@ -786,28 +863,7 @@ public class RecentServiceImpl implements RecentService {
|
||||
member.setWechatNum(member.getwechatMaskString());
|
||||
}
|
||||
entity.setMember(member);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 为RecentPageResVo设置isOnline信息
|
||||
*/
|
||||
private void convertRecentPageResVoSetupIsOnline(List<RecentPageResVo> list) {
|
||||
if (ObjectUtil.isEmpty(list)) return;
|
||||
List<Long> memberIds = list.stream().map(RecentPageResVo::getMemberId).collect(Collectors.toList());
|
||||
|
||||
ChatUserOnLineStatusReqVo dto = new ChatUserOnLineStatusReqVo();
|
||||
dto.setUserIdList(memberIds);
|
||||
|
||||
List<ChatUserOnLineStatusRespVo> userOnlineList = chatApi.getUserOnlineStatusById(dto);
|
||||
if (ObjectUtil.isEmpty(userOnlineList)) return;
|
||||
Map<Long, ChatUserOnLineStatusRespVo> userOnlineMap = IterUtil.toMap(userOnlineList, ChatUserOnLineStatusRespVo::getId);
|
||||
|
||||
for (RecentPageResVo entity : list) {
|
||||
ChatUserOnLineStatusRespVo userOnLineStatus = userOnlineMap.get(entity.getMemberId());
|
||||
if(ObjectUtil.isNotEmpty(userOnLineStatus)){
|
||||
entity.setOnLine(userOnLineStatus.getOnline());
|
||||
}
|
||||
entity.setOnLine(member == null ? null : member.getOnline());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -838,6 +894,7 @@ public class RecentServiceImpl implements RecentService {
|
||||
member.setWechatNum(member.getwechatMaskString());
|
||||
}
|
||||
detail.setMember(member);
|
||||
detail.setOnLine(member == null ? null : member.getOnline());
|
||||
}
|
||||
|
||||
// 为RecentPageResVo设置userOperationStatus信息
|
||||
|
||||
+4
-1
@@ -47,9 +47,12 @@ public class MemberExtBO {
|
||||
@Schema(description = "纬度")
|
||||
private Double memberLng;
|
||||
|
||||
@Schema(description = "最近登录时间")
|
||||
@Schema(description = "最后在线时间")
|
||||
private LocalDateTime appRefreshDate;
|
||||
|
||||
@Schema(description = "是否在线")
|
||||
private Boolean online;
|
||||
|
||||
|
||||
public String getwechatMaskString() {
|
||||
String str = this.wechatNum;
|
||||
|
||||
+46
-1
@@ -12,6 +12,8 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
||||
import cn.iocoder.yudao.module.member.client.service.ChatApi;
|
||||
import cn.iocoder.yudao.module.member.client.vo.chat.user.ChatCreateOrUpdateUserReqVO;
|
||||
import cn.iocoder.yudao.module.member.client.vo.chat.user.ChatUserOnLineStatusReqVo;
|
||||
import cn.iocoder.yudao.module.member.client.vo.chat.user.ChatUserOnLineStatusRespVo;
|
||||
import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserPageReqVO;
|
||||
import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.member.controller.app.user.vo.*;
|
||||
@@ -45,10 +47,14 @@ import org.springframework.transaction.support.TransactionSynchronizationManager
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
@@ -231,7 +237,7 @@ public class MemberUserServiceImpl implements MemberUserService {
|
||||
return ListUtil.empty();
|
||||
}
|
||||
List<MemberUserDO> memberUserDOS = memberUserMapper.selectBatchIds(ids);
|
||||
return memberUserDOS.stream().map(member -> {
|
||||
List<MemberExtBO> memberList = memberUserDOS.stream().map(member -> {
|
||||
MemberExtBO entity = new MemberExtBO();
|
||||
entity.setMemberId(member.getId());
|
||||
entity.setMemberAvatar(member.getAvatar());
|
||||
@@ -248,6 +254,45 @@ public class MemberUserServiceImpl implements MemberUserService {
|
||||
entity.setWechatNum(member.getWechatNum());
|
||||
return entity;
|
||||
}).collect(Collectors.toList());
|
||||
syncChatActivity(memberList);
|
||||
return memberList;
|
||||
}
|
||||
|
||||
private void syncChatActivity(List<MemberExtBO> memberList) {
|
||||
if (CollUtil.isEmpty(memberList)) {
|
||||
return;
|
||||
}
|
||||
ChatUserOnLineStatusReqVo reqVO = new ChatUserOnLineStatusReqVo();
|
||||
reqVO.setUserIdList(memberList.stream().map(MemberExtBO::getMemberId).collect(Collectors.toList()));
|
||||
List<ChatUserOnLineStatusRespVo> statusList = chatApi.getUserOnlineStatusById(reqVO);
|
||||
if (CollUtil.isEmpty(statusList)) {
|
||||
return;
|
||||
}
|
||||
Map<Long, ChatUserOnLineStatusRespVo> statusMap = statusList.stream()
|
||||
.filter(status -> status.getId() != null)
|
||||
.collect(Collectors.toMap(ChatUserOnLineStatusRespVo::getId, Function.identity(), (first, second) -> second));
|
||||
for (MemberExtBO member : memberList) {
|
||||
ChatUserOnLineStatusRespVo status = statusMap.get(member.getMemberId());
|
||||
if (status == null) {
|
||||
continue;
|
||||
}
|
||||
member.setOnline(status.getOnline());
|
||||
if (status.getLastActiveTime() == null || status.getLastActiveTime() <= 0) {
|
||||
continue;
|
||||
}
|
||||
LocalDateTime lastActiveDate = LocalDateTime.ofInstant(
|
||||
Instant.ofEpochMilli(status.getLastActiveTime()), ZoneId.systemDefault());
|
||||
if (member.getAppRefreshDate() != null && !lastActiveDate.isAfter(member.getAppRefreshDate())) {
|
||||
continue;
|
||||
}
|
||||
member.setAppRefreshDate(lastActiveDate);
|
||||
try {
|
||||
memberUserMapper.updateAppRefreshDateIfEarlier(member.getMemberId(), lastActiveDate);
|
||||
} catch (Exception e) {
|
||||
log.warn("回写用户最后在线时间失败,userId:{},lastActiveDate:{}",
|
||||
member.getMemberId(), lastActiveDate, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+16
-1
@@ -13,6 +13,12 @@
|
||||
a.issue_city,
|
||||
a.issue_district,
|
||||
a.issue_address,
|
||||
a.visible,
|
||||
a.audit_status,
|
||||
a.audit_source,
|
||||
a.audit_reason,
|
||||
a.audit_time,
|
||||
a.audit_user_id,
|
||||
a.creator,
|
||||
a.create_time,
|
||||
a.updater,
|
||||
@@ -41,8 +47,14 @@
|
||||
<if test="dto.issueAddress != null">
|
||||
and a.issue_address like concat('%', #{dto.issueAddress}, '%')
|
||||
</if>
|
||||
<if test="dto.auditStatus != null">
|
||||
and a.audit_status = #{dto.auditStatus}
|
||||
</if>
|
||||
<if test="dto.visible != null">
|
||||
and a.visible = #{dto.visible}
|
||||
</if>
|
||||
<if test="dto.createTime != null and dto.createTime.length == 2">
|
||||
and a.createTime between #{dto.createTime[0]} and #{dto.createTime[1]}
|
||||
and a.create_time between #{dto.createTime[0]} and #{dto.createTime[1]}
|
||||
</if>
|
||||
<if test="dto.excludeUserIdList != null and dto.excludeUserIdList.size() != 0">
|
||||
and a.member_id not in
|
||||
@@ -71,6 +83,9 @@
|
||||
<if test="dto.memberSex != null">
|
||||
and u.sex = #{dto.memberSex}
|
||||
</if>
|
||||
<if test="dto.memberNickname != null and dto.memberNickname != ''.toString()">
|
||||
and u.nickname like concat('%', #{dto.memberNickname}, '%')
|
||||
</if>
|
||||
<if test="dto.memberRecentTagList != null and dto.memberRecentTagList.size() != 0">
|
||||
and exists (select 1
|
||||
from member_recent_tag t
|
||||
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
package cn.iocoder.yudao.module.member.service.auth;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
||||
import cn.iocoder.yudao.module.member.client.service.ChatApi;
|
||||
import cn.iocoder.yudao.module.member.client.vo.chat.user.ChatLoginReqVO;
|
||||
import cn.iocoder.yudao.module.member.client.vo.chat.user.ChatLoginRespVO;
|
||||
import cn.iocoder.yudao.module.member.controller.app.auth.vo.AppAuthLoginRespVO;
|
||||
import cn.iocoder.yudao.module.member.service.user.MemberUserService;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import static cn.iocoder.yudao.module.member.enums.MemberErrorCode.CHAT_TOKEN_CREATE_FAILED;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class MemberAuthServiceImplChatTokenTest {
|
||||
|
||||
@InjectMocks
|
||||
private MemberAuthServiceImpl authService;
|
||||
|
||||
@Mock
|
||||
private MemberUserService userService;
|
||||
@Mock
|
||||
private ChatApi chatApi;
|
||||
|
||||
@Test
|
||||
void reissueChatToken_success() {
|
||||
Long userId = 100L;
|
||||
when(userService.getUser(userId)).thenReturn(new cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO());
|
||||
when(userService.syncChatUser(userId)).thenReturn(true);
|
||||
|
||||
ChatLoginRespVO chatLoginRespVO = new ChatLoginRespVO();
|
||||
chatLoginRespVO.setAccessToken("new-access-token");
|
||||
chatLoginRespVO.setAccessTokenExpiresIn(3600);
|
||||
chatLoginRespVO.setRefreshToken("new-refresh-token");
|
||||
chatLoginRespVO.setRefreshTokenExpiresIn(7200);
|
||||
when(chatApi.login(org.mockito.ArgumentMatchers.any(ChatLoginReqVO.class))).thenReturn(chatLoginRespVO);
|
||||
|
||||
AppAuthLoginRespVO.ChatToken result = authService.reissueChatToken(userId);
|
||||
|
||||
assertEquals("new-access-token", result.getAccessToken());
|
||||
assertEquals("new-refresh-token", result.getRefreshToken());
|
||||
ArgumentCaptor<ChatLoginReqVO> requestCaptor = ArgumentCaptor.forClass(ChatLoginReqVO.class);
|
||||
verify(chatApi).login(requestCaptor.capture());
|
||||
assertEquals(userId, requestCaptor.getValue().getUserId());
|
||||
assertEquals(1, requestCaptor.getValue().getTerminal());
|
||||
}
|
||||
|
||||
@Test
|
||||
void reissueChatToken_syncFailed() {
|
||||
Long userId = 100L;
|
||||
when(userService.getUser(userId)).thenReturn(new cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO());
|
||||
when(userService.syncChatUser(userId)).thenReturn(false);
|
||||
|
||||
ServiceException exception = assertThrows(ServiceException.class,
|
||||
() -> authService.reissueChatToken(userId));
|
||||
|
||||
assertEquals(CHAT_TOKEN_CREATE_FAILED.getCode(), exception.getCode());
|
||||
}
|
||||
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
package cn.iocoder.yudao.module.member.service.auth;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
||||
import cn.iocoder.yudao.module.member.controller.app.auth.vo.AppAuthLoginRespVO;
|
||||
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
|
||||
import cn.iocoder.yudao.module.member.dal.redis.auth.MemberAuthLoginCodeRedisDAO;
|
||||
import cn.iocoder.yudao.module.member.service.user.MemberUserService;
|
||||
import cn.iocoder.yudao.module.system.api.logger.LoginLogApi;
|
||||
import cn.iocoder.yudao.module.system.api.oauth2.OAuth2TokenApi;
|
||||
import cn.iocoder.yudao.module.system.api.oauth2.dto.OAuth2AccessTokenCreateReqDTO;
|
||||
import cn.iocoder.yudao.module.system.api.oauth2.dto.OAuth2AccessTokenRespDTO;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.module.member.enums.MemberErrorCode.AUTH_MINI_PROGRAM_LOGIN_CODE_INVALID;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class MemberAuthServiceImplMiniProgramLoginTest {
|
||||
|
||||
@InjectMocks
|
||||
private MemberAuthServiceImpl authService;
|
||||
|
||||
@Mock
|
||||
private MemberAuthLoginCodeRedisDAO authLoginCodeRedisDAO;
|
||||
@Mock
|
||||
private MemberUserService userService;
|
||||
@Mock
|
||||
private LoginLogApi loginLogApi;
|
||||
@Mock
|
||||
private OAuth2TokenApi oauth2TokenApi;
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
RequestContextHolder.resetRequestAttributes();
|
||||
}
|
||||
|
||||
@Test
|
||||
void createMiniProgramLoginCode_success() {
|
||||
when(authLoginCodeRedisDAO.create(100L)).thenReturn("login-code");
|
||||
|
||||
assertEquals("login-code", authService.createMiniProgramLoginCode(100L));
|
||||
}
|
||||
|
||||
@Test
|
||||
void loginByMiniProgramCode_invalidCode() {
|
||||
when(authLoginCodeRedisDAO.consume("invalid-code")).thenReturn(null);
|
||||
|
||||
ServiceException exception = assertThrows(ServiceException.class,
|
||||
() -> authService.loginByMiniProgramCode("invalid-code"));
|
||||
|
||||
assertEquals(AUTH_MINI_PROGRAM_LOGIN_CODE_INVALID.getCode(), exception.getCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void loginByMiniProgramCode_success() {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.setRemoteAddr("127.0.0.1");
|
||||
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));
|
||||
|
||||
MemberUserDO user = new MemberUserDO()
|
||||
.setId(100L)
|
||||
.setMobile("13800138000")
|
||||
.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
when(authLoginCodeRedisDAO.consume("login-code")).thenReturn(user.getId());
|
||||
when(userService.getUser(user.getId())).thenReturn(user);
|
||||
|
||||
OAuth2AccessTokenRespDTO token = new OAuth2AccessTokenRespDTO()
|
||||
.setUserId(user.getId())
|
||||
.setAccessToken("access-token")
|
||||
.setRefreshToken("refresh-token")
|
||||
.setExpiresTime(LocalDateTime.now().plusDays(1));
|
||||
when(oauth2TokenApi.createAccessToken(any(OAuth2AccessTokenCreateReqDTO.class))).thenReturn(token);
|
||||
|
||||
AppAuthLoginRespVO result = authService.loginByMiniProgramCode("login-code");
|
||||
|
||||
assertEquals(user.getId(), result.getUserId());
|
||||
assertEquals("access-token", result.getAccessToken());
|
||||
assertEquals("refresh-token", result.getRefreshToken());
|
||||
verify(authLoginCodeRedisDAO).consume("login-code");
|
||||
}
|
||||
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
package cn.iocoder.yudao.module.member.service.recent;
|
||||
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
import cn.iocoder.yudao.module.member.controller.admin.recent.vo.RecentSaveReqVO;
|
||||
import cn.iocoder.yudao.module.member.dal.dataobject.recent.RecentDO;
|
||||
import cn.iocoder.yudao.module.member.dal.mysql.recent.RecentMapper;
|
||||
import cn.iocoder.yudao.module.member.enums.MemberRecentAuditStatusEnum;
|
||||
import com.baomidou.mybatisplus.core.MybatisConfiguration;
|
||||
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
||||
import org.apache.ibatis.builder.MapperBuilderAssistant;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
|
||||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
|
||||
import static cn.iocoder.yudao.module.member.enums.MemberErrorCode.EMPTY;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.isNull;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class RecentServiceImplTest extends BaseMockitoUnitTest {
|
||||
|
||||
private static final Long RECENT_ID = 1L;
|
||||
private static final Long MEMBER_ID = 2L;
|
||||
|
||||
@InjectMocks
|
||||
private RecentServiceImpl recentService;
|
||||
|
||||
@Mock
|
||||
private RecentMapper recentMapper;
|
||||
@Mock
|
||||
private RecentAuditService recentAuditService;
|
||||
|
||||
@BeforeAll
|
||||
static void initTableInfo() {
|
||||
TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), ""), RecentDO.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateAndResubmitRecentRejectsUnchangedContent() {
|
||||
when(recentMapper.selectById(RECENT_ID)).thenReturn(createRejectedRecent());
|
||||
RecentSaveReqVO reqVO = createUpdateReqVO("原动态内容");
|
||||
|
||||
assertServiceException(() -> recentService.updateAndResubmitRecent(reqVO, MEMBER_ID),
|
||||
EMPTY, "请先修改动态内容再重新发布");
|
||||
|
||||
verify(recentMapper, never()).update(isNull(), any());
|
||||
verify(recentAuditService, never()).auditRecentAsync(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateAndResubmitRecentUpdatesContentAndTriggersAudit() {
|
||||
when(recentMapper.selectById(RECENT_ID)).thenReturn(createRejectedRecent());
|
||||
when(recentMapper.update(isNull(), any())).thenReturn(1);
|
||||
RecentSaveReqVO reqVO = createUpdateReqVO("修改后的动态内容");
|
||||
|
||||
recentService.updateAndResubmitRecent(reqVO, MEMBER_ID);
|
||||
|
||||
verify(recentMapper).update(isNull(), any());
|
||||
verify(recentAuditService).auditRecentAsync(RECENT_ID);
|
||||
}
|
||||
|
||||
private RecentDO createRejectedRecent() {
|
||||
return RecentDO.builder()
|
||||
.id(RECENT_ID)
|
||||
.memberId(MEMBER_ID)
|
||||
.description("原动态内容")
|
||||
.coverPic("https://example.com/image.jpg")
|
||||
.auditStatus(MemberRecentAuditStatusEnum.REJECTED.getStatus())
|
||||
.build();
|
||||
}
|
||||
|
||||
private RecentSaveReqVO createUpdateReqVO(String description) {
|
||||
RecentSaveReqVO reqVO = new RecentSaveReqVO();
|
||||
reqVO.setId(RECENT_ID);
|
||||
reqVO.setMemberId(MEMBER_ID);
|
||||
reqVO.setDescription(description);
|
||||
reqVO.setCoverPic("https://example.com/image.jpg");
|
||||
reqVO.setIssueLong(121.47D);
|
||||
reqVO.setIssueLat(31.23D);
|
||||
reqVO.setIssueProvince("上海市");
|
||||
reqVO.setIssueCity("上海市");
|
||||
reqVO.setIssueDistrict("浦东新区");
|
||||
reqVO.setIssueAddress("测试地址");
|
||||
return reqVO;
|
||||
}
|
||||
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
package cn.iocoder.yudao.module.member.service.user;
|
||||
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
import cn.iocoder.yudao.module.member.client.service.ChatApi;
|
||||
import cn.iocoder.yudao.module.member.client.vo.chat.user.ChatUserOnLineStatusReqVo;
|
||||
import cn.iocoder.yudao.module.member.client.vo.chat.user.ChatUserOnLineStatusRespVo;
|
||||
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
|
||||
import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper;
|
||||
import cn.iocoder.yudao.module.member.service.user.BO.MemberExtBO;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class MemberUserServiceImplChatActivityTest extends BaseMockitoUnitTest {
|
||||
|
||||
private static final Long USER_ID = 100L;
|
||||
|
||||
@InjectMocks
|
||||
private MemberUserServiceImpl memberUserService;
|
||||
|
||||
@Mock
|
||||
private MemberUserMapper memberUserMapper;
|
||||
@Mock
|
||||
private ChatApi chatApi;
|
||||
|
||||
@Test
|
||||
void getUserExtListUsesNewerChatActivity() {
|
||||
LocalDateTime databaseTime = LocalDateTime.of(2026, 7, 30, 10, 0);
|
||||
LocalDateTime chatTime = databaseTime.plusMinutes(5);
|
||||
when(memberUserMapper.selectBatchIds(Collections.singletonList(USER_ID)))
|
||||
.thenReturn(Collections.singletonList(new MemberUserDO().setId(USER_ID).setAppRefreshDate(databaseTime)));
|
||||
ChatUserOnLineStatusRespVo status = createStatus(true, chatTime);
|
||||
when(chatApi.getUserOnlineStatusById(any())).thenReturn(Collections.singletonList(status));
|
||||
|
||||
List<MemberExtBO> result = memberUserService.getUserExtList(Collections.singletonList(USER_ID));
|
||||
|
||||
assertEquals(1, result.size());
|
||||
assertTrue(result.get(0).getOnline());
|
||||
assertEquals(chatTime, result.get(0).getAppRefreshDate());
|
||||
verify(memberUserMapper).updateAppRefreshDateIfEarlier(USER_ID, chatTime);
|
||||
ArgumentCaptor<ChatUserOnLineStatusReqVo> requestCaptor = ArgumentCaptor.forClass(ChatUserOnLineStatusReqVo.class);
|
||||
verify(chatApi).getUserOnlineStatusById(requestCaptor.capture());
|
||||
assertEquals(Collections.singletonList(USER_ID), requestCaptor.getValue().getUserIdList());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getUserExtListKeepsNewerDatabaseActivity() {
|
||||
LocalDateTime databaseTime = LocalDateTime.of(2026, 7, 30, 10, 5);
|
||||
LocalDateTime chatTime = databaseTime.minusMinutes(5);
|
||||
when(memberUserMapper.selectBatchIds(Collections.singletonList(USER_ID)))
|
||||
.thenReturn(Collections.singletonList(new MemberUserDO().setId(USER_ID).setAppRefreshDate(databaseTime)));
|
||||
when(chatApi.getUserOnlineStatusById(any()))
|
||||
.thenReturn(Collections.singletonList(createStatus(false, chatTime)));
|
||||
|
||||
List<MemberExtBO> result = memberUserService.getUserExtList(Collections.singletonList(USER_ID));
|
||||
|
||||
assertEquals(databaseTime, result.get(0).getAppRefreshDate());
|
||||
verify(memberUserMapper, never()).updateAppRefreshDateIfEarlier(eq(USER_ID), any());
|
||||
}
|
||||
|
||||
private ChatUserOnLineStatusRespVo createStatus(boolean online, LocalDateTime lastActiveTime) {
|
||||
ChatUserOnLineStatusRespVo status = new ChatUserOnLineStatusRespVo();
|
||||
status.setId(USER_ID);
|
||||
status.setOnline(online);
|
||||
status.setLastActiveTime(lastActiveTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
|
||||
return status;
|
||||
}
|
||||
|
||||
}
|
||||
+3
-2
@@ -277,7 +277,7 @@ public class SocialClientServiceImpl implements SocialClientService {
|
||||
// 正式版本-release
|
||||
// 体验版-trial
|
||||
// 开发版-develop
|
||||
WxMaGenerateSchemeRequest.JumpWxa.newBuilder().path(path).query(json).envVersion("release").build()
|
||||
WxMaGenerateSchemeRequest.JumpWxa.newBuilder().path(path).query(json).envVersion("trial").build()
|
||||
)
|
||||
.expireTime(expireTime)
|
||||
.expireType(0)
|
||||
@@ -304,7 +304,8 @@ public class SocialClientServiceImpl implements SocialClientService {
|
||||
ObjUtil.defaultIfNull(reqVO.getWidth(), SocialWxQrcodeReqDTO.WIDTH),
|
||||
ObjUtil.defaultIfNull(reqVO.getAutoColor(), SocialWxQrcodeReqDTO.AUTO_COLOR),
|
||||
null,
|
||||
ObjUtil.defaultIfNull(reqVO.getHyaline(), SocialWxQrcodeReqDTO.HYALINE));
|
||||
ObjUtil.defaultIfNull(reqVO.getHyaline(), SocialWxQrcodeReqDTO.HYALINE)
|
||||
);
|
||||
} catch (WxErrorException e) {
|
||||
log.error("[getWxQrcode][reqVO({}) 获得小程序码失败]", reqVO, e);
|
||||
throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_QRCODE_ERROR);
|
||||
|
||||
@@ -180,6 +180,8 @@ yudao:
|
||||
transfer-notify-url: https://baac9a029ea84f36ba28c023fed4fc5f.hd.takin.cc/admin-api/pay/notify/transfer # 支付渠道的【转账】回调地址
|
||||
demo: false # 开启演示模式
|
||||
tencent-lbs-key: TVDBZ-TDILD-4ON4B-PFDZA-RNLKH-VVF6E # QQ 地图的密钥 https://lbs.qq.com/service/staticV2/staticGuide/staticDoc
|
||||
wxa-code:
|
||||
env-version: trial # 小程序版本: 正式版为 "release";体验版为 "trial";开发版为 "develop"
|
||||
|
||||
justauth:
|
||||
enabled: true
|
||||
|
||||
Reference in new issue
Block a user