Compare commits

...

11 Commits

Author SHA1 Message Date
37033f1b16 fix(notify):优化视频通知描述文案
All checks were successful
ZhenTu-BE/pipeline/head This commit looks good
- 简化视频生成完成通知的描述内容
- 调整未购买视频通知的表述方式
- 更新视频即将过期通知的提示语- 统一各类通知的描述风格
2025-10-20 17:11:53 +08:00
7ea78e4a17 refactor(logging): 将视频任务更新检查日志级别从 info 调整为 debug
- 修改 VideoTaskRepository 中的日志记录级别
- 将任务更新检查完成的日志从 info 级别改为 debug 级别
- 保持日志内容和参数不变,仅调整日志级别以减少生产环境日志量
2025-10-20 15:05:28 +08:00
9fcb472717 feat(notify): 增加景区通知模板标题和描述字段
- 在记录用户通知授权接口中将日志级别从 info 调整为 debug
- 在获取景区通知模板及用户授权余额接口中将日志级别从 info 调整为 debug
- 根据不同模板 ID 设置对应的标题和描述信息:
  - 视频生成通知  - 视频下载通知- 视频即将过期通知
  -未知模板类型兜底处理
- 在 ScenicTemplateAuthResp 类中新增 title 和 description 字段用于返回模板信息
2025-10-20 14:58:49 +08:00
70ef4eb09c fix(mapper): 更新用户通知授权检查逻辑
- 将XML中的小于等于符号从<=更正为&lt;=
- 确保SQL语句在XML文件中正确转义
- 避免因特殊字符导致的解析错误
2025-10-20 13:18:45 +08:00
58224a03c5 refactor(logging): 调整日志级别并移除冗余日志
- 将人脸识别成功日志从 info 级别调整为 debug 级别- 将 ZT-Source 消息处理成功日志从 info 级别调整为 debug 级别
- 移除 ZT-Source 照片素材保存成功的冗余 info 日志输出
2025-10-20 13:15:03 +08:00
927c4955f8 feat(notification):临时绕过授权检查逻辑
- 注释掉原有的授权次数检查和消费逻辑
- 直接返回 true 用于测试目的
- 保留原逻辑代码以备后续恢复
2025-10-20 13:15:03 +08:00
27f356519e feat(notification): 引入通知授权检查机制
- 在任务服务中集成通知授权工具类
- 实现视频生成通知前的权限校验逻辑- 在下载通知任务中增加授权消费检查- 添加过期提醒及额外下载通知的权限控制
- 完善无授权时的日志记录与处理流程
2025-10-20 13:15:03 +08:00
c80086ba69 feat(notify): 支持批量授权记录及景区模板查询
- 新增批量授权记录接口,支持一次请求处理多个模板ID
- 新增查询景区通知模板及用户授权余额接口
- 修改授权记录请求体,将单个templateId改为templateIds列表
- 增加授权记录响应结构,区分成功与失败记录
- 新增通知授权工具类,封装常用授权检查与消费方法
- 使用JwtTokenUtil获取当前用户ID替代BaseContextHandler
- 移除过时的BaseContextHandler导入及相关代码依赖
2025-10-20 13:15:02 +08:00
86d5f8ceb1 refactor(notify): 重构用户通知授权控制器以适应新的上下文处理器
- 将 JwtInfo 替换为 BaseContextHandler 来获取用户ID
- 更新 ResponseData 类型为 ApiResponse
- 修改 member ID 的获取方式为从 BaseContextHandler 中解析
- 使用 Jakarta Validation 注解替换旧的校验注解
- 调整 import 包路径以匹配新架构
2025-10-20 13:15:02 +08:00
ff708a3fc3 refactor(notify):重构通知授权模块,移除外部接口
- 移除用户通知授权检查、消费和记录查询的外部接口
- 废弃相关请求和响应 DTO 类文件
- 将授权检查和统计功能迁移至内部服务调用
- 新增批量检查授权方法 batchCheckAuthorization- 新增获取用户授权统计信息方法 getAuthorizationStats
- 更新 UserNotificationAuthorizationService 接口定义- 优化 ServiceImpl 中的数据处理逻辑和引入 Collectors 工具类
2025-10-20 13:15:02 +08:00
44b20890d5 feat(notify): 新增用户通知授权管理功能
- 添加用户通知授权记录的完整CRUD操作
- 实现授权次数的记录与消费逻辑
- 提供授权状态检查与剩余次数查询接口
- 支持按用户、模板或景区维度查询授权记录
- 新增授权统计信息接口,包括总授权数、消费数等
- 完成移动端相关请求/响应DTO定义
- 集成MyBatis Mapper实现数据持久化操作
- 添加服务层事务控制确保操作一致性
2025-10-20 13:15:02 +08:00
21 changed files with 1289 additions and 13 deletions

View File

@@ -0,0 +1,190 @@
package com.ycwl.basic.controller.mobile.notify;
import com.ycwl.basic.model.mobile.notify.req.NotificationAuthRecordReq;
import com.ycwl.basic.model.mobile.notify.resp.NotificationAuthRecordResp;
import com.ycwl.basic.model.mobile.notify.resp.ScenicTemplateAuthResp;
import com.ycwl.basic.service.UserNotificationAuthorizationService;
import com.ycwl.basic.utils.ApiResponse;
import com.ycwl.basic.utils.JwtTokenUtil;
import com.ycwl.basic.repository.ScenicRepository;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
/**
* 用户通知授权记录Controller (移动端API)
* 只提供用户主动授权记录功能,支持批量授权,其他检查和消费功能由系统内部调用
*
* @Author: System
* @Date: 2024/12/28
*/
@RestController
@RequestMapping("/api/mobile/notify/auth")
@Slf4j
public class UserNotificationAuthController {
@Autowired
private UserNotificationAuthorizationService userNotificationAuthorizationService;
@Autowired
private ScenicRepository scenicRepository;
/**
* 记录用户通知授权 - 支持批量授权
* 用户主动同意通知授权时调用
*/
@PostMapping("/record")
public ApiResponse<NotificationAuthRecordResp> recordAuthorization(
@RequestBody NotificationAuthRecordReq req) {
log.debug("记录用户通知授权: templateIds={}, scenicId={}", req.getTemplateIds(), req.getScenicId());
try {
// 获取当前用户ID
Long memberId = JwtTokenUtil.getWorker().getUserId();
// 调用批量授权记录方法
List<UserNotificationAuthorizationService.AuthorizationRecord> records =
userNotificationAuthorizationService.batchRecordAuthorization(
memberId, req.getTemplateIds(), req.getScenicId());
NotificationAuthRecordResp resp = new NotificationAuthRecordResp();
// 转换响应数据
List<NotificationAuthRecordResp.AuthorizationRecord> successRecords = new ArrayList<>();
List<String> failedTemplateIds = new ArrayList<>();
List<String> failureReasons = new ArrayList<>();
for (UserNotificationAuthorizationService.AuthorizationRecord record : records) {
if (record.isSuccess()) {
NotificationAuthRecordResp.AuthorizationRecord successRecord =
new NotificationAuthRecordResp.AuthorizationRecord();
successRecord.setId(record.getId());
successRecord.setTemplateId(record.getTemplateId());
successRecord.setScenicId(record.getScenicId());
successRecord.setAuthorizationCount(record.getAuthorizationCount());
successRecord.setConsumedCount(record.getConsumedCount());
successRecord.setRemainingCount(record.getRemainingCount());
successRecord.setLastAuthorizedTime(record.getLastAuthorizedTime());
successRecord.setLastConsumedTime(record.getLastConsumedTime());
successRecord.setStatus(record.getStatus());
successRecord.setCreateTime(record.getCreateTime());
successRecords.add(successRecord);
} else {
failedTemplateIds.add(record.getTemplateId());
failureReasons.add(record.getFailureReason());
}
}
resp.setAllSuccess(CollectionUtils.isEmpty(failedTemplateIds));
resp.setSuccessRecords(successRecords);
resp.setFailedTemplateIds(failedTemplateIds);
resp.setFailureReasons(failureReasons);
return ApiResponse.success(resp);
} catch (Exception e) {
log.error("记录用户通知授权失败", e);
return ApiResponse.fail("记录授权失败: " + e.getMessage());
}
}
/**
* 获取景区通知模板ID及用户授权余额
* 复制AppWxNotifyController中的逻辑,并额外返回用户对应的授权余额
*/
@GetMapping("/{scenicId}/templates")
public ApiResponse<ScenicTemplateAuthResp> getScenicTemplatesWithAuth(@PathVariable("scenicId") Long scenicId) {
log.debug("获取景区通知模板ID及用户授权余额: scenicId={}", scenicId);
try {
// 获取当前用户ID
Long memberId = JwtTokenUtil.getWorker().getUserId();
// 获取景区的所有模板ID(复制自AppWxNotifyController的逻辑)
List<String> templateIds = new ArrayList<>() {{
String videoGeneratedTemplateId = scenicRepository.getVideoGeneratedTemplateId(scenicId);
if (StringUtils.isNotBlank(videoGeneratedTemplateId)) {
add(videoGeneratedTemplateId);
}
String videoDownloadTemplateId = scenicRepository.getVideoDownloadTemplateId(scenicId);
if (StringUtils.isNotBlank(videoDownloadTemplateId)) {
add(videoDownloadTemplateId);
}
String videoPreExpireTemplateId = scenicRepository.getVideoPreExpireTemplateId(scenicId);
if (StringUtils.isNotBlank(videoPreExpireTemplateId)) {
add(videoPreExpireTemplateId);
}
}};
// 构建响应对象
ScenicTemplateAuthResp resp = new ScenicTemplateAuthResp();
resp.setScenicId(scenicId);
// 查询每个模板的授权余额信息
List<ScenicTemplateAuthResp.TemplateAuthInfo> templateAuthInfos = new ArrayList<>();
for (String templateId : templateIds) {
ScenicTemplateAuthResp.TemplateAuthInfo templateAuthInfo =
new ScenicTemplateAuthResp.TemplateAuthInfo();
templateAuthInfo.setTemplateId(templateId);
if (templateId.equals(scenicRepository.getVideoGeneratedTemplateId(scenicId))) {
templateAuthInfo.setTitle("视频生成通知");
templateAuthInfo.setDescription("当视频生成完成时,我们将提醒您");
} else if (templateId.equals(scenicRepository.getVideoDownloadTemplateId(scenicId))) {
templateAuthInfo.setTitle("视频下载通知");
templateAuthInfo.setDescription("当您的视频未购买时,我们将提醒您");
} else if (templateId.equals(scenicRepository.getVideoPreExpireTemplateId(scenicId))) {
templateAuthInfo.setTitle("视频即将过期通知");
templateAuthInfo.setDescription("当您的视频即将过期时,我们将提醒您及时下载");
} else {
templateAuthInfo.setTitle("未知模板类型");
templateAuthInfo.setDescription("未知的模板类型");
}
// 获取授权详情
try {
com.ycwl.basic.model.pc.notify.entity.UserNotificationAuthorizationEntity authEntity =
userNotificationAuthorizationService.checkAuthorization(memberId, templateId, scenicId);
if (authEntity != null) {
templateAuthInfo.setAuthorizationCount(authEntity.getAuthorizationCount());
templateAuthInfo.setConsumedCount(authEntity.getConsumedCount());
templateAuthInfo.setRemainingCount(authEntity.getRemainingCount());
templateAuthInfo.setHasAuthorization(authEntity.getRemainingCount() != null && authEntity.getRemainingCount() > 0);
} else {
// 没有授权记录
templateAuthInfo.setAuthorizationCount(0);
templateAuthInfo.setConsumedCount(0);
templateAuthInfo.setRemainingCount(0);
templateAuthInfo.setHasAuthorization(false);
}
} catch (Exception e) {
log.warn("获取模板授权信息失败: templateId={}, scenicId={}, memberId={}, error={}",
templateId, scenicId, memberId, e.getMessage());
// 获取失败时设置为无授权
templateAuthInfo.setAuthorizationCount(0);
templateAuthInfo.setConsumedCount(0);
templateAuthInfo.setRemainingCount(0);
templateAuthInfo.setHasAuthorization(false);
}
templateAuthInfos.add(templateAuthInfo);
}
resp.setTemplates(templateAuthInfos);
log.info("成功获取景区通知模板ID及用户授权余额: scenicId={}, templateCount={}, memberId={}",
scenicId, templateIds.size(), memberId);
return ApiResponse.success(resp);
} catch (Exception e) {
log.error("获取景区通知模板ID及用户授权余额失败: scenicId={}", scenicId, e);
return ApiResponse.fail("获取授权信息失败: " + e.getMessage());
}
}
}

View File

@@ -158,7 +158,7 @@ public class FaceProcessingKafkaService {
// 更新人脸样本得分和状态
faceSampleMapper.updateScore(faceSampleId, addFaceResp.getScore());
updateFaceSampleStatus(faceSampleId, 2);
log.info("人脸识别处理成功, faceSampleId: {}", faceSampleId);
log.debug("人脸识别处理成功, faceSampleId: {}", faceSampleId);
// 查询设备配置,判断是否启用预订功能
Long deviceId = message.getDeviceId();

View File

@@ -0,0 +1,92 @@
package com.ycwl.basic.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ycwl.basic.model.pc.notify.entity.UserNotificationAuthorizationEntity;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 用户通知授权记录Mapper接口
*
* @Author: System
* @Date: 2024/12/28
*/
@Mapper
public interface UserNotificationAuthorizationMapper extends BaseMapper<UserNotificationAuthorizationEntity> {
/**
* 根据用户ID、模板ID和景区ID查询授权记录
*
* @param memberId 用户ID
* @param templateId 模板ID
* @param scenicId 景区ID
* @return 授权记录
*/
UserNotificationAuthorizationEntity selectByMemberAndTemplateAndScenic(
@Param("memberId") Long memberId,
@Param("templateId") String templateId,
@Param("scenicId") Long scenicId
);
/**
* 根据用户ID查询授权记录列表
*
* @param memberId 用户ID
* @return 授权记录列表
*/
List<UserNotificationAuthorizationEntity> selectByMemberId(@Param("memberId") Long memberId);
/**
* 根据用户ID和模板ID查询授权记录列表
*
* @param memberId 用户ID
* @param templateId 模板ID
* @return 授权记录列表
*/
List<UserNotificationAuthorizationEntity> selectByMemberIdAndTemplateId(
@Param("memberId") Long memberId,
@Param("templateId") String templateId
);
/**
* 根据景区ID查询授权记录列表
*
* @param scenicId 景区ID
* @return 授权记录列表
*/
List<UserNotificationAuthorizationEntity> selectByScenicId(@Param("scenicId") Long scenicId);
/**
* 增加授权次数
*
* @param id 记录ID
* @param count 增加的数量
* @return 影响行数
*/
int increaseAuthorizationCount(@Param("id") Long id, @Param("count") Integer count);
/**
* 增加消费次数
*
* @param id 记录ID
* @param count 增加的数量
* @return 影响行数
*/
int increaseConsumedCount(@Param("id") Long id, @Param("count") Integer count);
/**
* 检查用户是否还有剩余授权次数
*
* @param memberId 用户ID
* @param templateId 模板ID
* @param scenicId 景区ID
* @return 剩余授权次数
*/
Integer selectRemainingCount(
@Param("memberId") Long memberId,
@Param("templateId") String templateId,
@Param("scenicId") Long scenicId
);
}

View File

@@ -0,0 +1,2 @@
// 该文件已废弃 - 检查授权功能改为内部服务调用
// 请使用 com.ycwl.basic.utils.NotificationAuthUtils 工具类

View File

@@ -0,0 +1,2 @@
// 该文件已废弃 - 消费授权功能改为内部服务调用
// 请使用 com.ycwl.basic.utils.NotificationAuthUtils 工具类

View File

@@ -0,0 +1,29 @@
package com.ycwl.basic.model.mobile.notify.req;
import lombok.Data;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.util.List;
/**
* 通知授权记录请求 - 支持批量授权
*
* @Author: System
* @Date: 2024/12/28
*/
@Data
public class NotificationAuthRecordReq {
/**
* 通知模板ID列表 - 支持批量授权
*/
@NotEmpty(message = "模板ID列表不能为空")
private List<String> templateIds;
/**
* 景区ID
*/
@NotNull(message = "景区ID不能为空")
private Long scenicId;
}

View File

@@ -0,0 +1,2 @@
// 该文件已废弃 - 查询授权记录功能改为内部服务调用
// 请使用 com.ycwl.basic.utils.NotificationAuthUtils 工具类

View File

@@ -0,0 +1,2 @@
// 该文件已废弃 - 检查授权响应功能改为内部服务调用
// 请使用 com.ycwl.basic.utils.NotificationAuthUtils 工具类

View File

@@ -0,0 +1,2 @@
// 该文件已废弃 - 消费授权响应功能改为内部服务调用
// 请使用 com.ycwl.basic.utils.NotificationAuthUtils 工具类

View File

@@ -0,0 +1,92 @@
package com.ycwl.basic.model.mobile.notify.resp;
import lombok.Data;
import java.util.Date;
import java.util.List;
/**
* 通知授权记录响应 - 支持批量记录
*
* @Author: System
* @Date: 2024/12/28
*/
@Data
public class NotificationAuthRecordResp {
/**
* 是否全部成功
*/
private Boolean allSuccess;
/**
* 成功记录的授权列表
*/
private List<AuthorizationRecord> successRecords;
/**
* 失败的模板ID列表
*/
private List<String> failedTemplateIds;
/**
* 失败原因列表(对应failedTemplateIds)
*/
private List<String> failureReasons;
/**
* 授权记录详情
*/
@Data
public static class AuthorizationRecord {
/**
* 记录ID
*/
private Long id;
/**
* 模板ID
*/
private String templateId;
/**
* 景区ID
*/
private Long scenicId;
/**
* 授权次数
*/
private Integer authorizationCount;
/**
* 已消费次数
*/
private Integer consumedCount;
/**
* 剩余授权次数
*/
private Integer remainingCount;
/**
* 最后授权时间
*/
private Date lastAuthorizedTime;
/**
* 最后消费时间
*/
private Date lastConsumedTime;
/**
* 状态
*/
private Integer status;
/**
* 创建时间
*/
private Date createTime;
}
}

View File

@@ -0,0 +1,66 @@
package com.ycwl.basic.model.mobile.notify.resp;
import lombok.Data;
import java.util.List;
/**
* 景区通知模板ID及用户授权余额响应
*
* @Author: System
* @Date: 2024/12/28
*/
@Data
public class ScenicTemplateAuthResp {
/**
* 景区ID
*/
private Long scenicId;
/**
* 模板授权信息列表
*/
private List<TemplateAuthInfo> templates;
/**
* 模板授权信息
*/
@Data
public static class TemplateAuthInfo {
/**
* 模板ID
*/
private String templateId;
/**
* 模板标题
*/
private String title;
/**
* 模板描述
*/
private String description;
/**
* 剩余授权次数
*/
private Integer remainingCount;
/**
* 是否有授权(剩余次数 > 0)
*/
private Boolean hasAuthorization;
/**
* 授权次数
*/
private Integer authorizationCount;
/**
* 已消费次数
*/
private Integer consumedCount;
}
}

View File

@@ -0,0 +1,79 @@
package com.ycwl.basic.model.pc.notify.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.util.Date;
/**
* 用户通知授权记录实体类
*
* @Author: System
* @Date: 2024/12/28
*/
@Data
@TableName("user_notification_authorization")
public class UserNotificationAuthorizationEntity {
/**
* 主键ID
*/
@TableId
private Long id;
/**
* 用户ID
*/
private Long memberId;
/**
* 通知模板ID(微信消息模板ID)
*/
private String templateId;
/**
* 景区ID
*/
private Long scenicId;
/**
* 授权次数
*/
private Integer authorizationCount;
/**
* 已消费次数
*/
private Integer consumedCount;
/**
* 剩余授权次数(计算字段)
*/
private Integer remainingCount;
/**
* 最后授权时间
*/
private Date lastAuthorizedTime;
/**
* 最后消费时间
*/
private Date lastConsumedTime;
/**
* 状态:1-有效,0-无效
*/
private Integer status;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
}

View File

@@ -179,7 +179,7 @@ public class VideoTaskRepository {
result.setCanUpdate(canUpdate);
result.setNewSegmentCount(newSegmentCount);
log.info("任务更新检查完成: taskId={}, canUpdate={}, newSegmentCount={}, originalCount={}, currentCount={}",
log.debug("任务更新检查完成: taskId={}, canUpdate={}, newSegmentCount={}, originalCount={}, currentCount={}",
taskId, result.isCanUpdate(), newSegmentCount, originalSegmentCount, currentSegmentCount);
} catch (Exception e) {

View File

@@ -0,0 +1,185 @@
package com.ycwl.basic.service;
import com.ycwl.basic.model.pc.notify.entity.UserNotificationAuthorizationEntity;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* 用户通知授权记录Service接口
*
* @Author: System
* @Date: 2024/12/28
*/
public interface UserNotificationAuthorizationService {
/**
* 检查用户是否对指定模板有授权
*
* @param memberId 用户ID
* @param templateId 模板ID
* @param scenicId 景区ID
* @return 授权记录,如果没有授权返回null
*/
UserNotificationAuthorizationEntity checkAuthorization(Long memberId, String templateId, Long scenicId);
/**
* 检查用户是否还有剩余授权次数
*
* @param memberId 用户ID
* @param templateId 模板ID
* @param scenicId 景区ID
* @return 剩余授权次数,0表示没有剩余
*/
Integer getRemainingCount(Long memberId, String templateId, Long scenicId);
/**
* 记录用户授权
*
* @param memberId 用户ID
* @param templateId 模板ID
* @param scenicId 景区ID
* @return 授权记录
*/
UserNotificationAuthorizationEntity recordAuthorization(Long memberId, String templateId, Long scenicId);
/**
* 批量记录用户授权
*
* @param memberId 用户ID
* @param templateIds 模板ID列表
* @param scenicId 景区ID
* @return 批量授权记录结果
*/
List<AuthorizationRecord> batchRecordAuthorization(Long memberId, List<String> templateIds, Long scenicId);
/**
* 消费一次授权
*
* @param memberId 用户ID
* @param templateId 模板ID
* @param scenicId 景区ID
* @return 是否消费成功
*/
boolean consumeAuthorization(Long memberId, String templateId, Long scenicId);
/**
* 查询用户的授权记录列表
*
* @param memberId 用户ID
* @return 授权记录列表
*/
List<UserNotificationAuthorizationEntity> getUserAuthorizations(Long memberId);
/**
* 查询用户对指定模板的授权记录列表
*
* @param memberId 用户ID
* @param templateId 模板ID
* @return 授权记录列表
*/
List<UserNotificationAuthorizationEntity> getUserAuthorizationsByTemplate(Long memberId, String templateId);
/**
* 查询景区的授权记录列表
*
* @param scenicId 景区ID
* @return 授权记录列表
*/
List<UserNotificationAuthorizationEntity> getScenicAuthorizations(Long scenicId);
/**
* 根据ID查询授权记录
*
* @param id 记录ID
* @return 授权记录
*/
UserNotificationAuthorizationEntity getById(Long id);
/**
* 批量检查多个模板的授权状态
*
* @param memberId 用户ID
* @param templateIds 模板ID列表
* @param scenicId 景区ID
* @return 授权记录映射
*/
Map<String, UserNotificationAuthorizationEntity> batchCheckAuthorization(
Long memberId, List<String> templateIds, Long scenicId);
/**
* 获取用户授权统计信息
*
* @param memberId 用户ID
* @return 统计信息
*/
AuthorizationStats getAuthorizationStats(Long memberId);
/**
* 授权记录结果
*/
class AuthorizationRecord {
private boolean success; // 是否成功
private String templateId; // 模板ID
private Long id; // 记录ID
private Long scenicId; // 景区ID
private Integer authorizationCount; // 授权次数
private Integer consumedCount; // 已消费次数
private Integer remainingCount; // 剩余次数
private Date lastAuthorizedTime; // 最后授权时间
private Date lastConsumedTime; // 最后消费时间
private Integer status; // 状态
private Date createTime; // 创建时间
private String failureReason; // 失败原因
// getters and setters
public boolean isSuccess() { return success; }
public void setSuccess(boolean success) { this.success = success; }
public String getTemplateId() { return templateId; }
public void setTemplateId(String templateId) { this.templateId = templateId; }
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public Long getScenicId() { return scenicId; }
public void setScenicId(Long scenicId) { this.scenicId = scenicId; }
public Integer getAuthorizationCount() { return authorizationCount; }
public void setAuthorizationCount(Integer authorizationCount) { this.authorizationCount = authorizationCount; }
public Integer getConsumedCount() { return consumedCount; }
public void setConsumedCount(Integer consumedCount) { this.consumedCount = consumedCount; }
public Integer getRemainingCount() { return remainingCount; }
public void setRemainingCount(Integer remainingCount) { this.remainingCount = remainingCount; }
public Date getLastAuthorizedTime() { return lastAuthorizedTime; }
public void setLastAuthorizedTime(Date lastAuthorizedTime) { this.lastAuthorizedTime = lastAuthorizedTime; }
public Date getLastConsumedTime() { return lastConsumedTime; }
public void setLastConsumedTime(Date lastConsumedTime) { this.lastConsumedTime = lastConsumedTime; }
public Integer getStatus() { return status; }
public void setStatus(Integer status) { this.status = status; }
public Date getCreateTime() { return createTime; }
public void setCreateTime(Date createTime) { this.createTime = createTime; }
public String getFailureReason() { return failureReason; }
public void setFailureReason(String failureReason) { this.failureReason = failureReason; }
}
/**
* 授权统计信息DTO
*/
class AuthorizationStats {
private int totalTemplates; // 总模板数
private int totalAuthorizations; // 总授权次数
private int totalConsumed; // 总消费次数
private int totalRemaining; // 剩余授权次数
private Date queryTime; // 查询时间
// getters and setters
public int getTotalTemplates() { return totalTemplates; }
public void setTotalTemplates(int totalTemplates) { this.totalTemplates = totalTemplates; }
public int getTotalAuthorizations() { return totalAuthorizations; }
public void setTotalAuthorizations(int totalAuthorizations) { this.totalAuthorizations = totalAuthorizations; }
public int getTotalConsumed() { return totalConsumed; }
public void setTotalConsumed(int totalConsumed) { this.totalConsumed = totalConsumed; }
public int getTotalRemaining() { return totalRemaining; }
public void setTotalRemaining(int totalRemaining) { this.totalRemaining = totalRemaining; }
public Date getQueryTime() { return queryTime; }
public void setQueryTime(Date queryTime) { this.queryTime = queryTime; }
}
}

View File

@@ -51,7 +51,7 @@ public class ZTSourceConsumerService {
if (processed) {
// 只有在处理成功后才手动提交
ack.acknowledge();
log.info("ZT-Source消息处理成功并已提交, sourceId: {}", sourceMessage.getSourceId());
log.debug("ZT-Source消息处理成功并已提交, sourceId: {}", sourceMessage.getSourceId());
} else {
log.warn("ZT-Source消息处理被跳过(非照片类型),消息不会被提交, sourceId: {}, sourceType: {}",
sourceMessage.getSourceId(), sourceMessage.getSourceType());

View File

@@ -65,8 +65,6 @@ public class ZTSourceDataService {
int result = sourceMapper.addFromZTSource(sourceEntity);
if (result > 0) {
log.info("成功保存ZT-Source照片素材: sourceId={}, entityId={}, scenicId={}, deviceId={}",
message.getSourceId(), sourceEntity.getId(), message.getScenicId(), message.getDeviceId());
return true;
} else {
log.error("保存ZT-Source照片素材失败: sourceId={}", message.getSourceId());

View File

@@ -0,0 +1,231 @@
package com.ycwl.basic.service.impl;
import com.ycwl.basic.mapper.UserNotificationAuthorizationMapper;
import com.ycwl.basic.model.pc.notify.entity.UserNotificationAuthorizationEntity;
import com.ycwl.basic.service.UserNotificationAuthorizationService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.stream.Collectors;
/**
* 用户通知授权记录Service实现类
*
* @Author: System
* @Date: 2024/12/28
*/
@Service
@Slf4j
public class UserNotificationAuthorizationServiceImpl implements UserNotificationAuthorizationService {
@Autowired
private UserNotificationAuthorizationMapper userNotificationAuthorizationMapper;
@Override
public UserNotificationAuthorizationEntity checkAuthorization(Long memberId, String templateId, Long scenicId) {
log.debug("检查用户授权: memberId={}, templateId={}, scenicId={}", memberId, templateId, scenicId);
return userNotificationAuthorizationMapper.selectByMemberAndTemplateAndScenic(memberId, templateId, scenicId);
}
@Override
public Integer getRemainingCount(Long memberId, String templateId, Long scenicId) {
log.debug("获取剩余授权次数: memberId={}, templateId={}, scenicId={}", memberId, templateId, scenicId);
Integer remainingCount = userNotificationAuthorizationMapper.selectRemainingCount(memberId, templateId, scenicId);
return remainingCount != null ? remainingCount : 0;
}
@Override
@Transactional(rollbackFor = Exception.class)
public UserNotificationAuthorizationEntity recordAuthorization(Long memberId, String templateId, Long scenicId) {
log.info("记录用户授权: memberId={}, templateId={}, scenicId={}", memberId, templateId, scenicId);
// 先查询是否已存在记录
UserNotificationAuthorizationEntity existingRecord = userNotificationAuthorizationMapper
.selectByMemberAndTemplateAndScenic(memberId, templateId, scenicId);
if (existingRecord != null) {
// 增加授权次数
int updated = userNotificationAuthorizationMapper.increaseAuthorizationCount(existingRecord.getId(), 1);
if (updated > 0) {
log.info("成功增加授权次数: recordId={}, newCount={}", existingRecord.getId(),
existingRecord.getAuthorizationCount() + 1);
// 重新查询最新数据
return userNotificationAuthorizationMapper.selectById(existingRecord.getId());
} else {
log.error("增加授权次数失败: recordId={}", existingRecord.getId());
throw new RuntimeException("增加授权次数失败");
}
} else {
// 创建新记录
UserNotificationAuthorizationEntity newRecord = new UserNotificationAuthorizationEntity();
newRecord.setMemberId(memberId);
newRecord.setTemplateId(templateId);
newRecord.setScenicId(scenicId);
newRecord.setAuthorizationCount(1);
newRecord.setConsumedCount(0);
newRecord.setLastAuthorizedTime(new Date());
newRecord.setStatus(1);
newRecord.setCreateTime(new Date());
newRecord.setUpdateTime(new Date());
int inserted = userNotificationAuthorizationMapper.insert(newRecord);
if (inserted > 0) {
log.info("成功创建授权记录: recordId={}", newRecord.getId());
return newRecord;
} else {
log.error("创建授权记录失败: memberId={}, templateId={}, scenicId={}", memberId, templateId, scenicId);
throw new RuntimeException("创建授权记录失败");
}
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public List<AuthorizationRecord> batchRecordAuthorization(Long memberId, List<String> templateIds, Long scenicId) {
log.info("批量记录用户授权: memberId={}, templateIds={}, scenicId={}", memberId, templateIds, scenicId);
List<AuthorizationRecord> results = new ArrayList<>();
if (templateIds == null || templateIds.isEmpty()) {
return results;
}
for (String templateId : templateIds) {
AuthorizationRecord record = new AuthorizationRecord();
record.setTemplateId(templateId);
try {
UserNotificationAuthorizationEntity entity = recordAuthorization(memberId, templateId, scenicId);
// 转换为响应对象
record.setSuccess(true);
record.setId(entity.getId());
record.setScenicId(entity.getScenicId());
record.setAuthorizationCount(entity.getAuthorizationCount());
record.setConsumedCount(entity.getConsumedCount());
record.setRemainingCount(entity.getRemainingCount());
record.setLastAuthorizedTime(entity.getLastAuthorizedTime());
record.setLastConsumedTime(entity.getLastConsumedTime());
record.setStatus(entity.getStatus());
record.setCreateTime(entity.getCreateTime());
} catch (Exception e) {
log.error("批量授权记录失败: memberId={}, templateId={}, scenicId={}", memberId, templateId, scenicId, e);
record.setSuccess(false);
record.setFailureReason(e.getMessage());
}
results.add(record);
}
return results;
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean consumeAuthorization(Long memberId, String templateId, Long scenicId) {
log.info("消费授权: memberId={}, templateId={}, scenicId={}", memberId, templateId, scenicId);
UserNotificationAuthorizationEntity record = userNotificationAuthorizationMapper
.selectByMemberAndTemplateAndScenic(memberId, templateId, scenicId);
if (record == null) {
log.warn("未找到授权记录: memberId={}, templateId={}, scenicId={}", memberId, templateId, scenicId);
return false;
}
if (record.getRemainingCount() <= 0) {
log.warn("剩余授权次数不足: recordId={}, remainingCount={}", record.getId(), record.getRemainingCount());
return false;
}
int updated = userNotificationAuthorizationMapper.increaseConsumedCount(record.getId(), 1);
if (updated > 0) {
log.info("成功消费授权: recordId={}, newConsumedCount={}", record.getId(),
record.getConsumedCount() + 1);
return true;
} else {
log.error("消费授权失败: recordId={}", record.getId());
return false;
}
}
@Override
public List<UserNotificationAuthorizationEntity> getUserAuthorizations(Long memberId) {
log.debug("查询用户授权记录: memberId={}", memberId);
return userNotificationAuthorizationMapper.selectByMemberId(memberId);
}
@Override
public List<UserNotificationAuthorizationEntity> getUserAuthorizationsByTemplate(Long memberId, String templateId) {
log.debug("查询用户模板授权记录: memberId={}, templateId={}", memberId, templateId);
return userNotificationAuthorizationMapper.selectByMemberIdAndTemplateId(memberId, templateId);
}
@Override
public List<UserNotificationAuthorizationEntity> getScenicAuthorizations(Long scenicId) {
log.debug("查询景区授权记录: scenicId={}", scenicId);
return userNotificationAuthorizationMapper.selectByScenicId(scenicId);
}
@Override
public UserNotificationAuthorizationEntity getById(Long id) {
log.debug("根据ID查询授权记录: id={}", id);
return userNotificationAuthorizationMapper.selectById(id);
}
@Override
public Map<String, UserNotificationAuthorizationEntity> batchCheckAuthorization(
Long memberId, List<String> templateIds, Long scenicId) {
log.debug("批量检查用户授权: memberId={}, templateIds={}, scenicId={}", memberId, templateIds, scenicId);
Map<String, UserNotificationAuthorizationEntity> result = new HashMap<>();
if (templateIds == null || templateIds.isEmpty()) {
return result;
}
// 查询用户在该景区的所有授权记录
List<UserNotificationAuthorizationEntity> userRecords = getUserAuthorizations(memberId);
// 过滤出指定景区和模板的记录
Map<String, UserNotificationAuthorizationEntity> recordMap = userRecords.stream()
.filter(record -> scenicId.equals(record.getScenicId()))
.filter(record -> templateIds.contains(record.getTemplateId()))
.collect(Collectors.toMap(
UserNotificationAuthorizationEntity::getTemplateId,
record -> record,
(existing, replacement) -> existing
));
// 为每个模板ID填充结果
for (String templateId : templateIds) {
result.put(templateId, recordMap.get(templateId));
}
return result;
}
@Override
public AuthorizationStats getAuthorizationStats(Long memberId) {
log.debug("获取用户授权统计信息: memberId={}", memberId);
List<UserNotificationAuthorizationEntity> records = getUserAuthorizations(memberId);
AuthorizationStats stats = new AuthorizationStats();
stats.setTotalTemplates(records.size());
stats.setTotalAuthorizations(records.stream()
.mapToInt(UserNotificationAuthorizationEntity::getAuthorizationCount)
.sum());
stats.setTotalConsumed(records.stream()
.mapToInt(UserNotificationAuthorizationEntity::getConsumedCount)
.sum());
stats.setTotalRemaining(stats.getTotalAuthorizations() - stats.getTotalConsumed());
stats.setQueryTime(new Date());
return stats;
}
}

View File

@@ -57,6 +57,7 @@ import com.ycwl.basic.task.VideoPieceGetter;
import com.ycwl.basic.repository.TemplateRepository;
import com.ycwl.basic.utils.SnowFlakeUtil;
import com.ycwl.basic.utils.VideoReUploader;
import com.ycwl.basic.utils.NotificationAuthUtils;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@@ -130,6 +131,8 @@ public class TaskTaskServiceImpl implements TaskService {
private MemberRelationRepository memberRelationRepository;
@Autowired
private ZtMessageProducerService ztMessageProducerService;
@Autowired
private NotificationAuthUtils notificationAuthUtils;
private RenderWorkerEntity getWorker(@NonNull WorkerAuthReqVo req) {
String accessKey = req.getAccessKey();
@@ -642,16 +645,23 @@ public class TaskTaskServiceImpl implements TaskService {
log.warn("未配置视频生成通知模板");
return;
}
// 检查并消费通知授权
if (!notificationAuthUtils.checkAndConsumeAuthorization(memberId, templateId, item.getScenicId())) {
log.debug("用户[memberId={}]对模板[templateId={}]授权不足,跳过视频生成通知", memberId, templateId);
return;
}
ScenicEntity scenic = scenicRepository.getScenic(item.getScenicId());
ScenicConfigManager configManager = scenicRepository.getScenicConfigManager(item.getScenicId());
String configTitle = configManager.getString("first_notification_title");
String configContent = configManager.getString("first_notification_content");
if (StringUtils.isBlank(configTitle) || StringUtils.isBlank(configContent)) {
log.info("景区[{}]未配置第一次通知内容,跳过发送通知", scenic.getName());
return;
}
String title = configTitle.replace("【景区】", scenic.getName());
String page;
if (configManager.getBoolean("grouping_enable", false)) {

View File

@@ -17,6 +17,7 @@ import com.ycwl.basic.model.pc.scenic.req.ScenicReqQuery;
import com.ycwl.basic.repository.ScenicRepository;
import com.ycwl.basic.repository.TemplateRepository;
import com.ycwl.basic.integration.common.manager.ScenicConfigManager;
import com.ycwl.basic.utils.NotificationAuthUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -50,6 +51,8 @@ public class DownloadNotificationTasker {
private CouponMapper couponMapper;
@Autowired
private ZtMessageProducerService ztMessageProducerService;
@Autowired
private NotificationAuthUtils notificationAuthUtils;
@Scheduled(cron = "0 0 21 * * *")
public void sendDownloadNotification() {
@@ -75,16 +78,23 @@ public class DownloadNotificationTasker {
log.info("模板消息为空");
return;
}
// 检查并消费通知授权
if (!notificationAuthUtils.checkAndConsumeAuthorization(item.getMemberId(), templateId, item.getScenicId())) {
log.debug("用户[memberId={}]对模板[templateId={}]授权不足,跳过下载通知", item.getMemberId(), templateId);
return;
}
ScenicEntity scenic = scenicRepository.getScenic(item.getScenicId());
ScenicConfigManager configManager = scenicRepository.getScenicConfigManager(item.getScenicId());
String configTitle = configManager.getString("second_notification_title");
String configContent = configManager.getString("second_notification_content");
if (StringUtils.isBlank(configTitle) || StringUtils.isBlank(configContent)) {
log.info("景区[{}]未配置第二次通知内容,跳过发送通知", scenic.getName());
return;
}
String title = configTitle.replace("【景区】", scenic.getName());
String page;
if (configManager.getBoolean("grouping_enable", false)) {
@@ -147,16 +157,23 @@ public class DownloadNotificationTasker {
log.info("模板消息为空");
return;
}
// 检查并消费通知授权
if (!notificationAuthUtils.checkAndConsumeAuthorization(item.getMemberId(), templateId, item.getScenicId())) {
log.debug("用户[memberId={}]对模板[templateId={}]授权不足,跳过过期提醒通知", item.getMemberId(), templateId);
return;
}
ScenicEntity scenic = scenicRepository.getScenic(item.getScenicId());
ScenicConfigManager configManager = scenicRepository.getScenicConfigManager(item.getScenicId());
String configTitle = configManager.getString("third_notification_title");
String configContent = configManager.getString("third_notification_content");
if (StringUtils.isBlank(configTitle) || StringUtils.isBlank(configContent)) {
log.info("景区[{}]未配置第三次通知内容,跳过发送通知", scenic.getName());
return;
}
String title = configTitle.replace("【景区】", scenic.getName());
String page;
if (configManager.getBoolean("grouping_enable", false)) {
@@ -242,15 +259,22 @@ public class DownloadNotificationTasker {
log.info("模板消息为空");
return;
}
// 检查并消费通知授权
if (!notificationAuthUtils.checkAndConsumeAuthorization(item.getMemberId(), templateId, item.getScenicId())) {
log.debug("用户[memberId={}]对模板[templateId={}]授权不足,跳过额外下载通知", item.getMemberId(), templateId);
return;
}
ScenicConfigManager configManager = scenicRepository.getScenicConfigManager(scenicId);
String configTitle = configManager.getString("second_notification_title");
String configContent = configManager.getString("second_notification_content");
if (StringUtils.isBlank(configTitle) || StringUtils.isBlank(configContent)) {
log.info("景区[{}]未配置第一次通知内容,跳过发送通知", scenic.getName());
return;
}
String title = configTitle.replace("【景区】", scenic.getName());
String page;
if (configManager.getBoolean("grouping_enable", false)) {

View File

@@ -0,0 +1,167 @@
package com.ycwl.basic.utils;
import com.ycwl.basic.model.pc.notify.entity.UserNotificationAuthorizationEntity;
import com.ycwl.basic.service.UserNotificationAuthorizationService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 通知授权工具类
* 提供系统内部调用的授权检查和消费功能
*
* @Author: System
* @Date: 2024/12/28
*/
@Component
@Slf4j
public class NotificationAuthUtils {
@Autowired
private UserNotificationAuthorizationService userNotificationAuthorizationService;
/**
* 检查用户是否对指定模板有授权
*
* @param memberId 用户ID
* @param templateId 模板ID
* @param scenicId 景区ID
* @return 是否有授权
*/
public boolean hasAuthorization(Long memberId, String templateId, Long scenicId) {
try {
Integer remainingCount = userNotificationAuthorizationService.getRemainingCount(memberId, templateId, scenicId);
return remainingCount != null && remainingCount > 0;
} catch (Exception e) {
log.error("检查用户授权失败: memberId={}, templateId={}, scenicId={}", memberId, templateId, scenicId, e);
return false;
}
}
/**
* 获取用户剩余授权次数
*
* @param memberId 用户ID
* @param templateId 模板ID
* @param scenicId 景区ID
* @return 剩余授权次数,0表示没有剩余
*/
public int getRemainingCount(Long memberId, String templateId, Long scenicId) {
try {
Integer remainingCount = userNotificationAuthorizationService.getRemainingCount(memberId, templateId, scenicId);
return remainingCount != null ? remainingCount : 0;
} catch (Exception e) {
log.error("获取剩余授权次数失败: memberId={}, templateId={}, scenicId={}", memberId, templateId, scenicId, e);
return 0;
}
}
/**
* 消费一次授权
* 在发送通知前调用此方法消费授权
*
* @param memberId 用户ID
* @param templateId 模板ID
* @param scenicId 景区ID
* @return 是否消费成功
*/
public boolean consumeAuthorization(Long memberId, String templateId, Long scenicId) {
try {
boolean result = userNotificationAuthorizationService.consumeAuthorization(memberId, templateId, scenicId);
if (result) {
log.info("成功消费授权: memberId={}, templateId={}, scenicId={}", memberId, templateId, scenicId);
} else {
log.warn("消费授权失败: memberId={}, templateId={}, scenicId={}", memberId, templateId, scenicId);
}
return result;
} catch (Exception e) {
log.error("消费授权异常: memberId={}, templateId={}, scenicId={}", memberId, templateId, scenicId, e);
return false;
}
}
/**
* 检查并消费授权(原子操作)
* 先检查是否有授权,如果有则直接消费
*
* @param memberId 用户ID
* @param templateId 模板ID
* @param scenicId 景区ID
* @return 是否检查并消费成功
*/
public boolean checkAndConsumeAuthorization(Long memberId, String templateId, Long scenicId) {
// 测试
return true;
// try {
// // 先检查剩余次数
// Integer remainingCount = userNotificationAuthorizationService.getRemainingCount(memberId, templateId, scenicId);
// if (remainingCount == null || remainingCount <= 0) {
// log.debug("用户剩余授权次数不足: memberId={}, templateId={}, scenicId={}, remainingCount={}",
// memberId, templateId, scenicId, remainingCount);
// return false;
// }
//
// // 尝试消费
// return consumeAuthorization(memberId, templateId, scenicId);
// } catch (Exception e) {
// log.error("检查并消费授权异常: memberId={}, templateId={}, scenicId={}", memberId, templateId, scenicId, e);
// return false;
// }
}
/**
* 记录用户授权
* 用户主动同意授权时调用
*
* @param memberId 用户ID
* @param templateId 模板ID
* @param scenicId 景区ID
* @return 授权记录
*/
public UserNotificationAuthorizationEntity recordAuthorization(Long memberId, String templateId, Long scenicId) {
try {
return userNotificationAuthorizationService.recordAuthorization(memberId, templateId, scenicId);
} catch (Exception e) {
log.error("记录用户授权失败: memberId={}, templateId={}, scenicId={}", memberId, templateId, scenicId, e);
throw new RuntimeException("记录用户授权失败", e);
}
}
/**
* 批量记录用户授权
* 用户主动同意多个授权时调用
*
* @param memberId 用户ID
* @param templateIds 模板ID列表
* @param scenicId 景区ID
* @return 批量授权记录结果
*/
public List<UserNotificationAuthorizationService.AuthorizationRecord> batchRecordAuthorization(
Long memberId, List<String> templateIds, Long scenicId) {
try {
return userNotificationAuthorizationService.batchRecordAuthorization(memberId, templateIds, scenicId);
} catch (Exception e) {
log.error("批量记录用户授权失败: memberId={}, templateIds={}, scenicId={}", memberId, templateIds, scenicId, e);
throw new RuntimeException("批量记录用户授权失败", e);
}
}
/**
* 获取用户授权详情
*
* @param memberId 用户ID
* @param templateId 模板ID
* @param scenicId 景区ID
* @return 授权记录,如果没有授权返回null
*/
public UserNotificationAuthorizationEntity getAuthorizationDetail(Long memberId, String templateId, Long scenicId) {
try {
return userNotificationAuthorizationService.checkAuthorization(memberId, templateId, scenicId);
} catch (Exception e) {
log.error("获取用户授权详情失败: memberId={}, templateId={}, scenicId={}", memberId, templateId, scenicId, e);
return null;
}
}
}

View File

@@ -0,0 +1,103 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.ycwl.basic.mapper.UserNotificationAuthorizationMapper">
<!-- 结果映射 -->
<resultMap id="BaseResultMap" type="com.ycwl.basic.model.pc.notify.entity.UserNotificationAuthorizationEntity">
<id column="id" property="id" jdbcType="BIGINT"/>
<result column="member_id" property="memberId" jdbcType="BIGINT"/>
<result column="template_id" property="templateId" jdbcType="VARCHAR"/>
<result column="scenic_id" property="scenicId" jdbcType="BIGINT"/>
<result column="authorization_count" property="authorizationCount" jdbcType="INTEGER"/>
<result column="consumed_count" property="consumedCount" jdbcType="INTEGER"/>
<result column="remaining_count" property="remainingCount" jdbcType="INTEGER"/>
<result column="last_authorized_time" property="lastAuthorizedTime" jdbcType="TIMESTAMP"/>
<result column="last_consumed_time" property="lastConsumedTime" jdbcType="TIMESTAMP"/>
<result column="status" property="status" jdbcType="TINYINT"/>
<result column="create_time" property="createTime" jdbcType="TIMESTAMP"/>
<result column="update_time" property="updateTime" jdbcType="TIMESTAMP"/>
</resultMap>
<!-- 基础字段 -->
<sql id="Base_Column_List">
id, member_id, template_id, scenic_id, authorization_count, consumed_count,
remaining_count, last_authorized_time, last_consumed_time, status, create_time, update_time
</sql>
<!-- 根据用户ID、模板ID和景区ID查询授权记录 -->
<select id="selectByMemberAndTemplateAndScenic" resultMap="BaseResultMap">
SELECT
<include refid="Base_Column_List"/>
FROM user_notification_authorization
WHERE member_id = #{memberId}
AND template_id = #{templateId}
AND scenic_id = #{scenicId}
AND status = 1
LIMIT 1
</select>
<!-- 根据用户ID查询授权记录列表 -->
<select id="selectByMemberId" resultMap="BaseResultMap">
SELECT
<include refid="Base_Column_List"/>
FROM user_notification_authorization
WHERE member_id = #{memberId}
AND status = 1
ORDER BY create_time DESC
</select>
<!-- 根据用户ID和模板ID查询授权记录列表 -->
<select id="selectByMemberIdAndTemplateId" resultMap="BaseResultMap">
SELECT
<include refid="Base_Column_List"/>
FROM user_notification_authorization
WHERE member_id = #{memberId}
AND template_id = #{templateId}
AND status = 1
ORDER BY create_time DESC
</select>
<!-- 根据景区ID查询授权记录列表 -->
<select id="selectByScenicId" resultMap="BaseResultMap">
SELECT
<include refid="Base_Column_List"/>
FROM user_notification_authorization
WHERE scenic_id = #{scenicId}
AND status = 1
ORDER BY create_time DESC
</select>
<!-- 增加授权次数 -->
<update id="increaseAuthorizationCount">
UPDATE user_notification_authorization
SET authorization_count = authorization_count + #{count},
last_authorized_time = NOW(),
update_time = NOW()
WHERE id = #{id}
AND status = 1
</update>
<!-- 增加消费次数 -->
<update id="increaseConsumedCount">
UPDATE user_notification_authorization
SET consumed_count = consumed_count + #{count},
last_consumed_time = NOW(),
update_time = NOW()
WHERE id = #{id}
AND status = 1
AND consumed_count + #{count} &lt;= authorization_count
</update>
<!-- 检查用户是否还有剩余授权次数 -->
<select id="selectRemainingCount" resultType="java.lang.Integer">
SELECT COALESCE(remaining_count, 0)
FROM user_notification_authorization
WHERE member_id = #{memberId}
AND template_id = #{templateId}
AND scenic_id = #{scenicId}
AND status = 1
AND remaining_count > 0
LIMIT 1
</select>
</mapper>