diff --git a/pom.xml b/pom.xml index 7293937..84f973d 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ com.imyeyu.timiserverapi TimiServerAPI - 1.0.16 + 1.0.17 jar TimiServerAPI imyeyu.com API diff --git a/src/main/java/com/imyeyu/api/bean/CorePermissionCode.java b/src/main/java/com/imyeyu/api/bean/CorePermissionCode.java index 652cdb4..2668094 100644 --- a/src/main/java/com/imyeyu/api/bean/CorePermissionCode.java +++ b/src/main/java/com/imyeyu/api/bean/CorePermissionCode.java @@ -50,6 +50,11 @@ public enum CorePermissionCode implements BuiltinPermissionCode { USER_UPDATE("USER:UPDATE", "用户修改", false, true), USER_DELETE("USER:DELETE", "用户删除", false, true), + FEEDBACK_CREATE("FEEDBACK:CREATE", "反馈创建", false, true), + FEEDBACK_READ("FEEDBACK:READ", "反馈读取", false, true), + FEEDBACK_UPDATE("FEEDBACK:UPDATE", "反馈修改", false, true), + FEEDBACK_DELETE("FEEDBACK:DELETE", "反馈删除", false, true), + USER_ROLE_CREATE("USER_ROLE:CREATE", "用户角色创建", true, true), USER_ROLE_READ("USER_ROLE:READ", "用户角色读取", true, true), USER_ROLE_DELETE("USER_ROLE:DELETE", "用户角色删除", true, true); diff --git a/src/main/java/com/imyeyu/api/modules/common/controller/FeedbackController.java b/src/main/java/com/imyeyu/api/modules/common/controller/FeedbackController.java new file mode 100644 index 0000000..9ae8e40 --- /dev/null +++ b/src/main/java/com/imyeyu/api/modules/common/controller/FeedbackController.java @@ -0,0 +1,116 @@ +package com.imyeyu.api.modules.common.controller; + +import com.fasterxml.jackson.annotation.JsonView; +import com.imyeyu.api.annotation.RequireCorePermission; +import com.imyeyu.api.bean.CorePermissionCode; +import com.imyeyu.api.modules.common.entity.Feedback; +import com.imyeyu.api.modules.common.service.FeedbackService; +import com.imyeyu.api.modules.user.service.UserLoginService; +import com.imyeyu.spring.TimiSpring; +import com.imyeyu.spring.annotation.AOPLog; +import com.imyeyu.spring.annotation.CaptchaValid; +import com.imyeyu.spring.annotation.RequestRateLimit; +import com.imyeyu.spring.bean.CaptchaData; +import com.imyeyu.spring.bean.Page; +import com.imyeyu.spring.bean.PageResult; +import com.imyeyu.spring.util.ResponseView; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +/// 通用反馈接口 +/// +/// @author Codex +/// @since 2026-08-19 +@RestController +@RequiredArgsConstructor +@RequestMapping("/feedback") +public class FeedbackController { + + private final FeedbackService service; + private final UserLoginService userLoginService; + + /// 提交反馈。登录用户和匿名用户均需提交图形验证码,匿名用户按 IP 限流 + /// + /// @param req 带图形验证码的反馈请求 + /// @return 已创建反馈 + @AOPLog + @JsonView(ResponseView.Public.class) + @CaptchaValid + @RequestRateLimit(value = 3, inSeconds = 60) + @PostMapping("/submit") + public Feedback submit(@RequestBody @Valid CaptchaData req) { + Feedback feedback = req.getData(); + feedback.setIp(TimiSpring.getRequestIP()); + if (userLoginService.getLoginUser() != null) { + feedback.setUserId(userLoginService.getLoginUser().getId()); + } + service.create(feedback); + return feedback; + } + + /// 查询反馈列表 + /// + /// @param page 分页参数 + /// @return 反馈分页 + @JsonView(ResponseView.Admin.class) + @RequestRateLimit + @RequireCorePermission(CorePermissionCode.FEEDBACK_READ) + @PostMapping("/list") + public PageResult list(@RequestBody Page page) { + return service.page(page); + } + + /// 查询反馈详情 + /// + /// @param id 反馈 ID + /// @return 反馈详情 + @JsonView(ResponseView.Admin.class) + @RequestRateLimit + @RequireCorePermission(CorePermissionCode.FEEDBACK_READ) + @PostMapping("/detail") + public Feedback detail(@RequestParam String id) { + return service.get(id); + } + + /// 后台创建反馈 + /// + /// @param feedback 反馈 + /// @return 反馈 + @AOPLog + @JsonView(ResponseView.Admin.class) + @RequestRateLimit + @RequireCorePermission(CorePermissionCode.FEEDBACK_CREATE) + @PostMapping("/create") + public Feedback create(@RequestBody Feedback feedback) { + service.create(feedback); + return feedback; + } + + /// 更新反馈 + /// + /// @param feedback 反馈 + @AOPLog + @RequestRateLimit + @RequireCorePermission(CorePermissionCode.FEEDBACK_UPDATE) + @PostMapping("/update") + public void update(@RequestBody Feedback feedback) { + feedback.setRespondedBy(userLoginService.getRequireLoginUserId()); + service.update(feedback); + } + + /// 删除反馈 + /// + /// @param id 反馈 ID + @AOPLog + @RequestRateLimit + @RequireCorePermission(CorePermissionCode.FEEDBACK_DELETE) + @PostMapping("/delete") + public void delete(@RequestParam String id) { + service.delete(id); + } +} diff --git a/src/main/java/com/imyeyu/api/modules/common/entity/Article.java b/src/main/java/com/imyeyu/api/modules/common/entity/Article.java index ce3353a..52b9973 100644 --- a/src/main/java/com/imyeyu/api/modules/common/entity/Article.java +++ b/src/main/java/com/imyeyu/api/modules/common/entity/Article.java @@ -28,7 +28,10 @@ public class Article extends UUIDEntity implements SettingSupport { public enum BizType { /** 博客 */ - BLOG + BLOG, + + /// 反馈 + FEEDBACK } /** @@ -79,4 +82,7 @@ public class Article extends UUIDEntity implements SettingSupport { @Transient protected List commentList; + + @Transient + protected List attachmentList; } diff --git a/src/main/java/com/imyeyu/api/modules/common/entity/Attachment.java b/src/main/java/com/imyeyu/api/modules/common/entity/Attachment.java index 3ef00fe..8b829bb 100644 --- a/src/main/java/com/imyeyu/api/modules/common/entity/Attachment.java +++ b/src/main/java/com/imyeyu/api/modules/common/entity/Attachment.java @@ -30,6 +30,8 @@ public class Attachment extends UUIDEntity { /// @since 2026-07-17 20:52 public enum BizType { + ARTICLE, + USER, GAO_CUSTOMER, diff --git a/src/main/java/com/imyeyu/api/modules/common/entity/Feedback.java b/src/main/java/com/imyeyu/api/modules/common/entity/Feedback.java new file mode 100644 index 0000000..98bc433 --- /dev/null +++ b/src/main/java/com/imyeyu/api/modules/common/entity/Feedback.java @@ -0,0 +1,79 @@ +package com.imyeyu.api.modules.common.entity; + +import com.fasterxml.jackson.annotation.JsonView; +import com.imyeyu.api.bean.ModuleCode; +import com.imyeyu.spring.annotation.table.Transient; +import com.imyeyu.spring.entity.UUIDEntity; +import com.imyeyu.spring.util.ResponseView; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/// 通用反馈 +/// +/// @author Codex +/// @since 2026-08-19 +@Data +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class Feedback extends UUIDEntity { + + /// 反馈处理状态 + public enum Status { + + /// 待处理 + PENDING, + + /// 已回复 + REPLIED, + + /// 已关闭 + CLOSED + } + + /// 所属模块 + @JsonView(ResponseView.Public.class) + private ModuleCode module; + + /// 提交用户 ID,匿名反馈为 null + @JsonView(ResponseView.Admin.class) + private String userId; + + /// 反馈内容文章 ID + @JsonView(ResponseView.Public.class) + private String contentId; + + /// 联系方式 + @JsonView(ResponseView.Public.class) + private String contact; + + /// 提交 IP + @JsonView(ResponseView.Admin.class) + private String ip; + + /// 处理状态 + @JsonView(ResponseView.Public.class) + private Status status; + + /// 管理员回复文章 ID + @JsonView(ResponseView.Public.class) + private String responseId; + + /// 回复用户 ID + @JsonView(ResponseView.Admin.class) + private String respondedBy; + + /// 回复时间 + @JsonView(ResponseView.Public.class) + private Long respondedAt; + + /// 管理员回复文章 + @Transient + @JsonView(ResponseView.Public.class) + private Article content; + + /// 管理员回复文章 + @Transient + @JsonView(ResponseView.Public.class) + private Article response; +} diff --git a/src/main/java/com/imyeyu/api/modules/common/mapper/AttachmentMapper.java b/src/main/java/com/imyeyu/api/modules/common/mapper/AttachmentMapper.java index 6c5d57f..bed703e 100644 --- a/src/main/java/com/imyeyu/api/modules/common/mapper/AttachmentMapper.java +++ b/src/main/java/com/imyeyu/api/modules/common/mapper/AttachmentMapper.java @@ -32,7 +32,7 @@ public interface AttachmentMapper extends BaseMapper, RawMap List selectByBizId(Attachment.BizType bizType, String bizId, List attachTypes, Page page); - List selectByBizIdList(Attachment.BizType bizType, Collection bizIdList, List attachTypes); + List selectAllByBizIdList(Attachment.BizType bizType, Collection bizIdList, List attachTypes); long countByBizId(Attachment.BizType bizType, String bizId, List attachTypes); diff --git a/src/main/java/com/imyeyu/api/modules/common/mapper/FeedbackMapper.java b/src/main/java/com/imyeyu/api/modules/common/mapper/FeedbackMapper.java new file mode 100644 index 0000000..d96bf9c --- /dev/null +++ b/src/main/java/com/imyeyu/api/modules/common/mapper/FeedbackMapper.java @@ -0,0 +1,11 @@ +package com.imyeyu.api.modules.common.mapper; + +import com.imyeyu.api.modules.common.entity.Feedback; +import com.imyeyu.spring.mapper.BaseMapper; + +/// 通用反馈 Mapper +/// +/// @author Codex +/// @since 2026-08-19 +public interface FeedbackMapper extends BaseMapper { +} diff --git a/src/main/java/com/imyeyu/api/modules/common/service/FeedbackService.java b/src/main/java/com/imyeyu/api/modules/common/service/FeedbackService.java new file mode 100644 index 0000000..0bc4524 --- /dev/null +++ b/src/main/java/com/imyeyu/api/modules/common/service/FeedbackService.java @@ -0,0 +1,11 @@ +package com.imyeyu.api.modules.common.service; + +import com.imyeyu.api.modules.common.entity.Feedback; +import com.imyeyu.spring.service.BaseService; + +/// 通用反馈服务 +/// +/// @author Codex +/// @since 2026-08-19 +public interface FeedbackService extends BaseService { +} diff --git a/src/main/java/com/imyeyu/api/modules/common/service/implement/ArticleServiceImplement.java b/src/main/java/com/imyeyu/api/modules/common/service/implement/ArticleServiceImplement.java index 616b3b4..9f8b58a 100644 --- a/src/main/java/com/imyeyu/api/modules/common/service/implement/ArticleServiceImplement.java +++ b/src/main/java/com/imyeyu/api/modules/common/service/implement/ArticleServiceImplement.java @@ -1,8 +1,15 @@ package com.imyeyu.api.modules.common.service.implement; import com.imyeyu.api.modules.common.entity.Article; +import com.imyeyu.api.modules.common.entity.Attachment; import com.imyeyu.api.modules.common.mapper.ArticleMapper; import com.imyeyu.api.modules.common.service.ArticleService; +import com.imyeyu.api.modules.common.service.AttachmentService; +import com.imyeyu.api.modules.system.entity.Logger; +import com.imyeyu.api.modules.system.service.LoggerService; +import com.imyeyu.java.TimiJava; +import com.imyeyu.java.bean.timi.TimiCode; +import com.imyeyu.java.bean.timi.TimiException; import com.imyeyu.spring.mapper.BaseMapper; import com.imyeyu.spring.service.AbstractEntityService; import lombok.RequiredArgsConstructor; @@ -20,10 +27,57 @@ import org.springframework.stereotype.Service; @RequiredArgsConstructor public class ArticleServiceImplement extends AbstractEntityService implements ArticleService { + private final LoggerService loggerService; + private final AttachmentService attachmentService; + private final ArticleMapper mapper; @Override protected BaseMapper mapper() { return mapper; } + + @Override + public void create(Article article) { + Logger logger = new Logger(Logger.Module.COMMON, "COMMON_ARTICLE_CREATE"); + try { + super.create(article); + attachmentService.updateByBizId(Attachment.BizType.ARTICLE, article.getId(), article.getAttachmentList()); + logger.setLevel(Logger.Level.INFO); + logger.setResult(article.getId()); + } catch (TimiException e) { + logger.setLevel(Logger.Level.WARN); + logger.setException(e.getMessage()); + throw e; + } catch (Exception e) { + logger.setLevel(Logger.Level.ERROR); + logger.setException(TimiJava.serializeThrowable(e)); + log.error("create article error", e); + throw new TimiException(TimiCode.ERROR, "create article error", e); + } finally { + loggerService.create(logger); + } + } + + @Override + public void update(Article article) { + Logger logger = new Logger(Logger.Module.COMMON, "COMMON_ARTICLE_UPDATE"); + try { + super.update(article); + attachmentService.updateByBizId(Attachment.BizType.ARTICLE, article.getId(), article.getAttachmentList()); + logger.setLevel(Logger.Level.INFO); + logger.setResult(article.getId()); + } catch (TimiException e) { + logger.setLevel(Logger.Level.WARN); + logger.setException(e.getMessage()); + throw e; + } catch (Exception e) { + logger.setLevel(Logger.Level.ERROR); + logger.setException(TimiJava.serializeThrowable(e)); + log.error("update article error", e); + throw new TimiException(TimiCode.ERROR, "update article error", e); + } finally { + loggerService.create(logger); + } + } } diff --git a/src/main/java/com/imyeyu/api/modules/common/service/implement/AttachmentServiceImplement.java b/src/main/java/com/imyeyu/api/modules/common/service/implement/AttachmentServiceImplement.java index 701d4db..9bc93c2 100644 --- a/src/main/java/com/imyeyu/api/modules/common/service/implement/AttachmentServiceImplement.java +++ b/src/main/java/com/imyeyu/api/modules/common/service/implement/AttachmentServiceImplement.java @@ -319,12 +319,12 @@ public class AttachmentServiceImplement extends AbstractEntityService listByBizId(Attachment.BizType bizType, String bizId, String... attachTypes) { - return mapper.selectByBizId(bizType, bizId, List.of(attachTypes), null); + return mapper.selectAllByBizIdList(bizType, List.of(bizId), List.of(attachTypes)); } @Override public Map> mapByBizIdList(Attachment.BizType bizType, Collection bizIdList, String... attachTypes) { - return mapper.selectByBizIdList(bizType, bizIdList, List.of(attachTypes)).stream().collect(Collectors.groupingBy(Attachment::getBizId)); + return mapper.selectAllByBizIdList(bizType, bizIdList, List.of(attachTypes)).stream().collect(Collectors.groupingBy(Attachment::getBizId)); } @Override diff --git a/src/main/java/com/imyeyu/api/modules/common/service/implement/FeedbackServiceImplement.java b/src/main/java/com/imyeyu/api/modules/common/service/implement/FeedbackServiceImplement.java new file mode 100644 index 0000000..5383458 --- /dev/null +++ b/src/main/java/com/imyeyu/api/modules/common/service/implement/FeedbackServiceImplement.java @@ -0,0 +1,141 @@ +package com.imyeyu.api.modules.common.service.implement; + +import com.imyeyu.api.config.dbsource.TimiServerDBConfig; +import com.imyeyu.api.modules.common.entity.Feedback; +import com.imyeyu.api.modules.common.mapper.FeedbackMapper; +import com.imyeyu.api.modules.common.service.ArticleService; +import com.imyeyu.api.modules.common.service.AttachmentService; +import com.imyeyu.api.modules.common.service.FeedbackService; +import com.imyeyu.api.modules.system.entity.Logger; +import com.imyeyu.api.modules.system.service.LoggerService; +import com.imyeyu.api.modules.user.service.UserLoginService; +import com.imyeyu.java.TimiJava; +import com.imyeyu.java.bean.timi.TimiCode; +import com.imyeyu.java.bean.timi.TimiException; +import com.imyeyu.spring.mapper.BaseMapper; +import com.imyeyu.spring.service.AbstractEntityService; +import com.imyeyu.utils.Time; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/// 通用反馈服务实现 +/// +/// @author Codex +/// @since 2026-08-19 +@Service +@RequiredArgsConstructor +@Slf4j +public class FeedbackServiceImplement extends AbstractEntityService implements FeedbackService { + + private static final long ANONYMOUS_ATTACHMENT_MAX_SIZE = 500L * 1024; + + private final LoggerService loggerService; + private final ArticleService articleService; + private final UserLoginService userLoginService; + private final AttachmentService attachmentService; + + private final FeedbackMapper mapper; + + @Override + protected BaseMapper mapper() { + return mapper; + } + + @Transactional(TimiServerDBConfig.ROLLBACKER) + @Override + public void create(Feedback feedback) { + Logger logger = new Logger(Logger.Module.COMMON, "COMMON_FEEDBACK_CREATE"); + try { + TimiException.required(feedback, "未找到反馈"); + articleService.create(feedback.getContent()); + feedback.setContentId(feedback.getContent().getId()); + feedback.setStatus(TimiJava.defaultIfNull(feedback.getStatus(), Feedback.Status.PENDING)); + super.create(feedback); + logger.setLevel(Logger.Level.INFO); + logger.setResult(feedback.getId()); + } catch (TimiException e) { + logger.setLevel(Logger.Level.WARN); + logger.setException(e.getMessage()); + throw e; + } catch (Exception e) { + logger.setLevel(Logger.Level.ERROR); + logger.setException(TimiJava.serializeThrowable(e)); + log.error("create feedback error", e); + throw new TimiException(TimiCode.ERROR, "create feedback error", e); + } finally { + loggerService.create(logger); + } + } + + @Transactional(TimiServerDBConfig.ROLLBACKER) + @Override + public void update(Feedback feedback) { + Logger logger = new Logger(Logger.Module.COMMON, "COMMON_FEEDBACK_UPDATE"); + try { + TimiException.required(feedback, "未找到反馈"); + TimiException.required(feedback.getId(), "未找到反馈 ID"); + Feedback dbFeedback = get(feedback.getId()); + TimiException.required(dbFeedback, "未找到反馈"); + + feedback.getContent().setId(dbFeedback.getContentId()); + articleService.update(feedback.getContent()); + + dbFeedback.setModule(TimiJava.defaultIfNull(feedback.getModule(), dbFeedback.getModule())); + if (TimiJava.isNotEmpty(feedback.getResponse())) { + if (TimiJava.isEmpty(feedback.getResponseId())) { + articleService.create(feedback.getResponse()); + dbFeedback.setResponseId(feedback.getResponse().getId()); + dbFeedback.setRespondedAt(Time.now()); + } else { + feedback.getResponse().setId(dbFeedback.getResponseId()); + articleService.update(feedback.getResponse()); + } + dbFeedback.setStatus(Feedback.Status.REPLIED); + dbFeedback.setRespondedBy(userLoginService.getRequireLoginUserId()); + } + super.update(dbFeedback); + logger.setLevel(Logger.Level.INFO); + logger.setResult(feedback.getId()); + } catch (TimiException e) { + logger.setLevel(Logger.Level.WARN); + logger.setException(e.getMessage()); + throw e; + } catch (Exception e) { + logger.setLevel(Logger.Level.ERROR); + logger.setException(TimiJava.serializeThrowable(e)); + log.error("修改反馈失败", e); + throw new TimiException(TimiCode.ERROR, "修改反馈失败", e); + } finally { + loggerService.create(logger); + } + } + + @Transactional(TimiServerDBConfig.ROLLBACKER) + @Override + public void delete(String id) { + Logger logger = new Logger(Logger.Module.COMMON, "COMMON_FEEDBACK_DELETE"); + try { + Feedback feedback = get(id); + articleService.delete(feedback.getContentId()); + if (TimiJava.isNotEmpty(feedback.getResponseId())) { + articleService.delete(feedback.getResponseId()); + } + super.delete(id); + logger.setLevel(Logger.Level.INFO); + logger.setResult(id); + } catch (TimiException e) { + logger.setLevel(Logger.Level.WARN); + logger.setException(e.getMessage()); + throw e; + } catch (Exception e) { + logger.setLevel(Logger.Level.ERROR); + logger.setException(TimiJava.serializeThrowable(e)); + log.error("delete feedback error", e); + throw new TimiException(TimiCode.ERROR, "delete feedback error", e); + } finally { + loggerService.create(logger); + } + } +} diff --git a/src/main/java/com/imyeyu/api/modules/gao/controller/GaoPointRuleController.java b/src/main/java/com/imyeyu/api/modules/gao/controller/GaoPointRuleController.java index 02c6e9d..e786ca1 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/controller/GaoPointRuleController.java +++ b/src/main/java/com/imyeyu/api/modules/gao/controller/GaoPointRuleController.java @@ -21,6 +21,8 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import java.util.List; + /// GAO 客户积分规则接口 /// /// @author Codex @@ -78,6 +80,19 @@ public class GaoPointRuleController { ruleService.update(rule); } + @AOPLog + @RequestRateLimit + @RequireGaoPermission(GaoPermissionCode.POINT_RULE_UPDATE) + @PostMapping("/rule/sort") + public void sort(@RequestBody List idList) { + if (idList != null) { + for (GaoPointRule rule : ruleService.listByIdList(idList)) { + checkStore(rule.getStoreId()); + } + } + ruleService.sort(idList); + } + @AOPLog @RequestRateLimit @RequireGaoPermission(GaoPermissionCode.POINT_RULE_DELETE) diff --git a/src/main/java/com/imyeyu/api/modules/gao/controller/GaoStoreChartController.java b/src/main/java/com/imyeyu/api/modules/gao/controller/GaoStoreChartController.java index 88a004e..b68c4b0 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/controller/GaoStoreChartController.java +++ b/src/main/java/com/imyeyu/api/modules/gao/controller/GaoStoreChartController.java @@ -7,6 +7,7 @@ import com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStateView; import com.imyeyu.api.modules.gao.vo.GaoCustomerGenderStatView; import com.imyeyu.api.modules.gao.vo.GaoEventRecordCalendarView; import com.imyeyu.api.modules.gao.vo.GaoEventRecordDailyStatView; +import com.imyeyu.api.modules.gao.vo.GaoEventRecordNumberValueDailyStatView; import com.imyeyu.api.modules.gao.vo.GaoStoreChartReq; import com.imyeyu.spring.annotation.AOPLog; import com.imyeyu.spring.annotation.RequestRateLimit; @@ -57,6 +58,15 @@ public class GaoStoreChartController { return service.eventRecordDailyStat(req); } + /// 查询登记事件每日数值合计 + @AOPLog + @RequestRateLimit + @RequireGaoPermission(GaoPermissionCode.STAT_READ) + @PostMapping("/event/record/number-value/daily") + public List eventRecordNumberValueDaily(@RequestBody GaoStoreChartReq req) { + return service.eventRecordNumberValueDailyStat(req); + } + /// 查询登记事件日历记录 @AOPLog @RequestRateLimit diff --git a/src/main/java/com/imyeyu/api/modules/gao/controller/GaoUserController.java b/src/main/java/com/imyeyu/api/modules/gao/controller/GaoUserController.java index 65570ba..ced8d8b 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/controller/GaoUserController.java +++ b/src/main/java/com/imyeyu/api/modules/gao/controller/GaoUserController.java @@ -123,7 +123,9 @@ public class GaoUserController { { User user = userService.get(result.getUserId()); user.setAttachmentList(attachmentService.listByBizId(Attachment.BizType.USER, user.getId())); + user.setRoleList(userRoleService.listAllRoleCodeByUserId(user.getId())); user.setRoleEntityList(userRoleService.listRoleByUserId(user.getId())); + user.setPermissionList(userRoleService.listAllPermissionCodeByUserId(user.getId())); result.setUser(user); } return result; diff --git a/src/main/java/com/imyeyu/api/modules/gao/entity/GaoCustomer.java b/src/main/java/com/imyeyu/api/modules/gao/entity/GaoCustomer.java index 36573a3..d4613fc 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/entity/GaoCustomer.java +++ b/src/main/java/com/imyeyu/api/modules/gao/entity/GaoCustomer.java @@ -30,7 +30,11 @@ public class GaoCustomer extends UUIDEntity { /// @since 2026-07-27 public enum AttachType { - PHOTO + PHOTO, + + POINT_CARD, + + OTHER } /// diff --git a/src/main/java/com/imyeyu/api/modules/gao/entity/GaoPointLedger.java b/src/main/java/com/imyeyu/api/modules/gao/entity/GaoPointLedger.java index 1e63444..00ab9ab 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/entity/GaoPointLedger.java +++ b/src/main/java/com/imyeyu/api/modules/gao/entity/GaoPointLedger.java @@ -17,7 +17,7 @@ public class GaoPointLedger extends UUIDEntity { /// 流水变化类型 public enum ChangeType { - /// 登记事件获得积分 + /// 登记事件积分变化 EARN, /// 删除登记记录撤销积分 diff --git a/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoEventRecordMapper.java b/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoEventRecordMapper.java index 07d79d4..5e7a644 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoEventRecordMapper.java +++ b/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoEventRecordMapper.java @@ -5,6 +5,7 @@ import com.imyeyu.api.modules.gao.vo.GaoCustomerChartReq; import com.imyeyu.api.modules.gao.vo.GaoCustomerEventRankView; import com.imyeyu.api.modules.gao.vo.GaoEventRecordCalendarView; import com.imyeyu.api.modules.gao.vo.GaoEventRecordDailyStatView; +import com.imyeyu.api.modules.gao.vo.GaoEventRecordNumberValueDailyStatView; import com.imyeyu.api.modules.gao.vo.GaoEventRecordPage; import com.imyeyu.api.modules.gao.vo.GaoEventRecordRankPage; import com.imyeyu.api.modules.gao.vo.GaoStoreChartReq; @@ -88,6 +89,9 @@ public interface GaoEventRecordMapper extends BaseMapper /// @return 每日登记数 List stateDailyByRange(GaoStoreChartReq req); + /// 按时间范围和事件统计每日登记数值合计 + List stateNumberValueDailyByRange(GaoStoreChartReq req); + /// 按时间范围查询登记日历记录 /// /// @param req 门店报表查询请求 diff --git a/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoPointRuleMapper.java b/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoPointRuleMapper.java index 15a5807..97a1fe1 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoPointRuleMapper.java +++ b/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoPointRuleMapper.java @@ -4,6 +4,7 @@ import com.imyeyu.api.modules.gao.entity.GaoPointRule; import com.imyeyu.spring.mapper.BaseMapper; import org.apache.ibatis.annotations.Param; +import java.util.Collection; import java.util.List; /// GAO 客户积分规则 Mapper @@ -12,6 +13,8 @@ import java.util.List; /// @since 2026-08-11 public interface GaoPointRuleMapper extends BaseMapper { + List selectByIdList(Collection idList); + /// 查询当前时间可匹配的启用规则 /// /// @param storeId 门店 ID diff --git a/src/main/java/com/imyeyu/api/modules/gao/service/GaoPointRuleService.java b/src/main/java/com/imyeyu/api/modules/gao/service/GaoPointRuleService.java index 5dbc7ff..7101126 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/service/GaoPointRuleService.java +++ b/src/main/java/com/imyeyu/api/modules/gao/service/GaoPointRuleService.java @@ -19,6 +19,17 @@ public interface GaoPointRuleService extends BaseService { /// @return 规则列表 List listActive(String storeId, String eventId, long now); + /// 查询规则 ID 列表 + /// + /// @param idList 规则 ID 列表 + /// @return 规则列表 + List listByIdList(List idList); + + /// 按客户端提交的顺序更新启用规则优先级 + /// + /// @param idList 规则 ID 列表 + void sort(List idList); + /// 创建积分规则 /// /// @param rule 积分规则 diff --git a/src/main/java/com/imyeyu/api/modules/gao/service/GaoStoreChartService.java b/src/main/java/com/imyeyu/api/modules/gao/service/GaoStoreChartService.java index da556d5..cc2e6e1 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/service/GaoStoreChartService.java +++ b/src/main/java/com/imyeyu/api/modules/gao/service/GaoStoreChartService.java @@ -4,6 +4,7 @@ import com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStateView; import com.imyeyu.api.modules.gao.vo.GaoCustomerGenderStatView; import com.imyeyu.api.modules.gao.vo.GaoEventRecordCalendarView; import com.imyeyu.api.modules.gao.vo.GaoEventRecordDailyStatView; +import com.imyeyu.api.modules.gao.vo.GaoEventRecordNumberValueDailyStatView; import com.imyeyu.api.modules.gao.vo.GaoStoreChartReq; import java.util.List; @@ -23,6 +24,9 @@ public interface GaoStoreChartService { /// 查询登记事件每日统计 List eventRecordDailyStat(GaoStoreChartReq req); + /// 查询登记事件每日数值合计 + List eventRecordNumberValueDailyStat(GaoStoreChartReq req); + /// 查询登记事件日历记录 List eventRecordCalendar(GaoStoreChartReq req); } diff --git a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoEventRecordServiceImplement.java b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoEventRecordServiceImplement.java index 14afa78..3c68083 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoEventRecordServiceImplement.java +++ b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoEventRecordServiceImplement.java @@ -10,9 +10,9 @@ import com.imyeyu.api.modules.gao.entity.GaoEvent; import com.imyeyu.api.modules.gao.entity.GaoEventRecord; import com.imyeyu.api.modules.gao.mapper.GaoEventRecordMapper; import com.imyeyu.api.modules.gao.service.GaoCustomerService; -import com.imyeyu.api.modules.gao.service.GaoPointTriggerService; import com.imyeyu.api.modules.gao.service.GaoEventRecordService; import com.imyeyu.api.modules.gao.service.GaoEventService; +import com.imyeyu.api.modules.gao.service.GaoPointTriggerService; import com.imyeyu.api.modules.gao.service.GaoStoreBusinessDayService; import com.imyeyu.api.modules.gao.util.EventRecordXSSFBuilder; import com.imyeyu.api.modules.gao.vo.GaoCustomerEventRankView; @@ -321,7 +321,7 @@ public class GaoEventRecordServiceImplement extends AbstractEntityService listByIdList(List idList) { + if (TimiJava.isEmpty(idList)) { + return List.of(); + } + return mapper.selectByIdList(new LinkedHashSet<>(idList)); + } + + @Transactional(TimiServerDBConfig.ROLLBACKER) + @Override + public void sort(List idList) { + if (TimiJava.isEmpty(idList)) { + return; + } + List distinctIdList = new ArrayList<>(new LinkedHashSet<>(idList)); + Map ruleMap = mapper.selectByIdList(new LinkedHashSet<>(distinctIdList)).stream() + .collect(Collectors.toMap(GaoPointRule::getId, Function.identity())); + TimiException.requiredTrue(ruleMap.size() == distinctIdList.size(), "积分规则列表包含不存在的规则"); + for (int i = 0; i < distinctIdList.size(); i++) { + GaoPointRule rule = ruleMap.get(distinctIdList.get(i)); + TimiException.requiredTrue(rule.getStatus() == GaoPointRule.Status.ACTIVE, "只能调整启用规则排序"); + rule.setPriority(i); + mapper.update(rule); + } + } + @Transactional(TimiServerDBConfig.ROLLBACKER) @Override public void create(GaoPointRule rule) { @@ -127,7 +158,7 @@ public class GaoPointRuleServiceImplement extends AbstractEntityService ruleList = ruleService.listActive(record.getStoreId(), record.getEventId(), Time.now()); Long count = null; - long earnedPointValue = 0L; + long pointDeltaTotal = 0L; for (GaoPointRule rule : ruleList) { RuleResolution resolution = resolveRule(record, rule, count); if (resolution == null) { @@ -72,16 +72,16 @@ public class GaoPointTriggerServiceImplement implements GaoPointTriggerService { count = count == null ? count(record) : count; } long pointDelta = resolution.pointDelta(); - if (0 < pointDelta) { + if (pointDelta != 0) { ledgerService.earn(record, rule, pointDelta, resolution.sourcePayload()); - earnedPointValue = Math.addExact(earnedPointValue, pointDelta); + pointDeltaTotal = Math.addExact(pointDeltaTotal, pointDelta); } if (Boolean.TRUE.equals(rule.getExclusive())) { break; } } - record.setEarnedPointValue(earnedPointValue); - eventRecordMapper.updateEarnedPointValue(record.getId(), earnedPointValue); + record.setEarnedPointValue(pointDeltaTotal); + eventRecordMapper.updateEarnedPointValue(record.getId(), pointDeltaTotal); } @Transactional(TimiServerDBConfig.ROLLBACKER) @@ -140,7 +140,7 @@ public class GaoPointTriggerServiceImplement implements GaoPointTriggerService { } permissionChecker.checkAny(ModuleCode.GAO, GaoPermissionCode.POINT_SOURCE_VALUE.getValue()); long pointValue = record.getPointValue(); - TimiException.requiredTrue(0 < pointValue, "pointValue invalid"); + TimiException.requiredTrue(pointValue != 0, "pointValue invalid"); JsonNode condition = rule.getConditionJson(); if (condition != null) { JsonNode min = condition.get("min"); diff --git a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoStoreChartServiceImplement.java b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoStoreChartServiceImplement.java index 00f02ff..f25e073 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoStoreChartServiceImplement.java +++ b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoStoreChartServiceImplement.java @@ -8,6 +8,7 @@ import com.imyeyu.api.modules.gao.vo.GaoCustomerGenderStatView; import com.imyeyu.api.modules.gao.vo.GaoCustomerTrendStateReq; import com.imyeyu.api.modules.gao.vo.GaoEventRecordCalendarView; import com.imyeyu.api.modules.gao.vo.GaoEventRecordDailyStatView; +import com.imyeyu.api.modules.gao.vo.GaoEventRecordNumberValueDailyStatView; import com.imyeyu.api.modules.gao.vo.GaoStoreChartReq; import com.imyeyu.java.bean.timi.TimiException; import lombok.RequiredArgsConstructor; @@ -48,6 +49,12 @@ public class GaoStoreChartServiceImplement implements GaoStoreChartService { return eventRecordMapper.stateDailyByRange(req); } + @Override + public List eventRecordNumberValueDailyStat(GaoStoreChartReq req) { + checkRangeReq(req); + return eventRecordMapper.stateNumberValueDailyByRange(req); + } + @Override public List eventRecordCalendar(GaoStoreChartReq req) { checkRangeReq(req); diff --git a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoEventRecordNumberValueDailyStatView.java b/src/main/java/com/imyeyu/api/modules/gao/vo/GaoEventRecordNumberValueDailyStatView.java new file mode 100644 index 0000000..6912627 --- /dev/null +++ b/src/main/java/com/imyeyu/api/modules/gao/vo/GaoEventRecordNumberValueDailyStatView.java @@ -0,0 +1,17 @@ +package com.imyeyu.api.modules.gao.vo; + +import lombok.Data; + +/// 登记事件数值每日统计视图 +@Data +public class GaoEventRecordNumberValueDailyStatView { + + /// 登记事件 ID + private String eventId; + + /// 日期值,格式为 yyyyMMdd + private Integer dateValue; + + /// 当日登记数值合计 + private Long dailyNumberValue; +} diff --git a/src/main/resources/db/migration/timiserver/V33__create_feedback_table.sql b/src/main/resources/db/migration/timiserver/V33__create_feedback_table.sql new file mode 100644 index 0000000..041c696 --- /dev/null +++ b/src/main/resources/db/migration/timiserver/V33__create_feedback_table.sql @@ -0,0 +1,21 @@ +CREATE TABLE `feedback` ( + `id` CHAR(36) NOT NULL COMMENT '主键', + `module` VARCHAR(32) NOT NULL COMMENT '所属模块', + `user_id` CHAR(36) NULL COMMENT '提交用户 ID,匿名反馈为空', + `content_id` VARCHAR(36) NOT NULL COMMENT '反馈内容文章 ID', + `contact` VARCHAR(255) NULL COMMENT '联系方式', + `ip` VARCHAR(64) NULL COMMENT '提交 IP', + `status` VARCHAR(16) NOT NULL DEFAULT 'PENDING' COMMENT '处理状态', + `response_id` VARCHAR(36) NULL COMMENT '管理员回复文章 ID', + `responded_by` CHAR(36) NULL COMMENT '回复用户 ID', + `responded_at` BIGINT NULL COMMENT '回复时间', + `created_at` BIGINT NOT NULL COMMENT '创建时间', + `updated_at` BIGINT NOT NULL COMMENT '更新时间', + `deleted_at` BIGINT NULL COMMENT '删除时间', + PRIMARY KEY (`id`), + KEY `idx_feedback_module_status_created` (`module`, `status`, `created_at`), + KEY `idx_feedback_user_created` (`user_id`, `created_at`), + KEY `idx_feedback_content_id` (`content_id`), + KEY `idx_feedback_response_id` (`response_id`), + KEY `idx_feedback_deleted_at` (`deleted_at`) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = '通用反馈'; diff --git a/src/main/resources/mapper/timi-server/common/AttachmentMapper.xml b/src/main/resources/mapper/timi-server/common/AttachmentMapper.xml index 65bbae8..2910e83 100644 --- a/src/main/resources/mapper/timi-server/common/AttachmentMapper.xml +++ b/src/main/resources/mapper/timi-server/common/AttachmentMapper.xml @@ -1,7 +1,7 @@ - SELECT * FROM `attachment` diff --git a/src/main/resources/mapper/timi-server/gao/GaoEventRecordMapper.xml b/src/main/resources/mapper/timi-server/gao/GaoEventRecordMapper.xml index 2b9cbdc..c20aca6 100644 --- a/src/main/resources/mapper/timi-server/gao/GaoEventRecordMapper.xml +++ b/src/main/resources/mapper/timi-server/gao/GaoEventRecordMapper.xml @@ -350,6 +350,22 @@ r.`registered_date_value` ASC, r.`event_id` ASC + + SELECT * + FROM gao_point_rule + WHERE deleted_at IS NULL + AND id IN + + #{id} + + +