From 1709316ce101c053debc1a5537ba46c3cda25e5d Mon Sep 17 00:00:00 2001 From: Timi Date: Thu, 6 Aug 2026 10:52:50 +0800 Subject: [PATCH] refactor gao module --- .idea/sqldialects.xml | 1 + pom.xml | 4 +- .../controller/AttachmentController.java | 26 - .../api/modules/common/entity/Attachment.java | 15 +- .../common/mapper/AttachmentMapper.java | 4 +- .../common/service/AttachmentService.java | 20 +- .../implement/AttachmentServiceImplement.java | 232 +++--- .../common/vo/attach/BizDeleteReq.java | 20 - .../common/vo/attach/BizUpdateReq.java | 26 - .../modules/gao/bean/GaoPermissionCode.java | 29 +- .../gao/controller/GaoCustomerController.java | 230 +++--- ...ontroller.java => GaoEventController.java} | 53 +- .../controller/GaoEventRecordController.java | 152 ++++ .../GaoRegistrationEventRecordController.java | 201 ----- .../gao/controller/GaoRoleController.java | 7 +- .../gao/controller/GaoUserController.java | 5 +- .../api/modules/gao/entity/GaoCustomer.java | 29 +- ...aoRegistrationEvent.java => GaoEvent.java} | 33 +- ...onEventRecord.java => GaoEventRecord.java} | 10 +- ...elation.java => GaoEventRoleRelation.java} | 4 +- .../modules/gao/mapper/GaoCustomerMapper.java | 31 +- .../modules/gao/mapper/GaoEventMapper.java | 19 + .../gao/mapper/GaoEventRecordMapper.java | 37 + .../mapper/GaoEventRoleRelationMapper.java | 16 + .../mapper/GaoRegistrationEventMapper.java | 19 - .../GaoRegistrationEventRecordMapper.java | 157 ---- ...aoRegistrationEventRoleRelationMapper.java | 36 - .../gao/service/GaoCustomerService.java | 53 +- .../gao/service/GaoEventRecordService.java | 35 + .../modules/gao/service/GaoEventService.java | 36 + .../GaoRegistrationEventRecordService.java | 127 --- .../service/GaoRegistrationEventService.java | 38 - .../GaoCustomerServiceImplement.java | 525 ++++-------- .../GaoEventRecordServiceImplement.java | 252 ++++++ .../implement/GaoEventServiceImplement.java | 215 +++++ ...gistrationEventRecordServiceImplement.java | 761 ------------------ .../GaoRegistrationEventServiceImplement.java | 260 ------ .../modules/gao/util/CustomerXSSFBuilder.java | 67 ++ .../gao/util/EventRecordXSSFBuilder.java | 53 ++ ...ew.java => GaoCustomerDailyStateView.java} | 2 +- ...iew.java => GaoCustomerEventRankView.java} | 19 +- .../gao/vo/GaoCustomerInviteUpdateReq.java | 18 - .../gao/vo/GaoCustomerRegisterStatView.java | 30 - .../GaoCustomerRegistrationDayStatView.java | 33 - .../modules/gao/vo/GaoCustomerSearchReq.java | 18 - ...Req.java => GaoCustomerTrendStateReq.java} | 2 +- ...dRangeReq.java => GaoEventRecordPage.java} | 18 +- .../gao/vo/GaoEventRecordRankPage.java | 28 + .../GaoQuickRegistrationEventRecordReq.java | 27 - .../GaoRegistrationEventPeriodStatView.java | 21 - ...aoRegistrationEventRecordCalendarView.java | 30 - .../GaoRegistrationEventRecordCreateReq.java | 30 - ...oRegistrationEventRecordDailyStatView.java | 21 - ...aoRegistrationEventRecordListItemView.java | 33 - .../GaoRegistrationEventRecordPeriodReq.java | 35 - .../vo/GaoRegistrationEventRecordStatReq.java | 21 - ...aoRegistrationEventRecordTrendStatReq.java | 24 - .../user/controller/UserController.java | 7 +- .../com/imyeyu/api/util/BaseXSSFBuilder.java | 65 ++ .../V23__rename_gao_event_tables.sql | 54 ++ .../mapper/timi-server/AttachmentMapper.xml | 33 +- .../mapper/timi-server/GaoCustomerMapper.xml | 78 +- ...tionEventMapper.xml => GaoEventMapper.xml} | 18 +- .../timi-server/GaoEventRecordMapper.xml | 171 ++++ .../GaoEventRoleRelationMapper.xml | 22 + .../GaoRegistrationEventRecordMapper.xml | 389 --------- .../mapper/timi-server/UserMapper.xml | 4 +- 67 files changed, 1770 insertions(+), 3319 deletions(-) delete mode 100644 src/main/java/com/imyeyu/api/modules/common/vo/attach/BizDeleteReq.java delete mode 100644 src/main/java/com/imyeyu/api/modules/common/vo/attach/BizUpdateReq.java rename src/main/java/com/imyeyu/api/modules/gao/controller/{GaoRegistrationEventController.java => GaoEventController.java} (57%) create mode 100644 src/main/java/com/imyeyu/api/modules/gao/controller/GaoEventRecordController.java delete mode 100644 src/main/java/com/imyeyu/api/modules/gao/controller/GaoRegistrationEventRecordController.java rename src/main/java/com/imyeyu/api/modules/gao/entity/{GaoRegistrationEvent.java => GaoEvent.java} (77%) rename src/main/java/com/imyeyu/api/modules/gao/entity/{GaoRegistrationEventRecord.java => GaoEventRecord.java} (81%) rename src/main/java/com/imyeyu/api/modules/gao/entity/{GaoRegistrationEventRoleRelation.java => GaoEventRoleRelation.java} (76%) create mode 100644 src/main/java/com/imyeyu/api/modules/gao/mapper/GaoEventMapper.java create mode 100644 src/main/java/com/imyeyu/api/modules/gao/mapper/GaoEventRecordMapper.java create mode 100644 src/main/java/com/imyeyu/api/modules/gao/mapper/GaoEventRoleRelationMapper.java delete mode 100644 src/main/java/com/imyeyu/api/modules/gao/mapper/GaoRegistrationEventMapper.java delete mode 100644 src/main/java/com/imyeyu/api/modules/gao/mapper/GaoRegistrationEventRecordMapper.java delete mode 100644 src/main/java/com/imyeyu/api/modules/gao/mapper/GaoRegistrationEventRoleRelationMapper.java create mode 100644 src/main/java/com/imyeyu/api/modules/gao/service/GaoEventRecordService.java create mode 100644 src/main/java/com/imyeyu/api/modules/gao/service/GaoEventService.java delete mode 100644 src/main/java/com/imyeyu/api/modules/gao/service/GaoRegistrationEventRecordService.java delete mode 100644 src/main/java/com/imyeyu/api/modules/gao/service/GaoRegistrationEventService.java create mode 100644 src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoEventRecordServiceImplement.java create mode 100644 src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoEventServiceImplement.java delete mode 100644 src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoRegistrationEventRecordServiceImplement.java delete mode 100644 src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoRegistrationEventServiceImplement.java create mode 100644 src/main/java/com/imyeyu/api/modules/gao/util/CustomerXSSFBuilder.java create mode 100644 src/main/java/com/imyeyu/api/modules/gao/util/EventRecordXSSFBuilder.java rename src/main/java/com/imyeyu/api/modules/gao/vo/{GaoCustomerDailyStatView.java => GaoCustomerDailyStateView.java} (89%) rename src/main/java/com/imyeyu/api/modules/gao/vo/{GaoCustomerRegistrationRankView.java => GaoCustomerEventRankView.java} (52%) delete mode 100644 src/main/java/com/imyeyu/api/modules/gao/vo/GaoCustomerInviteUpdateReq.java delete mode 100644 src/main/java/com/imyeyu/api/modules/gao/vo/GaoCustomerRegisterStatView.java delete mode 100644 src/main/java/com/imyeyu/api/modules/gao/vo/GaoCustomerRegistrationDayStatView.java delete mode 100644 src/main/java/com/imyeyu/api/modules/gao/vo/GaoCustomerSearchReq.java rename src/main/java/com/imyeyu/api/modules/gao/vo/{GaoCustomerTrendStatReq.java => GaoCustomerTrendStateReq.java} (86%) rename src/main/java/com/imyeyu/api/modules/gao/vo/{GaoRegistrationEventRecordRangeReq.java => GaoEventRecordPage.java} (58%) create mode 100644 src/main/java/com/imyeyu/api/modules/gao/vo/GaoEventRecordRankPage.java delete mode 100644 src/main/java/com/imyeyu/api/modules/gao/vo/GaoQuickRegistrationEventRecordReq.java delete mode 100644 src/main/java/com/imyeyu/api/modules/gao/vo/GaoRegistrationEventPeriodStatView.java delete mode 100644 src/main/java/com/imyeyu/api/modules/gao/vo/GaoRegistrationEventRecordCalendarView.java delete mode 100644 src/main/java/com/imyeyu/api/modules/gao/vo/GaoRegistrationEventRecordCreateReq.java delete mode 100644 src/main/java/com/imyeyu/api/modules/gao/vo/GaoRegistrationEventRecordDailyStatView.java delete mode 100644 src/main/java/com/imyeyu/api/modules/gao/vo/GaoRegistrationEventRecordListItemView.java delete mode 100644 src/main/java/com/imyeyu/api/modules/gao/vo/GaoRegistrationEventRecordPeriodReq.java delete mode 100644 src/main/java/com/imyeyu/api/modules/gao/vo/GaoRegistrationEventRecordStatReq.java delete mode 100644 src/main/java/com/imyeyu/api/modules/gao/vo/GaoRegistrationEventRecordTrendStatReq.java create mode 100644 src/main/java/com/imyeyu/api/util/BaseXSSFBuilder.java create mode 100644 src/main/resources/db/migration/timiserver/V23__rename_gao_event_tables.sql rename src/main/resources/mapper/timi-server/{GaoRegistrationEventMapper.xml => GaoEventMapper.xml} (63%) create mode 100644 src/main/resources/mapper/timi-server/GaoEventRecordMapper.xml create mode 100644 src/main/resources/mapper/timi-server/GaoEventRoleRelationMapper.xml delete mode 100644 src/main/resources/mapper/timi-server/GaoRegistrationEventRecordMapper.xml diff --git a/.idea/sqldialects.xml b/.idea/sqldialects.xml index eb4a267..1d0a883 100644 --- a/.idea/sqldialects.xml +++ b/.idea/sqldialects.xml @@ -3,6 +3,7 @@ + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 5e66368..a77ebe1 100644 --- a/pom.xml +++ b/pom.xml @@ -188,12 +188,12 @@ com.imyeyu.java timi-java - 0.0.6 + 0.0.7 com.imyeyu.utils timi-utils - 0.0.12 + 0.0.13 com.imyeyu.font.icon diff --git a/src/main/java/com/imyeyu/api/modules/common/controller/AttachmentController.java b/src/main/java/com/imyeyu/api/modules/common/controller/AttachmentController.java index 28712f7..10ed758 100644 --- a/src/main/java/com/imyeyu/api/modules/common/controller/AttachmentController.java +++ b/src/main/java/com/imyeyu/api/modules/common/controller/AttachmentController.java @@ -3,8 +3,6 @@ package com.imyeyu.api.modules.common.controller; import com.imyeyu.api.modules.common.entity.Attachment; import com.imyeyu.api.modules.common.service.AttachmentService; import com.imyeyu.api.modules.common.service.TempFileService; -import com.imyeyu.api.modules.common.vo.attach.BizDeleteReq; -import com.imyeyu.api.modules.common.vo.attach.BizUpdateReq; import com.imyeyu.api.modules.common.vo.attach.TempFileResp; import com.imyeyu.java.TimiJava; import com.imyeyu.network.Network; @@ -183,30 +181,6 @@ public class AttachmentController { return service.pageByBizId(bizType, bizId, attachTypeList, page); } - /** - * 按业务差分更新附件 - * - * @param req 更新请求 - */ - @AOPLog - @RequestRateLimit - @PostMapping("/update/biz") - public void updateByBiz(@RequestBody BizUpdateReq req) { - service.updateByBiz(req); - } - - /** - * 按业务删除全部附件 - * - * @param req 删除请求 - */ - @AOPLog - @RequestRateLimit - @PostMapping("/delete/biz") - public void deleteByBiz(@RequestBody BizDeleteReq req) { - service.deleteByBiz(req); - } - @AOPLog @IgnoreGlobalReturn @GetMapping({"/read/{id}", "/download/{id}"}) 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 6b1c10d..3ef00fe 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 @@ -1,9 +1,10 @@ package com.imyeyu.api.modules.common.entity; -import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.imyeyu.api.TimiServerAPI; import com.imyeyu.api.modules.common.bean.attachment.AccessKey; import com.imyeyu.api.modules.common.service.AttachmentService; +import com.imyeyu.java.TimiJava; import com.imyeyu.java.ref.Ref; import com.imyeyu.spring.TimiSpring; import com.imyeyu.spring.annotation.table.Transient; @@ -33,7 +34,7 @@ public class Attachment extends UUIDEntity { GAO_CUSTOMER, - GAO_REGISTRATION_EVENT_RECORD, + GAO_EVENT_RECORD, TEMP_FILE, @@ -54,7 +55,7 @@ public class Attachment extends UUIDEntity { protected String mimeType; - protected JsonNode metadata; + protected ObjectNode metadata; protected Long size; @@ -82,6 +83,14 @@ public class Attachment extends UUIDEntity { @Transient protected MultipartFile file; + public String buildMongoName() { + StringBuilder result = new StringBuilder(bizType.name()); + if (TimiJava.isNotEmpty(attachType)) { + result.append('_').append(attachType.toUpperCase()); + } + return result.append('_').append(name).toString(); + } + public InputStream openInputStream() { AttachmentService service = TimiServerAPI.applicationContext.getBean(AttachmentService.class); return service.getInputStreamByMongoId(mongoId); 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 60e7d61..6c5d57f 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 @@ -4,9 +4,9 @@ import com.imyeyu.api.modules.common.entity.Attachment; import com.imyeyu.spring.bean.Page; import com.imyeyu.spring.mapper.BaseMapper; import com.imyeyu.spring.mapper.RawMapper; -import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; +import java.util.Collection; import java.util.List; /** @@ -32,7 +32,7 @@ public interface AttachmentMapper extends BaseMapper, RawMap List selectByBizId(Attachment.BizType bizType, String bizId, List attachTypes, Page page); - List selectByBizIdList(@Param("bizType") Attachment.BizType bizType, @Param("bizIdList") List bizIdList, @Param("attachTypes") List attachTypes); + List selectByBizIdList(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/service/AttachmentService.java b/src/main/java/com/imyeyu/api/modules/common/service/AttachmentService.java index ddd367c..b89df9c 100644 --- a/src/main/java/com/imyeyu/api/modules/common/service/AttachmentService.java +++ b/src/main/java/com/imyeyu/api/modules/common/service/AttachmentService.java @@ -1,13 +1,12 @@ package com.imyeyu.api.modules.common.service; import com.imyeyu.api.modules.common.entity.Attachment; -import com.imyeyu.api.modules.common.vo.attach.BizDeleteReq; -import com.imyeyu.api.modules.common.vo.attach.BizUpdateReq; import com.imyeyu.spring.bean.Page; import com.imyeyu.spring.bean.PageResult; import com.imyeyu.spring.service.BaseService; import java.io.InputStream; +import java.util.Collection; import java.util.List; import java.util.Map; @@ -19,6 +18,13 @@ import java.util.Map; */ public interface AttachmentService extends BaseService { + /// + /// 克隆附件,复制业务属性和文件内容并生成新的附件记录 + /// + /// @param id 源附件 ID + /// @return 克隆后的附件 + Attachment clone(String id); + /** * 按需获取或创建缩略图附件。 *

当附件为图片或视频时,若指定尺寸的缩略图已存在则直接返回缓存, @@ -52,14 +58,14 @@ public interface AttachmentService extends BaseService { List listByBizId(Attachment.BizType bizType, String bizId, String... attachTypes); /** - * 根据多个业务 ID 批量获取附件。 + * 根据多个业务 ID 批量获取附件 * * @param bizType 业务类型 * @param bizIdList 业务 ID 列表 * @param attachTypes 附件类型,可为 null * @return 按业务 ID 分组的附件列表 */ - Map> mapByBizIdList(Attachment.BizType bizType, List bizIdList, String... attachTypes); + Map> mapByBizIdList(Attachment.BizType bizType, Collection bizIdList, String... attachTypes); /** * 根据业务获取所有附件 @@ -84,10 +90,8 @@ public interface AttachmentService extends BaseService { *

  • 请求有、数据库有、无 tempFileId → 仅更新元数据
  • *
  • 请求有、数据库有、有 tempFileId → 更新文件与元数据
  • * - * - * @param req 差分更新请求 */ - void updateByBiz(BizUpdateReq req); + void updateByBizId(Attachment.BizType bizType, String bizId, List list); - void deleteByBiz(BizDeleteReq req); + void deleteByBizId(Attachment.BizType bizType, String bizId); } 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 4346370..f1b1bf5 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 @@ -8,8 +8,6 @@ import com.imyeyu.api.modules.common.bean.attachment.Metadata; import com.imyeyu.api.modules.common.entity.Attachment; import com.imyeyu.api.modules.common.mapper.AttachmentMapper; import com.imyeyu.api.modules.common.service.AttachmentService; -import com.imyeyu.api.modules.common.vo.attach.BizDeleteReq; -import com.imyeyu.api.modules.common.vo.attach.BizUpdateReq; import com.imyeyu.api.util.JavaCV; import com.imyeyu.io.IO; import com.imyeyu.java.TimiJava; @@ -43,7 +41,7 @@ import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Objects; @@ -71,6 +69,88 @@ public class AttachmentServiceImplement extends AbstractEntityService> mapByBizIdList(Attachment.BizType bizType, List bizIdList, String... attachTypes) { - if (bizIdList == null || bizIdList.isEmpty()) { - return Map.of(); - } - List normalizedBizIdList = bizIdList.stream() - .filter(TimiJava::isNotEmpty) - .distinct() - .toList(); - if (normalizedBizIdList.isEmpty()) { - return Map.of(); - } - return mapper.selectByBizIdList(bizType, new ArrayList<>(normalizedBizIdList), List.of(attachTypes)).stream() - .collect(Collectors.groupingBy(Attachment::getBizId)); + public Map> mapByBizIdList(Attachment.BizType bizType, Collection bizIdList, String... attachTypes) { + return mapper.selectByBizIdList(bizType, bizIdList, List.of(attachTypes)).stream().collect(Collectors.groupingBy(Attachment::getBizId)); } @Override @@ -348,16 +346,12 @@ public class AttachmentServiceImplement extends AbstractEntityService items = req.getItems(); - + public void updateByBizId(Attachment.BizType bizType, String bizId, List list) { // 数据库现有附件 List dbList = mapper.selectAllByBizId(bizType, bizId); Set dbIds = dbList.stream().map(Attachment::getId).collect(Collectors.toSet()); // 请求附件 ID - Set reqIds = items.stream().map(Attachment::getId).filter(Objects::nonNull).collect(Collectors.toSet()); + Set reqIds = list.stream().map(Attachment::getId).filter(Objects::nonNull).collect(Collectors.toSet()); // 数据库有、请求无:删除 for (Attachment attach : dbList) { if (!reqIds.contains(attach.getId())) { @@ -365,7 +359,7 @@ public class AttachmentServiceImplement extends AbstractEntityService attachmentList = mapper.selectAllByBizId(req.getBizType(), req.getBizId()); + public void deleteByBizId(Attachment.BizType bizType, String bizId) { + List attachmentList = mapper.selectAllByBizId(bizType, bizId); for (Attachment attachment : attachmentList) { delete(attachment.getId()); } diff --git a/src/main/java/com/imyeyu/api/modules/common/vo/attach/BizDeleteReq.java b/src/main/java/com/imyeyu/api/modules/common/vo/attach/BizDeleteReq.java deleted file mode 100644 index 7ebb47a..0000000 --- a/src/main/java/com/imyeyu/api/modules/common/vo/attach/BizDeleteReq.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.imyeyu.api.modules.common.vo.attach; - -import com.imyeyu.api.modules.common.entity.Attachment; -import lombok.Data; - -/** - * 业务附件差分更新请求 - * - * @author 夜雨 - * @since 2026-03-11 00:00 - */ -@Data -public class BizDeleteReq { - - /** 业务类型 */ - private Attachment.BizType bizType; - - /** 业务 ID */ - private String bizId; -} diff --git a/src/main/java/com/imyeyu/api/modules/common/vo/attach/BizUpdateReq.java b/src/main/java/com/imyeyu/api/modules/common/vo/attach/BizUpdateReq.java deleted file mode 100644 index 6b90f1f..0000000 --- a/src/main/java/com/imyeyu/api/modules/common/vo/attach/BizUpdateReq.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.imyeyu.api.modules.common.vo.attach; - -import com.imyeyu.api.modules.common.entity.Attachment; -import lombok.Data; - -import java.util.ArrayList; -import java.util.List; - -/** - * 业务附件差分更新请求 - * - * @author 夜雨 - * @since 2026-03-11 00:00 - */ -@Data -public class BizUpdateReq { - - /** 业务类型 */ - private Attachment.BizType bizType; - - /** 业务 ID */ - private String bizId; - - /** 附件项列表 */ - private List items = new ArrayList<>(); -} diff --git a/src/main/java/com/imyeyu/api/modules/gao/bean/GaoPermissionCode.java b/src/main/java/com/imyeyu/api/modules/gao/bean/GaoPermissionCode.java index cfc07be..3f25cb5 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/bean/GaoPermissionCode.java +++ b/src/main/java/com/imyeyu/api/modules/gao/bean/GaoPermissionCode.java @@ -2,6 +2,7 @@ package com.imyeyu.api.modules.gao.bean; import com.imyeyu.api.bean.ModuleCode; import com.imyeyu.api.modules.user.bean.BuiltinPermissionCode; +import lombok.Getter; /// /// GAO 模块权限枚举 @@ -20,23 +21,23 @@ public enum GaoPermissionCode implements BuiltinPermissionCode { CUSTOMER_DELETE("CUSTOMER:DELETE", "客户删除"), - REGISTRATION_EVENT_CREATE("REGISTRATION_EVENT:CREATE", "登记事件创建"), + EVENT_CREATE("EVENT:CREATE", "登记事件创建"), - REGISTRATION_EVENT_READ("REGISTRATION_EVENT:READ", "登记事件读取"), + EVENT_READ("EVENT:READ", "登记事件读取"), - REGISTRATION_EVENT_UPDATE("REGISTRATION_EVENT:UPDATE", "登记事件修改"), + EVENT_UPDATE("EVENT:UPDATE", "登记事件修改"), - REGISTRATION_EVENT_DELETE("REGISTRATION_EVENT:DELETE", "登记事件删除"), + EVENT_DELETE("EVENT:DELETE", "登记事件删除"), - REGISTRATION_EVENT_RECORD_CREATE("REGISTRATION_EVENT_RECORD:CREATE", "登记事件记录创建"), + EVENT_RECORD_CREATE("EVENT_RECORD:CREATE", "登记事件记录创建"), - REGISTRATION_EVENT_RECORD_READ("REGISTRATION_EVENT_RECORD:READ", "登记事件记录读取"), + EVENT_RECORD_READ("EVENT_RECORD:READ", "登记事件记录读取"), - REGISTRATION_EVENT_RECORD_EXPORT("REGISTRATION_EVENT_RECORD:EXPORT", "登记事件记录导出", true), + EVENT_RECORD_EXPORT("EVENT_RECORD:EXPORT", "登记事件记录导出", true), - REGISTRATION_EVENT_RECORD_UPDATE("REGISTRATION_EVENT_RECORD:UPDATE", "登记事件记录修改"), + EVENT_RECORD_UPDATE("EVENT_RECORD:UPDATE", "登记事件记录修改"), - REGISTRATION_EVENT_RECORD_DELETE("REGISTRATION_EVENT_RECORD:DELETE", "登记事件记录删除"), + EVENT_RECORD_DELETE("EVENT_RECORD:DELETE", "登记事件记录删除"), STAT_READ("STAT:READ", "统计读取"), @@ -52,6 +53,9 @@ public enum GaoPermissionCode implements BuiltinPermissionCode { final String value; final String defaultName; + + /// `true` 为初始化时授权给店长角色 + @Getter final boolean grantToStoreManager; GaoPermissionCode(String value, String defaultName) { @@ -88,11 +92,4 @@ public enum GaoPermissionCode implements BuiltinPermissionCode { public boolean isGrantToAdmin() { return false; } - - /// true 为初始化时授权给店长角色 - /// - /// @return 是否授权给店长角色 - public boolean isGrantToStoreManager() { - return grantToStoreManager; - } } diff --git a/src/main/java/com/imyeyu/api/modules/gao/controller/GaoCustomerController.java b/src/main/java/com/imyeyu/api/modules/gao/controller/GaoCustomerController.java index 480a64b..ee984b0 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/controller/GaoCustomerController.java +++ b/src/main/java/com/imyeyu/api/modules/gao/controller/GaoCustomerController.java @@ -1,29 +1,29 @@ package com.imyeyu.api.modules.gao.controller; import com.fasterxml.jackson.annotation.JsonView; -import com.imyeyu.api.modules.gao.bean.GaoPermissionCode; -import com.imyeyu.api.modules.gao.bean.GaoRoleCode; import com.imyeyu.api.bean.RoleMatchMode; +import com.imyeyu.api.modules.common.entity.Attachment; +import com.imyeyu.api.modules.common.service.AttachmentService; import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission; import com.imyeyu.api.modules.gao.annotation.RequireGaoRole; +import com.imyeyu.api.modules.gao.bean.GaoPermissionCode; +import com.imyeyu.api.modules.gao.bean.GaoRoleCode; import com.imyeyu.api.modules.gao.entity.GaoCustomer; -import com.imyeyu.api.modules.gao.entity.GaoRegistrationEvent; -import com.imyeyu.api.modules.gao.entity.GaoRegistrationEventRecord; -import com.imyeyu.api.modules.gao.service.GaoRegistrationEventService; import com.imyeyu.api.modules.gao.service.GaoCustomerService; -import com.imyeyu.api.modules.gao.service.GaoRegistrationEventRecordService; -import com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStatView; +import com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStateView; import com.imyeyu.api.modules.gao.vo.GaoCustomerGenderStatView; -import com.imyeyu.api.modules.gao.vo.GaoCustomerInviteUpdateReq; -import com.imyeyu.api.modules.gao.vo.GaoCustomerSearchReq; -import com.imyeyu.api.modules.gao.vo.GaoCustomerTrendStatReq; -import com.imyeyu.api.modules.gao.vo.GaoQuickRegistrationEventRecordReq; +import com.imyeyu.api.modules.gao.vo.GaoCustomerTrendStateReq; +import com.imyeyu.api.modules.user.service.UserService; +import com.imyeyu.java.TimiJava; +import com.imyeyu.network.Network; +import com.imyeyu.spring.TimiSpring; import com.imyeyu.spring.annotation.AOPLog; import com.imyeyu.spring.annotation.IgnoreGlobalReturn; import com.imyeyu.spring.annotation.RequestRateLimit; import com.imyeyu.spring.bean.Page; import com.imyeyu.spring.bean.PageResult; import com.imyeyu.spring.util.ResponseView; +import com.imyeyu.utils.Time; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.PostMapping; @@ -33,28 +33,22 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.io.IOException; -import java.net.URLEncoder; import java.util.List; import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; -/// /// GAO 客户接口 /// -/// @author Codex -/// @since 2026-07-27 +/// @author 夜雨 +/// @since 2026-07-27 14:12 @RestController +@RequireGaoRole(value = {GaoRoleCode.STORE_MANAGER, GaoRoleCode.EMPLOYEE}, mode = RoleMatchMode.ANY) @RequiredArgsConstructor @RequestMapping("/gao/customer") -@RequireGaoRole(value = {GaoRoleCode.STORE_MANAGER, GaoRoleCode.EMPLOYEE}, mode = RoleMatchMode.ANY) public class GaoCustomerController { - private static final String SIGN_IN_REGISTRATION_EVENT_NAME = "签到"; - + private final UserService userService; + private final AttachmentService attachmentService; private final GaoCustomerService service; - private final GaoRegistrationEventRecordService registrationEventRecordService; - private final GaoRegistrationEventService registrationEventService; @AOPLog @RequestRateLimit @@ -63,74 +57,32 @@ public class GaoCustomerController { @JsonView(ResponseView.Public.class) public PageResult list(@RequestBody Page page) { PageResult result = service.page(page); - fillLastSignInAt(result.getList()); + + List idList = result.getList().stream().map(GaoCustomer::getId).distinct().toList(); + Map> attachmentMap = attachmentService.mapByBizIdList(Attachment.BizType.GAO_CUSTOMER, idList); + for (GaoCustomer customer : result.getList()) { + customer.setAttachmentList(attachmentMap.get(customer.getId())); + } return result; } - @AOPLog - @RequestRateLimit - @RequireGaoPermission(GaoPermissionCode.CUSTOMER_READ) - @PostMapping("/search") - @JsonView(ResponseView.Public.class) - public List search(@RequestBody GaoCustomerSearchReq req) { - return service.search(req); - } - - @AOPLog - @RequestRateLimit - @RequireGaoPermission(GaoPermissionCode.CUSTOMER_READ) - @PostMapping("/search/page") - @JsonView(ResponseView.Public.class) - public PageResult searchPage(@RequestBody Page page) { - return service.searchPage(page); - } - - @AOPLog - @RequestRateLimit - @IgnoreGlobalReturn - @RequireGaoPermission(GaoPermissionCode.CUSTOMER_EXPORT) - @PostMapping("/export") - public void export(@RequestBody GaoCustomerSearchReq req, HttpServletResponse resp) throws IOException { - String fileName = URLEncoder.encode("客户列表.xlsx", "UTF-8").replace("+", "%20"); - resp.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); - resp.setHeader("Content-Disposition", "attachment; filename=\"gao-customer.xlsx\"; filename*=UTF-8''" + fileName); - resp.getOutputStream().write(service.exportSearchExcel(req)); - } - @AOPLog @RequestRateLimit @RequireGaoPermission(GaoPermissionCode.CUSTOMER_READ) @PostMapping("/detail") @JsonView(ResponseView.Public.class) public GaoCustomer detail(@RequestParam String id) { - return service.get(id); - } - - @AOPLog - @RequestRateLimit - @RequireGaoPermission(GaoPermissionCode.CUSTOMER_READ) - @PostMapping("/detail/code") - @JsonView(ResponseView.Public.class) - public GaoCustomer detailByCode(@RequestParam String code) { - return service.getByCode(code); - } - - @AOPLog - @RequestRateLimit - @RequireGaoPermission(GaoPermissionCode.CUSTOMER_READ) - @PostMapping("/detail/code/nullable") - @JsonView(ResponseView.Public.class) - public GaoCustomer nullableDetailByCode(@RequestParam String code) { - return service.getNullableByCode(code); - } - - @AOPLog - @RequestRateLimit - @RequireGaoPermission(GaoPermissionCode.CUSTOMER_READ) - @PostMapping("/invited/list") - @JsonView(ResponseView.Public.class) - public List invitedList(@RequestParam String introducerCustomerId) { - return service.listInvited(introducerCustomerId); + GaoCustomer customer = service.get(id); + if (TimiJava.isNotEmpty(customer.getIntroducerCustomerId())) { + GaoCustomer introducerCustomer = service.get(customer.getIntroducerCustomerId()); + introducerCustomer.setAttachmentList(attachmentService.listByBizId(Attachment.BizType.GAO_CUSTOMER, introducerCustomer.getId())); + customer.setIntroducerCustomer(introducerCustomer); + } + if (TimiJava.isNotEmpty(customer.getCreatorUserId())) { + customer.setCreatorUser(userService.get(customer.getCreatorUserId())); + } + customer.setAttachmentList(attachmentService.listByBizId(Attachment.BizType.GAO_CUSTOMER, customer.getId())); + return customer; } @AOPLog @@ -138,17 +90,9 @@ public class GaoCustomerController { @RequireGaoPermission(GaoPermissionCode.CUSTOMER_CREATE) @PostMapping("/create") @JsonView(ResponseView.Public.class) - public GaoCustomer create(@RequestBody GaoCustomer customer) { + public String create(@RequestBody GaoCustomer customer) { service.create(customer); - return customer; - } - - @AOPLog - @RequestRateLimit - @PostMapping("/quick/register") - @JsonView(ResponseView.Public.class) - public GaoRegistrationEventRecord quickRegister(@RequestBody GaoQuickRegistrationEventRecordReq req) { - return registrationEventRecordService.quickRegister(req); + return customer.getId(); } @AOPLog @@ -169,27 +113,66 @@ public class GaoCustomerController { @AOPLog @RequestRateLimit - @RequireGaoPermission(GaoPermissionCode.CUSTOMER_UPDATE) - @PostMapping("/invite/create") - public void createInvite(@RequestBody GaoCustomerInviteUpdateReq req) { - service.saveInviteRelation(req); + @RequireGaoPermission(GaoPermissionCode.CUSTOMER_READ) + @PostMapping("/find/code") + @JsonView(ResponseView.Public.class) + public GaoCustomer findByCode(@RequestParam String code) { + GaoCustomer customer = service.getByCode(code); + if (customer == null) { + return null; + } + customer.setAttachmentList(attachmentService.listByBizId(Attachment.BizType.GAO_CUSTOMER, customer.getId())); + return customer; } @AOPLog @RequestRateLimit @RequireGaoPermission(GaoPermissionCode.CUSTOMER_READ) - @PostMapping("/invite/detail") + @PostMapping("/find/name") @JsonView(ResponseView.Public.class) - public GaoCustomer inviteDetail(@RequestParam String customerId) { - return service.get(customerId); + public GaoCustomer findByName(@RequestParam String name) { + GaoCustomer customer = service.getByName(name); + if (customer == null) { + return null; + } + customer.setAttachmentList(attachmentService.listByBizId(Attachment.BizType.GAO_CUSTOMER, customer.getId())); + return customer; + } + + @AOPLog + @RequestRateLimit + @IgnoreGlobalReturn + @RequireGaoPermission(GaoPermissionCode.CUSTOMER_EXPORT) + @PostMapping("/export") + public void export(@RequestBody Page page) throws IOException { + HttpServletResponse resp = TimiSpring.getResponse(); + String contentDisposition = Network.getFileDownloadHeader("客户列表-%s.xlsx".formatted(Time.serialize.format(Time.now()))); + resp.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + resp.setHeader("Content-Disposition", contentDisposition); + resp.getOutputStream().write(service.export(page)); + } + + @AOPLog + @RequestRateLimit + @RequireGaoPermission(GaoPermissionCode.CUSTOMER_READ) + @PostMapping("/invited/list") + @JsonView(ResponseView.Public.class) + public List listInvited(@RequestParam String introducerCustomerId) { + List result = service.listInvited(introducerCustomerId); + List idList = result.stream().map(GaoCustomer::getId).distinct().toList(); + Map> attachMap = attachmentService.mapByBizIdList(Attachment.BizType.GAO_CUSTOMER, idList); + for (GaoCustomer customer : result) { + customer.setAttachmentList(attachMap.get(customer.getId())); + } + return result; } @AOPLog @RequestRateLimit @RequireGaoPermission(GaoPermissionCode.CUSTOMER_UPDATE) - @PostMapping("/invite/update") - public void updateInvite(@RequestBody GaoCustomerInviteUpdateReq req) { - service.saveInviteRelation(req); + @PostMapping("/invite/save") + public void saveInvite(@RequestParam String introducerCustomerId, @RequestParam String customerId) { + service.saveInviteRelation(introducerCustomerId, customerId); } @AOPLog @@ -200,46 +183,21 @@ public class GaoCustomerController { service.deleteInviteRelation(customerId); } - @AOPLog - @RequestRateLimit - @RequireGaoPermission(GaoPermissionCode.CUSTOMER_READ) - @PostMapping("/stat/daily") /// 按天统计客户新增数和累计总数 - public List dailyStat(@RequestBody GaoCustomerTrendStatReq req) { - return service.statDailyTrend(req); - } - @AOPLog @RequestRateLimit @RequireGaoPermission(GaoPermissionCode.CUSTOMER_READ) - @PostMapping("/stat/gender") - /// 统计客户性别分布 - public List genderStat() { - return service.statGender(); + @PostMapping("/state/daily") + public List dailyStat(@RequestBody GaoCustomerTrendStateReq req) { + return service.stateDailyTrend(req); } - private void fillLastSignInAt(List customerList) { - if (customerList == null || customerList.isEmpty()) { - return; - } - String signInRegistrationEventId = null; - for (GaoRegistrationEvent registrationEvent : registrationEventService.listEnabled()) { - if (SIGN_IN_REGISTRATION_EVENT_NAME.equals(registrationEvent.getName())) { - signInRegistrationEventId = registrationEvent.getId(); - break; - } - } - if (signInRegistrationEventId == null) { - return; - } - List customerIdList = customerList.stream().map(GaoCustomer::getId).toList(); - Map recordMap = registrationEventRecordService.listLatestByCustomerIdListAndRegistrationEventId(customerIdList, signInRegistrationEventId).stream() - .collect(Collectors.toMap(GaoRegistrationEventRecord::getCustomerId, Function.identity(), (left, right) -> left)); - for (GaoCustomer customer : customerList) { - GaoRegistrationEventRecord record = recordMap.get(customer.getId()); - if (record != null) { - customer.setLastSignInAt(record.getRegisteredAt()); - } - } + /// 统计客户性别分布 + @AOPLog + @RequestRateLimit + @RequireGaoPermission(GaoPermissionCode.CUSTOMER_READ) + @PostMapping("/state/gender") + public List genderStat() { + return service.stateGender(); } } diff --git a/src/main/java/com/imyeyu/api/modules/gao/controller/GaoRegistrationEventController.java b/src/main/java/com/imyeyu/api/modules/gao/controller/GaoEventController.java similarity index 57% rename from src/main/java/com/imyeyu/api/modules/gao/controller/GaoRegistrationEventController.java rename to src/main/java/com/imyeyu/api/modules/gao/controller/GaoEventController.java index 0063ad6..6103e94 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/controller/GaoRegistrationEventController.java +++ b/src/main/java/com/imyeyu/api/modules/gao/controller/GaoEventController.java @@ -1,12 +1,12 @@ package com.imyeyu.api.modules.gao.controller; +import com.imyeyu.api.bean.RoleMatchMode; import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission; import com.imyeyu.api.modules.gao.annotation.RequireGaoRole; -import com.imyeyu.api.bean.RoleMatchMode; import com.imyeyu.api.modules.gao.bean.GaoPermissionCode; import com.imyeyu.api.modules.gao.bean.GaoRoleCode; -import com.imyeyu.api.modules.gao.entity.GaoRegistrationEvent; -import com.imyeyu.api.modules.gao.service.GaoRegistrationEventService; +import com.imyeyu.api.modules.gao.entity.GaoEvent; +import com.imyeyu.api.modules.gao.service.GaoEventService; import com.imyeyu.spring.annotation.AOPLog; import com.imyeyu.spring.annotation.RequestRateLimit; import com.imyeyu.spring.bean.Page; @@ -19,62 +19,67 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.List; +import java.util.Map; -/// /// GAO 登记事件接口 /// -/// @author Codex -/// @since 2026-07-27 +/// @author 夜雨 +/// @since 2026-07-27 14:13 @RestController -@RequiredArgsConstructor -@RequestMapping("/gao/registration-event") @RequireGaoRole(value = {GaoRoleCode.STORE_MANAGER, GaoRoleCode.EMPLOYEE}, mode = RoleMatchMode.ANY) -public class GaoRegistrationEventController { +@RequiredArgsConstructor +@RequestMapping("/gao/event") +public class GaoEventController { - private final GaoRegistrationEventService service; + private final GaoEventService service; @AOPLog @RequestRateLimit - @RequireGaoPermission(GaoPermissionCode.REGISTRATION_EVENT_READ) + @RequireGaoPermission(GaoPermissionCode.EVENT_READ) @PostMapping("/list") - public PageResult list(@RequestBody Page page) { - return service.pageWithRegistrationEventLimit(page); + public PageResult list(@RequestBody Page page) { + PageResult result = service.page(page); + Map> roleMap = service.mapRoleCodeListByEventIdList(result.getList().stream().map(GaoEvent::getId).toList()); + for (GaoEvent event : result.getList()) { + event.setRoleCodeList(roleMap.get(event.getId())); + } + return result; } @AOPLog @RequestRateLimit - @PostMapping("/enabled/list") - public List enabledList() { - return service.listEnabled(); + @PostMapping("/list/valid") + public List listValid() { + return service.listValid(); } @AOPLog @RequestRateLimit - @RequireGaoPermission(GaoPermissionCode.REGISTRATION_EVENT_READ) + @RequireGaoPermission(GaoPermissionCode.EVENT_READ) @PostMapping("/detail") - public GaoRegistrationEvent detail(@RequestParam String id) { + public GaoEvent detail(@RequestParam String id) { return service.get(id); } @AOPLog @RequestRateLimit - @RequireGaoPermission(GaoPermissionCode.REGISTRATION_EVENT_CREATE) + @RequireGaoPermission(GaoPermissionCode.EVENT_CREATE) @PostMapping("/create") - public void create(@RequestBody GaoRegistrationEvent registrationEvent) { + public void create(@RequestBody GaoEvent registrationEvent) { service.create(registrationEvent); } @AOPLog @RequestRateLimit - @RequireGaoPermission(GaoPermissionCode.REGISTRATION_EVENT_UPDATE) + @RequireGaoPermission(GaoPermissionCode.EVENT_UPDATE) @PostMapping("/update") - public void update(@RequestBody GaoRegistrationEvent registrationEvent) { + public void update(@RequestBody GaoEvent registrationEvent) { service.update(registrationEvent); } @AOPLog @RequestRateLimit - @RequireGaoPermission(GaoPermissionCode.REGISTRATION_EVENT_UPDATE) + @RequireGaoPermission(GaoPermissionCode.EVENT_UPDATE) @PostMapping("/sort") public void sort(@RequestBody List idList) { service.sort(idList); @@ -82,7 +87,7 @@ public class GaoRegistrationEventController { @AOPLog @RequestRateLimit - @RequireGaoPermission(GaoPermissionCode.REGISTRATION_EVENT_DELETE) + @RequireGaoPermission(GaoPermissionCode.EVENT_DELETE) @PostMapping("/delete") public void delete(@RequestParam String id) { service.delete(id); diff --git a/src/main/java/com/imyeyu/api/modules/gao/controller/GaoEventRecordController.java b/src/main/java/com/imyeyu/api/modules/gao/controller/GaoEventRecordController.java new file mode 100644 index 0000000..e79352b --- /dev/null +++ b/src/main/java/com/imyeyu/api/modules/gao/controller/GaoEventRecordController.java @@ -0,0 +1,152 @@ +package com.imyeyu.api.modules.gao.controller; + +import com.fasterxml.jackson.annotation.JsonView; +import com.imyeyu.api.bean.RoleMatchMode; +import com.imyeyu.api.modules.common.entity.Attachment; +import com.imyeyu.api.modules.common.service.AttachmentService; +import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission; +import com.imyeyu.api.modules.gao.annotation.RequireGaoRole; +import com.imyeyu.api.modules.gao.bean.GaoPermissionCode; +import com.imyeyu.api.modules.gao.bean.GaoRoleCode; +import com.imyeyu.api.modules.gao.entity.GaoCustomer; +import com.imyeyu.api.modules.gao.entity.GaoEvent; +import com.imyeyu.api.modules.gao.entity.GaoEventRecord; +import com.imyeyu.api.modules.gao.service.GaoCustomerService; +import com.imyeyu.api.modules.gao.service.GaoEventRecordService; +import com.imyeyu.api.modules.gao.service.GaoEventService; +import com.imyeyu.api.modules.gao.vo.GaoCustomerEventRankView; +import com.imyeyu.api.modules.gao.vo.GaoEventRecordPage; +import com.imyeyu.api.modules.gao.vo.GaoEventRecordRankPage; +import com.imyeyu.api.modules.user.service.UserService; +import com.imyeyu.network.Network; +import com.imyeyu.spring.TimiSpring; +import com.imyeyu.spring.annotation.AOPLog; +import com.imyeyu.spring.annotation.IgnoreGlobalReturn; +import com.imyeyu.spring.annotation.RequestRateLimit; +import com.imyeyu.spring.bean.PageResult; +import com.imyeyu.spring.util.ResponseView; +import com.imyeyu.utils.Time; +import jakarta.servlet.http.HttpServletResponse; +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; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +/// GAO 登记事件记录接口 +/// +/// @author 夜雨 +/// @since 2026-07-27 14:11 +@RestController +@RequireGaoRole(value = {GaoRoleCode.STORE_MANAGER, GaoRoleCode.EMPLOYEE}, mode = RoleMatchMode.ANY) +@RequiredArgsConstructor +@RequestMapping("/gao/event/record") +public class GaoEventRecordController { + + private final UserService userService; + private final GaoEventService eventService; + private final AttachmentService attachmentService; + private final GaoCustomerService customerService; + private final GaoEventRecordService service; + + @AOPLog + @RequestRateLimit + @RequireGaoPermission(GaoPermissionCode.EVENT_RECORD_READ) + @PostMapping("/list") + @JsonView(ResponseView.Public.class) + public PageResult list(@RequestBody GaoEventRecordPage page) { + PageResult result = service.pageByRange(page); + { + List customerIdList = result.getList().stream().map(GaoEventRecord::getCustomerId).toList(); + Map eventMap = eventService.mapByIdList(result.getList().stream().map(GaoEventRecord::getEventId).toList()); + Map customerMap = customerService.mapByIdList(customerIdList); + Map> attachMap = attachmentService.mapByBizIdList(Attachment.BizType.GAO_CUSTOMER, customerIdList); + for (GaoEventRecord record : result.getList()) { + record.setEvent(eventMap.get(record.getEventId())); + record.setCustomer(customerMap.get(record.getCustomerId())); + record.getCustomer().setAttachmentList(attachMap.get(record.getCustomerId())); + } + } + return result; + } + + @AOPLog + @RequestRateLimit + @RequireGaoPermission(GaoPermissionCode.EVENT_RECORD_READ) + @PostMapping("/detail") + @JsonView(ResponseView.Public.class) + public GaoEventRecord detail(@RequestParam String id) { + GaoEventRecord record = service.get(id); + record.setCustomer(customerService.get(record.getCustomerId())); + record.getCustomer().setAttachmentList(attachmentService.listByBizId(Attachment.BizType.GAO_CUSTOMER, record.getCustomerId())); + record.setEvent(eventService.get(record.getEventId())); + record.setOperator(userService.get(record.getOperatorUserId())); + record.setAttachmentList(attachmentService.listByBizId(Attachment.BizType.GAO_EVENT_RECORD, record.getId())); + return record; + } + + @AOPLog + @RequestRateLimit + @PostMapping("/create") + @JsonView(ResponseView.Public.class) + public void create(@RequestBody GaoEventRecord record) { + service.create(record); + } + + @AOPLog + @RequestRateLimit + @RequireGaoPermission(GaoPermissionCode.EVENT_RECORD_UPDATE) + @PostMapping("/update") + public void update(@RequestBody GaoEventRecord record) { + service.update(record); + } + + @AOPLog + @RequestRateLimit + @RequireGaoPermission(GaoPermissionCode.EVENT_RECORD_DELETE) + @PostMapping("/delete") + public void delete(@RequestParam String id) { + service.delete(id); + } + + @AOPLog + @RequestRateLimit + @IgnoreGlobalReturn + @RequireGaoPermission(GaoPermissionCode.EVENT_RECORD_EXPORT) + @PostMapping("/export") + public void export(@RequestBody GaoEventRecordPage page) throws IOException { + HttpServletResponse resp = TimiSpring.getResponse(); + String contentDisposition = Network.getFileDownloadHeader("登记事件记录-%s.xlsx".formatted(Time.serialize.format(Time.now()))); + resp.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + resp.setHeader("Content-Disposition", contentDisposition); + resp.getOutputStream().write(service.export(page)); + } + + /// 按时间范围查询客户登记排行榜 + @AOPLog + @RequestRateLimit + @RequireGaoPermission(GaoPermissionCode.STAT_READ) + @PostMapping("/rank") + public PageResult eventRank(@RequestBody GaoEventRecordRankPage page) { + PageResult result = service.pageRankByRange(page); + List customerIdList = result.getList().stream().map(GaoCustomerEventRankView::getCustomerId).distinct().toList(); + if (customerIdList.isEmpty()) { + return result; + } + Map customerMap = customerService.mapByIdList(customerIdList); + Map> attachmentMap = attachmentService.mapByBizIdList(Attachment.BizType.GAO_CUSTOMER, customerIdList); + for (GaoCustomerEventRankView view : result.getList()) { + GaoCustomer customer = customerMap.get(view.getCustomerId()); + if (customer != null) { + customer.setAttachmentList(attachmentMap.get(view.getCustomerId())); + } + view.setCustomer(customer); + } + return result; + } +} diff --git a/src/main/java/com/imyeyu/api/modules/gao/controller/GaoRegistrationEventRecordController.java b/src/main/java/com/imyeyu/api/modules/gao/controller/GaoRegistrationEventRecordController.java deleted file mode 100644 index 6cdfc4a..0000000 --- a/src/main/java/com/imyeyu/api/modules/gao/controller/GaoRegistrationEventRecordController.java +++ /dev/null @@ -1,201 +0,0 @@ -package com.imyeyu.api.modules.gao.controller; - -import com.fasterxml.jackson.annotation.JsonView; -import com.imyeyu.api.modules.gao.bean.GaoPermissionCode; -import com.imyeyu.api.modules.gao.bean.GaoRoleCode; -import com.imyeyu.api.bean.RoleMatchMode; -import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission; -import com.imyeyu.api.modules.gao.annotation.RequireGaoRole; -import com.imyeyu.api.modules.gao.entity.GaoRegistrationEventRecord; -import com.imyeyu.api.modules.gao.service.GaoRegistrationEventRecordService; -import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventPeriodStatView; -import com.imyeyu.api.modules.gao.vo.GaoCustomerRegisterStatView; -import com.imyeyu.api.modules.gao.vo.GaoCustomerRegistrationRankView; -import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordCreateReq; -import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordCalendarView; -import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordDailyStatView; -import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordListItemView; -import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordPeriodReq; -import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordRangeReq; -import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordStatReq; -import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordTrendStatReq; -import com.imyeyu.spring.annotation.AOPLog; -import com.imyeyu.spring.annotation.IgnoreGlobalReturn; -import com.imyeyu.spring.annotation.RequestRateLimit; -import com.imyeyu.spring.bean.Page; -import com.imyeyu.spring.bean.PageResult; -import com.imyeyu.spring.util.ResponseView; -import jakarta.servlet.http.HttpServletResponse; -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; - -import java.io.IOException; -import java.net.URLEncoder; -import java.util.List; - -/// -/// GAO 登记事件记录接口 -/// -/// @author Codex -/// @since 2026-07-27 -@RestController -@RequiredArgsConstructor -@RequestMapping("/gao/registration-event-record") -@RequireGaoRole(value = {GaoRoleCode.STORE_MANAGER, GaoRoleCode.EMPLOYEE}, mode = RoleMatchMode.ANY) -public class GaoRegistrationEventRecordController { - - private final GaoRegistrationEventRecordService service; - - @AOPLog - @RequestRateLimit - @RequireGaoPermission(GaoPermissionCode.REGISTRATION_EVENT_RECORD_READ) - @PostMapping("/list") - @JsonView(ResponseView.Public.class) - public PageResult list(@RequestBody Page page) { - return service.page(page); - } - - @AOPLog - @RequestRateLimit - @RequireGaoPermission(GaoPermissionCode.REGISTRATION_EVENT_RECORD_READ) - @PostMapping("/range/page") - public PageResult rangePage(@RequestBody Page page) { - return service.pageByRange(page); - } - - @AOPLog - @RequestRateLimit - @RequireGaoPermission(GaoPermissionCode.REGISTRATION_EVENT_RECORD_READ) - @PostMapping("/detail") - @JsonView(ResponseView.Public.class) - public GaoRegistrationEventRecord detail(@RequestParam String id) { - return service.get(id); - } - - @AOPLog - @RequestRateLimit - @PostMapping("/create") - @JsonView(ResponseView.Public.class) - public List create(@RequestBody GaoRegistrationEventRecordCreateReq req) { - return service.createRecords(req); - } - - @AOPLog - @RequestRateLimit - @RequireGaoPermission(GaoPermissionCode.REGISTRATION_EVENT_RECORD_UPDATE) - @PostMapping("/update") - public void update(@RequestBody GaoRegistrationEventRecord record) { - service.update(record); - } - - @AOPLog - @RequestRateLimit - @RequireGaoPermission(GaoPermissionCode.REGISTRATION_EVENT_RECORD_DELETE) - @PostMapping("/delete") - public void delete(@RequestParam String id) { - service.delete(id); - } - - @AOPLog - @RequestRateLimit - @RequireGaoPermission(GaoPermissionCode.REGISTRATION_EVENT_RECORD_READ) - @PostMapping("/period/list") - @JsonView(ResponseView.Public.class) - public List periodList(@RequestBody GaoRegistrationEventRecordPeriodReq req) { - return service.listByRegistrationEventAndPeriod(req); - } - - @AOPLog - @RequestRateLimit - @RequireGaoPermission(GaoPermissionCode.REGISTRATION_EVENT_RECORD_READ) - @PostMapping("/range/list") - @JsonView(ResponseView.Public.class) - public List rangeList(@RequestBody GaoRegistrationEventRecordRangeReq req) { - return service.listByRange(req); - } - - @AOPLog - @RequestRateLimit - @RequireGaoPermission(GaoPermissionCode.REGISTRATION_EVENT_RECORD_READ) - @PostMapping("/range/calendar/list") - public List calendarRangeList(@RequestBody GaoRegistrationEventRecordRangeReq req) { - return service.listCalendarByRange(req); - } - - @AOPLog - @RequestRateLimit - @IgnoreGlobalReturn - @RequireGaoPermission(GaoPermissionCode.REGISTRATION_EVENT_RECORD_EXPORT) - @PostMapping("/range/export") - public void rangeExport(@RequestBody GaoRegistrationEventRecordRangeReq req, HttpServletResponse resp) throws IOException { - String fileName = URLEncoder.encode("登记事件记录.xlsx", "UTF-8").replace("+", "%20"); - resp.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); - resp.setHeader("Content-Disposition", "attachment; filename=\"gao-registration-event-record.xlsx\"; filename*=UTF-8''" + fileName); - resp.getOutputStream().write(service.exportRangeExcel(req)); - } - - @AOPLog - @RequestRateLimit - @RequireGaoPermission(GaoPermissionCode.STAT_READ) - @PostMapping("/period/stat") - public List periodStat(@RequestBody GaoRegistrationEventRecordPeriodReq req) { - return service.statByRegistrationEventAndPeriodType(req.getRegistrationEventId(), req.getPeriodType()); - } - - @AOPLog - @RequestRateLimit - @RequireGaoPermission(GaoPermissionCode.STAT_READ) - @PostMapping("/customer/stat") - public List customerStat(@RequestParam String customerId) { - return service.statByCustomerId(customerId); - } - - @AOPLog - @RequestRateLimit - @RequireGaoPermission(GaoPermissionCode.STAT_READ) - @PostMapping("/customer/range/stat") - /// 按时间段统计单个客户各登记事件记录数 - public List customerRangeStat(@RequestBody GaoRegistrationEventRecordStatReq req) { - return service.statByCustomerAndRange(req); - } - - @AOPLog - @RequestRateLimit - @RequireGaoPermission(GaoPermissionCode.STAT_READ) - @PostMapping("/all-customer/range/stat") - /// 按时间段统计全部客户各登记事件记录数 - public List allCustomerRangeStat(@RequestBody GaoRegistrationEventRecordStatReq req) { - return service.statAllCustomerByRange(req); - } - - @AOPLog - @RequestRateLimit - @RequireGaoPermission(GaoPermissionCode.STAT_READ) - @PostMapping("/customer/registration-rank") - /// 按时间范围查询客户登记排行榜 - public List customerRegistrationRank(@RequestBody(required = false) GaoRegistrationEventRecordRangeReq req) { - return service.listCustomerRegistrationRank(req); - } - - @AOPLog - @RequestRateLimit - @RequireGaoPermission(GaoPermissionCode.STAT_READ) - @PostMapping("/customer/registration-event/daily/stat") - /// 按天统计单个客户单个登记事件记录趋势 - public List customerRegistrationEventDailyStat(@RequestBody GaoRegistrationEventRecordTrendStatReq req) { - return service.statDailyByCustomerAndRegistrationEvent(req); - } - - @AOPLog - @RequestRateLimit - @RequireGaoPermission(GaoPermissionCode.STAT_READ) - @PostMapping("/all-customer/registration-event/daily/stat") - /// 按天统计全部客户单个登记事件记录趋势 - public List allCustomerRegistrationEventDailyStat(@RequestBody GaoRegistrationEventRecordTrendStatReq req) { - return service.statDailyByRegistrationEvent(req); - } -} diff --git a/src/main/java/com/imyeyu/api/modules/gao/controller/GaoRoleController.java b/src/main/java/com/imyeyu/api/modules/gao/controller/GaoRoleController.java index cff4cd3..cbc4d27 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/controller/GaoRoleController.java +++ b/src/main/java/com/imyeyu/api/modules/gao/controller/GaoRoleController.java @@ -14,15 +14,14 @@ import org.springframework.web.bind.annotation.RestController; import java.util.List; -/// /// GAO 角色接口 /// -/// @author Codex -/// @since 2026-07-28 +/// @author 夜雨 +/// @since 2026-07-28 14:15 @RestController +@RequireGaoRole(GaoRoleCode.STORE_MANAGER) @RequiredArgsConstructor @RequestMapping("/gao/role") -@RequireGaoRole(GaoRoleCode.STORE_MANAGER) public class GaoRoleController { private final RoleService roleService; 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 1d38644..cf26252 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 @@ -24,11 +24,10 @@ import org.springframework.web.bind.annotation.RestController; import java.util.List; -/// /// GAO 用户接口 /// -/// @author Codex -/// @since 2026-07-30 +/// @author 夜雨 +/// @since 2026-07-30 14:15 @RestController @RequiredArgsConstructor @RequestMapping("/gao/user") 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 a37ed88..9a5b573 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 @@ -1,11 +1,11 @@ package com.imyeyu.api.modules.gao.entity; -import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.databind.JsonNode; import com.imyeyu.api.modules.common.entity.Attachment; -import com.imyeyu.api.modules.user.entity.User; import com.imyeyu.api.modules.user.bean.Gender; +import com.imyeyu.api.modules.user.entity.User; +import com.imyeyu.java.TimiJava; import com.imyeyu.spring.annotation.table.Transient; import com.imyeyu.spring.entity.UUIDEntity; import lombok.Data; @@ -55,7 +55,7 @@ public class GaoCustomer extends UUIDEntity { private Gender gender; /// 年龄 - private Integer age; + private Long age; /// 出生日期,数据库只保存公历日期 @JsonFormat(pattern = "yyyy-MM-dd") @@ -64,15 +64,6 @@ public class GaoCustomer extends UUIDEntity { /// 出生日期录入时使用的历法 private BirthdateCalendar birthdateCalendar; - @JsonIgnore - @Transient - private boolean birthdateProvided; - - public void setBirthdate(LocalDate birthdate) { - this.birthdate = birthdate; - this.birthdateProvided = true; - } - /// 电话 private String telephone; @@ -91,24 +82,24 @@ public class GaoCustomer extends UUIDEntity { /// 介绍人客户 ID private String introducerCustomerId; + private Long invitedCount; + /// 录入用户 ID private String creatorUserId; - @Transient - private Long lastSignInAt; - @Transient private GaoCustomer introducerCustomer; @Transient private User creatorUser; - private Long invitedCount; - @Transient private List attachmentList; - /// 临时照片附件 ID 列表 @Transient - private List attachmentIdList; + private GaoEventRecord eventRecord; + + public boolean hasIntroducer() { + return TimiJava.isNotEmpty(introducerCustomerId); + } } diff --git a/src/main/java/com/imyeyu/api/modules/gao/entity/GaoRegistrationEvent.java b/src/main/java/com/imyeyu/api/modules/gao/entity/GaoEvent.java similarity index 77% rename from src/main/java/com/imyeyu/api/modules/gao/entity/GaoRegistrationEvent.java rename to src/main/java/com/imyeyu/api/modules/gao/entity/GaoEvent.java index 40a792b..1142178 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/entity/GaoRegistrationEvent.java +++ b/src/main/java/com/imyeyu/api/modules/gao/entity/GaoEvent.java @@ -2,8 +2,10 @@ package com.imyeyu.api.modules.gao.entity; import com.imyeyu.spring.annotation.table.Transient; import com.imyeyu.spring.entity.UUIDEntity; +import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.Getter; import java.util.List; @@ -14,7 +16,7 @@ import java.util.List; /// @since 2026-07-27 @Data @EqualsAndHashCode(callSuper = true) -public class GaoRegistrationEvent extends UUIDEntity { +public class GaoEvent extends UUIDEntity { /// /// 登记事件状态 @@ -36,33 +38,36 @@ public class GaoRegistrationEvent extends UUIDEntity { DELETED } - /// /// 登记限制类型 /// - /// @author Codex - /// @since 2026-08-01 + /// @author 夜雨 + /// @since 2026-08-01 14:37 + @Getter + @AllArgsConstructor public enum LimitType { /// 不限制 - NONE, + NONE(null), - /// 每天上午、下午各一次 - DAILY_HALF_DAY, + /// 每天上午、下午各 N 次 + DAILY_HALF_DAY(1L), - /// 每个自然日一次 - NATURAL_DAY, + /// 每个自然日 N 次 + NATURAL_DAY(1L), /// 每隔 N 小时一次 - INTERVAL_HOUR, + INTERVAL_HOUR(1L), - /// 每个自然月一次 - NATURAL_MONTH, + /// 每个自然月 N 次 + NATURAL_MONTH(1L), /// 事件总登记次数上限 - TOTAL_LIMIT, + TOTAL_LIMIT(1L), /// 单个客户总登记次数上限 - CUSTOMER_TOTAL_LIMIT + CUSTOMER_TOTAL_LIMIT(1L); + + final Long min; } /// 登记事件名称 diff --git a/src/main/java/com/imyeyu/api/modules/gao/entity/GaoRegistrationEventRecord.java b/src/main/java/com/imyeyu/api/modules/gao/entity/GaoEventRecord.java similarity index 81% rename from src/main/java/com/imyeyu/api/modules/gao/entity/GaoRegistrationEventRecord.java rename to src/main/java/com/imyeyu/api/modules/gao/entity/GaoEventRecord.java index ec92999..4dacd05 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/entity/GaoRegistrationEventRecord.java +++ b/src/main/java/com/imyeyu/api/modules/gao/entity/GaoEventRecord.java @@ -16,7 +16,7 @@ import java.util.List; /// @since 2026-07-27 @Data @EqualsAndHashCode(callSuper = true) -public class GaoRegistrationEventRecord extends UUIDEntity { +public class GaoEventRecord extends UUIDEntity { /// /// 登记事件记录附件类型 @@ -32,7 +32,7 @@ public class GaoRegistrationEventRecord extends UUIDEntity { private String customerId; /// 登记登记事件 ID - private String registrationEventId; + private String eventId; /// 操作管理员用户 ID private String operatorUserId; @@ -47,7 +47,7 @@ public class GaoRegistrationEventRecord extends UUIDEntity { private GaoCustomer customer; @Transient - private GaoRegistrationEvent registrationEvent; + private GaoEvent event; @Transient private User operator; @@ -55,7 +55,7 @@ public class GaoRegistrationEventRecord extends UUIDEntity { @Transient private List attachmentList; - /// 临时照片附件 ID 列表 + /// 客户 ID 列表,批量登记时使用 @Transient - private List attachmentIdList; + private List customerIdList; } diff --git a/src/main/java/com/imyeyu/api/modules/gao/entity/GaoRegistrationEventRoleRelation.java b/src/main/java/com/imyeyu/api/modules/gao/entity/GaoEventRoleRelation.java similarity index 76% rename from src/main/java/com/imyeyu/api/modules/gao/entity/GaoRegistrationEventRoleRelation.java rename to src/main/java/com/imyeyu/api/modules/gao/entity/GaoEventRoleRelation.java index 0922068..e5f0ba7 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/entity/GaoRegistrationEventRoleRelation.java +++ b/src/main/java/com/imyeyu/api/modules/gao/entity/GaoEventRoleRelation.java @@ -11,10 +11,10 @@ import lombok.EqualsAndHashCode; /// @since 2026-07-28 @Data @EqualsAndHashCode(callSuper = true) -public class GaoRegistrationEventRoleRelation extends UUIDEntity { +public class GaoEventRoleRelation extends UUIDEntity { /// 登记事件 ID - private String registrationEventId; + private String eventId; /// 角色代码 private String roleCode; diff --git a/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoCustomerMapper.java b/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoCustomerMapper.java index 3c6aa26..1ee6b93 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoCustomerMapper.java +++ b/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoCustomerMapper.java @@ -1,12 +1,11 @@ package com.imyeyu.api.modules.gao.mapper; import com.imyeyu.api.modules.gao.entity.GaoCustomer; -import com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStatView; +import com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStateView; import com.imyeyu.api.modules.gao.vo.GaoCustomerGenderStatView; import com.imyeyu.spring.mapper.BaseMapper; -import org.apache.ibatis.annotations.Param; -import org.apache.ibatis.annotations.Select; +import java.util.Collection; import java.util.List; /// @@ -16,41 +15,23 @@ import java.util.List; /// @since 2026-07-27 public interface GaoCustomerMapper extends BaseMapper { - @Select("SELECT * FROM `gao_customer` WHERE `code` = #{code} AND `deleted_at` IS NULL LIMIT 1") - GaoCustomer selectByCode(@Param("code") String code); - - @Select("SELECT * FROM `gao_customer` WHERE `name` = #{name} AND `deleted_at` IS NULL LIMIT 1") - GaoCustomer selectByName(@Param("name") String name); - - List selectByIdList(@Param("idList") List idList); - - List search(@Param("keyword") String keyword, @Param("introducerCustomerId") String introducerCustomerId); - - Long countSearch(@Param("keyword") String keyword, @Param("introducerCustomerId") String introducerCustomerId); - - List searchPage(@Param("keyword") String keyword, @Param("introducerCustomerId") String introducerCustomerId, @Param("offset") Long offset, @Param("size") Long size); - - List selectInvitedList(@Param("introducerCustomerId") String introducerCustomerId); - - Long countInvited(@Param("introducerCustomerId") String introducerCustomerId); - - void updateInvitedCountDelta(@Param("customerId") String customerId, @Param("delta") Long delta); + List selectByIdList(Collection idList); /// 统计每日新增客户数 /// /// @param beginCreatedAt 开始创建时间 /// @param endCreatedAt 结束创建时间 /// @return 每日新增客户数 - List statDailyNewCustomer(@Param("beginCreatedAt") Long beginCreatedAt, @Param("endCreatedAt") Long endCreatedAt); + List stateDailyNewCustomer(Long beginCreatedAt, Long endCreatedAt); /// 统计开始时间前客户总数 /// /// @param beginCreatedAt 开始创建时间 /// @return 客户总数 - Long countCreatedBefore(@Param("beginCreatedAt") Long beginCreatedAt); + Long countCreatedBefore(Long beginCreatedAt); /// 统计客户性别分布 /// /// @return 性别统计列表 - List statGender(); + List stateGender(); } diff --git a/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoEventMapper.java b/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoEventMapper.java new file mode 100644 index 0000000..56040d3 --- /dev/null +++ b/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoEventMapper.java @@ -0,0 +1,19 @@ +package com.imyeyu.api.modules.gao.mapper; + +import com.imyeyu.api.modules.gao.entity.GaoEvent; +import com.imyeyu.spring.mapper.BaseMapper; + +import java.util.Collection; +import java.util.List; + +/// +/// 登记事件 Mapper +/// +/// @author 夜雨 +/// @since 2026-07-27 15:53 +public interface GaoEventMapper extends BaseMapper { + + List selectByIdList(Collection idList); + + List selectValidList(Long now); +} 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 new file mode 100644 index 0000000..460900a --- /dev/null +++ b/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoEventRecordMapper.java @@ -0,0 +1,37 @@ +package com.imyeyu.api.modules.gao.mapper; + +import com.imyeyu.api.modules.gao.entity.GaoEventRecord; +import com.imyeyu.api.modules.gao.vo.GaoCustomerEventRankView; +import com.imyeyu.api.modules.gao.vo.GaoEventRecordPage; +import com.imyeyu.api.modules.gao.vo.GaoEventRecordRankPage; +import com.imyeyu.spring.mapper.BaseMapper; + +import java.util.List; + +/// 登记事件记录 Mapper +/// +/// @author 夜雨 +/// @since 2026-07-27 14:11 +public interface GaoEventRecordMapper extends BaseMapper { + + Long count(String customerId, String eventId, Long beginAt, Long endAt); + + long countByRange(GaoEventRecordPage page); + + /// 按任意时间范围查询登记事件记录 + /// + /// @return 登记事件记录列表 + List selectByRange(GaoEventRecordPage page); + + /// 统计客户登记排行榜总数 + /// + /// @param page 排行榜分页请求 + /// @return 客户数 + long countRankByRange(GaoEventRecordRankPage page); + + /// 分页查询客户登记排行榜 + /// + /// @param page 排行榜分页请求 + /// @return 客户登记排行榜 + List selectRankByRange(GaoEventRecordRankPage page); +} diff --git a/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoEventRoleRelationMapper.java b/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoEventRoleRelationMapper.java new file mode 100644 index 0000000..ec5cc81 --- /dev/null +++ b/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoEventRoleRelationMapper.java @@ -0,0 +1,16 @@ +package com.imyeyu.api.modules.gao.mapper; + +import com.imyeyu.api.modules.gao.entity.GaoEventRoleRelation; +import com.imyeyu.spring.mapper.BaseMapper; + +import java.util.Collection; +import java.util.List; + +/// 登记事件角色关联 Mapper +/// +/// @author 夜雨 +/// @since 2026-07-28 14:23 +public interface GaoEventRoleRelationMapper extends BaseMapper { + + List selectAllByEventIdList(Collection idList); +} diff --git a/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoRegistrationEventMapper.java b/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoRegistrationEventMapper.java deleted file mode 100644 index 5ccd039..0000000 --- a/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoRegistrationEventMapper.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.imyeyu.api.modules.gao.mapper; - -import com.imyeyu.api.modules.gao.entity.GaoRegistrationEvent; -import com.imyeyu.spring.mapper.BaseMapper; -import org.apache.ibatis.annotations.Param; - -import java.util.List; - -/// -/// 登记事件 Mapper -/// -/// @author Codex -/// @since 2026-07-27 -public interface GaoRegistrationEventMapper extends BaseMapper { - - List selectByIdList(@Param("idList") List idList); - - List selectEnabledList(@Param("now") Long now); -} diff --git a/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoRegistrationEventRecordMapper.java b/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoRegistrationEventRecordMapper.java deleted file mode 100644 index 71c1dc3..0000000 --- a/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoRegistrationEventRecordMapper.java +++ /dev/null @@ -1,157 +0,0 @@ -package com.imyeyu.api.modules.gao.mapper; - -import com.imyeyu.api.modules.gao.entity.GaoRegistrationEventRecord; -import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventPeriodStatView; -import com.imyeyu.api.modules.gao.vo.GaoCustomerRegisterStatView; -import com.imyeyu.api.modules.gao.vo.GaoCustomerRegistrationDayStatView; -import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordCalendarView; -import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordDailyStatView; -import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordListItemView; -import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordPeriodReq; -import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordRangeReq; -import com.imyeyu.spring.mapper.BaseMapper; -import org.apache.ibatis.annotations.Param; - -import java.util.List; - -/// -/// 登记事件记录 Mapper -/// -/// @author Codex -/// @since 2026-07-27 -public interface GaoRegistrationEventRecordMapper extends BaseMapper { - - /// 按登记事件查询客户最后登记事件记录 - /// - /// @param customerIdList 客户 ID 列表 - /// @param registrationEventId 登记事件 ID - /// @return 最后登记事件记录列表 - List selectByRegistrationEventAndPeriod(@Param("registrationEventId") String registrationEventId, @Param("periodType") GaoRegistrationEventRecordPeriodReq.PeriodType periodType, @Param("periodValue") Integer periodValue); - - /// 统计客户指定登记事件在时间范围内的登记数 - /// - /// @param customerId 客户 ID - /// @param registrationEventId 登记事件 ID - /// @param beginRegisteredAt 开始登记时间 - /// @param endRegisteredAt 结束登记时间 - /// @return 登记数 - Long countByCustomerIdAndRegistrationEventIdRange(@Param("customerId") String customerId, @Param("registrationEventId") String registrationEventId, @Param("beginRegisteredAt") Long beginRegisteredAt, @Param("endRegisteredAt") Long endRegisteredAt); - - /// 统计客户指定登记事件总登记数 - /// - /// @param customerId 客户 ID - /// @param registrationEventId 登记事件 ID - /// @return 登记数 - Long countByCustomerIdAndRegistrationEventId(@Param("customerId") String customerId, @Param("registrationEventId") String registrationEventId); - - /// 统计登记事件总登记数 - /// - /// @param registrationEventId 登记事件 ID - /// @return 登记数 - Long countByRegistrationEventId(@Param("registrationEventId") String registrationEventId); - - /// 按周期统计登记事件记录 - /// - /// @param registrationEventId 登记事件 ID - /// @param periodType 周期类型 - /// @return 统计结果 - List statByRegistrationEventAndPeriodType(@Param("registrationEventId") String registrationEventId, @Param("periodType") GaoRegistrationEventRecordPeriodReq.PeriodType periodType); - - /// 统计客户所有登记 - /// - /// @param customerId 客户 ID - /// @return 统计结果 - List statByCustomerId(@Param("customerId") String customerId); - - /// 按时间段统计单个客户登记 - /// - /// @param customerId 客户 ID - /// @param beginRegisteredAt 开始登记时间 - /// @param endRegisteredAt 结束登记时间 - /// @return 统计结果 - List statByCustomerIdAndRange(@Param("customerId") String customerId, @Param("beginRegisteredAt") Long beginRegisteredAt, @Param("endRegisteredAt") Long endRegisteredAt); - - /// 按时间段统计全部客户登记 - /// - /// @param beginRegisteredAt 开始登记时间 - /// @param endRegisteredAt 结束登记时间 - /// @return 统计结果 - List statAllCustomerByRange(@Param("beginRegisteredAt") Long beginRegisteredAt, @Param("endRegisteredAt") Long endRegisteredAt); - - /// 按时间范围统计全部客户每日登记 - /// - /// @param req 范围查询请求 - /// @return 每个有登记客户的每日统计 - List statCustomerRegistrationDay(@Param("req") GaoRegistrationEventRecordRangeReq req); - - /// 统计单个客户单个登记事件的每日登记事件记录数 - /// - /// @param customerId 客户 ID - /// @param registrationEventId 登记事件 ID - /// @param beginRegisteredAt 开始登记时间 - /// @param endRegisteredAt 结束登记时间 - /// @return 每日统计 - List statDailyCountByCustomerIdAndRegistrationEventIdRange(@Param("customerId") String customerId, @Param("registrationEventId") String registrationEventId, @Param("beginRegisteredAt") Long beginRegisteredAt, @Param("endRegisteredAt") Long endRegisteredAt); - - /// 统计全部客户单个登记事件的每日登记事件记录数 - /// - /// @param registrationEventId 登记事件 ID - /// @param beginRegisteredAt 开始登记时间 - /// @param endRegisteredAt 结束登记时间 - /// @return 每日统计 - List statDailyCountByRegistrationEventIdRange(@Param("registrationEventId") String registrationEventId, @Param("beginRegisteredAt") Long beginRegisteredAt, @Param("endRegisteredAt") Long endRegisteredAt); - - /// 统计时间段前单个客户单个登记事件累计登记事件记录数 - /// - /// @param customerId 客户 ID - /// @param registrationEventId 登记事件 ID - /// @param beginRegisteredAt 开始登记时间 - /// @return 累计登记数 - Long countBeforeByCustomerIdAndRegistrationEventId(@Param("customerId") String customerId, @Param("registrationEventId") String registrationEventId, @Param("beginRegisteredAt") Long beginRegisteredAt); - - /// 统计时间段前全部客户单个登记事件累计登记事件记录数 - /// - /// @param registrationEventId 登记事件 ID - /// @param beginRegisteredAt 开始登记时间 - /// @return 累计登记数 - Long countBeforeByRegistrationEventId(@Param("registrationEventId") String registrationEventId, @Param("beginRegisteredAt") Long beginRegisteredAt); - - /// 查询客户指定登记事件的最后登记事件记录 - /// - /// @param customerIdList 客户 ID 列表 - /// @param registrationEventId 登记事件 ID - /// @return 最后登记事件记录列表 - List selectLatestByCustomerIdListAndRegistrationEventId(@Param("customerIdList") List customerIdList, @Param("registrationEventId") String registrationEventId); - - /// 查询客户全部登记事件的最后登记事件记录 - /// - /// @param customerId 客户 ID - /// @return 最后登记事件记录列表 - List selectLatestByCustomerId(@Param("customerId") String customerId); - - /// 按任意时间范围查询登记事件记录 - /// - /// @param req 范围查询请求 - /// @return 登记事件记录列表 - List selectByRange(@Param("req") GaoRegistrationEventRecordRangeReq req); - - /// 按任意时间范围查询日历轻量记录 - /// - /// @param req 范围查询请求 - /// @return 日历轻量记录列表 - List selectCalendarByRange(@Param("req") GaoRegistrationEventRecordRangeReq req); - - /// 按任意时间范围统计登记事件记录 - /// - /// @param req 范围查询请求 - /// @return 登记事件记录数 - Long countByRange(@Param("req") GaoRegistrationEventRecordRangeReq req); - - /// 按任意时间范围分页查询登记事件记录 - /// - /// @param req 范围查询请求 - /// @param offset 偏移量 - /// @param size 每页条数 - /// @return 登记事件记录列表 - List selectPageByRange(@Param("req") GaoRegistrationEventRecordRangeReq req, @Param("offset") Long offset, @Param("size") Long size); -} diff --git a/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoRegistrationEventRoleRelationMapper.java b/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoRegistrationEventRoleRelationMapper.java deleted file mode 100644 index 0a66c9d..0000000 --- a/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoRegistrationEventRoleRelationMapper.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.imyeyu.api.modules.gao.mapper; - -import com.imyeyu.api.modules.gao.entity.GaoRegistrationEventRoleRelation; -import com.imyeyu.spring.mapper.BaseMapper; -import org.apache.ibatis.annotations.Param; -import org.apache.ibatis.annotations.Select; -import org.apache.ibatis.annotations.Update; - -import java.util.List; - -/// -/// 登记事件角色关联 Mapper -/// -/// @author Codex -/// @since 2026-07-28 -public interface GaoRegistrationEventRoleRelationMapper extends BaseMapper { - - @Select("SELECT `role_code` FROM `gao_registration_event_role_relation` WHERE `registration_event_id` = #{registrationEventId} AND `deleted_at` IS NULL ORDER BY `created_at` ASC") - List selectRoleCodeListByRegistrationEventId(@Param("registrationEventId") String registrationEventId); - - @Select({ - "" - }) - List selectByRegistrationEventIdList(@Param("registrationEventIdList") List registrationEventIdList); - - @Update("UPDATE `gao_registration_event_role_relation` SET `deleted_at` = UNIX_TIMESTAMP() WHERE `registration_event_id` = #{registrationEventId} AND `deleted_at` IS NULL") - void deleteByRegistrationEventId(@Param("registrationEventId") String registrationEventId); -} diff --git a/src/main/java/com/imyeyu/api/modules/gao/service/GaoCustomerService.java b/src/main/java/com/imyeyu/api/modules/gao/service/GaoCustomerService.java index 928e23f..f544164 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/service/GaoCustomerService.java +++ b/src/main/java/com/imyeyu/api/modules/gao/service/GaoCustomerService.java @@ -1,23 +1,22 @@ package com.imyeyu.api.modules.gao.service; import com.imyeyu.api.modules.gao.entity.GaoCustomer; -import com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStatView; +import com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStateView; import com.imyeyu.api.modules.gao.vo.GaoCustomerGenderStatView; -import com.imyeyu.api.modules.gao.vo.GaoCustomerInviteUpdateReq; -import com.imyeyu.api.modules.gao.vo.GaoCustomerSearchReq; -import com.imyeyu.api.modules.gao.vo.GaoCustomerTrendStatReq; +import com.imyeyu.api.modules.gao.vo.GaoCustomerTrendStateReq; import com.imyeyu.spring.bean.Page; -import com.imyeyu.spring.bean.PageResult; import com.imyeyu.spring.service.BaseService; import java.io.IOException; +import java.util.Collection; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; -/// /// 客户服务 /// -/// @author Codex -/// @since 2026-07-27 +/// @author 夜雨 +/// @since 2026-07-27 14:12 public interface GaoCustomerService extends BaseService { /// 按编码查询客户 @@ -26,35 +25,18 @@ public interface GaoCustomerService extends BaseService { /// @return 客户 GaoCustomer getByCode(String code); - /// 按编码查询客户,不存在时返回 null - /// - /// @param code 客户编码 - /// @return 客户 - GaoCustomer getNullableByCode(String code); + GaoCustomer getByName(String name); - /// 按编码获取或快速创建客户 - /// - /// @param code 客户编码 - /// @return 客户 - GaoCustomer getOrQuickCreateByCode(String code); + List listByIdList(Collection idList); - /// 搜索客户 - /// - /// @param req 搜索请求 - /// @return 客户列表 - List search(GaoCustomerSearchReq req); - - /// 分页搜索客户 - /// - /// @param page 分页搜索请求 - /// @return 客户分页结果 - PageResult searchPage(Page page); + default Map mapByIdList(Collection idList) { + return listByIdList(idList).stream().collect(Collectors.toMap(GaoCustomer::getId, item -> item)); + } /// 按搜索条件导出客户 Excel /// - /// @param req 搜索请求 /// @return xlsx 文件字节 - byte[] exportSearchExcel(GaoCustomerSearchReq req) throws IOException; + byte[] export(Page page) throws IOException; /// 查询邀请的客户 /// @@ -64,8 +46,9 @@ public interface GaoCustomerService extends BaseService { /// 保存邀请关系 /// - /// @param req 邀请关系请求 - void saveInviteRelation(GaoCustomerInviteUpdateReq req); + /// @param introducerCustomerId 邀请来源客户 ID + /// @param customerId 客户 ID + void saveInviteRelation(String introducerCustomerId, String customerId); /// 删除邀请关系 /// @@ -76,10 +59,10 @@ public interface GaoCustomerService extends BaseService { /// /// @param req 趋势统计请求 /// @return 每日趋势列表 - List statDailyTrend(GaoCustomerTrendStatReq req); + List stateDailyTrend(GaoCustomerTrendStateReq req); /// 统计客户性别分布 /// /// @return 性别统计列表 - List statGender(); + List stateGender(); } diff --git a/src/main/java/com/imyeyu/api/modules/gao/service/GaoEventRecordService.java b/src/main/java/com/imyeyu/api/modules/gao/service/GaoEventRecordService.java new file mode 100644 index 0000000..3ad884f --- /dev/null +++ b/src/main/java/com/imyeyu/api/modules/gao/service/GaoEventRecordService.java @@ -0,0 +1,35 @@ +package com.imyeyu.api.modules.gao.service; + +import com.imyeyu.api.modules.gao.entity.GaoEventRecord; +import com.imyeyu.api.modules.gao.vo.GaoCustomerEventRankView; +import com.imyeyu.api.modules.gao.vo.GaoEventRecordPage; +import com.imyeyu.api.modules.gao.vo.GaoEventRecordRankPage; +import com.imyeyu.spring.bean.PageResult; +import com.imyeyu.spring.service.BaseService; + +import java.io.IOException; + +/// 登记事件记录服务 +/// +/// @author 夜雨 +/// @since 2026-07-27 14:09 +public interface GaoEventRecordService extends BaseService { + + /// 按任意时间范围查询登记事件记录 + /// + /// @param req 范围查询请求 + /// @return 登记事件记录列表 + PageResult pageByRange(GaoEventRecordPage req); + + /// 按时间范围查询客户登记排行榜 + /// + /// @param page 范围查询请求,不传时间为全部时间 + /// @return 客户登记排行榜 + PageResult pageRankByRange(GaoEventRecordRankPage page); + + /// 按任意时间范围导出登记事件记录 Excel + /// + /// @param page 范围查询请求 + /// @return xlsx 文件字节 + byte[] export(GaoEventRecordPage page) throws IOException; +} diff --git a/src/main/java/com/imyeyu/api/modules/gao/service/GaoEventService.java b/src/main/java/com/imyeyu/api/modules/gao/service/GaoEventService.java new file mode 100644 index 0000000..f37f2db --- /dev/null +++ b/src/main/java/com/imyeyu/api/modules/gao/service/GaoEventService.java @@ -0,0 +1,36 @@ +package com.imyeyu.api.modules.gao.service; + +import com.imyeyu.api.modules.gao.entity.GaoEvent; +import com.imyeyu.spring.service.BaseService; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/// +/// 登记事件服务 +/// +/// @author Codex +/// @since 2026-07-27 +public interface GaoEventService extends BaseService { + + Map mapByIdList(Collection idList); + + Map> mapRoleCodeListByEventIdList(Collection eventIdList); + + /// 查询启用的登记事件 + /// + /// @return 登记事件列表 + List listValid(); + + /// 查询登记事件要求的角色 + /// + /// @param eventId 登记事件 ID + /// @return 角色代码列表 + List listRoleCode(String eventId); + + /// 按客户端提交的顺序更新登记事件排序 + /// + /// @param idList 登记事件 ID 列表 + void sort(List idList); +} diff --git a/src/main/java/com/imyeyu/api/modules/gao/service/GaoRegistrationEventRecordService.java b/src/main/java/com/imyeyu/api/modules/gao/service/GaoRegistrationEventRecordService.java deleted file mode 100644 index 6ad2a85..0000000 --- a/src/main/java/com/imyeyu/api/modules/gao/service/GaoRegistrationEventRecordService.java +++ /dev/null @@ -1,127 +0,0 @@ -package com.imyeyu.api.modules.gao.service; - -import com.imyeyu.api.modules.gao.entity.GaoRegistrationEventRecord; -import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventPeriodStatView; -import com.imyeyu.api.modules.gao.vo.GaoCustomerRegisterStatView; -import com.imyeyu.api.modules.gao.vo.GaoCustomerRegistrationRankView; -import com.imyeyu.api.modules.gao.vo.GaoQuickRegistrationEventRecordReq; -import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordCalendarView; -import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordCreateReq; -import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordDailyStatView; -import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordListItemView; -import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordPeriodReq; -import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordRangeReq; -import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordStatReq; -import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordTrendStatReq; -import com.imyeyu.spring.bean.Page; -import com.imyeyu.spring.bean.PageResult; -import com.imyeyu.spring.service.BaseService; - -import java.io.IOException; -import java.util.List; - -/// -/// 登记事件记录服务 -/// -/// @author Codex -/// @since 2026-07-27 -public interface GaoRegistrationEventRecordService extends BaseService { - - /// 批量创建登记事件记录 - /// - /// @param req 批量创建请求 - /// @return 创建后的登记事件记录列表 - List createRecords(GaoRegistrationEventRecordCreateReq req); - - /// 快速录入客户并创建登记事件记录 - /// - /// @param req 快速录入请求 - /// @return 创建后的登记事件记录 - GaoRegistrationEventRecord quickRegister(GaoQuickRegistrationEventRecordReq req); - - /// 按任意时间范围查询登记事件记录 - /// - /// @param req 范围查询请求 - /// @return 登记事件记录列表 - List listByRange(GaoRegistrationEventRecordRangeReq req); - - /// 按任意时间范围查询日历轻量记录 - /// - /// @param req 范围查询请求 - /// @return 日历轻量记录列表 - List listCalendarByRange(GaoRegistrationEventRecordRangeReq req); - - /// 按任意时间范围分页查询登记事件记录 - /// - /// @param page 分页参数 - /// @return 登记事件记录分页结果 - PageResult pageByRange(Page page); - - /// 按任意时间范围导出登记事件记录 Excel - /// - /// @param req 范围查询请求 - /// @return xlsx 文件字节 - byte[] exportRangeExcel(GaoRegistrationEventRecordRangeReq req) throws IOException; - - /// 按周期查询登记事件记录 - /// - /// @param req 周期请求 - /// @return 登记事件记录列表 - List listByRegistrationEventAndPeriod(GaoRegistrationEventRecordPeriodReq req); - - /// 按周期统计登记事件记录 - /// - /// @param registrationEventId 登记事件 ID - /// @param periodType 周期类型 - /// @return 统计列表 - List statByRegistrationEventAndPeriodType(String registrationEventId, GaoRegistrationEventRecordPeriodReq.PeriodType periodType); - - /// 查询客户指定登记事件的最后登记事件记录 - /// - /// @param customerIdList 客户 ID 列表 - /// @param registrationEventId 登记事件 ID - /// @return 每个客户的最后登记事件记录 - List listLatestByCustomerIdListAndRegistrationEventId(List customerIdList, String registrationEventId); - - /// 统计客户所有登记 - /// - /// @param customerId 客户 ID - /// @return 统计结果 - List statByCustomerId(String customerId); - - /// 按时间段统计单个客户登记 - /// - /// @param req 统计请求 - /// @return 统计结果 - List statByCustomerAndRange(GaoRegistrationEventRecordStatReq req); - - /// 按时间段统计全部客户登记 - /// - /// @param req 统计请求 - /// @return 统计结果 - List statAllCustomerByRange(GaoRegistrationEventRecordStatReq req); - - /// 按时间范围查询客户登记排行榜 - /// - /// @param req 范围查询请求,不传时间为全部时间 - /// @return 客户登记排行榜 - List listCustomerRegistrationRank(GaoRegistrationEventRecordRangeReq req); - - /// 按时间段统计单个客户单个登记事件每日趋势 - /// - /// @param req 趋势统计请求 - /// @return 每日趋势列表 - List statDailyByCustomerAndRegistrationEvent(GaoRegistrationEventRecordTrendStatReq req); - - /// 按时间段统计全部客户单个登记事件每日趋势 - /// - /// @param req 趋势统计请求 - /// @return 每日趋势列表 - List statDailyByRegistrationEvent(GaoRegistrationEventRecordTrendStatReq req); - - /// 查询客户全部登记事件的最后登记事件记录 - /// - /// @param customerId 客户 ID - /// @return 每个登记事件的最后登记事件记录 - List listLatestByCustomerId(String customerId); -} diff --git a/src/main/java/com/imyeyu/api/modules/gao/service/GaoRegistrationEventService.java b/src/main/java/com/imyeyu/api/modules/gao/service/GaoRegistrationEventService.java deleted file mode 100644 index f6f609c..0000000 --- a/src/main/java/com/imyeyu/api/modules/gao/service/GaoRegistrationEventService.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.imyeyu.api.modules.gao.service; - -import com.imyeyu.api.modules.gao.entity.GaoRegistrationEvent; -import com.imyeyu.spring.bean.Page; -import com.imyeyu.spring.bean.PageResult; -import com.imyeyu.spring.service.BaseService; - -import java.util.List; - -/// -/// 登记事件服务 -/// -/// @author Codex -/// @since 2026-07-27 -public interface GaoRegistrationEventService extends BaseService { - - /// 分页查询登记事件并填充可登记角色 - /// - /// @param page 分页参数 - /// @return 登记事件分页 - PageResult pageWithRegistrationEventLimit(Page page); - - /// 查询启用的登记事件 - /// - /// @return 登记事件列表 - List listEnabled(); - - /// 查询登记事件要求的角色 - /// - /// @param registrationEventId 登记事件 ID - /// @return 角色代码列表 - List listRoleCode(String registrationEventId); - - /// 按客户端提交的顺序更新登记事件排序 - /// - /// @param idList 登记事件 ID 列表 - void sort(List idList); -} diff --git a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoCustomerServiceImplement.java b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoCustomerServiceImplement.java index 471526e..f268ac7 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoCustomerServiceImplement.java +++ b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoCustomerServiceImplement.java @@ -4,54 +4,39 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.imyeyu.api.config.dbsource.TimiServerDBConfig; import com.imyeyu.api.modules.common.entity.Attachment; import com.imyeyu.api.modules.common.service.AttachmentService; -import com.imyeyu.api.modules.common.vo.attach.BizUpdateReq; import com.imyeyu.api.modules.gao.entity.GaoCustomer; import com.imyeyu.api.modules.gao.mapper.GaoCustomerMapper; import com.imyeyu.api.modules.gao.service.GaoCustomerService; -import com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStatView; +import com.imyeyu.api.modules.gao.util.CustomerXSSFBuilder; +import com.imyeyu.api.modules.gao.vo.GaoCustomerDailyStateView; import com.imyeyu.api.modules.gao.vo.GaoCustomerGenderStatView; -import com.imyeyu.api.modules.gao.vo.GaoCustomerInviteUpdateReq; -import com.imyeyu.api.modules.gao.vo.GaoCustomerSearchReq; -import com.imyeyu.api.modules.gao.vo.GaoCustomerTrendStatReq; +import com.imyeyu.api.modules.gao.vo.GaoCustomerTrendStateReq; 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.api.modules.user.service.UserService; import com.imyeyu.java.TimiJava; import com.imyeyu.java.bean.timi.TimiCode; import com.imyeyu.java.bean.timi.TimiException; -import com.imyeyu.spring.TimiSpring; import com.imyeyu.spring.bean.Page; -import com.imyeyu.spring.bean.PageResult; import com.imyeyu.spring.mapper.BaseMapper; import com.imyeyu.spring.service.AbstractEntityService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.apache.poi.ss.usermodel.CellStyle; -import org.apache.poi.ss.usermodel.Font; -import org.apache.poi.ss.usermodel.Row; -import org.apache.poi.ss.usermodel.Sheet; -import org.apache.poi.ss.usermodel.Workbook; -import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.time.Instant; import java.time.LocalDate; -import java.time.Period; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Objects; -import java.util.stream.Collectors; -/// /// 客户服务实现 /// /// @author Codex @@ -62,14 +47,16 @@ import java.util.stream.Collectors; public class GaoCustomerServiceImplement extends AbstractEntityService implements GaoCustomerService { private static final DateTimeFormatter DAY_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd"); - private static final DateTimeFormatter EXPORT_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault()); - private final GaoCustomerMapper mapper; private final ObjectMapper jackson; - private final AttachmentService attachmentService; + private final LoggerService loggerService; private final UserLoginService userLoginService; - private final UserService userService; + private final AttachmentService attachmentService; + + private final GaoCustomerMapper mapper; + + private final CustomerXSSFBuilder customerXSSFBuilder; @Override protected BaseMapper mapper() { @@ -77,26 +64,41 @@ public class GaoCustomerServiceImplement extends AbstractEntityService page(Page page) { - PageResult result = super.page(page); - loadListTransient(result.getList()); - return result; - } + public void create(GaoCustomer customer) { + TimiException.required(customer.getName(), "not found customer.name"); - @Override - public void create(GaoCustomer entity) { Logger logger = new Logger(Logger.Module.GAO, "GAO_CUSTOMER_CREATE"); try { - logger.setContent(jackson.writeValueAsString(entity)); - checkEntity(entity, false); - entity.setCreatorUserId(userLoginService.getRequireLoginUserId()); - entity.setInvitedCount(0L); - super.create(entity); - saveAttachment(entity.getId(), resolveAttachmentList(entity)); - changeInvitedCount(entity.getIntroducerCustomerId(), 1L); - loadDetailTransient(entity); + logger.setContent(jackson.writeValueAsString(customer)); + { + GaoCustomer byName = getByName(customer.getName()); + TimiException.requiredNull(byName, "该姓名客户已登记"); + } + if (TimiJava.isNotEmpty(customer.getCode())) { + GaoCustomer byCode = getByCode(customer.getCode()); + TimiException.requiredNull(byCode, "该编码客户已登记"); + } + if (TimiJava.isNotEmpty(customer.getIntroducerCustomerId())) { + TimiException.requiredTrue(!customer.getIntroducerCustomerId().equals(customer.getId()), "customer.introducerCustomerId invalid"); + TimiException.required(super.get(customer.getIntroducerCustomerId()), "not found introducer customer"); + } + + customer.setBirthdateCalendar(TimiJava.defaultIfNull(customer.getBirthdateCalendar(), GaoCustomer.BirthdateCalendar.SOLAR)); + if (TimiJava.isNotEmpty(customer.getBirthdate())) { + customer.setAge(ChronoUnit.YEARS.between(customer.getBirthdate(), LocalDate.now())); + } + customer.setInvitedCount(0L); + if (customer.hasIntroducer()) { + customer.setInvitedCount(1L); + } + customer.setCreatorUserId(userLoginService.getRequireLoginUserId()); + + super.create(customer); + // 附件 + attachmentService.updateByBizId(Attachment.BizType.GAO_CUSTOMER, customer.getId(), customer.getAttachmentList()); + logger.setLevel(Logger.Level.INFO); - logger.setResult(entity.getId()); + logger.setResult(customer.getId()); } catch (TimiException e) { logger.setLevel(Logger.Level.WARN); logger.setException(e.getMessage()); @@ -112,23 +114,44 @@ public class GaoCustomerServiceImplement extends AbstractEntityService search(GaoCustomerSearchReq req) { - req = resolveSearchReq(req); - List list = mapper.search(req.getKeyword(), req.getIntroducerCustomerId()); - loadListTransient(list); - return list; + public List listByIdList(Collection idList) { + return mapper.selectByIdList(idList); } @Override - public PageResult searchPage(Page page) { - TimiException.required(page, "not found page"); - TimiException.required(page.getIndex(), "not found page.index"); - TimiException.required(page.getSize(), "not found page.size"); - TimiException.requiredTrue(0 <= page.getIndex(), "page.index lt 0"); - TimiException.requiredTrue(0 < page.getSize(), "page.size lte 0"); - GaoCustomerSearchReq req = resolveSearchReq(page.getEqualsExample()); - Long total = TimiJava.defaultIfNull(mapper.countSearch(req.getKeyword(), req.getIntroducerCustomerId()), 0L); - List list = mapper.searchPage(req.getKeyword(), req.getIntroducerCustomerId(), page.getIndex() * page.getSize(), page.getSize()); - loadListTransient(list); - PageResult result = new PageResult<>(); - result.setTotal(total); - result.setList(list); - return result; - } - - @Override - public byte[] exportSearchExcel(GaoCustomerSearchReq req) throws IOException { - List list = search(req); - loadIntroducerCustomer(list); - try (Workbook workbook = new XSSFWorkbook(); ByteArrayOutputStream output = new ByteArrayOutputStream()) { - Sheet sheet = workbook.createSheet("客户列表"); - CellStyle titleStyle = workbook.createCellStyle(); - Font titleFont = workbook.createFont(); - titleFont.setBold(true); - titleFont.setFontHeightInPoints((short) 14); - titleStyle.setFont(titleFont); - CellStyle headerStyle = workbook.createCellStyle(); - Font headerFont = workbook.createFont(); - headerFont.setBold(true); - headerStyle.setFont(headerFont); - - appendRow(sheet, 0, titleStyle, "客户列表导出"); - appendRow(sheet, 1, null, "生成时间", EXPORT_TIME_FORMATTER.format(Instant.now())); - appendRow(sheet, 2, null, "关键词", req == null || req.getKeyword() == null || req.getKeyword().isBlank() ? "全部" : req.getKeyword().trim()); - appendRow(sheet, 3, null, "记录总数", String.valueOf(list.size())); - appendRow(sheet, 5, headerStyle, "客户编码", "姓名", "性别", "出生日期(公历)", "出生日期历法", "年龄", "电话", "地址", "病症", "备注", "邀请人", "邀请客户数", "创建时间"); - int rowIndex = 6; + public byte[] export(Page page) throws IOException { + page.setIndex(0); + page.setSize(Long.MAX_VALUE); + List list = mapper.selectByPage(page); + { + List idList = list.stream().map(GaoCustomer::getIntroducerCustomerId).filter(TimiJava::isNotEmpty).distinct().toList(); + Map map = mapByIdList(idList); for (GaoCustomer customer : list) { - appendRow(sheet, rowIndex, null, - text(customer.getCode()), - text(customer.getName()), - formatGender(customer), - text(customer.getBirthdate()), - formatBirthdateCalendar(customer), - customer.getAge() == null ? "" : String.valueOf(customer.getAge()), - text(customer.getTelephone()), - text(customer.getAddress()), - text(customer.getDisease()), - text(customer.getRemark()), - customer.getIntroducerCustomer() == null ? "" : text(customer.getIntroducerCustomer().getName()), - customer.getInvitedCount() == null ? "0" : String.valueOf(customer.getInvitedCount()), - formatExportTime(customer.getCreatedAt()) - ); - rowIndex++; + customer.setIntroducerCustomer(map.get(customer.getIntroducerCustomerId())); } - for (int index = 0; index < 13; index++) { - sheet.autoSizeColumn(index); - sheet.setColumnWidth(index, Math.min(Math.max(sheet.getColumnWidth(index), 10 * 256), 36 * 256)); - } - workbook.write(output); - return output.toByteArray(); } + return customerXSSFBuilder.build(list); } @Override public List listInvited(String introducerCustomerId) { TimiException.required(introducerCustomerId, "not found introducerCustomerId"); - List list = mapper.selectInvitedList(introducerCustomerId); - loadListTransient(list); - return list; + GaoCustomer example = new GaoCustomer(); + example.setIntroducerCustomerId(introducerCustomerId); + return mapper.selectAllByExample(example); } @Transactional(TimiServerDBConfig.ROLLBACKER) @Override - public void saveInviteRelation(GaoCustomerInviteUpdateReq req) { + public void saveInviteRelation(String introducerCustomerId, String customerId) { + TimiException.required(customerId, "not found customerId"); + TimiException.required(introducerCustomerId, "not found introducerCustomerId"); + Logger logger = new Logger(Logger.Module.GAO, "GAO_CUSTOMER_SAVE_INVITE_RELATION"); try { - logger.setContent(jackson.writeValueAsString(req)); - TimiException.required(req, "not found invite req"); - TimiException.required(req.getCustomerId(), "not found invite req.customerId"); - GaoCustomer customer = super.get(req.getCustomerId()); - String oldIntroducerCustomerId = customer.getIntroducerCustomerId(); - customer.setIntroducerCustomerId(req.getIntroducerCustomerId()); - checkEntity(customer, true); - super.update(customer); - syncIntroducerInvitedCount(oldIntroducerCustomerId, customer.getIntroducerCustomerId()); + logger.setContent("introducerCustomerId: %s, customerId: %s".formatted(introducerCustomerId, customerId)); + GaoCustomer customer = get(customerId); + TimiException.required(customer, "not found customer"); + GaoCustomer introducerCustomer = get(introducerCustomerId); + TimiException.required(introducerCustomer, "not found introducerCustomer"); + if (introducerCustomer.getIntroducerCustomerId().equals(customer.getIntroducerCustomerId())) { + throw new TimiException(TimiCode.ARG_BAD, "customer can not upper be introducer customer"); + } + // 存在旧的邀请来源 + if (TimiJava.isNotEmpty(customer.getIntroducerCustomerId())) { + if (customer.getIntroducerCustomerId().equals(introducerCustomerId)) { + return; + } + changeInvitedCount(customer.getIntroducerCustomerId(), -1); + } + customer.setIntroducerCustomerId(introducerCustomerId); + mapper.update(customer); + changeInvitedCount(introducerCustomerId, +1); logger.setLevel(Logger.Level.INFO); - logger.setResult(req.getCustomerId()); } catch (TimiException e) { logger.setLevel(Logger.Level.WARN); logger.setException(e.getMessage()); @@ -321,15 +279,16 @@ public class GaoCustomerServiceImplement extends AbstractEntityService statDailyTrend(GaoCustomerTrendStatReq req) { + public List stateDailyTrend(GaoCustomerTrendStateReq req) { TimiException.required(req, "not found req"); TimiException.required(req.getBeginCreatedAt(), "not found req.beginCreatedAt"); TimiException.required(req.getEndCreatedAt(), "not found req.endCreatedAt"); @@ -355,20 +314,23 @@ public class GaoCustomerServiceImplement extends AbstractEntityService newCustomerCountMap = new HashMap<>(); - for (GaoCustomerDailyStatView view : mapper.statDailyNewCustomer(beginCreatedAt, endCreatedAt)) { + for (GaoCustomerDailyStateView view : mapper.stateDailyNewCustomer(beginCreatedAt, endCreatedAt)) { newCustomerCountMap.put(view.getDateValue(), TimiJava.defaultIfNull(view.getNewCustomerCount(), 0L)); } long totalCustomerCount = TimiJava.defaultIfNull(mapper.countCreatedBefore(beginCreatedAt), 0L); - List list = new ArrayList<>(); + List list = new ArrayList<>(); for (LocalDate date = beginDate; !date.isAfter(endDate); date = date.plusDays(1)) { int dateValue = Integer.parseInt(DAY_FORMATTER.format(date)); long newCustomerCount = TimiJava.defaultIfNull(newCustomerCountMap.get(dateValue), 0L); totalCustomerCount += newCustomerCount; - GaoCustomerDailyStatView view = new GaoCustomerDailyStatView(); + GaoCustomerDailyStateView view = new GaoCustomerDailyStateView(); view.setDateValue(dateValue); view.setNewCustomerCount(newCustomerCount); view.setTotalCustomerCount(totalCustomerCount); @@ -378,219 +340,16 @@ public class GaoCustomerServiceImplement extends AbstractEntityService statGender() { - return mapper.statGender(); - } - - private void checkDailyStatRange(LocalDate beginDate, LocalDate endDate) { - long days = ChronoUnit.DAYS.between(beginDate, endDate) + 1; - TimiException.requiredTrue(days <= 366, "daily stat range too large"); - } - - private GaoCustomerSearchReq resolveSearchReq(GaoCustomerSearchReq req) { - return req == null ? new GaoCustomerSearchReq() : req; - } - - private GaoCustomer checkEntity(GaoCustomer entity, boolean requireId) { - TimiException.required(entity, "not found customer"); - GaoCustomer dbCustomer = null; - entity.setCode(normalizeText(entity.getCode())); - entity.setIntroducerCustomerId(normalizeText(entity.getIntroducerCustomerId())); - if (entity.getBirthdateCalendar() == null) { - entity.setBirthdateCalendar(GaoCustomer.BirthdateCalendar.SOLAR); - } - refreshAge(entity); - if (requireId) { - TimiException.required(entity.getId(), "not found customer.id"); - dbCustomer = super.get(entity.getId()); - entity.setCreatorUserId(dbCustomer.getCreatorUserId()); - } - TimiException.required(entity.getName(), "not found customer.name"); - entity.setName(entity.getName().trim()); - TimiException.requiredTrue(!entity.getName().isEmpty(), "not found customer.name"); - GaoCustomer duplicateCustomer = mapper.selectByName(entity.getName()); - if (duplicateCustomer != null && !duplicateCustomer.getId().equals(entity.getId())) { - throw new TimiException(TimiCode.ARG_BAD, "客户姓名已存在"); - } - if (TimiJava.isNotEmpty(entity.getCode())) { - duplicateCustomer = mapper.selectByCode(entity.getCode()); - if (duplicateCustomer != null && !duplicateCustomer.getId().equals(entity.getId())) { - throw new TimiException(TimiCode.ARG_BAD, "客户编码已存在"); - } - } - if (TimiJava.isNotEmpty(entity.getIntroducerCustomerId())) { - TimiException.requiredTrue(!entity.getIntroducerCustomerId().equals(entity.getId()), "customer.introducerCustomerId invalid"); - TimiException.required(super.get(entity.getIntroducerCustomerId()), "not found introducer customer"); - } - return dbCustomer; - } - - private void loadDetailTransient(GaoCustomer customer) { - if (customer == null) { - return; - } - refreshAge(customer); - loadIntroducerCustomer(List.of(customer)); - if (TimiJava.isNotEmpty(customer.getCreatorUserId())) { - customer.setCreatorUser(userService.get(customer.getCreatorUserId())); - } - List detailCustomerList = new ArrayList<>(); - detailCustomerList.add(customer); - if (customer.getIntroducerCustomer() != null) { - detailCustomerList.add(customer.getIntroducerCustomer()); - } - loadAttachmentList(detailCustomerList); - } - - private void loadListTransient(List customerList) { - if (customerList != null) { - customerList.forEach(this::refreshAge); - } - loadAttachmentList(customerList); - } - - /// 根据出生日期计算当前周岁。出生日期为空时保留旧数据中的手工年龄。 - private void refreshAge(GaoCustomer customer) { - if (customer.getBirthdate() == null) { - return; - } - LocalDate today = LocalDate.now(); - TimiException.requiredTrue(!customer.getBirthdate().isAfter(today), "customer.birthdate invalid"); - customer.setAge(Period.between(customer.getBirthdate(), today).getYears()); - } - - private void loadIntroducerCustomer(List customerList) { - if (customerList == null || customerList.isEmpty()) { - return; - } - List introducerCustomerIdList = customerList.stream() - .map(GaoCustomer::getIntroducerCustomerId) - .filter(TimiJava::isNotEmpty) - .distinct() - .toList(); - if (introducerCustomerIdList.isEmpty()) { - return; - } - Map introducerCustomerMap = mapper.selectByIdList(new ArrayList<>(introducerCustomerIdList)).stream() - .collect(Collectors.toMap(GaoCustomer::getId, item -> item, (left, right) -> left)); - for (GaoCustomer customer : customerList) { - customer.setIntroducerCustomer(introducerCustomerMap.get(customer.getIntroducerCustomerId())); - } - } - - private void loadAttachmentList(List customerList) { - if (customerList == null || customerList.isEmpty()) { - return; - } - List customerIdList = customerList.stream() - .map(GaoCustomer::getId) - .filter(TimiJava::isNotEmpty) - .distinct() - .toList(); - Map> attachmentMap = attachmentService.mapByBizIdList(Attachment.BizType.GAO_CUSTOMER, customerIdList, GaoCustomer.AttachType.PHOTO.name()); - for (GaoCustomer customer : customerList) { - customer.setAttachmentList(attachmentMap.getOrDefault(customer.getId(), List.of())); - } - } - - private void syncIntroducerInvitedCount(String oldIntroducerCustomerId, String newIntroducerCustomerId) { - String normalizedOldIntroducerCustomerId = normalizeText(oldIntroducerCustomerId); - String normalizedNewIntroducerCustomerId = normalizeText(newIntroducerCustomerId); - if (Objects.equals(normalizedOldIntroducerCustomerId, normalizedNewIntroducerCustomerId)) { - return; - } - changeInvitedCount(normalizedOldIntroducerCustomerId, -1L); - changeInvitedCount(normalizedNewIntroducerCustomerId, 1L); + public List stateGender() { + return mapper.stateGender(); } private void changeInvitedCount(String customerId, long delta) { - String normalizedCustomerId = normalizeText(customerId); - if (normalizedCustomerId == null || delta == 0) { + if (delta == 0) { return; } - mapper.updateInvitedCountDelta(normalizedCustomerId, delta); - } - - private String normalizeText(String value) { - if (value == null) { - return null; - } - String trimmedValue = value.trim(); - return trimmedValue.isEmpty() ? null : trimmedValue; - } - - private void saveAttachment(String customerId, List attachmentList) { - if (attachmentList == null) { - return; - } - for (Attachment attachment : attachmentList) { - attachment.setBizType(Attachment.BizType.GAO_CUSTOMER); - attachment.setBizId(customerId); - attachment.setAttachType(GaoCustomer.AttachType.PHOTO.name()); - } - BizUpdateReq req = new BizUpdateReq(); - req.setBizType(Attachment.BizType.GAO_CUSTOMER); - req.setBizId(customerId); - req.setItems(attachmentList); - attachmentService.updateByBiz(req); - } - - private List resolveAttachmentList(GaoCustomer customer) { - if (customer.getAttachmentIdList() != null) { - return buildPhotoAttachmentList(customer.getAttachmentIdList()); - } - return customer.getAttachmentList(); - } - - private List buildPhotoAttachmentList(List attachmentIdList) { - List list = new ArrayList<>(); - for (String attachmentId : attachmentIdList) { - if (attachmentId == null || attachmentId.isBlank()) { - continue; - } - Attachment attachment = new Attachment(); - attachment.setTempFileId(attachmentId.trim()); - attachment.setAttachType(GaoCustomer.AttachType.PHOTO.name()); - list.add(attachment); - } - return list; - } - - private void appendRow(Sheet sheet, int rowIndex, CellStyle style, String... values) { - Row row = sheet.createRow(rowIndex); - for (int index = 0; index < values.length; index++) { - row.createCell(index).setCellValue(values[index]); - if (style != null) { - row.getCell(index).setCellStyle(style); - } - } - } - - private String formatGender(GaoCustomer customer) { - if (customer.getGender() == null) { - return ""; - } - return switch (customer.getGender()) { - case MALE -> "男"; - case FEMALE -> "女"; - }; - } - - private String formatBirthdateCalendar(GaoCustomer customer) { - if (customer.getBirthdateCalendar() == null) { - return "公历"; - } - return customer.getBirthdateCalendar() == GaoCustomer.BirthdateCalendar.LUNAR ? "农历" : "公历"; - } - - private String formatExportTime(Long value) { - if (value == null) { - return ""; - } - return EXPORT_TIME_FORMATTER.format(Instant.ofEpochMilli(value)); - } - - private String text(Object value) { - return value == null ? "" : value.toString(); + GaoCustomer customer = get(customerId); + customer.setInvitedCount(customer.getInvitedCount() + delta); + mapper.update(customer); } } 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 new file mode 100644 index 0000000..5f15c5c --- /dev/null +++ b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoEventRecordServiceImplement.java @@ -0,0 +1,252 @@ +package com.imyeyu.api.modules.gao.service.implement; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.imyeyu.api.bean.ModuleCode; +import com.imyeyu.api.config.dbsource.TimiServerDBConfig; +import com.imyeyu.api.modules.common.entity.Attachment; +import com.imyeyu.api.modules.common.service.AttachmentService; +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.GaoEventRecordService; +import com.imyeyu.api.modules.gao.service.GaoEventService; +import com.imyeyu.api.modules.gao.util.EventRecordXSSFBuilder; +import com.imyeyu.api.modules.gao.vo.GaoCustomerEventRankView; +import com.imyeyu.api.modules.gao.vo.GaoEventRecordPage; +import com.imyeyu.api.modules.gao.vo.GaoEventRecordRankPage; +import com.imyeyu.api.modules.system.entity.Logger; +import com.imyeyu.api.modules.system.service.LoggerService; +import com.imyeyu.api.modules.user.service.RoleChecker; +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.bean.PageResult; +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; + +import java.io.IOException; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; +import java.time.temporal.TemporalAdjusters; +import java.util.ArrayList; +import java.util.List; + +/// 登记事件记录服务实现 +/// +/// @author 夜雨 +/// @since 2026-07-27 14:11 +@Slf4j +@Service +@RequiredArgsConstructor +public class GaoEventRecordServiceImplement extends AbstractEntityService implements GaoEventRecordService { + + private final ObjectMapper jackson; + private final RoleChecker roleChecker; + private final LoggerService loggerService; + private final GaoEventService eventService; + private final UserLoginService userLoginService; + private final AttachmentService attachmentService; + private final GaoCustomerService customerService; + + private final GaoEventRecordMapper mapper; + + private final EventRecordXSSFBuilder xssfBuilder; + + @Override + protected BaseMapper mapper() { + return mapper; + } + + @Transactional(TimiServerDBConfig.ROLLBACKER) + @Override + public void create(GaoEventRecord record) { + TimiException.required(record, "not found record"); + + Logger logger = new Logger(Logger.Module.GAO, "GAO_EVENT_RECORD_BATCH_CREATE"); + try { + logger.setContent(jackson.writeValueAsString(record)); + + String loginUserId = userLoginService.getRequireLoginUserId(); + Long registeredAt = TimiJava.defaultIfNull(record.getRegisteredAt(), Time.now()); + GaoEvent event = eventService.get(record.getEventId()); + // 检查权限 + checkEventPermission(event); + + List customerIdList = TimiJava.defaultIfEmpty(record.getCustomerIdList(), List.of(record.getCustomerId())); + if (TimiJava.isNotEmpty(record.getCustomer())) { + // 客户未创建,补录客户后登记 + customerService.create(record.getCustomer()); + customerIdList.add(record.getCustomer().getId()); + } + TimiException.required(customerIdList, "not found customerId or empty customerIdList"); + + GaoEventRecord firstRecord = null; + List logIdList = new ArrayList<>(); + for (int i = 0; i < customerIdList.size(); i++) { + String customerId = customerIdList.get(i); + // 检查事件可登记 + checkEventCreatable(customerId, event, registeredAt); + + GaoEventRecord dbRecord = new GaoEventRecord(); + dbRecord.setCustomerId(customerId); + dbRecord.setEventId(record.getEventId()); + dbRecord.setOperatorUserId(loginUserId); + dbRecord.setRemark(record.getRemark()); + dbRecord.setRegisteredAt(registeredAt); + super.create(dbRecord); + // 附件 + if (i == 0) { + // 第一个记录储存附件 + firstRecord = dbRecord; + attachmentService.updateByBizId(Attachment.BizType.GAO_EVENT_RECORD, dbRecord.getId(), record.getAttachmentList()); + } else { + // 其他记录克隆第一个记录附件 + List attachmentList = attachmentService.listByBizId(Attachment.BizType.GAO_EVENT_RECORD, firstRecord.getId()); + for (Attachment attachment : attachmentList) { + Attachment clone = attachmentService.clone(attachment.getId()); + clone.setId(dbRecord.getId()); + attachmentService.update(clone); + } + } + logIdList.add(dbRecord.getId()); + } + logger.setLevel(Logger.Level.INFO); + logger.setResult(String.join(",", logIdList)); + } 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 event record error", e); + throw new TimiException(TimiCode.ERROR, "create event record error", e); + } finally { + loggerService.create(logger); + } + } + + @Transactional(TimiServerDBConfig.ROLLBACKER) + @Override + public void update(GaoEventRecord record) { + TimiException.required(record, "not found record"); + TimiException.required(record.getId(), "not found record.id"); + + Logger logger = new Logger(Logger.Module.GAO, "GAO_EVENT_RECORD_UPDATE"); + try { + logger.setContent(jackson.writeValueAsString(record)); + TimiException.required(record.getCustomerId(), "not found record.customerId"); + TimiException.required(record.getEventId(), "not found record.eventId"); + + record.setRegisteredAt(TimiJava.defaultIfNull(record.getRegisteredAt(), Time.now())); + + GaoEvent event = eventService.get(record.getEventId()); + checkEventPermission(event); + checkEventCreatable(record.getCustomerId(), event, record.getRegisteredAt()); + + super.update(record); + attachmentService.updateByBizId(Attachment.BizType.GAO_EVENT_RECORD, record.getId(), record.getAttachmentList()); + logger.setLevel(Logger.Level.INFO); + logger.setResult(record.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("修改 GAO 登记记录失败", e); + throw new TimiException(TimiCode.ERROR, "修改 GAO 登记记录失败", e); + } finally { + loggerService.create(logger); + } + } + + @Override + public PageResult pageByRange(GaoEventRecordPage page) { + PageResult result = new PageResult<>(); + result.setTotal(mapper.countByRange(page)); + result.setList(mapper.selectByRange(page)); + return result; + } + + @Override + public byte[] export(GaoEventRecordPage page) throws IOException { + List list = mapper.selectByRange(page); + return xssfBuilder.build(list); + } + + @Override + public PageResult pageRankByRange(GaoEventRecordRankPage page) { + PageResult result = new PageResult<>(); + result.setTotal(mapper.countRankByRange(page)); + result.setList(mapper.selectRankByRange(page)); + return result; + } + + private void checkEventPermission(GaoEvent event) { + List roleCodeList = eventService.listRoleCode(event.getId()); + TimiException.requiredTrue(roleCodeList != null && !roleCodeList.isEmpty(), "登记事件未配置可登记角色"); + roleChecker.checkAny(ModuleCode.GAO, roleCodeList.toArray(String[]::new)); + } + + private void checkEventCreatable(String customerId, GaoEvent event, long registeredAt) { + TimiException.requiredTrue(event.getStatus() == null || event.getStatus() == GaoEvent.Status.ACTIVE, "登记事件不可登记"); + TimiException.requiredTrue(event.getBeginAt() == null || event.getBeginAt() <= registeredAt, "登记事件未到可登记时间"); + TimiException.requiredTrue(event.getEndAt() == null || registeredAt <= event.getEndAt(), "登记事件已过可登记时间"); + GaoEvent.LimitType limitType = TimiJava.defaultIfNull(event.getLimitType(), GaoEvent.LimitType.NONE); + switch (limitType) { + case DAILY_HALF_DAY -> { + long today = Time.today(registeredAt); + long noon = today + Time.D / 2; + long beginAt = today, endAt; + if (today + registeredAt < noon) { + // 上半日 + endAt = noon; + } else { + // 下半日 + beginAt = noon; + endAt = today + Time.D; + } + Long count = mapper.count(customerId, event.getId(), beginAt, endAt); + TimiException.requiredTrue(TimiJava.defaultIfNull(count, 0L) < 1, "该客户当前半日已登记该事件"); + } + case NATURAL_DAY -> { + long beginAt = Time.today(registeredAt); + long endAt = beginAt + Time.D; + Long count = mapper.count(customerId, event.getId(), beginAt, endAt); + TimiException.requiredTrue(TimiJava.defaultIfNull(count, 0L) < 1, "该客户当前半日已登记该事件"); + } + case INTERVAL_HOUR -> { + long beginAt = registeredAt - event.getLimitValue() * Time.H; + Long count = mapper.count(customerId, event.getId(), beginAt, registeredAt); + TimiException.requiredTrue(TimiJava.defaultIfNull(count, 0L) < 1, "该客户登记该事件间隔不足"); + } + case NATURAL_MONTH -> { + ZoneId zone = ZoneId.systemDefault(); + ZonedDateTime zdt = Instant.ofEpochMilli(registeredAt).atZone(zone); + long beginAt = zdt.with(TemporalAdjusters.firstDayOfMonth()).truncatedTo(ChronoUnit.DAYS).toInstant().toEpochMilli(); + long endAt = zdt.with(TemporalAdjusters.firstDayOfNextMonth()).truncatedTo(ChronoUnit.DAYS).toInstant().toEpochMilli(); + Long count = mapper.count(customerId, event.getId(), beginAt, endAt); + TimiException.requiredTrue(TimiJava.defaultIfNull(count, 0L) < 1, "该客户当月已登记该事件"); + } + case TOTAL_LIMIT -> { + Long count = mapper.count(null, event.getId(), null, null); + TimiException.requiredTrue(TimiJava.defaultIfNull(count, 0L) < event.getLimitValue(), "登记事件总次数已达上限"); + } + case CUSTOMER_TOTAL_LIMIT -> { + Long count = mapper.count(customerId, event.getId(), null, null); + TimiException.requiredTrue(TimiJava.defaultIfNull(count, 0L) < event.getLimitValue(), "该客户登记该事件次数已达上限"); + } + } + } +} diff --git a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoEventServiceImplement.java b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoEventServiceImplement.java new file mode 100644 index 0000000..6f42c65 --- /dev/null +++ b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoEventServiceImplement.java @@ -0,0 +1,215 @@ +package com.imyeyu.api.modules.gao.service.implement; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.imyeyu.api.config.dbsource.TimiServerDBConfig; +import com.imyeyu.api.modules.gao.entity.GaoEvent; +import com.imyeyu.api.modules.gao.entity.GaoEventRoleRelation; +import com.imyeyu.api.modules.gao.mapper.GaoEventMapper; +import com.imyeyu.api.modules.gao.mapper.GaoEventRoleRelationMapper; +import com.imyeyu.api.modules.gao.service.GaoEventService; +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 com.imyeyu.utils.Time; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; + +/// +/// 登记事件服务实现 +/// +/// @author 夜雨 +/// @since 2026-07-27 09:00 +@Slf4j +@Service +@RequiredArgsConstructor +public class GaoEventServiceImplement extends AbstractEntityService implements GaoEventService { + + private final ObjectMapper jackson; + + private final LoggerService loggerService; + + private final GaoEventMapper mapper; + private final GaoEventRoleRelationMapper roleRelationMapper; + + @Override + protected BaseMapper mapper() { + return mapper; + } + + @Transactional(TimiServerDBConfig.ROLLBACKER) + @Override + public void create(GaoEvent event) { + TimiException.required(event, "not found event"); + TimiException.required(event.getName(), "not found event.name"); + + Logger logger = new Logger(Logger.Module.GAO, "GAO_EVENT_CREATE"); + try { + logger.setContent(jackson.writeValueAsString(event)); + + event.setStatus(TimiJava.defaultIfNull(event.getStatus(), GaoEvent.Status.DRAFT)); + event.setRequirePhoto(TimiJava.defaultIfNull(event.getRequirePhoto(), false)); + event.setLimitType(TimiJava.defaultIfNull(event.getLimitType(), GaoEvent.LimitType.NONE)); + if (TimiJava.isNotEmpty(event.getLimitType())) { + TimiException.requiredTrue(event.getLimitType().getMin() < event.getLimitValue(), "约束值最低 %s".formatted(event.getLimitType().getMin())); + } + if (event.getBeginAt() != null || event.getEndAt() != null) { + TimiException.requiredTrue(event.getBeginAt() == null || event.getEndAt() == null || event.getBeginAt() <= event.getEndAt(), "invalid beginAt or endAt"); + } + event.setSort(TimiJava.defaultIfNull(event.getSort(), 0)); + super.create(event); + + saveRoleRelation(event.getId(), event.getRoleCodeList()); + logger.setLevel(Logger.Level.INFO); + logger.setResult(event.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 event error", e); + throw new TimiException(TimiCode.ERROR, "create event error", e); + } finally { + loggerService.create(logger); + } + } + + @Transactional(TimiServerDBConfig.ROLLBACKER) + @Override + public void update(GaoEvent entity) { + Logger logger = new Logger(Logger.Module.GAO, "GAO_EVENT_UPDATE"); + try { + logger.setContent(jackson.writeValueAsString(entity)); + GaoEvent dbEvent = get(entity.getId()); + dbEvent.setName(entity.getName()); + dbEvent.setDescription(entity.getDescription()); + dbEvent.setColor(entity.getColor()); + dbEvent.setStatus(entity.getStatus()); + dbEvent.setRequirePhoto(entity.getRequirePhoto()); + dbEvent.setLimitType(entity.getLimitType()); + dbEvent.setLimitValue(entity.getLimitValue()); + dbEvent.setBeginAt(entity.getBeginAt()); + dbEvent.setEndAt(entity.getEndAt()); + mapper.update(dbEvent); + saveRoleRelation(entity.getId(), entity.getRoleCodeList()); + logger.setLevel(Logger.Level.INFO); + logger.setResult(entity.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 event error", e); + throw new TimiException(TimiCode.ERROR, "update event error", e); + } finally { + loggerService.create(logger); + } + } + + @Transactional(TimiServerDBConfig.ROLLBACKER) + @Override + public void delete(String id) { + Logger logger = new Logger(Logger.Module.GAO, "GAO_EVENT_DELETE"); + try { + logger.setContent(id); + GaoEvent event = get(id); + event.setStatus(GaoEvent.Status.DELETED); + super.update(event); + 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 event error", e); + throw new TimiException(TimiCode.ERROR, "delete event error", e); + } finally { + loggerService.create(logger); + } + } + + @Override + public Map mapByIdList(Collection idList) { + return mapper.selectByIdList(new HashSet<>(idList)).stream().collect(Collectors.toMap(GaoEvent::getId, Function.identity())); + } + + @Override + public Map> mapRoleCodeListByEventIdList(Collection eventIdList) { + return roleRelationMapper.selectAllByEventIdList(new HashSet<>(eventIdList)) + .stream() + .collect(Collectors.groupingBy(GaoEventRoleRelation::getEventId, Collectors.mapping(GaoEventRoleRelation::getRoleCode, Collectors.toList()))); + } + + @Override + public List listValid() { + return mapper.selectValidList(Time.now()); + } + + @Override + public List listRoleCode(String eventId) { + GaoEventRoleRelation example = new GaoEventRoleRelation(); + example.setEventId(eventId); + return roleRelationMapper.selectAllByExample(example).stream().map(GaoEventRoleRelation::getRoleCode).toList(); + } + + @Transactional(TimiServerDBConfig.ROLLBACKER) + @Override + public void sort(List idList) { + if (TimiJava.isEmpty(idList)) { + return; + } + idList = new ArrayList<>(new LinkedHashSet<>(idList)); + Map eventMap = mapper.selectByIdList(idList).stream().collect(Collectors.toMap(GaoEvent::getId, Function.identity())); + for (int i = 0; i < idList.size(); i++) { + GaoEvent event = eventMap.get(idList.get(i)); + event.setSort(i); + mapper.update(event); + } + } + + private void saveRoleRelation(String eventId, List roleCodeList) { + List dbRoleRelationList = roleRelationMapper.selectAllByEventIdList(List.of(eventId)); + Set roleCodeSet = TimiJava.defaultIfEmpty(roleCodeList, List.of()).stream().filter(TimiJava::isNotEmpty).collect(Collectors.toSet()); + Set retainedRoleCodeSet = new HashSet<>(); + for (GaoEventRoleRelation dbEventRole : dbRoleRelationList) { + String roleCode = dbEventRole.getRoleCode(); + if (roleCode != null) { + roleCode = roleCode.trim(); + } + if (!roleCodeSet.contains(roleCode) || !retainedRoleCodeSet.add(roleCode)) { + roleRelationMapper.delete(dbEventRole.getId()); + } + } + for (String roleCode : roleCodeSet) { + if (retainedRoleCodeSet.contains(roleCode)) { + continue; + } + GaoEventRoleRelation relation = new GaoEventRoleRelation(); + relation.setEventId(eventId); + relation.setRoleCode(roleCode); + roleRelationMapper.insert(relation); + } + } +} diff --git a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoRegistrationEventRecordServiceImplement.java b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoRegistrationEventRecordServiceImplement.java deleted file mode 100644 index 0a00508..0000000 --- a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoRegistrationEventRecordServiceImplement.java +++ /dev/null @@ -1,761 +0,0 @@ -package com.imyeyu.api.modules.gao.service.implement; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.imyeyu.api.config.dbsource.TimiServerDBConfig; -import com.imyeyu.api.bean.ModuleCode; -import com.imyeyu.api.modules.common.entity.Attachment; -import com.imyeyu.api.modules.common.service.AttachmentService; -import com.imyeyu.api.modules.common.vo.attach.BizUpdateReq; -import com.imyeyu.api.modules.gao.entity.GaoCustomer; -import com.imyeyu.api.modules.gao.entity.GaoRegistrationEvent; -import com.imyeyu.api.modules.gao.entity.GaoRegistrationEventRecord; -import com.imyeyu.api.modules.gao.mapper.GaoCustomerMapper; -import com.imyeyu.api.modules.gao.mapper.GaoRegistrationEventMapper; -import com.imyeyu.api.modules.gao.mapper.GaoRegistrationEventRecordMapper; -import com.imyeyu.api.modules.gao.service.GaoCustomerService; -import com.imyeyu.api.modules.gao.service.GaoRegistrationEventService; -import com.imyeyu.api.modules.gao.service.GaoRegistrationEventRecordService; -import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventPeriodStatView; -import com.imyeyu.api.modules.gao.vo.GaoCustomerRegisterStatView; -import com.imyeyu.api.modules.gao.vo.GaoCustomerRegistrationDayStatView; -import com.imyeyu.api.modules.gao.vo.GaoCustomerRegistrationRankView; -import com.imyeyu.api.modules.gao.vo.GaoQuickRegistrationEventRecordReq; -import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordCalendarView; -import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordCreateReq; -import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordDailyStatView; -import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordListItemView; -import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordPeriodReq; -import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordRangeReq; -import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordStatReq; -import com.imyeyu.api.modules.gao.vo.GaoRegistrationEventRecordTrendStatReq; -import com.imyeyu.api.modules.system.entity.Logger; -import com.imyeyu.api.modules.system.service.LoggerService; -import com.imyeyu.api.modules.user.service.RoleChecker; -import com.imyeyu.api.modules.user.service.UserLoginService; -import com.imyeyu.api.modules.user.service.UserService; -import com.imyeyu.api.modules.user.mapper.UserMapper; -import com.imyeyu.java.TimiJava; -import com.imyeyu.java.bean.timi.TimiCode; -import com.imyeyu.java.bean.timi.TimiException; -import com.imyeyu.spring.TimiSpring; -import com.imyeyu.spring.bean.Page; -import com.imyeyu.spring.bean.PageResult; -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.apache.poi.ss.usermodel.CellStyle; -import org.apache.poi.ss.usermodel.Font; -import org.apache.poi.ss.usermodel.Row; -import org.apache.poi.ss.usermodel.Sheet; -import org.apache.poi.ss.usermodel.Workbook; -import org.apache.poi.xssf.usermodel.XSSFWorkbook; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.io.ByteArrayOutputStream; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.time.Instant; -import java.time.LocalDate; -import java.time.ZoneId; -import java.time.temporal.ChronoUnit; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.StringJoiner; -import java.util.function.Function; -import java.util.stream.Collectors; - -/// -/// 登记事件记录服务实现 -/// -/// @author Codex -/// @since 2026-07-27 -@Slf4j -@Service -@RequiredArgsConstructor -public class GaoRegistrationEventRecordServiceImplement extends AbstractEntityService implements GaoRegistrationEventRecordService { - - private static final DateTimeFormatter DAY_FORMATTER = DateTimeFormatter.BASIC_ISO_DATE; - private static final DateTimeFormatter EXPORT_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault()); - - private final GaoRegistrationEventRecordMapper mapper; - private final ObjectMapper jackson; - private final GaoCustomerMapper customerMapper; - private final GaoRegistrationEventMapper registrationEventMapper; - private final UserMapper userMapper; - private final AttachmentService attachmentService; - private final LoggerService loggerService; - private final RoleChecker roleChecker; - private final UserLoginService userLoginService; - private final UserService userService; - private final GaoCustomerService customerService; - private final GaoRegistrationEventService registrationEventService; - - @Override - protected BaseMapper mapper() { - return mapper; - } - - @Transactional(TimiServerDBConfig.ROLLBACKER) - @Override - public List createRecords(GaoRegistrationEventRecordCreateReq req) { - Logger logger = new Logger(Logger.Module.GAO, "GAO_REGISTRATION_EVENT_RECORD_BATCH_CREATE"); - try { - TimiException.required(req, "not found req"); - TimiException.requiredTrue(req.getCustomerIdList() != null && !req.getCustomerIdList().isEmpty(), "customerIdList is empty"); - logger.setContent(jackson.writeValueAsString(req)); - List recordList = new ArrayList<>(); - for (int index = 0; index < req.getCustomerIdList().size(); index++) { - String customerId = req.getCustomerIdList().get(index); - GaoRegistrationEventRecordCreateReq customerReq = copyCreateReq(req); - customerReq.setAttachmentIdList(0 == index && canUseSourceTempFileList(req.getAttachmentIdList()) - ? req.getAttachmentIdList() - : duplicateTempFileList(req.getAttachmentIdList())); - recordList.add(doCreateRecord(customerReq, customerId)); - } - logger.setLevel(Logger.Level.INFO); - logger.setResult(recordList.stream().map(GaoRegistrationEventRecord::getId).collect(Collectors.joining(","))); - return recordList; - } 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("批量新增 GAO 登记记录失败", e); - throw new TimiException(TimiCode.ERROR, "批量新增 GAO 登记记录失败", e); - } finally { - loggerService.create(logger); - } - } - - private List duplicateTempFileList(List sourceIdList) { - if (sourceIdList == null) { - return null; - } - List targetIdList = new ArrayList<>(); - for (String sourceId : sourceIdList) { - if (sourceId == null || sourceId.isBlank()) { - continue; - } - Attachment source = attachmentService.get(sourceId.trim()); - TimiException.required(source, "not found attachment"); - TimiException.required(source.getMongoId(), "not found attachment.mongoId"); - Attachment target = new Attachment(); - target.setBizType(Attachment.BizType.TEMP_FILE); - target.setName(source.getName()); - target.setInputStream(new ByteArrayInputStream(attachmentService.readAllByMongoId(source.getMongoId()))); - attachmentService.create(target); - targetIdList.add(target.getId()); - } - return targetIdList; - } - - private boolean canUseSourceTempFileList(List sourceIdList) { - if (sourceIdList == null || sourceIdList.isEmpty()) { - return true; - } - for (String sourceId : sourceIdList) { - if (sourceId == null || sourceId.isBlank()) { - continue; - } - Attachment source = attachmentService.get(sourceId.trim()); - if (source == null || source.getBizType() != Attachment.BizType.TEMP_FILE) { - return false; - } - } - return true; - } - - private GaoRegistrationEventRecordCreateReq copyCreateReq(GaoRegistrationEventRecordCreateReq source) { - GaoRegistrationEventRecordCreateReq target = new GaoRegistrationEventRecordCreateReq(); - target.setRegistrationEventId(source.getRegistrationEventId()); - target.setRemark(source.getRemark()); - target.setRegisteredAt(source.getRegisteredAt()); - return target; - } - - @Transactional(TimiServerDBConfig.ROLLBACKER) - @Override - public GaoRegistrationEventRecord quickRegister(GaoQuickRegistrationEventRecordReq req) { - Logger logger = new Logger(Logger.Module.GAO, "GAO_REGISTRATION_EVENT_RECORD_QUICK_REGISTER"); - try { - logger.setContent(jackson.writeValueAsString(req)); - TimiException.required(req, "not found req"); - TimiException.required(req.getCustomerCode(), "not found req.customerCode"); - TimiException.required(req.getRegistrationEventId(), "not found req.registrationEventId"); - GaoCustomer customer = customerService.getOrQuickCreateByCode(req.getCustomerCode()); - GaoRegistrationEventRecordCreateReq recordReq = new GaoRegistrationEventRecordCreateReq(); - recordReq.setCustomerIdList(List.of(customer.getId())); - recordReq.setRegistrationEventId(req.getRegistrationEventId()); - recordReq.setRemark(req.getRemark()); - recordReq.setAttachmentIdList(req.getAttachmentIdList()); - GaoRegistrationEventRecord record = createRecords(recordReq).get(0); - logger.setLevel(Logger.Level.INFO); - logger.setResult(record.getId()); - return record; - } 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("GAO 快速登记失败", e); - throw new TimiException(TimiCode.ERROR, "GAO 快速登记失败", e); - } finally { - loggerService.create(logger); - } - } - - @Transactional(TimiServerDBConfig.ROLLBACKER) - @Override - public void update(GaoRegistrationEventRecord entity) { - Logger logger = new Logger(Logger.Module.GAO, "GAO_REGISTRATION_EVENT_RECORD_UPDATE"); - try { - logger.setContent(jackson.writeValueAsString(entity)); - TimiException.required(entity, "not found record"); - TimiException.required(entity.getId(), "not found record.id"); - TimiException.required(entity.getCustomerId(), "not found record.customerId"); - TimiException.required(entity.getRegistrationEventId(), "not found record.registrationEventId"); - customerService.get(entity.getCustomerId()); - GaoRegistrationEvent registrationEvent = registrationEventService.get(entity.getRegistrationEventId()); - checkRegistrationEventPermission(registrationEvent); - entity.setRegisteredAt(TimiJava.defaultIfNull(entity.getRegisteredAt(), Time.now())); - super.update(entity); - saveAttachment(entity.getId(), resolveAttachmentList(entity)); - logger.setLevel(Logger.Level.INFO); - logger.setResult(entity.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("修改 GAO 登记记录失败", e); - throw new TimiException(TimiCode.ERROR, "修改 GAO 登记记录失败", e); - } finally { - loggerService.create(logger); - } - } - - @Override - public GaoRegistrationEventRecord get(String id) { - GaoRegistrationEventRecord record = super.get(id); - loadTransient(List.of(record)); - return record; - } - - @Override - public List listByRegistrationEventAndPeriod(GaoRegistrationEventRecordPeriodReq req) { - TimiException.required(req, "not found req"); - TimiException.required(req.getRegistrationEventId(), "not found req.registrationEventId"); - TimiException.required(req.getPeriodType(), "not found req.periodType"); - TimiException.required(req.getPeriodValue(), "not found req.periodValue"); - List list = mapper.selectByRegistrationEventAndPeriod(req.getRegistrationEventId(), req.getPeriodType(), req.getPeriodValue()); - loadTransient(list); - return list; - } - - @Override - public List listByRange(GaoRegistrationEventRecordRangeReq req) { - TimiException.required(req, "not found req"); - checkStatRange(req.getBeginRegisteredAt(), req.getEndRegisteredAt()); - List list = mapper.selectByRange(req); - loadTransient(list); - return list; - } - - @Override - public List listCalendarByRange(GaoRegistrationEventRecordRangeReq req) { - TimiException.required(req, "not found req"); - checkStatRange(req.getBeginRegisteredAt(), req.getEndRegisteredAt()); - return mapper.selectCalendarByRange(req); - } - - @Override - public PageResult pageByRange(Page page) { - TimiException.required(page, "not found page"); - TimiException.required(page.getIndex(), "not found page.index"); - TimiException.required(page.getSize(), "not found page.size"); - TimiException.requiredTrue(0 <= page.getIndex(), "page.index lt 0"); - TimiException.requiredTrue(0 < page.getSize(), "page.size lte 0"); - GaoRegistrationEventRecordRangeReq req = TimiJava.defaultIfNull(page.getEqualsExample(), new GaoRegistrationEventRecordRangeReq()); - checkStatRange(req.getBeginRegisteredAt(), req.getEndRegisteredAt()); - Long total = TimiJava.defaultIfNull(mapper.countByRange(req), 0L); - List list = mapper.selectPageByRange(req, page.getIndex() * page.getSize(), page.getSize()); - PageResult result = new PageResult<>(); - result.setTotal(total); - result.setList(list); - return result; - } - - @Override - public byte[] exportRangeExcel(GaoRegistrationEventRecordRangeReq req) throws IOException { - List list = listByRange(req); - try (Workbook workbook = new XSSFWorkbook(); ByteArrayOutputStream output = new ByteArrayOutputStream()) { - Sheet sheet = workbook.createSheet("登记事件记录"); - CellStyle titleStyle = workbook.createCellStyle(); - Font titleFont = workbook.createFont(); - titleFont.setBold(true); - titleFont.setFontHeightInPoints((short) 14); - titleStyle.setFont(titleFont); - CellStyle headerStyle = workbook.createCellStyle(); - Font headerFont = workbook.createFont(); - headerFont.setBold(true); - headerStyle.setFont(headerFont); - - appendRow(sheet, 0, titleStyle, "登记事件记录导出"); - appendRow(sheet, 1, null, "生成时间", EXPORT_TIME_FORMATTER.format(Instant.now())); - appendRow(sheet, 2, null, "时间范围", "%s ~ %s".formatted(formatExportTime(req.getBeginRegisteredAt()), formatExportTime(req.getEndRegisteredAt()))); - appendRow(sheet, 3, null, "事件筛选", buildEventFilterText(req)); - appendRow(sheet, 4, null, "关键词", req.getKeyword() == null || req.getKeyword().isBlank() ? "全部" : req.getKeyword().trim()); - appendRow(sheet, 5, null, "记录总数", String.valueOf(list.size())); - appendRow(sheet, 7, headerStyle, "客户编码", "客户姓名", "登记事件", "登记时间", "登记人", "备注"); - int rowIndex = 8; - for (GaoRegistrationEventRecord record : list) { - appendRow(sheet, rowIndex, null, - record.getCustomer() == null ? "" : text(record.getCustomer().getCode()), - record.getCustomer() == null ? "" : text(record.getCustomer().getName()), - record.getRegistrationEvent() == null ? "" : text(record.getRegistrationEvent().getName()), - formatExportTime(record.getRegisteredAt()), - getOperatorName(record), - text(record.getRemark()) - ); - rowIndex++; - } - for (int index = 0; index < 6; index++) { - sheet.autoSizeColumn(index); - sheet.setColumnWidth(index, Math.min(Math.max(sheet.getColumnWidth(index), 12 * 256), 40 * 256)); - } - workbook.write(output); - return output.toByteArray(); - } - } - - @Override - public List statByRegistrationEventAndPeriodType(String registrationEventId, GaoRegistrationEventRecordPeriodReq.PeriodType periodType) { - TimiException.required(registrationEventId, "not found registrationEventId"); - TimiException.required(periodType, "not found periodType"); - return mapper.statByRegistrationEventAndPeriodType(registrationEventId, periodType); - } - - @Override - public List listLatestByCustomerIdListAndRegistrationEventId(List customerIdList, String registrationEventId) { - TimiException.required(registrationEventId, "not found registrationEventId"); - if (TimiJava.isEmpty(customerIdList)) { - return Collections.emptyList(); - } - List list = mapper.selectLatestByCustomerIdListAndRegistrationEventId(customerIdList, registrationEventId); - loadTransient(list); - return list; - } - - @Override - public List statByCustomerId(String customerId) { - TimiException.required(customerId, "not found customerId"); - return mapper.statByCustomerId(customerId); - } - - @Override - public List statByCustomerAndRange(GaoRegistrationEventRecordStatReq req) { - TimiException.required(req, "not found req"); - TimiException.required(req.getCustomerId(), "not found req.customerId"); - checkStatRange(req.getBeginRegisteredAt(), req.getEndRegisteredAt()); - return mapper.statByCustomerIdAndRange(req.getCustomerId(), req.getBeginRegisteredAt(), req.getEndRegisteredAt()); - } - - @Override - public List statAllCustomerByRange(GaoRegistrationEventRecordStatReq req) { - TimiException.required(req, "not found req"); - checkStatRange(req.getBeginRegisteredAt(), req.getEndRegisteredAt()); - return mapper.statAllCustomerByRange(req.getBeginRegisteredAt(), req.getEndRegisteredAt()); - } - - @Override - public List listCustomerRegistrationRank(GaoRegistrationEventRecordRangeReq req) { - GaoRegistrationEventRecordRangeReq rangeReq = TimiJava.defaultIfNull(req, new GaoRegistrationEventRecordRangeReq()); - if (rangeReq.getBeginRegisteredAt() != null || rangeReq.getEndRegisteredAt() != null) { - checkStatRange(rangeReq.getBeginRegisteredAt(), rangeReq.getEndRegisteredAt()); - } - List dayStatList = mapper.statCustomerRegistrationDay(rangeReq); - Map> customerDayStatMap = dayStatList.stream() - .collect(Collectors.groupingBy(GaoCustomerRegistrationDayStatView::getCustomerId)); - List rankList = new ArrayList<>(); - for (Map.Entry> entry : customerDayStatMap.entrySet()) { - rankList.add(buildCustomerRegistrationRank(entry.getValue())); - } - rankList.sort(Comparator - .comparing(GaoCustomerRegistrationRankView::getTotalRegistrationDays, Comparator.nullsLast(Comparator.reverseOrder())) - .thenComparing(GaoCustomerRegistrationRankView::getTotalRegistrationCount, Comparator.nullsLast(Comparator.reverseOrder())) - .thenComparing(GaoCustomerRegistrationRankView::getLatestRegisteredAt, Comparator.nullsLast(Comparator.reverseOrder()))); - for (int index = 0; index < rankList.size(); index++) { - rankList.get(index).setRank(index + 1); - } - return rankList; - } - - @Override - public List statDailyByCustomerAndRegistrationEvent(GaoRegistrationEventRecordTrendStatReq req) { - TimiException.required(req, "not found req"); - TimiException.required(req.getCustomerId(), "not found req.customerId"); - customerService.get(req.getCustomerId()); - return buildDailyTrend(req, true); - } - - @Override - public List statDailyByRegistrationEvent(GaoRegistrationEventRecordTrendStatReq req) { - TimiException.required(req, "not found req"); - return buildDailyTrend(req, false); - } - - @Override - public List listLatestByCustomerId(String customerId) { - TimiException.required(customerId, "not found customerId"); - List list = mapper.selectLatestByCustomerId(customerId); - loadTransient(list); - return list; - } - - private String resolveCustomerId(String customerId) { - TimiException.required(customerId, "not found customerId"); - customerService.get(customerId); - return customerId; - } - - private GaoRegistrationEventRecord doCreateRecord(GaoRegistrationEventRecordCreateReq req, String customerId) { - TimiException.required(req, "not found req"); - TimiException.required(req.getRegistrationEventId(), "not found req.registrationEventId"); - customerId = resolveCustomerId(customerId); - GaoRegistrationEvent registrationEvent = registrationEventService.get(req.getRegistrationEventId()); - checkRegistrationEventPermission(registrationEvent); - long registeredAt = TimiJava.defaultIfNull(req.getRegisteredAt(), Time.now()); - checkRegistrationEventCreatable(registrationEvent, customerId, registeredAt); - if (Boolean.TRUE.equals(registrationEvent.getRequirePhoto())) { - TimiException.requiredTrue(req.getAttachmentIdList() != null && !req.getAttachmentIdList().isEmpty(), "required record photo"); - } - - GaoRegistrationEventRecord record = new GaoRegistrationEventRecord(); - record.setCustomerId(customerId); - record.setRegistrationEventId(req.getRegistrationEventId()); - record.setOperatorUserId(userLoginService.getRequireLoginUserId()); - record.setRemark(req.getRemark()); - record.setRegisteredAt(registeredAt); - super.create(record); - saveAttachment(record.getId(), buildPhotoAttachmentList(req.getAttachmentIdList())); - return get(record.getId()); - } - - private void checkRegistrationEventPermission(GaoRegistrationEvent registrationEvent) { - List roleCodeList = registrationEventService.listRoleCode(registrationEvent.getId()); - TimiException.requiredTrue(roleCodeList != null && !roleCodeList.isEmpty(), "登记事件未配置可登记角色"); - roleChecker.checkAny(ModuleCode.GAO, roleCodeList.toArray(String[]::new)); - } - - private void checkRegistrationEventCreatable(GaoRegistrationEvent registrationEvent, String customerId, long registeredAt) { - TimiException.requiredTrue(registrationEvent.getStatus() == null || registrationEvent.getStatus() == GaoRegistrationEvent.Status.ACTIVE, "登记事件不可登记"); - TimiException.requiredTrue(registrationEvent.getBeginAt() == null || registrationEvent.getBeginAt() <= registeredAt, "登记事件未到可登记时间"); - TimiException.requiredTrue(registrationEvent.getEndAt() == null || registeredAt <= registrationEvent.getEndAt(), "登记事件已过可登记时间"); - GaoRegistrationEvent.LimitType limitType = TimiJava.defaultIfNull(registrationEvent.getLimitType(), GaoRegistrationEvent.LimitType.NONE); - switch (limitType) { - case DAILY_HALF_DAY -> checkRegistrationLimitRange(customerId, registrationEvent.getId(), getHalfDayBegin(registeredAt), getHalfDayEnd(registeredAt), "该客户当前半日已登记该事件"); - case NATURAL_DAY -> checkRegistrationLimitRange(customerId, registrationEvent.getId(), getDayBegin(registeredAt), getDayEnd(registeredAt), "该客户当日已登记该事件"); - case INTERVAL_HOUR -> checkIntervalHourRegistrationLimit(registrationEvent, customerId, registeredAt); - case NATURAL_MONTH -> checkRegistrationLimitRange(customerId, registrationEvent.getId(), getMonthBegin(registeredAt), getMonthEnd(registeredAt), "该客户当月已登记该事件"); - case TOTAL_LIMIT -> checkTotalRegistrationLimit(registrationEvent); - case CUSTOMER_TOTAL_LIMIT -> checkCustomerTotalRegistrationLimit(registrationEvent, customerId); - default -> { - } - } - } - - private void checkRegistrationLimitRange(String customerId, String registrationEventId, long beginRegisteredAt, long endRegisteredAt, String message) { - Long count = mapper.countByCustomerIdAndRegistrationEventIdRange(customerId, registrationEventId, beginRegisteredAt, endRegisteredAt); - TimiException.requiredTrue(TimiJava.defaultIfNull(count, 0L) < 1, message); - } - - private void checkIntervalHourRegistrationLimit(GaoRegistrationEvent registrationEvent, String customerId, long registeredAt) { - Integer limitValue = registrationEvent.getLimitValue(); - TimiException.requiredTrue(limitValue != null && 0 < limitValue, "登记事件间隔小时配置错误"); - long beginRegisteredAt = registeredAt - limitValue * 60L * 60L * 1000L + 1; - checkRegistrationLimitRange(customerId, registrationEvent.getId(), beginRegisteredAt, registeredAt, "该客户登记该事件间隔不足"); - } - - private void checkCustomerTotalRegistrationLimit(GaoRegistrationEvent registrationEvent, String customerId) { - Integer limitValue = registrationEvent.getLimitValue(); - TimiException.requiredTrue(limitValue != null && 0 < limitValue, "登记事件单客户总次数配置错误"); - Long count = mapper.countByCustomerIdAndRegistrationEventId(customerId, registrationEvent.getId()); - TimiException.requiredTrue(TimiJava.defaultIfNull(count, 0L) < limitValue, "该客户登记该事件次数已达上限"); - } - - private void checkTotalRegistrationLimit(GaoRegistrationEvent registrationEvent) { - Integer limitValue = registrationEvent.getLimitValue(); - TimiException.requiredTrue(limitValue != null && 0 < limitValue, "登记事件总次数配置错误"); - Long count = mapper.countByRegistrationEventId(registrationEvent.getId()); - TimiException.requiredTrue(TimiJava.defaultIfNull(count, 0L) < limitValue, "登记事件总次数已达上限"); - } - - private long getDayBegin(long registeredAt) { - ZoneId zoneId = ZoneId.systemDefault(); - return Instant.ofEpochMilli(registeredAt).atZone(zoneId).toLocalDate().atStartOfDay(zoneId).toInstant().toEpochMilli(); - } - - private long getDayEnd(long registeredAt) { - ZoneId zoneId = ZoneId.systemDefault(); - return Instant.ofEpochMilli(registeredAt).atZone(zoneId).toLocalDate().plusDays(1).atStartOfDay(zoneId).toInstant().toEpochMilli() - 1; - } - - private long getHalfDayBegin(long registeredAt) { - ZoneId zoneId = ZoneId.systemDefault(); - int hour = Instant.ofEpochMilli(registeredAt).atZone(zoneId).getHour(); - long dayBegin = getDayBegin(registeredAt); - return hour < 12 ? dayBegin : dayBegin + 12L * 60L * 60L * 1000L; - } - - private long getHalfDayEnd(long registeredAt) { - ZoneId zoneId = ZoneId.systemDefault(); - int hour = Instant.ofEpochMilli(registeredAt).atZone(zoneId).getHour(); - long dayBegin = getDayBegin(registeredAt); - return hour < 12 ? dayBegin + 12L * 60L * 60L * 1000L - 1 : getDayEnd(registeredAt); - } - - private long getMonthBegin(long registeredAt) { - ZoneId zoneId = ZoneId.systemDefault(); - LocalDate date = Instant.ofEpochMilli(registeredAt).atZone(zoneId).toLocalDate(); - return date.withDayOfMonth(1).atStartOfDay(zoneId).toInstant().toEpochMilli(); - } - - private long getMonthEnd(long registeredAt) { - ZoneId zoneId = ZoneId.systemDefault(); - LocalDate date = Instant.ofEpochMilli(registeredAt).atZone(zoneId).toLocalDate(); - return date.withDayOfMonth(1).plusMonths(1).atStartOfDay(zoneId).toInstant().toEpochMilli() - 1; - } - - private void checkStatRange(Long beginRegisteredAt, Long endRegisteredAt) { - TimiException.required(beginRegisteredAt, "not found beginRegisteredAt"); - TimiException.required(endRegisteredAt, "not found endRegisteredAt"); - TimiException.requiredTrue(endRegisteredAt >= beginRegisteredAt, "endRegisteredAt lt beginRegisteredAt"); - } - - private List buildDailyTrend(GaoRegistrationEventRecordTrendStatReq req, boolean byCustomer) { - TimiException.required(req.getRegistrationEventId(), "not found req.registrationEventId"); - registrationEventService.get(req.getRegistrationEventId()); - checkStatRange(req.getBeginRegisteredAt(), req.getEndRegisteredAt()); - ZoneId zoneId = ZoneId.systemDefault(); - LocalDate beginDate = Instant.ofEpochMilli(req.getBeginRegisteredAt()).atZone(zoneId).toLocalDate(); - LocalDate endDate = Instant.ofEpochMilli(req.getEndRegisteredAt()).atZone(zoneId).toLocalDate(); - checkDailyStatRange(beginDate, endDate); - long beginRegisteredAt = beginDate.atStartOfDay(zoneId).toInstant().toEpochMilli(); - long endRegisteredAt = endDate.plusDays(1).atStartOfDay(zoneId).toInstant().toEpochMilli() - 1; - Map dailyCountMap = new HashMap<>(); - List dbList = byCustomer - ? mapper.statDailyCountByCustomerIdAndRegistrationEventIdRange(req.getCustomerId(), req.getRegistrationEventId(), beginRegisteredAt, endRegisteredAt) - : mapper.statDailyCountByRegistrationEventIdRange(req.getRegistrationEventId(), beginRegisteredAt, endRegisteredAt); - for (GaoRegistrationEventRecordDailyStatView view : dbList) { - dailyCountMap.put(view.getDateValue(), TimiJava.defaultIfNull(view.getDailyCount(), 0L)); - } - long totalCount = byCustomer - ? TimiJava.defaultIfNull(mapper.countBeforeByCustomerIdAndRegistrationEventId(req.getCustomerId(), req.getRegistrationEventId(), beginRegisteredAt), 0L) - : TimiJava.defaultIfNull(mapper.countBeforeByRegistrationEventId(req.getRegistrationEventId(), beginRegisteredAt), 0L); - List list = new ArrayList<>(); - for (LocalDate date = beginDate; !date.isAfter(endDate); date = date.plusDays(1)) { - int dateValue = Integer.parseInt(DAY_FORMATTER.format(date)); - long dailyCount = TimiJava.defaultIfNull(dailyCountMap.get(dateValue), 0L); - totalCount += dailyCount; - GaoRegistrationEventRecordDailyStatView view = new GaoRegistrationEventRecordDailyStatView(); - view.setDateValue(dateValue); - view.setDailyCount(dailyCount); - view.setTotalCount(totalCount); - list.add(view); - } - return list; - } - - private GaoCustomerRegistrationRankView buildCustomerRegistrationRank(List dayStatList) { - List sortedDayStatList = dayStatList.stream() - .sorted(Comparator.comparing(GaoCustomerRegistrationDayStatView::getDateValue, Comparator.nullsLast(Comparator.reverseOrder()))) - .toList(); - GaoCustomerRegistrationDayStatView first = sortedDayStatList.get(0); - GaoCustomerRegistrationRankView view = new GaoCustomerRegistrationRankView(); - view.setCustomerId(first.getCustomerId()); - view.setCustomerCode(first.getCustomerCode()); - view.setCustomerName(first.getCustomerName()); - view.setCustomerPhotoAttachmentId(first.getCustomerPhotoAttachmentId()); - view.setTotalRegistrationDays((long) sortedDayStatList.size()); - view.setTotalRegistrationCount(sortedDayStatList.stream().mapToLong(item -> TimiJava.defaultIfNull(item.getDailyCount(), 0L)).sum()); - view.setLatestRegisteredAt(sortedDayStatList.stream().map(GaoCustomerRegistrationDayStatView::getLatestRegisteredAt).filter(Objects::nonNull).max(Long::compareTo).orElse(null)); - view.setRecentContinuousRegistrationDays(countRecentContinuousRegistrationDays(sortedDayStatList)); - return view; - } - - private long countRecentContinuousRegistrationDays(List sortedDayStatList) { - if (sortedDayStatList.isEmpty() || sortedDayStatList.get(0).getDateValue() == null) { - return 0; - } - LocalDate expectedDate = LocalDate.parse(String.valueOf(sortedDayStatList.get(0).getDateValue()), DAY_FORMATTER); - long count = 0; - for (GaoCustomerRegistrationDayStatView item : sortedDayStatList) { - if (item.getDateValue() == null) { - continue; - } - LocalDate date = LocalDate.parse(String.valueOf(item.getDateValue()), DAY_FORMATTER); - if (!date.equals(expectedDate)) { - break; - } - count++; - expectedDate = expectedDate.minusDays(1); - } - return count; - } - - private void loadTransient(List recordList) { - if (recordList == null || recordList.isEmpty()) { - return; - } - List customerIdList = recordList.stream() - .map(GaoRegistrationEventRecord::getCustomerId) - .filter(TimiJava::isNotEmpty) - .distinct() - .toList(); - Map customerMap = customerMapper.selectByIdList(new ArrayList<>(customerIdList)).stream() - .collect(Collectors.toMap(GaoCustomer::getId, Function.identity(), (left, right) -> left)); - - List registrationEventIdList = recordList.stream() - .map(GaoRegistrationEventRecord::getRegistrationEventId) - .filter(TimiJava::isNotEmpty) - .distinct() - .toList(); - Map registrationEventMap = registrationEventMapper.selectByIdList(new ArrayList<>(registrationEventIdList)).stream() - .collect(Collectors.toMap(GaoRegistrationEvent::getId, Function.identity(), (left, right) -> left)); - - List operatorUserIdList = recordList.stream() - .map(GaoRegistrationEventRecord::getOperatorUserId) - .filter(TimiJava::isNotEmpty) - .distinct() - .toList(); - Map userMap = userMapper.listByIdList(new ArrayList<>(operatorUserIdList)).stream() - .collect(Collectors.toMap(com.imyeyu.api.modules.user.entity.User::getId, Function.identity(), (left, right) -> left)); - - List recordIdList = recordList.stream() - .map(GaoRegistrationEventRecord::getId) - .filter(TimiJava::isNotEmpty) - .distinct() - .toList(); - Map> attachmentMap = attachmentService.mapByBizIdList( - Attachment.BizType.GAO_REGISTRATION_EVENT_RECORD, - recordIdList, - GaoRegistrationEventRecord.AttachType.PHOTO.name() - ); - - for (GaoRegistrationEventRecord record : recordList) { - record.setCustomer(customerMap.get(record.getCustomerId())); - record.setRegistrationEvent(registrationEventMap.get(record.getRegistrationEventId())); - record.setOperator(userMap.get(record.getOperatorUserId())); - record.setAttachmentList(attachmentMap.getOrDefault(record.getId(), List.of())); - } - } - - private void checkDailyStatRange(LocalDate beginDate, LocalDate endDate) { - long days = ChronoUnit.DAYS.between(beginDate, endDate) + 1; - TimiException.requiredTrue(days <= 366, "daily stat range too large"); - } - - private void saveAttachment(String recordId, List attachmentList) { - if (attachmentList == null) { - return; - } - for (Attachment attachment : attachmentList) { - attachment.setBizType(Attachment.BizType.GAO_REGISTRATION_EVENT_RECORD); - attachment.setBizId(recordId); - attachment.setAttachType(GaoRegistrationEventRecord.AttachType.PHOTO.name()); - } - BizUpdateReq req = new BizUpdateReq(); - req.setBizType(Attachment.BizType.GAO_REGISTRATION_EVENT_RECORD); - req.setBizId(recordId); - req.setItems(attachmentList); - attachmentService.updateByBiz(req); - } - - private List resolveAttachmentList(GaoRegistrationEventRecord record) { - if (record.getAttachmentIdList() != null) { - return buildPhotoAttachmentList(record.getAttachmentIdList()); - } - return record.getAttachmentList(); - } - - private List buildPhotoAttachmentList(List attachmentIdList) { - if (attachmentIdList == null) { - return null; - } - List list = new ArrayList<>(); - for (String attachmentId : attachmentIdList) { - if (attachmentId == null || attachmentId.isBlank()) { - continue; - } - Attachment attachment = new Attachment(); - attachment.setTempFileId(attachmentId.trim()); - attachment.setAttachType(GaoRegistrationEventRecord.AttachType.PHOTO.name()); - list.add(attachment); - } - return list; - } - - private void appendRow(Sheet sheet, int rowIndex, CellStyle style, String... values) { - Row row = sheet.createRow(rowIndex); - for (int index = 0; index < values.length; index++) { - row.createCell(index).setCellValue(values[index]); - if (style != null) { - row.getCell(index).setCellStyle(style); - } - } - } - - private String buildEventFilterText(GaoRegistrationEventRecordRangeReq req) { - if ((req.getRegistrationEventId() == null || req.getRegistrationEventId().isBlank()) && (req.getRegistrationEventIdList() == null || req.getRegistrationEventIdList().isEmpty())) { - return "全部事件"; - } - if (req.getRegistrationEventId() != null && !req.getRegistrationEventId().isBlank()) { - return "事件 ID:" + req.getRegistrationEventId(); - } - StringJoiner joiner = new StringJoiner("、"); - req.getRegistrationEventIdList().stream() - .filter(Objects::nonNull) - .filter(item -> !item.isBlank()) - .forEach(joiner::add); - return "事件 ID:" + joiner; - } - - private String formatExportTime(Long value) { - if (value == null) { - return ""; - } - return EXPORT_TIME_FORMATTER.format(Instant.ofEpochMilli(value)); - } - - private String getOperatorName(GaoRegistrationEventRecord record) { - if (record.getOperator() == null) { - return text(record.getOperatorUserId()); - } - if (record.getOperator().getNick() != null && !record.getOperator().getNick().isBlank()) { - return record.getOperator().getNick(); - } - if (record.getOperator().getName() != null && !record.getOperator().getName().isBlank()) { - return record.getOperator().getName(); - } - return text(record.getOperatorUserId()); - } - - private String text(Object value) { - return value == null ? "" : value.toString(); - } -} diff --git a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoRegistrationEventServiceImplement.java b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoRegistrationEventServiceImplement.java deleted file mode 100644 index eda704e..0000000 --- a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoRegistrationEventServiceImplement.java +++ /dev/null @@ -1,260 +0,0 @@ -package com.imyeyu.api.modules.gao.service.implement; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.imyeyu.api.config.dbsource.TimiServerDBConfig; -import com.imyeyu.api.modules.gao.entity.GaoRegistrationEvent; -import com.imyeyu.api.modules.gao.entity.GaoRegistrationEventRoleRelation; -import com.imyeyu.api.modules.gao.mapper.GaoRegistrationEventMapper; -import com.imyeyu.api.modules.gao.mapper.GaoRegistrationEventRoleRelationMapper; -import com.imyeyu.api.modules.gao.service.GaoRegistrationEventService; -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.bean.Page; -import com.imyeyu.spring.bean.PageResult; -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; - -import java.util.ArrayList; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -/// -/// 登记事件服务实现 -/// -/// @author 夜雨 -/// @since 2026-07-27 09:00 -@Slf4j -@Service -@RequiredArgsConstructor -public class GaoRegistrationEventServiceImplement extends AbstractEntityService implements GaoRegistrationEventService { - - private final ObjectMapper jackson; - - private final LoggerService loggerService; - - private final GaoRegistrationEventMapper mapper; - private final GaoRegistrationEventRoleRelationMapper roleRelationMapper; - - @Override - protected BaseMapper mapper() { - return mapper; - } - - @Override - public PageResult pageWithRegistrationEventLimit(Page page) { - PageResult result = page(page); - result.setList(result.getList().stream().filter(item -> item.getStatus() != GaoRegistrationEvent.Status.DELETED).toList()); - loadRegistrationEventLimit(result.getList()); - return result; - } - - @Transactional(TimiServerDBConfig.ROLLBACKER) - @Override - public void create(GaoRegistrationEvent entity) { - Logger logger = new Logger(Logger.Module.GAO, "GAO_REGISTRATION_EVENT_CREATE"); - try { - logger.setContent(jackson.writeValueAsString(entity)); - checkEntity(entity, false); - super.create(entity); - saveRoleRelation(entity.getId(), entity.getRoleCodeList()); - logger.setLevel(Logger.Level.INFO); - logger.setResult(entity.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("新增 GAO 登记事件失败", e); - throw new TimiException(TimiCode.ERROR, "新增 GAO 登记事件失败", e); - } finally { - loggerService.create(logger); - } - } - - @Transactional(TimiServerDBConfig.ROLLBACKER) - @Override - public void update(GaoRegistrationEvent entity) { - Logger logger = new Logger(Logger.Module.GAO, "GAO_REGISTRATION_EVENT_UPDATE"); - try { - logger.setContent(jackson.writeValueAsString(entity)); - Integer sort = entity.getSort(); - checkEntity(entity, true); - GaoRegistrationEvent current = get(entity.getId()); - current.setName(entity.getName()); - current.setDescription(entity.getDescription()); - current.setColor(entity.getColor()); - current.setStatus(entity.getStatus()); - current.setRequirePhoto(entity.getRequirePhoto()); - current.setLimitType(entity.getLimitType()); - current.setLimitValue(entity.getLimitValue()); - current.setBeginAt(entity.getBeginAt()); - current.setEndAt(entity.getEndAt()); - if (sort != null) { - current.setSort(sort); - } - mapper.update(current); - saveRoleRelation(entity.getId(), entity.getRoleCodeList()); - logger.setLevel(Logger.Level.INFO); - logger.setResult(entity.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("修改 GAO 登记事件失败", e); - throw new TimiException(TimiCode.ERROR, "修改 GAO 登记事件失败", e); - } finally { - loggerService.create(logger); - } - } - - @Transactional(TimiServerDBConfig.ROLLBACKER) - @Override - public void delete(String id) { - Logger logger = new Logger(Logger.Module.GAO, "GAO_REGISTRATION_EVENT_DELETE"); - try { - logger.setContent(id); - GaoRegistrationEvent entity = get(id); - entity.setStatus(GaoRegistrationEvent.Status.DELETED); - super.update(entity); - 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("删除 GAO 登记事件失败", e); - throw new TimiException(TimiCode.ERROR, "删除 GAO 登记事件失败", e); - } finally { - loggerService.create(logger); - } - } - - @Override - public GaoRegistrationEvent get(String id) { - GaoRegistrationEvent registrationEvent = super.get(id); - registrationEvent.setRoleCodeList(roleRelationMapper.selectRoleCodeListByRegistrationEventId(id)); - return registrationEvent; - } - - @Override - public List listEnabled() { - List list = mapper.selectEnabledList(Time.now()); - loadRegistrationEventLimit(list); - list = list.stream().filter(item -> !TimiJava.isEmpty(item.getRoleCodeList())).toList(); - return list; - } - - @Override - public List listRoleCode(String registrationEventId) { - return roleRelationMapper.selectRoleCodeListByRegistrationEventId(registrationEventId); - } - - @Transactional(TimiServerDBConfig.ROLLBACKER) - @Override - public void sort(List idList) { - if (idList == null || idList.isEmpty()) { - return; - } - List distinctIdList = new ArrayList<>(new LinkedHashSet<>(idList)); - Map eventMap = mapper.selectByIdList(distinctIdList).stream().collect(Collectors.toMap(GaoRegistrationEvent::getId, item -> item)); - TimiException.requiredTrue(eventMap.size() == distinctIdList.size(), "not found registrationEvent"); - for (int index = 0; index < distinctIdList.size(); index++) { - GaoRegistrationEvent registrationEvent = eventMap.get(distinctIdList.get(index)); - registrationEvent.setSort(index); - mapper.update(registrationEvent); - } - } - - private void checkEntity(GaoRegistrationEvent entity, boolean requireId) { - TimiException.required(entity, "not found registrationEvent"); - if (requireId) { - TimiException.required(entity.getId(), "not found registrationEvent.id"); - } - TimiException.required(entity.getName(), "not found registrationEvent.name"); - if (entity.getStatus() == null) { - entity.setStatus(GaoRegistrationEvent.Status.ACTIVE); - } - if (entity.getRequirePhoto() == null) { - entity.setRequirePhoto(false); - } - if (entity.getLimitType() == null) { - entity.setLimitType(GaoRegistrationEvent.LimitType.NONE); - } - TimiException.requiredTrue(entity.getRoleCodeList() != null && entity.getRoleCodeList() - .stream() - .anyMatch(item -> item != null && !item.isBlank()), "not found registrationEvent.roleCodeList"); - checkRegistrationLimit(entity); - checkRegisterablePeriod(entity); - if (entity.getColor() != null) { - entity.setColor(entity.getColor().trim()); - } - if (entity.getSort() == null) { - entity.setSort(0); - } - } - - private void checkRegistrationLimit(GaoRegistrationEvent entity) { - if (entity.getLimitType() == GaoRegistrationEvent.LimitType.INTERVAL_HOUR || entity.getLimitType() == GaoRegistrationEvent.LimitType.TOTAL_LIMIT || entity.getLimitType() == GaoRegistrationEvent.LimitType.CUSTOMER_TOTAL_LIMIT) { - TimiException.required(entity.getLimitValue(), "not found registrationEvent.limitValue"); - TimiException.requiredTrue(0 < entity.getLimitValue(), "registrationEvent.limitValue lte 0"); - return; - } - entity.setLimitValue(null); - } - - private void checkRegisterablePeriod(GaoRegistrationEvent entity) { - if (entity.getBeginAt() == null && entity.getEndAt() == null) { - return; - } - TimiException.requiredTrue(entity.getBeginAt() == null || entity.getEndAt() == null || entity.getBeginAt() <= entity.getEndAt(), "registrationEvent.beginAt gt registrationEvent.endAt"); - } - - private void saveRoleRelation(String registrationEventId, List roleCodeList) { - roleRelationMapper.deleteByRegistrationEventId(registrationEventId); - if (roleCodeList == null || roleCodeList.isEmpty()) { - return; - } - Set distinctCodeSet = new LinkedHashSet<>(roleCodeList); - for (String roleCode : distinctCodeSet) { - if (roleCode == null || roleCode.isBlank()) { - continue; - } - GaoRegistrationEventRoleRelation relation = new GaoRegistrationEventRoleRelation(); - relation.setRegistrationEventId(registrationEventId); - relation.setRoleCode(roleCode.trim()); - roleRelationMapper.insert(relation); - } - } - - private void loadRegistrationEventLimit(List registrationEventList) { - if (registrationEventList == null || registrationEventList.isEmpty()) { - return; - } - List registrationEventIdList = registrationEventList.stream().map(GaoRegistrationEvent::getId).distinct().toList(); - Map> roleCodeMap = roleRelationMapper.selectByRegistrationEventIdList(new ArrayList<>(registrationEventIdList)) - .stream() - .collect(Collectors.groupingBy(GaoRegistrationEventRoleRelation::getRegistrationEventId, Collectors.mapping(GaoRegistrationEventRoleRelation::getRoleCode, Collectors.toList()))); - for (GaoRegistrationEvent registrationEvent : registrationEventList) { - registrationEvent.setRoleCodeList(roleCodeMap.getOrDefault(registrationEvent.getId(), List.of())); - } - } -} diff --git a/src/main/java/com/imyeyu/api/modules/gao/util/CustomerXSSFBuilder.java b/src/main/java/com/imyeyu/api/modules/gao/util/CustomerXSSFBuilder.java new file mode 100644 index 0000000..eed8e8b --- /dev/null +++ b/src/main/java/com/imyeyu/api/modules/gao/util/CustomerXSSFBuilder.java @@ -0,0 +1,67 @@ +package com.imyeyu.api.modules.gao.util; + +import com.imyeyu.api.modules.gao.entity.GaoCustomer; +import com.imyeyu.api.util.BaseXSSFBuilder; +import com.imyeyu.utils.Time; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.springframework.stereotype.Component; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.List; + +/// +/// +/// @author 夜雨 +/// @since 2026-08-04 15:59 +@Component +public class CustomerXSSFBuilder extends BaseXSSFBuilder { + + public byte[] build(List list) throws IOException { + try (Workbook workbook = new XSSFWorkbook(); ByteArrayOutputStream output = new ByteArrayOutputStream()) { + Sheet sheet = workbook.createSheet("客户列表"); + CellStyle titleStyle = createTitleStyle(workbook); + CellStyle headerStyle = createHeaderStyle(workbook); + + appendRow(sheet, 0, titleStyle, "客户列表导出"); + appendRow(sheet, 1, null, "生成时间", Time.nowString()); + appendRow(sheet, 2, null, "记录总数", String.valueOf(list.size())); + appendRow(sheet, 3, headerStyle, "客户编码", "姓名", "性别", "出生日期(公历)", "出生日期历法", "年龄", "电话", "地址", "病症", "备注", "邀请人", "邀请客户数", "创建时间"); + int rowIndex = 4; + for (GaoCustomer customer : list) { + appendRow(sheet, rowIndex, null, + text(customer.getCode()), + text(customer.getName()), + gender(customer.getGender()), + text(customer.getBirthdate()), + formatBirthdateCalendar(customer), + customer.getAge() == null ? "" : String.valueOf(customer.getAge()), + text(customer.getTelephone()), + text(customer.getAddress()), + text(customer.getDisease()), + text(customer.getRemark()), + customer.getIntroducerCustomer() == null ? "" : text(customer.getIntroducerCustomer().getName()), + String.valueOf(customer.getInvitedCount()), + Time.toDateTime(customer.getCreatedAt()) + ); + rowIndex++; + } + for (int index = 0; index < 13; index++) { + sheet.autoSizeColumn(index); + sheet.setColumnWidth(index, Math.clamp(sheet.getColumnWidth(index), 10 * 256, 36 * 256)); + } + workbook.write(output); + return output.toByteArray(); + } + } + + private String formatBirthdateCalendar(GaoCustomer customer) { + if (customer.getBirthdateCalendar() == null) { + return "公历"; + } + return customer.getBirthdateCalendar() == GaoCustomer.BirthdateCalendar.LUNAR ? "农历" : "公历"; + } +} diff --git a/src/main/java/com/imyeyu/api/modules/gao/util/EventRecordXSSFBuilder.java b/src/main/java/com/imyeyu/api/modules/gao/util/EventRecordXSSFBuilder.java new file mode 100644 index 0000000..ce4db1f --- /dev/null +++ b/src/main/java/com/imyeyu/api/modules/gao/util/EventRecordXSSFBuilder.java @@ -0,0 +1,53 @@ +package com.imyeyu.api.modules.gao.util; + +import com.imyeyu.api.modules.gao.entity.GaoEventRecord; +import com.imyeyu.api.util.BaseXSSFBuilder; +import com.imyeyu.utils.Time; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.springframework.stereotype.Component; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.List; + +/// +/// +/// @author 夜雨 +/// @since 2026-08-05 11:03 +@Component +public class EventRecordXSSFBuilder extends BaseXSSFBuilder { + + public byte[] build(List list) throws IOException { + try (Workbook workbook = new XSSFWorkbook(); ByteArrayOutputStream output = new ByteArrayOutputStream()) { + Sheet sheet = workbook.createSheet("登记事件记录"); + CellStyle titleStyle = createTitleStyle(workbook); + CellStyle headerStyle = createHeaderStyle(workbook); + + appendRow(sheet, 0, titleStyle, "登记事件记录导出"); + appendRow(sheet, 1, null, "生成时间", Time.nowString()); + appendRow(sheet, 2, null, "记录总数", String.valueOf(list.size())); + appendRow(sheet, 3, headerStyle, "客户编码", "客户姓名", "登记事件", "登记时间", "登记人", "备注"); + int rowIndex = 4; + for (GaoEventRecord record : list) { + appendRow(sheet, rowIndex, null, + record.getCustomer() == null ? "" : text(record.getCustomer().getCode()), + record.getCustomer() == null ? "" : text(record.getCustomer().getName()), + record.getEvent() == null ? "" : text(record.getEvent().getName()), + Time.toDateTime(record.getRegisteredAt()), + record.getOperator().getName(), + text(record.getRemark()) + ); + rowIndex++; + } + for (int i = 0; i < 6; i++) { + sheet.autoSizeColumn(i); + sheet.setColumnWidth(i, Math.clamp(sheet.getColumnWidth(i), 12 * 256, 40 * 256)); + } + workbook.write(output); + return output.toByteArray(); + } + } +} diff --git a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoCustomerDailyStatView.java b/src/main/java/com/imyeyu/api/modules/gao/vo/GaoCustomerDailyStateView.java similarity index 89% rename from src/main/java/com/imyeyu/api/modules/gao/vo/GaoCustomerDailyStatView.java rename to src/main/java/com/imyeyu/api/modules/gao/vo/GaoCustomerDailyStateView.java index 3a75c4c..8198c95 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoCustomerDailyStatView.java +++ b/src/main/java/com/imyeyu/api/modules/gao/vo/GaoCustomerDailyStateView.java @@ -8,7 +8,7 @@ import lombok.Data; /// @author Codex /// @since 2026-07-28 @Data -public class GaoCustomerDailyStatView { +public class GaoCustomerDailyStateView { /// 日期值,格式为 yyyyMMdd private Integer dateValue; diff --git a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoCustomerRegistrationRankView.java b/src/main/java/com/imyeyu/api/modules/gao/vo/GaoCustomerEventRankView.java similarity index 52% rename from src/main/java/com/imyeyu/api/modules/gao/vo/GaoCustomerRegistrationRankView.java rename to src/main/java/com/imyeyu/api/modules/gao/vo/GaoCustomerEventRankView.java index ee9fb11..db3a492 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoCustomerRegistrationRankView.java +++ b/src/main/java/com/imyeyu/api/modules/gao/vo/GaoCustomerEventRankView.java @@ -1,5 +1,6 @@ package com.imyeyu.api.modules.gao.vo; +import com.imyeyu.api.modules.gao.entity.GaoCustomer; import lombok.Data; /// @@ -8,7 +9,7 @@ import lombok.Data; /// @author Codex /// @since 2026-08-03 @Data -public class GaoCustomerRegistrationRankView { +public class GaoCustomerEventRankView { /// 排名 private Integer rank; @@ -16,23 +17,17 @@ public class GaoCustomerRegistrationRankView { /// 客户 ID private String customerId; - /// 客户编码 - private String customerCode; - - /// 客户姓名 - private String customerName; - - /// 客户头像附件 ID - private String customerPhotoAttachmentId; + /// 客户 + private GaoCustomer customer; /// 累计登记天数 - private Long totalRegistrationDays; + private Long totalEventDays; /// 累计登记次数 - private Long totalRegistrationCount; + private Long totalEventCount; /// 最近连续登记天数 - private Long recentContinuousRegistrationDays; + private Long recentContinuousEventDays; /// 最后登记时间 private Long latestRegisteredAt; diff --git a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoCustomerInviteUpdateReq.java b/src/main/java/com/imyeyu/api/modules/gao/vo/GaoCustomerInviteUpdateReq.java deleted file mode 100644 index fe21f48..0000000 --- a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoCustomerInviteUpdateReq.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.imyeyu.api.modules.gao.vo; - -import lombok.Data; - -/// -/// 客户邀请关系更新请求 -/// -/// @author Codex -/// @since 2026-07-27 -@Data -public class GaoCustomerInviteUpdateReq { - - /// 客户 ID - private String customerId; - - /// 介绍人客户 ID - private String introducerCustomerId; -} diff --git a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoCustomerRegisterStatView.java b/src/main/java/com/imyeyu/api/modules/gao/vo/GaoCustomerRegisterStatView.java deleted file mode 100644 index 741b448..0000000 --- a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoCustomerRegisterStatView.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.imyeyu.api.modules.gao.vo; - -import lombok.Data; - -/// -/// 客户登记统计视图 -/// -/// @author Codex -/// @since 2026-07-27 -@Data -public class GaoCustomerRegisterStatView { - - /// 客户 ID - private String customerId; - - /// 客户姓名 - private String customerName; - - /// 登记事件 ID - private String registrationEventId; - - /// 登记事件名称 - private String registrationEventName; - - /// 数量 - private Long count; - - /// 最后登记时间 - private Long latestRegisteredAt; -} diff --git a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoCustomerRegistrationDayStatView.java b/src/main/java/com/imyeyu/api/modules/gao/vo/GaoCustomerRegistrationDayStatView.java deleted file mode 100644 index 902977c..0000000 --- a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoCustomerRegistrationDayStatView.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.imyeyu.api.modules.gao.vo; - -import lombok.Data; - -/// -/// 客户登记日期统计视图 -/// -/// @author Codex -/// @since 2026-08-03 -@Data -public class GaoCustomerRegistrationDayStatView { - - /// 客户 ID - private String customerId; - - /// 客户编码 - private String customerCode; - - /// 客户姓名 - private String customerName; - - /// 客户头像附件 ID - private String customerPhotoAttachmentId; - - /// 登记日期,格式 yyyyMMdd - private Integer dateValue; - - /// 当日登记次数 - private Long dailyCount; - - /// 当日最后登记时间 - private Long latestRegisteredAt; -} diff --git a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoCustomerSearchReq.java b/src/main/java/com/imyeyu/api/modules/gao/vo/GaoCustomerSearchReq.java deleted file mode 100644 index 244c1f9..0000000 --- a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoCustomerSearchReq.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.imyeyu.api.modules.gao.vo; - -import lombok.Data; - -/// -/// 客户搜索请求 -/// -/// @author Codex -/// @since 2026-07-27 -@Data -public class GaoCustomerSearchReq { - - /// 关键字,匹配编码、姓名、手机号、电话、住址、病症、条例部位 - private String keyword; - - /// 介绍人客户 ID - private String introducerCustomerId; -} diff --git a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoCustomerTrendStatReq.java b/src/main/java/com/imyeyu/api/modules/gao/vo/GaoCustomerTrendStateReq.java similarity index 86% rename from src/main/java/com/imyeyu/api/modules/gao/vo/GaoCustomerTrendStatReq.java rename to src/main/java/com/imyeyu/api/modules/gao/vo/GaoCustomerTrendStateReq.java index b0ca66a..2763f5e 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoCustomerTrendStatReq.java +++ b/src/main/java/com/imyeyu/api/modules/gao/vo/GaoCustomerTrendStateReq.java @@ -8,7 +8,7 @@ import lombok.Data; /// @author Codex /// @since 2026-07-28 @Data -public class GaoCustomerTrendStatReq { +public class GaoCustomerTrendStateReq { /// 开始创建时间 private Long beginCreatedAt; diff --git a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoRegistrationEventRecordRangeReq.java b/src/main/java/com/imyeyu/api/modules/gao/vo/GaoEventRecordPage.java similarity index 58% rename from src/main/java/com/imyeyu/api/modules/gao/vo/GaoRegistrationEventRecordRangeReq.java rename to src/main/java/com/imyeyu/api/modules/gao/vo/GaoEventRecordPage.java index d579c34..d64917d 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoRegistrationEventRecordRangeReq.java +++ b/src/main/java/com/imyeyu/api/modules/gao/vo/GaoEventRecordPage.java @@ -1,32 +1,32 @@ package com.imyeyu.api.modules.gao.vo; +import com.imyeyu.java.bean.BasePage; import lombok.Data; +import lombok.EqualsAndHashCode; import java.util.List; /// /// 登记事件记录范围查询请求 /// -/// @author Codex -/// @since 2026-07-28 +/// @author 夜雨 +/// @since 2026-07-28 11:01 @Data -public class GaoRegistrationEventRecordRangeReq { +@EqualsAndHashCode(callSuper = true) +public class GaoEventRecordPage extends BasePage { /// 客户 ID,为空查询全部客户 private String customerId; - /// 登记事件 ID,为空查询全部登记事件 - private String registrationEventId; - /// 登记事件 ID 列表,为空查询全部登记事件 - private List registrationEventIdList; + private List eventIdList; /// 关键词,匹配客户、登记事件、备注、登记人 private String keyword; /// 开始登记时间 - private Long beginRegisteredAt; + private Long beginAt; /// 结束登记时间 - private Long endRegisteredAt; + private Long endAt; } diff --git a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoEventRecordRankPage.java b/src/main/java/com/imyeyu/api/modules/gao/vo/GaoEventRecordRankPage.java new file mode 100644 index 0000000..7073947 --- /dev/null +++ b/src/main/java/com/imyeyu/api/modules/gao/vo/GaoEventRecordRankPage.java @@ -0,0 +1,28 @@ +package com.imyeyu.api.modules.gao.vo; + +import com.imyeyu.java.bean.BasePage; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +/// +/// +/// @author 夜雨 +/// @since 2026-08-05 12:44 +@Data +@EqualsAndHashCode(callSuper = true) +public class GaoEventRecordRankPage extends BasePage { + + /// 客户 ID,为空查询全部客户 + private String customerId; + + /// 登记事件 ID 列表,为空查询全部登记事件 + private List eventIdList; + + /// 开始登记时间 + private Long beginAt; + + /// 结束登记时间 + private Long endAt; +} diff --git a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoQuickRegistrationEventRecordReq.java b/src/main/java/com/imyeyu/api/modules/gao/vo/GaoQuickRegistrationEventRecordReq.java deleted file mode 100644 index 96d7dc6..0000000 --- a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoQuickRegistrationEventRecordReq.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.imyeyu.api.modules.gao.vo; - -import lombok.Data; - -import java.util.ArrayList; -import java.util.List; - -/// -/// 快速录入并登记请求 -/// -/// @author Codex -/// @since 2026-07-28 -@Data -public class GaoQuickRegistrationEventRecordReq { - - /// 客户编码 - private String customerCode; - - /// 登记登记事件 ID - private String registrationEventId; - - /// 备注 - private String remark; - - /// 临时照片附件 ID 列表 - private List attachmentIdList = new ArrayList<>(); -} diff --git a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoRegistrationEventPeriodStatView.java b/src/main/java/com/imyeyu/api/modules/gao/vo/GaoRegistrationEventPeriodStatView.java deleted file mode 100644 index c461e9f..0000000 --- a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoRegistrationEventPeriodStatView.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.imyeyu.api.modules.gao.vo; - -import lombok.Data; - -/// -/// 登记事件周期统计视图 -/// -/// @author Codex -/// @since 2026-07-27 -@Data -public class GaoRegistrationEventPeriodStatView { - - /// 登记事件 ID - private String registrationEventId; - - /// 周期值 - private Integer periodValue; - - /// 数量 - private Long count; -} diff --git a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoRegistrationEventRecordCalendarView.java b/src/main/java/com/imyeyu/api/modules/gao/vo/GaoRegistrationEventRecordCalendarView.java deleted file mode 100644 index 3ee29a9..0000000 --- a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoRegistrationEventRecordCalendarView.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.imyeyu.api.modules.gao.vo; - -import lombok.Data; - -/// -/// 登记事件记录日历轻量视图 -/// -/// @author Codex -/// @since 2026-08-01 -@Data -public class GaoRegistrationEventRecordCalendarView { - - /// 记录 ID - private String id; - - /// 客户 ID - private String customerId; - - /// 客户编码 - private String customerCode; - - /// 客户姓名 - private String customerName; - - /// 登记事件 ID - private String registrationEventId; - - /// 登记时间 - private Long registeredAt; -} diff --git a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoRegistrationEventRecordCreateReq.java b/src/main/java/com/imyeyu/api/modules/gao/vo/GaoRegistrationEventRecordCreateReq.java deleted file mode 100644 index 72d8b81..0000000 --- a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoRegistrationEventRecordCreateReq.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.imyeyu.api.modules.gao.vo; - -import lombok.Data; - -import java.util.ArrayList; -import java.util.List; - -/// -/// 登记登记事件记录创建请求 -/// -/// @author Codex -/// @since 2026-07-27 -@Data -public class GaoRegistrationEventRecordCreateReq { - - /// 客户 ID 列表,批量登记时使用 - private List customerIdList = new ArrayList<>(); - - /// 登记事件 ID - private String registrationEventId; - - /// 备注 - private String remark; - - /// 登记时间 - private Long registeredAt; - - /// 临时照片附件 ID 列表 - private List attachmentIdList = new ArrayList<>(); -} diff --git a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoRegistrationEventRecordDailyStatView.java b/src/main/java/com/imyeyu/api/modules/gao/vo/GaoRegistrationEventRecordDailyStatView.java deleted file mode 100644 index 477b902..0000000 --- a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoRegistrationEventRecordDailyStatView.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.imyeyu.api.modules.gao.vo; - -import lombok.Data; - -/// -/// 登记事件记录每日统计视图 -/// -/// @author Codex -/// @since 2026-07-28 -@Data -public class GaoRegistrationEventRecordDailyStatView { - - /// 日期值,格式为 yyyyMMdd - private Integer dateValue; - - /// 当日登记数 - private Long dailyCount; - - /// 截止当日累计登记数 - private Long totalCount; -} diff --git a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoRegistrationEventRecordListItemView.java b/src/main/java/com/imyeyu/api/modules/gao/vo/GaoRegistrationEventRecordListItemView.java deleted file mode 100644 index 5bca512..0000000 --- a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoRegistrationEventRecordListItemView.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.imyeyu.api.modules.gao.vo; - -import lombok.Data; - -/// -/// 登记事件记录列表项视图 -/// -/// @author Codex -/// @since 2026-08-01 -@Data -public class GaoRegistrationEventRecordListItemView { - - /// 记录 ID - private String id; - - /// 客户 ID - private String customerId; - - /// 客户姓名 - private String customerName; - - /// 客户头像附件 ID - private String customerPhotoAttachmentId; - - /// 登记事件 ID - private String registrationEventId; - - /// 登记事件名称 - private String registrationEventName; - - /// 登记时间 - private Long registeredAt; -} diff --git a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoRegistrationEventRecordPeriodReq.java b/src/main/java/com/imyeyu/api/modules/gao/vo/GaoRegistrationEventRecordPeriodReq.java deleted file mode 100644 index 5fe8e2a..0000000 --- a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoRegistrationEventRecordPeriodReq.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.imyeyu.api.modules.gao.vo; - -import lombok.Data; - -/// -/// 登记事件记录按周期查询请求 -/// -/// @author Codex -/// @since 2026-07-27 -@Data -public class GaoRegistrationEventRecordPeriodReq { - - /// - /// 周期类型 - /// - /// @author Codex - /// @since 2026-07-27 - public enum PeriodType { - - DAY, - - MONTH, - - YEAR - } - - /// 登记事件 ID - private String registrationEventId; - - /// 周期类型 - private PeriodType periodType; - - /// 周期值,日为 yyyyMMdd,月为 yyyyMM,年为 yyyy - private Integer periodValue; -} diff --git a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoRegistrationEventRecordStatReq.java b/src/main/java/com/imyeyu/api/modules/gao/vo/GaoRegistrationEventRecordStatReq.java deleted file mode 100644 index 51214bf..0000000 --- a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoRegistrationEventRecordStatReq.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.imyeyu.api.modules.gao.vo; - -import lombok.Data; - -/// -/// 登记事件记录统计请求 -/// -/// @author Codex -/// @since 2026-07-28 -@Data -public class GaoRegistrationEventRecordStatReq { - - /// 客户 ID,全部客户统计时不传 - private String customerId; - - /// 开始登记时间 - private Long beginRegisteredAt; - - /// 结束登记时间 - private Long endRegisteredAt; -} diff --git a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoRegistrationEventRecordTrendStatReq.java b/src/main/java/com/imyeyu/api/modules/gao/vo/GaoRegistrationEventRecordTrendStatReq.java deleted file mode 100644 index b83be89..0000000 --- a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoRegistrationEventRecordTrendStatReq.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.imyeyu.api.modules.gao.vo; - -import lombok.Data; - -/// -/// 登记事件记录趋势统计请求 -/// -/// @author Codex -/// @since 2026-07-28 -@Data -public class GaoRegistrationEventRecordTrendStatReq { - - /// 客户 ID,全部客户趋势时不传 - private String customerId; - - /// 登记事件 ID - private String registrationEventId; - - /// 开始登记时间 - private Long beginRegisteredAt; - - /// 结束登记时间 - private Long endRegisteredAt; -} diff --git a/src/main/java/com/imyeyu/api/modules/user/controller/UserController.java b/src/main/java/com/imyeyu/api/modules/user/controller/UserController.java index 5ff2101..c5315e2 100644 --- a/src/main/java/com/imyeyu/api/modules/user/controller/UserController.java +++ b/src/main/java/com/imyeyu/api/modules/user/controller/UserController.java @@ -7,7 +7,6 @@ import com.imyeyu.api.bean.CorePermissionCode; import com.imyeyu.api.bean.CoreRoleCode; import com.imyeyu.api.modules.common.entity.Attachment; import com.imyeyu.api.modules.common.service.AttachmentService; -import com.imyeyu.api.modules.common.vo.attach.BizUpdateReq; import com.imyeyu.api.modules.user.entity.User; import com.imyeyu.api.modules.user.service.UserLoginService; import com.imyeyu.api.modules.user.service.UserService; @@ -245,9 +244,6 @@ public class UserController { if (tempFileIdList == null || tempFileIdList.isEmpty()) { return; } - BizUpdateReq req = new BizUpdateReq(); - req.setBizType(Attachment.BizType.USER); - req.setBizId(userId); List items = attachmentService.listByBizId(Attachment.BizType.USER, userId).stream() .filter(item -> !User.AttachType.AVATAR.name().equals(item.getAttachType())) .map(item -> { @@ -264,7 +260,6 @@ public class UserController { }).toList(); items = new ArrayList<>(items); items.addAll(avatarItems); - req.setItems(items); - attachmentService.updateByBiz(req); + attachmentService.updateByBizId(Attachment.BizType.USER, userId, items); } } diff --git a/src/main/java/com/imyeyu/api/util/BaseXSSFBuilder.java b/src/main/java/com/imyeyu/api/util/BaseXSSFBuilder.java new file mode 100644 index 0000000..74d9afb --- /dev/null +++ b/src/main/java/com/imyeyu/api/util/BaseXSSFBuilder.java @@ -0,0 +1,65 @@ +package com.imyeyu.api.util; + +import com.imyeyu.api.modules.user.bean.Gender; +import com.imyeyu.java.TimiJava; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.Font; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; + +import java.io.IOException; +import java.util.List; + +/// +/// +/// @author 夜雨 +/// @since 2026-08-04 15:57 +public abstract class BaseXSSFBuilder { + + public abstract byte[] build(List list) throws IOException; + + protected CellStyle createTitleStyle(Workbook workbook) { + CellStyle style = workbook.createCellStyle(); + Font font = workbook.createFont(); + font.setBold(true); + font.setFontHeightInPoints((short) 14); + style.setFont(font); + return style; + } + + protected CellStyle createHeaderStyle(Workbook workbook) { + CellStyle style = workbook.createCellStyle(); + Font font = workbook.createFont(); + font.setBold(true); + style.setFont(font); + return style; + } + + protected void appendRow(Sheet sheet, int rowIndex, CellStyle style, String... values) { + Row row = sheet.createRow(rowIndex); + for (int index = 0; index < values.length; index++) { + row.createCell(index).setCellValue(values[index]); + if (style != null) { + row.getCell(index).setCellStyle(style); + } + } + } + + protected String text(Object obj) { + if (obj == null) { + return ""; + } + return TimiJava.defaultIfEmpty(obj.toString(), ""); + } + + protected String gender(Gender gender) { + if (gender == null) { + return ""; + } + return switch (gender) { + case MALE -> "男"; + case FEMALE -> "女"; + }; + } +} diff --git a/src/main/resources/db/migration/timiserver/V23__rename_gao_event_tables.sql b/src/main/resources/db/migration/timiserver/V23__rename_gao_event_tables.sql new file mode 100644 index 0000000..2e4ec83 --- /dev/null +++ b/src/main/resources/db/migration/timiserver/V23__rename_gao_event_tables.sql @@ -0,0 +1,54 @@ +RENAME TABLE `gao_registration_event` TO `gao_event`; + +ALTER TABLE `gao_event` + DROP KEY `idx_gao_registration_event_status`, + ADD KEY `idx_gao_event_status` (`status`, `deleted_at`, `sort`, `created_at`); + +RENAME TABLE `gao_registration_event_role_relation` TO `gao_event_role_relation`; + +ALTER TABLE `gao_event_role_relation` + DROP KEY `idx_gao_registration_event_role_relation_registration_event_id`, + CHANGE COLUMN `registration_event_id` `event_id` VARCHAR(36) NOT NULL COMMENT '登记事件 ID', + ADD KEY `idx_gao_event_role_relation_event_id` (`event_id`); + +RENAME TABLE `gao_registration_event_record` TO `gao_event_record`; + +ALTER TABLE `gao_event_record` + DROP KEY `idx_gao_registration_event_record_customer_id`, + DROP KEY `idx_gao_registration_event_record_registration_event_id`, + DROP KEY `idx_gao_registration_event_record_registered_at`, + CHANGE COLUMN `registration_event_id` `event_id` VARCHAR(36) NOT NULL COMMENT '登记事件 ID', + ADD KEY `idx_gao_event_record_customer_id` (`customer_id`), + ADD KEY `idx_gao_event_record_event_id` (`event_id`), + ADD KEY `idx_gao_event_record_registered_at` (`registered_at`); + +UPDATE `attachment` +SET `biz_type` = 'GAO_EVENT_RECORD' +WHERE `biz_type` = 'GAO_REGISTRATION_EVENT_RECORD'; + +UPDATE `permission` +SET `code` = CASE `code` + WHEN 'REGISTRATION_EVENT:CREATE' THEN 'EVENT:CREATE' + WHEN 'REGISTRATION_EVENT:READ' THEN 'EVENT:READ' + WHEN 'REGISTRATION_EVENT:UPDATE' THEN 'EVENT:UPDATE' + WHEN 'REGISTRATION_EVENT:DELETE' THEN 'EVENT:DELETE' + WHEN 'REGISTRATION_EVENT_RECORD:CREATE' THEN 'EVENT_RECORD:CREATE' + WHEN 'REGISTRATION_EVENT_RECORD:READ' THEN 'EVENT_RECORD:READ' + WHEN 'REGISTRATION_EVENT_RECORD:EXPORT' THEN 'EVENT_RECORD:EXPORT' + WHEN 'REGISTRATION_EVENT_RECORD:UPDATE' THEN 'EVENT_RECORD:UPDATE' + WHEN 'REGISTRATION_EVENT_RECORD:DELETE' THEN 'EVENT_RECORD:DELETE' + ELSE `code` +END +WHERE + `module_code` = 'GAO' + AND `code` IN ( + 'REGISTRATION_EVENT:CREATE', + 'REGISTRATION_EVENT:READ', + 'REGISTRATION_EVENT:UPDATE', + 'REGISTRATION_EVENT:DELETE', + 'REGISTRATION_EVENT_RECORD:CREATE', + 'REGISTRATION_EVENT_RECORD:READ', + 'REGISTRATION_EVENT_RECORD:EXPORT', + 'REGISTRATION_EVENT_RECORD:UPDATE', + 'REGISTRATION_EVENT_RECORD:DELETE' + ); diff --git a/src/main/resources/mapper/timi-server/AttachmentMapper.xml b/src/main/resources/mapper/timi-server/AttachmentMapper.xml index e06d291..65bbae8 100644 --- a/src/main/resources/mapper/timi-server/AttachmentMapper.xml +++ b/src/main/resources/mapper/timi-server/AttachmentMapper.xml @@ -14,7 +14,7 @@ - AND 1 = 0 + AND FALSE AND `deleted_at` IS NULL AND (`destroy_at` IS NULL OR UNIX_TIMESTAMP() < `destroy_at`) @@ -24,6 +24,35 @@ #{attachType} - ORDER BY `created_at` ASC + ORDER BY `created_at` + + diff --git a/src/main/resources/mapper/timi-server/GaoCustomerMapper.xml b/src/main/resources/mapper/timi-server/GaoCustomerMapper.xml index b7db501..6cbed41 100644 --- a/src/main/resources/mapper/timi-server/GaoCustomerMapper.xml +++ b/src/main/resources/mapper/timi-server/GaoCustomerMapper.xml @@ -1,33 +1,6 @@ - - 1 = 1 - AND `deleted_at` IS NULL - - AND ( - `code` LIKE CONCAT('%', #{keyword}, '%') - OR `name` LIKE CONCAT('%', #{keyword}, '%') - OR `telephone` LIKE CONCAT('%', #{keyword}, '%') - OR `address` LIKE CONCAT('%', #{keyword}, '%') - OR `disease` LIKE CONCAT('%', #{keyword}, '%') - OR CAST(`conditioning_part` AS CHAR) LIKE CONCAT('%', #{keyword}, '%') - ) - - - AND `introducer_customer_id` = #{introducerCustomerId} - - - - - - - - - - - - - - - UPDATE `gao_customer` - SET - `invited_count` = GREATEST(IFNULL(`invited_count`, 0) + #{delta}, 0), - `updated_at` = UNIX_TIMESTAMP() * 1000 - WHERE - `id` = #{customerId} - AND `deleted_at` IS NULL - - - SELECT CAST(DATE_FORMAT(FROM_UNIXTIME(`created_at` / 1000), '%Y%m%d') AS UNSIGNED) AS `dateValue`, COUNT(1) AS `newCustomerCount` @@ -109,7 +35,7 @@ AND `deleted_at` IS NULL - SELECT IFNULL(CAST(`gender` AS CHAR), 'UNKNOWN') AS `genderCode`, COUNT(1) AS `count` diff --git a/src/main/resources/mapper/timi-server/GaoRegistrationEventMapper.xml b/src/main/resources/mapper/timi-server/GaoEventMapper.xml similarity index 63% rename from src/main/resources/mapper/timi-server/GaoRegistrationEventMapper.xml rename to src/main/resources/mapper/timi-server/GaoEventMapper.xml index 9709039..8750dcd 100644 --- a/src/main/resources/mapper/timi-server/GaoRegistrationEventMapper.xml +++ b/src/main/resources/mapper/timi-server/GaoEventMapper.xml @@ -1,12 +1,12 @@ - - SELECT * - FROM `gao_registration_event` + FROM `gao_event` WHERE - 1 = 1 + TRUE AND `id` IN @@ -14,20 +14,20 @@ - AND 1 = 0 + AND FALSE AND `deleted_at` IS NULL - SELECT * - FROM `gao_registration_event` + FROM `gao_event` WHERE - (`status` IS NULL OR `status` = 'ACTIVE') + `status` = '${@com.imyeyu.api.modules.gao.entity.GaoEvent$Status@ACTIVE.name()}' AND (`begin_at` IS NULL OR `begin_at` <= #{now}) AND (`end_at` IS NULL OR #{now} <= `end_at`) AND `deleted_at` IS NULL - ORDER BY `sort` ASC, `created_at` ASC + ORDER BY `sort`, `created_at` diff --git a/src/main/resources/mapper/timi-server/GaoEventRecordMapper.xml b/src/main/resources/mapper/timi-server/GaoEventRecordMapper.xml new file mode 100644 index 0000000..aaea72d --- /dev/null +++ b/src/main/resources/mapper/timi-server/GaoEventRecordMapper.xml @@ -0,0 +1,171 @@ + + + + + + + FROM `gao_event_record` r + LEFT JOIN `gao_customer` c ON r.`customer_id` = c.`id` + + + r.`registered_at` >= #{beginAt} + AND r.`registered_at` <= #{endAt} + AND r.`deleted_at` IS NULL + + AND r.`customer_id` = #{customerId} + + + AND r.`event_id` IN + + #{eventId} + + + + AND ( + c.`code` LIKE CONCAT('%', #{keyword}, '%') + OR c.`name` LIKE CONCAT('%', #{keyword}, '%') + OR r.`remark` LIKE CONCAT('%', #{keyword}, '%') + OR r.`operator_user_id` LIKE CONCAT('%', #{keyword}, '%') + ) + + + + + + + r.`deleted_at` IS NULL + AND c.`deleted_at` IS NULL + + AND r.`customer_id` = #{customerId} + + + AND r.`registered_at` >= #{beginAt} + + + AND r.`registered_at` <= #{endAt} + + + AND r.`event_id` IN + + #{eventId} + + + + + + diff --git a/src/main/resources/mapper/timi-server/GaoEventRoleRelationMapper.xml b/src/main/resources/mapper/timi-server/GaoEventRoleRelationMapper.xml new file mode 100644 index 0000000..410bb9e --- /dev/null +++ b/src/main/resources/mapper/timi-server/GaoEventRoleRelationMapper.xml @@ -0,0 +1,22 @@ + + + + + diff --git a/src/main/resources/mapper/timi-server/GaoRegistrationEventRecordMapper.xml b/src/main/resources/mapper/timi-server/GaoRegistrationEventRecordMapper.xml deleted file mode 100644 index 12e0902..0000000 --- a/src/main/resources/mapper/timi-server/GaoRegistrationEventRecordMapper.xml +++ /dev/null @@ -1,389 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - r.`registered_at` >= #{req.beginRegisteredAt} - AND r.`registered_at` <= #{req.endRegisteredAt} - AND r.`deleted_at` IS NULL - - AND r.`customer_id` = #{req.customerId} - - - AND r.`registration_event_id` = #{req.registrationEventId} - - - AND r.`registration_event_id` IN - - #{registrationEventId} - - - - AND ( - c.`code` LIKE CONCAT('%', #{req.keyword}, '%') - OR c.`name` LIKE CONCAT('%', #{req.keyword}, '%') - OR e.`name` LIKE CONCAT('%', #{req.keyword}, '%') - OR r.`remark` LIKE CONCAT('%', #{req.keyword}, '%') - OR u.`name` LIKE CONCAT('%', #{req.keyword}, '%') - OR u.`nick` LIKE CONCAT('%', #{req.keyword}, '%') - OR r.`operator_user_id` LIKE CONCAT('%', #{req.keyword}, '%') - ) - - - - - FROM `gao_registration_event_record` r - LEFT JOIN `gao_customer` c ON r.`customer_id` = c.`id` - LEFT JOIN `gao_registration_event` e ON r.`registration_event_id` = e.`id` - LEFT JOIN `user` u ON r.`operator_user_id` = u.`id` - - - - - - - - - - diff --git a/src/main/resources/mapper/timi-server/UserMapper.xml b/src/main/resources/mapper/timi-server/UserMapper.xml index fd49dd3..2440a33 100644 --- a/src/main/resources/mapper/timi-server/UserMapper.xml +++ b/src/main/resources/mapper/timi-server/UserMapper.xml @@ -6,7 +6,7 @@ * FROM `user` WHERE - 1 = 1 + TRUE AND `id` IN @@ -14,7 +14,7 @@ - AND 1 = 0 + AND FALSE AND `deleted_at` IS NULL