diff --git a/src/main/java/com/ycwl/basic/controller/mobile/notify/UserNotificationAuthController.java b/src/main/java/com/ycwl/basic/controller/mobile/notify/UserNotificationAuthController.java index 17d44f20..f855c288 100644 --- a/src/main/java/com/ycwl/basic/controller/mobile/notify/UserNotificationAuthController.java +++ b/src/main/java/com/ycwl/basic/controller/mobile/notify/UserNotificationAuthController.java @@ -1,21 +1,24 @@ package com.ycwl.basic.controller.mobile.notify; -import com.ycwl.basic.constant.BaseContextHandler; import com.ycwl.basic.model.mobile.notify.req.NotificationAuthRecordReq; import com.ycwl.basic.model.mobile.notify.resp.NotificationAuthRecordResp; -import com.ycwl.basic.model.pc.notify.entity.UserNotificationAuthorizationEntity; +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.springframework.beans.BeanUtils; +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 jakarta.validation.Valid; +import java.util.ArrayList; +import java.util.List; /** * 用户通知授权记录Controller (移动端API) - * 只提供用户主动授权记录功能,其他检查和消费功能由系统内部调用 + * 只提供用户主动授权记录功能,支持批量授权,其他检查和消费功能由系统内部调用 * * @Author: System * @Date: 2024/12/28 @@ -27,24 +30,60 @@ public class UserNotificationAuthController { @Autowired private UserNotificationAuthorizationService userNotificationAuthorizationService; + + @Autowired + private ScenicRepository scenicRepository; /** - * 记录用户通知授权 + * 记录用户通知授权 - 支持批量授权 * 用户主动同意通知授权时调用 */ @PostMapping("/record") public ApiResponse recordAuthorization( - @Valid @RequestBody NotificationAuthRecordReq req) { - Long memberId = Long.parseLong(BaseContextHandler.getUserId()); - log.info("记录用户通知授权: memberId={}, templateId={}, scenicId={}", - memberId, req.getTemplateId(), req.getScenicId()); + @RequestBody NotificationAuthRecordReq req) { + log.info("记录用户通知授权: templateIds={}, scenicId={}", req.getTemplateIds(), req.getScenicId()); try { - UserNotificationAuthorizationEntity record = userNotificationAuthorizationService - .recordAuthorization(memberId, req.getTemplateId(), req.getScenicId()); + // 获取当前用户ID + Long memberId = JwtTokenUtil.getWorker().getUserId(); + + // 调用批量授权记录方法 + List records = + userNotificationAuthorizationService.batchRecordAuthorization( + memberId, req.getTemplateIds(), req.getScenicId()); NotificationAuthRecordResp resp = new NotificationAuthRecordResp(); - BeanUtils.copyProperties(record, resp); + + // 转换响应数据 + List successRecords = new ArrayList<>(); + List failedTemplateIds = new ArrayList<>(); + List 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) { @@ -52,4 +91,86 @@ public class UserNotificationAuthController { return ApiResponse.fail("记录授权失败: " + e.getMessage()); } } + + /** + * 获取景区通知模板ID及用户授权余额 + * 复制AppWxNotifyController中的逻辑,并额外返回用户对应的授权余额 + */ + @GetMapping("/{scenicId}/templates") + public ApiResponse getScenicTemplatesWithAuth(@PathVariable("scenicId") Long scenicId) { + log.info("获取景区通知模板ID及用户授权余额: scenicId={}", scenicId); + + try { + // 获取当前用户ID + Long memberId = JwtTokenUtil.getWorker().getUserId(); + + // 获取景区的所有模板ID(复制自AppWxNotifyController的逻辑) + List 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 templateAuthInfos = new ArrayList<>(); + for (String templateId : templateIds) { + ScenicTemplateAuthResp.TemplateAuthInfo templateAuthInfo = + new ScenicTemplateAuthResp.TemplateAuthInfo(); + templateAuthInfo.setTemplateId(templateId); + + // 获取授权详情 + 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()); + } + } } \ No newline at end of file diff --git a/src/main/java/com/ycwl/basic/model/mobile/notify/req/NotificationAuthRecordReq.java b/src/main/java/com/ycwl/basic/model/mobile/notify/req/NotificationAuthRecordReq.java index 2b3c1068..37324387 100644 --- a/src/main/java/com/ycwl/basic/model/mobile/notify/req/NotificationAuthRecordReq.java +++ b/src/main/java/com/ycwl/basic/model/mobile/notify/req/NotificationAuthRecordReq.java @@ -2,11 +2,12 @@ package com.ycwl.basic.model.mobile.notify.req; import lombok.Data; -import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; +import java.util.List; /** - * 通知授权记录请求 + * 通知授权记录请求 - 支持批量授权 * * @Author: System * @Date: 2024/12/28 @@ -15,10 +16,10 @@ import jakarta.validation.constraints.NotNull; public class NotificationAuthRecordReq { /** - * 通知模板ID + * 通知模板ID列表 - 支持批量授权 */ - @NotBlank(message = "模板ID不能为空") - private String templateId; + @NotEmpty(message = "模板ID列表不能为空") + private List templateIds; /** * 景区ID diff --git a/src/main/java/com/ycwl/basic/model/mobile/notify/resp/NotificationAuthRecordResp.java b/src/main/java/com/ycwl/basic/model/mobile/notify/resp/NotificationAuthRecordResp.java index 89db3a42..e63eab98 100644 --- a/src/main/java/com/ycwl/basic/model/mobile/notify/resp/NotificationAuthRecordResp.java +++ b/src/main/java/com/ycwl/basic/model/mobile/notify/resp/NotificationAuthRecordResp.java @@ -3,9 +3,10 @@ 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 @@ -14,57 +15,78 @@ import java.util.Date; public class NotificationAuthRecordResp { /** - * 记录ID + * 是否全部成功 */ - private Long id; + private Boolean allSuccess; /** - * 模板ID + * 成功记录的授权列表 */ - private String templateId; + private List successRecords; /** - * 景区ID + * 失败的模板ID列表 */ - private Long scenicId; + private List failedTemplateIds; /** - * 景区名称 + * 失败原因列表(对应failedTemplateIds) */ - private String scenicName; + private List failureReasons; /** - * 授权次数 + * 授权记录详情 */ - private Integer authorizationCount; - - /** - * 已消费次数 - */ - private Integer consumedCount; - - /** - * 剩余授权次数 - */ - private Integer remainingCount; - - /** - * 最后授权时间 - */ - private Date lastAuthorizedTime; - - /** - * 最后消费时间 - */ - private Date lastConsumedTime; - - /** - * 状态 - */ - private Integer status; - - /** - * 创建时间 - */ - private Date createTime; + @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; + } } \ No newline at end of file diff --git a/src/main/java/com/ycwl/basic/model/mobile/notify/resp/ScenicTemplateAuthResp.java b/src/main/java/com/ycwl/basic/model/mobile/notify/resp/ScenicTemplateAuthResp.java new file mode 100644 index 00000000..1dd8fcb4 --- /dev/null +++ b/src/main/java/com/ycwl/basic/model/mobile/notify/resp/ScenicTemplateAuthResp.java @@ -0,0 +1,56 @@ +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 templates; + + /** + * 模板授权信息 + */ + @Data + public static class TemplateAuthInfo { + /** + * 模板ID + */ + private String templateId; + + /** + * 剩余授权次数 + */ + private Integer remainingCount; + + /** + * 是否有授权(剩余次数 > 0) + */ + private Boolean hasAuthorization; + + /** + * 授权次数 + */ + private Integer authorizationCount; + + /** + * 已消费次数 + */ + private Integer consumedCount; + } +} \ No newline at end of file diff --git a/src/main/java/com/ycwl/basic/service/UserNotificationAuthorizationService.java b/src/main/java/com/ycwl/basic/service/UserNotificationAuthorizationService.java index 8c4fa1cf..3adf0125 100644 --- a/src/main/java/com/ycwl/basic/service/UserNotificationAuthorizationService.java +++ b/src/main/java/com/ycwl/basic/service/UserNotificationAuthorizationService.java @@ -44,6 +44,16 @@ public interface UserNotificationAuthorizationService { */ UserNotificationAuthorizationEntity recordAuthorization(Long memberId, String templateId, Long scenicId); + /** + * 批量记录用户授权 + * + * @param memberId 用户ID + * @param templateIds 模板ID列表 + * @param scenicId 景区ID + * @return 批量授权记录结果 + */ + List batchRecordAuthorization(Long memberId, List templateIds, Long scenicId); + /** * 消费一次授权 * @@ -106,6 +116,50 @@ public interface UserNotificationAuthorizationService { */ 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 */ diff --git a/src/main/java/com/ycwl/basic/service/impl/UserNotificationAuthorizationServiceImpl.java b/src/main/java/com/ycwl/basic/service/impl/UserNotificationAuthorizationServiceImpl.java index 539f4300..57b7402a 100644 --- a/src/main/java/com/ycwl/basic/service/impl/UserNotificationAuthorizationServiceImpl.java +++ b/src/main/java/com/ycwl/basic/service/impl/UserNotificationAuthorizationServiceImpl.java @@ -82,6 +82,48 @@ public class UserNotificationAuthorizationServiceImpl implements UserNotificatio } } + @Override + @Transactional(rollbackFor = Exception.class) + public List batchRecordAuthorization(Long memberId, List templateIds, Long scenicId) { + log.info("批量记录用户授权: memberId={}, templateIds={}, scenicId={}", memberId, templateIds, scenicId); + + List 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) { diff --git a/src/main/java/com/ycwl/basic/utils/NotificationAuthUtils.java b/src/main/java/com/ycwl/basic/utils/NotificationAuthUtils.java new file mode 100644 index 00000000..873e2ad6 --- /dev/null +++ b/src/main/java/com/ycwl/basic/utils/NotificationAuthUtils.java @@ -0,0 +1,165 @@ +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) { + 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 batchRecordAuthorization( + Long memberId, List 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; + } + } +} \ No newline at end of file